PythonIota 1.8.0__tar.gz → 1.9.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 (59) hide show
  1. {pythoniota-1.8.0 → pythoniota-1.9.0}/PKG-INFO +116 -12
  2. pythoniota-1.8.0/src/PythonIota.egg-info/PKG-INFO → pythoniota-1.9.0/README.md +642 -555
  3. {pythoniota-1.8.0 → pythoniota-1.9.0}/pyproject.toml +1 -1
  4. pythoniota-1.8.0/README.md → pythoniota-1.9.0/src/PythonIota.egg-info/PKG-INFO +659 -538
  5. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/PythonIota.egg-info/SOURCES.txt +4 -0
  6. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/__init__.py +1 -1
  7. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/cli/_argument.py +3 -2
  8. pythoniota-1.9.0/src/pythoniota/cli/_completion.py +222 -0
  9. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/cli/_formatter.py +12 -3
  10. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/cli/_parser.py +99 -26
  11. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/cli/_sources.py +26 -8
  12. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/regex/_compiler.py +22 -1
  13. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/regex/_parser.py +4 -0
  14. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/regex/_pattern.py +13 -5
  15. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/regex/_vm.py +26 -8
  16. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/sequence.py +161 -16
  17. pythoniota-1.9.0/tests/test_cli_groups_version.py +347 -0
  18. pythoniota-1.9.0/tests/test_cli_scripts.py +316 -0
  19. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_integration.py +1 -1
  20. pythoniota-1.9.0/tests/test_regex_search.py +169 -0
  21. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_sequence.py +2 -2
  22. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_sequence_typing.py +50 -0
  23. pythoniota-1.9.0/tests/test_sequence_utilities.py +507 -0
  24. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_workflows.py +29 -0
  25. pythoniota-1.8.0/src/pythoniota/cli/_completion.py +0 -112
  26. {pythoniota-1.8.0 → pythoniota-1.9.0}/MANIFEST.in +0 -0
  27. {pythoniota-1.8.0 → pythoniota-1.9.0}/setup.cfg +0 -0
  28. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/PythonIota.egg-info/dependency_links.txt +0 -0
  29. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/PythonIota.egg-info/top_level.txt +0 -0
  30. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/_bitflag.py +0 -0
  31. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/_compat.py +0 -0
  32. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/_safe_eval.py +0 -0
  33. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/cli/__init__.py +0 -0
  34. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/cli/_errors.py +0 -0
  35. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/cli/_namespace.py +0 -0
  36. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/enum.py +0 -0
  37. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/py.typed +0 -0
  38. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/recipes.py +0 -0
  39. {pythoniota-1.8.0 → pythoniota-1.9.0}/src/pythoniota/regex/__init__.py +0 -0
  40. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/__init__.py +0 -0
  41. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_advanced.py +0 -0
  42. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_bitflags.py +0 -0
  43. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_cli.py +0 -0
  44. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_cli_completion.py +0 -0
  45. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_cli_sources.py +0 -0
  46. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_compat.py +0 -0
  47. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_coverage_fill.py +0 -0
  48. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_coverage_fill2.py +0 -0
  49. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_coverage_fill3.py +0 -0
  50. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_enum.py +0 -0
  51. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_enum_enhanced.py +0 -0
  52. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_new_features.py +0 -0
  53. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_recipes.py +0 -0
  54. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_regex.py +0 -0
  55. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_regex_budget.py +0 -0
  56. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_regex_extended.py +0 -0
  57. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_safe_eval.py +0 -0
  58. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_serialization.py +0 -0
  59. {pythoniota-1.8.0 → pythoniota-1.9.0}/tests/test_string_enum.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PythonIota
3
- Version: 1.8.0
3
+ Version: 1.9.0
4
4
  Summary: A zero-dependency Python toolkit: Go-style iota enums, sequence generators, a from-scratch regex engine, and an argparse-style CLI parser
5
5
  Author: Equinox
6
6
  License: MIT
@@ -26,6 +26,24 @@ pip install PythonIota
26
26
 
27
27
  Requires Python 3.10+.
28
28
 
29
+ ## 1.9.0
30
+
31
+ ### New features
32
+
33
+ - Regex searches skip impossible starts using a conservative literal prefix;
34
+ diagnostics expose the prefix and account for prefilter operations.
35
+ - CLI mutually exclusive groups, version output and static completion scripts for
36
+ Bash, Zsh and PowerShell extend the existing metadata-only completion API.
37
+ - Sequence pipelines add bounded `limit`, grouping, Top-K, partitioning, iterator
38
+ copies and combinatorics, with protection for known non-exhausting inputs.
39
+
40
+ ### Fixes
41
+
42
+ - Pattern backreferences now fail explicitly instead of silently matching digits.
43
+ - Out-of-range regex positions are clamped to the text, matching `re` semantics.
44
+ - Counted nullable repetitions stop after zero-width iterations without losing
45
+ the preferred non-empty alternative during iteration.
46
+
29
47
  ## 1.8.0
