Skip to content

1.0.0

Choose a tag to compare

@Solganis Solganis released this 12 Jul 18:27
ae957bc

docopt2 is a typed, zero-dependency, maintained successor to docopt. The usage message is the parser: you write the Usage: and Options: text you'd write anyway, and docopt2 parses the command line against it. Every argument vector the original docopt accepts, docopt2 accepts identically, so switching over is a one-line import change. A differential property test against a vendored copy of the original keeps that promise honest.

This is the first public release. It is also, in a specific sense, a release that was promised nine years ago and never shipped.

"That last 1%"

docopt appeared in 2012, was widely loved for turning a help message into a parser, and then went quiet - the last release, 0.6.2, is from 2014. In 2017, on the issue thread "Is docopt maintained?", the original author Vladimir Keleshev laid out exactly what he wanted to finish first:

I really want to go that extra mile, that 1 last percent, before releasing version 1.0.0. I'm really
aiming for 1.0.0 to be the last Docopt version. That last 1% will probably include:

  • Formal grammar,
  • Gorgeous, detailed error-messages, both for argv parsing and docstring parsing,
  • More powerful matching with non-finite automaton, (will handle cases like <args>... <last>),
  • Remove ARG convention in favor of <arg>,
  • Remove "unambiguous partial matching" rule (e.g. --ver matching --version),
  • Handle -- specially even if it is absent from the docstring,
  • Disallow dropping = for options with arguments in docstring (but not when parsing argv).

As you might have noticed, in 99% of cases the above changes do not matter, so you can happily
continue using docopt as it is.

That 1.0.0 never came - the docopt.ml experiment stalled, and docopt stayed at 0.6.2.

docopt2 wasn't built from that list. It was built to fix docopt's rough edges and make it typed and maintained - and only then did the result turn out to line up with his comment, almost point for point. That convergence is the frame for this release: the docopt 1.0 Keleshev sketched, reached by another road - and then some.

The roadmap, delivered

Keleshev's goal docopt2 1.0.0
1. Formal grammar Done. The usage DSL - docstring structure, the Usage: pattern language, the Options: section, argv - is specified and snapshot-tested. See Usage DSL.
2. Gorgeous, detailed errors Done. Every error path - a mismatch, an unknown or mistyped option, a value that won't coerce, a malformed usage - renders in one rustc-style shape, in color on a terminal. See Diagnostics.
3. Powerful matching (<args>... <last>) Done. The matcher is a greedy-first backtracking generator, so patterns the old greedy matcher starved (<src>... <dest>, [<a>] <b>, [-i X...] <out>) now resolve. See Repetition.
4. Remove the ARG convention Declined by default. Erroring on NAME-style positionals helps no one. ARG stays a valid synonym for <arg>. See Positional arguments.
5. Remove partial matching (--ver -> --version) Opt-in. allow_abbrev=False turns off long-option de-abbreviation (argparse's spelling). The default stays on, for compatibility. See allow_abbrev.
6. Handle -- even if absent Done. -- is dropped as the POSIX separator instead of leaking in as a positional value. See End of options.
7. Disallow --opt <x> (no =) Not needed. docopt2 already parses --opt <x> in the usage correctly, so forbidding it would break a form that works. See Options with a value.

Goals 4 and 5 are the breaking ones - the two that would change what existing usages accept. Keleshev himself flagged that "in 99% of cases the above changes do not matter," so docopt2's first commitment is drop-in compatibility: it ships the non-breaking goals, keeps ARG (#4) as a synonym, and makes removing partial matching (#5) an opt-in flag rather than a forced break. You get the 1.0 that was designed, without the migration it would have cost.

Everything docopt never had

docopt handed you a dict of strings and reprinted the whole usage on any mistake.
docopt2 wraps the same parser in a modern toolkit - every piece opt-in, layered around the parser rather than baked into it:

  • Diagnostics that point at the mistake - rustc-style, and in color on a terminal: a caret under the offending token in your argv, cross-referenced to the exact spot in the usage that rejected it, a "did you mean" on a mistyped option, and the closest usage line on a near-miss. This is Keleshev's goal 2, shipped in full - and the thing you notice first.
  • Typed results - pass a dataclass, TypedDict, Cli base class, or pydantic model as schema= and get a typed object back, values coerced to their field types, validated Literal/Enum choices included.
  • Schema codegen - docopt2 stub generates that schema from your usage, in three styles.
  • Subcommand dispatch - Dispatch routes each command path to its own handler.
  • Shell completion - context-aware completion for bash, zsh, fish, and PowerShell, derived from the grammar.
  • Layered configuration - [env: VAR] and [config: key] fallbacks (CLI > env > config > default), with args.source() reporting where each value actually came from.
  • Self-documenting --help - help_style="rich" renders an aligned, colored help screen that shows each value's resolution chain.
  • Usage linter - docopt2 check flags dead defaults and unusable options before they ship.
  • Usage formatter - docopt2 fmt aligns the Options: block, the format half of a lint-and-format pair for your usage text.
  • Compatibility checking - docopt2 compat old new reports the changes that would break callers.
  • Example generation - docopt2 examples samples the argvs your usage accepts, and doubles as a shrinking Hypothesis strategy.
  • Round-trip codec - format_argv is the inverse of docopt: a parsed result back to a canonical argv.

Install

pip install docopt2  # then: from docopt2 import docopt

Start with Getting started. The full documentation is at solganis.github.io/docopt2.

Thanks

To Vladimir Keleshev for docopt, and for the 2017 comment that put into words what its 1.0 should be. docopt2 got there by its own road, but his sketch is the map it turned out to match. And to the docopt-ng contributors, from whom two concrete improvements are ported (long-option spellcheck, and the collected/left parse view on DocoptExit).

Full attribution is in NOTICE.