diff options
| author | natsuoto <[email protected]> | 2026-07-01 16:03:30 +0530 |
|---|---|---|
| committer | natsuoto <[email protected]> | 2026-07-01 16:03:30 +0530 |
| commit | 48554b6e049cd845ab7caa6321b5976ab3becd85 (patch) | |
| tree | b4ea0ae364450cd76cd81ae3cda4c466cd91b8cc | |
| parent | a3647c5bde1ae63e1af6316b353949365c49f774 (diff) | |
| download | edify-48554b6e049cd845ab7caa6321b5976ab3becd85.tar.xz edify-48554b6e049cd845ab7caa6321b5976ab3becd85.zip | |
feat: .test() boolean shortcut on Pattern and RegexBuilder
| -rw-r--r-- | edify/builder/mixins/matcher.py | 10 | ||||
| -rw-r--r-- | tests/builder/matcher.test.py | 33 |
2 files changed, 41 insertions, 2 deletions
diff --git a/edify/builder/mixins/matcher.py b/edify/builder/mixins/matcher.py index 653ea31..b4106af 100644 --- a/edify/builder/mixins/matcher.py +++ b/edify/builder/mixins/matcher.py @@ -5,7 +5,9 @@ to the returned :class:`re.Pattern`. Adding this mixin to a class lets users write ``pattern.match("10001")`` instead of ``pattern.to_regex().match("10001")``. The signatures mirror :class:`re.Pattern` exactly so IDE autocomplete and -type inference stay unchanged. +type inference stay unchanged. :meth:`test` is the one non-:mod:`re` method: +a boolean shortcut that returns ``True`` when the pattern matches anywhere +in the input, ``False`` otherwise. """ from __future__ import annotations @@ -18,7 +20,11 @@ from edify.builder.types.protocol import BuilderProtocol class MatcherMixin(BuilderProtocol): - """Provides eight :class:`re.Pattern` proxy methods on any fluent surface.""" + """Provides nine :class:`re.Pattern` proxy methods plus :meth:`test` on any fluent surface.""" + + def test(self, string: str, pos: int = 0, endpos: int = sys.maxsize) -> bool: + """Return ``True`` when the pattern matches anywhere in ``string``, else ``False``.""" + return self.to_regex().search(string, pos, endpos) is not None def match(self, string: str, pos: int = 0, endpos: int = sys.maxsize) -> re.Match[str] | None: """Delegate to :meth:`re.Pattern.match`.""" diff --git a/tests/builder/matcher.test.py b/tests/builder/matcher.test.py index e200d90..05a763a 100644 --- a/tests/builder/matcher.test.py +++ b/tests/builder/matcher.test.py @@ -80,3 +80,36 @@ def test_pattern_and_builder_yield_identical_results_for_same_regex(): def test_pattern_match_falls_back_when_only_pattern_class_used(): fluent = Pattern().start_of_input().exactly(5).digit().end_of_input() assert fluent.match("10001") is not None + + +def test_test_returns_true_when_pattern_matches_anywhere(): + lower = one_or_more(range_of("a", "z")) + assert lower.test("HELLO world") is True + + +def test_test_returns_false_when_pattern_does_not_match(): + lower = one_or_more(range_of("a", "z")) + assert lower.test("12345") is False + + +def test_test_respects_anchors(): + zip_code = START + exactly(5, DIGIT) + END + assert zip_code.test("10001") is True + assert zip_code.test("foo 10001 bar") is False + + +def test_test_returns_a_bool_not_a_match_object(): + lower = one_or_more(range_of("a", "z")) + assert isinstance(lower.test("hello"), bool) + + +def test_test_respects_the_pos_argument(): + lower = one_or_more(range_of("a", "z")) + assert lower.test("!hello", pos=1) is True + assert lower.test("!hello", pos=6) is False + + +def test_test_on_regex_builder_works(): + lower = RegexBuilder().one_or_more().range("a", "z") + assert lower.test("hello") is True + assert lower.test("12345") is False |