30
48
 
31
49
  ### New features
@@ -294,9 +312,50 @@ with element types preserved through generic transformations. `take(n)` still
294
312
  returns a list. Chunk/window sizes must be positive and are checked before consumption.
295
313
 
296
314
  Base sequences and their `.map()` results remain reusable. Iterator pipelines
297
- have no length or infinite-status metadata: exhaustive terminals such as
298
- `to_list()`, `sum()` and `last()` can run forever on an unbounded stream. Take a
299
- bounded prefix first when needed.
315
+ have no length, but expose `infinite`: `True` for known non-exhausting sources,
316
+ `False` for known finite sources, and `None` when unknown. Exhaustive terminals
317
+ reject known non-exhausting pipelines before consuming them. Filtering/distinct
318
+ can yield few values without ever exhausting their infinite input; these retain
319
+ the protection. Arbitrary generators and early-stopping `takewhile` may remain
320
+ unknown, so this is not a general termination detector.
321
+
322
+ Use `.limit(n)` to keep a bounded prefix chainable (`take(n)` still returns a list):
323
+
324
+ ```python
325
+ iota().filter(lambda x: x % 2 == 0).limit(5).sum() # 20
326
+ ```
327
+
328
+ `limit` bounds the number of returned elements, not the time to produce one:
329
+ `iota().filter(lambda x: False).limit(1)` still cannot yield a value. Short-circuit
330
+ operations (`first`, `find`, `any`, `all`, `take`) remain available on infinite input.
331
+
332
+ ### Grouping, selection and iterator utilities
333
+
334
+ ```python
335
+ iota(6).group_by(lambda x: x % 2) # {0: [0, 2, 4], 1: [1, 3, 5]}
336
+ iota(10).top_k(3) # [9, 8, 7]
337
+ iota(10).top_k(3, largest=False) # [0, 1, 2]
338
+ iota(2).zip_longest(["a"], fillvalue="-").to_list() # [(0, 'a'), (1, '-')]
339
+ iota(2).product(["a", "b"]).to_list() # [(0, 'a'), (0, 'b'), (1, 'a'), (1, 'b')]
340
+ iota(3).permutations(2).take(3) # [(0, 1), (0, 2), (1, 0)]
341
+
342
+ odds, evens = iota(6).partition(lambda x: x % 2 == 0)
343
+ odds.to_list(), evens.to_list() # ([1, 3, 5], [0, 2, 4])
344
+ left, right = iota(3).tee()
345
+ left.take(2), right.to_list() # ([0, 1], [0, 1, 2])
346
+ ```
347
+
348
+ `group_by(key=None)` groups globally into lists, preserving key/element insertion
349
+ order; unlike `itertools.groupby`, equal keys need not be adjacent. `top_k` supports
350
+ `key=`, preserves input order for ties and keeps O(k) selection storage, but must
351
+ consume the entire finite input. `top_k(0)` returns immediately without consuming.
352
+ Neither operation can compute a global result from an infinite stream.
353
+
354
+ `partition` returns false/true branches and evaluates its predicate once per item.
355
+ Like `tee`, it buffers when consumers advance at different rates; that buffer can
356
+ grow without bound. `product` and `permutations` defer input consumption until
357
+ iteration, then cache their inputs like `itertools`; they reject known infinite
358
+ inputs and are not constant-memory streaming operations.
300
359
 
301
360
  ### Terminal operations
302
361
 
@@ -411,9 +470,10 @@ regex.findall(r"[a-z]+", "AbCdEf", regex.IGNORECASE) # ['AbCdEf']
411
470
  regex.findall(r"\d+(?=px)", "10px 20em 30px") # ['10', '30'] (lookahead)
412
471
  ```
413
472
 
414
- Backreferences (`\1`) are intentionally not supported; this is an NFA-based
415
- engine rather than a general backtracking engine. Empty matches follow the
416
- iteration and splitting behavior of Python 3.10–3.13 `re`, including a non-empty
473
+ Pattern backreferences (`\1`, `(?P=name)`) raise `RegexError`; this is an NFA-based
474
+ engine rather than a general backtracking engine. Replacement templates still
475
+ support backreferences. Patterns are text strings, not bytes. Empty matches follow
476
+ the iteration and splitting behavior of Python 3.10–3.13 `re`, including a non-empty
417
477
  match immediately after an empty one at the same position.
418
478
 
419
479
  ```python
