backrefs 5.8__tar.gz → 6.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-5.8 → backrefs-6.0}/LICENSE.md +1 -1
- {backrefs-5.8 → backrefs-6.0}/PKG-INFO +6 -7
- {backrefs-5.8 → backrefs-6.0}/README.md +4 -6
- {backrefs-5.8 → backrefs-6.0}/backrefs/__meta__.py +1 -1
- {backrefs-5.8 → backrefs-6.0}/backrefs/_bre_parse.py +54 -27
- {backrefs-5.8 → backrefs-6.0}/backrefs/_bregex_parse.py +39 -24
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/about/changelog.md +18 -2
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/refs.md +83 -57
- {backrefs-5.8 → backrefs-6.0}/hatch_build.py +1 -0
- {backrefs-5.8 → backrefs-6.0}/mkdocs.yml +1 -3
- {backrefs-5.8 → backrefs-6.0}/pyproject.toml +2 -2
- {backrefs-5.8 → backrefs-6.0}/tests/test_bre.py +25 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_bregex.py +6 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unipropgen.py +41 -45
- backrefs-5.8/tools/unicodedata/11.0.0.zip +0 -0
- {backrefs-5.8 → backrefs-6.0}/.coveragerc +0 -0
- {backrefs-5.8 → backrefs-6.0}/.gitignore +0 -0
- {backrefs-5.8 → backrefs-6.0}/.pyspelling.yml +0 -0
- {backrefs-5.8 → backrefs-6.0}/backrefs/__init__.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/backrefs/_bregex_typing.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/backrefs/bre.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/backrefs/bregex.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/backrefs/py.typed +0 -0
- {backrefs-5.8 → backrefs-6.0}/backrefs/uniprops/__init__.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/backrefs/util.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/.snippets/links.md +0 -0
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/about/contributing.md +0 -0
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/about/license.md +0 -0
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/about/security.md +0 -0
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/index.md +0 -0
- {backrefs-5.8 → backrefs-6.0}/docs/src/markdown/usage.md +0 -0
- {backrefs-5.8 → backrefs-6.0}/docs/theme/announce.html +0 -0
- {backrefs-5.8 → backrefs-6.0}/requirements/docs.txt +0 -0
- {backrefs-5.8 → backrefs-6.0}/requirements/lint.txt +0 -0
- {backrefs-5.8 → backrefs-6.0}/requirements/test.txt +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/__init__.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_age.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_bidiclass.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_bidipairedbrackettype.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_binary.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_block.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_canonicalcombiningclass.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_decompositiontype.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_eastasianwidth.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_generalcategory.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_graphemeclusterbreak.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_hangulsyllabletype.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_indicpositionalcategory.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_indicsylabiccategory.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_joininggroup.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_joiningtype.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_linebreak.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_numerictype.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_numericvalue.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_quickcheck.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_script.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_scriptextensions.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_sentencebreak.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_uniprops.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_versions.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_verticalorientation.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tests/test_wordbreak.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/__init__.py +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unicodedata/13.0.0.zip +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unicodedata/14.0.0.zip +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unicodedata/15.0.0.zip +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unicodedata/15.1.0.zip +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unicodedata/16.0.0.zip +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unicodedata/LICENSE +0 -0
- {backrefs-5.8 → backrefs-6.0}/tools/unidatadownload.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: backrefs
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.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>
|
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
23
|
Classifier: Typing :: Typed
|
|
23
24
|
Requires-Python: >=3.9
|
|
@@ -26,13 +27,12 @@ Requires-Dist: regex; extra == 'extras'
|
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
27
28
|
|
|
28
29
|
[![Donate via PayPal][donate-image]][donate-link]
|
|
29
|
-
[![Discord][discord-image]][discord-link]
|
|
30
30
|
[![Build][github-ci-image]][github-ci-link]
|
|
31
31
|
[![Coverage Status][codecov-image]][codecov-link]
|
|
32
32
|
[![PyPI Version][pypi-image]][pypi-link]
|
|
33
33
|
[![PyPI Downloads][pypi-down]][pypi-link]
|
|
34
34
|
[![PyPI - Python Version][python-image]][pypi-link]
|
|
35
|
-
![License][license-image-mit]
|
|
35
|
+
[![License][license-image-mit]][license-link]
|
|
36
36
|
|
|
37
37
|
# Backrefs
|
|
38
38
|
|
|
@@ -42,7 +42,7 @@ Python's Re and/or Regex. The supported back references actually vary depending
|
|
|
42
42
|
used as the engine may already have support for some.
|
|
43
43
|
|
|
44
44
|
```python
|
|
45
|
-
from backrefs import bre
|
|
45
|
+
>>> from backrefs import bre
|
|
46
46
|
>>> pattern = bre.compile(r'(\p{Letter}+)')
|
|
47
47
|
>>> pattern.sub(r'\C\1\E', 'sometext')
|
|
48
48
|
'SOMETEXT'
|
|
@@ -56,10 +56,8 @@ https://facelessuser.github.io/backrefs/
|
|
|
56
56
|
|
|
57
57
|
MIT
|
|
58
58
|
|
|
59
|
-
[github-ci-image]: https://github.com/facelessuser/backrefs/workflows/build/badge.svg
|
|
59
|
+
[github-ci-image]: https://github.com/facelessuser/backrefs/workflows/build/badge.svg
|
|
60
60
|
[github-ci-link]: https://github.com/facelessuser/backrefs/actions?query=workflow%3Abuild+branch%3Amaster
|
|
61
|
-
[discord-image]: https://img.shields.io/discord/678289859768745989?logo=discord&logoColor=aaaaaa&color=mediumpurple&labelColor=333333
|
|
62
|
-
[discord-link]:https://discord.gg/TWs8Tgr
|
|
63
61
|
[codecov-image]: https://img.shields.io/codecov/c/github/facelessuser/backrefs/master.svg?logo=codecov&logoColor=aaaaaa&labelColor=333333
|
|
64
62
|
[codecov-link]: https://codecov.io/github/facelessuser/backrefs
|
|
65
63
|
[pypi-image]: https://img.shields.io/pypi/v/backrefs.svg?logo=pypi&logoColor=aaaaaa&labelColor=333333
|
|
@@ -67,6 +65,7 @@ MIT
|
|
|
67
65
|
[pypi-link]: https://pypi.python.org/pypi/backrefs
|
|
68
66
|
[python-image]: https://img.shields.io/pypi/pyversions/backrefs?logo=python&logoColor=aaaaaa&labelColor=333333
|
|
69
67
|
[license-image-mit]: https://img.shields.io/badge/license-MIT-blue.svg?labelColor=333333
|
|
68
|
+
[license-link]: https://github.com/facelessuser/backrefs/blob/main/LICENSE.md
|
|
70
69
|
[donate-image]: https://img.shields.io/badge/Donate-PayPal-3fabd1?logo=paypal
|
|
71
70
|
[donate-link]: https://www.paypal.me/facelessuser
|
|
72
71
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
[![Donate via PayPal][donate-image]][donate-link]
|
|
2
|
-
[![Discord][discord-image]][discord-link]
|
|
3
2
|
[![Build][github-ci-image]][github-ci-link]
|
|
4
3
|
[![Coverage Status][codecov-image]][codecov-link]
|
|
5
4
|
[![PyPI Version][pypi-image]][pypi-link]
|
|
6
5
|
[![PyPI Downloads][pypi-down]][pypi-link]
|
|
7
6
|
[![PyPI - Python Version][python-image]][pypi-link]
|
|
8
|
-
![License][license-image-mit]
|
|
7
|
+
[![License][license-image-mit]][license-link]
|
|
9
8
|
|
|
10
9
|
# Backrefs
|
|
11
10
|
|
|
@@ -15,7 +14,7 @@ Python's Re and/or Regex. The supported back references actually vary depending
|
|
|
15
14
|
used as the engine may already have support for some.
|
|
16
15
|
|
|
17
16
|
```python
|
|
18
|
-
from backrefs import bre
|
|
17
|
+
>>> from backrefs import bre
|
|
19
18
|
>>> pattern = bre.compile(r'(\p{Letter}+)')
|
|
20
19
|
>>> pattern.sub(r'\C\1\E', 'sometext')
|
|
21
20
|
'SOMETEXT'
|
|
@@ -29,10 +28,8 @@ https://facelessuser.github.io/backrefs/
|
|
|
29
28
|
|
|
30
29
|
MIT
|
|
31
30
|
|
|
32
|
-
[github-ci-image]: https://github.com/facelessuser/backrefs/workflows/build/badge.svg
|
|
31
|
+
[github-ci-image]: https://github.com/facelessuser/backrefs/workflows/build/badge.svg
|
|
33
32
|
[github-ci-link]: https://github.com/facelessuser/backrefs/actions?query=workflow%3Abuild+branch%3Amaster
|
|
34
|
-
[discord-image]: https://img.shields.io/discord/678289859768745989?logo=discord&logoColor=aaaaaa&color=mediumpurple&labelColor=333333
|
|
35
|
-
[discord-link]:https://discord.gg/TWs8Tgr
|
|
36
33
|
[codecov-image]: https://img.shields.io/codecov/c/github/facelessuser/backrefs/master.svg?logo=codecov&logoColor=aaaaaa&labelColor=333333
|
|
37
34
|
[codecov-link]: https://codecov.io/github/facelessuser/backrefs
|
|
38
35
|
[pypi-image]: https://img.shields.io/pypi/v/backrefs.svg?logo=pypi&logoColor=aaaaaa&labelColor=333333
|
|
@@ -40,6 +37,7 @@ MIT
|
|
|
40
37
|
[pypi-link]: https://pypi.python.org/pypi/backrefs
|
|
41
38
|
[python-image]: https://img.shields.io/pypi/pyversions/backrefs?logo=python&logoColor=aaaaaa&labelColor=333333
|
|
42
39
|
[license-image-mit]: https://img.shields.io/badge/license-MIT-blue.svg?labelColor=333333
|
|
40
|
+
[license-link]: https://github.com/facelessuser/backrefs/blob/main/LICENSE.md
|
|
43
41
|
[donate-image]: https://img.shields.io/badge/Donate-PayPal-3fabd1?logo=paypal
|
|
44
42
|
[donate-link]: https://www.paypal.me/facelessuser
|
|
45
43
|
|
|
@@ -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(6, 0, 0, "final")
|
|
197
197
|
__version__ = __version_info__._get_canonical()
|
|
@@ -37,11 +37,19 @@ _STANDARD_ESCAPES = frozenset(('a', 'b', 'f', 'n', 'r', 't', 'v'))
|
|
|
37
37
|
_CURLY_BRACKETS = frozenset(('{', '}'))
|
|
38
38
|
_PROPERTY_STRIP = frozenset((' ', '-', '_'))
|
|
39
39
|
_PROPERTY = _WORD | _DIGIT | _PROPERTY_STRIP
|
|
40
|
-
_GLOBAL_FLAGS = frozenset(('a', '
|
|
41
|
-
_SCOPED_FLAGS = frozenset(('i', 'm', 's', '
|
|
40
|
+
_GLOBAL_FLAGS = frozenset(('a', 'i', 'L', 'm', 's', 'u', 'x'))
|
|
41
|
+
_SCOPED_FLAGS = frozenset(('i', 'm', 's', 'x'))
|
|
42
|
+
_SCOPED_END = frozenset((':', ')'))
|
|
42
43
|
|
|
43
44
|
_CURLY_BRACKETS_ORD = frozenset((0x7b, 0x7d))
|
|
44
45
|
|
|
46
|
+
_COMPATIBILITY_PROPERTIES = frozenset(
|
|
47
|
+
(
|
|
48
|
+
'alpha', 'lower', 'upper', 'punct', 'digit', 'xdigit', 'alnum',
|
|
49
|
+
'space', 'blank', 'cntrl', 'graph', 'print', 'word'
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
|
|
45
53
|
# Case upper or lower
|
|
46
54
|
_UPPER = 1
|
|
47
55
|
_LOWER = 2
|
|
@@ -173,20 +181,24 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
173
181
|
def flags(self, text: str, scoped: bool = False) -> None:
|
|
174
182
|
"""Analyze flags."""
|
|
175
183
|
|
|
184
|
+
flags = text.split('-')
|
|
185
|
+
enable = flags[0]
|
|
186
|
+
disable = flags[1] if len(flags) > 1 else ''
|
|
187
|
+
|
|
176
188
|
global_retry = False
|
|
177
|
-
if ('a' in
|
|
189
|
+
if ('a' in enable or 'L' in enable) and self.unicode:
|
|
178
190
|
self.unicode = False
|
|
179
191
|
if not scoped:
|
|
180
192
|
self.temp_global_flag_swap["unicode"] = True
|
|
181
193
|
global_retry = True
|
|
182
|
-
elif 'u' in
|
|
194
|
+
elif 'u' in enable and not self.unicode and not self.is_bytes:
|
|
183
195
|
self.unicode = True
|
|
184
196
|
if not scoped:
|
|
185
197
|
self.temp_global_flag_swap["unicode"] = True
|
|
186
198
|
global_retry = True
|
|
187
|
-
if '
|
|
199
|
+
if 'x' in disable and self.verbose:
|
|
188
200
|
self.verbose = False
|
|
189
|
-
elif 'x' in
|
|
201
|
+
elif 'x' in enable and not self.verbose:
|
|
190
202
|
self.verbose = True
|
|
191
203
|
if not scoped:
|
|
192
204
|
self.temp_global_flag_swap["verbose"] = True
|
|
@@ -253,7 +265,15 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
253
265
|
else:
|
|
254
266
|
raise SyntaxError(f"Missing or unmatched '{{' at {index}!") from e
|
|
255
267
|
|
|
256
|
-
|
|
268
|
+
p = ''.join(prop).lower()
|
|
269
|
+
v = ''.join(value).lower()
|
|
270
|
+
|
|
271
|
+
# Ensure when using POSIX form, that any property considered a compatibility property uses the POSIX form.
|
|
272
|
+
# POSIX form is not guaranteed to be different from standard form and sometimes is just an alias for standard.
|
|
273
|
+
if brackets and p in _COMPATIBILITY_PROPERTIES:
|
|
274
|
+
p = 'posix' + p
|
|
275
|
+
|
|
276
|
+
return p, v
|
|
257
277
|
|
|
258
278
|
def get_named_unicode(self, i: _util.StringIter) -> str:
|
|
259
279
|
"""Get Unicode name."""
|
|
@@ -345,51 +365,56 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
345
365
|
|
|
346
366
|
return ''.join(value)
|
|
347
367
|
|
|
348
|
-
def get_flags(self, i: _util.StringIter
|
|
349
|
-
"""
|
|
368
|
+
def get_flags(self, i: _util.StringIter) -> tuple[str | None, bool]:
|
|
369
|
+
"""
|
|
370
|
+
Get flags.
|
|
371
|
+
|
|
372
|
+
In Re, flags are quite predictable when global or scoped.
|
|
373
|
+
Global can never be disabled with minus, and never have a `:` after them.
|
|
374
|
+
The global flag set is also very specific, but can be used as enablers in scoped.
|
|
375
|
+
|
|
376
|
+
The returned scoped status will indicate whether flags are generally considered
|
|
377
|
+
scoped flags or global flags.
|
|
378
|
+
"""
|
|
350
379
|
|
|
351
380
|
index = i.index
|
|
352
381
|
value = ['(']
|
|
353
382
|
toggle = False
|
|
354
|
-
|
|
383
|
+
smells_scoped = False
|
|
355
384
|
try:
|
|
356
385
|
c = next(i)
|
|
357
386
|
if c != '?':
|
|
358
387
|
i.rewind(1)
|
|
359
|
-
return None
|
|
388
|
+
return None, False
|
|
360
389
|
value.append(c)
|
|
361
390
|
c = next(i)
|
|
362
|
-
while c
|
|
391
|
+
while c not in _SCOPED_END:
|
|
363
392
|
if toggle:
|
|
364
393
|
if c not in _SCOPED_FLAGS:
|
|
365
394
|
raise ValueError('Bad scope')
|
|
366
|
-
|
|
367
|
-
|
|
395
|
+
elif c == '-':
|
|
396
|
+
smells_scoped = True
|
|
368
397
|
toggle = True
|
|
369
|
-
elif
|
|
370
|
-
raise ValueError("Bad flag")
|
|
371
|
-
elif c not in _GLOBAL_FLAGS and c not in _SCOPED_FLAGS:
|
|
398
|
+
elif c not in _GLOBAL_FLAGS:
|
|
372
399
|
raise ValueError("Bad flag")
|
|
373
400
|
value.append(c)
|
|
374
401
|
c = next(i)
|
|
402
|
+
if smells_scoped and c != ':':
|
|
403
|
+
raise ValueError("Bad flag")
|
|
404
|
+
elif c == ':':
|
|
405
|
+
smells_scoped = True
|
|
375
406
|
value.append(c)
|
|
376
407
|
except Exception:
|
|
377
408
|
i.rewind(i.index - index)
|
|
378
409
|
value = []
|
|
379
410
|
|
|
380
|
-
return ''.join(value) if value else None
|
|
411
|
+
return ''.join(value) if value else None, smells_scoped
|
|
381
412
|
|
|
382
413
|
def subgroup(self, t: str, i: _util.StringIter) -> list[str]:
|
|
383
414
|
"""Handle parenthesis."""
|
|
384
415
|
|
|
385
416
|
current = [] # type: list[str]
|
|
386
417
|
|
|
387
|
-
# (?flags)
|
|
388
|
-
flags = self.get_flags(i)
|
|
389
|
-
if flags:
|
|
390
|
-
self.flags(flags[2:-1])
|
|
391
|
-
return [flags]
|
|
392
|
-
|
|
393
418
|
# (?#comment)
|
|
394
419
|
comments = self.get_comments(i)
|
|
395
420
|
if comments:
|
|
@@ -398,11 +423,13 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
398
423
|
verbose = self.verbose
|
|
399
424
|
unicode_flag = self.unicode
|
|
400
425
|
|
|
401
|
-
# (?flags:pattern)
|
|
402
|
-
flags = self.get_flags(i
|
|
426
|
+
# (?flags:pattern) or (?flags)
|
|
427
|
+
flags, scoped = self.get_flags(i)
|
|
403
428
|
if flags: # pragma: no cover
|
|
404
429
|
t = flags
|
|
405
|
-
self.flags(flags[2:-1], scoped=
|
|
430
|
+
self.flags(flags[2:-1], scoped=scoped)
|
|
431
|
+
if not scoped:
|
|
432
|
+
return [flags]
|
|
406
433
|
|
|
407
434
|
current = []
|
|
408
435
|
try:
|
|
@@ -30,9 +30,10 @@ _STANDARD_ESCAPES = frozenset(('a', 'b', 'f', 'n', 'r', 't', 'v'))
|
|
|
30
30
|
_CURLY_BRACKETS = frozenset(('{', '}'))
|
|
31
31
|
_PROPERTY_STRIP = frozenset((' ', '-', '_'))
|
|
32
32
|
_PROPERTY = _WORD | _DIGIT | _PROPERTY_STRIP
|
|
33
|
-
_GLOBAL_FLAGS = frozenset(('
|
|
34
|
-
_SCOPED_FLAGS = frozenset(('i', 'm', 's', '
|
|
33
|
+
_GLOBAL_FLAGS = frozenset(('b', 'e', 'p', 'r', 'u'))
|
|
34
|
+
_SCOPED_FLAGS = frozenset(('a', 'f', 'i', 'L', 'm', 's', 'u', 'w', 'x'))
|
|
35
35
|
_VERSIONS = frozenset(('0', '1'))
|
|
36
|
+
_SCOPED_END = frozenset((':', ')'))
|
|
36
37
|
|
|
37
38
|
_CURLY_BRACKETS_ORD = frozenset((0x7b, 0x7d))
|
|
38
39
|
|
|
@@ -163,21 +164,25 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
163
164
|
def flags(self, text: str, scoped: bool = False) -> None:
|
|
164
165
|
"""Analyze flags."""
|
|
165
166
|
|
|
167
|
+
flags = text.split('-')
|
|
168
|
+
enable = flags[0]
|
|
169
|
+
disable = flags[1] if len(flags) > 1 else ''
|
|
170
|
+
|
|
166
171
|
global_retry = False
|
|
167
|
-
if (self.version == _regex.V1 or scoped) and '
|
|
172
|
+
if (self.version == _regex.V1 or scoped) and 'x' in disable and self.verbose:
|
|
168
173
|
self.verbose = False
|
|
169
|
-
elif 'x' in
|
|
174
|
+
elif 'x' in enable and not self.verbose:
|
|
170
175
|
self.verbose = True
|
|
171
176
|
if not scoped and self.version == _regex.V0:
|
|
172
177
|
self.temp_global_flag_swap['verbose'] = True
|
|
173
178
|
global_retry = True
|
|
174
|
-
if 'V0' in
|
|
179
|
+
if 'V0' in enable and self.version == _regex.V1: # pragma: no cover
|
|
175
180
|
# Default is V0 if none is selected,
|
|
176
181
|
# so it is unlikely that this will be selected.
|
|
177
182
|
self.temp_global_flag_swap['version'] = True
|
|
178
183
|
self.version = _regex.V0
|
|
179
184
|
global_retry = True
|
|
180
|
-
elif "V1" in
|
|
185
|
+
elif "V1" in enable and self.version == _regex.V0:
|
|
181
186
|
self.temp_global_flag_swap['version'] = True
|
|
182
187
|
self.version = _regex.V1
|
|
183
188
|
global_retry = True
|
|
@@ -260,54 +265,61 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
260
265
|
|
|
261
266
|
return ''.join(value) if value else None
|
|
262
267
|
|
|
263
|
-
def get_flags(self, i: _util.StringIter
|
|
264
|
-
"""
|
|
268
|
+
def get_flags(self, i: _util.StringIter) -> tuple[str | None, bool]:
|
|
269
|
+
"""
|
|
270
|
+
Get flags.
|
|
271
|
+
|
|
272
|
+
Regex is more difficult to determine when flags are used in a global and scoped context.
|
|
273
|
+
There is a specific list of global flags, but they can be used in scoped notation and will still
|
|
274
|
+
be considered global, but that does not mean other flags are global. Additionally, flags that can be
|
|
275
|
+
scoped can also used be used in global syntax, but can be disabled with a minus unlike in RE.
|
|
276
|
+
|
|
277
|
+
Bregex only cares about capturing the "verbose" flag and the version flags. Version flags are always
|
|
278
|
+
global and verbose flags will be scoped based on whether they are in a scoped group. The returned
|
|
279
|
+
"scoped" parameter only refers to "verbose".
|
|
280
|
+
"""
|
|
265
281
|
|
|
266
282
|
index = i.index
|
|
267
283
|
value = ['(']
|
|
268
284
|
version = False
|
|
269
285
|
toggle = False
|
|
270
|
-
|
|
286
|
+
smells_scoped = False
|
|
271
287
|
try:
|
|
272
288
|
c = next(i)
|
|
273
289
|
if c != '?':
|
|
274
290
|
i.rewind(1)
|
|
275
|
-
return None
|
|
291
|
+
return None, False
|
|
276
292
|
value.append(c)
|
|
277
293
|
c = next(i)
|
|
278
|
-
while c
|
|
294
|
+
while c not in _SCOPED_END:
|
|
279
295
|
if toggle:
|
|
280
296
|
if c not in _SCOPED_FLAGS:
|
|
281
297
|
raise ValueError('Bad scope')
|
|
282
|
-
toggle = False
|
|
283
|
-
elif (not version0 or scoped) and c == '-':
|
|
284
|
-
toggle = True
|
|
285
298
|
elif version:
|
|
286
299
|
if c not in _VERSIONS:
|
|
287
300
|
raise ValueError('Bad version')
|
|
288
301
|
version = False
|
|
302
|
+
elif c == '-':
|
|
303
|
+
toggle = True
|
|
289
304
|
elif c == 'V':
|
|
290
305
|
version = True
|
|
291
306
|
elif c not in _GLOBAL_FLAGS and c not in _SCOPED_FLAGS:
|
|
292
307
|
raise ValueError("Bad flag")
|
|
293
308
|
value.append(c)
|
|
294
309
|
c = next(i)
|
|
310
|
+
if c == ':':
|
|
311
|
+
smells_scoped = True
|
|
312
|
+
|
|
295
313
|
value.append(c)
|
|
296
314
|
except Exception:
|
|
297
315
|
i.rewind(i.index - index)
|
|
298
316
|
value = []
|
|
299
317
|
|
|
300
|
-
return ''.join(value) if value else None
|
|
318
|
+
return ''.join(value) if value else None, smells_scoped
|
|
301
319
|
|
|
302
320
|
def subgroup(self, t: str, i: _util.StringIter) -> list[str]:
|
|
303
321
|
"""Handle parenthesis."""
|
|
304
322
|
|
|
305
|
-
# (?flags)
|
|
306
|
-
flags = self.get_flags(i, self.version == _regex.V0)
|
|
307
|
-
if flags:
|
|
308
|
-
self.flags(flags[2:-1])
|
|
309
|
-
return [flags]
|
|
310
|
-
|
|
311
323
|
# (?#comment)
|
|
312
324
|
comments = self.get_comments(i)
|
|
313
325
|
if comments:
|
|
@@ -315,11 +327,14 @@ class _SearchParser(Generic[AnyStr]):
|
|
|
315
327
|
|
|
316
328
|
verbose = self.verbose
|
|
317
329
|
|
|
318
|
-
# (?flags:pattern)
|
|
319
|
-
|
|
330
|
+
# (?flags:pattern) or (?flags)
|
|
331
|
+
# "scoped" only refers to verbose
|
|
332
|
+
flags, scoped = self.get_flags(i)
|
|
320
333
|
if flags:
|
|
321
334
|
t = flags
|
|
322
|
-
self.flags(flags[2:-1], scoped=
|
|
335
|
+
self.flags(flags[2:-1], scoped=scoped)
|
|
336
|
+
if not scoped:
|
|
337
|
+
return [flags]
|
|
323
338
|
|
|
324
339
|
current = [] # type: list[str]
|
|
325
340
|
try:
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 6.0
|
|
4
|
+
|
|
5
|
+
- **NEW**: POSIX character classes will now always use POSIX compatibility rules instead of Unicode standard rules,
|
|
6
|
+
if any are specified in the Unicode specification. The affected character classes are: `[[:alnum:]]`, `[[:digit:]]`,
|
|
7
|
+
`[[:xdigit:]]`, and `[[:punct:]]`. To explicitly use standard Unicode rules for these compatibility properties, use
|
|
8
|
+
the Unicode property form instead: `[\p{Alnum}]`, `[\p{Digit}]`, `[\p{Punct}]`, or `[\p{XDigit}]`. This has changed
|
|
9
|
+
to ensure no confusion for users expecting compatible POSIX style character class properties.
|
|
10
|
+
- **FIX**: Scoped ASCII/Unicode flags (`(?a:pattern)`/`(?u:pattern)`) should be respected for Unicode properties in
|
|
11
|
+
`bre` and will ensure ASCII or Unicode range if used.
|
|
12
|
+
- **FIX**: Fix issues related to detecting disabled scoped flags.
|
|
13
|
+
|
|
14
|
+
## 5.9
|
|
15
|
+
|
|
16
|
+
- **NEW**: Add support for Python 3.14.
|
|
17
|
+
- **ENHANCE**: Switch to deploying with PyPI's "Trusted Publisher".
|
|
18
|
+
|
|
3
19
|
## 5.8
|
|
4
20
|
|
|
5
21
|
- **NEW**: Drop Python 3.8.
|
|
@@ -23,8 +39,8 @@
|
|
|
23
39
|
|
|
24
40
|
## 5.5
|
|
25
41
|
|
|
26
|
-
- **NEW**: `\e` and `\h` have both been deprecated
|
|
27
|
-
|
|
42
|
+
- **NEW**: `\e` and `\h` have both been deprecated. Please migrate to using `\x1b` and `\p{Horiz_Space}` in their
|
|
43
|
+
respective place.
|
|
28
44
|
- **FIX**: Fix flag issue with `sub` functions.
|
|
29
45
|
|
|
30
46
|
## 5.4
|
|
@@ -199,12 +199,12 @@ Block properties have a similar short form as Script and Binary properties. For
|
|
|
199
199
|
a block. If we wanted to match characters in the `Basic_Latin` block, we could use the syntax `\p{InBasic_Latin}`. This
|
|
200
200
|
would be the same as `\p{Block: Basic_Latin}` or `\p{Basic_Latin}`.
|
|
201
201
|
|
|
202
|
-
///
|
|
202
|
+
/// warning | Short Name Conflicts
|
|
203
203
|
When it comes to short names, each new Unicode version, there is a risk that new properties could cause conflicts
|
|
204
204
|
with existing names and/or aliases. Currently, most of the conflicts involve the Block properties. To reduce
|
|
205
205
|
friction, they are evaluated last.
|
|
206
206
|
|
|
207
|
-
Generally, it is discouraged to use short names for Block properties. But the option is still supported, but Block
|
|
207
|
+
Generally, it is discouraged to use short names for Block properties. But the option is still supported, but Block
|
|
208
208
|
properties will be evaluated last. There are currently no known conflicts with `In*` properties, but in future
|
|
209
209
|
Unicode versions there could.
|
|
210
210
|
|
|
@@ -259,67 +259,18 @@ Verbose\ Property\ Form | Terse\ Property\ Form
|
|
|
259
259
|
`Line_Separator` | `Zl`
|
|
260
260
|
`Paragraph_Separator` | `Z`
|
|
261
261
|
|
|
262
|
-
### POSIX Style Properties
|
|
263
|
-
|
|
264
|
-
/// new | New in 5.0
|
|
265
|
-
5.0 brings significant improvements and bug fixes to Unicode property handling. Properties are sensitive to the
|
|
266
|
-
`ASCII` flag along with more extensive testing and bug fixes. Additionally, POSIX style properties are now just
|
|
267
|
-
an extension of normal Unicode properties. All the POSIX names are available and now conform to
|
|
268
|
-
the [Unicode specification for POSIX compatibility](https://unicode.org/reports/tr18/#Compatibility_Properties).
|
|
269
|
-
Read on to learn more.
|
|
270
|
-
///
|
|
271
|
-
|
|
272
|
-
Backrefs allows for POSIX style properties in the form `[:name:]`. These properties can only be used inside character
|
|
273
|
-
classes and are another form for expressing Unicode properties. Any Unicode property that can be expressed via the
|
|
274
|
-
`\p{name}` form can also be expressed in the `[:name:]` form. To illustrate, the following are all the same:
|
|
275
|
-
|
|
276
|
-
- `[[:upper:]]` == `[\p{upper}]`
|
|
277
|
-
- `[[:^upper:]]` == `[\p{^upper}]`
|
|
278
|
-
- `[[:alpha=yes:]]` == `[\p{alpha=yes}]`
|
|
279
|
-
|
|
280
|
-
A number of POSIX property names are available via compatibility properties as outlined in the
|
|
281
|
-
[Unicode specification for POSIX compatibility](https://unicode.org/reports/tr18/#Compatibility_Properties). These
|
|
282
|
-
properties will operate in the Unicode range and the ASCII range depending on the regular expression mode. These
|
|
283
|
-
patterns, like all Unicode properties, are sensitive to the `ASCII` flag (or `LOCALE` which will treat them as `ASCII`).
|
|
284
|
-
|
|
285
|
-
It is important to note that whether used in the form `[[:name:]]` or `\p{name}`, each POSIX name is available both with
|
|
286
|
-
and without the `posix` prefix, but it is recommended to use the `posix` prefix to get the POSIX definition of the
|
|
287
|
-
pattern as number of patterns have both a POSIX and Unicode definition that differ. The
|
|
288
|
-
[Unicode specification for POSIX compatibility](https://unicode.org/reports/tr18/#Compatibility_Properties) outlines all
|
|
289
|
-
the POSIX compatible properties and the ones which have dual definitions: `punct`, `alnum`, `digit`, and `xdigit` all
|
|
290
|
-
have a Unicode standard and a POSIX compatibility variant and must be accessed with the `posix` prefix to get the POSIX
|
|
291
|
-
form.
|
|
292
|
-
|
|
293
|
-
In the table below, patterns with `--` mean `[[in this] -- [but not this]]`.
|
|
294
|
-
|
|
295
|
-
\[:posix:] | \\p\{Posix} | ASCII | Unicode
|
|
296
|
-
---------- | ------------- | ------------------------------------------------- | -------
|
|
297
|
-
`alpha` | `Alpha` | `[a-zA-Z]` | `\p{Alphabetic}`
|
|
298
|
-
`alnum` | `PosixAlnum` | `[[:alpha:][:digit:]]` | `[[:alpha:][:digit:]]`
|
|
299
|
-
`blank` | `Blank` | `[ \t]` | `[\p{Zs}\t]`
|
|
300
|
-
`cntrl` | `Cntrl` | `[\x00-\x1F\x7F]` | `\p{Cc}`
|
|
301
|
-
`digit` | `PosixDigit` | `[0-9]` | `[0-9]`
|
|
302
|
-
`graph` | `Graph` | `[^ [:cntrl:]]` | `[^[:space:][:cntrl:]\p{Cn}\p{Cs}]`
|
|
303
|
-
`lower` | `Lower` | `[a-z]` | `[\p{Lowercase}]`
|
|
304
|
-
`print` | `Print` | `[[:graph:] ]` | `[[\p{P}\p{S}]--[\p{alpha}]]`
|
|
305
|
-
`punct` | `PosixPunct` | ``[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^_`{}~]`` | `[[[:graph:][:blank:]]--[[:cntrl:]]]`
|
|
306
|
-
`space` | `Space` | `[ \t\r\n\v\f]` | `[\p{Whitespace}]`
|
|
307
|
-
`upper` | `Upper` | `[A-Z]` | `[\p{Uppercase}]`
|
|
308
|
-
`xdigit` | `PosixXDigit` | `[A-Fa-f0-9]` | `[A-Fa-f0-9]`
|
|
309
|
-
|
|
310
262
|
### Compatibility Properties
|
|
311
263
|
|
|
312
264
|
/// new | New in 5.0
|
|
313
265
|
While many of the properties were available before 5.0, `word` is newly available. And all the properties now
|
|
314
|
-
conform to the [Unicode specification for
|
|
266
|
+
conform to the [Unicode specification for compatibility properties](https://unicode.org/reports/tr18/#Compatibility_Properties).
|
|
315
267
|
///
|
|
316
268
|
|
|
317
|
-
[Unicode specification for
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
In the table below, patterns with `--` mean `[[in this] -- [but not this]]`.
|
|
269
|
+
[Unicode specification for compatibility properties][unicode-posix] defines a number of properties for use in regular
|
|
270
|
+
expressions. The table below shows the names of these properties along with their standard rules as implemented in
|
|
271
|
+
Backrefs. Patterns with `--` mean `[[in this] -- [but not this]]`.
|
|
321
272
|
|
|
322
|
-
\\p\{
|
|
273
|
+
\\p\{name} | Unicode
|
|
323
274
|
------------- | -------
|
|
324
275
|
`Alpha` | `\p{Alphabetic}`
|
|
325
276
|
`Alnum` | `[\p{Alpha}\p{Digit}]`
|
|
@@ -328,9 +279,84 @@ In the table below, patterns with `--` mean `[[in this] -- [but not this]]`.
|
|
|
328
279
|
`Digit` | `\p{Nd}`
|
|
329
280
|
`Graph` | `[^\p{Space}\p{Cntrl}\p{Cn}\p{Cs}]`
|
|
330
281
|
`Lower` | `\p{Lowercase}`
|
|
331
|
-
`Print` | `[
|
|
282
|
+
`Print` | `[\p{graph}\p{blank}--\p{cntrl}]`
|
|
332
283
|
`Punct` | `\p{P}`
|
|
333
284
|
`Space` | `\p{Whitespace}`
|
|
334
285
|
`Upper` | `\p{Uppercase}`
|
|
335
286
|
`Word` | `[\p{Alnum}\p{M}\p{Pc}\p{JoinControl}]`
|
|
336
287
|
`XDigit` | `[\p{Nd}\p{HexDigit}]`
|
|
288
|
+
|
|
289
|
+
The [Unicode specification for compatibility properties](https://unicode.org/reports/tr18/#Compatibility_Properties)
|
|
290
|
+
also defines rules for POSIX compatibility which provides modifications to the standard recommendation required to meet
|
|
291
|
+
the formal requirements of POSIX, and also to maintain (as much as possible) compatibility with the POSIX usage in
|
|
292
|
+
practice.
|
|
293
|
+
|
|
294
|
+
To accommodate these POSIX compatible rules, Backrefs defines POSIX property names for all the compatibility properties.
|
|
295
|
+
For any that do not have special accommodations, the POSIX property name will act as an alias for the standard Unicode
|
|
296
|
+
name. In all, four properties are specifically affected with POSIX specific accommodations: `Alnum`, `Digit`, `Punct`,
|
|
297
|
+
and `XDigit`.
|
|
298
|
+
|
|
299
|
+
\\p\{name} | Unicode
|
|
300
|
+
------------- | -------
|
|
301
|
+
`PosixAlpha` | `\p{Alpha}`
|
|
302
|
+
`PosixAlnum` | `[\p{Alpha}\p{PosixDigit}]`
|
|
303
|
+
`PosixBlank` | `\p{Blank}`
|
|
304
|
+
`PosixCntrl` | `\p{Cntrl}`
|
|
305
|
+
`PosixDigit` | `[0-9]`
|
|
306
|
+
`PosixGraph` | `\p{Graph}`
|
|
307
|
+
`PosixLower` | `\p{Lower}`
|
|
308
|
+
`PosixPrint` | `\p{Print}`
|
|
309
|
+
`PosixPunct` | `[[\p{P}\p{S}]--[\p{alpha}]]`
|
|
310
|
+
`PosixSpace` | `\p{Space}`
|
|
311
|
+
`PosixUpper` | `\p{Uppper}`
|
|
312
|
+
`PosixWord` | `\p{Word}`
|
|
313
|
+
`PosixXDigit` | `[A-Fa-f0-9]`
|
|
314
|
+
|
|
315
|
+
### POSIX Character Classes
|
|
316
|
+
|
|
317
|
+
/// new | New in 6.0
|
|
318
|
+
POSIX character classes in the form of `[:name:]` are now forced to always use POSIX compatible rules if available.
|
|
319
|
+
These rules are defined in the [Unicode specification for compatibility properties]
|
|
320
|
+
(https://unicode.org/reports/tr18/#Compatibility_Properties).
|
|
321
|
+
///
|
|
322
|
+
|
|
323
|
+
Backrefs allows for POSIX style character classes in the form of `[:name:]`. These properties can only be used inside
|
|
324
|
+
character classes, e.g. `[[:name:]]`.
|
|
325
|
+
|
|
326
|
+
When using POSIX character classes, all character classes will use the matching rules as stated in the
|
|
327
|
+
[Unicode specification for compatibility properties](https://unicode.org/reports/tr18/#Compatibility_Properties). If
|
|
328
|
+
there are specific rules for POSIX compatibility, those will be used in the bracket expression instead of the Unicode
|
|
329
|
+
standard form. For all others, the Unicode standard form will be used.
|
|
330
|
+
|
|
331
|
+
The table below outlines the supported POSIX names that can be used in the form `[:name:]` and details the differences
|
|
332
|
+
in rules if using ASCII mode or Unicode mode.
|
|
333
|
+
|
|
334
|
+
\[:posix:] | ASCII | Unicode
|
|
335
|
+
---------- | ------------------------------------------------------------- | -------
|
|
336
|
+
`alpha` | `[a-zA-Z]` | `\p{Alphabetic}`
|
|
337
|
+
`alnum` | `[[:alpha:][:digit:]]` | `[[:alpha:][:digit:]]`
|
|
338
|
+
`blank` | `[ \t]` | `[\p{Zs}\t]`
|
|
339
|
+
`cntrl` | `[\x00-\x1F\x7F]` | `\p{Cc}`
|
|
340
|
+
`digit` | `[0-9]` | `[0-9]`
|
|
341
|
+
`graph` | `[^ [:cntrl:]]` | `[^[:space:][:cntrl:]\p{Cn}\p{Cs}]`
|
|
342
|
+
`lower` | `[a-z]` | `[\p{Lowercase}]`
|
|
343
|
+
`print` | `[[:graph:] ]` | `[[[:graph:][:blank:]]--[[:cntrl:]]]`
|
|
344
|
+
`punct` | ``[!"#$%&'()*+,\-./:;<=>?@[\]^_`{|}~]`` | `[[\p{P}\p{S}]--[\p{alpha}]]`
|
|
345
|
+
`space` | `[ \t\r\n\v\f]` | `[\p{Whitespace}]`
|
|
346
|
+
`upper` | `[A-Z]` | `[\p{Uppercase}]`
|
|
347
|
+
`word` | `[A-Za-z0-9_]` | `[\p{Alnum}\p{M}\p{Pc}\p{JoinControl}]`
|
|
348
|
+
`xdigit` | `[A-Fa-f0-9]` | `[A-Fa-f0-9]`
|
|
349
|
+
|
|
350
|
+
/// Tip
|
|
351
|
+
Using POSIX character classes is equivalent to using Unicode compatibility properties for POSIX, e.g. `[[:punct:]]`
|
|
352
|
+
== `\p{Posix Punct}`.
|
|
353
|
+
///
|
|
354
|
+
|
|
355
|
+
POSIX style character classes have also been extended to allow for specifying normal Unicode properties as well. Any
|
|
356
|
+
Unicode property, outside the compatibility properties, will be accepted in the bracket extension form and behave just
|
|
357
|
+
like normal Unicode properties. To illustrate, the following are equivalent:
|
|
358
|
+
|
|
359
|
+
- `[[:math:]]` == `[\p{math}]`
|
|
360
|
+
- `[[:scx=latin:]]` == `[\p{scx: latin}]`
|
|
361
|
+
- `[[:^scx=latin:]]` == `[\P{scx: latin}]`
|
|
362
|
+
|
|
@@ -79,6 +79,7 @@ class CustomMetadataHook(MetadataHookInterface):
|
|
|
79
79
|
'Programming Language :: Python :: 3.11',
|
|
80
80
|
'Programming Language :: Python :: 3.12',
|
|
81
81
|
'Programming Language :: Python :: 3.13',
|
|
82
|
+
'Programming Language :: Python :: 3.14',
|
|
82
83
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
83
84
|
'Typing :: Typed'
|
|
84
85
|
]
|
|
@@ -4,7 +4,7 @@ repo_url: https://github.com/facelessuser/backrefs
|
|
|
4
4
|
edit_uri: tree/master/docs/src/markdown
|
|
5
5
|
site_description: A library to add additional back references to regular expressions.
|
|
6
6
|
copyright: |
|
|
7
|
-
Copyright © 2015 -
|
|
7
|
+
Copyright © 2015 - 2025 <a href="https://github.com/facelessuser" target="_blank" rel="noopener">Isaac Muse</a>
|
|
8
8
|
|
|
9
9
|
docs_dir: docs/src/markdown
|
|
10
10
|
theme:
|
|
@@ -152,8 +152,6 @@ extra:
|
|
|
152
152
|
social:
|
|
153
153
|
- icon: fontawesome/brands/github
|
|
154
154
|
link: https://github.com/facelessuser
|
|
155
|
-
- icon: fontawesome/brands/discord
|
|
156
|
-
link: https://discord.gg/TWs8Tgr
|
|
157
155
|
|
|
158
156
|
plugins:
|
|
159
157
|
- search
|
|
@@ -135,7 +135,7 @@ legacy_tox_ini = """
|
|
|
135
135
|
[tox]
|
|
136
136
|
isolated_build = true
|
|
137
137
|
envlist =
|
|
138
|
-
py39,py310,py311,py312,py313,
|
|
138
|
+
py39,py310,py311,py312,py313,py314,
|
|
139
139
|
lint
|
|
140
140
|
|
|
141
141
|
[testenv]
|
|
@@ -157,7 +157,7 @@ passenv = *
|
|
|
157
157
|
deps=
|
|
158
158
|
-r requirements/docs.txt
|
|
159
159
|
commands=
|
|
160
|
-
{
|
|
160
|
+
{envbindir}/mkdocs build --clean --verbose --strict
|
|
161
161
|
{envbindir}/pyspelling
|
|
162
162
|
|
|
163
163
|
[testenv:lint]
|
|
@@ -11,6 +11,7 @@ import copy
|
|
|
11
11
|
|
|
12
12
|
PY39_PLUS = (3, 9) <= sys.version_info
|
|
13
13
|
PY311_PLUS = (3, 11) <= sys.version_info
|
|
14
|
+
PY313_PLUS = (3, 13) <= sys.version_info
|
|
14
15
|
|
|
15
16
|
if PY311_PLUS:
|
|
16
17
|
import re._constants as _constants
|
|
@@ -21,6 +22,20 @@ else:
|
|
|
21
22
|
class TestSearchTemplate(unittest.TestCase):
|
|
22
23
|
"""Search template tests."""
|
|
23
24
|
|
|
25
|
+
def test_multi_disable_flag(self):
|
|
26
|
+
"""Test when multiple flags are disabled."""
|
|
27
|
+
|
|
28
|
+
self.assertTrue(bre.match(r'(?-ix:Test Test)', 'Test Test', flags=bre.IGNORECASE | bre.VERBOSE))
|
|
29
|
+
self.assertTrue(bre.match(r'(?s-ix:Test Test)', 'Test Test', flags=bre.IGNORECASE | bre.VERBOSE))
|
|
30
|
+
|
|
31
|
+
def test_inline_unicode(self):
|
|
32
|
+
"""Test inline Unicode/ASCII cases."""
|
|
33
|
+
|
|
34
|
+
self.assertTrue(bre.match(r'\p{N}', '\uff19', flags=bre.ASCII) is None)
|
|
35
|
+
self.assertTrue(bre.match(r'(?u:\p{N})', '\uff19', flags=bre.ASCII) is not None)
|
|
36
|
+
self.assertTrue(bre.match(r'\p{N}', '\uff19', flags=bre.UNICODE) is not None)
|
|
37
|
+
self.assertTrue(bre.match(r'(?a:\p{N})', '\uff19', flags=bre.UNICODE) is None)
|
|
38
|
+
|
|
24
39
|
def test_custom_binary_properties(self):
|
|
25
40
|
"""Test new custom binary properties."""
|
|
26
41
|
|
|
@@ -1986,6 +2001,16 @@ class TestReplaceTemplate(unittest.TestCase):
|
|
|
1986
2001
|
class TestExceptions(unittest.TestCase):
|
|
1987
2002
|
"""Test Exceptions."""
|
|
1988
2003
|
|
|
2004
|
+
def test_bad_flag(self):
|
|
2005
|
+
"""Test bad flag."""
|
|
2006
|
+
|
|
2007
|
+
if PY313_PLUS:
|
|
2008
|
+
with self.assertRaises(re.PatternError):
|
|
2009
|
+
bre.compile(r'(?-i)')
|
|
2010
|
+
else:
|
|
2011
|
+
with self.assertRaises(re.error):
|
|
2012
|
+
bre.compile(r'(?-i)')
|
|
2013
|
+
|
|
1989
2014
|
def test_format_existing_group_no_match_with_index(self):
|
|
1990
2015
|
"""Test format group with no match and attempt at indexing."""
|
|
1991
2016
|
|
|
@@ -20,6 +20,12 @@ PY39_PLUS = (3, 9) <= sys.version_info
|
|
|
20
20
|
class TestSearchTemplate(unittest.TestCase):
|
|
21
21
|
"""Search template tests."""
|
|
22
22
|
|
|
23
|
+
def test_multi_disable_flag(self):
|
|
24
|
+
"""Test when multiple flags are disabled."""
|
|
25
|
+
|
|
26
|
+
self.assertTrue(bregex.match(r'(?-ix:Test Test)', 'Test Test', flags=bregex.IGNORECASE | bregex.VERBOSE))
|
|
27
|
+
self.assertTrue(bregex.match(r'(?s-ix:Test Test)', 'Test Test', flags=bregex.IGNORECASE | bregex.VERBOSE))
|
|
28
|
+
|
|
23
29
|
def test_named_lists_attribute(self):
|
|
24
30
|
"""Test named list attribute."""
|
|
25
31
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Generate a Unicode prop table for Python builds."""
|
|
2
2
|
import sys
|
|
3
3
|
import unicodedata
|
|
4
|
-
import codecs
|
|
5
4
|
import os
|
|
6
5
|
import re
|
|
7
6
|
|
|
@@ -201,7 +200,7 @@ def discover_categories():
|
|
|
201
200
|
|
|
202
201
|
# NF Quick Check categories
|
|
203
202
|
file_name = os.path.join(HOME, 'unicodedata', UNIVERSION, 'DerivedNormalizationProps.txt')
|
|
204
|
-
with
|
|
203
|
+
with open(file_name, 'r', encoding='utf-8') as uf:
|
|
205
204
|
for line in uf:
|
|
206
205
|
if not line.startswith('#'):
|
|
207
206
|
data = line.split('#')[0].split(';')
|
|
@@ -227,7 +226,7 @@ def discover_categories():
|
|
|
227
226
|
binary_props.append(('emoji-data.txt', None))
|
|
228
227
|
|
|
229
228
|
for filename, include in binary_props:
|
|
230
|
-
with
|
|
229
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, filename), 'r', encoding='utf-8') as uf:
|
|
231
230
|
for line in uf:
|
|
232
231
|
if not line.startswith('#'):
|
|
233
232
|
data = line.split('#')[0].split(';')
|
|
@@ -245,7 +244,7 @@ def discover_categories():
|
|
|
245
244
|
def gen_blocks(output, ascii_props=False, append=False, prefix="", aliases=None):
|
|
246
245
|
"""Generate Unicode blocks."""
|
|
247
246
|
|
|
248
|
-
with
|
|
247
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
249
248
|
if not append:
|
|
250
249
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
251
250
|
f.write('%s_blocks: dict[str, str] = {' % prefix)
|
|
@@ -257,7 +256,7 @@ def gen_blocks(output, ascii_props=False, append=False, prefix="", aliases=None)
|
|
|
257
256
|
max_range = MAXUNICODE
|
|
258
257
|
formatter = uniformat
|
|
259
258
|
|
|
260
|
-
with
|
|
259
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'Blocks.txt'), 'r', encoding='utf-8') as uf:
|
|
261
260
|
for line in uf:
|
|
262
261
|
if not line.startswith('#'):
|
|
263
262
|
data = line.split(';')
|
|
@@ -321,7 +320,7 @@ def gen_ccc(output, ascii_props=False, append=False, prefix="", aliases=None):
|
|
|
321
320
|
for v in aliases.get('canonicalcombiningclass', {}).values():
|
|
322
321
|
obj[v] = []
|
|
323
322
|
|
|
324
|
-
with
|
|
323
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'DerivedCombiningClass.txt'), 'r', encoding='utf-8') as uf:
|
|
325
324
|
for line in uf:
|
|
326
325
|
if not line.startswith('#'):
|
|
327
326
|
data = line.split('#')[0].split(';')
|
|
@@ -350,7 +349,7 @@ def gen_ccc(output, ascii_props=False, append=False, prefix="", aliases=None):
|
|
|
350
349
|
# Convert characters values to ranges
|
|
351
350
|
char2range(obj, is_bytes=ascii_props)
|
|
352
351
|
|
|
353
|
-
with
|
|
352
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
354
353
|
if not append:
|
|
355
354
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
356
355
|
# Write out the Unicode properties
|
|
@@ -382,13 +381,13 @@ def gen_scripts(
|
|
|
382
381
|
obj2[v] = []
|
|
383
382
|
|
|
384
383
|
alias = {}
|
|
385
|
-
with
|
|
384
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'PropertyValueAliases.txt'), 'r', encoding='utf-8') as uf:
|
|
386
385
|
for line in uf:
|
|
387
386
|
if line.startswith('sc ;'):
|
|
388
387
|
values = line.split(';')
|
|
389
388
|
alias[format_name(values[1].strip())] = format_name(values[2].strip())
|
|
390
389
|
|
|
391
|
-
with
|
|
390
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, file_name_ext), 'r', encoding='utf-8') as uf:
|
|
392
391
|
for line in uf:
|
|
393
392
|
if not line.startswith('#'):
|
|
394
393
|
data = line.split('#')[0].split(';')
|
|
@@ -404,7 +403,7 @@ def gen_scripts(
|
|
|
404
403
|
|
|
405
404
|
obj2[ext].extend(span)
|
|
406
405
|
|
|
407
|
-
with
|
|
406
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, file_name), 'r', encoding='utf-8') as uf:
|
|
408
407
|
for line in uf:
|
|
409
408
|
if not line.startswith('#'):
|
|
410
409
|
data = line.split('#')[0].split(';')
|
|
@@ -439,7 +438,7 @@ def gen_scripts(
|
|
|
439
438
|
char2range(obj, is_bytes=ascii_props)
|
|
440
439
|
char2range(obj2, is_bytes=ascii_props)
|
|
441
440
|
|
|
442
|
-
with
|
|
441
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
443
442
|
if not append:
|
|
444
443
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
445
444
|
# Write out the Unicode properties
|
|
@@ -454,7 +453,7 @@ def gen_scripts(
|
|
|
454
453
|
f.write(',\n')
|
|
455
454
|
i += 1
|
|
456
455
|
|
|
457
|
-
with
|
|
456
|
+
with open(output_ext, 'a' if append else 'w', encoding='utf-8') as f:
|
|
458
457
|
if not append:
|
|
459
458
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
460
459
|
# Write out the Unicode properties
|
|
@@ -482,7 +481,7 @@ def gen_enum(
|
|
|
482
481
|
for v in aliases.get(format_name(obj_name), {}).values():
|
|
483
482
|
obj[v] = []
|
|
484
483
|
|
|
485
|
-
with
|
|
484
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, file_name), 'r', encoding='utf-8') as uf:
|
|
486
485
|
for line in uf:
|
|
487
486
|
if not line.startswith('#'):
|
|
488
487
|
data = line.split('#')[0].split(';')
|
|
@@ -508,7 +507,7 @@ def gen_enum(
|
|
|
508
507
|
# Convert characters values to ranges
|
|
509
508
|
char2range(obj, is_bytes=ascii_props)
|
|
510
509
|
|
|
511
|
-
with
|
|
510
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
512
511
|
if not append:
|
|
513
512
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
514
513
|
# Write out the Unicode properties
|
|
@@ -535,7 +534,7 @@ def gen_age(output, ascii_props=False, append=False, prefix="", aliases=None):
|
|
|
535
534
|
obj[v] = []
|
|
536
535
|
|
|
537
536
|
all_chars = (ALL_CHARS - ASCII_UNUSED) if ascii_props else ALL_CHARS
|
|
538
|
-
with
|
|
537
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'DerivedAge.txt'), 'r', encoding='utf-8') as uf:
|
|
539
538
|
for line in uf:
|
|
540
539
|
if not line.startswith('#'):
|
|
541
540
|
data = line.split('#')[0].split(';')
|
|
@@ -564,7 +563,7 @@ def gen_age(output, ascii_props=False, append=False, prefix="", aliases=None):
|
|
|
564
563
|
# Convert characters values to ranges
|
|
565
564
|
char2range(obj, is_bytes=ascii_props)
|
|
566
565
|
|
|
567
|
-
with
|
|
566
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
568
567
|
if not append:
|
|
569
568
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
570
569
|
# Write out the Unicode properties
|
|
@@ -586,7 +585,7 @@ def gen_nf_quick_check(output, ascii_props=False, append=False, prefix="", alias
|
|
|
586
585
|
nf = {}
|
|
587
586
|
all_chars = (ALL_CHARS - ASCII_UNUSED) if ascii_props else ALL_CHARS
|
|
588
587
|
file_name = os.path.join(HOME, 'unicodedata', UNIVERSION, 'DerivedNormalizationProps.txt')
|
|
589
|
-
with
|
|
588
|
+
with open(file_name, 'r', encoding='utf-8') as uf:
|
|
590
589
|
for line in uf:
|
|
591
590
|
if not line.startswith('#'):
|
|
592
591
|
data = line.split('#')[0].split(';')
|
|
@@ -626,7 +625,7 @@ def gen_nf_quick_check(output, ascii_props=False, append=False, prefix="", alias
|
|
|
626
625
|
# Convert characters values to ranges
|
|
627
626
|
char2range(nf, is_bytes=ascii_props)
|
|
628
627
|
|
|
629
|
-
with
|
|
628
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
630
629
|
if not append:
|
|
631
630
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
632
631
|
for key, value in sorted(nf.items()):
|
|
@@ -670,7 +669,7 @@ def gen_binary(table, output, ascii_props=False, append=False, prefix="", aliase
|
|
|
670
669
|
binary['vertspace'] = [x for x in binary['vertspace'] if x <= max_range]
|
|
671
670
|
|
|
672
671
|
for filename, include in binary_props:
|
|
673
|
-
with
|
|
672
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, filename), 'r', encoding='utf-8') as uf:
|
|
674
673
|
for line in uf:
|
|
675
674
|
if not line.startswith('#'):
|
|
676
675
|
data = line.split('#')[0].split(';')
|
|
@@ -687,7 +686,7 @@ def gen_binary(table, output, ascii_props=False, append=False, prefix="", aliase
|
|
|
687
686
|
continue
|
|
688
687
|
binary[name].extend(span)
|
|
689
688
|
|
|
690
|
-
with
|
|
689
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'CompositionExclusions.txt'), 'r', encoding='utf-8') as uf:
|
|
691
690
|
name = 'compositionexclusion'
|
|
692
691
|
for line in uf:
|
|
693
692
|
if not line.startswith('#'):
|
|
@@ -703,7 +702,7 @@ def gen_binary(table, output, ascii_props=False, append=False, prefix="", aliase
|
|
|
703
702
|
binary[name].extend(span)
|
|
704
703
|
|
|
705
704
|
file_name = os.path.join(HOME, 'unicodedata', UNIVERSION, 'DerivedNormalizationProps.txt')
|
|
706
|
-
with
|
|
705
|
+
with open(file_name, 'r', encoding='utf-8') as uf:
|
|
707
706
|
name = "fullcompositionexclusion"
|
|
708
707
|
for line in uf:
|
|
709
708
|
if not line.startswith('#'):
|
|
@@ -720,7 +719,7 @@ def gen_binary(table, output, ascii_props=False, append=False, prefix="", aliase
|
|
|
720
719
|
binary[name] = []
|
|
721
720
|
binary[name].extend(span)
|
|
722
721
|
|
|
723
|
-
with
|
|
722
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'UnicodeData.txt'), 'r', encoding='utf-8') as uf:
|
|
724
723
|
name = 'bidimirrored'
|
|
725
724
|
for line in uf:
|
|
726
725
|
data = line.strip().split(';')
|
|
@@ -741,16 +740,6 @@ def gen_binary(table, output, ascii_props=False, append=False, prefix="", aliase
|
|
|
741
740
|
|
|
742
741
|
gen_uposix(table, binary, ascii_props)
|
|
743
742
|
|
|
744
|
-
# One off Unicode property found under https://unicode.org/reports/tr18/#Compatibility_Properties
|
|
745
|
-
# `Word: [\p{alnum}\p{M}\p{Pc}\p{JoinControl}]`
|
|
746
|
-
s = set(binary["alnum"])
|
|
747
|
-
for k, v in table['m'].items():
|
|
748
|
-
if not k.startswith('^'):
|
|
749
|
-
s |= set(v)
|
|
750
|
-
s |= set(table["p"]["c"])
|
|
751
|
-
s |= set(binary["joincontrol"])
|
|
752
|
-
binary["word"] = list(s)
|
|
753
|
-
|
|
754
743
|
if aliases:
|
|
755
744
|
for v in aliases.get('binary', {}).values():
|
|
756
745
|
if v not in binary and '^' + v not in binary:
|
|
@@ -759,7 +748,7 @@ def gen_binary(table, output, ascii_props=False, append=False, prefix="", aliase
|
|
|
759
748
|
# Convert characters values to ranges
|
|
760
749
|
char2range(binary, is_bytes=ascii_props)
|
|
761
750
|
|
|
762
|
-
with
|
|
751
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
763
752
|
if not append:
|
|
764
753
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
765
754
|
# Write out the Unicode properties
|
|
@@ -786,7 +775,7 @@ def gen_bidi(output, ascii_props=False, append=False, prefix="", aliases=None):
|
|
|
786
775
|
bidi_class[v] = []
|
|
787
776
|
|
|
788
777
|
max_range = MAXVALIDASCII if ascii_props else MAXUNICODE
|
|
789
|
-
with
|
|
778
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'UnicodeData.txt'), 'r', encoding='utf-8') as uf:
|
|
790
779
|
for line in uf:
|
|
791
780
|
data = line.strip().split(';')
|
|
792
781
|
if data:
|
|
@@ -810,7 +799,7 @@ def gen_bidi(output, ascii_props=False, append=False, prefix="", aliases=None):
|
|
|
810
799
|
# Convert characters values to ranges
|
|
811
800
|
char2range(bidi_class, is_bytes=ascii_props)
|
|
812
801
|
|
|
813
|
-
with
|
|
802
|
+
with open(output, 'a' if append else 'w', encoding='utf-8') as f:
|
|
814
803
|
if not append:
|
|
815
804
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
816
805
|
f.write('%s_bidi_classes: dict[str, str] = {\n' % prefix)
|
|
@@ -882,9 +871,14 @@ def gen_uposix(table, posix_table, ascii_props):
|
|
|
882
871
|
s -= set(posix_table["posixcntrl"])
|
|
883
872
|
posix_table["posixprint"] = list(s)
|
|
884
873
|
|
|
885
|
-
# `
|
|
886
|
-
s = set(
|
|
887
|
-
|
|
874
|
+
# `Word: [\p{alnum}\p{M}\p{Pc}\p{JoinControl}]`
|
|
875
|
+
s = set(posix_table["alnum"])
|
|
876
|
+
for k, v in table['m'].items():
|
|
877
|
+
if not k.startswith('^'):
|
|
878
|
+
s |= set(v)
|
|
879
|
+
s |= set(table["p"]["c"])
|
|
880
|
+
s |= set(posix_table["joincontrol"])
|
|
881
|
+
posix_table['posixword'] = list(s)
|
|
888
882
|
|
|
889
883
|
|
|
890
884
|
def gen_alias(nonbinary, binary, output):
|
|
@@ -905,15 +899,17 @@ def gen_alias(nonbinary, binary, output):
|
|
|
905
899
|
'posixlower': 'lowercase',
|
|
906
900
|
'posixupper': 'uppercase',
|
|
907
901
|
'posixspace': 'whitespace',
|
|
902
|
+
'posixascii': 'basiclatin',
|
|
908
903
|
'blank': 'posixblank',
|
|
909
904
|
'graph': 'posixgraph',
|
|
910
|
-
'print': 'posixprint'
|
|
905
|
+
'print': 'posixprint',
|
|
906
|
+
'word': 'posixword'
|
|
911
907
|
}
|
|
912
908
|
toplevel = (
|
|
913
909
|
'catalog', 'enumerated', 'numeric', 'miscellaneous'
|
|
914
910
|
)
|
|
915
911
|
|
|
916
|
-
with
|
|
912
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'PropertyAliases.txt'), 'r', encoding='utf-8') as uf:
|
|
917
913
|
div = False
|
|
918
914
|
capture = False
|
|
919
915
|
name = None
|
|
@@ -952,7 +948,7 @@ def gen_alias(nonbinary, binary, output):
|
|
|
952
948
|
for d in data[1:]:
|
|
953
949
|
alias[name][d] = data[0]
|
|
954
950
|
|
|
955
|
-
with
|
|
951
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'PropertyValueAliases.txt'), 'r', encoding='utf-8') as uf:
|
|
956
952
|
for line in uf:
|
|
957
953
|
m = alias_re.match(line)
|
|
958
954
|
if m:
|
|
@@ -992,7 +988,7 @@ def gen_alias(nonbinary, binary, output):
|
|
|
992
988
|
alias['binary']['h'] = 'horizspace'
|
|
993
989
|
alias['binary']['v'] = 'vertspace'
|
|
994
990
|
|
|
995
|
-
with
|
|
991
|
+
with open(output, 'w', encoding='utf-8') as f:
|
|
996
992
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
997
993
|
f.write('%s_alias: dict[str, dict[str, str]] = {\n' % prefix)
|
|
998
994
|
count = len(alias) - 1
|
|
@@ -1037,7 +1033,7 @@ def gen_properties(output, files, aliases, ascii_props=False, append=False):
|
|
|
1037
1033
|
# before sending it through.
|
|
1038
1034
|
table = {'l': {'c': []}}
|
|
1039
1035
|
itable = {'l': {}}
|
|
1040
|
-
with
|
|
1036
|
+
with open(os.path.join(HOME, 'unicodedata', UNIVERSION, 'UnicodeData.txt'), 'r', encoding='utf-8') as uf:
|
|
1041
1037
|
for line in uf:
|
|
1042
1038
|
data = line.strip().split(';')
|
|
1043
1039
|
if data:
|
|
@@ -1194,7 +1190,7 @@ def gen_properties(output, files, aliases, ascii_props=False, append=False):
|
|
|
1194
1190
|
for k1, v1 in itable.items():
|
|
1195
1191
|
table[k1]['^'] = v1['^']
|
|
1196
1192
|
|
|
1197
|
-
with
|
|
1193
|
+
with open(files['gc'], 'a' if append else 'w', encoding='utf-8') as f:
|
|
1198
1194
|
if not append:
|
|
1199
1195
|
f.write(HEADER.format(UNIVERSION, TYPING))
|
|
1200
1196
|
# Write out the Unicode properties
|
|
@@ -1219,7 +1215,7 @@ def gen_properties(output, files, aliases, ascii_props=False, append=False):
|
|
|
1219
1215
|
i += 1
|
|
1220
1216
|
|
|
1221
1217
|
if not append:
|
|
1222
|
-
with
|
|
1218
|
+
with open(os.path.join(output, '__init__.py'), 'w') as f:
|
|
1223
1219
|
f.write(HEADER.format(UNIVERSION, ''))
|
|
1224
1220
|
for x in sorted(files):
|
|
1225
1221
|
f.write('from .%s import * # noqa\n' % os.path.basename(files[x])[:-3])
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|