backrefs 7.0__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-7.0 → backrefs-8.0}/PKG-INFO +1 -1
- {backrefs-7.0 → backrefs-8.0}/backrefs/__meta__.py +1 -1
- {backrefs-7.0 → backrefs-8.0}/backrefs/_bre_parse.py +70 -123
- {backrefs-7.0 → backrefs-8.0}/backrefs/_bregex_parse.py +72 -116
- {backrefs-7.0 → backrefs-8.0}/backrefs/bre.py +3 -2
- {backrefs-7.0 → backrefs-8.0}/backrefs/bregex.py +2 -1
- {backrefs-7.0 → backrefs-8.0}/backrefs/util.py +4 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/changelog.md +21 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/refs.md +12 -1
- {backrefs-7.0 → backrefs-8.0}/pyproject.toml +1 -1
- {backrefs-7.0 → backrefs-8.0}/tests/test_bre.py +93 -100
- {backrefs-7.0 → backrefs-8.0}/tests/test_bregex.py +73 -81
- {backrefs-7.0 → backrefs-8.0}/.coveragerc +0 -0
- {backrefs-7.0 → backrefs-8.0}/.gitignore +0 -0
- {backrefs-7.0 → backrefs-8.0}/.pyspelling.yml +0 -0
- {backrefs-7.0 → backrefs-8.0}/LICENSE.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/README.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/backrefs/__init__.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/backrefs/_bregex_typing.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/backrefs/py.typed +0 -0
- {backrefs-7.0 → backrefs-8.0}/backrefs/uniprops/__init__.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/.snippets/links.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/contributing.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/license.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/security.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/index.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/usage.md +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/theme/announce.html +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/theme/assets/pymdownx-extras/extra-95634471d6.css +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/theme/assets/pymdownx-extras/extra-loader-Ccztcqfq.js +0 -0
- {backrefs-7.0 → backrefs-8.0}/docs/theme/main.html +0 -0
- {backrefs-7.0 → backrefs-8.0}/hatch_build.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/requirements/docs.txt +0 -0
- {backrefs-7.0 → backrefs-8.0}/requirements/lint.txt +0 -0
- {backrefs-7.0 → backrefs-8.0}/requirements/test.txt +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/__init__.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_age.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_bidiclass.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_bidipairedbrackettype.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_binary.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_block.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_canonicalcombiningclass.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_decompositiontype.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_eastasianwidth.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_generalcategory.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_graphemeclusterbreak.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_hangulsyllabletype.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_indicpositionalcategory.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_indicsylabiccategory.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_joininggroup.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_joiningtype.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_linebreak.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_numerictype.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_numericvalue.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_quickcheck.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_script.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_scriptextensions.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_sentencebreak.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_uniprops.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_versions.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_verticalorientation.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tests/test_wordbreak.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/__init__.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/13.0.0.zip +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/14.0.0.zip +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/15.0.0.zip +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/15.1.0.zip +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/16.0.0.zip +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/17.0.0.zip +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/LICENSE +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unidatadownload.py +0 -0
- {backrefs-7.0 → backrefs-8.0}/tools/unipropgen.py +0 -0
|
@@ -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,14 +70,6 @@ _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
|
|
|
@@ -89,8 +82,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
89
82
|
|
|
90
83
|
verbose: bool
|
|
91
84
|
unicode: bool
|
|
92
|
-
global_flag_swap: dict[str, bool]
|
|
93
|
-
temp_global_flag_swap: dict[str, bool]
|
|
94
85
|
ascii: bool # noqa: A003
|
|
95
86
|
is_bytes: bool
|
|
96
87
|
search: AnyStr
|
|
@@ -177,34 +168,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
177
168
|
current.append(t)
|
|
178
169
|
return current
|
|
179
170
|
|
|
180
|
-
def flags(self, text: str, scoped: bool = False) -> None:
|
|
181
|
-
"""Analyze flags."""
|
|
182
|
-
|
|
183
|
-
flags = text.split('-')
|
|
184
|
-
enable = flags[0]
|
|
185
|
-
disable = flags[1] if len(flags) > 1 else ''
|
|
186
|
-
|
|
187
|
-
global_retry = False
|
|
188
|
-
if ('a' in enable or 'L' in enable) and self.unicode:
|
|
189
|
-
self.unicode = False
|
|
190
|
-
if not scoped:
|
|
191
|
-
self.temp_global_flag_swap["unicode"] = True
|
|
192
|
-
global_retry = True
|
|
193
|
-
elif 'u' in enable and not self.unicode and not self.is_bytes:
|
|
194
|
-
self.unicode = True
|
|
195
|
-
if not scoped:
|
|
196
|
-
self.temp_global_flag_swap["unicode"] = True
|
|
197
|
-
global_retry = True
|
|
198
|
-
if 'x' in disable and self.verbose:
|
|
199
|
-
self.verbose = False
|
|
200
|
-
elif 'x' in enable and not self.verbose:
|
|
201
|
-
self.verbose = True
|
|
202
|
-
if not scoped:
|
|
203
|
-
self.temp_global_flag_swap["verbose"] = True
|
|
204
|
-
global_retry = True
|
|
205
|
-
if global_retry:
|
|
206
|
-
raise GlobalRetryException('Global Retry')
|
|
207
|
-
|
|
208
171
|
def get_unicode_property(self, i: _util.StringIter, brackets: bool = False) -> tuple[str, str]:
|
|
209
172
|
"""Get Unicode property."""
|
|
210
173
|
|
|
@@ -216,7 +179,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
216
179
|
if c.upper() in _ASCII_LETTERS:
|
|
217
180
|
prop.append(c)
|
|
218
181
|
elif (not brackets and c != '{') or (brackets and c != ':'):
|
|
219
|
-
raise
|
|
182
|
+
raise PatternError(f"Unicode property missing '{{' at {i.index - 1}!")
|
|
220
183
|
else:
|
|
221
184
|
c = next(i)
|
|
222
185
|
if c == '^':
|
|
@@ -225,7 +188,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
225
188
|
|
|
226
189
|
while c not in (':', '=', '}'):
|
|
227
190
|
if c not in _PROPERTY:
|
|
228
|
-
raise
|
|
191
|
+
raise PatternError(f'Invalid Unicode property character at {i.index - 1}!')
|
|
229
192
|
if c not in _PROPERTY_STRIP:
|
|
230
193
|
prop.append(c)
|
|
231
194
|
c = next(i)
|
|
@@ -247,22 +210,22 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
247
210
|
if not skip:
|
|
248
211
|
while c != end:
|
|
249
212
|
if c not in _PROPERTY:
|
|
250
|
-
raise
|
|
213
|
+
raise PatternError(f'Invalid Unicode property character at {i.index - 1}!')
|
|
251
214
|
if c not in _PROPERTY_STRIP:
|
|
252
215
|
value.append(c)
|
|
253
216
|
c = next(i)
|
|
254
217
|
if brackets and c == ':':
|
|
255
218
|
c = next(i)
|
|
256
219
|
if c != ']':
|
|
257
|
-
raise
|
|
220
|
+
raise PatternError(f'Invalid Unicode property character at {i.index - 1}!')
|
|
258
221
|
if not value:
|
|
259
|
-
raise
|
|
222
|
+
raise PatternError('Invalid Unicode property!')
|
|
260
223
|
|
|
261
224
|
except StopIteration as e:
|
|
262
225
|
if brackets:
|
|
263
|
-
raise
|
|
226
|
+
raise PatternError(f"Missing or unmatched ':]' at {index}!") from e
|
|
264
227
|
else:
|
|
265
|
-
raise
|
|
228
|
+
raise PatternError(f"Missing or unmatched '{{' at {index}!") from e
|
|
266
229
|
|
|
267
230
|
p = ''.join(prop).lower()
|
|
268
231
|
v = ''.join(value).lower()
|
|
@@ -281,13 +244,13 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
281
244
|
value = []
|
|
282
245
|
try:
|
|
283
246
|
if next(i) != '{':
|
|
284
|
-
raise
|
|
247
|
+
raise PatternError(f"Named Unicode missing '{{' {i.index - 1}!")
|
|
285
248
|
c = next(i)
|
|
286
249
|
while c != '}':
|
|
287
250
|
value.append(c)
|
|
288
251
|
c = next(i)
|
|
289
252
|
except Exception as e:
|
|
290
|
-
raise
|
|
253
|
+
raise PatternError(f"Unmatched '{{' at {index}!") from e
|
|
291
254
|
|
|
292
255
|
return ''.join(value)
|
|
293
256
|
|
|
@@ -352,10 +315,26 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
352
315
|
c = next(i)
|
|
353
316
|
value.append(c)
|
|
354
317
|
except StopIteration as e:
|
|
355
|
-
raise
|
|
318
|
+
raise PatternError(f"Unmatched '(' at {index - 1}!") from e
|
|
356
319
|
|
|
357
320
|
return ''.join(value)
|
|
358
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
|
+
|
|
359
338
|
def get_flags(self, i: _util.StringIter) -> tuple[str | None, bool]:
|
|
360
339
|
"""
|
|
361
340
|
Get flags.
|
|
@@ -370,7 +349,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
370
349
|
|
|
371
350
|
index = i.index
|
|
372
351
|
value = ['(']
|
|
373
|
-
toggle = False
|
|
374
352
|
smells_scoped = False
|
|
375
353
|
try:
|
|
376
354
|
c = next(i)
|
|
@@ -380,18 +358,18 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
380
358
|
value.append(c)
|
|
381
359
|
c = next(i)
|
|
382
360
|
while c not in _SCOPED_END:
|
|
383
|
-
if
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
361
|
+
if c == '-':
|
|
362
|
+
c2 = next(i)
|
|
363
|
+
if c2 not in _SCOPED_INV_FLAGS:
|
|
364
|
+
raise PatternError('Bad scope')
|
|
365
|
+
c += c2
|
|
387
366
|
smells_scoped = True
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
raise ValueError("Bad flag")
|
|
367
|
+
elif c not in _SCOPED_FLAGS:
|
|
368
|
+
raise PatternError("Bad flag")
|
|
391
369
|
value.append(c)
|
|
392
370
|
c = next(i)
|
|
393
371
|
if smells_scoped and c != ':':
|
|
394
|
-
raise
|
|
372
|
+
raise PatternError("Bad flag")
|
|
395
373
|
elif c == ':':
|
|
396
374
|
smells_scoped = True
|
|
397
375
|
value.append(c)
|
|
@@ -413,13 +391,16 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
413
391
|
|
|
414
392
|
verbose = self.verbose
|
|
415
393
|
unicode_flag = self.unicode
|
|
394
|
+
pos = i.index - 1
|
|
416
395
|
|
|
417
396
|
# (?flags:pattern) or (?flags)
|
|
418
397
|
flags, scoped = self.get_flags(i)
|
|
419
|
-
if flags
|
|
398
|
+
if flags and flags != '(?)':
|
|
420
399
|
t = flags
|
|
421
400
|
self.flags(flags[2:-1], scoped=scoped)
|
|
422
401
|
if not scoped:
|
|
402
|
+
if pos != 0:
|
|
403
|
+
raise PatternError(f'Global flags not at the start of the expression at position {pos}')
|
|
423
404
|
return [flags]
|
|
424
405
|
|
|
425
406
|
current = []
|
|
@@ -431,8 +412,8 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
431
412
|
current.extend(self.normal(t, i))
|
|
432
413
|
|
|
433
414
|
t = next(i)
|
|
434
|
-
except StopIteration:
|
|
435
|
-
|
|
415
|
+
except StopIteration as e:
|
|
416
|
+
raise PatternError(f"Missing ')', unterminated subpattern at position {pos}") from e
|
|
436
417
|
|
|
437
418
|
# Restore flags after group
|
|
438
419
|
self.verbose = verbose
|
|
@@ -508,11 +489,8 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
508
489
|
current.append(t)
|
|
509
490
|
pos += 1
|
|
510
491
|
t = next(i)
|
|
511
|
-
except StopIteration:
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if escaped:
|
|
515
|
-
current.append(t)
|
|
492
|
+
except StopIteration as e:
|
|
493
|
+
raise PatternError(f"Missing ']', unterminated character set at position {pos}") from e
|
|
516
494
|
|
|
517
495
|
# Handle properties that return an empty string.
|
|
518
496
|
# This will occur when a property's values exceed
|
|
@@ -523,7 +501,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
523
501
|
if temp == '[]':
|
|
524
502
|
# We specified some properties, but they are all
|
|
525
503
|
# out of reach. Therefore we can match nothing.
|
|
526
|
-
current = [
|
|
504
|
+
current = ['(?!)']
|
|
527
505
|
elif temp == '[^]':
|
|
528
506
|
current = [f'[{_uniprops.ASCII_RANGE if self.is_bytes else _uniprops.UNICODE_RANGE}]']
|
|
529
507
|
else:
|
|
@@ -558,7 +536,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
558
536
|
value = ord(_unicodedata.lookup(name))
|
|
559
537
|
if self.is_bytes and value > 0xFF:
|
|
560
538
|
if not in_group:
|
|
561
|
-
return [
|
|
539
|
+
return ['(?!)']
|
|
562
540
|
else:
|
|
563
541
|
return ['']
|
|
564
542
|
return [f'\\{value:03o}' if value <= 0xFF else chr(value)]
|
|
@@ -618,14 +596,6 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
618
596
|
|
|
619
597
|
self.verbose = bool(self.re_verbose)
|
|
620
598
|
self.unicode = bool(self.re_unicode)
|
|
621
|
-
self.global_flag_swap = {
|
|
622
|
-
"unicode": False,
|
|
623
|
-
"verbose": False
|
|
624
|
-
}
|
|
625
|
-
self.temp_global_flag_swap = {
|
|
626
|
-
"unicode": False,
|
|
627
|
-
"verbose": False
|
|
628
|
-
}
|
|
629
599
|
self.ascii = self.re_unicode is not None and not self.re_unicode
|
|
630
600
|
if not self.unicode and not self.ascii:
|
|
631
601
|
self.unicode = True
|
|
@@ -633,30 +603,7 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
633
603
|
new_pattern = []
|
|
634
604
|
i = _util.StringIter(self.process_quotes(search))
|
|
635
605
|
|
|
636
|
-
|
|
637
|
-
while retry:
|
|
638
|
-
retry = False
|
|
639
|
-
try:
|
|
640
|
-
new_pattern = self.main_group(i)
|
|
641
|
-
except GlobalRetryException as e:
|
|
642
|
-
# Prevent a loop of retry over and over for a pattern like ((?u)(?a))
|
|
643
|
-
# or (?-x:(?x))
|
|
644
|
-
if self.temp_global_flag_swap['unicode']:
|
|
645
|
-
if self.global_flag_swap['unicode']:
|
|
646
|
-
raise LoopException('Global unicode flag recursion.') from e
|
|
647
|
-
else:
|
|
648
|
-
self.global_flag_swap["unicode"] = True
|
|
649
|
-
if self.temp_global_flag_swap['verbose']:
|
|
650
|
-
if self.global_flag_swap['verbose']:
|
|
651
|
-
raise LoopException('Global verbose flag recursion.') from e
|
|
652
|
-
else:
|
|
653
|
-
self.global_flag_swap['verbose'] = True
|
|
654
|
-
self.temp_global_flag_swap = {
|
|
655
|
-
"unicode": False,
|
|
656
|
-
"verbose": False
|
|
657
|
-
}
|
|
658
|
-
i.rewind(i.index)
|
|
659
|
-
retry = True
|
|
606
|
+
new_pattern = self.main_group(i)
|
|
660
607
|
return "".join(new_pattern)
|
|
661
608
|
|
|
662
609
|
def parse(self) -> AnyStr:
|
|
@@ -762,7 +709,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
762
709
|
findex.append(c)
|
|
763
710
|
c = self.format_next(i)
|
|
764
711
|
except StopIteration as e:
|
|
765
|
-
raise
|
|
712
|
+
raise PatternError(f"Unmatched '[' at {sindex - 1}") from e
|
|
766
713
|
idx = self.parse_format_index(''.join(findex))
|
|
767
714
|
value.append((_util.FMT_INDEX, idx))
|
|
768
715
|
c = self.format_next(i)
|
|
@@ -778,7 +725,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
778
725
|
if c == '!':
|
|
779
726
|
c = self.format_next(i)
|
|
780
727
|
if c not in _FMT_CONV_TYPE:
|
|
781
|
-
raise
|
|
728
|
+
raise PatternError(f"Invalid conversion type at {i.index - 1}!")
|
|
782
729
|
value.append((_util.FMT_CONV, c))
|
|
783
730
|
c = self.format_next(i)
|
|
784
731
|
|
|
@@ -816,7 +763,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
816
763
|
fill = None
|
|
817
764
|
if fill is not None:
|
|
818
765
|
if c not in ('<', '>', '^'):
|
|
819
|
-
raise
|
|
766
|
+
raise PatternError(f'Invalid format spec char at {i.index - 1}!')
|
|
820
767
|
align = c
|
|
821
768
|
c = self.format_next(i)
|
|
822
769
|
|
|
@@ -825,7 +772,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
825
772
|
c = self.format_next(i)
|
|
826
773
|
|
|
827
774
|
if not align and len(width) and width[0] == '0':
|
|
828
|
-
raise
|
|
775
|
+
raise PatternError("'=' alignment is not supported!")
|
|
829
776
|
if align and not fill and len(width) and width[0] == '0':
|
|
830
777
|
fill = '0'
|
|
831
778
|
|
|
@@ -849,9 +796,9 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
849
796
|
)
|
|
850
797
|
|
|
851
798
|
if c != '}':
|
|
852
|
-
raise
|
|
799
|
+
raise PatternError(f"Unmatched '{{' at {index - 1}")
|
|
853
800
|
except StopIteration as e:
|
|
854
|
-
raise
|
|
801
|
+
raise PatternError(f"Unmatched '{{' at {index - 1}!") from e
|
|
855
802
|
|
|
856
803
|
return field, value
|
|
857
804
|
|
|
@@ -872,7 +819,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
872
819
|
self.get_single_stack()
|
|
873
820
|
self.result.append(t)
|
|
874
821
|
else:
|
|
875
|
-
raise
|
|
822
|
+
raise PatternError(f"Unmatched '}}' at {i.index - 2}!")
|
|
876
823
|
|
|
877
824
|
def get_octal(self, c: str, i: _util.StringIter) -> str | None:
|
|
878
825
|
"""Get octal."""
|
|
@@ -911,7 +858,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
911
858
|
value = int(text, 8)
|
|
912
859
|
if value > 0xFF and self.is_bytes:
|
|
913
860
|
# Re fails on octal greater than `0o377` or `0xFF`
|
|
914
|
-
raise
|
|
861
|
+
raise PatternError("Octal escape value outside of range 0-0o377!")
|
|
915
862
|
else:
|
|
916
863
|
single = self.get_single_stack()
|
|
917
864
|
if self.span_stack:
|
|
@@ -933,13 +880,13 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
933
880
|
value = []
|
|
934
881
|
try:
|
|
935
882
|
if next(i) != '{':
|
|
936
|
-
raise
|
|
883
|
+
raise PatternError(f"Named Unicode missing '{{' at {i.index - 1}!")
|
|
937
884
|
c = next(i)
|
|
938
885
|
while c != '}':
|
|
939
886
|
value.append(c)
|
|
940
887
|
c = next(i)
|
|
941
888
|
except StopIteration as e:
|
|
942
|
-
raise
|
|
889
|
+
raise PatternError(f"Unmatched '}}' at {index}!") from e
|
|
943
890
|
|
|
944
891
|
return ''.join(value)
|
|
945
892
|
|
|
@@ -969,20 +916,20 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
969
916
|
if c == '0':
|
|
970
917
|
value.append(c)
|
|
971
918
|
else: # pragma: no cover
|
|
972
|
-
raise
|
|
919
|
+
raise PatternError(f'Invalid wide Unicode character at {i.index - 1}!')
|
|
973
920
|
|
|
974
921
|
c = next(i)
|
|
975
922
|
if c in ('0', '1'):
|
|
976
923
|
value.append(c)
|
|
977
924
|
else: # pragma: no cover
|
|
978
|
-
raise
|
|
925
|
+
raise PatternError(f'Invalid wide Unicode character at {i.index - 1}!')
|
|
979
926
|
|
|
980
927
|
for _ in range(4):
|
|
981
928
|
c = next(i)
|
|
982
929
|
if c.lower() in _HEX:
|
|
983
930
|
value.append(c)
|
|
984
931
|
else: # pragma: no cover
|
|
985
|
-
raise
|
|
932
|
+
raise PatternError(f'Invalid wide Unicode character at {i.index - 1}!')
|
|
986
933
|
return ''.join(value)
|
|
987
934
|
|
|
988
935
|
def get_narrow_unicode(self, i: _util.StringIter) -> str:
|
|
@@ -994,7 +941,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
994
941
|
if c.lower() in _HEX:
|
|
995
942
|
value.append(c)
|
|
996
943
|
else: # pragma: no cover
|
|
997
|
-
raise
|
|
944
|
+
raise PatternError(f'Invalid Unicode character at {i.index - 1}!')
|
|
998
945
|
return ''.join(value)
|
|
999
946
|
|
|
1000
947
|
def parse_unicode(self, i: _util.StringIter, wide: bool = False) -> None:
|
|
@@ -1024,7 +971,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1024
971
|
if c.lower() in _HEX:
|
|
1025
972
|
value.append(c)
|
|
1026
973
|
else: # pragma: no cover
|
|
1027
|
-
raise
|
|
974
|
+
raise PatternError(f'Invalid byte character at {i.index - 1}!')
|
|
1028
975
|
return ''.join(value)
|
|
1029
976
|
|
|
1030
977
|
def parse_bytes(self, i: _util.StringIter) -> None:
|
|
@@ -1050,7 +997,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1050
997
|
try:
|
|
1051
998
|
c = next(i)
|
|
1052
999
|
if c != "<":
|
|
1053
|
-
raise
|
|
1000
|
+
raise PatternError(f"Group missing '<' at {i.index - 1}!")
|
|
1054
1001
|
value.append(c)
|
|
1055
1002
|
c = next(i)
|
|
1056
1003
|
if c in _DIGIT:
|
|
@@ -1070,9 +1017,9 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1070
1017
|
c = next(i)
|
|
1071
1018
|
value.append(c)
|
|
1072
1019
|
else:
|
|
1073
|
-
raise
|
|
1020
|
+
raise PatternError(f"Invalid group character at {i.index - 1}!")
|
|
1074
1021
|
except StopIteration as e:
|
|
1075
|
-
raise
|
|
1022
|
+
raise PatternError(f"Unmatched '<' at {index}!") from e
|
|
1076
1023
|
|
|
1077
1024
|
return ''.join(value)
|
|
1078
1025
|
|
|
@@ -1106,7 +1053,7 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1106
1053
|
o = int(octal, 8)
|
|
1107
1054
|
if o > 0xFF and self.is_bytes:
|
|
1108
1055
|
# Re fails on octal greater than `0o377` or `0xFF`
|
|
1109
|
-
raise
|
|
1056
|
+
raise PatternError("Octal escape value outside of range 0-0o377!")
|
|
1110
1057
|
value = chr(o)
|
|
1111
1058
|
elif t in _STANDARD_ESCAPES or t == '\\':
|
|
1112
1059
|
value = _BACK_SLASH_TRANSLATION['\\' + t]
|
|
@@ -1316,11 +1263,11 @@ class _ReplaceParser(Generic[AnyStr]):
|
|
|
1316
1263
|
text[0] = (_util.FMT_FIELD, field)
|
|
1317
1264
|
self.auto_index += 1
|
|
1318
1265
|
else:
|
|
1319
|
-
raise
|
|
1266
|
+
raise PatternError("Cannot switch to auto format during manual format!")
|
|
1320
1267
|
elif not self.manual and not self.auto:
|
|
1321
1268
|
self.manual = True
|
|
1322
1269
|
elif not self.manual:
|
|
1323
|
-
raise
|
|
1270
|
+
raise PatternError("Cannot switch to manual format during auto format!")
|
|
1324
1271
|
|
|
1325
1272
|
self.handle_group(field, tuple(text), True)
|
|
1326
1273
|
|