Document Python Code with ChatGPT and Verify the Contract

AI Published Updated 4 mins read By Leon Wei
Document Python Code with ChatGPT and Verify the Contract cover image

Quick summary

Summarize this blog with AI

Ask ChatGPT to draft documentation only after supplying the actual code and the behavior you want documented. Then check every claim about accepted inputs, returned values, errors and side effects. Generated prose can sound precise while describing a different function. This lesson uses one complete Python 3.12.14 function, a deliberately inaccurate illustrative draft, and tests that settle the disagreement.

No model call was executed for this article. The prompts and generated-looking prose are teaching examples. The function and tests were executed locally, including doctest examples.

Read the implementation before describing it

def order_total(lines):
    total = 0
    for quantity, unit_cents in lines:
        if type(quantity) is not int or type(unit_cents) is not int:
            raise TypeError("quantity and unit_cents must be integers")
        if quantity < 1 or unit_cents < 0:
            raise ValueError("quantity must be positive and unit_cents nonnegative")
        total += quantity * unit_cents
    return total

print(order_total([(2, 1200), (1, 800)]))
print(order_total([]))

Expected output:

3200
0

The function expects an iterable of two-element rows, uses integer cents, and performs no rounding. It returns zero for no rows. It rejects Boolean fields because the checks require the exact built-in int type. It iterates the input without assigning to its rows, but a supplied iterator is consumed. Malformed row lengths fail during unpacking; that error should not be relabeled as an invalid price.

Give a narrow documentation request

Draft a docstring for this exact function without changing the code. Describe the two row fields, units, return type, empty input, expected errors, and input consumption. Do not invent supported types or side effects. List any assumption you cannot establish from the implementation separately.

Provide only code and data you are authorized to share. If a business rule is missing from the code, decide whether to change the implementation or document its current boundary. A docstring cannot enforce a new rule on its own.

Reject a plausible draft that promises unsupported behavior

Illustrative incorrect draft: “Calculate a floating-point dollar total from quantities and prices. Rounds each line to two decimals, ignores invalid rows, and leaves any input iterator reusable.”

Every sentence is wrong for this implementation: the output is integer cents, no rounding occurs, invalid rows raise, and iteration consumes an iterator. A generic “looks good” review would miss these mismatches. Ask the model to identify the exact code supporting each claim, then remove claims it cannot support.

Write a corrected docstring with executable examples

order_total.__doc__ = """Return the total integer cents for an iterable of order lines.

Each row must unpack into (quantity, unit_cents). Both fields must be
built-in int values (not bool); quantity is positive and unit_cents is
nonnegative. Empty input returns 0. No currency conversion or rounding
is performed. The function iterates input once and does not modify rows;
a supplied iterator is consumed.

Raises TypeError for unsupported field types and ValueError for invalid
numeric ranges. Malformed rows can also raise unpacking errors.

>>> order_total([(2, 1200), (1, 800)])
3200
>>> order_total([])
0
"""
import doctest
doctest.run_docstring_examples(order_total, globals(), name="order_total")
print("docstring examples passed")

Expected output:

docstring examples passed

The assignment makes the example easy to run after the first block. In a source file, put the same triple-quoted text as the function’s first statement. Use comments to explain a surprising decision, such as excluding bool from integer money fields; do not narrate every arithmetic operator. Include a currency assumption in the surrounding application contract because “cents” alone does not identify a currency.

Test claims that examples alone would miss

lines = [(2, 1200), (1, 800)]
before = lines.copy()
assert order_total(lines) == 3200 and lines == before
assert order_total([(1, 0)]) == 0
for row, error_type in [((True, 100), TypeError), ((1, 1.5), TypeError),
                        ((0, 100), ValueError), ((1, -1), ValueError)]:
    try:
        order_total([row])
    except error_type:
        pass
    else:
        raise AssertionError("documented rejection missing")
iterator = iter([(1, 250)])
assert order_total(iterator) == 250
assert list(iterator) == []
print("values, errors, unchanged list, and consumed iterator verified")

Expected output:

values, errors, unchanged list, and consumed iterator verified

These tests verify the stated contract; they do not prove every possible custom iterable behavior. A generator may itself raise during iteration, and that exception propagates. If this function becomes an external input boundary, add explicit row-shape and resource limits under a separately reviewed policy.

Review documentation whenever behavior changes. Keep one authoritative docstring with linked higher-level usage examples, rather than copying slightly different claims into several files. Record the code version and tests used for the review. OpenAI’s guidance recommends explicit task context and evaluation; the implementation and observed tests remain the evidence for this function’s behavior.

References and next steps

Official ChatGPT prompting guide, OpenAI prompt engineering, Python docstring conventions, doctest.

Interview Prep

Begin Your SQL, Python, and R Journey

Master 230 interview-style coding questions and build the data skills needed for analyst, scientist, and engineering roles.

Related Articles

All Articles