Skip to content

docopt & results

The primary entry point, the mapping it returns, the inverse format_argv, and the tree-inspection helper. See the Typed results and Round-trip guides.

docopt

docopt(
    doc: str | None,
    argv: list[str] | tuple[str, ...] | str | None = ...,
    help: bool = ...,
    version: object = ...,
    options_first: bool = ...,
    *,
    default_help: bool | None = ...,
    suggest: bool = ...,
    negative_numbers: bool = ...,
    allow_abbrev: bool = ...,
    allow_extra: bool = ...,
    exit_code: int = ...,
    complete: bool = ...,
    schema: type[SchemaT],
    config: Mapping[str, Any] | None = ...,
    help_style: Literal["raw", "rich"] = ...,
) -> SchemaT
docopt(
    doc: str | None,
    argv: list[str] | tuple[str, ...] | str | None = ...,
    help: bool = ...,
    version: object = ...,
    options_first: bool = ...,
    *,
    default_help: bool | None = ...,
    suggest: bool = ...,
    negative_numbers: bool = ...,
    allow_abbrev: bool = ...,
    allow_extra: bool = ...,
    exit_code: int = ...,
    complete: bool = ...,
    schema: None = ...,
    config: Mapping[str, Any] | None = ...,
    help_style: Literal["raw", "rich"] = ...,
) -> Arguments

Parse argv against the command-line interface described in doc.

Parameters:

Name Type Description Default
doc str | None

Description of the command-line interface (the usage message).

required
argv list[str] | tuple[str, ...] | str | None

Argument vector to parse. sys.argv[1:] is used if omitted. A string is split on whitespace.

None
help bool

Set to False to disable automatic help on -h/--help. This is the original docopt name, kept for drop-in compatibility.

True
version object

If truthy, printed when --version appears in argv.

None
options_first bool

Set to True to require options to precede positional arguments.

False
default_help bool | None

Alias for help. When not None it takes precedence.

None
suggest bool

On a failed parse, add a "did you mean ..." hint to the DocoptExit message for a mistyped long option that resembles a known one, and for a mistyped command where the usage accepts the argv once the command meant stands in its place. False leaves these two hints out. A value a schema refuses keeps its own "did you mean" either way.

True
negative_numbers bool

Treat tokens like -3 or -6.28 as positional arguments instead of short-option clusters.

False
allow_abbrev bool

When False, a long option in argv must be written in full. An unambiguous prefix like --ver no longer de-abbreviates to --version.

True
allow_extra bool

When True, leftover argv tokens that the usage cannot place no longer raise. The best partial match is returned and the surplus is exposed on the result's extra list (the parse_known_args idiom). Missing required elements still fail.

False
exit_code int

Process status carried by a DocoptExit from a failed parse. The default, 1, keeps the usage message auto-printing on an uncaught error. Any other value exits with that status (per SystemExit, the message then travels on str(exc)).

1
complete bool

Answer shell completion requests (on by default). docopt inspects the environment for a request from a generate_completion script. When one is present it prints the candidates and exits, otherwise it parses normally. Set to False to opt out, so this call never responds to the completion protocol (the check is one environment lookup).

True
schema type[SchemaT] | None

If given (a dataclass, TypedDict, or pydantic model), the parsed result is bound to it and returned as that type instead of an Arguments mapping.

None
config Mapping[str, Any] | None

A mapping (from a config file you loaded) to resolve [config: key] fallbacks against. Precedence is command-line argument > [env: VAR] > [config: key] > [default: ...]. Left as None, any [config: ...] annotation is inert.

None
help_style Literal['raw', 'rich']

"raw" (the default) prints the usage message verbatim on --help, as docopt does. "rich" renders an aligned, colored help screen that documents each option's value source - its [env: ...]/[config: ...]/[default: ...] chain - and is scoped to the command path already typed (prog sub --help shows only sub).

'raw'

Returns:

Type Description
Arguments | SchemaT

An Arguments mapping of element names to parsed values, or an instance of

Arguments | SchemaT

schema when one is supplied. On the mapping, provided is the set of names given

Arguments | SchemaT

in argv and extra holds surplus tokens kept by allow_extra.

Raises:

Type Description
DocoptLanguageError

