Parser

use declarative_parser.parser module to create custom parsers and arguments.

class Argument(name=None, short=None, optional=True, as_many_as=None, **kwargs)[source]

Defines argument for Parser.

In essence, this is a wrapper for argparse.ArgumentParser.add_argument(), so most options (type, help) which work in standard Python parser will work with Argument too. Additionally, some nice features, like automated naming are available.

Worth to mention that when used with ConstructorParser, type and help will be automatically deduced.

Parameters:
  • name – overrides deduced argument name
  • short – a single letter to be used as a short name (e.g. “c” will enable using “-c”)
  • optional – by default True, provide False to make the argument required
  • as_many_as (Optional[Argument]) – if provided, will check if len() of the produced value is equal to len() of the provided argument
  • **kwargs – other keyword arguments which are supported by argparse.add_argument()
class Parser(parser_name=None, **kwargs)[source]

Parser is a wrapper around Python built-in argparse.ArgumentParser.

Subclass the Parser to create your own parser.

Use help, description and epilog properties to adjust the help screen. By default help and description will be auto-generated using docstring and defined arguments.

Attach custom arguments and sub-parsers by defining class-variables with Argument and Parser instances.

Example:

class TheParser(Parser):
    help = 'This takes only one argument, but it is required'

    arg = Argument(optional=False, help='This is required')

class MyParser(Parser):
    description = 'This should be a longer text'

    my_argument = Argument(type=int, help='some number')
    my_sub_parser = TheParser()

    epilog = 'You can create a footer with this'

# To execute the parser use:

parser = MyParser()

# The commands will usually be `sys.argv[1:]`
commands = '--my_argument 4 my_sub_parser value'.split()

namespace = parser.parse_args(commands)

# `namespace` is a normal `argparse.Namespace`
assert namespace.my_argument == 4
assert namespace.my_sub_parser.arg == 'value'

Implementation details:

To enable behaviour not possible with limited, plain ArgumentParser (e.g. to dynamically attach a sub-parser, or to chain two or more sub-parsers together) the stored actions and sub-parsers are:

  • not attached permanently to the parser,
  • attached in a tricky way to enable desired behaviour,
  • executed directly or in hierarchical order.

Class-variables with parsers will be deep-copied on initialization, so you do not have to worry about re-use of parsers.

Uses kwargs to pre-populate namespace of the Parser.

Parameters:parser_name – a name used for identification of sub-parser
__error_verbosity__

How much details of the errors should be shown?

The higher value, the more debug hints will be displayed.

__parsing_order__

What should be parsed first – arguments of this parser (‘breadth-first’) or arguments and parsers of sup-parsers (‘depth-first’)?

__pull_to_namespace_above__

Makes the parser “translucent” for the end user.

Though parsing methods (as well as validate & produce) are still evaluated, the user won’t be able to see this sub-parser in command-line interface.

This is intended to provide additional logic separation layer & to keep the parsers nicely organized and nested, without forcing the end user to type in prolonged names to localise an argument in a sub-parser of a sub-parser of some other parser.

__skip_if_absent__

Only invoke sub-parser parsing if it was explicitly enlisted

attach_argument(argument, parser=None)[source]

Attach Argument instance to given (or own) argparse.parser.

attach_subparsers()[source]

Only in order to show a nice help, really.

There are some issues when using subparsers added with the built-in add_subparsers for parsing. Instead subparsers are handled in a custom implementation of parse_known_args (which really builds upon the built-in one, just tweaking some places).

bind_argument(argument, name=None)[source]

Bind argument to current instance of Parser.

bind_parser(parser, name)[source]

Bind deep-copy of Parser with this instance (as a sub-parser).

Parameters:
  • parser (Parser) – parser to be bound as a sub-parser (must be already initialized)
  • name – name of the new sub-parser

This method takes care of ‘translucent’ sub-parsers (i.e. parsers which expose their arguments and sub-parsers to namespace above), saving their members to appropriate dicts (lifted_args/parsers).

description

Longer description of the parser.

Description is shown when user narrows down the help to the parser with: ./run.py sub_parser_name -h.

epilog

Use this to append text after the help message

error(message)[source]

Raises SystemExit with status code 2 and shows usage message.

help

A short message, shown as summary on >parent< parser help screen.

Help will be displayed for sub-parsers only.

parse_args(args=None)[source]

Same as parse_known_args() but all arguments must be parsed.

This is an equivalent of argparse.ArgumentParser.parse_args() although it does >not< support namespace keyword argument.

Comparing to parse_known_args(), this method handles help messages nicely (i.e. passes everything to argparse).

Parameters:args (Optional[Sequence[str]]) – strings to parse, default is sys.argv[1:]
parse_known_args(args)[source]

Parse known arguments, like argparse.ArgumentParser.parse_known_args().

Additional features (when compared to argparse implementation) are:
  • ability to handle multiple sub-parsers
  • validation with self.validate (run after parsing)
  • additional post-processing with self.produce (after validation)
produce(unknown_args)[source]

Post-process already parsed namespace.

You can override this method to create a custom objects in the parsed namespace (e.g. if you cannot specify the target class with Argument(type=X), because X depends on two or more arguments).

You can chery-pick the arguments which were not parsed by the current parser (e.g. when some step of parsing depends on provided arguments), but please remember to remove those from unknown_args list.

Remember to operate on the provided list object (do not rebind the name with unknown_args = [], as doing so will have no effect: use unknown_args.remove() instead).

validate(opts)[source]

Perform additional validation, using Argument.validate.

As validation is performed after parsing, all arguments should be already accessible in self.namespace. This enables testing if arguments depending one on another have proper values.

action(method)[source]

Decorator for Action.

Parameters:method – static or class method for use as a callback
create_action(callback, exit_immediately=True)[source]

Factory for argparse.Action, for simple callback execution

dedent_help(text)[source]

Dedent text by four spaces

group_arguments(args, group_names)[source]

Group arguments into given groups + None group for all others