Usage DSL¶
docopt2 reads the same usage DSL as docopt. The Usage: and Options: blocks in your help message are
the parser spec.
Nothing is configured in code. You write the interface the way you would document it, and docopt2 derives the parser from that text.
This guide is the full reference: every form, the value it produces, and how to inspect the parse tree.
Every result below is real docopt() output for the usage and argv above it.
The two blocks¶
A help message has two blocks docopt2 reads, both found case-insensitively:
Usage:- one or more invocation patterns. Each line until the next blank line is an alternative way to call the program. The leading word is the program name and is ignored when matching.Options:- one line per option, giving its short and/or long form, whether it takes a value, and an optional[default: ...].
Naval Fate.
Usage:
naval ship new <name>...
naval ship <name> move <x> <y> [--speed=<kn>]
naval --help
Options:
--speed=<kn> Speed in knots [default: 10].
Elements¶
A quick reference, then a worked example of each form.
| Form | Meaning |
|---|---|
command |
A literal (sub)command, matched as-is |
<arg>, ARG |
A positional argument |
-o, --option |
An option (flag) |
--option=<val> |
An option that takes a value |
[ ] |
Optional |
( ) |
Required group |
a \| b |
Mutually exclusive: pick one |
element... |
Repeatable: one or more |
[options] |
Every option from the Options: block |
-- |
Ends option parsing. The rest is positional |
[default: <val>] |
An option's default, declared in Options: |
[env: <var>] |
An option's environment-variable fallback, resolved after the command line |
[config: <key>] |
An option's config-file fallback, resolved against the docopt(config=...) mapping |
Commands¶
A bare word is a literal command, matched verbatim. Its key in the result is True when present and
False otherwise. Chaining words builds a command path.
docopt("Usage: prog ship new <name>", "ship new titanic", complete=False)
# {'ship': True, 'new': True, '<name>': 'titanic'}
Positional arguments¶
<name>, or the uppercase NAME form. The value is the raw string. An optional positional that is
absent is None.
docopt("Usage: prog <name>", "alice", complete=False) # {'<name>': 'alice'}
docopt("Usage: prog [<name>]", "", complete=False) # {'<name>': None}
Options (flags)¶
-v or --verbose. Present is True, absent is False. Short flags stack, so -abc is -a -b -c.
docopt("Usage: prog [-v]", "-v", complete=False) # {'-v': True}
docopt("Usage: prog [-v]", "", complete=False) # {'-v': False}
docopt("Usage: prog [-a] [-b] [-c]", "-abc", complete=False)
# {'-a': True, '-b': True, '-c': True}
A repeatable flag counts instead of toggling:
Options with a value¶
--port=<n> takes an argument. On the command line both --port=8080 and --port 8080 are accepted, and
the value is always a string.
docopt("Usage: prog --port=<n>", "--port=8080", complete=False) # {'--port': '8080'}
docopt("Usage: prog --port=<n>", "--port 8080", complete=False) # {'--port': '8080'}
Defaults¶
A [default: ...] in the Options: block supplies the value when the option is omitted.
doc = "Usage: prog [--port=<n>]\n\nOptions:\n --port=<n> Port [default: 80]."
docopt(doc, "", complete=False) # {'--port': '80'}
Note
[default: ...] applies only to options declared in the Options: block. A variadic positional
that is absent defaults to an empty list, any other absent optional is None, and an absent flag is
False.
Environment and config fallback¶
Two annotations declare where an omitted option's value comes from, layered under [default: ...]:
[env: VAR]reads an environment variable.[config: dotted.key]reads the mapping you pass asdocopt(config=...)- a config file you loaded.
The precedence is command-line argument > [env: VAR] > [config: key] > [default: ...]. An explicit
argument always wins, and the default is the last resort.
So below, 8080 comes from the config, 7000 from APP_PORT once it is set, and 9000 from the explicit
--port:
import os
from docopt2 import docopt
doc = """Usage: prog [--port=<n>]
Options:
--port=<n> Port [default: 80] [env: APP_PORT] [config: server.port]."""
cfg = {"server": {"port": 8080}}
os.environ.pop("APP_PORT", None)
docopt(doc, "", config=cfg, complete=False) # {'--port': '8080'}
os.environ["APP_PORT"] = "7000"
docopt(doc, "", config=cfg, complete=False) # {'--port': '7000'}
docopt(doc, "--port=9000", config=cfg, complete=False) # {'--port': '9000'}
docopt2 never reads a file itself. That would add a dependency and lock in a format.
You load the config however you like (TOML, JSON, a [tool.<prog>] table) and pass the resulting mapping.
[config: a.b.c] walks the dotted path into it.
On a flag the value is read as a boolean: set, unless it reads as empty, 0, false, no, or off.
Both fallbacks are opt-in per option, apply only to options that appear in the usage, and coerce through a schema like any other value.
Where a value came from stays visible:
was_givenreports an env- or config-sourced value as not given, since it did not come from the command line.args.source(name)reports which layer actually supplied it.- The rich
--helpscreen documents the same source chain, so users see where a value resolves from without reading the code.
Empty is treated as absent
A blank or unset source falls through to the next layer - the shell ${VAR:-default} convention - so
an empty APP_PORT= never silently overrides the config or the default with an empty string. The same
holds for a null or empty config value.
A config key must name a value, not a table
An option takes one value, so [config: server] against {"server": {"port": 80}} is a mistake. The
key stops one level short.
docopt2 says so, with a caret and the keys that would have worked, instead of handing --server the
string {'port': 80}. Scalars, dates and times pass through as written.
Generate a config skeleton¶
Once options declare [config: key], generate_config_template turns
those annotations into a ready-to-fill TOML file. It is the mirror image of the resolution above: the file
that the docopt(config=...) mapping is loaded from.
Each key lands under its table, seeded with the option's [default: ...], and commented with the flag and
any [env: VAR] it also reads.
from docopt2 import generate_config_template
doc = """Usage: prog [options]
Options:
--host=<h> Bind address [default: 0.0.0.0] [config: server.host].
--port=<n> Port [default: 8080] [env: APP_PORT] [config: server.port].
--verbose Log verbosely [config: logging.verbose]."""
print(generate_config_template(doc))
# [server]
# host = "0.0.0.0" # --host
# port = 8080 # --port, env APP_PORT
#
# [logging]
# verbose = false # --verbose
Options without a [config:] key are left out, and the output is valid TOML - integers, floats, and
booleans stay bare, everything else is quoted - so it round-trips straight back through tomllib.
Contradictory keys fail loudly
Config keys that cannot coexist in one TOML document raise DocoptLanguageError rather than emit a
broken file:
- a duplicate key
- a path that is a prefix of another.
[config: server]beside[config: server.port]would useserveras both a value and a table.
Sibling keys under one table (server.host and server.port) are fine.
The docopt2 config-template <source> CLI prints the same skeleton from a .py module docstring, a
usage file, or - for standard input:
Optional [ ] and required ( )¶
[ ] wraps elements that may be omitted. ( ) groups elements that must all be present, most often to
scope an alternation so the | binds where you intend.
Mutually exclusive |¶
Inside a group, | offers a choice and exactly one branch matches. Branches that were not taken still
appear in the result, as False.
docopt("Usage: prog (add|rm) <x>", "add foo", complete=False)
# {'add': True, 'rm': False, '<x>': 'foo'}
Repetition ...¶
A trailing ... means one or more. A repeated positional collects into a list, and a repeated flag counts.
The [options] shortcut¶
[options] stands in for every option described in the Options: block, so a pattern does not have to
list them all.
doc = "Usage: prog [options] <f>\n\nOptions:\n -v Verbose.\n --port=<n> Port."
docopt(doc, "-v --port=9 file", complete=False)
# {'-v': True, '--port': '9', '<f>': 'file'}
End of options --¶
A literal -- on the command line stops option parsing. Everything after it is positional, even if it
looks like an option. Allow it in a pattern with [--].
docopt("Usage: prog [--] <args>...", "-- -x -y", complete=False)
# {'--': True, '<args>': ['-x', '-y']}
What each form produces¶
The value type is fixed by the form, which is exactly what the typed schema keys off:
| Form | Present | Absent |
|---|---|---|
flag -v |
True |
False |
repeatable flag -v... |
count (int) |
0 |
command cmd |
True |
False |
positional <x> |
str |
None |
variadic positional <x>... |
list[str] |
[] |
option --opt=<v> |
str |
the [default:], otherwise None |
Those are the docopt-native forms: a flag or command answers with a boolean (a count, once it repeats), and anything that carries a value answers with a string, or a list of them. Coercion to your own field types happens when you pass a schema.
Multiple usage patterns¶
Each line under Usage: is a full alternative. docopt2 matches the argument vector against the set of
patterns and accepts the first that fits. If none fit, it raises the diagnostic that
points at where the argv diverged from the usage.
Inspecting the parse tree¶
parse_tree(doc) builds the usage-pattern tree without matching any argv, so you can repr() it, walk
it, or diff how a grammar change reshapes parsing. The [options] shortcut stays an OptionsShortcut
node rather than being expanded.
from docopt2 import parse_tree
parse_tree("Usage: prog <host> <port>")
# Required(Required(Argument('<host>', None), Argument('<port>', None)))
Parser primitives¶
The pattern node classes (Pattern, Argument, Command, Option, Required,
Optional, Either, OneOrMore, OptionsShortcut) and the low-level parser functions are exported,
because tools built on docopt reach into them.
They are the parser's own vocabulary, not a promise. The drop-in guarantee covers docopt() and the mapping
it returns, nothing below it. These names also follow docopt's unreleased master rather than the 0.6.2
spelling (TokenStream, ChildPattern, ParentPattern, AnyOptions).
For new code, prefer docopt() with a schema.
See also¶
- Usage linting - validate a usage message statically before it ships.
- Typed results - map the DSL onto a dataclass,
TypedDict,Cli, or pydantic model. - Usage grammar - the pattern node classes in the API reference.