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.
- {avrae_ls-0.4.1.dist-info → avrae_ls-0.5.0.dist-info}/METADATA +31 -11
- avrae_ls-0.5.0.dist-info/RECORD +6 -0
- {avrae_ls-0.4.1.dist-info → avrae_ls-0.5.0.dist-info}/WHEEL +1 -2
- avrae_ls/__init__.py +0 -3
- avrae_ls/__main__.py +0 -108
- avrae_ls/alias_preview.py +0 -346
- avrae_ls/api.py +0 -2014
- avrae_ls/argparser.py +0 -430
- avrae_ls/argument_parsing.py +0 -67
- avrae_ls/code_actions.py +0 -282
- avrae_ls/codes.py +0 -3
- avrae_ls/completions.py +0 -1391
- avrae_ls/config.py +0 -496
- avrae_ls/context.py +0 -229
- avrae_ls/cvars.py +0 -115
- avrae_ls/diagnostics.py +0 -751
- avrae_ls/dice.py +0 -33
- avrae_ls/parser.py +0 -44
- avrae_ls/runtime.py +0 -661
- avrae_ls/server.py +0 -399
- avrae_ls/signature_help.py +0 -252
- avrae_ls/symbols.py +0 -266
- avrae_ls-0.4.1.dist-info/RECORD +0 -34
- avrae_ls-0.4.1.dist-info/top_level.txt +0 -2
- draconic/__init__.py +0 -4
- draconic/exceptions.py +0 -157
- draconic/helpers.py +0 -236
- draconic/interpreter.py +0 -1091
- draconic/string.py +0 -100
- draconic/types.py +0 -364
- draconic/utils.py +0 -78
- draconic/versions.py +0 -4
- {avrae_ls-0.4.1.dist-info → avrae_ls-0.5.0.dist-info}/entry_points.txt +0 -0
- {avrae_ls-0.4.1.dist-info → avrae_ls-0.5.0.dist-info}/licenses/LICENSE +0 -0
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
|
File without changes
|
|
File without changes
|