I've got this repository of Python utility functions. You know the type: small, often-used snippets, written quickly over months, mostly undocumented. I know, I know. "Future me" was supposed to do it. Well, "future me" is now "present me," and present me just wants to be able to remember what process_complex_csv_data(filepath, schema_config, output_dir) actually does without diving into its guts every time.
Docstrings. Glorious, helpful docstrings. I dread writing them. It feels like busywork. So, naturally, my mind drifted to our new AI overlords. Surely, a large language model could whip up some decent docstrings for my functions, saving me hours?
What I Tried (And Failed)
My first attempts were... optimistic, to say the least. I'd grab a function, paste it into the OpenAI playground (or sometimes directly into a script using gpt-3.5-turbo), and give it a prompt like: "Generate a Google-style docstring for the following Python function:"
Here's an example of one of my offenders, simplified for the article (the original uses Python 3.9+ type hints):
python
import re
def normalize_string_for_search(text: str, lower_case: bool = True, strip_punct: bool = True) -> str:
if lower_case:
text = text.lower()
if strip_punct:
text = re.sub(r'[^\w\s]', '', text)
return text
And gpt-3.5-turbo's typical output for that with a simple prompt:
python
"""
Normalizes a string for search purposes.
Args:
text (str): The input string to normalize.
lower_case (bool, optional): Whether to convert the string to lowercase. Defaults to True.
strip_punct (bool, optional): Whether to strip punctuation from the string. Defaults to True.
Returns:
str: The normalized string.
"""
See? It's okay. It's not wrong. But it's also not particularly useful. "Normalizes a string for search purposes" is literally just a rephrase of the function name. It doesn't tell me how it normalizes beyond the lower_case and strip_punct parameters. What kind of punctuation? What's the output if I pass "Hello, World!" with defaults? This was generic fluff. It offered no real insight, no usage examples, no edge cases. It was the docstring equivalent of saying "this function processes data." Thanks, captain obvious.
I spent a good four hours over a couple of evenings trying different permutations. "Be more specific!" "Include examples!" "Think like a human!" The results ranged from slightly better to equally bland. I even tried gpt-4 with the same basic prompt, hoping brute force intelligence would solve it. While it was a little smarter, it still struggled to infer the intent and impact of the function beyond its surface-level code. My productivity was actually negative at this point. I was generating more code (the docstrings) that required more manual review and correction than if I'd just written them myself from scratch.
What Actually Fixed It
The breakthrough came when I realized I was treating the AI like a magical black box that could infer context. It can't. Not entirely. It needs to be told what to look for and what purpose the docstring serves. It needs examples, not just instructions.
My winning strategy revolved around providing the AI with a richer context, effectively treating it like a junior developer who needs to understand the why and how, not just the what.
Here's the prompt structure that finally started yielding genuinely useful docstrings, using OpenAI's gpt-4-0125-preview model via their API (version 2024-05-15):
markdown
You are an expert Python developer tasked with writing comprehensive Google-style docstrings.
The docstring should explain what the function does, its parameters, return values, and include at least two clear, distinct usage examples demonstrating its behavior, including edge cases if applicable.
Here is the Python function:
python
import re
def normalize_string_for_search(text: str, lower_case: bool = True, strip_punct: bool = True) -> str:
if lower_case:
text = text.lower()
if strip_punct:
text = re.sub(r'[^\w\s]', '', text)
return text
Examples of expected behavior:
- Input: text="Hello, World!", lower_case=True, strip_punct=True -> Output: "hello world"
- Input: text="AI & ML rocks!", lower_case=False, strip_punct=True -> Output: "AI ML rocks"
- Input: text="What's up, Doc?", lower_case=True, strip_punct=False -> Output: "what's up, doc?"
Now, generate the complete Google-style docstring for the function above, incorporating these examples.
The difference was night and day. By giving it concrete examples of inputs and their expected outputs, I was essentially teaching the model the function's behavior in a way it could truly understand. It's like unit tests for documentation. The generated docstrings immediately jumped from maybe 30% useful to about 85% useful. I still needed to tweak them occasionally, especially for very complex functions, but the heavy lifting was done.
Here's what it produced for normalize_string_for_search with that detailed prompt:
python
"""
Normalizes a string for search queries by optionally converting it to lowercase and stripping punctuation.
Args:
text (str): The input string to be normalized.
lower_case (bool, optional): If True, converts the string to lowercase. Defaults to True.
strip_punct (bool, optional): If True, removes all punctuation characters (non-alphanumeric, non-whitespace). Defaults to True.
Returns:
str: The normalized string suitable for search.
Examples:
>>> normalize_string_for_search("Hello, World!", lower_case=True, strip_punct=True)
'hello world'
>>> normalize_string_for_search("AI & ML rocks!", lower_case=False, strip_punct=True)
'AI ML rocks'
>>> normalize_string_for_search("What's up, Doc?", lower_case=True, strip_punct=False)
"what's up, doc?"
"""
Much better! The description is more precise ("non-alphanumeric, non-whitespace"). It now includes actual >>> examples, which are invaluable for quickly grasping usage. It understood the "search purposes" context because I told it.
Reflections
The big takeaway for me? AI isn't a magic documentation generator. It's a powerful tool, but it's only as good as the instructions and context you give it. For truly useful docstrings, especially for tricky utility functions, you can't just throw the code at it and expect brilliance. You have to guide it, provide concrete examples of what success looks like, and define your expectations clearly.
It took some extra effort upfront to figure out the right prompting strategy, but now, I can process a batch of undocumented functions relatively quickly. I just write a few key examples, feed them to the AI with the function, and get a solid first draft. My messy utils.py is finally getting the love it deserves, and "future me" is already sending thank-you notes.
This approach has dramatically boosted my "docstring completion rate" for these older functions. It's not full automation, but it's a super effective co-pilot.
Top comments (0)