@@ -434,10 +494,16 @@ report = pattern.diagnose("aaaa!", max_steps=1000)
434
494
  print(report["status"], report["steps"], report["op_counts"])
435
495
  ```
436
496
 
497
+ Search and iteration use a conservative mandatory literal prefix when available.
498
+ `explain()["literal_prefix"]` shows it; case-insensitive patterns and leading
499
+ branching/nullable patterns fall back to the normal VM. Existing higher-priority
500
+ threads keep running: the optimization does not restart the VM for each candidate.
501
+
437
502
  All matching, iteration, splitting and replacement functions accept keyword-only
438
503
  `max_steps` (nonnegative integer) and `timeout` (nonnegative seconds), defaulting
439
- to no limits. A step is a bytecode analysis/visit, including epsilon closure and
440
- consuming states; counts are implementation-specific, not a portable time unit.
504
+ to no limits. A step is a bytecode analysis/visit or one native prefix lookup
505
+ (`PREFILTER`), including epsilon closure and consuming states; counts are
506
+ implementation-specific, not a portable time unit or a per-character work bound.
441
507
  Nested lookarounds and all matches within one call share a single budget.
442
508
  `finditer` starts its timeout when created, including pauses between yields.
443
509
 
@@ -481,6 +547,28 @@ Bad input prints a usage line and raises `SystemExit(2)` — the same contract a
481
547
  argparse, so it drops into scripts and tests unchanged. `parse_known_args`
482
548
  returns `(namespace, extras)` instead of erroring on unknown tokens.
483
549
 
550
+ ### Mutually exclusive options and version output
551
+
552
+ ```python
553
+ p = cli.ArgumentParser(prog="convert")
554
+ mode = p.add_mutually_exclusive_group(required=True)
555
+ mode.add_argument("--json", action="store_true")
556
+ mode.add_argument("--text", action="store_true")
557
+ p.add_argument("--version", action="version", version="%(prog)s 1.0")
558
+ p.parse_args(["--json"])
559
+ ```
560
+
561
+ Groups accept optional arguments only. A required group needs exactly one explicit
562
+ member; an optional group permits zero or one. CLI, environment and configuration
563
+ values count as explicit, even `False` or zero; defaults and caller `Namespace`
564
+ values do not. CLI overrides lower sources for the same destination, not another
565
+ group member: CLI `--json` plus config `text=true` is a conflict. Repeating the same
566
+ member is allowed. Group members cannot themselves be required or share a destination.
567
+
568
+ A valid version request prints to stdout and exits with status 0 without reading
569
+ configuration or converting unrelated values. It bypasses missing required options
570
+ but still respects argument consumption, subcommand boundaries and `--`.
571
+
484
572
  ### Sub-commands
485
573
 
486
574
  ```python
@@ -544,9 +632,25 @@ p.complete([], "--mode=f") # ['--mode=fast']
544
632
  word, then returns sorted, unique full replacement words for options, choices
545
633
  and subcommands. It does not parse, read config/environment, call type converters,
546
634
  execute shells or mutate parse state. Variable-length positionals are best-effort;
547
- only collection-backed choices are enumerated, not generators. This is an adapter
548
- API for shells/editors: it does not install shell hooks, quote candidates or
549
- perform filesystem completion.
635
+ only built-in collections of plain scalar choices are enumerated, not generators
636
+ or custom objects. This is an adapter API for shells/editors: it does not install
637
+ shell hooks, quote candidates or perform filesystem completion.
638
+
639
+ ### Static shell completion scripts
640
+
641
+ ```python
642
+ script = p.completion_script("bash", command="convert")
643
+ # Also accepts "zsh" and "powershell"; save/load the returned text yourself.
644
+ ```
645
+
646
+ This generates a quoted static snapshot of options, subcommands and finite scalar
647
+ choices. It does not run the application, read sources, call converters or change
648
+ shell configuration. Unlike `complete(tokens, prefix)`, the snapshot is not a
649
+ context-sensitive parser: it may suggest candidates from other options or commands.
650
+ Regenerate it after changing parser metadata. Shell metacharacters are emitted as
651
+ literal data, not executable fragments. Command names containing control characters
652
+ are rejected; such candidates are omitted. Zsh requires an initialized completion
653
+ system (`compinit`).
550
654
 
551
655
  ---
552
656