Skip to content

Example generation

Sample argument vectors from the usage grammar: concrete invocations your usage accepts, as data you can diff, replay, or fuzz against. See the Example generation guide.

generate_examples

generate_examples(
    doc: str,
    *,
    count: int = 10,
    valid: bool = True,
    seed: int | None = None,
) -> list[list[str]]

Generate example argument vectors derived from the usage message.

Each returned item is an argv token list (no program name). Everything is derived from the Usage: and Options: blocks, so the examples cannot drift from what docopt parses.

Parameters:

Name Type Description Default
doc str

The usage message (the same string given to docopt).

required
count int

How many examples to return. Duplicates are dropped, so a small grammar (or one where few argvs can be made invalid) may yield fewer.

10
valid bool

When True, each example is one docopt accepts. When False, each is one docopt rejects, verified against the parser before it is returned rather than assumed.

True
seed int | None

Fixes the sampler, making the output reproducible.

None
Source code in src/docopt2/_generate.py
def generate_examples(doc: str, *, count: int = 10, valid: bool = True, seed: int | None = None) -> list[list[str]]:
    """Generate example argument vectors derived from the usage message.

    Each returned item is an argv token list (no program name). Everything is derived from the ``Usage:`` and
    ``Options:`` blocks, so the examples cannot drift from what [`docopt`][docopt2.docopt] parses.

    Args:
        doc: The usage message (the same string given to [`docopt`][docopt2.docopt]).
        count: How many examples to return. Duplicates are dropped, so a small grammar (or one where
            few argvs can be made invalid) may yield fewer.
        valid: When True, each example is one ``docopt`` accepts. When False, each is one ``docopt``
            rejects, verified against the parser before it is returned rather than assumed.
        seed: Fixes the sampler, making the output reproducible.
    """
    single_usage_section(doc)  # fail loudly here on a malformed usage, not mid-walk
    pattern = _usage_pattern(doc)
    chooser = _RandomChooser(random.Random(seed))
    counter = itertools.count(1)
    unknown = _unknown_option(doc) if not valid else ""
    seen: set[tuple[str, ...]] = set()
    examples: list[list[str]] = []
    for _ in range(max(count, 0) * 20):
        if len(examples) >= count:
            break
        argv = _sample(pattern, chooser, counter)
        if not valid:
            argv.append(unknown)
        key = tuple(argv)
        if key in seen:
            continue
        seen.add(key)
        if not valid and not _is_rejected(doc, argv):
            continue  # the appended option did not make it invalid; this argv is not an example of a refusal
        examples.append(argv)
    return examples

Hypothesis strategy

argv_strategy lives in docopt2.hypothesis and needs the optional docopt2[hypothesis] extra.

argv_strategy

argv_strategy(doc: str) -> SearchStrategy[list[str]]

A Hypothesis strategy of argument vectors the usage message accepts.

Every drawn argv is one the usage accepts, so you can property-test a program against its own usage without hand-writing inputs. Map the strategy through docopt to drive downstream code with parsed results::

from hypothesis import given
from docopt2 import docopt
from docopt2.hypothesis import argv_strategy

@given(argv_strategy(DOC))
def test_never_crashes(argv):
    run(docopt(DOC, argv, help=False))

Pass help=False. A usage that declares -h/--help (the canonical naval-fate example does) can draw exactly that argv, and docopt answers it by printing the doc and exiting the process - which inside a property test is a SystemExit, not a parse. The same holds for version=.

Requires the docopt2[hypothesis] extra. Raises DocoptLanguageError at once on a malformed usage, before any example is drawn.

Source code in src/docopt2/hypothesis.py
def argv_strategy(doc: str) -> SearchStrategy[list[str]]:
    """A Hypothesis strategy of argument vectors the usage message accepts.

    Every drawn argv is one the usage accepts, so you can property-test a program against its own usage
    without hand-writing inputs. Map the strategy through ``docopt`` to drive downstream code with parsed
    results::

        from hypothesis import given
        from docopt2 import docopt
        from docopt2.hypothesis import argv_strategy

        @given(argv_strategy(DOC))
        def test_never_crashes(argv):
            run(docopt(DOC, argv, help=False))

    Pass ``help=False``. A usage that declares ``-h``/``--help`` (the canonical naval-fate example does)
    can draw exactly that argv, and ``docopt`` answers it by printing the doc and exiting the process -
    which inside a property test is a ``SystemExit``, not a parse. The same holds for ``version=``.

    Requires the ``docopt2[hypothesis]`` extra. Raises [`DocoptLanguageError`][docopt2.DocoptLanguageError] at once
    on a malformed usage, before any example is drawn.
    """
    pattern = _usage_pattern(doc)  # eager, so a malformed usage fails here, not mid-draw

    @st.composite
    def _build(draw: DrawFn) -> list[str]:
        return _sample(pattern, _DrawChooser(draw), itertools.count(1))

    return _build()