The usage message is malformed, or the schema disagrees with it (see bind_schema).

DocoptExit

The user-supplied argv does not match, or a value cannot be coerced to a schema field's declared type.

Example

docopt("Usage: prog <host> <port>", "127.0.0.1 8080") returns an Arguments mapping {"<host>": "127.0.0.1", "<port>": "8080"}. Passing schema=Args (a dataclass with host: str and port: int) instead returns an Args whose port is an int.

Source code in src/docopt2/_core.py
def docopt(
    doc: str | None,
    argv: list[str] | tuple[str, ...] | str | None = None,
    help: bool = True,  # noqa: A002 - original docopt public parameter name; kept for drop-in compatibility
    version: object = None,
    options_first: bool = False,
    *,
    default_help: bool | None = None,
    suggest: bool = True,
    negative_numbers: bool = False,
    allow_abbrev: bool = True,
    allow_extra: bool = False,
    exit_code: int = 1,
    complete: bool = True,
    schema: type[SchemaT] | None = None,
    config: Mapping[str, Any] | None = None,
    help_style: Literal["raw", "rich"] = "raw",
) -> Arguments | SchemaT:
    """Parse ``argv`` against the command-line interface described in ``doc``.

    Args:
        doc: Description of the command-line interface (the usage message).
        argv: Argument vector to parse. ``sys.argv[1:]`` is used if omitted. A string
            is split on whitespace.
        help: Set to False to disable automatic help on ``-h``/``--help``. This is the
            original docopt name, kept for drop-in compatibility.
        version: If truthy, printed when ``--version`` appears in ``argv``.
        options_first: Set to True to require options to precede positional arguments.
        default_help: Alias for ``help``. When not None it takes precedence.
        suggest: On a failed parse, add a "did you mean ..." hint to the ``DocoptExit`` message
            for a mistyped long option that resembles a known one, and for a mistyped command where
            the usage accepts the argv once the command meant stands in its place. False leaves these
            two hints out. A value a schema refuses keeps its own "did you mean" either way.
        negative_numbers: Treat tokens like ``-3`` or ``-6.28`` as positional arguments
            instead of short-option clusters.
        allow_abbrev: When False, a long option in ``argv`` must be written in full. An
            unambiguous prefix like ``--ver`` no longer de-abbreviates to ``--version``.
        allow_extra: When True, leftover ``argv`` tokens that the usage cannot place no longer
            raise. The best partial match is returned and the surplus is exposed on the result's
            ``extra`` list (the ``parse_known_args`` idiom). Missing required elements still fail.
        exit_code: Process status carried by a ``DocoptExit`` from a failed parse. The default,
            1, keeps the usage message auto-printing on an uncaught error. Any other value exits
            with that status (per ``SystemExit``, the message then travels on ``str(exc)``).
        complete: Answer shell completion requests (on by default). docopt inspects the environment
            for a request from a ``generate_completion`` script. When one is present it prints the
            candidates and exits, otherwise it parses normally. Set to False to opt out, so this
            call never responds to the completion protocol (the check is one environment lookup).
        schema: If given (a dataclass, TypedDict, or pydantic model), the parsed result
            is bound to it and returned as that type instead of an ``Arguments`` mapping.
        config: A mapping (from a config file you loaded) to resolve ``[config: key]`` fallbacks
            against. Precedence is command-line argument > ``[env: VAR]`` > ``[config: key]`` >
            ``[default: ...]``. Left as ``None``, any ``[config: ...]`` annotation is inert.
        help_style: ``"raw"`` (the default) prints the usage message verbatim on ``--help``, as docopt
            does. ``"rich"`` renders an aligned, colored help screen that documents each option's value
            source - its ``[env: ...]``/``[config: ...]``/``[default: ...]`` chain - and is scoped to the
            command path already typed (``prog sub --help`` shows only ``sub``).

    Returns:
        An ``Arguments`` mapping of element names to parsed values, or an instance of
        ``schema`` when one is supplied. On the mapping, ``provided`` is the set of names given
        in ``argv`` and ``extra`` holds surplus tokens kept by ``allow_extra``.

    Raises:
        DocoptLanguageError: The usage message is malformed, or the schema disagrees
            with it (see ``bind_schema``).
        DocoptExit: The user-supplied ``argv`` does not match, or a value cannot be
            coerced to a schema field's declared type.

    Example:
        ``docopt("Usage: prog <host> <port>", "127.0.0.1 8080")`` returns an ``Arguments``
        mapping ``{"<host>": "127.0.0.1", "<port>": "8080"}``. Passing ``schema=Args`` (a
        dataclass with ``host: str`` and ``port: int``) instead returns an ``Args`` whose
        ``port`` is an ``int``.
    """
    if doc is None:
        raise language_error(Diagnostic(summary="doc (the usage message) must not be None"))
    if complete and COMPLETION_REQUEST_ENV in os.environ:
        # On by default (opt out with complete=False): answer a shell completion request and exit.
        from docopt2._completion import reply_to_completion_request  # deferred: 0.3 of the 9.1 ms import

        print(reply_to_completion_request(doc, [] if schema is None else [schema]))
        sys.exit()
    show_help = help if default_help is None else default_help
    if help_style not in ("raw", "rich"):
        raise ValueError(f"help_style must be 'raw' or 'rich', not {help_style!r}")
    argv = sys.argv[1:] if argv is None else argv

    usage = single_usage_section(doc)

    def _exit(diagnostic: Diagnostic, **fields: Any) -> DocoptExit:
        """A DocoptExit carrying this call's usage text and exit code (no shared class state)."""
        return DocoptExit(diagnostic=diagnostic, usage=usage, exit_code=exit_code, **fields)

    argument_defaults = parse_argument_defaults(doc)
    options = parse_defaults(doc)
    pattern = parse_pattern(formal_tokens(usage), options)
    # the same five arguments at each of the three reads, spelled out: as one tuple they cost 220 ns more per parse
    try:
        tokens = Tokens(argv, usage=usage, exit_code=exit_code)
        argv_patterns = _argv_leaves(tokens, pattern, options, options_first, negative_numbers, allow_abbrev)
    except DocoptExit:
        # refused while being read: read it again with its places known, and the same refusal carries its caret
        tokens = _placed_tokens(argv, usage, exit_code)
        _argv_leaves(tokens, pattern, options, options_first, negative_numbers, allow_abbrev)
        raise  # an argv that can be read only once is not refused the second time
    expand_options_shortcut(pattern, options)
    _extras(show_help, version, argv_patterns, doc, help_style)
    # Greedy-first: the first outcome is the greedy result, so every argv vanilla accepts is unchanged;
    # if it leaves leaves over, the search continues (bounded) for a fully-consuming match.
    # Defaults are the "nothing matched" result: the whole argv is left over. Overwritten once a match runs.
    left: list[Pattern] = argv_patterns
    collected: list[Pattern] = []
    complete_match: list[Pattern] | None = None
    greedy: MatchOutcome | None = None
    try:
        pattern.fix()  # mutates in place and returns self, so `pattern` is the fixed tree from here on
        with MatchBudget():
            outcome_iter = pattern.matches(argv_patterns, [])
            greedy = next(outcome_iter, None)
            if greedy is not None:
                left, collected = greedy
                if left == []:
                    complete_match = collected
                else:
                    bounded = itertools.islice(outcome_iter, MATCH_LIMIT)
                    complete_match = next((accumulated for remaining, accumulated in bounded if remaining == []), None)
    except _MatchBudgetExceededError:
        # The pattern is too ambiguous to search fully (a malformed usage or an adversarial argv). Stop and
        # keep the greedy partial; complete_match stays None unless the greedy match was already complete, so
        # an unmatched argv is still rejected - the same result as exhausting the cap, in bounded time.
        pass
    except RecursionError:
        raise _exit(Diagnostic(summary="the arguments are too deeply nested to match")) from None
    extra_tokens: list[str] = []
    if complete_match is None and allow_extra and greedy is not None:
        # A prefix matched but not fully: keep it and return the surplus as `extra` instead of failing.
        # A missing required element leaves `greedy is None`, so it still fails - surplus tolerated, not gaps.
        complete_match = collected
        extra_tokens = [token for leaf in left for token in _surplus_tokens(leaf)]
    if complete_match is not None:
        result = Arguments((cast("str", leaf.name), leaf.value) for leaf in [*pattern.flat(), *complete_match])
        result.provided = frozenset(cast("str", leaf.name) for leaf in complete_match)
        result.extra = extra_tokens
        try:
            _apply_fallbacks(result, options, config)
        except _ConfigShapeError as exc:
            raise _exit(_config_shape_diagnostic(doc, exc), collected=complete_match, left=left) from exc
        for name, default in argument_defaults.items():
            if name in result and result[name] is None:
                result[name] = default
        for name in result:
            # _apply_fallbacks already recorded ENV/CONFIG; here CLI wins for anything given on argv,
            # and everything else settles on its literal default.
            if name in result.provided:
                result._sources[name] = Source.CLI
            else:
                result._sources.setdefault(name, Source.DEFAULT)
        if schema is None:
            return result
        try:
            return bind_schema(result, schema)
        except _CoercionError as exc:
            reading = (options_first, negative_numbers, allow_abbrev)
            refused = _coercion_diagnostic(doc, argv, exc, result, reading)
            raise _exit(refused, collected=complete_match, left=left) from exc
    # by where a leaf stands, not by its text: the same text can stand earlier in the line, in a token the usage took
    placed = _placed_tokens(argv, usage, exit_code)
    placed_leaves = _argv_leaves(placed, pattern, options, options_first, negative_numbers, allow_abbrev)
    # the usage as written: fix() makes equal leaves one object, whose span is that of the last one written
    written = parse_pattern(formal_tokens(usage), parse_defaults(doc))
    # an option is cross-referenced in the line the argv came closest to, and failing that where it is first written
    declared_options = [*closest_usage_line(written, argv_patterns).flat(Option), *written.flat(Option)]
    if suggest:
        # the options as the parser read them: a token it took as a value or as a positional is no mistyped option
        typed = [leaf for leaf in argv_patterns if isinstance(leaf, Option) and leaf.long is not None]
        hint = suggest_option([str(leaf.long) for leaf in typed], options, allow_abbrev)
        if hint is not None:
            unknown, suggestion = hint
            mistyped = next(leaf for leaf in typed if leaf.long == unknown)
            carets = _caret_under(mistyped, argv_patterns, placed_leaves, "not a known option")
            in_argv = Snippet(placed.text, "in the arguments:", carets)
            declared_at = _span_of(declared_options, suggestion)
            diagnostic = _mistyped("option", unknown, suggestion, in_argv, usage, declared_at)
            raise _exit(diagnostic, collected=collected, left=left)
        command = _mistyped_command(pattern, argv_patterns)
        if command is not None:
            at, corrected = command
            typo, meant = argv_patterns[at], str(corrected[at].value)
            carets = _caret_under(typo, argv_patterns, placed_leaves, "not a known command")
            in_argv = Snippet(placed.text, "in the arguments:", carets)
            declared_at = _command_written_at(written, corrected, at)
            diagnostic = _mistyped("command", str(typo.value), meant, in_argv, usage, declared_at)
            raise _exit(diagnostic, collected=collected, left=left)
    if left and greedy is not None:
        # A prefix matched, so left[0] is the first token with no place in the usage. Caret it in the
        # argv; if it is an option the usage declares (mutual exclusion, or a non-repeatable option
        # given twice), add a second caret at that declaration - the argv-to-usage cross-reference.
        offending = left[0]
        shown = str(offending.name) if isinstance(offending, Option) else str(offending.value)
        carets = _caret_under(offending, argv_patterns, placed_leaves, "not allowed here")
        snippets = [Snippet(placed.text, "in the arguments:", carets)]
        usage_span = _span_of(declared_options, shown)
        advice: str | None
        if usage_span is not None:
            snippets.append(Snippet(usage, "in the usage:", [Caret(*usage_span, "declared here")]))
            advice = "give it at most once, not with a mutually exclusive option"
        else:
            advice = None
        summary = f"unexpected argument `{shown}`"
        raise _exit(Diagnostic(summary=summary, snippets=snippets, help=advice), collected=collected, left=left)
    near_miss = nearest_usage_line(written, argv_patterns)
    if near_miss is not None:
        name, span, total = near_miss
        snippet = Snippet(usage, "in the usage:", [Caret(*span, "required here")])
        diagnostic = Diagnostic(
            summary=f"missing required `{name}`",
            snippets=[snippet],
            note=f"of {total} usage patterns, your arguments came closest to this one" if total > 1 else None,
        )
        raise _exit(diagnostic, collected=collected, left=left)
    required = required_leaf_names(pattern)
    if required:
        missing = Diagnostic(
            summary="missing or mismatched arguments", note=f"the usage requires: {' '.join(required)}"
        )
        raise _exit(missing, collected=collected, left=left)
    raise _exit(Diagnostic(summary="the arguments do not match the usage"), collected=collected, left=left)

