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