File: ast.py

package info (click to toggle)
python-tatsu 5.16.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,196 kB
  • sloc: python: 10,037; makefile: 46
file content (140 lines) | stat: -rw-r--r-- 3,884 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
from __future__ import annotations

import copy
import operator
from collections.abc import Iterable
from functools import reduce
from typing import Any

from .infos import ParseInfo
from .util import asjson, is_list


class AST(dict[str, Any]):
    _frozen = False

    def __init__(self, *args: Any, **kwargs: Any):
        super().__init__()
        self.update(*args, **kwargs)
        self._frozen = True

    @property
    def frozen(self) -> bool:
        return self._frozen

    @property
    def parseinfo(self) -> ParseInfo | None:
        try:
            return super().__getitem__('parseinfo')
        except KeyError:
            return None

    def set_parseinfo(self, value: ParseInfo | None) -> None:
        if value is None:
            return
        super().__setitem__('parseinfo', value)

    def copy(self) -> AST:
        return copy.copy(self)

    def asjson(self) -> Any:
        return asjson(self)

    def _set(self, key: str, value: Any, force_list: bool = False) -> None:
        key = self._safekey(key)
        previous = self.get(key)

        if previous is None and force_list:
            value = [value]
        elif previous is None:
            pass
        elif is_list(previous):
            value = [*previous, value]
        else:
            value = [previous, value]

        super().__setitem__(key, value)

    def _setlist(self, key: str, value: list[Any]) -> None:
        self._set(key, value, force_list=True)

    def __copy__(self) -> AST:
        return AST(self)

    def __getitem__(self, key: str) -> Any:
        if key in self:
            return super().__getitem__(key)
        key = self._safekey(key)
        if key in self:
            return super().__getitem__(key)
        return None

    def __setitem__(self, key: str, value: Any) -> None:
        self._set(key, value)

    def __delitem__(self, key: str) -> None:
        key = self._safekey(key)
        super().__delitem__(key)

    def __setattr__(self, name: str, value: Any) -> None:
        if self._frozen and name not in vars(self):
            raise AttributeError(
                f'{type(self).__name__} attributes are fixed. '
                f' Cannot set attribute "{name}".',
            )
        super().__setattr__(name, value)

    def __getattr__(self, name: str) -> Any:
        key = self._safekey(name)
        if key in self:
            return self[key]
        elif name in self:
            return self[name]

        try:
            return super().__getattribute__(name)
        except AttributeError:
            return None

    def __hasattribute__(self, name: str) -> bool:
        try:
            super().__getattribute__(name)
        except (TypeError, AttributeError):
            return False
        else:
            return True

    def __reduce__(self) -> tuple[Any, Any]:
        return (AST, (list(self.items()),))

    def _safekey(self, key: str) -> str:
        while self.__hasattribute__(key):
            key += '_'
        return key

    def _define(self, keys: Iterable[str], list_keys: Iterable[str] | None = None):
        for key in (self._safekey(k) for k in keys):
            if key not in self:
                super().__setitem__(key, None)

        for key in (self._safekey(k) for k in list_keys or []):
            if key not in self:
                super().__setitem__(key, [])

    def __json__(self, seen: set[int] | None = None) -> Any:
        return {name: asjson(value, seen=seen) for name, value in self.items()}

    def __repr__(self) -> str:
        return repr(self.asjson())

    def __str__(self) -> str:
        return str(self.asjson())

    def __hash__(self) -> int:  # type: ignore
        return hash(
            reduce(
                operator.xor,
                (hash((name, id(value))) for name, value in self.items()),
                0,
            ),
        )