Arguments

Arguments(*args: Any, **kwargs: Any)

Mapping of parsed element names ("--flag", "<arg>", "command") to their values (str | bool | int | list[str] | None). This is the back-compat return type, narrowed by the typed API.

provided is the set of names actually supplied in argv, so a defaulted value is distinguishable from an explicit one. extra holds leftover tokens kept by allow_extra=True.

Source code in src/docopt2/_core.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    super().__init__(*args, **kwargs)
    self.provided: frozenset[str] = frozenset()
    self.extra: list[str] = []
    self._sources: dict[str, Source] = {}

was_given

was_given(name: str) -> bool

Return whether name was supplied in argv (as opposed to left at its default).

Source code in src/docopt2/_core.py
def was_given(self, name: str) -> bool:
    """Return whether ``name`` was supplied in ``argv`` (as opposed to left at its default)."""
    return name in self.provided

source

source(name: str) -> Source

Where name's value was resolved from: the command line, [env:], [config:], or the default.

Answers "why is this value what it is?" for layered configuration. A name never resolved from a fallback reports Source.DEFAULT.

Source code in src/docopt2/_core.py
def source(self, name: str) -> Source:
    """Where ``name``'s value was resolved from: the command line, ``[env:]``, ``[config:]``, or the default.

    Answers "why is this value what it is?" for layered configuration. A name never resolved from a
    fallback reports [`Source.DEFAULT`][docopt2.Source].
    """
    return self._sources.get(name, Source.DEFAULT)

