diff options
Diffstat (limited to 'pint/delegates/txt_defparser')
-rw-r--r-- | pint/delegates/txt_defparser/__init__.py | 4 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/block.py | 19 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/common.py | 6 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/context.py | 78 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/defaults.py | 20 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/defparser.py | 45 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/group.py | 24 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/plain.py | 18 | ||||
-rw-r--r-- | pint/delegates/txt_defparser/system.py | 23 |
9 files changed, 163 insertions, 74 deletions
diff --git a/pint/delegates/txt_defparser/__init__.py b/pint/delegates/txt_defparser/__init__.py index 5572ca1..49e4a0b 100644 --- a/pint/delegates/txt_defparser/__init__.py +++ b/pint/delegates/txt_defparser/__init__.py @@ -11,4 +11,6 @@ from .defparser import DefParser -__all__ = [DefParser] +__all__ = [ + "DefParser", +] diff --git a/pint/delegates/txt_defparser/block.py b/pint/delegates/txt_defparser/block.py index 20ebcba..e8d8aa4 100644 --- a/pint/delegates/txt_defparser/block.py +++ b/pint/delegates/txt_defparser/block.py @@ -17,11 +17,14 @@ from __future__ import annotations from dataclasses import dataclass +from typing import Generic, TypeVar + +from ..base_defparser import PintParsedStatement, ParserConfig from ..._vendor import flexparser as fp @dataclass(frozen=True) -class EndDirectiveBlock(fp.ParsedStatement): +class EndDirectiveBlock(PintParsedStatement): """An EndDirectiveBlock is simply an "@end" statement.""" @classmethod @@ -31,8 +34,16 @@ class EndDirectiveBlock(fp.ParsedStatement): return None +OPST = TypeVar("OPST", bound="PintParsedStatement") +IPST = TypeVar("IPST", bound="PintParsedStatement") + +DefT = TypeVar("DefT") + + @dataclass(frozen=True) -class DirectiveBlock(fp.Block): +class DirectiveBlock( + Generic[DefT, OPST, IPST], fp.Block[OPST, IPST, EndDirectiveBlock, ParserConfig] +): """Directive blocks have beginning statement starting with a @ character. and ending with a "@end" (captured using a EndDirectiveBlock). @@ -41,5 +52,5 @@ class DirectiveBlock(fp.Block): closing: EndDirectiveBlock - def derive_definition(self): - pass + def derive_definition(self) -> DefT: + ... diff --git a/pint/delegates/txt_defparser/common.py b/pint/delegates/txt_defparser/common.py index 493d0ec..a1195b3 100644 --- a/pint/delegates/txt_defparser/common.py +++ b/pint/delegates/txt_defparser/common.py @@ -30,7 +30,7 @@ class DefinitionSyntaxError(errors.DefinitionSyntaxError, fp.ParsingError): location: str = field(init=False, default="") - def __str__(self): + def __str__(self) -> str: msg = ( self.msg + "\n " + (self.format_position or "") + " " + (self.raw or "") ) @@ -38,7 +38,7 @@ class DefinitionSyntaxError(errors.DefinitionSyntaxError, fp.ParsingError): msg += "\n " + self.location return msg - def set_location(self, value): + def set_location(self, value: str) -> None: super().__setattr__("location", value) @@ -47,7 +47,7 @@ class ImportDefinition(fp.IncludeStatement): value: str @property - def target(self): + def target(self) -> str: return self.value @classmethod diff --git a/pint/delegates/txt_defparser/context.py b/pint/delegates/txt_defparser/context.py index b7e5a67..ce9fc9b 100644 --- a/pint/delegates/txt_defparser/context.py +++ b/pint/delegates/txt_defparser/context.py @@ -23,32 +23,32 @@ from dataclasses import dataclass from ..._vendor import flexparser as fp from ...facets.context import definitions -from ..base_defparser import ParserConfig +from ..base_defparser import ParserConfig, PintParsedStatement from . import block, common, plain +# TODO check syntax +T = ty.TypeVar("T", bound="ForwardRelation | BidirectionalRelation") -@dataclass(frozen=True) -class Relation(definitions.Relation): - @classmethod - def _from_string_and_context_sep( - cls, s: str, config: ParserConfig, separator: str - ) -> fp.FromString[Relation]: - if separator not in s: - return None - if ":" not in s: - return None - rel, eq = s.split(":") +def _from_string_and_context_sep( + cls: type[T], s: str, config: ParserConfig, separator: str +) -> T | None: + if separator not in s: + return None + if ":" not in s: + return None + + rel, eq = s.split(":") - parts = rel.split(separator) + parts = rel.split(separator) - src, dst = (config.to_dimension_container(s) for s in parts) + src, dst = (config.to_dimension_container(s) for s in parts) - return cls(src, dst, eq.strip()) + return cls(src, dst, eq.strip()) @dataclass(frozen=True) -class ForwardRelation(fp.ParsedStatement, definitions.ForwardRelation, Relation): +class ForwardRelation(PintParsedStatement, definitions.ForwardRelation): """A relation connecting a dimension to another via a transformation function. <source dimension> -> <target dimension>: <transformation function> @@ -58,13 +58,11 @@ class ForwardRelation(fp.ParsedStatement, definitions.ForwardRelation, Relation) def from_string_and_config( cls, s: str, config: ParserConfig ) -> fp.FromString[ForwardRelation]: - return super()._from_string_and_context_sep(s, config, "->") + return _from_string_and_context_sep(cls, s, config, "->") @dataclass(frozen=True) -class BidirectionalRelation( - fp.ParsedStatement, definitions.BidirectionalRelation, Relation -): +class BidirectionalRelation(PintParsedStatement, definitions.BidirectionalRelation): """A bidirectional relation connecting a dimension to another via a simple transformation function. @@ -76,11 +74,11 @@ class BidirectionalRelation( def from_string_and_config( cls, s: str, config: ParserConfig ) -> fp.FromString[BidirectionalRelation]: - return super()._from_string_and_context_sep(s, config, "<->") + return _from_string_and_context_sep(cls, s, config, "<->") @dataclass(frozen=True) -class BeginContext(fp.ParsedStatement): +class BeginContext(PintParsedStatement): """Being of a context directive. @context[(defaults)] <canonical name> [= <alias>] [= <alias>] @@ -91,7 +89,7 @@ class BeginContext(fp.ParsedStatement): ) name: str - aliases: tuple[str, ...] + aliases: tuple[str] defaults: dict[str, numbers.Number] @classmethod @@ -130,7 +128,18 @@ class BeginContext(fp.ParsedStatement): @dataclass(frozen=True) -class ContextDefinition(block.DirectiveBlock): +class ContextDefinition( + block.DirectiveBlock[ + definitions.ContextDefinition, + BeginContext, + ty.Union[ + plain.CommentDefinition, + BidirectionalRelation, + ForwardRelation, + plain.UnitDefinition, + ], + ] +): """Definition of a Context @context[(defaults)] <canonical name> [= <alias>] [= <alias>] @@ -169,27 +178,34 @@ class ContextDefinition(block.DirectiveBlock): ] ] - def derive_definition(self): + def derive_definition(self) -> definitions.ContextDefinition: return definitions.ContextDefinition( self.name, self.aliases, self.defaults, self.relations, self.redefinitions ) @property - def name(self): + def name(self) -> str: + assert isinstance(self.opening, BeginContext) return self.opening.name @property - def aliases(self): + def aliases(self) -> tuple[str]: + assert isinstance(self.opening, BeginContext) return self.opening.aliases @property - def defaults(self): + def defaults(self) -> dict[str, numbers.Number]: + assert isinstance(self.opening, BeginContext) return self.opening.defaults @property - def relations(self): - return tuple(r for r in self.body if isinstance(r, Relation)) + def relations(self) -> tuple[BidirectionalRelation | ForwardRelation]: + return tuple( + r + for r in self.body + if isinstance(r, (ForwardRelation, BidirectionalRelation)) + ) @property - def redefinitions(self): + def redefinitions(self) -> tuple[plain.UnitDefinition]: return tuple(r for r in self.body if isinstance(r, plain.UnitDefinition)) diff --git a/pint/delegates/txt_defparser/defaults.py b/pint/delegates/txt_defparser/defaults.py index af6e31f..688d90f 100644 --- a/pint/delegates/txt_defparser/defaults.py +++ b/pint/delegates/txt_defparser/defaults.py @@ -19,10 +19,11 @@ from dataclasses import dataclass, fields from ..._vendor import flexparser as fp from ...facets.plain import definitions from . import block, plain +from ..base_defparser import PintParsedStatement @dataclass(frozen=True) -class BeginDefaults(fp.ParsedStatement): +class BeginDefaults(PintParsedStatement): """Being of a defaults directive. @defaults @@ -36,7 +37,16 @@ class BeginDefaults(fp.ParsedStatement): @dataclass(frozen=True) -class DefaultsDefinition(block.DirectiveBlock): +class DefaultsDefinition( + block.DirectiveBlock[ + definitions.DefaultsDefinition, + BeginDefaults, + ty.Union[ + plain.CommentDefinition, + plain.Equality, + ], + ] +): """Directive to store values. @defaults @@ -55,10 +65,10 @@ class DefaultsDefinition(block.DirectiveBlock): ] @property - def _valid_fields(self): + def _valid_fields(self) -> tuple[str]: return tuple(f.name for f in fields(definitions.DefaultsDefinition)) - def derive_definition(self): + def derive_definition(self) -> definitions.DefaultsDefinition: for definition in self.filter_by(plain.Equality): if definition.lhs not in self._valid_fields: raise ValueError( @@ -70,7 +80,7 @@ class DefaultsDefinition(block.DirectiveBlock): *tuple(self.get_key(key) for key in self._valid_fields) ) - def get_key(self, key): + def get_key(self, key: str) -> str: for stmt in self.body: if isinstance(stmt, plain.Equality) and stmt.lhs == key: return stmt.rhs diff --git a/pint/delegates/txt_defparser/defparser.py b/pint/delegates/txt_defparser/defparser.py index 0b99d6d..f1b8e45 100644 --- a/pint/delegates/txt_defparser/defparser.py +++ b/pint/delegates/txt_defparser/defparser.py @@ -5,11 +5,28 @@ import typing as ty from ..._vendor import flexcache as fc from ..._vendor import flexparser as fp -from .. import base_defparser +from ..base_defparser import ParserConfig from . import block, common, context, defaults, group, plain, system -class PintRootBlock(fp.RootBlock): +class PintRootBlock( + fp.RootBlock[ + ty.Union[ + plain.CommentDefinition, + common.ImportDefinition, + context.ContextDefinition, + defaults.DefaultsDefinition, + system.SystemDefinition, + group.GroupDefinition, + plain.AliasDefinition, + plain.DerivedDimensionDefinition, + plain.DimensionDefinition, + plain.PrefixDefinition, + plain.UnitDefinition, + ], + ParserConfig, + ] +): body: fp.Multi[ ty.Union[ plain.CommentDefinition, @@ -27,11 +44,15 @@ class PintRootBlock(fp.RootBlock): ] +class PintSource(fp.ParsedSource[PintRootBlock, ParserConfig]): + """Source code in Pint.""" + + class HashTuple(tuple): pass -class _PintParser(fp.Parser): +class _PintParser(fp.Parser[PintRootBlock, ParserConfig]): """Parser for the original Pint definition file, with cache.""" _delimiters = { @@ -46,11 +67,11 @@ class _PintParser(fp.Parser): _diskcache: fc.DiskCache - def __init__(self, config: base_defparser.ParserConfig, *args, **kwargs): + def __init__(self, config: ParserConfig, *args, **kwargs): self._diskcache = kwargs.pop("diskcache", None) super().__init__(config, *args, **kwargs) - def parse_file(self, path: pathlib.Path) -> fp.ParsedSource: + def parse_file(self, path: pathlib.Path) -> PintSource: if self._diskcache is None: return super().parse_file(path) content, basename = self._diskcache.load(path, super().parse_file) @@ -58,7 +79,13 @@ class _PintParser(fp.Parser): class DefParser: - skip_classes = (fp.BOF, fp.BOR, fp.BOS, fp.EOS, plain.CommentDefinition) + skip_classes: tuple[type] = ( + fp.BOF, + fp.BOR, + fp.BOS, + fp.EOS, + plain.CommentDefinition, + ) def __init__(self, default_config, diskcache): self._default_config = default_config @@ -78,6 +105,8 @@ class DefParser: continue if isinstance(stmt, common.DefinitionSyntaxError): + # TODO: check why this assert fails + # assert isinstance(last_location, str) stmt.set_location(last_location) raise stmt elif isinstance(stmt, block.DirectiveBlock): @@ -101,7 +130,7 @@ class DefParser: else: yield stmt - def parse_file(self, filename: pathlib.Path, cfg=None): + def parse_file(self, filename: pathlib.Path, cfg: ParserConfig | None = None): return fp.parse( filename, _PintParser, @@ -109,7 +138,7 @@ class DefParser: diskcache=self._diskcache, ) - def parse_string(self, content: str, cfg=None): + def parse_string(self, content: str, cfg: ParserConfig | None = None): return fp.parse_bytes( content.encode("utf-8"), _PintParser, diff --git a/pint/delegates/txt_defparser/group.py b/pint/delegates/txt_defparser/group.py index 5be42ac..e96d44b 100644 --- a/pint/delegates/txt_defparser/group.py +++ b/pint/delegates/txt_defparser/group.py @@ -23,10 +23,11 @@ from dataclasses import dataclass from ..._vendor import flexparser as fp from ...facets.group import definitions from . import block, common, plain +from ..base_defparser import PintParsedStatement @dataclass(frozen=True) -class BeginGroup(fp.ParsedStatement): +class BeginGroup(PintParsedStatement): """Being of a group directive. @group <name> [using <group 1>, ..., <group N>] @@ -59,7 +60,16 @@ class BeginGroup(fp.ParsedStatement): @dataclass(frozen=True) -class GroupDefinition(block.DirectiveBlock): +class GroupDefinition( + block.DirectiveBlock[ + definitions.GroupDefinition, + BeginGroup, + ty.Union[ + plain.CommentDefinition, + plain.UnitDefinition, + ], + ] +): """Definition of a group. @group <name> [using <group 1>, ..., <group N>] @@ -88,19 +98,21 @@ class GroupDefinition(block.DirectiveBlock): ] ] - def derive_definition(self): + def derive_definition(self) -> definitions.GroupDefinition: return definitions.GroupDefinition( self.name, self.using_group_names, self.definitions ) @property - def name(self): + def name(self) -> str: + assert isinstance(self.opening, BeginGroup) return self.opening.name @property - def using_group_names(self): + def using_group_names(self) -> tuple[str]: + assert isinstance(self.opening, BeginGroup) return self.opening.using_group_names @property - def definitions(self) -> ty.Tuple[plain.UnitDefinition, ...]: + def definitions(self) -> tuple[plain.UnitDefinition]: return tuple(el for el in self.body if isinstance(el, plain.UnitDefinition)) diff --git a/pint/delegates/txt_defparser/plain.py b/pint/delegates/txt_defparser/plain.py index 749e7fd..9c7bd42 100644 --- a/pint/delegates/txt_defparser/plain.py +++ b/pint/delegates/txt_defparser/plain.py @@ -29,12 +29,12 @@ from ..._vendor import flexparser as fp from ...converters import Converter from ...facets.plain import definitions from ...util import UnitsContainer -from ..base_defparser import ParserConfig +from ..base_defparser import ParserConfig, PintParsedStatement from . import common @dataclass(frozen=True) -class Equality(fp.ParsedStatement, definitions.Equality): +class Equality(PintParsedStatement, definitions.Equality): """An equality statement contains a left and right hand separated lhs and rhs should be space stripped. @@ -53,7 +53,7 @@ class Equality(fp.ParsedStatement, definitions.Equality): @dataclass(frozen=True) -class CommentDefinition(fp.ParsedStatement, definitions.CommentDefinition): +class CommentDefinition(PintParsedStatement, definitions.CommentDefinition): """Comments start with a # character. # This is a comment. @@ -63,14 +63,14 @@ class CommentDefinition(fp.ParsedStatement, definitions.CommentDefinition): """ @classmethod - def from_string(cls, s: str) -> fp.FromString[fp.ParsedStatement]: + def from_string(cls, s: str) -> fp.FromString[CommentDefinition]: if not s.startswith("#"): return None return cls(s[1:].strip()) @dataclass(frozen=True) -class PrefixDefinition(fp.ParsedStatement, definitions.PrefixDefinition): +class PrefixDefinition(PintParsedStatement, definitions.PrefixDefinition): """Definition of a prefix:: <prefix>- = <value> [= <symbol>] [= <alias>] [ = <alias> ] [...] @@ -119,7 +119,7 @@ class PrefixDefinition(fp.ParsedStatement, definitions.PrefixDefinition): @dataclass(frozen=True) -class UnitDefinition(fp.ParsedStatement, definitions.UnitDefinition): +class UnitDefinition(PintParsedStatement, definitions.UnitDefinition): """Definition of a unit:: <canonical name> = <relation to another unit or dimension> [= <symbol>] [= <alias>] [ = <alias> ] [...] @@ -194,7 +194,7 @@ class UnitDefinition(fp.ParsedStatement, definitions.UnitDefinition): @dataclass(frozen=True) -class DimensionDefinition(fp.ParsedStatement, definitions.DimensionDefinition): +class DimensionDefinition(PintParsedStatement, definitions.DimensionDefinition): """Definition of a root dimension:: [dimension name] @@ -221,7 +221,7 @@ class DimensionDefinition(fp.ParsedStatement, definitions.DimensionDefinition): @dataclass(frozen=True) class DerivedDimensionDefinition( - fp.ParsedStatement, definitions.DerivedDimensionDefinition + PintParsedStatement, definitions.DerivedDimensionDefinition ): """Definition of a derived dimension:: @@ -261,7 +261,7 @@ class DerivedDimensionDefinition( @dataclass(frozen=True) -class AliasDefinition(fp.ParsedStatement, definitions.AliasDefinition): +class AliasDefinition(PintParsedStatement, definitions.AliasDefinition): """Additional alias(es) for an already existing unit:: @alias <canonical name or previous alias> = <alias> [ = <alias> ] [...] diff --git a/pint/delegates/txt_defparser/system.py b/pint/delegates/txt_defparser/system.py index b21fd7a..4efbb4d 100644 --- a/pint/delegates/txt_defparser/system.py +++ b/pint/delegates/txt_defparser/system.py @@ -14,11 +14,12 @@ from dataclasses import dataclass from ..._vendor import flexparser as fp from ...facets.system import definitions +from ..base_defparser import PintParsedStatement from . import block, common, plain @dataclass(frozen=True) -class BaseUnitRule(fp.ParsedStatement, definitions.BaseUnitRule): +class BaseUnitRule(PintParsedStatement, definitions.BaseUnitRule): @classmethod def from_string(cls, s: str) -> fp.FromString[BaseUnitRule]: if ":" not in s: @@ -32,7 +33,7 @@ class BaseUnitRule(fp.ParsedStatement, definitions.BaseUnitRule): @dataclass(frozen=True) -class BeginSystem(fp.ParsedStatement): +class BeginSystem(PintParsedStatement): """Being of a system directive. @system <name> [using <group 1>, ..., <group N>] @@ -67,7 +68,13 @@ class BeginSystem(fp.ParsedStatement): @dataclass(frozen=True) -class SystemDefinition(block.DirectiveBlock): +class SystemDefinition( + block.DirectiveBlock[ + definitions.SystemDefinition, + BeginSystem, + ty.Union[plain.CommentDefinition, BaseUnitRule], + ] +): """Definition of a System: @system <name> [using <group 1>, ..., <group N>] @@ -92,19 +99,21 @@ class SystemDefinition(block.DirectiveBlock): opening: fp.Single[BeginSystem] body: fp.Multi[ty.Union[plain.CommentDefinition, BaseUnitRule]] - def derive_definition(self): + def derive_definition(self) -> definitions.SystemDefinition: return definitions.SystemDefinition( self.name, self.using_group_names, self.rules ) @property - def name(self): + def name(self) -> str: + assert isinstance(self.opening, BeginSystem) return self.opening.name @property - def using_group_names(self): + def using_group_names(self) -> tuple[str]: + assert isinstance(self.opening, BeginSystem) return self.opening.using_group_names @property - def rules(self): + def rules(self) -> tuple[BaseUnitRule]: return tuple(el for el in self.body if isinstance(el, BaseUnitRule)) |