Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tomlkit/source.py: 98%
100 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 07:01 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-03-26 07:01 +0000
1from copy import copy
2from typing import Any
3from typing import Optional
4from typing import Tuple
5from typing import Type
7from tomlkit.exceptions import ParseError
8from tomlkit.exceptions import UnexpectedCharError
9from tomlkit.toml_char import TOMLChar
12class _State:
13 def __init__(
14 self,
15 source: "Source",
16 save_marker: Optional[bool] = False,
17 restore: Optional[bool] = False,
18 ) -> None:
19 self._source = source
20 self._save_marker = save_marker
21 self.restore = restore
23 def __enter__(self) -> "_State":
24 # Entering this context manager - save the state
25 self._chars = copy(self._source._chars)
26 self._idx = self._source._idx
27 self._current = self._source._current
28 self._marker = self._source._marker
30 return self
32 def __exit__(self, exception_type, exception_val, trace):
33 # Exiting this context manager - restore the prior state
34 if self.restore or exception_type:
35 self._source._chars = self._chars
36 self._source._idx = self._idx
37 self._source._current = self._current
38 if self._save_marker:
39 self._source._marker = self._marker
42class _StateHandler:
43 """
44 State preserver for the Parser.
45 """
47 def __init__(self, source: "Source") -> None:
48 self._source = source
49 self._states = []
51 def __call__(self, *args, **kwargs):
52 return _State(self._source, *args, **kwargs)
54 def __enter__(self) -> None:
55 state = self()
56 self._states.append(state)
57 return state.__enter__()
59 def __exit__(self, exception_type, exception_val, trace):
60 state = self._states.pop()
61 return state.__exit__(exception_type, exception_val, trace)
64class Source(str):
65 EOF = TOMLChar("\0")
67 def __init__(self, _: str) -> None:
68 super().__init__()
70 # Collection of TOMLChars
71 self._chars = iter([(i, TOMLChar(c)) for i, c in enumerate(self)])
73 self._idx = 0
74 self._marker = 0
75 self._current = TOMLChar("")
77 self._state = _StateHandler(self)
79 self.inc()
81 def reset(self):
82 # initialize both idx and current
83 self.inc()
85 # reset marker
86 self.mark()
88 @property
89 def state(self) -> _StateHandler:
90 return self._state
92 @property
93 def idx(self) -> int:
94 return self._idx
96 @property
97 def current(self) -> TOMLChar:
98 return self._current
100 @property
101 def marker(self) -> int:
102 return self._marker
104 def extract(self) -> str:
105 """
106 Extracts the value between marker and index
107 """
108 return self[self._marker : self._idx]
110 def inc(self, exception: Optional[Type[ParseError]] = None) -> bool:
111 """
112 Increments the parser if the end of the input has not been reached.
113 Returns whether or not it was able to advance.
114 """
115 try:
116 self._idx, self._current = next(self._chars)
118 return True
119 except StopIteration:
120 self._idx = len(self)
121 self._current = self.EOF
122 if exception:
123 raise self.parse_error(exception)
125 return False
127 def inc_n(self, n: int, exception: Optional[Type[ParseError]] = None) -> bool:
128 """
129 Increments the parser by n characters
130 if the end of the input has not been reached.
131 """
132 return all(self.inc(exception=exception) for _ in range(n))
134 def consume(self, chars, min=0, max=-1):
135 """
136 Consume chars until min/max is satisfied is valid.
137 """
138 while self.current in chars and max != 0:
139 min -= 1
140 max -= 1
141 if not self.inc():
142 break
144 # failed to consume minimum number of characters
145 if min > 0:
146 raise self.parse_error(UnexpectedCharError, self.current)
148 def end(self) -> bool:
149 """
150 Returns True if the parser has reached the end of the input.
151 """
152 return self._current is self.EOF
154 def mark(self) -> None:
155 """
156 Sets the marker to the index's current position
157 """
158 self._marker = self._idx
160 def parse_error(
161 self,
162 exception: Type[ParseError] = ParseError,
163 *args: Any,
164 **kwargs: Any,
165 ) -> ParseError:
166 """
167 Creates a generic "parse error" at the current position.
168 """
169 line, col = self._to_linecol()
171 return exception(line, col, *args, **kwargs)
173 def _to_linecol(self) -> Tuple[int, int]:
174 cur = 0
175 for i, line in enumerate(self.splitlines()):
176 if cur + len(line) + 1 > self.idx:
177 return (i + 1, self.idx - cur)
179 cur += len(line) + 1
181 return len(self.splitlines()), 0