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.
Files changed (28) hide show
  1. {bracex-3.1 → bracex-3.2}/.pyspelling.yml +1 -1
  2. {bracex-3.1 → bracex-3.2}/PKG-INFO +2 -3
  3. {bracex-3.1 → bracex-3.2}/bracex/__init__.py +17 -13
  4. {bracex-3.1 → bracex-3.2}/bracex/__meta__.py +1 -1
  5. {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/changelog.md +7 -0
  6. {bracex-3.1 → bracex-3.2}/hatch_build.py +0 -1
  7. {bracex-3.1 → bracex-3.2}/pyproject.toml +4 -7
  8. {bracex-3.1 → bracex-3.2}/tests/brace-cases.txt +26 -0
  9. {bracex-3.1 → bracex-3.2}/tests/brace-results.txt +28 -1
  10. {bracex-3.1 → bracex-3.2}/tests/test_brace.py +28 -0
  11. {bracex-3.1 → bracex-3.2}/.gitignore +0 -0
  12. {bracex-3.1 → bracex-3.2}/LICENSE.md +0 -0
  13. {bracex-3.1 → bracex-3.2}/README.md +0 -0
  14. {bracex-3.1 → bracex-3.2}/bracex/__main__.py +0 -0
  15. {bracex-3.1 → bracex-3.2}/bracex/py.typed +0 -0
  16. {bracex-3.1 → bracex-3.2}/docs/src/markdown/.snippets/abbr.md +0 -0
  17. {bracex-3.1 → bracex-3.2}/docs/src/markdown/.snippets/links.md +0 -0
  18. {bracex-3.1 → bracex-3.2}/docs/src/markdown/.snippets/refs.md +0 -0
  19. {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/contributing.md +0 -0
  20. {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/development.md +0 -0
  21. {bracex-3.1 → bracex-3.2}/docs/src/markdown/about/license.md +0 -0
  22. {bracex-3.1 → bracex-3.2}/docs/src/markdown/index.md +0 -0
  23. {bracex-3.1 → bracex-3.2}/docs/theme/announce.html +0 -0
  24. {bracex-3.1 → bracex-3.2}/tests/__init__.py +0 -0
  25. {bracex-3.1 → bracex-3.2}/tests/generate.sh +0 -0
  26. {bracex-3.1 → bracex-3.2}/tests/test_main.py +0 -0
  27. {bracex-3.1 → bracex-3.2}/tests/test_versions.py +0 -0
  28. {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.1
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.10
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
- last_str = False
403
- for r in self.flatten(result):
404
- is_str = isinstance(r, str)
405
- temp2 = self.squash(temp2, [(',' if last_str else '') + r] if is_str else r)
406
- last_str = is_str
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 extent and capture numerical values without padding and limited to 19 digits.
442
- # 64-bit integers are no longer than 19 digits.
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 + 19]
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 + 19]
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 + 19] if m.group(5) else '1'
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 = math.ceil(span / ainc) if ainc <= span else 1
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 = math.ceil(span / ainc) if ainc <= span else 1
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 + 19] if m.group(3) else '1'
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, 1, 0, "final")
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.10"
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.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
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.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
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