summaryrefslogtreecommitdiff
path: root/pint/delegates/txt_defparser
diff options
context:
space:
mode:
Diffstat (limited to 'pint/delegates/txt_defparser')
-rw-r--r--pint/delegates/txt_defparser/__init__.py4
-rw-r--r--pint/delegates/txt_defparser/block.py19
-rw-r--r--pint/delegates/txt_defparser/common.py6
-rw-r--r--pint/delegates/txt_defparser/context.py78
-rw-r--r--pint/delegates/txt_defparser/defaults.py20
-rw-r--r--pint/delegates/txt_defparser/defparser.py45
-rw-r--r--pint/delegates/txt_defparser/group.py24
-rw-r--r--pint/delegates/txt_defparser/plain.py18
-rw-r--r--pint/delegates/txt_defparser/system.py23
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))