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: str = ...,
) -> 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: str = ...,
) -> 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, if a mistyped long option resembles a known one, include a "did you mean ..." hint in the DocoptExit message.

False
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 str

"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 = False,
    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: str = "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, if a mistyped long option resembles a known one,
            include a "did you mean ..." hint in the ``DocoptExit`` message.
        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 DocoptLanguageError(Diagnostic(summary="doc (the usage message) must not be None").render())
    if complete:
        # On by default (opt out with complete=False): answer a shell completion request from the
        # environment and exit; only a generate_completion script sets it, so a normal run gets None.
        completion_reply = reply_to_completion_request(doc)
        if completion_reply is not None:
            print(completion_reply)
            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)
    try:
        pattern = parse_pattern(formal_tokens(usage), options)
    except RecursionError:
        raise DocoptLanguageError(Diagnostic(summary="the usage pattern nests too deeply to parse").render()) from None
    argv_tokens = Tokens(argv, usage=usage, exit_code=exit_code)
    argv_patterns = parse_argv(argv_tokens, list(options), options_first, negative_numbers, allow_abbrev)
    expand_options_shortcut(pattern, options)
    # POSIX `--` ends option parsing; drop it unless the usage declares a `--`, so it is not a positional.
    if not any(leaf.name == "--" for leaf in pattern.flat(Command)):
        for index, leaf in enumerate(argv_patterns):
            if type(leaf) is Argument and leaf.value == "--":
                del argv_patterns[index]
                break
    _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 match_budget():
            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:
            raise _exit(_coercion_diagnostic(doc, argv, exc), collected=complete_match, left=left) from exc
    if suggest:
        raw_tokens = argv.split() if isinstance(argv, str) else argv
        hint = suggest_option(raw_tokens, options, allow_abbrev)
        if hint is not None:
            unknown, suggestion = hint
            snippets = [_argv_snippet(argv, unknown, "not a known option")]
            declared_at = _span_of(pattern.flat(Option), suggestion)
            if declared_at is not None:  # the suggested option is written in the usage: cross-reference it
                where = Snippet(usage, "in the usage:", [Caret(*declared_at, f"`{suggestion}` is defined here")])
                snippets.append(where)
            diagnostic = Diagnostic(
                summary=f"unknown option `{unknown}`", snippets=snippets, help=f"did you mean `{suggestion}`?"
            )
            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)
        snippets = [_argv_snippet(argv, shown, "not allowed here")]
        usage_span = _span_of(pattern.flat(Option), 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)
    # Score against a freshly-parsed (unfixed) pattern: fix() dedups identical leaves across lines onto
    # one shared span, which would caret the wrong line when a name repeats (e.g. `<y>` in several lines).
    near_miss = nearest_usage_line(parse_pattern(formal_tokens(usage), parse_defaults(doc)), 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)) == 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.

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.

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.

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 a hand-built or inconsistent mapping, or a degenerate grammar where one value is reachable through differently-shaped positions ((<name> | <name> ...), (-a | -b)..., [<name>] <path> <name>).

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)) == 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.

    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.

    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``.

    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 a hand-built or inconsistent mapping,
            or a degenerate grammar where one value is reachable through differently-shaped positions
            (``(<name> | <name> ...)``, ``(-a | -b)...``, ``[<name>] <path> <name>``).
    """
    provided = {name for name in result if result.source(name) is not Source.DEFAULT and _is_present(result[name])}
    candidates: list[list[str]] = []
    for line in _usage_lines(_usage_pattern(doc)):
        tokens: list[str] = []
        _emit(line, result, provided, tokens, {})
        candidates.append(tokens)
    for tokens in candidates:  # generate-and-verify: return the first line whose argv parses back to result
        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)