diff --git a/pint/delegates/__init__.py b/pint/delegates/__init__.py index d22810cec..46b61fab6 100644 --- a/pint/delegates/__init__.py +++ b/pint/delegates/__init__.py @@ -1,23 +1,25 @@ """ - pint.delegates - ~~~~~~~~~~~~~~ +pint.delegates +~~~~~~~~~~~~~~ - Defines methods and classes to handle autonomous tasks. +Defines methods and classes to handle autonomous tasks. - :copyright: 2022 by Pint Authors, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. +:copyright: 2022 by Pint Authors, see AUTHORS for more details. +:license: BSD, see LICENSE for more details. """ + from __future__ import annotations from . import txt_defparser from .base_defparser import ParserConfig, build_disk_cache_class from .formatter import Formatter -from .toml_parser import toml_parser +from .toml_parser import toml_defparser, write_definitions __all__ = [ "txt_defparser", "ParserConfig", "build_disk_cache_class", "Formatter", - "toml_parser", + "toml_defparser", + "write_definitions", ] diff --git a/pint/delegates/toml_parser/__init__.py b/pint/delegates/toml_parser/__init__.py index b81750d96..e30c7ae77 100644 --- a/pint/delegates/toml_parser/__init__.py +++ b/pint/delegates/toml_parser/__init__.py @@ -1,16 +1,19 @@ """ - pint.delegates.toml_parser - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +pint.delegates.toml_parser +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Parser for the toml Pint Definition file. +Parser for the toml Pint Definition file. - :copyright: 2025 by Pint Authors, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. +:copyright: 2025 by Pint Authors, see AUTHORS for more details. +:license: BSD, see LICENSE for more details. """ + from __future__ import annotations -from .toml_parser import TomlParser +from .toml_defparser import TomlParser +from .toml_writer import write_definitions __all__ = [ "TomlParser", + "write_definitions", ] diff --git a/pint/delegates/toml_parser/toml_defparser.py b/pint/delegates/toml_parser/toml_defparser.py new file mode 100644 index 000000000..d51dfc170 --- /dev/null +++ b/pint/delegates/toml_parser/toml_defparser.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import copy +import pathlib + +import flexcache as fc + +from ...compat import tomllib +from ..base_defparser import ParserConfig +from . import plain + + +class TomlParser: + def __init__(self, default_config: ParserConfig, diskcache: fc.DiskCache): + self._default_config = default_config + self._diskcache = diskcache + + def iter_parsed_project(self, parsed_project: dict): + stmts = { + "unit": plain.UnitDefinition, + "prefix": plain.PrefixDefinition, + "dimension": plain.DerivedDimensionDefinition, + "system": plain.SystemDefinition, + "context": plain.ContextDefinition, + "group": plain.GroupDefinition, + } + for definition_type in parsed_project.keys(): + for key, value in parsed_project[definition_type].items(): + d = copy.copy(value) + d["name"] = key + stmt = stmts[definition_type].from_dict_and_config( + d, self._default_config + ) + yield stmt + + def parse_file( + self, filename: pathlib.Path | str, cfg: ParserConfig | None = None + ) -> dict: + with open(filename, "rb") as f: + data = tomllib.load(f) + return data + + # def parse_string(self, content: str, cfg: ParserConfig | None = None): + # return fp.parse_bytes( + # content.encode("utf-8"), + # _PintParser, + # cfg or self._default_config, + # diskcache=self._diskcache, + # strip_spaces=True, + # delimiters=_PintParser._delimiters, + # ) diff --git a/pint/delegates/toml_parser/toml_writer.py b/pint/delegates/toml_parser/toml_writer.py new file mode 100644 index 000000000..b0fde2510 --- /dev/null +++ b/pint/delegates/toml_parser/toml_writer.py @@ -0,0 +1,103 @@ +from __future__ import annotations + +from dataclasses import fields + +import tomli_w + +from ...facets.plain import GenericPlainRegistry + +keep_fields = [ + "value", + "defined_symbol", + "aliases", +] + + +def add_key_if_not_empty(dat, key, value): + if value == () or value is None: + return dat + else: + dat[key] = value + return dat + + +def parse_simple_definition(definition, definition_type): + attrs = [field.name for field in fields(definition) if field.name in keep_fields] + dat = {} + for attr in attrs: + dat = add_key_if_not_empty(dat, attr, getattr(definition, attr)) + if definition_type in ["units", "dimensions", "prefixes"] and hasattr( + definition, "raw" + ): + dat["value"] = definition.raw.split("=")[1].strip() + return dat + + +def prefixes_units_dimensions(ureg): + data = { + "prefix": {}, + "unit": {}, + "dimension": {}, + } + for definition_type, ureg_attr in zip( + ["unit", "prefix", "dimension"], + ["_units", "_prefixes", "_dimensions"], + ): + definitions = getattr(ureg, ureg_attr).values() + for definition in definitions: + for name, definition in getattr(ureg, ureg_attr).items(): + if hasattr(definition, "raw"): + data[definition_type][definition.name] = parse_simple_definition( + definition, definition_type + ) + return data + + +def groups(ureg): + group_data = {} + for group in ureg._group_definitions: + dat = {} + for attr in ["using_group_names"]: + dat = add_key_if_not_empty(dat, attr, getattr(group, attr)) + dat["definitions"] = {} + for definition in group.definitions: + dat["definitions"][definition.name] = parse_simple_definition( + definition, "_units" + ) + group_data[group.name] = dat + return group_data + + +def systems(ureg): + system_data = {} + for group in ureg._system_definitions: + dat = {} + for attr in ["using_group_names"]: + dat = add_key_if_not_empty(dat, attr, getattr(group, attr)) + dat["rules"] = [] + for rule in group.rules: + dat["rules"].append(rule.raw) + system_data[group.name] = dat + return system_data + + +def contexts(ureg): + context_data = {} + for group in ureg._context_definitions: + dat = {} + for attr in ["aliases", "defaults", "redefinitions"]: + dat = add_key_if_not_empty(dat, attr, getattr(group, attr)) + dat["relations"] = [] + for rule in group.relations: + dat["relations"].append(rule.raw) + context_data[group.name] = dat + return context_data + + +def write_definitions(filename: str, ureg: GenericPlainRegistry): + data = prefixes_units_dimensions(ureg) + data["group"] = groups(ureg) + data["system"] = systems(ureg) + data["context"] = contexts(ureg) + with open("test.toml", "wb") as f: + tomli_w.dump(data, f) diff --git a/pint/facets/plain/registry.py b/pint/facets/plain/registry.py index 12d1c4b3d..15aef0d3b 100644 --- a/pint/facets/plain/registry.py +++ b/pint/facets/plain/registry.py @@ -1,23 +1,23 @@ """ - pint.facets.plain.registry - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - :copyright: 2022 by Pint Authors, see AUTHORS for more details. - :license: BSD, see LICENSE for more details. - - The registry contains the following important methods: - - - parse_unit_name: Parse a unit to identify prefix, unit name and suffix - by walking the list of prefix and suffix. - Result is cached: NO - - parse_units: Parse a units expression and returns a UnitContainer with - the canonical names. - The expression can only contain products, ratios and powers of units; - prefixed units and pluralized units. - Result is cached: YES - - parse_expression: Parse a mathematical expression including units and - return a quantity object. - Result is cached: NO +pint.facets.plain.registry +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:copyright: 2022 by Pint Authors, see AUTHORS for more details. +:license: BSD, see LICENSE for more details. + +The registry contains the following important methods: + +- parse_unit_name: Parse a unit to identify prefix, unit name and suffix + by walking the list of prefix and suffix. + Result is cached: NO +- parse_units: Parse a units expression and returns a UnitContainer with + the canonical names. + The expression can only contain products, ratios and powers of units; + prefixed units and pluralized units. + Result is cached: YES +- parse_expression: Parse a mathematical expression including units and + return a quantity object. + Result is cached: NO """ @@ -250,9 +250,10 @@ def __init__( self._def_parser = delegates.txt_defparser.DefParser( delegates.ParserConfig(non_int_type), diskcache=self._diskcache ) - self._toml_parser = delegates.toml_parser.TomlParser( + self._toml_parser = delegates.toml_defparser.TomlParser( delegates.ParserConfig(non_int_type), diskcache=self._diskcache ) + self.write_definitions = lambda x: delegates.write_definitions(x, self) self.formatter = delegates.Formatter(self) self._filename = filename self.force_ndarray = force_ndarray @@ -495,7 +496,7 @@ def _helper_dispatch_adder(self, definition: Any) -> None: break else: raise TypeError( - f"No loader function defined " f"for {definition.__class__.__name__}" + f"No loader function defined for {definition.__class__.__name__}" ) adder_func(definition)