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.
Files changed (72) hide show
  1. {backrefs-7.0 → backrefs-8.0}/PKG-INFO +1 -1
  2. {backrefs-7.0 → backrefs-8.0}/backrefs/__meta__.py +1 -1
  3. {backrefs-7.0 → backrefs-8.0}/backrefs/_bre_parse.py +70 -123
  4. {backrefs-7.0 → backrefs-8.0}/backrefs/_bregex_parse.py +72 -116
  5. {backrefs-7.0 → backrefs-8.0}/backrefs/bre.py +3 -2
  6. {backrefs-7.0 → backrefs-8.0}/backrefs/bregex.py +2 -1
  7. {backrefs-7.0 → backrefs-8.0}/backrefs/util.py +4 -0
  8. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/changelog.md +21 -0
  9. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/refs.md +12 -1
  10. {backrefs-7.0 → backrefs-8.0}/pyproject.toml +1 -1
  11. {backrefs-7.0 → backrefs-8.0}/tests/test_bre.py +93 -100
  12. {backrefs-7.0 → backrefs-8.0}/tests/test_bregex.py +73 -81
  13. {backrefs-7.0 → backrefs-8.0}/.coveragerc +0 -0
  14. {backrefs-7.0 → backrefs-8.0}/.gitignore +0 -0
  15. {backrefs-7.0 → backrefs-8.0}/.pyspelling.yml +0 -0
  16. {backrefs-7.0 → backrefs-8.0}/LICENSE.md +0 -0
  17. {backrefs-7.0 → backrefs-8.0}/README.md +0 -0
  18. {backrefs-7.0 → backrefs-8.0}/backrefs/__init__.py +0 -0
  19. {backrefs-7.0 → backrefs-8.0}/backrefs/_bregex_typing.py +0 -0
  20. {backrefs-7.0 → backrefs-8.0}/backrefs/py.typed +0 -0
  21. {backrefs-7.0 → backrefs-8.0}/backrefs/uniprops/__init__.py +0 -0
  22. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/.snippets/links.md +0 -0
  23. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/contributing.md +0 -0
  24. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/license.md +0 -0
  25. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/about/security.md +0 -0
  26. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/index.md +0 -0
  27. {backrefs-7.0 → backrefs-8.0}/docs/src/markdown/usage.md +0 -0
  28. {backrefs-7.0 → backrefs-8.0}/docs/theme/announce.html +0 -0
  29. {backrefs-7.0 → backrefs-8.0}/docs/theme/assets/pymdownx-extras/extra-95634471d6.css +0 -0
  30. {backrefs-7.0 → backrefs-8.0}/docs/theme/assets/pymdownx-extras/extra-loader-Ccztcqfq.js +0 -0
  31. {backrefs-7.0 → backrefs-8.0}/docs/theme/main.html +0 -0
  32. {backrefs-7.0 → backrefs-8.0}/hatch_build.py +0 -0
  33. {backrefs-7.0 → backrefs-8.0}/requirements/docs.txt +0 -0
  34. {backrefs-7.0 → backrefs-8.0}/requirements/lint.txt +0 -0
  35. {backrefs-7.0 → backrefs-8.0}/requirements/test.txt +0 -0
  36. {backrefs-7.0 → backrefs-8.0}/tests/__init__.py +0 -0
  37. {backrefs-7.0 → backrefs-8.0}/tests/test_age.py +0 -0
  38. {backrefs-7.0 → backrefs-8.0}/tests/test_bidiclass.py +0 -0
  39. {backrefs-7.0 → backrefs-8.0}/tests/test_bidipairedbrackettype.py +0 -0
  40. {backrefs-7.0 → backrefs-8.0}/tests/test_binary.py +0 -0
  41. {backrefs-7.0 → backrefs-8.0}/tests/test_block.py +0 -0
  42. {backrefs-7.0 → backrefs-8.0}/tests/test_canonicalcombiningclass.py +0 -0
  43. {backrefs-7.0 → backrefs-8.0}/tests/test_decompositiontype.py +0 -0
  44. {backrefs-7.0 → backrefs-8.0}/tests/test_eastasianwidth.py +0 -0
  45. {backrefs-7.0 → backrefs-8.0}/tests/test_generalcategory.py +0 -0
  46. {backrefs-7.0 → backrefs-8.0}/tests/test_graphemeclusterbreak.py +0 -0
  47. {backrefs-7.0 → backrefs-8.0}/tests/test_hangulsyllabletype.py +0 -0
  48. {backrefs-7.0 → backrefs-8.0}/tests/test_indicpositionalcategory.py +0 -0
  49. {backrefs-7.0 → backrefs-8.0}/tests/test_indicsylabiccategory.py +0 -0
  50. {backrefs-7.0 → backrefs-8.0}/tests/test_joininggroup.py +0 -0
  51. {backrefs-7.0 → backrefs-8.0}/tests/test_joiningtype.py +0 -0
  52. {backrefs-7.0 → backrefs-8.0}/tests/test_linebreak.py +0 -0
  53. {backrefs-7.0 → backrefs-8.0}/tests/test_numerictype.py +0 -0
  54. {backrefs-7.0 → backrefs-8.0}/tests/test_numericvalue.py +0 -0
  55. {backrefs-7.0 → backrefs-8.0}/tests/test_quickcheck.py +0 -0
  56. {backrefs-7.0 → backrefs-8.0}/tests/test_script.py +0 -0
  57. {backrefs-7.0 → backrefs-8.0}/tests/test_scriptextensions.py +0 -0
  58. {backrefs-7.0 → backrefs-8.0}/tests/test_sentencebreak.py +0 -0
  59. {backrefs-7.0 → backrefs-8.0}/tests/test_uniprops.py +0 -0
  60. {backrefs-7.0 → backrefs-8.0}/tests/test_versions.py +0 -0
  61. {backrefs-7.0 → backrefs-8.0}/tests/test_verticalorientation.py +0 -0
  62. {backrefs-7.0 → backrefs-8.0}/tests/test_wordbreak.py +0 -0
  63. {backrefs-7.0 → backrefs-8.0}/tools/__init__.py +0 -0
  64. {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/13.0.0.zip +0 -0
  65. {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/14.0.0.zip +0 -0
  66. {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/15.0.0.zip +0 -0
  67. {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/15.1.0.zip +0 -0
  68. {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/16.0.0.zip +0 -0
  69. {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/17.0.0.zip +0 -0
  70. {backrefs-7.0 → backrefs-8.0}/tools/unicodedata/LICENSE +0 -0
  71. {backrefs-7.0 → backrefs-8.0}/tools/unidatadownload.py +0 -0
  72. {backrefs-7.0 → backrefs-8.0}/tools/unipropgen.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: backrefs
3
- Version: 7.0
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>
@@ -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(7, 0, 0, "final")
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
- _GLOBAL_FLAGS = frozenset(('a', 'i', 'L', 'm', 's', 'u', 'x'))
41
- _SCOPED_FLAGS = frozenset(('i', 'm', 's', 'x'))
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 SyntaxError(f"Unicode property missing '{{' at {i.index - 1}!")
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 SyntaxError(f'Invalid Unicode property character at {i.index - 1}!')
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 SyntaxError(f'Invalid Unicode property character at {i.index - 1}!')
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 SyntaxError(f'Invalid Unicode property character at {i.index - 1}!')
220
+ raise PatternError(f'Invalid Unicode property character at {i.index - 1}!')
258
221
  if not value:
259
- raise SyntaxError('Invalid Unicode property!')
222
+ raise PatternError('Invalid Unicode property!')
260
223
 
261
224
  except StopIteration as e:
262
225
  if brackets:
263
- raise SyntaxError(f"Missing or unmatched ':]' at {index}!") from e
226
+ raise PatternError(f"Missing or unmatched ':]' at {index}!") from e
264
227
  else:
265
- raise SyntaxError(f"Missing or unmatched '{{' at {index}!") from e
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 ValueError(f"Named Unicode missing '{{' {i.index - 1}!")
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 SyntaxError(f"Unmatched '{{' at {index}!") from e
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 SyntaxError(f"Unmatched '(' at {index - 1}!") from e
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 toggle:
384
- if c not in _SCOPED_FLAGS:
385
- raise ValueError('Bad scope')
386
- elif c == '-':
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
- toggle = True
389
- elif c not in _GLOBAL_FLAGS:
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 ValueError("Bad flag")
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: # pragma: no cover
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
- pass
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
- pass
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 = [f'[^{_uniprops.ASCII_RANGE if self.is_bytes else _uniprops.UNICODE_RANGE}]']
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 [f'[^{_uniprops.ASCII_RANGE if self.is_bytes else _uniprops.UNICODE_RANGE}]']
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
- retry = True
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 SyntaxError(f"Unmatched '[' at {sindex - 1}") from e
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 SyntaxError(f"Invalid conversion type at {i.index - 1}!")
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 SyntaxError(f'Invalid format spec char at {i.index - 1}!')
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 ValueError("'=' alignment is not supported!")
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 SyntaxError(f"Unmatched '{{' at {index - 1}")
799
+ raise PatternError(f"Unmatched '{{' at {index - 1}")
853
800
  except StopIteration as e:
854
- raise SyntaxError(f"Unmatched '{{' at {index - 1}!") from e
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 SyntaxError(f"Unmatched '}}' at {i.index - 2}!")
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 ValueError("octal escape value outside of range 0-0o377!")
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 SyntaxError(f"Named Unicode missing '{{' at {i.index - 1}!")
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 SyntaxError(f"Unmatched '}}' at {index}!") from e
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 SyntaxError(f'Invalid wide Unicode character at {i.index - 1}!')
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 SyntaxError(f'Invalid wide Unicode character at {i.index - 1}!')
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 SyntaxError(f'Invalid wide Unicode character at {i.index - 1}!')
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 SyntaxError(f'Invalid Unicode character at {i.index - 1}!')
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 SyntaxError(f'Invalid byte character at {i.index - 1}!')
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 SyntaxError(f"Group missing '<' at {i.index - 1}!")
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 SyntaxError(f"Invalid group character at {i.index - 1}!")
1020
+ raise PatternError(f"Invalid group character at {i.index - 1}!")
1074
1021
  except StopIteration as e:
1075
- raise SyntaxError(f"Unmatched '<' at {index}!") from e
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 ValueError("octal escape value outside of range 0-0o377!")
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 ValueError("Cannot switch to auto format during manual format!")
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 ValueError("Cannot switch to manual format during auto format!")
1270
+ raise PatternError("Cannot switch to manual format during auto format!")
1324
1271
 
1325
1272
  self.handle_group(field, tuple(text), True)
1326
1273