bracex 3.1__tar.gz → 3.2__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.
- {bracex-3.1 → bracex-3.2}/.pyspelling.yml +1 -1
- {bracex-3.1 → bracex-3.2}/PKG-INFO +2 -3
- {bracex-3.1 → bracex-3.2}/bracex/__init__.py +17 -13
- {bracex-3.1 → bracex-3.2}/bracex/__meta__.py +1 -1
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/changelog.md +7 -0
- {bracex-3.1 → bracex-3.2}/hatch_build.py +0 -1
- {bracex-3.1 → bracex-3.2}/pyproject.toml +4 -7
- {bracex-3.1 → bracex-3.2}/tests/brace-cases.txt +26 -0
- {bracex-3.1 → bracex-3.2}/tests/brace-results.txt +28 -1
- {bracex-3.1 → bracex-3.2}/tests/test_brace.py +28 -0
- {bracex-3.1 → bracex-3.2}/.gitignore +0 -0
- {bracex-3.1 → bracex-3.2}/LICENSE.md +0 -0
- {bracex-3.1 → bracex-3.2}/README.md +0 -0
- {bracex-3.1 → bracex-3.2}/bracex/__main__.py +0 -0
- {bracex-3.1 → bracex-3.2}/bracex/py.typed +0 -0
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/.snippets/abbr.md +0 -0
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/.snippets/links.md +0 -0
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/.snippets/refs.md +0 -0
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/contributing.md +0 -0
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/development.md +0 -0
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/license.md +0 -0
- {bracex-3.1 → bracex-3.2}/docs/src/markdown/index.md +0 -0
- {bracex-3.1 → bracex-3.2}/docs/theme/announce.html +0 -0
- {bracex-3.1 → bracex-3.2}/tests/__init__.py +0 -0
- {bracex-3.1 → bracex-3.2}/tests/generate.sh +0 -0
- {bracex-3.1 → bracex-3.2}/tests/test_main.py +0 -0
- {bracex-3.1 → bracex-3.2}/tests/test_versions.py +0 -0
- {bracex-3.1 → bracex-3.2}/tools/pycon.py +0 -0
|
@@ -20,7 +20,7 @@ matrix:
|
|
|
20
20
|
- alt
|
|
21
21
|
ignores:
|
|
22
22
|
- 'code, pre'
|
|
23
|
-
- 'a:is(.magiclink-compare, .magiclink-commit, .magiclink-repository)'
|
|
23
|
+
- 'a:is(.magiclink-compare, .magiclink-commit, .magiclink-repository, .magiclink-mention)'
|
|
24
24
|
- 'span.keys'
|
|
25
25
|
- '.MathJax_Preview, .md-nav__link, .md-footer-custom-text, .md-source__repository, .headerlink, .md-icon'
|
|
26
26
|
- '.md-social__link'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: bracex
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2
|
|
4
4
|
Summary: Bash style brace expander.
|
|
5
5
|
Project-URL: Homepage, https://github.com/facelessuser/bracex
|
|
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.10
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -21,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.15
|
|
22
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
22
|
Classifier: Typing :: Typed
|
|
24
|
-
Requires-Python: >=3.
|
|
23
|
+
Requires-Python: >=3.11
|
|
25
24
|
Description-Content-Type: text/markdown
|
|
26
25
|
|
|
27
26
|
[![Donate via PayPal][donate-image]][donate-link]
|
|
@@ -396,14 +396,18 @@ class ExpandBrace:
|
|
|
396
396
|
except StopIteration:
|
|
397
397
|
self.release_expanding(release)
|
|
398
398
|
|
|
399
|
+
# Handle empty slot
|
|
400
|
+
if has_comma and is_empty:
|
|
401
|
+
result.append(EMPTY)
|
|
402
|
+
|
|
399
403
|
# Sequence is not valid
|
|
400
404
|
temp2 = iter(['{']) # type: Iterator[str]
|
|
401
405
|
l = len(result)
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
406
|
+
for e, slot in enumerate(result):
|
|
407
|
+
member = iter([',' if e else '']) # type: Iterator[str]
|
|
408
|
+
for r in self.flatten([slot]):
|
|
409
|
+
member = self.squash(member, [r] if isinstance(r, str) else r)
|
|
410
|
+
temp2 = self.squash(temp2, member)
|
|
407
411
|
return temp2, self.account(math.prod(counts, start=1))
|
|
408
412
|
|
|
409
413
|
def get_range(self, i: StringIter) -> tuple[Iterator[str] | None, int]:
|
|
@@ -438,19 +442,19 @@ class ExpandBrace:
|
|
|
438
442
|
def get_int_range(self, m: re.Match[str]) -> tuple[Iterator[str], int]:
|
|
439
443
|
"""Get an integer range between start and end and increments of increment."""
|
|
440
444
|
|
|
441
|
-
# Capture zero padding
|
|
442
|
-
# 64-bit
|
|
445
|
+
# Capture zero padding and at most 20 significant digits. The extra digit ensures
|
|
446
|
+
# values longer than a 64-bit integer are rejected instead of silently truncated.
|
|
443
447
|
spad = m.start(2), m.end(2)
|
|
444
448
|
off = m.start(1)
|
|
445
|
-
start = m.group(1)[:spad[0] - off] + m.group(1)[spad[1] - off:spad[1] - off +
|
|
449
|
+
start = m.group(1)[:spad[0] - off] + m.group(1)[spad[1] - off:spad[1] - off + 20]
|
|
446
450
|
|
|
447
451
|
epad = m.start(4), m.end(4)
|
|
448
452
|
off = m.start(3)
|
|
449
|
-
end = m.group(3)[:epad[0] - off] + m.group(3)[epad[1] - off :epad[1] - off +
|
|
453
|
+
end = m.group(3)[:epad[0] - off] + m.group(3)[epad[1] - off :epad[1] - off + 20]
|
|
450
454
|
|
|
451
455
|
ipad = m.start(6), m.end(6)
|
|
452
456
|
off = m.start(5)
|
|
453
|
-
increment = m.group(5)[0:ipad[0] - off] + m.group(5)[ipad[1] - off:ipad[1] - off +
|
|
457
|
+
increment = m.group(5)[0:ipad[0] - off] + m.group(5)[ipad[1] - off:ipad[1] - off + 20] if m.group(5) else '1'
|
|
454
458
|
|
|
455
459
|
# Ensure values are within 64 bit range.
|
|
456
460
|
first = int64(start)
|
|
@@ -464,12 +468,12 @@ class ExpandBrace:
|
|
|
464
468
|
if first < last:
|
|
465
469
|
span = abs(last - first + 1)
|
|
466
470
|
ainc = abs(inc)
|
|
467
|
-
count =
|
|
471
|
+
count = (span + ainc - 1) // ainc if ainc <= span else 1
|
|
468
472
|
r = range(first, last + 1, inc)
|
|
469
473
|
else:
|
|
470
474
|
span = abs(first - last + 1)
|
|
471
475
|
ainc = abs(inc)
|
|
472
|
-
count =
|
|
476
|
+
count = (span + ainc - 1) // ainc if ainc <= span else 1
|
|
473
477
|
r = range(first, last - 1, -inc)
|
|
474
478
|
|
|
475
479
|
return self.format_values(r, padding), count
|
|
@@ -484,7 +488,7 @@ class ExpandBrace:
|
|
|
484
488
|
# 64-bit integers are no longer than 19 digits.
|
|
485
489
|
ipad = m.start(4), m.end(4)
|
|
486
490
|
off = m.start(3)
|
|
487
|
-
increment = m.group(3)[0:ipad[0] - off] + m.group(3)[ipad[1] - off:ipad[1] - off +
|
|
491
|
+
increment = m.group(3)[0:ipad[0] - off] + m.group(3)[ipad[1] - off:ipad[1] - off + 20] if m.group(3) else '1'
|
|
488
492
|
|
|
489
493
|
# Ensure values are within 64 bit range.
|
|
490
494
|
inc = max(1, int64(increment))
|
|
@@ -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(3,
|
|
196
|
+
__version_info__ = Version(3, 2, 0, "final")
|
|
197
197
|
__version__ = __version_info__._get_canonical()
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 3.2
|
|
4
|
+
|
|
5
|
+
- **NEW**: Drop Python 3.10 support.
|
|
6
|
+
- **FIX**: Fix some division that could cause 1 off issues with limit checking.
|
|
7
|
+
- **FIX**: Fix issues related to large range numbers and truncation when compared to Bash (@Rexyysilent).
|
|
8
|
+
- **FIX**: Fix commas dropped or added when rebuilding invalid brace groups.
|
|
9
|
+
|
|
3
10
|
## 3.1
|
|
4
11
|
|
|
5
12
|
- **NEW**: Add support for Python 3.15.
|
|
@@ -28,7 +28,6 @@ class CustomMetadataHook(MetadataHookInterface):
|
|
|
28
28
|
'License :: OSI Approved :: MIT License',
|
|
29
29
|
'Operating System :: OS Independent',
|
|
30
30
|
'Programming Language :: Python :: 3',
|
|
31
|
-
'Programming Language :: Python :: 3.10',
|
|
32
31
|
'Programming Language :: Python :: 3.11',
|
|
33
32
|
'Programming Language :: Python :: 3.12',
|
|
34
33
|
'Programming Language :: Python :: 3.13',
|
|
@@ -9,7 +9,7 @@ name = "bracex"
|
|
|
9
9
|
description = "Bash style brace expander."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
license = "MIT"
|
|
12
|
-
requires-python = ">=3.
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
13
|
authors = [
|
|
14
14
|
{ name = "Isaac Muse", email = "Isaac.Muse@gmail.com" },
|
|
15
15
|
]
|
|
@@ -127,17 +127,14 @@ dependencies = [
|
|
|
127
127
|
"pymdown_lexers @ git+https://github.com/facelessuser/pymdown-lexers.git",
|
|
128
128
|
"pyspelling",
|
|
129
129
|
"build",
|
|
130
|
-
"python-minifier"
|
|
131
|
-
"beautifulsoup4",
|
|
132
|
-
"lxml",
|
|
133
|
-
"html5lib"
|
|
130
|
+
"python-minifier"
|
|
134
131
|
]
|
|
135
132
|
|
|
136
133
|
[[tool.hatch.envs.dev.matrix]]
|
|
137
|
-
python = ["3.
|
|
134
|
+
python = ["3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
138
135
|
|
|
139
136
|
[[tool.hatch.envs.dev-stdlib.matrix]]
|
|
140
|
-
python = ["3.
|
|
137
|
+
python = ["3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
141
138
|
|
|
142
139
|
[tool.hatch.envs.dev.scripts]
|
|
143
140
|
types = "mypy"
|
|
@@ -263,3 +263,29 @@ x{{
|
|
|
263
263
|
{9223372036854775807..9223372036854775808}
|
|
264
264
|
{-9223372036854775808..-9223372036854775805}
|
|
265
265
|
{-9223372036854775809..-9223372036854775807}
|
|
266
|
+
{10000000000000000000..1000000000000000001}
|
|
267
|
+
{1000000000000000000..10000000000000000000}
|
|
268
|
+
{-10000000000000000000..-1000000000000000001}
|
|
269
|
+
{-1000000000000000000..-10000000000000000000}
|
|
270
|
+
{10000000000000000000..10000000000000000001}
|
|
271
|
+
{100000000000000000000..1000000000000000001}
|
|
272
|
+
{0010000000000000000000..1000000000000000001}
|
|
273
|
+
{1000000000000000000..0010000000000000000000}
|
|
274
|
+
{1..3..10000000000000000000}
|
|
275
|
+
{3..1..-10000000000000000000}
|
|
276
|
+
{1..3..0010000000000000000000}
|
|
277
|
+
{a..c..10000000000000000000}
|
|
278
|
+
{c..a..-10000000000000000000}
|
|
279
|
+
{a..c..0010000000000000000000
|
|
280
|
+
# Unclosed groups keep their commas
|
|
281
|
+
{a},{b}
|
|
282
|
+
{x},{y},{z}
|
|
283
|
+
{x},
|
|
284
|
+
a{b},
|
|
285
|
+
{a,
|
|
286
|
+
{a,,
|
|
287
|
+
{,
|
|
288
|
+
{a,{b,c}
|
|
289
|
+
{a,b{c,d}
|
|
290
|
+
{a,b{
|
|
291
|
+
{2{
|
|
@@ -1235,4 +1235,31 @@ A{b,{d,e},{f,g}}Z
|
|
|
1235
1235
|
[-9223372036854775807]
|
|
1236
1236
|
[-9223372036854775806]
|
|
1237
1237
|
[-9223372036854775805]><><><><{-9223372036854775809..-9223372036854775807}
|
|
1238
|
-
[{-9223372036854775809..-9223372036854775807}]><><><><
|
|
1238
|
+
[{-9223372036854775809..-9223372036854775807}]><><><><{10000000000000000000..1000000000000000001}
|
|
1239
|
+
[{10000000000000000000..1000000000000000001}]><><><><{1000000000000000000..10000000000000000000}
|
|
1240
|
+
[{1000000000000000000..10000000000000000000}]><><><><{-10000000000000000000..-1000000000000000001}
|
|
1241
|
+
[{-10000000000000000000..-1000000000000000001}]><><><><{-1000000000000000000..-10000000000000000000}
|
|
1242
|
+
[{-1000000000000000000..-10000000000000000000}]><><><><{10000000000000000000..10000000000000000001}
|
|
1243
|
+
[{10000000000000000000..10000000000000000001}]><><><><{100000000000000000000..1000000000000000001}
|
|
1244
|
+
[{100000000000000000000..1000000000000000001}]><><><><{0010000000000000000000..1000000000000000001}
|
|
1245
|
+
[{0010000000000000000000..1000000000000000001}]><><><><{1000000000000000000..0010000000000000000000}
|
|
1246
|
+
[{1000000000000000000..0010000000000000000000}]><><><><{1..3..10000000000000000000}
|
|
1247
|
+
[{1..3..10000000000000000000}]><><><><{3..1..-10000000000000000000}
|
|
1248
|
+
[{3..1..-10000000000000000000}]><><><><{1..3..0010000000000000000000}
|
|
1249
|
+
[{1..3..0010000000000000000000}]><><><><{a..c..10000000000000000000}
|
|
1250
|
+
[{a..c..10000000000000000000}]><><><><{c..a..-10000000000000000000}
|
|
1251
|
+
[{c..a..-10000000000000000000}]><><><><{a..c..0010000000000000000000
|
|
1252
|
+
[{a..c..0010000000000000000000]><><><><{a},{b}
|
|
1253
|
+
[{a},{b}]><><><><{x},{y},{z}
|
|
1254
|
+
[{x},{y},{z}]><><><><{x},
|
|
1255
|
+
[{x},]><><><><a{b},
|
|
1256
|
+
[a{b},]><><><><{a,
|
|
1257
|
+
[{a,]><><><><{a,,
|
|
1258
|
+
[{a,,]><><><><{,
|
|
1259
|
+
[{,]><><><><{a,{b,c}
|
|
1260
|
+
[{a,b]
|
|
1261
|
+
[{a,c]><><><><{a,b{c,d}
|
|
1262
|
+
[{a,bc]
|
|
1263
|
+
[{a,bd]><><><><{a,b{
|
|
1264
|
+
[{a,b{]><><><><{2{
|
|
1265
|
+
[{2{]><><><><
|
|
@@ -379,6 +379,34 @@ class TestExpansionLimit(unittest.TestCase):
|
|
|
379
379
|
with self.assertRaises(bracex.ExpansionLimitException):
|
|
380
380
|
bracex.expand('{11..1}', limit=10)
|
|
381
381
|
|
|
382
|
+
def test_large_integer_range_limit_boundaries(self):
|
|
383
|
+
"""Count large integer ranges exactly at the expansion limit."""
|
|
384
|
+
|
|
385
|
+
limit = 1000
|
|
386
|
+
step = 2**54
|
|
387
|
+
ranges = (
|
|
388
|
+
(-(2**63), 1),
|
|
389
|
+
(2**63 - 1, -1),
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
for start, direction in ranges:
|
|
393
|
+
with self.subTest(start=start):
|
|
394
|
+
at_limit_end = start + direction * (limit - 1) * step
|
|
395
|
+
over_limit_end = start + direction * limit * step
|
|
396
|
+
at_limit = f'{{{start}..{at_limit_end}..{step}}}'
|
|
397
|
+
over_limit = f'{{{start}..{over_limit_end}..{step}}}'
|
|
398
|
+
|
|
399
|
+
results = bracex.expand(at_limit, limit=limit)
|
|
400
|
+
self.assertEqual(len(results), limit)
|
|
401
|
+
self.assertEqual(results[0], str(start))
|
|
402
|
+
self.assertEqual(results[-1], str(at_limit_end))
|
|
403
|
+
|
|
404
|
+
with self.assertRaises(bracex.ExpansionLimitException):
|
|
405
|
+
bracex.expand(over_limit, limit=limit)
|
|
406
|
+
|
|
407
|
+
self.assertEqual(len(bracex.expand(over_limit, limit=limit + 1)), limit + 1)
|
|
408
|
+
self.assertEqual(len(bracex.expand(over_limit, limit=0)), limit + 1)
|
|
409
|
+
|
|
382
410
|
def test_expansion_num_negative_limt_expand(self):
|
|
383
411
|
"""Test expansion numeric negative limit with `expand`."""
|
|
384
412
|
|
|
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
|