backrefs 6.2__tar.gz → 8.0__tar.gz
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.
- {backrefs-6.2 → backrefs-8.0}/PKG-INFO +2 -3
- {backrefs-6.2 → backrefs-8.0}/backrefs/__meta__.py +1 -1
- {backrefs-6.2 → backrefs-8.0}/backrefs/_bre_parse.py +70 -132
- {backrefs-6.2 → backrefs-8.0}/backrefs/_bregex_parse.py +72 -120
- {backrefs-6.2 → backrefs-8.0}/backrefs/bre.py +3 -2
- {backrefs-6.2 → backrefs-8.0}/backrefs/bregex.py +2 -1
- {backrefs-6.2 → backrefs-8.0}/backrefs/uniprops/__init__.py +163 -90
- {backrefs-6.2 → backrefs-8.0}/backrefs/util.py +4 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/about/changelog.md +30 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/refs.md +12 -12
- {backrefs-6.2 → backrefs-8.0}/hatch_build.py +0 -1
- {backrefs-6.2 → backrefs-8.0}/pyproject.toml +3 -3
- {backrefs-6.2 → backrefs-8.0}/tests/test_age.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_bidiclass.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_bidipairedbrackettype.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_binary.py +22 -20
- {backrefs-6.2 → backrefs-8.0}/tests/test_block.py +38 -34
- {backrefs-6.2 → backrefs-8.0}/tests/test_bre.py +109 -135
- {backrefs-6.2 → backrefs-8.0}/tests/test_bregex.py +73 -96
- {backrefs-6.2 → backrefs-8.0}/tests/test_canonicalcombiningclass.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_decompositiontype.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_eastasianwidth.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_generalcategory.py +20 -18
- {backrefs-6.2 → backrefs-8.0}/tests/test_graphemeclusterbreak.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_hangulsyllabletype.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_indicpositionalcategory.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_indicsylabiccategory.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_joininggroup.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_joiningtype.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_linebreak.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_numerictype.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_numericvalue.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_quickcheck.py +59 -57
- {backrefs-6.2 → backrefs-8.0}/tests/test_script.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_scriptextensions.py +21 -19
- {backrefs-6.2 → backrefs-8.0}/tests/test_sentencebreak.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_verticalorientation.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tests/test_wordbreak.py +17 -15
- {backrefs-6.2 → backrefs-8.0}/tools/unipropgen.py +6 -4
- {backrefs-6.2 → backrefs-8.0}/.coveragerc +0 -0
- {backrefs-6.2 → backrefs-8.0}/.gitignore +0 -0
- {backrefs-6.2 → backrefs-8.0}/.pyspelling.yml +0 -0
- {backrefs-6.2 → backrefs-8.0}/LICENSE.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/README.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/backrefs/__init__.py +0 -0
- {backrefs-6.2 → backrefs-8.0}/backrefs/_bregex_typing.py +0 -0
- {backrefs-6.2 → backrefs-8.0}/backrefs/py.typed +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/.snippets/links.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/about/contributing.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/about/license.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/about/security.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/index.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/src/markdown/usage.md +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/theme/announce.html +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/theme/assets/pymdownx-extras/extra-95634471d6.css +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/theme/assets/pymdownx-extras/extra-loader-Ccztcqfq.js +0 -0
- {backrefs-6.2 → backrefs-8.0}/docs/theme/main.html +0 -0
- {backrefs-6.2 → backrefs-8.0}/requirements/docs.txt +0 -0
- {backrefs-6.2 → backrefs-8.0}/requirements/lint.txt +0 -0
- {backrefs-6.2 → backrefs-8.0}/requirements/test.txt +0 -0
- {backrefs-6.2 → backrefs-8.0}/tests/__init__.py +0 -0
- {backrefs-6.2 → backrefs-8.0}/tests/test_uniprops.py +0 -0
- {backrefs-6.2 → backrefs-8.0}/tests/test_versions.py +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/__init__.py +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unicodedata/13.0.0.zip +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unicodedata/14.0.0.zip +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unicodedata/15.0.0.zip +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unicodedata/15.1.0.zip +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unicodedata/16.0.0.zip +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unicodedata/17.0.0.zip +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unicodedata/LICENSE +0 -0
- {backrefs-6.2 → backrefs-8.0}/tools/unidatadownload.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backrefs
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.0
|
|
4
4
|
Summary: A wrapper around re and regex that adds additional back references.
|
|
5
5
|
Project-URL: Homepage, https://github.com/facelessuser/backrefs
|
|
6
6
|
Author-email: Isaac Muse <Isaac.Muse@gmail.com>
|
|
@@ -13,7 +13,6 @@ Classifier: Intended Audience :: Developers
|
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -21,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.14
|
|
22
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
22
|
Classifier: Typing :: Typed
|
|
24
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.10
|
|
25
24
|
Provides-Extra: extras
|
|
26
25
|
Requires-Dist: regex; extra == 'extras'
|
|
27
26
|
Description-Content-Type: text/markdown
|
|
@@ -193,5 +193,5 @@ def parse_version(ver: str) -> Version:
|
|
|
193
193
|
return Version(major, minor, micro, release, pre, post, dev)
|
|
194
194
|
|
|
195
195
|
|
|
196
|
-
__version_info__ = Version(
|
|
196
|
+
__version_info__ = Version(8, 0, 0, "final")
|
|
197
197
|
__version__ = __version_info__._get_canonical()
|
|
@@ -9,6 +9,7 @@ import re as _re
|
|
|
9
9
|
import sys
|
|
10
10
|
import copyreg as _copyreg
|
|
11
11
|
from . import util as _util
|
|
12
|
+
from .util import PatternError
|
|
12
13
|
import unicodedata as _unicodedata
|
|
13
14
|
from . import uniprops as _uniprops
|
|
14
15
|
from typing import Generic, AnyStr, Match, Any, Pattern, cast
|
|
@@ -18,7 +19,7 @@ if sys.version_info >= (3, 11):
|
|
|
18
19
|
else:
|
|
19
20
|
import sre_parse as _parser
|
|
20
21
|
|
|
21
|
-
__all__ = ("ReplaceTemplate",)
|
|
22
|
+
__all__ = ("ReplaceTemplate", "PatternError")
|
|
22
23
|
|
|
23
24
|
_ASCII_LETTERS = frozenset(
|
|
24
25
|
(
|
|
@@ -37,8 +38,8 @@ _STANDARD_ESCAPES = frozenset(('a', 'b', 'f', 'n', 'r', 't', 'v'))
|
|
|
37
38
|
_CURLY_BRACKETS = frozenset(('{', '}'))
|
|
38
39
|
_PROPERTY_STRIP = frozenset((' ', '-', '_'))
|
|
39
40
|
_PROPERTY = _WORD | _DIGIT | _PROPERTY_STRIP
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
_SCOPED_FLAGS = frozenset(('a', 'i', 'L', 'm', 'u', 's', 'x'))
|
|
42
|
+
_SCOPED_INV_FLAGS = frozenset(('i', 'm', 's', 'x'))
|
|
42
43
|
_SCOPED_END = frozenset((':', ')'))
|
|
43
44
|
|
|
44
45
|
_CURLY_BRACKETS_ORD = frozenset((0x7b, 0x7d))
|
|
@@ -69,19 +70,10 @@ _BACK_SLASH_TRANSLATION = {
|
|
|
69
70
|
_FMT_CONV_TYPE = ('a', 'r', 's')
|
|
70
71
|
|
|
71
72
|
|
|
72
|
-
class LoopException(Exception):
|
|
73
|
-
"""Loop exception."""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class GlobalRetryException(Exception):
|
|
77
|
-
"""Global retry exception."""
|
|
78
|
-
|
|
79
|
-
|
|
80
73
|
class _SearchParser(Generic[AnyStr]):
|
|
81
74
|
"""Search Template."""
|
|
82
75
|
|
|
83
76
|
_new_refs = ("c", "C", "e", "E", "h", "l", "L", "m", "M", "N", "p", "P", "Q", "R", "X")
|
|
84
|
-
_re_escape = r"\x1b"
|
|
85
77
|
_re_start_wb = r"\b(?=\w)"
|
|
86
78
|
_re_end_wb = r"\b(?<=\w)"
|
|
87
79
|
_line_break = r'(?:\r\n|(?!\r\n)[\n\v\f\r\x85\u2028\u2029])'
|
|
@@ -90,8 +82,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
90
82
|
|
|
91
83
|
verbose: bool
|
|
92
84
|
unicode: bool
|
|
93
|
-
global_flag_swap: dict[str, bool]
|
|
94
|
-
temp_global_flag_swap: dict[str, bool]
|
|
95
85
|
ascii: bool # noqa: A003
|
|
96
86
|
is_bytes: bool
|
|
97
87
|
search: AnyStr
|
|
@@ -178,34 +168,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
178
168
|
current.append(t)
|
|
179
169
|
return current
|
|
180
170
|
|
|
181
|
-
def flags(self, text: str, scoped: bool = False) -> None:
|
|
182
|
-
"""Analyze flags."""
|
|
183
|
-
|
|
184
|
-
flags = text.split('-')
|
|
185
|
-
enable = flags[0]
|
|
186
|
-
disable = flags[1] if len(flags) > 1 else ''
|
|
187
|
-
|
|
188
|
-
global_retry = False
|
|
189
|
-
if ('a' in enable or 'L' in enable) and self.unicode:
|
|
190
|
-
self.unicode = False
|
|
191
|
-
if not scoped:
|
|
192
|
-
self.temp_global_flag_swap["unicode"] = True
|
|
193
|
-
global_retry = True
|
|
194
|
-
elif 'u' in enable and not self.unicode and not self.is_bytes:
|
|
195
|
-
self.unicode = True
|
|
196
|
-
if not scoped:
|
|
197
|
-
self.temp_global_flag_swap["unicode"] = True
|
|
198
|
-
global_retry = True
|
|
199
|
-
if 'x' in disable and self.verbose:
|
|
200
|
-
self.verbose = False
|
|
201
|
-
elif 'x' in enable and not self.verbose:
|
|
202
|
-
self.verbose = True
|
|
203
|
-
if not scoped:
|
|
204
|
-
self.temp_global_flag_swap["verbose"] = True
|
|
205
|
-
global_retry = True
|
|
206
|
-
if global_retry:
|
|
207
|
-
raise GlobalRetryException('Global Retry')
|
|
208
|
-
|
|
209
171
|
def get_unicode_property(self, i: _util.StringIter, brackets: bool = False) -> tuple[str, str]:
|
|
210
172
|
"""Get Unicode property."""
|
|
211
173
|
|
|
@@ -217,7 +179,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
217
179
|
if c.upper() in _ASCII_LETTERS:
|
|
218
180
|
prop.append(c)
|
|
219
181
|
elif (not brackets and c != '{') or (brackets and c != ':'):
|
|
220
|
-
raise
|
|
182
|
+
raise PatternError(f"Unicode property missing '{{' at {i.index - 1}!")
|
|
221
183
|
else:
|
|
222
184
|
c = next(i)
|
|
223
185
|
if c == '^':
|
|
@@ -226,7 +188,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
226
188
|
|
|
227
189
|
while c not in (':', '=', '}'):
|
|
228
190
|
if c not in _PROPERTY:
|
|
229
|
-
raise
|
|
191
|
+
raise PatternError(f'Invalid Unicode property character at {i.index - 1}!')
|
|
230
192
|
if c not in _PROPERTY_STRIP:
|
|
231
193
|
prop.append(c)
|
|
232
194
|
c = next(i)
|
|
@@ -248,22 +210,22 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
248
210
|
if not skip:
|
|
249
211
|
while c != end:
|
|
250
212
|
if c not in _PROPERTY:
|
|
251
|
-
raise
|
|
213
|
+
raise PatternError(f'Invalid Unicode property character at {i.index - 1}!')
|
|
252
214
|
if c not in _PROPERTY_STRIP:
|
|
253
215
|
value.append(c)
|
|
254
216
|
c = next(i)
|
|
255
217
|
if brackets and c == ':':
|
|
256
218
|
c = next(i)
|
|
257
219
|
if c != ']':
|
|
258
|
-
raise
|
|
220
|
+
raise PatternError(f'Invalid Unicode property character at {i.index - 1}!')
|
|
259
221
|
if not value:
|
|
260
|
-
raise
|
|
222
|
+
raise PatternError('Invalid Unicode property!')
|
|
261
223
|
|
|
262
224
|
except StopIteration as e:
|
|
263
225
|
if brackets:
|
|
264
|
-
raise
|
|
226
|
+
raise PatternError(f"Missing or unmatched ':]' at {index}!") from e
|
|
265
227
|
else:
|
|
266
|
-
raise
|
|
228
|
+
raise PatternError(f"Missing or unmatched '{{' at {index}!") from e
|
|
267
229
|
|
|
268
230
|
p = ''.join(prop).lower()
|
|
269
231
|
v = ''.join(value).lower()
|
|
@@ -282,13 +244,13 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
282
244
|
value = []
|
|
283
245
|
try:
|
|
284
246
|
if next(i) != '{':
|
|
285
|
-
raise
|
|
247
|
+
raise PatternError(f"Named Unicode missing '{{' {i.index - 1}!")
|
|
286
248
|
c = next(i)
|
|
287
249
|
while c != '}':
|
|
288
250
|
value.append(c)
|
|
289
251
|
c = next(i)
|
|
290
252
|
except Exception as e:
|
|
291
|
-
raise
|
|
253
|
+
raise PatternError(f"Unmatched '{{' at {index}!") from e
|
|
292
254
|
|
|
293
255
|
return ''.join(value)
|
|
294
256
|
|
|
@@ -307,14 +269,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
307
269
|
no_mark = self.unicode_props("^m", None, in_group=False)[0]
|
|
308
270
|
mark = self.unicode_props("m", None, in_group=False)[0]
|
|
309
271
|
current.extend(self._grapheme_cluster.format(no_mark, mark, mark))
|
|
310
|
-
elif t == "e":
|
|
311
|
-
_util.warn_deprecated(R"The \e reference has been deprecated, please use \x1b instead")
|
|
312
|
-
current.append(self._re_escape)
|
|
313
|
-
elif t == "h":
|
|
314
|
-
_util.warn_deprecated(R"The \h reference has been deprecated, please use \p{Horiz_Space} instead")
|
|
315
|
-
current.extend(self.unicode_props('blank', None, in_group=in_group))
|
|
316
|
-
if in_group:
|
|
317
|
-
self.found_property = True
|
|
318
272
|
elif t == 'p':
|
|
319
273
|
prop = self.get_unicode_property(i)
|
|
320
274
|
current.extend(self.unicode_props(prop[0], prop[1], in_group=in_group))
|
|
@@ -361,10 +315,26 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
361
315
|
c = next(i)
|
|
362
316
|
value.append(c)
|
|
363
317
|
except StopIteration as e:
|
|
364
|
-
raise
|
|
318
|
+
raise PatternError(f"Unmatched '(' at {index - 1}!") from e
|
|
365
319
|
|
|
366
320
|
return ''.join(value)
|
|
367
321
|
|
|
322
|
+
def flags(self, text: str, scoped: bool = False) -> None:
|
|
323
|
+
"""Analyze flags."""
|
|
324
|
+
|
|
325
|
+
flags = text.split('-')
|
|
326
|
+
enable = flags[0]
|
|
327
|
+
disable = flags[1] if len(flags) > 1 else ''
|
|
328
|
+
|
|
329
|
+
if ('a' in enable or 'L' in enable) and self.unicode:
|
|
330
|
+
self.unicode = False
|
|
331
|
+
elif 'u' in enable and not self.unicode and not self.is_bytes:
|
|
332
|
+
self.unicode = True
|
|
333
|
+
if 'x' in disable and self.verbose:
|
|
334
|
+
self.verbose = False
|
|
335
|
+
elif 'x' in enable and not self.verbose:
|
|
336
|
+
self.verbose = True
|
|
337
|
+
|
|
368
338
|
def get_flags(self, i: _util.StringIter) -> tuple[str | None, bool]:
|
|
369
339
|
"""
|
|
370
340
|
Get flags.
|
|
@@ -379,7 +349,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
379
349
|
|
|
380
350
|
index = i.index
|
|
381
351
|
value = ['(']
|
|
382
|
-
toggle = False
|
|
383
352
|
smells_scoped = False
|
|
384
353
|
try:
|
|
385
354
|
c = next(i)
|
|
@@ -389,18 +358,18 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
389
358
|
value.append(c)
|
|
390
359
|
c = next(i)
|
|
391
360
|
while c not in _SCOPED_END:
|
|
392
|
-
if
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
361
|
+
if c == '-':
|
|
362
|
+
c2 = next(i)
|
|
363
|
+
if c2 not in _SCOPED_INV_FLAGS:
|
|
364
|
+
raise PatternError('Bad scope')
|
|
365
|
+
c += c2
|
|
396
366
|
smells_scoped = True
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
raise ValueError("Bad flag")
|
|
367
|
+
elif c not in _SCOPED_FLAGS:
|
|
368
|
+
raise PatternError("Bad flag")
|
|
400
369
|
value.append(c)
|
|
401
370
|
c = next(i)
|
|
402
371
|
if smells_scoped and c != ':':
|
|
403
|
-
raise
|
|
372
|
+
raise PatternError("Bad flag")
|
|
404
373
|
elif c == ':':
|
|
405
374
|
smells_scoped = True
|
|
406
375
|
value.append(c)
|
|
@@ -422,13 +391,16 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
422
391
|
|
|
423
392
|
verbose = self.verbose
|
|
424
393
|
unicode_flag = self.unicode
|
|
394
|
+
pos = i.index - 1
|
|
425
395
|
|
|
426
396
|
# (?flags:pattern) or (?flags)
|
|
427
397
|
flags, scoped = self.get_flags(i)
|
|
428
|
-
if flags
|
|
398
|
+
if flags and flags != '(?)':
|
|
429
399
|
t = flags
|
|
430
400
|
self.flags(flags[2:-1], scoped=scoped)
|
|
431
401
|
if not scoped:
|
|
402
|
+
if pos != 0:
|
|
403
|
+
raise PatternError(f'Global flags not at the start of the expression at position {pos}')
|
|
432
404
|
return [flags]
|
|
433
405
|
|
|
434
406
|
current = []
|
|
@@ -440,8 +412,8 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
440
412
|
current.extend(self.normal(t, i))
|
|
441
413
|
|
|
442
414
|
t = next(i)
|
|
443
|
-
except StopIteration:
|
|
444
|
-
|
|
415
|
+
except StopIteration as e:
|
|
416
|
+
raise PatternError(f"Missing ')', unterminated subpattern at position {pos}") from e
|
|
445
417
|
|
|
446
418
|
# Restore flags after group
|
|
447
419
|
self.verbose = verbose
|
|
@@ -517,11 +489,8 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
517
489
|
current.append(t)
|
|
518
490
|
pos += 1
|
|
519
491
|
t = next(i)
|
|
520
|
-
except StopIteration:
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if escaped:
|
|
524
|
-
current.append(t)
|
|
492
|
+
except StopIteration as e:
|
|
493
|
+
raise PatternError(f"Missing ']', unterminated character set at position {pos}") from e
|
|
525
494
|
|
|
526
495
|
# Handle properties that return an empty string.
|
|
527
496
|
# This will occur when a property's values exceed
|
|
@@ -532,7 +501,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
532
501
|
if temp == '[]':
|
|
533
502
|
# We specified some properties, but they are all
|
|
534
503
|
# out of reach. Therefore we can match nothing.
|
|
535
|
-
current = [
|
|
504
|
+
current = ['(?!)']
|
|
536
505
|
elif temp == '[^]':
|
|
537
506
|
current = [f'[{_uniprops.ASCII_RANGE if self.is_bytes else _uniprops.UNICODE_RANGE}]']
|
|
538
507
|
else:
|
|
@@ -567,7 +536,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
567
536
|
value = ord(_unicodedata.lookup(name))
|
|
568
537
|
if self.is_bytes and value > 0xFF:
|
|
569
538
|
if not in_group:
|
|
570
|
-
return [
|
|
539
|
+
return ['(?!)']
|
|
571
540
|
else:
|
|
572
541
|
return ['']
|
|
573
542
|
return [f'\\{value:03o}' if value <= 0xFF else chr(value)]
|
|
@@ -627,14 +596,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
627
596
|
|
|
628
597
|
self.verbose = bool(self.re_verbose)
|
|
629
598
|
self.unicode = bool(self.re_unicode)
|
|
630
|
-
self.global_flag_swap = {
|
|
631
|
-
"unicode": False,
|
|
632
|
-
"verbose": False
|
|
633
|
-
}
|
|
634
|
-
self.temp_global_flag_swap = {
|
|
635
|
-
"unicode": False,
|
|
636
|
-
"verbose": False
|
|
637
|
-
}
|
|
638
599
|
self.ascii = self.re_unicode is not None and not self.re_unicode
|
|
639
600
|
if not self.unicode and not self.ascii:
|
|
640
601
|
self.unicode = True
|
|
@@ -642,30 +603,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
642
603
|
new_pattern = []
|
|
643
604
|
i = _util.StringIter(self.process_quotes(search))
|
|
644
605
|
|
|
645
|
-
|
|
646
|
-
while retry:
|
|
647
|
-
retry = False
|
|
648
|
-
try:
|
|
649
|
-
new_pattern = self.main_group(i)
|
|
650
|
-
except GlobalRetryException as e:
|
|
651
|
-
# Prevent a loop of retry over and over for a pattern like ((?u)(?a))
|
|
652
|
-
# or (?-x:(?x))
|
|
653
|
-
if self.temp_global_flag_swap['unicode']:
|
|
654
|
-
if self.global_flag_swap['unicode']:
|
|
655
|
-
raise LoopException('Global unicode flag recursion.') from e
|
|
656
|
-
else:
|
|
657
|
-
self.global_flag_swap["unicode"] = True
|
|
658
|
-
if self.temp_global_flag_swap['verbose']:
|
|
659
|
-
if self.global_flag_swap['verbose']:
|
|
660
|
-
raise LoopException('Global verbose flag recursion.') from e
|
|
661
|
-
else:
|
|
662
|
-
self.global_flag_swap['verbose'] = True
|
|
663
|
-
self.temp_global_flag_swap = {
|
|
664
|
-
"unicode": False,
|
|
665
|
-
"verbose": False
|
|
666
|
-
}
|
|
667
|
-
i.rewind(i.index)
|
|
668
|
-
retry = True
|
|
606
|
+
new_pattern = self.main_group(i)
|
|
669
607
|
return "".join(new_pattern)
|
|
670
608
|
|
|
671
609
|
def parse(self) -> AnyStr:
|
|
@@ -771,7 +709,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
771
709
|
findex.append(c)
|
|
772
710
|
c = self.format_next(i)
|
|
773
711
|
except StopIteration as e:
|
|
774
|
-
raise
|
|
712
|
+
raise PatternError(f"Unmatched '[' at {sindex - 1}") from e
|
|
775
713
|
idx = self.parse_format_index(''.join(findex))
|
|
776
714
|
value.append((_util.FMT_INDEX, idx))
|
|
777
715
|
c = self.format_next(i)
|
|
@@ -787,7 +725,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
787
725
|
if c == '!':
|
|
788
726
|
c = self.format_next(i)
|
|
789
727
|
if c not in _FMT_CONV_TYPE:
|
|
790
|
-
raise
|
|
728
|
+
raise PatternError(f"Invalid conversion type at {i.index - 1}!")
|
|
791
729
|
value.append((_util.FMT_CONV, c))
|
|
792
730
|
c = self.format_next(i)
|
|
793
731
|
|
|
@@ -825,7 +763,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
825
763
|
fill = None
|
|
826
764
|
if fill is not None:
|
|
827
765
|
if c not in ('<', '>', '^'):
|
|
828
|
-
raise
|
|
766
|
+
raise PatternError(f'Invalid format spec char at {i.index - 1}!')
|
|
829
767
|
align = c
|
|
830
768
|
c = self.format_next(i)
|
|
831
769
|
|
|
@@ -834,7 +772,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
834
772
|
c = self.format_next(i)
|
|
835
773
|
|
|
836
774
|
if not align and len(width) and width[0] == '0':
|
|
837
|
-
raise
|
|
775
|
+
raise PatternError("'=' alignment is not supported!")
|
|
838
776
|
if align and not fill and len(width) and width[0] == '0':
|
|
839
777
|
fill = '0'
|
|
840
778
|
|
|
@@ -858,9 +796,9 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
858
796
|
)
|
|
859
797
|
|
|
860
798
|
if c != '}':
|
|
861
|
-
raise
|
|
799
|
+
raise PatternError(f"Unmatched '{{' at {index - 1}")
|
|
862
800
|
except StopIteration as e:
|
|
863
|
-
raise
|
|
801
|
+
raise PatternError(f"Unmatched '{{' at {index - 1}!") from e
|
|
864
802
|
|
|
865
803
|
return field, value
|
|
866
804
|
|
|
@@ -881,7 +819,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
881
819
|
self.get_single_stack()
|
|
882
820
|
self.result.append(t)
|
|
883
821
|
else:
|
|
884
|
-
raise
|
|
822
|
+
raise PatternError(f"Unmatched '}}' at {i.index - 2}!")
|
|
885
823
|
|
|
886
824
|
def get_octal(self, c: str, i: _util.StringIter) -> str | None:
|
|
887
825
|
"""Get octal."""
|
|
@@ -920,7 +858,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
920
858
|
value = int(text, 8)
|
|
921
859
|
if value > 0xFF and self.is_bytes:
|
|
922
860
|
# Re fails on octal greater than `0o377` or `0xFF`
|
|
923
|
-
raise
|
|
861
|
+
raise PatternError("Octal escape value outside of range 0-0o377!")
|
|
924
862
|
else:
|
|
925
863
|
single = self.get_single_stack()
|
|
926
864
|
if self.span_stack:
|
|
@@ -942,13 +880,13 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
942
880
|
value = []
|
|
943
881
|
try:
|
|
944
882
|
if next(i) != '{':
|
|
945
|
-
raise
|
|
883
|
+
raise PatternError(f"Named Unicode missing '{{' at {i.index - 1}!")
|
|
946
884
|
c = next(i)
|
|
947
885
|
while c != '}':
|
|
948
886
|
value.append(c)
|
|
949
887
|
c = next(i)
|
|
950
888
|
except StopIteration as e:
|
|
951
|
-
raise
|
|
889
|
+
raise PatternError(f"Unmatched '}}' at {index}!") from e
|
|
952
890
|
|
|
953
891
|
return ''.join(value)
|
|
954
892
|
|
|
@@ -978,20 +916,20 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
978
916
|
if c == '0':
|
|
979
917
|
value.append(c)
|
|
980
918
|
else: # pragma: no cover
|
|
981
|
-
raise
|
|
919
|
+
raise PatternError(f'Invalid wide Unicode character at {i.index - 1}!')
|
|
982
920
|
|
|
983
921
|
c = next(i)
|
|
984
922
|
if c in ('0', '1'):
|
|
985
923
|
value.append(c)
|
|
986
924
|
else: # pragma: no cover
|
|
987
|
-
raise
|
|
925
|
+
raise PatternError(f'Invalid wide Unicode character at {i.index - 1}!')
|
|
988
926
|
|
|
989
927
|
for _ in range(4):
|
|
990
928
|
c = next(i)
|
|
991
929
|
if c.lower() in _HEX:
|
|
992
930
|
value.append(c)
|
|
993
931
|
else: # pragma: no cover
|
|
994
|
-
raise
|
|
932
|
+
raise PatternError(f'Invalid wide Unicode character at {i.index - 1}!')
|
|
995
933
|
return ''.join(value)
|
|
996
934
|
|
|
997
935
|
def get_narrow_unicode(self, i: _util.StringIter) -> str:
|
|
@@ -1003,7 +941,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1003
941
|
if c.lower() in _HEX:
|
|
1004
942
|
value.append(c)
|
|
1005
943
|
else: # pragma: no cover
|
|
1006
|
-
raise
|
|
944
|
+
raise PatternError(f'Invalid Unicode character at {i.index - 1}!')
|
|
1007
945
|
return ''.join(value)
|
|
1008
946
|
|
|
1009
947
|
def parse_unicode(self, i: _util.StringIter, wide: bool = False) -> None:
|
|
@@ -1033,7 +971,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1033
971
|
if c.lower() in _HEX:
|
|
1034
972
|
value.append(c)
|
|
1035
973
|
else: # pragma: no cover
|
|
1036
|
-
raise
|
|
974
|
+
raise PatternError(f'Invalid byte character at {i.index - 1}!')
|
|
1037
975
|
return ''.join(value)
|
|
1038
976
|
|
|
1039
977
|
def parse_bytes(self, i: _util.StringIter) -> None:
|
|
@@ -1059,7 +997,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1059
997
|
try:
|
|
1060
998
|
c = next(i)
|
|
1061
999
|
if c != "<":
|
|
1062
|
-
raise
|
|
1000
|
+
raise PatternError(f"Group missing '<' at {i.index - 1}!")
|
|
1063
1001
|
value.append(c)
|
|
1064
1002
|
c = next(i)
|
|
1065
1003
|
if c in _DIGIT:
|
|
@@ -1079,9 +1017,9 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1079
1017
|
c = next(i)
|
|
1080
1018
|
value.append(c)
|
|
1081
1019
|
else:
|
|
1082
|
-
raise
|
|
1020
|
+
raise PatternError(f"Invalid group character at {i.index - 1}!")
|
|
1083
1021
|
except StopIteration as e:
|
|
1084
|
-
raise
|
|
1022
|
+
raise PatternError(f"Unmatched '<' at {index}!") from e
|
|
1085
1023
|
|
|
1086
1024
|
return ''.join(value)
|
|
1087
1025
|
|
|
@@ -1115,7 +1053,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1115
1053
|
o = int(octal, 8)
|
|
1116
1054
|
if o > 0xFF and self.is_bytes:
|
|
1117
1055
|
# Re fails on octal greater than `0o377` or `0xFF`
|
|
1118
|
-
raise
|
|
1056
|
+
raise PatternError("Octal escape value outside of range 0-0o377!")
|
|
1119
1057
|
value = chr(o)
|
|
1120
1058
|
elif t in _STANDARD_ESCAPES or t == '\\':
|
|
1121
1059
|
value = _BACK_SLASH_TRANSLATION['\\' + t]
|
|
@@ -1325,11 +1263,11 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1325
1263
|
text[0] = (_util.FMT_FIELD, field)
|
|
1326
1264
|
self.auto_index += 1
|
|
1327
1265
|
else:
|
|
1328
|
-
raise
|
|
1266
|
+
raise PatternError("Cannot switch to auto format during manual format!")
|
|
1329
1267
|
elif not self.manual and not self.auto:
|
|
1330
1268
|
self.manual = True
|
|
1331
1269
|
elif not self.manual:
|
|
1332
|
-
raise
|
|
1270
|
+
raise PatternError("Cannot switch to manual format during auto format!")
|
|
1333
1271
|
|
|
1334
1272
|
self.handle_group(field, tuple(text), True)
|
|
1335
1273
|
|