Source

Where a resolved value came from, in the precedence order docopt2 applies (highest first).

format_argv

format_argv(result: Arguments, doc: str) -> list[str]

Synthesize a canonical argv that docopt parses back to result.

This is the inverse of parsing. Given an Arguments mapping returned by docopt(doc, ...), return an argv token list (no program name) that round-trips: docopt(doc, format_argv(result, doc), help=False, complete=False) == result.

The canonical form emits every element that carries a value, in usage order, with options in long --name=value form. That is what the user supplied, plus whatever [env:] or [config:] resolved. It is a valid argv, not necessarily the shortest or the one originally typed. A positional that would read back as an option (-x, --) is written behind a --, with the options in front of it.

An env- or config-sourced value is emitted rather than skipped, so the argv reproduces the result on its own, without that environment. A persisted command that silently depended on an unrecorded variable would not reproduce the run. An off flag and a zero count are the exception: no token says them, so that part of the result is read from its source again.

Two things are omitted: an element left at its [default: ...], and one whose value is absent (an off flag, a zero count). A source other than DEFAULT is not enough on its own, since an [env: V] flag read as off has source ENV and value False, and emitting its name would parse back to True. Where the omission does not parse back (the environment changed since the parse, or the mapping was built by hand and records no sources), the defaults are written out as well.

