avrae-ls 0.4.1__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
draconic/string.py DELETED
@@ -1,100 +0,0 @@
1
- """String utilities for the userstring type."""
2
-
3
- import re
4
-
5
- from .exceptions import IterableTooLong, _raise_in_context
6
-
7
- __all__ = ("check_format_spec", "FORMAT_SPEC_RE", "PRINTF_TEMPLATE_RE", "JoinProxy", "TranslateTableProxy")
8
-
9
- # ==== format spec ====
10
- # .format()-style
11
- # https://docs.python.org/3/library/string.html#format-specification-mini-language
12
- _FILL = r"."
13
- _ALIGN = r"[<>=^]"
14
- _SIGN = r"[+\- ]"
15
- _WIDTH = r"\d+"
16
- _GROUPING_OPTION = r"[_,]"
17
- _PRECISION = r"\d+"
18
- _TYPE = r"[bcdeEfFgGnosxX%]"
19
- FORMAT_SPEC_RE = re.compile(
20
- rf"(?:(?P<fill>{_FILL})?(?P<align>{_ALIGN}))?"
21
- rf"(?P<sign>{_SIGN})?"
22
- rf"(?P<alt_form>#)?"
23
- rf"(?P<zero_pad>0)?"
24
- rf"(?P<width>{_WIDTH})?"
25
- rf"(?P<grouping_option>{_GROUPING_OPTION})?"
26
- rf"(?:\.(?P<precision>{_PRECISION}))?"
27
- rf"(?P<type>{_TYPE})?"
28
- )
29
-
30
-
31
- def check_format_spec(config, format_spec):
32
- # validate that the format string is safe
33
- match = FORMAT_SPEC_RE.match(format_spec)
34
- if not match:
35
- raise ValueError("Invalid format specifier")
36
-
37
- precision_len = 0
38
- w = match.group("width")
39
- p = match.group("precision")
40
- if w:
41
- precision_len += int(w)
42
- if p:
43
- precision_len += int(p)
44
- if precision_len > config.max_const_len:
45
- _raise_in_context(IterableTooLong, "This str is too large")
46
-
47
-
48
- # printf-style
49
- # https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
50
- _PF_MAPPING_KEY = r"[^)]*" # any sequence of non-")"
51
- _PF_CONVERSION_FLAGS = r"[#0\- +]"
52
- _PF_WIDTH = r"\*|\d+"
53
- _PF_PRECISION = r"\*|\d+"
54
- _PF_LENGTH_MODIFIER = r"[hlL]"
55
- _PF_TYPE = r"[diouxXeEfFgGcrsa%]"
56
- PRINTF_TEMPLATE_RE = re.compile(
57
- rf"%"
58
- rf"(?:\((?P<mapping_key>{_PF_MAPPING_KEY})\))?"
59
- rf"(?P<conversion_flags>{_PF_CONVERSION_FLAGS})*"
60
- rf"(?P<width>{_PF_WIDTH})?"
61
- rf"(?:\.(?P<precision>{_PF_PRECISION}))?"
62
- rf"(?P<length_modifier>{_PF_LENGTH_MODIFIER})?"
63
- rf"(?P<type>{_PF_TYPE})"
64
- )
65
-
66
-
67
- # ==== helpers ====
68
- class JoinProxy:
69
- """
70
- A helper to return the right types for str.join().
71
- If the sequence would return a userstring, returns it as a base str instead.
72
- """
73
-
74
- def __init__(self, str_type, seq):
75
- self._str_type = str_type
76
- self.seq = seq
77
-
78
- def __iter__(self):
79
- for item in self.seq:
80
- if isinstance(item, self._str_type):
81
- yield str(item)
82
- else:
83
- yield item
84
-
85
-
86
- class TranslateTableProxy:
87
- """
88
- A helper to return the right types for str.translate().
89
- If the table would return a userstring, returns it as a base str instead.
90
- """
91
-
92
- def __init__(self, str_type, table):
93
- self._str_type = str_type
94
- self.table = table
95
-
96
- def __getitem__(self, item):
97
- out = self.table[item]
98
- if isinstance(out, self._str_type): # translate() expects strictly a str type
99
- return str(out)
100
- return out
draconic/types.py DELETED
@@ -1,364 +0,0 @@
1
- import collections.abc
2
- import operator as op
3
- from collections import UserList, UserString
4
-
5
- from .exceptions import *
6
- from .string import JoinProxy, PRINTF_TEMPLATE_RE, TranslateTableProxy
7
- from .versions import PY_39
8
-
9
- __all__ = ("safe_list", "safe_dict", "safe_set", "safe_str", "approx_len_of")
10
-
11
- _sentinel = object()
12
-
13
-
14
- # ---- size helper ----
15
- def approx_len_of(obj, visited=None):
16
- """Gets the approximate size of an object (including recursive objects)."""
17
- if isinstance(obj, (str, bytes, UserString)):
18
- return len(obj)
19
-
20
- if hasattr(obj, "__approx_len__"):
21
- return obj.__approx_len__
22
-
23
- if visited is None:
24
- visited = [obj]
25
-
26
- size = op.length_hint(obj)
27
-
28
- if isinstance(obj, dict):
29
- obj = obj.items()
30
-
31
- try:
32
- obj_iter = iter(obj)
33
- except TypeError: # object is not iterable
34
- pass
35
- else:
36
- for child in obj_iter:
37
- if child in visited:
38
- continue
39
- size += approx_len_of(child, visited)
40
- visited.append(child)
41
-
42
- try:
43
- setattr(obj, "__approx_len__", size)
44
- except (AttributeError, TypeError):
45
- pass
46
-
47
- return size
48
-
49
-
50
- # ---- types ----
51
- # each function is a function that returns a class based on Draconic config
52
- # ... look, it works
53
- def safe_list(config):
54
- class SafeList(UserList): # extends UserList so that [x] * y returns a SafeList, not a list
55
- def __init__(self, *args, **kwargs):
56
- super().__init__(*args, **kwargs)
57
- self.__approx_len__ = approx_len_of(self)
58
-
59
- def append(self, obj):
60
- if approx_len_of(self) + 1 > config.max_const_len:
61
- _raise_in_context(IterableTooLong, "This list is too long")
62
- super().append(obj)
63
- self.__approx_len__ += 1
64
-
65
- def extend(self, iterable):
66
- other_len = approx_len_of(iterable)
67
- if approx_len_of(self) + other_len > config.max_const_len:
68
- _raise_in_context(IterableTooLong, "This list is too long")
69
- super().extend(iterable)
70
- self.__approx_len__ += other_len
71
-
72
- def pop(self, i=-1):
73
- retval = super().pop(i)
74
- self.__approx_len__ -= 1
75
- return retval
76
-
77
- def remove(self, item):
78
- super().remove(item)
79
- self.__approx_len__ -= 1
80
-
81
- def clear(self):
82
- super().clear()
83
- self.__approx_len__ = 0
84
-
85
- def __mul__(self, n):
86
- # to prevent the recalculation of the length on list mult we manually set a new instance's
87
- # data and approx len (JIRA-54)
88
- new = SafeList()
89
- new.data = self.data * n
90
- new.__approx_len__ = self.__approx_len__ * n
91
- return new
92
-
93
- return SafeList
94
-
95
-
96
- def safe_set(config):
97
- class SafeSet(set):
98
- def __init__(self, *args, **kwargs):
99
- super().__init__(*args, **kwargs)
100
- self.__approx_len__ = approx_len_of(self)
101
-
102
- def union(self, *s):
103
- if approx_len_of(self) + sum(approx_len_of(other) for other in s) > config.max_const_len:
104
- _raise_in_context(IterableTooLong, "This set is too large")
105
- return SafeSet(super().union(*s))
106
-
107
- def __or__(self, other):
108
- return self.union(other)
109
-
110
- def intersection(self, *s):
111
- if any(approx_len_of(other) > config.max_const_len for other in s):
112
- _raise_in_context(IterableTooLong, "This set is too large")
113
- return SafeSet(super().intersection(*s))
114
-
115
- def __and__(self, other):
116
- return self.intersection(other)
117
-
118
- def symmetric_difference(self, *s):
119
- if approx_len_of(self) + sum(approx_len_of(other) for other in s) > config.max_const_len:
120
- _raise_in_context(IterableTooLong, "This set is too large")
121
- return SafeSet(super().symmetric_difference(*s))
122
-
123
- def __xor__(self, other):
124
- return self.symmetric_difference(other)
125
-
126
- # difference not reimplemented as it cannot grow the set and has no cheap approximation for len
127
-
128
- def update(self, *s):
129
- other_lens = sum(approx_len_of(other) for other in s)
130
- if approx_len_of(self) + other_lens > config.max_const_len:
131
- _raise_in_context(IterableTooLong, "This set is too large")
132
- super().update(*s)
133
- self.__approx_len__ += other_lens
134
-
135
- def intersection_update(self, *s):
136
- if any(approx_len_of(other) > config.max_const_len for other in s):
137
- _raise_in_context(IterableTooLong, "This set is too large")
138
- super().intersection_update(*s)
139
- self.__approx_len__ = min(self.__approx_len__, *(approx_len_of(other) for other in s))
140
-
141
- def symmetric_difference_update(self, s):
142
- total_approx = approx_len_of(self) + approx_len_of(s)
143
- if total_approx > config.max_const_len:
144
- _raise_in_context(IterableTooLong, "This set is too large")
145
- super().symmetric_difference_update(s)
146
- self.__approx_len__ = total_approx
147
-
148
- # difference_update not reimplemented as it cannot grow the set and has no cheap approximation for len
149
-
150
- def add(self, element):
151
- if approx_len_of(self) + 1 > config.max_const_len:
152
- _raise_in_context(IterableTooLong, "This set is too large")
153
- super().add(element)
154
- self.__approx_len__ += 1
155
-
156
- def pop(self):
157
- retval = super().pop()
158
- self.__approx_len__ -= 1
159
- return retval
160
-
161
- def remove(self, element):
162
- super().remove(element)
163
- self.__approx_len__ -= 1
164
-
165
- # discard not reimplemented as the discarded element may not be a member
166
-
167
- def clear(self):
168
- super().clear()
169
- self.__approx_len__ = 0
170
-
171
- return SafeSet
172
-
173
-
174
- def safe_dict(config):
175
- class SafeDict(dict):
176
- def __init__(self, *args, **kwargs):
177
- super().__init__(*args, **kwargs)
178
- self.__approx_len__ = approx_len_of(self)
179
-
180
- def update(self, other_dict=None, **kvs):
181
- if other_dict is None:
182
- other_dict = {}
183
-
184
- other_lens = approx_len_of(other_dict) + approx_len_of(kvs)
185
- if approx_len_of(self) + other_lens > config.max_const_len:
186
- _raise_in_context(IterableTooLong, "This dict is too large")
187
-
188
- super().update(other_dict, **kvs)
189
- self.__approx_len__ += other_lens
190
-
191
- def __setitem__(self, key, value):
192
- other_len = approx_len_of(value)
193
- if approx_len_of(self) + other_len > config.max_const_len:
194
- _raise_in_context(IterableTooLong, "This dict is too large")
195
- self.__approx_len__ += other_len
196
- return super().__setitem__(key, value)
197
-
198
- def pop(self, k, default=_sentinel):
199
- if default is not _sentinel:
200
- retval = super().pop(k, default)
201
- else:
202
- retval = super().pop(k)
203
- self.__approx_len__ -= 1
204
- return retval
205
-
206
- def __delitem__(self, key):
207
- super().__delitem__(key)
208
- self.__approx_len__ -= 1
209
-
210
- def __getattr__(self, attr):
211
- try:
212
- return self[attr]
213
- except KeyError:
214
- raise AttributeError
215
-
216
- if PY_39:
217
-
218
- def __or__(self, other):
219
- if approx_len_of(self) + approx_len_of(other) > config.max_const_len:
220
- _raise_in_context(IterableTooLong, "This dict is too large")
221
-
222
- return SafeDict(super().__or__(other))
223
-
224
- return SafeDict
225
-
226
-
227
- _real_str = str
228
-
229
-
230
- def safe_str(config):
231
- # noinspection PyShadowingBuiltins, PyPep8Naming
232
- # naming it SafeStr would break typeof backward compatibility :(
233
- class str(UserString, _real_str):
234
- def __init__(self, seq):
235
- if isinstance(seq, UserString):
236
- self.data = seq.data[:]
237
- elif isinstance(seq, _real_str):
238
- self.data = seq
239
- else:
240
- self.data = _real_str(seq)
241
-
242
- def center(self, width, *args):
243
- if width > config.max_const_len:
244
- _raise_in_context(IterableTooLong, "This str is too large")
245
- return super().center(width, *args)
246
-
247
- def encode(self, *_, **__):
248
- _raise_in_context(FeatureNotAvailable, "This method is not allowed")
249
-
250
- def expandtabs(self, tabsize=8):
251
- if self.count("\t") * tabsize > config.max_const_len:
252
- _raise_in_context(IterableTooLong, "This str is too large")
253
- return super().expandtabs(tabsize)
254
-
255
- def format(self, *args, **kwargs):
256
- _raise_in_context(FeatureNotAvailable, "This method is not allowed")
257
-
258
- def format_map(self, mapping):
259
- _raise_in_context(FeatureNotAvailable, "This method is not allowed")
260
-
261
- def join(self, seq):
262
- full_seq = list(seq) # consume the entire iterator so we can do length checking
263
- i = JoinProxy(self.__class__, full_seq) # proxy it so that .join gets the right types
264
- if len(full_seq) * len(self) + approx_len_of(full_seq) > config.max_const_len:
265
- _raise_in_context(IterableTooLong, "This str is too large")
266
- return super().join(i)
267
-
268
- def ljust(self, width, *args):
269
- if width > config.max_const_len:
270
- _raise_in_context(IterableTooLong, "This str is too large")
271
- return super().ljust(width, *args)
272
-
273
- @staticmethod
274
- def maketrans(*args):
275
- if len(args) == 1 and isinstance(args[0], dict):
276
- # str.maketrans expects a dict object and nothing else
277
- # So SafeDict needs to be cast to dict for the method to work
278
- return _real_str.maketrans(dict(args[0]))
279
-
280
- if sum(approx_len_of(a) for a in args) > config.max_const_len:
281
- _raise_in_context(IterableTooLong, "This dict is too large")
282
-
283
- return _real_str.maketrans(*args)
284
-
285
- def replace(self, old, new, maxsplit=-1):
286
- if maxsplit > 0:
287
- n = maxsplit
288
- else:
289
- n = self.count(old)
290
- if n * (len(new) - len(old)) + len(self) > config.max_const_len:
291
- _raise_in_context(IterableTooLong, "This str is too large")
292
- return super().replace(old, new, maxsplit)
293
-
294
- def rjust(self, width, *args):
295
- if width > config.max_const_len:
296
- _raise_in_context(IterableTooLong, "This str is too large")
297
- return super().rjust(width, *args)
298
-
299
- def translate(self, table):
300
- # this is kind of a disgusting way to check the worst-case length
301
- # and is an overestimate by a multiplicative factor of len(table)
302
- # but it is certainly an overestimate
303
- if approx_len_of(table) * len(self) > config.max_const_len:
304
- _raise_in_context(IterableTooLong, "This str is too large")
305
- table_proxy = TranslateTableProxy(self.__class__, table)
306
- return super().translate(table_proxy)
307
-
308
- def zfill(self, width):
309
- if width > config.max_const_len:
310
- _raise_in_context(IterableTooLong, "This str is too large")
311
- return super().zfill(width)
312
-
313
- def __format__(self, format_spec):
314
- # format it using default str formatter
315
- return self.data.__format__(format_spec)
316
-
317
- def __mod__(self, values):
318
- new_len_bound = len(self)
319
- values_is_sequence = isinstance(values, collections.abc.Sequence)
320
- values_is_mapping = isinstance(values, collections.abc.Mapping)
321
-
322
- # validate that the template is safe (no massive widths/precisions)
323
- i = 0
324
- for match in PRINTF_TEMPLATE_RE.finditer(self.data):
325
- w = match.group("width")
326
- if w:
327
- if w == "*":
328
- _raise_in_context(FeatureNotAvailable, "Star precision in printf-style formatting not allowed")
329
- else:
330
- new_len_bound += int(w)
331
-
332
- p = match.group("precision")
333
- if p:
334
- if p == "*":
335
- _raise_in_context(FeatureNotAvailable, "Star precision in printf-style formatting not allowed")
336
- else:
337
- new_len_bound += int(p)
338
-
339
- mapping_key = match.group("mapping_key")
340
- if mapping_key is not None: # '%(foo)s %(foo)s'
341
- if not values_is_mapping: # '%(foo)s' % 0
342
- raise TypeError("format requires a mapping")
343
- val = values[mapping_key]
344
- new_len_bound += approx_len_of(val)
345
- elif values_is_sequence: # '%s %s'
346
- try:
347
- val = values[i]
348
- except IndexError: # '%s %s' % [0]
349
- raise TypeError("not enough arguments for format string")
350
- new_len_bound += approx_len_of(val)
351
- elif i > 0: # '%s %s' % 0
352
- raise TypeError("not enough arguments for format string")
353
- else: # '%s' % 0
354
- new_len_bound += approx_len_of(values)
355
-
356
- if match.group("type") != "%": # percent literals do not increase index
357
- i += 1
358
-
359
- if new_len_bound > config.max_const_len:
360
- _raise_in_context(IterableTooLong, "This str is too large")
361
-
362
- return _real_str.__mod__(self.data, values)
363
-
364
- return str
draconic/utils.py DELETED
@@ -1,78 +0,0 @@
1
- """
2
- This submodule contains helpful public utilities.
3
- """
4
-
5
- import textwrap
6
- from collections import namedtuple
7
- from typing import List, Union
8
-
9
- from .exceptions import AnnotatedException, DraconicException, DraconicSyntaxError, InvalidExpression, NestedException
10
-
11
- LineInfo = namedtuple("LineInfo", "lineno col_offset end_lineno end_col_offset")
12
-
13
-
14
- def format_traceback(exc: DraconicException) -> List[str]:
15
- """
16
- Given an exception raised by this library during execution of a userscript, provide a ``traceback``-like format
17
- of the call stack leading to the exception.
18
- """
19
- tb = ["Traceback (most recent call last):\n"]
20
-
21
- # show the call stack with pointers
22
- while isinstance(exc, NestedException):
23
- if exc.__drac_context__ is not None:
24
- tb.append(f" Line {exc.node.lineno}, col {exc.node.col_offset}, in {exc.__drac_context__}\n")
25
- else:
26
- tb.append(f" Line {exc.node.lineno}, col {exc.node.col_offset}\n")
27
- tb.append(textwrap.indent(format_exc_line_pointer(exc.node, exc.expr), " "))
28
-
29
- exc = exc.last_exc
30
-
31
- # show the pointer to the original (draconic) exception
32
- in_func = f", in {exc.__drac_context__}" if exc.__drac_context__ is not None else ""
33
- if isinstance(exc, InvalidExpression):
34
- tb.append(f" Line {exc.node.lineno}, col {exc.node.col_offset}{in_func}\n")
35
- tb.append(textwrap.indent(format_exc_line_pointer(extract_line_info(exc), exc.expr), " "))
36
- elif isinstance(exc, DraconicSyntaxError):
37
- tb.append(f" Line {exc.lineno}, col {exc.offset}{in_func}\n")
38
- tb.append(textwrap.indent(format_exc_line_pointer(extract_line_info(exc), exc.expr), " "))
39
- else: # pragma: no cover # generic fallback, should never be hit
40
- tb.append(f" While parsing expression{in_func}\n")
41
-
42
- # show the original exception message
43
- if isinstance(exc, AnnotatedException):
44
- exc = exc.original
45
- tb.append(f"{type(exc).__name__}: {exc!s}\n")
46
- return tb
47
-
48
-
49
- def extract_line_info(exc: Union[InvalidExpression, DraconicSyntaxError]):
50
- if isinstance(exc, InvalidExpression):
51
- return LineInfo(
52
- exc.node.lineno,
53
- exc.node.col_offset,
54
- getattr(exc.node, "end_lineno", None),
55
- getattr(exc.node, "end_col_offset", None),
56
- )
57
- return LineInfo(
58
- exc.lineno,
59
- exc.offset - 1,
60
- exc.end_lineno,
61
- (exc.end_offset - 1) if exc.end_offset is not None else None,
62
- )
63
-
64
-
65
- def format_exc_line_pointer(line_info: LineInfo, expr: str) -> str:
66
- the_line = expr.split("\n")[line_info.lineno - 1]
67
-
68
- # if the error spans multiple lines just point to the start
69
- if line_info.end_lineno is not None and line_info.end_lineno - line_info.lineno:
70
- return f"{the_line}\n{' ' * line_info.col_offset}^\n"
71
-
72
- # otherwise, if we have end_col_offset info, we need more than 1 carat
73
- if line_info.end_col_offset is not None:
74
- carats = "^" * (line_info.end_col_offset - line_info.col_offset)
75
- else:
76
- carats = "^"
77
-
78
- return textwrap.dedent(f"{the_line}\n{' ' * line_info.col_offset}{carats}\n")
draconic/versions.py DELETED
@@ -1,4 +0,0 @@
1
- import sys
2
-
3
- PY_310 = sys.version_info >= (3, 10)
4
- PY_39 = sys.version_info >= (3, 9)