rwskit.config ============= .. py:module:: rwskit.config Attributes ---------- .. autoapisummary:: rwskit.config.log rwskit.config.T rwskit.config.E rwskit.config.I rwskit.config.TypeParser Classes ------- .. autoapisummary:: rwskit.config.YamlConfig rwskit.config.EnvironmentConfig Module Contents --------------- .. py:data:: log .. py:data:: T .. py:data:: E .. py:data:: I .. py:data:: TypeParser .. py:class:: YamlConfig Bases: :py:obj:`yamlable.YamlAble` A base class for serializable configuration objects. Classes that inherit from this class can easily be serialized to and from YAML files. Given a YAML file, the class can be reconstructed as long as the YAML attributes can be uniquely mapped to a subclass of :class:`YamlConfig`. Additionally, the configuration can be split across multiple files for better modularity using the ``!include`` directive. .. rubric:: Examples >>> class ChildConfig(YamlConfig): ... id: int ... name: str ... timestamp: datetime.datetime >>> class ParentConfig(YamlConfig): ... parent_attr: str = "parent_attr_value" ... child_attr: >>> expected_config = ParentConfig( ... id=1, ... child_config=ChildConfig( ... id=2, ... name="child_config", ... timestamp=datetime.datetime.now() ... ) >>> plain_yaml = ''' ... child_config: ... id: 2 ... name: child_config ... timestamp: 2024-11-19 13:55:34.064388 ... id: 1 ... ''' >>> from_plain_yaml = YamlConfig.loads_yaml(plain_yaml) >>> assert from_plain_yaml == expected_config The ``!yamlable`` tag can be used to explicitly tell the YAML parser which class to construct. The syntax is ``!yamlable/``. >>> tagged_yaml = ''' ... !yamlable/my_package.my_module.ParentConfig ... child_config: !yamlable/my_package.my_module.ChildConfig ... id: 2 ... name: child_config ... timestamp: 2024-11-19 13:55:34.064388 ... id: 1 ... ''' >>> assert YamlConfig.loads_yaml(tagged_yaml) == expected_config You can use the ``!include`` directive to include other YAML files. For example, assume you have the following two YAML files: .. code-block:: yaml # child_config.yaml id: 2 name: "child_config" timestamp: 2024-11-19 13:55:34.064388 .. code-block:: yaml # parent_config.yaml id: 1 child_config: !include child_config.yaml You can load the parent config using ``YamlConfig.load_yaml`` as follows: >>> YamlConfig.load_yaml("parent_config.yaml") .. py:attribute:: default_type_parsers .. py:method:: __init_subclass__(**kwargs: Any) :classmethod: Initialize subclasses to make them suitable configuration objects. * Automatically assign the ``__yaml_tag_suffix__`` using the fully qualified class name. * Convert the class to a ``pydantic.dataclasses.dataclass`` that has ``frozen=True`` and ``kw_only=True``. :param kwargs: :type kwargs: Any .. py:method:: get_registered_classes() -> set[Type[YamlConfig]] :classmethod: Get the set of classes currently registered as configuration objects. :returns: The set of registered yaml config classes. :rtype: set[Type[YamlConfig]] .. py:method:: dumps_plain_yaml() -> str Represent the class as plain YAML without any tags. .. note:: It may not be possible to reconstruct the python object from this string. :returns: The object as plain YAML without any tags. :rtype: str .. py:method:: load_yaml(file_path_or_stream: str | pathlib.Path | io.IOBase | io.StringIO, safe: bool = True) -> T :classmethod: Parses the given file path or stream as a yaml document. This methods only returns successfully if the result is an instance of `cls`. :param file_path_or_stream: :param safe: True (default) uses `yaml.safe_load`. False uses `yaml.load` :return: .. py:class:: EnvironmentConfig(*args, **kwargs) Bases: :py:obj:`abc.ABC` A mixin clas for configuration objects to help constructing them from environment variables. This mixin adds a method :meth:`from_environment` that will try to parse environment variables into the correct type for the dataclass. All you have to do is implement the :meth:`environment_mapping` method to provide a mapping between environment variable names and the dataclass field names. .. py:method:: get_default_type_parsers() -> dict[Type[I], TypeParser] :classmethod: Get the default type parsers for this class. .. py:method:: environment_mapping() -> Mapping[str, str] :classmethod: :abstractmethod: Returns a mapping from environment variable names to their corresponding field name. :rtype: Mapping[str, str] :raises ViolationError: If any of the returned values are not a field name of this class. Note, not all fields need to be included in the mapping, but any entry that is included must correspond to a field name. .. py:method:: from_environment(type_parsers: Optional[dict[Type[I], TypeParser]] = None, **kwargs) -> E :classmethod: Create an instance from environment variables. Environment variables can represent python primitive values, date objects, datetime objects. They can also be lists, sets, tuples, or dicts of these types. Collections are space separated string. Multiword strings should be enclosed in double quotes. Dictionary key value pairs are delimited by '='. Spaces are not allowed in keys or values. :param type_parsers: A mapping from python types to functions that parse a string into that type. These will be combined and override the default parsing rules described above. :type type_parsers: dict[Type[I], TypeParser] :param kwargs: Additional keyword arguments to override the values from the environment or composite fields (e.g., other dataclasses), which are constructed externally. .. py:method:: get_environment_kwargs(type_parsers: dict[Type[I], TypeParser]) -> dict[str, I] :classmethod: Get the keyword arguments for this dataclass that are available from the environment. :param type_parsers: The type parsers to use when parsing environment variables. :type type_parsers: dict[Type, TypeParser] :returns: The keyword arguments as a dictionary. :rtype: dict[str, Any] .. py:method:: get_dataclass_field_map() -> dict[str, dataclasses.Field] :classmethod: Get a mapping from field names to their dataclass fields. .. py:method:: get_field_from_name(field_name: str) -> dataclasses.Field :classmethod: Get the dataclass field from its name. .. py:method:: parse_value_from_string(input_string: str, target_type: Type[I], type_parsers: Optional[dict[Type[I], TypeParser]] = None) :classmethod: Try to parse an input string into the given python ``target_type``. The input string can be a single value that can be parsed by the default parsing rules or any of the rules implemented by the given ``type_parsers``. By default, the parseable values are: str, int, float, bool, datetime.datetime, datetime.date, re.Pattern, and any class that can be constructed from a single string argument (e.g., ``pathlib.Path``. To handle more complex ``target_types``, the input string can also be a JSON string. The string will be parsed as JSON and the resulting object will be traversed to try to convert the leaf values using the all the available parsing rules. :param input_string: The string to parse. :type input_string: str :param target_type: The python type to parse the value into. :type target_type: Type :param type_parsers: An optional dictionary that maps a type to a function that parses a string to that type. These rules will augment the default parsing rules given by :meth:`get_default_type_parsers` and will take precedent over the default rules if there is a conflict. :type type_parsers: dict[Type, TypeParser], optional :rtype: An instance of the target type parsed from the environment value.