The round trip is promised for one replay, the one the answer is checked with: help=False and every other setting at its default. So a result that carries --help formats, and its argv parses back only where --help is not acted on. A result that took another setting to produce (options_first=True, negative_numbers=True) is formatted to parse back under the defaults.

Parameters:

Name Type Description Default
result Arguments

An Arguments mapping returned by docopt(doc, ...).

required
doc str

The same usage message that produced result.

required

Returns:

Type Description
list[str]

An argv token list, without the program name. Each candidate usage line is generated and then

list[str]

re-parsed to verify it round-trips, so the output is never a wrong argv, only a valid one.

Raises:

Type Description
ValueError

No usage pattern reproduces result. That means an inconsistent mapping, a degenerate grammar where one value is reachable through differently-shaped positions ((<name> | <name> ...), (-a | -b)..., [<name>] <path> <name>), or a value that only a parser setting put there and no -- can protect: a -5 read as a positional by negative_numbers=True in front of a -- the usage itself declares.

Source code in src/docopt2/_format.py
def format_argv(result: Arguments, doc: str) -> list[str]:
    """Synthesize a canonical argv that [`docopt`][docopt2.docopt] parses back to ``result``.

    This is the inverse of parsing. Given an [`Arguments`][docopt2.Arguments] mapping returned by
    ``docopt(doc, ...)``, return an argv token list (no program name) that round-trips:
    ``docopt(doc, format_argv(result, doc), help=False, complete=False) == result``.

    The canonical form emits every element that *carries* a value, in usage order, with options in long
    ``--name=value`` form. That is what the user supplied, plus whatever ``[env:]`` or ``[config:]`` resolved.
    It is *a* valid argv, not necessarily the shortest or the one originally typed. A positional that would
    read back as an option (``-x``, ``--``) is written behind a ``--``, with the options in front of it.

    An env- or config-sourced value is emitted rather than skipped, so the argv reproduces the result on its
    own, without that environment. A persisted command that silently depended on an unrecorded variable would
    not reproduce the run. An off flag and a zero count are the exception: no token says them, so that part
    of the result is read from its source again.

    Two things are omitted: an element left at its ``[default: ...]``, and one whose value is absent (an off
    flag, a zero count). A source other than ``DEFAULT`` is not enough on its own, since an ``[env: V]`` flag
    read as off has source ``ENV`` and value ``False``, and emitting its name would parse back to ``True``.
    Where the omission does not parse back (the environment changed since the parse, or the mapping was built
    by hand and records no sources), the defaults are written out as well.

    The round trip is promised for one replay, the one the answer is checked with: ``help=False`` and
    every other setting at its default. So a result that carries ``--help`` formats, and its argv parses
    back only where ``--help`` is not acted on. A result that took another setting to produce
    (``options_first=True``, ``negative_numbers=True``) is formatted to parse back under the defaults.

    Args:
        result: An [`Arguments`][docopt2.Arguments] mapping returned by ``docopt(doc, ...)``.
        doc: The same usage message that produced ``result``.

    Returns:
        An argv token list, without the program name. Each candidate usage line is generated and then
        re-parsed to verify it round-trips, so the output is never a *wrong* argv, only a valid one.

    Raises:
        ValueError: No usage pattern reproduces ``result``. That means an inconsistent mapping,
            a degenerate grammar where one value is reachable through differently-shaped positions
            (``(<name> | <name> ...)``, ``(-a | -b)...``, ``[<name>] <path> <name>``), or a value that only a
            parser setting put there and no ``--`` can protect: a ``-5`` read as a positional by
            ``negative_numbers=True`` in front of a ``--`` the usage itself declares.
    """
    carried = {name for name in result if _is_present(result[name])}
    provided = {name for name in carried if result.source(name) is not Source.DEFAULT}
    for written in [provided] if provided == carried else [provided, carried]:
        for tokens in _candidates(doc, result, written):  # generate-and-verify: the first that parses back
            if _round_trips(doc, tokens, result):
                return tokens
    raise ValueError("cannot format: the result matches no usage pattern in the doc")

parse_tree

parse_tree(doc: str) -> Pattern

Build the usage-pattern Pattern tree for doc without matching argv - repr() it, walk it, or serialize with Pattern.to_dict to diff how a change affects parsing. The [options] shortcut stays an OptionsShortcut node rather than expanded.

Source code in src/docopt2/_core.py
def parse_tree(doc: str) -> Pattern:
    """Build the usage-pattern [`Pattern`][docopt2.Pattern] tree for ``doc`` without matching argv - ``repr()`` it,
    walk it, or serialize with [`Pattern.to_dict`][docopt2.Pattern.to_dict] to diff how a change affects parsing. The
    ``[options]`` shortcut stays an [`OptionsShortcut`][docopt2.OptionsShortcut] node rather than expanded."""
    options = parse_defaults(doc)
    return parse_pattern(formal_usage(single_usage_section(doc)), options)