diff options
| author | 夏音 / natsuoto.exe <[email protected]> | 2026-07-01 17:08:33 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-07-01 17:08:33 +0530 |
| commit | 2d9e7395dc62ca7ff9c0bf508ca4fb5e823b538e (patch) | |
| tree | f9fb46ce588f8269da5c58072bbf23729aebd0e4 /tests/builder/fork.test.py | |
| parent | 3d5094a875fbfb167d53278585414fc7884ad521 (diff) | |
| parent | 4a0a1fa3d8f7f0a54c36aae34b23b0585eb1fea7 (diff) | |
| download | edify-2d9e7395dc62ca7ff9c0bf508ca4fb5e823b538e.tar.xz edify-2d9e7395dc62ca7ff9c0bf508ca4fb5e823b538e.zip | |
feat: builder ergonomics — __repr__, __eq__/__hash__, .fork()/.copy() (#268)
Three builder-ergonomics wins so `RegexBuilder` / `Pattern` behave like
proper Python values in interactive shells, dicts, and comparisons.
## `__repr__` shows the pattern-so-far
Replaces the useless default `<edify.builder.builder.RegexBuilder object
at 0x...>` with `<RegexBuilder '^\d+'>`. Builders whose frames are still
open render as `<RegexBuilder '<unclosed>'>` so the repr never raises.
## `__eq__` / `__hash__` — value equality
Two builders are equal when their underlying immutable `BuilderState`
matches; the hash derives from the same state. Builders become safe as
dict keys, set members, or assertion targets. `RegexBuilder` and
`Pattern` share every mixin below the class name, so a builder and
pattern with identical state compare equal.
**Breaking:** identity-based `==` on builders becomes value-based. Only
code that intentionally relied on identity equality is affected.
## `.fork()` / `.copy()` — explicit branch construction
Chaining already yields implicit forking (immutable state means
`root.digit()` and `root.word()` share nothing after the split);
`.fork()` and `.copy()` are aliases that surface the property via
autocomplete and signal branching intent in code.
## Example
```python
from edify import RegexBuilder
builder = RegexBuilder().start_of_input().exactly(4).digit().end_of_input()
repr(builder) # <RegexBuilder '^\d{4}$'>
RegexBuilder().digit() == RegexBuilder().digit() # True
lookup = {RegexBuilder().digit(): "hit"} # hashable, dict-key safe
root = RegexBuilder().digit()
branch = root.fork().word() # explicit branch off root
```
Closes #136
Closes #137
Closes #139
Diffstat (limited to 'tests/builder/fork.test.py')
| -rw-r--r-- | tests/builder/fork.test.py | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/tests/builder/fork.test.py b/tests/builder/fork.test.py new file mode 100644 index 0000000..bcb8d1b --- /dev/null +++ b/tests/builder/fork.test.py @@ -0,0 +1,47 @@ +"""Tests for the :meth:`BuilderCore.fork` and :meth:`BuilderCore.copy` aliases.""" + +from edify import Pattern, RegexBuilder + + +def test_fork_returns_a_regex_builder_with_the_same_state(): + original = RegexBuilder().digit() + forked = original.fork() + assert forked == original + assert forked is not original + + +def test_fork_returns_a_pattern_with_the_same_state(): + original = Pattern().word() + forked = original.fork() + assert forked == original + assert forked is not original + + +def test_copy_returns_a_regex_builder_with_the_same_state(): + original = RegexBuilder().digit() + copied = original.copy() + assert copied == original + assert copied is not original + + +def test_copy_returns_a_pattern_with_the_same_state(): + original = Pattern().word() + copied = original.copy() + assert copied == original + assert copied is not original + + +def test_fork_and_copy_produce_independent_branches(): + root = RegexBuilder().digit() + branch_a = root.fork().word() + branch_b = root.copy().whitespace_char() + assert root.to_regex_string() == "\\d" + assert branch_a.to_regex_string() == "\\d\\w" + assert branch_b.to_regex_string() == "\\d\\s" + + +def test_fork_and_copy_return_the_concrete_class_type(): + assert isinstance(RegexBuilder().fork(), RegexBuilder) + assert isinstance(RegexBuilder().copy(), RegexBuilder) + assert isinstance(Pattern().fork(), Pattern) + assert isinstance(Pattern().copy(), Pattern) |
