# Copyright 2022 Sean Robertson
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import argparse
import abc
import sys
from typing import List, Optional, Sequence, TextIO, Tuple, Type, Union, Collection
from xml.etree.ElementInclude import include
try:
from typing import Literal
except ImportError:
from typing_extensions import Literal
import param
from ._classic_serialization import (
deserialize_from_ini,
deserialize_from_json,
deserialize_from_yaml,
serialize_to_ini,
serialize_to_json,
serialize_to_yaml,
)
from ._file_serialization import yaml_is_available
from . import config
[docs]
class ParameterizedFileReadAction(argparse.Action, metaclass=abc.ABCMeta):
"""Base class for deserializing files into a Parameterized object
Subclasses of this class can be added as the 'action' keyword to an
:func:`argparse.ArgumentParser.add_argument` call. The action will read the file
path passed as an argument via command line and use the contents of the file to
populate :class:`param.parameterized.Parameterized` instances. The subclass
deserializes the contents of the file according to the
:func:`pydrobert.param.serialization.deserialize_from_filetype` function, where
``filetype`` is replaced with the subclass' file type.
There are three ways to specify parameterized objects to populate. They are mutually
exclusive:
1. Set the keyword `type` with the subclass of
:class:`param.parameterized.Parameterized` you want to deserialize into. A new
instance of that subclass will be created with the name ``type.__name__``. The
instance will be returned in the parsed namespace's attribute whose name matches
`dest` as well.
2. Set the keyword `parameterized` with an instance of
:class:`param.parameterized.Parameterized`. That instance will be populated and
also returned in the parsed namespace's attribute whose name matches `dest`.
3. Set the keyword `parameterized` as a hierarchical dictionary of
:class:`param.parameterized.Parameterized` instances. The leaves of the
dictionary will be populated according to the "hierarchical mode" specified in
the documentation of :func:`pydrobert.param.serialization.deserialize_from_dict`.
The same dictionary will be returned in the parsed namespace's attribute whose
name matches `dest`.
Parameters
----------
option_strings
A list of command-line option strings which should be associated with this
action.
dest
The name of the attribute to hold the created object.
parameterized
type
deserializer_name_dict
Use specific deserializers for parameters with specific names.
deserializer_type_dict
Use specific deserializers for parameters with exactly matching types.
on_missing
What to do if the parameterized instance does not have a parameter listed in the
config file.
required
:obj:`True` if the action must always be specified at the command line. This is
only meaningful for optional command-line arguments.
help
The help string describing the argument.
metavar
The name to be used for the option's argument with the help string. If
:obj:`None`, the `dest` value will be used as the name.
nargs
The number of command line arguments to be consumed. When more than one argument
is specified, each will be deserialized in the order that they were presented on
the command line. Thus, later config values will clobber earlier ones
const
If `nargs` is :obj:`'?'` but the flag is present on the command line, this value
will be supplied as the missing argument
See Also
--------
pydrobert.param.serialization.deserialize_from_dict
For more information on how values are deserialized from the config
"""
parameterized: Union[param.Parameterized, dict]
deserializer_name_dict: Optional[dict]
deserializer_type_dict: Optional[dict]
on_missing: Literal["ignore", "warn", "raise"]
def __init__(
self,
option_strings: List[str],
dest: str,
parameterized: Optional[Union[param.Parameterized, dict]] = None,
type: Optional[Type[param.Parameterized]] = None,
deserializer_name_dict: Optional[dict] = None,
deserializer_type_dict: Optional[dict] = None,
on_missing: Literal["ignore", "warn", "raise"] = "warn",
required: bool = False,
help: Optional[str] = None,
metavar: Optional[str] = None,
nargs: Optional[Union[str, int]] = None,
const: Optional[str] = None,
):
if parameterized is None and type is None:
raise TypeError("one of parameterized or type must be set")
if parameterized is not None and type is not None:
raise TypeError("only one of parameterized or type can be set")
if parameterized is None:
self.parameterized = type(name=type.__name__)
else:
self.parameterized = parameterized
self.deserializer_name_dict = deserializer_name_dict
self.deserializer_type_dict = deserializer_type_dict
self.on_missing = on_missing
super(ParameterizedFileReadAction, self).__init__(
option_strings,
dest,
type=argparse.FileType("r"),
default=self.parameterized,
required=required,
help=help,
metavar=metavar,
nargs=nargs,
const=const,
)
[docs]
@abc.abstractmethod
def deserialize(self, fp: Union[TextIO, str]) -> None:
"""Read the file pointer into parameterized objects
Called during the callable section of this class. Implemented by
subclasses.
"""
raise NotImplementedError()
def __call__(
self,
parser: argparse.ArgumentParser,
namespace: argparse.Namespace,
values,
option_string: Optional[str] = None,
) -> None:
# the latter occurs with, for example, nargs='*' and empty args
if values is None or values == self.parameterized:
return
if not isinstance(values, list):
values = [values]
for fp in values:
self.deserialize(fp)
[docs]
class ParameterizedIniReadAction(ParameterizedFileReadAction):
"""Deserialize an INI file into a parameterized object
Parameters
----------
option_strings
dest
parameterized
type
deserializer_name_dict
deserializer_type_dict
on_missing
required
help
metavar
nargs
const
defaults
comment_prefixes
inline_comment_prefixes
one_param_section
See Also
--------
ParameterizedFileReadAction
A full description of the parameters and behaviour of like actions.
pydrobert.param.serialization.deserialize_from_ini
A description of the deserialization process and of the additional
parameters.
"""
defaults: Optional[dict]
comment_prefixes: Tuple[str, ...]
inline_comment_prefixes: Tuple[str, ...]
one_param_section: Optional[str]
def __init__(
self,
option_strings: List[str],
dest: str,
parameterized: Optional[Union[param.Parameterized, dict]] = None,
type: Optional[Type[param.Parameterized]] = None,
deserializer_name_dict: Optional[dict] = None,
deserializer_type_dict: Optional[dict] = None,
on_missing: Literal["ignore", "warn", "raise"] = "warn",
required: bool = False,
help: Optional[str] = None,
metavar: Optional[str] = None,
nargs: Optional[Union[str, int]] = None,
const: Optional[str] = None,
defaults: Optional[dict] = None,
comment_prefixes: Sequence[str] = ("#", ";"),
inline_comment_prefixes: Sequence[str] = (";",),
one_param_section: Optional[str] = None,
):
self.defaults = defaults
self.comment_prefixes = tuple(comment_prefixes)
self.inline_comment_prefixes = tuple(inline_comment_prefixes)
self.one_param_section = one_param_section
super().__init__(
option_strings,
dest,
parameterized=parameterized,
type=type,
deserializer_name_dict=deserializer_name_dict,
deserializer_type_dict=deserializer_type_dict,
on_missing=on_missing,
required=required,
help=help,
metavar=metavar,
nargs=nargs,
const=const,
)
def deserialize(self, fp: Union[TextIO, str]) -> None:
deserialize_from_ini(
fp,
self.parameterized,
deserializer_name_dict=self.deserializer_name_dict,
deserializer_type_dict=self.deserializer_type_dict,
on_missing=self.on_missing,
defaults=self.defaults,
comment_prefixes=self.comment_prefixes,
inline_comment_prefixes=self.inline_comment_prefixes,
one_param_section=self.one_param_section,
)
[docs]
class ParameterizedYamlReadAction(ParameterizedFileReadAction):
"""Deserialize a YAML file into a parameterized object
Parameters
----------
option_strings
dest
parameterized
type
deserializer_name_dict
deserializer_type_dict
on_missing
required
help
metavar
nargs
const
See Also
--------
ParameterizedFileReadAction
A full description of the parameters and behaviour of like actions
pydrobert.param.serialization.deserialize_from_yaml
A description of the deserialization process.
"""
def deserialize(self, fp: Union[TextIO, str]) -> None:
deserialize_from_yaml(
fp,
self.parameterized,
deserializer_name_dict=self.deserializer_name_dict,
deserializer_type_dict=self.deserializer_type_dict,
on_missing=self.on_missing,
)
[docs]
class ParameterizedJsonReadAction(ParameterizedFileReadAction):
"""Deserialize a JSON file into a parameterized object
Parameters
----------
option_strings
dest
parameterized
type
deserializer_name_dict
deserializer_type_dict
on_missing
required
help
metavar
nargs
const
See Also
--------
ParameterizedFileReadAction
A full description of the parameters and behaviour of like actions.
pydrobert.param.serialization.deserialize_from_json
A description of the deserialization process.
"""
def deserialize(self, fp: Union[TextIO, str]) -> None:
deserialize_from_json(
fp,
self.parameterized,
deserializer_name_dict=self.deserializer_name_dict,
deserializer_type_dict=self.deserializer_type_dict,
on_missing=self.on_missing,
)
[docs]
def add_parameterized_read_group(
parser: argparse.ArgumentParser,
parameterized: Optional[Union[param.Parameterized, dict]] = None,
type: Optional[Type[param.Parameterized]] = None,
include_yaml: Optional[bool] = None,
ini_option_strings: Sequence[str] = ("--read-ini",),
json_option_strings: Sequence[str] = ("--read-json",),
yaml_option_strings: Sequence[str] = ("--read-yaml",),
dest: str = "params",
ini_kwargs: dict = dict(),
json_kwargs: dict = dict(),
yaml_kwargs: dict = dict(),
):
r'''Add flags to read configs from INI, JSON, or YAML sources
This convenience function adds a mutually exclusive group of read actions
to `parser` to read in parameters from 3 file types: INI, JSON, or YAML.
What to read into is determined by the keyword args `type` or
`parameterized`.
1. If `type` is specified, it will be instantiated and populated. Its name will
match ``type.__name__``
2. If `parameterized` is specified and is a
:class:`param.parameterized.Parameterized` instance, it will be populated
directly.
3. If `parameterized` is a dictionary whose leaves are
:class:`param.parameterized.Parameterized`, sections of the config whose keys
match the keys of the dictionary will populate the corresponding
:class:`param.parameterized.Parameterized` instances. `parameterized` can nest
those instances repeatedly, but only a shallow dict will be able to be parsed
from an INI file
Parameters
----------
parser
type
parametrized
include_yaml
Whether to include the YAML config flags. YAML requires one of
:mod:`ruamel.yaml` or :mod:`yaml` to be installed. If unset, we will include the
flags if it is possible to import a YAML module.
ini_option_strings
Zero or more option strings specifying that the next argument is an INI file to
be read. If no option strings are specified, INI reading is disabled
json_option_strings
Zero or more option strings specifying that the next argument is an JSON file to
be read. If no option strings are specified, JSON reading is disabled
yaml_option_strings
Zero or more option strings specifying that the next argument is an YAML file to
be read. If no option strings are specified, YAML reading is disabled
dest
Under what name to store parameters in the returned namespace of
``parser.parse_args(...)``
ini_kwargs
Additional keyword arguments to use when creating the INI flag. See
:class:`ParameterizedIniReadAction` for more info
json_kwargs
Additional keyword arguments to use when creating the JSON flag. See
:class`ParameterizedJsonReadAction` for more info
yaml_kwargs
Additional keyword arguments to use when creating the YAML flag. See
:class`ParameterizedYamlReadAction` for more info
Returns
-------
group : obj
The mutually exclusive group containing the flags
Examples
--------
>>> # write some configs
>>> with open('config.ini', 'w') as f:
>>> f.write('[DEFAULT]\nfoo = a\n')
>>> with open('config.json', 'w') as f:
>>> f.write('{"foo": "b"}\n')
>>> # make our Parameterized type
>>> import param, argparse
>>> class MyParams(param.Parameterized):
>>> foo = param.String(None)
>>> # make our parser
>>> parser = argparse.ArgumentParser()
>>> add_parameterized_read_group(parser, type=MyParams)
>>> # parse an INI
>>> options = parser.parse_args(['--read-ini', 'config.ini'])
>>> assert options.params.foo == "a"
>>> # parse a JSON
>>> options = parser.parse_args(['--read-json', 'config.json'])
>>> assert options.params.foo == "b"
>>> # write a hierarchical config
>>> with open('config.yaml', 'w') as f:
>>> f.write("""
... A:
... foo: bar
... baz: 1
... B:
... B.1:
... me: I may
... B.2:
... me: me me mee
... """)
>>> # make our Parameterized types
>>> class A(param.Parameterized):
>>> foo = param.String(None)
>>> bar = param.Integer(None)
>>> class B(param.Parameterized):
>>> me = param.String(None)
>>> parameterized = {'A': A(), 'B': {'B.1': B(), 'B.2': B()}}
>>> # make our parser
>>> parser = argparse.ArgumentParser()
>>> add_parameterized_read_group(parser, parameterized=parameterized)
>>> # parse YAML (requires package ruamel.yaml/ruamel_yaml or pyyaml)
>>> parser.parse_args(['--read-yaml', 'config.yaml'])
>>> assert parameterized['A'].baz == 1
>>> assert parameterized['B']['B.2'].me == 'me me mee'
'''
if parameterized is None and type is None:
raise TypeError("one of parameterized or type must be set")
if parameterized is not None and type is not None:
raise TypeError("only one of parameterized or type can be set")
for name, dict_ in (
("ini", ini_kwargs),
("json", json_kwargs),
("yaml", yaml_kwargs),
):
keys = set(dict_) & {"dest", "type", "parameterized"}
if keys:
raise TypeError(
"{}_kwargs contains unexpected keyword arguments: {}"
"".format(name, ", ".join(sorted(keys)))
)
if parameterized is None:
parameterized = type(name=type.__name__)
group = parser.add_mutually_exclusive_group()
if ini_option_strings:
group.add_argument(
*ini_option_strings,
action=ParameterizedIniReadAction,
dest=dest,
parameterized=parameterized,
**ini_kwargs
)
if json_option_strings:
group.add_argument(
*json_option_strings,
action=ParameterizedJsonReadAction,
dest=dest,
parameterized=parameterized,
**json_kwargs
)
if include_yaml is None:
include_yaml = yaml_is_available()
if include_yaml and len(yaml_option_strings):
group.add_argument(
*yaml_option_strings,
action=ParameterizedYamlReadAction,
dest=dest,
parameterized=parameterized,
**yaml_kwargs
)
return group
[docs]
class ParameterizedPrintAction(argparse.Action, metaclass=abc.ABCMeta):
"""Base class for printing parameters to stdout and exiting
Subclasses of this class can be added as the 'action' keyword to an
:func:`argparse.ArgumentParser.add_argument` call. Like the ``--help`` flag, after
this action is called, the program will try to exit, but not before printing out
parameters.
There are three ways to specify what parameters to print, analogous to how they are
specified in :class:`ParameterizedFileReadAction`:
1. Set the keyword `type` with a subclass of
:class:`param.parameterized.Parameterized`. A new instance of that type will be
created to be printed. Its name will be ``type.__name__``
2. Set the keyword `parameterized` with an instance of
:class:`param.parameterized.Parameterized`. That instance will be printed.
3. Set the keyword `parameterized` as a hierarchical dictionary of
:class:`param.parameterized.Parameterized` instances. The leaves of the
dictionary will be populated according to the "hierarchical mode" specified in
the documentation of :func:`pydrobert.param.serialization.serialize_to_dict`
Note that if a :class:`ParameterizedFileReadAction` has been called on the command
line prior to the print that shares the same `parameterized` value as in 2. or 3.,
`parameterized` will be populated by that file's contents.
Parameters
----------
option_strings
A list of command-line option strings which should be associated with this
action.
dest
Ignored
parameterized
type
serializer_name_dict
Use specific serializers for parameters with specific names
serializer_type_dict
Use specific serializers for parameters with exactly matching types
only
If specified, only the parameters with their names in this set will be printed
on_missing
What to do if the parameterized instance does not have a parameter listed in
`only`
include_help
Whether to print parameter help when printing parameters
help
The help string describing the argument
out_stream
Where to print the parameters to
"""
parameterized: Union[param.Parameterized, dict]
serializer_name_dict: Optional[dict]
serializer_type_dict: Optional[dict]
only: Collection[str]
on_missing: Literal["ignore", "warn", "raise"]
include_help: bool
out_stream: TextIO
def __init__(
self,
option_strings: List[str],
dest: str,
parameterized: Optional[Union[dict, param.Parameterized]] = None,
type: Optional[Type[param.Parameterized]] = None,
serializer_name_dict: Optional[dict] = None,
serializer_type_dict: Optional[dict] = None,
only: Optional[Collection[str]] = None,
on_missing: Literal["ignore", "warn", "raise"] = "raise",
include_help: bool = True,
help: Optional[str] = None,
out_stream: TextIO = sys.stdout,
):
if parameterized is None and type is None:
raise TypeError("one of parameterized or type must be set")
if parameterized is not None and type is not None:
raise TypeError("only one of parameterized or type can be set")
if parameterized is None:
self.parameterized = type(name=type.__name__)
else:
self.parameterized = parameterized
self.serializer_name_dict = serializer_name_dict
self.serializer_type_dict = serializer_type_dict
self.only = only
self.on_missing = on_missing
self.include_help = include_help
self.out_stream = out_stream
super(ParameterizedPrintAction, self).__init__(
option_strings, dest, help=help, nargs=0
)
[docs]
@abc.abstractmethod
def print_parameters(self) -> None:
"""Print the parameters
Called during the callable section of this class. Should print to the
attribute `out_stream`
"""
raise NotImplementedError()
def __call__(
self,
parser: argparse.ArgumentParser,
namespace: argparse.Namespace,
values,
option_string: Optional[str] = None,
) -> None:
self.print_parameters()
parser.exit()
[docs]
class ParameterizedIniPrintAction(ParameterizedPrintAction):
"""Print parameters as INI and exit
Parameters
----------
option_strings
dest
parameterized
type
serializer_name_dict
serializer_type_dict
only
on_missing
include_help
help
out_stream
help_prefix
one_param_section
See Also
--------
ParameterizedPrintAction
A full description of the parameters and behaviour of like actions
pydrobert.param.serialization.serialize_to_ini
A description of the serialization process and of the additional parameters
"""
help_prefix: str
one_param_section: Optional[str]
def __init__(
self,
option_strings: List[str],
dest: str,
parameterized: Optional[Union[param.Parameterized, dict]] = None,
type: Optional[Type[param.Parameterized]] = None,
serializer_name_dict: Optional[dict] = None,
serializer_type_dict: Optional[dict] = None,
only: Optional[Collection[str]] = None,
on_missing: Literal["ignore", "warn", "raise"] = "raise",
include_help: bool = True,
help: Optional[str] = None,
out_stream: TextIO = sys.stdout,
help_prefix: str = "#",
one_param_section: Optional[str] = None,
):
self.help_prefix = help_prefix
self.one_param_section = one_param_section
super(ParameterizedIniPrintAction, self).__init__(
option_strings,
dest,
parameterized=parameterized,
type=type,
serializer_name_dict=serializer_name_dict,
serializer_type_dict=serializer_type_dict,
only=only,
on_missing=on_missing,
include_help=include_help,
help=help,
out_stream=out_stream,
)
def print_parameters(self):
serialize_to_ini(
self.out_stream,
self.parameterized,
only=self.only,
serializer_name_dict=self.serializer_name_dict,
serializer_type_dict=self.serializer_type_dict,
on_missing=self.on_missing,
include_help=self.include_help,
help_prefix=self.help_prefix,
one_param_section=self.one_param_section,
)
[docs]
class ParameterizedJsonPrintAction(ParameterizedPrintAction):
"""Print parameters as JSON and exit
Parameters
----------
option_strings
dest
parameterized
type
serializer_name_dict
serializer_type_dict
only
on_missing
out_stream
indent
See Also
--------
ParameterizedPrintAction
A full description of the parameters and behaviour of like actions
pydrobert.param.serialization.serialize_to_json
A description of the serialization process and of the additional parameters
"""
indent: int
def __init__(
self,
option_strings: List[str],
dest: str,
parameterized: Optional[Union[param.Parameterized, dict]] = None,
type: Optional[Type[param.Parameterized]] = None,
serializer_name_dict: Optional[dict] = None,
serializer_type_dict: Optional[dict] = None,
only: Optional[Collection[str]] = None,
on_missing: Literal["ignore", "warn", "raise"] = "raise",
help: Optional[str] = None,
out_stream: TextIO = sys.stdout,
indent: int = 2,
):
self.indent = indent
super(ParameterizedJsonPrintAction, self).__init__(
option_strings,
dest,
parameterized=parameterized,
type=type,
serializer_name_dict=serializer_name_dict,
serializer_type_dict=serializer_type_dict,
only=only,
on_missing=on_missing,
include_help=False,
help=help,
out_stream=out_stream,
)
def print_parameters(self) -> None:
serialize_to_json(
self.out_stream,
self.parameterized,
only=self.only,
serializer_name_dict=self.serializer_name_dict,
serializer_type_dict=self.serializer_type_dict,
on_missing=self.on_missing,
indent=self.indent,
)
[docs]
class ParameterizedYamlPrintAction(ParameterizedPrintAction):
"""Print parameters as YAML and exit
Parameters
----------
option_strings
dest
parameterized
type
serializer_name_dict
serializer_type_dict
only
on_missing
include_help
help
out_stream
See Also
--------
ParameterizedPrintAction
A full description of the parameters and behaviour of like actions
pydrobert.param.serialization.serialize_to_yaml
A description of the serialization process and of the additional parameters
"""
def print_parameters(self) -> None:
serialize_to_yaml(
self.out_stream,
self.parameterized,
only=self.only,
serializer_name_dict=self.serializer_name_dict,
serializer_type_dict=self.serializer_type_dict,
on_missing=self.on_missing,
include_help=self.include_help,
)
[docs]
def add_parameterized_print_group(
parser: argparse.ArgumentParser,
type: Optional[Type[param.Parameterized]] = None,
parameterized: Optional[Union[param.Parameterized, dict]] = None,
include_yaml: Optional[bool] = None,
ini_option_strings: Sequence[str] = ("--print-ini",),
json_option_strings: Sequence[str] = ("--print-json",),
yaml_option_strings: Sequence[str] = ("--print-yaml",),
ini_kwargs: dict = dict(),
json_kwargs: dict = dict(),
yaml_kwargs: dict = dict(),
):
"""Add flags to print parameters as INI, JSON, or YAML
This convenience function adds a group of print actions to `parser` to
print parameters and exit in one of INI, JSON, or YAML format.
What to print is determined by the keyword args `type` or `parameterized`.
1. If `type` is specified, it will be instantiated and printed with
whatever defaults it has. The instance will have the name
``type.__name__``
2. If `parameterized` is a :class:`param.parameterized.Parameterized` instance, that
instance will be printed
3. If `parameterized` is a dictionary of :class:`param.parameterized.Parameterized`
instances, those instances will be serialized to dictionaries, then the
dictionary of dictionaries will be printed. `parameterized` can contain nested
dictionaries of :class:`param.parameterized.Parameterized` instances, but it will
be unable to be printed as an INI file, only JSON or YAML
Parameters
----------
parser
type
parametrized
include_yaml
Whether to include the YAML print flags. YAML requires one of :mod:`ruamel.yaml`
or :mod:`yaml` to be installed. If unset, we will include the flags if it is
possible to import a YAML module.
ini_option_strings
Zero or more option strings specifying that INI format should be printed. If no
option strings are specified, INI printing is disabled
json_option_strings
Zero or more option strings specifying that JSON format should be printed. If no
option strings are specified, JSON printing is disabled
yaml_option_strings
Zero or more option strings specifying that YAML format should be printed. If no
option strings are specified, YAML printing is disabled
ini_kwargs
Additional keyword arguments to use when creating the INI flag. See
:class:`ParameterizedIniPrintAction` for more info
json_kwargs
Additional keyword arguments to use when creating the JSON flag. See
:class:`ParameterizedJsonPrintAction` for more info
yaml_kwargs
Additional keyword arguments to use when creating the YAML flag. See
:class:`ParameterizedYamlPrintAction` for more info
Returns
-------
group : obj
The group containing the flags
Examples
--------
>>> import param, argparse
>>> class MyParams(param.Parameterized):
... an_int = param.Integer(1)
... a_bool = param.Boolean(True)
>>> parser = argparse.ArgumentParser()
>>> add_parameterized_print_group(parser, type=MyParams)
>>> try:
... parser.parse_args(['--print-ini'])
... except SystemExit:
... pass
[MyParams]
a_bool = true
an_int = 1
>>> try:
... # only works if ruamel.yaml/ruamel_yaml or pyyaml installed
... parser.parse_args(['--print-yaml'])
... except SystemExit:
... pass
a_bool: true
an_int: 1
>>> import param, argparse
>>> class A(param.Parameterized):
... something = param.Integer(None)
... else_ = param.List([1, 2])
>>> class B(param.Parameterized):
... float_ = param.Number(3.14)
>>> parameterized = {'A': {'AA': A()}, 'B': B()}
>>> parser = argparse.ArgumentParser()
>>> add_parameterized_print_group(
... parser, parameterized=parameterized, json_kwargs={'indent': None})
>>> try:
... parser.parse_args(['--print-json'])
... except SystemExit:
... pass
{"A": {"AA": {"else_": [1, 2], "something": null}}, "B": {"float_": 3.14}}
Notes
-----
The returned `group` is technically mutally exclusive. However, since the print
action ends with a :func:`sys.exit` call, mutual exclusivity will never be enforced
"""
if parameterized is None and type is None:
raise TypeError("one of parameterized or type must be set")
if parameterized is not None and type is not None:
raise TypeError("only one of parameterized or type can be set")
for name, dict_ in (
("ini", ini_kwargs),
("json", json_kwargs),
("yaml", yaml_kwargs),
):
keys = set(dict_) & {"type", "parameterized"}
if keys:
raise TypeError(
"{}_kwargs contains unexpected keyword arguments: {}"
"".format(name, ", ".join(sorted(keys)))
)
if type is not None:
parameterized = type(name=type.__name__)
group = parser.add_mutually_exclusive_group()
if ini_option_strings:
group.add_argument(
*ini_option_strings,
action=ParameterizedIniPrintAction,
parameterized=parameterized,
**ini_kwargs
)
if json_option_strings:
group.add_argument(
*json_option_strings,
action=ParameterizedJsonPrintAction,
parameterized=parameterized,
**json_kwargs
)
if include_yaml is None:
include_yaml = yaml_is_available()
if include_yaml and len(yaml_option_strings):
group.add_argument(
*yaml_option_strings,
action=ParameterizedYamlPrintAction,
parameterized=parameterized,
**yaml_kwargs
)
return group