File: defparser.py

package info (click to toggle)
python-pint 0.25.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,940 kB
  • sloc: python: 20,478; makefile: 148
file content (143 lines) | stat: -rw-r--r-- 4,658 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from __future__ import annotations

import pathlib
import typing as ty

import flexcache as fc
import flexparser as fp

from ..base_defparser import ParserConfig
from . import block, common, context, defaults, group, plain, system


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,
    ]
):
    pass


class _PintParser(fp.Parser[PintRootBlock, ParserConfig]):
    """Parser for the original Pint definition file, with cache."""

    _delimiters = {
        "#": (
            fp.DelimiterInclude.SPLIT_BEFORE,
            fp.DelimiterAction.CAPTURE_NEXT_TIL_EOL,
        ),
        **fp.SPLIT_EOL,
    }
    _root_block_class = PintRootBlock
    _strip_spaces = True

    _diskcache: fc.DiskCache | None

    def __init__(self, config: ParserConfig, *args: ty.Any, **kwargs: ty.Any):
        self._diskcache = kwargs.pop("diskcache", None)
        super().__init__(config, *args, **kwargs)

    def parse_file(
        self, path: pathlib.Path
    ) -> fp.ParsedSource[PintRootBlock, ParserConfig]:
        if self._diskcache is None:
            return super().parse_file(path)
        content, _basename = self._diskcache.load(path, super().parse_file)
        return content


class DefParser:
    skip_classes: tuple[type, ...] = (
        fp.BOF,
        fp.BOR,
        fp.BOS,
        fp.EOS,
        plain.CommentDefinition,
    )

    def __init__(self, default_config: ParserConfig, diskcache: fc.DiskCache):
        self._default_config = default_config
        self._diskcache = diskcache

    def iter_parsed_project(
        self, parsed_project: fp.ParsedProject[PintRootBlock, ParserConfig]
    ) -> ty.Generator[fp.ParsedStatement[ParserConfig], None, None]:
        last_location = None
        for stmt in parsed_project.iter_blocks():
            if isinstance(stmt, fp.BOS):
                if isinstance(stmt, fp.BOF):
                    last_location = str(stmt.path)
                    continue
                elif isinstance(stmt, fp.BOR):
                    last_location = (
                        f"[package: {stmt.package}, resource: {stmt.resource_name}]"
                    )
                    continue
                else:
                    last_location = "orphan string"
                    continue

            if isinstance(stmt, self.skip_classes):
                continue

            assert isinstance(last_location, str)
            if isinstance(stmt, common.DefinitionSyntaxError):
                stmt.set_location(last_location)
                raise stmt
            elif isinstance(stmt, block.DirectiveBlock):
                for exc in stmt.errors:
                    exc = common.DefinitionSyntaxError(str(exc))
                    exc.set_position(*stmt.get_position())
                    exc.set_raw(
                        (stmt.opening.raw or "") + " [...] " + (stmt.closing.raw or "")
                    )
                    exc.set_location(last_location)
                    raise exc

                try:
                    yield stmt.derive_definition()
                except Exception as exc:
                    exc = common.DefinitionSyntaxError(str(exc))
                    exc.set_position(*stmt.get_position())
                    exc.set_raw(stmt.opening.raw + " [...] " + stmt.closing.raw)
                    exc.set_location(last_location)
                    raise exc
            else:
                yield stmt

    def parse_file(
        self, filename: pathlib.Path | str, cfg: ParserConfig | None = None
    ) -> fp.ParsedProject[PintRootBlock, ParserConfig]:
        return fp.parse(
            filename,
            _PintParser,
            cfg or self._default_config,
            diskcache=self._diskcache,
            strip_spaces=True,
            delimiters=_PintParser._delimiters,
        )

    def parse_string(
        self, content: str, cfg: ParserConfig | None = None
    ) -> fp.ParsedProject[PintRootBlock, ParserConfig]:
        return fp.parse_bytes(
            content.encode("utf-8"),
            _PintParser,
            cfg or self._default_config,
            diskcache=self._diskcache,
            strip_spaces=True,
            delimiters=_PintParser._delimiters,
        )