diff --git a/.gitignore b/.gitignore index c23ab75..80095ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +**/__pycache__/ doc/conf.shelve.db docs/ # pixi environments diff --git a/README.md b/README.md index 49b9ddb..44534f9 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,40 @@ options: -d, --debug debug mode ``` +### Customizing the underlying `argparse.ArgumentParser` + +It is possible to pass argument `parser_config` to `call`, which then gets +passed on to the underlying `argparse.ArgumentParser` as keyword arguments. +This allows for advanced control of behavior, such as using a custom +help formatter. + +For example, you may want to add a line break in the help string for +a parameter, to make it more readable in case it's long. In such case +`plac` will automatically remove these line breaks by default, whether +you create them using escape newline characters (`\n`) or using +a multi-line string literal. To preserve line breaks in argument +help strings, you can use a custom `HelpFormatter` by passing a parser +config containing the `formatter_class` key to `plac.call()`: + +```python +class MyCustomFormatter(argparse.HelpFormatter): + ... + +if __name__ == '__main__': + plac.call( + main, + parser_config=dict( + formatter_class=MyCustomFormatter + ) + ) +``` + +If you wish to preserve the help strings verbatim, you can use +`argparse.RawTextHelpFormatter` as the `formatter_class`; however, +there are some caveats, e.g. it does not de-dent the text like +the standard formatter does (although this can be easily accomplished +using `textwrap.dedent`). + ## Decorator reference To use `plac` all you need to know are the following three decorators: diff --git a/plac_core.py b/plac_core.py index fe8b21b..893a6dd 100644 --- a/plac_core.py +++ b/plac_core.py @@ -419,17 +419,19 @@ def iterable(obj): return hasattr(obj, '__iter__') and not inspect.isclass(obj) and not isinstance(obj, (str, bytes)) -def call(obj, arglist=None, eager=True, version=None): +def call(obj, arglist=None, eager=True, version=None, parser_config={}): """ If obj is a function or a bound method, parse the given arglist by using the parser inferred from the annotations of obj and call obj with the parsed arguments. If obj is an object with attribute .commands, dispatch to the associated subparser. + + :param parser_config: dict of keyword arguments passed to ArgumentParser """ if arglist is None: arglist = sys.argv[1:] - parser = parser_from(obj) + parser = parser_from(obj, **parser_config) if version: parser.add_argument( '--version', '-v', action='version', version=version)