diff options
| author | natsuoto <[email protected]> | 2026-07-01 17:38:09 +0530 |
|---|---|---|
| committer | natsuoto <[email protected]> | 2026-07-01 17:38:09 +0530 |
| commit | 060b4089f93ea7add1ca70a2b63e6411967f9a97 (patch) | |
| tree | bf9051c6478b532f79b8855886092384f6acfdb4 | |
| parent | 7e5df3512b2a00fb225caf45979350395b40a823 (diff) | |
| download | edify-060b4089f93ea7add1ca70a2b63e6411967f9a97.tar.xz edify-060b4089f93ea7add1ca70a2b63e6411967f9a97.zip | |
feat!: to_regex() returns the Regex wrapper instead of raw re.Pattern
| -rw-r--r-- | edify/__init__.py | 2 | ||||
| -rw-r--r-- | edify/builder/mixins/terminals.py | 16 | ||||
| -rw-r--r-- | edify/result/__init__.py | 2 | ||||
| -rw-r--r-- | edify/result/regex.py | 10 | ||||
| -rw-r--r-- | tests/builder/builder.test.py | 2 | ||||
| -rw-r--r-- | tests/builder/use.test.py | 2 | ||||
| -rw-r--r-- | tests/pattern/composition.test.py | 2 | ||||
| -rw-r--r-- | tests/result/regex.test.py | 2 |
8 files changed, 22 insertions, 16 deletions
diff --git a/edify/__init__.py b/edify/__init__.py index 9c0dee5..5f946d0 100644 --- a/edify/__init__.py +++ b/edify/__init__.py @@ -23,6 +23,7 @@ from edify.pattern.classes import ( WORD, ) from edify.pattern.composition import Pattern +from edify.result import Regex from edify.pattern.factories import ( any_of, assert_ahead, @@ -87,6 +88,7 @@ __all__ = [ "EdifyError", "EdifySyntaxError", "Pattern", + "Regex", "RegexBuilder", "__version__", "any_of", diff --git a/edify/builder/mixins/terminals.py b/edify/builder/mixins/terminals.py index b648b77..9b7e62a 100644 --- a/edify/builder/mixins/terminals.py +++ b/edify/builder/mixins/terminals.py @@ -3,7 +3,8 @@ Both methods require the builder to be fully specified: every opened frame must have been closed with ``.end()`` so only the root frame remains. The string terminal returns exactly the pattern you'd hand to ``re.compile``; -the compiled terminal performs that compilation with the current flag set. +the compiled terminal performs that compilation with the current flag set +and returns the emitted string wrapped in an :class:`edify.result.Regex`. """ from __future__ import annotations @@ -17,6 +18,7 @@ from edify.elements.types.root import RootElement from edify.errors.internal import FailedToCompileRegexError from edify.errors.quantifier import DanglingQuantifierError from edify.errors.structure import CannotCallSubexpressionError +from edify.result import Regex _EMPTY_NON_CAPTURING_GROUP = "(?:)" _ESCAPED_SPACE = "\\ " @@ -41,14 +43,20 @@ class TerminalsMixin(BuilderProtocol): return _EMPTY_NON_CAPTURING_GROUP return unescaped_pattern - def to_regex(self) -> re.Pattern[str]: - """Return a :class:`re.Pattern` compiled from the builder's pattern + flags.""" + def to_regex(self) -> Regex: + """Return the pattern + flags compiled and wrapped in :class:`edify.result.Regex`. + + The wrapper exposes the pattern string as ``.source`` and the underlying + :class:`re.Pattern` as ``.compiled``, plus the eight :mod:`re` query + methods as direct delegates. + """ pattern_string = self.to_regex_string() flag_bitmask = _build_flag_bitmask(self._state.flags) try: - return re.compile(pattern_string, flags=flag_bitmask) + compiled_pattern = re.compile(pattern_string, flags=flag_bitmask) except re.error as compile_error: raise FailedToCompileRegexError(str(compile_error)) from compile_error + return Regex(source=pattern_string, compiled=compiled_pattern) def _ensure_fully_specified(builder: BuilderProtocol) -> None: diff --git a/edify/result/__init__.py b/edify/result/__init__.py index 0dd2430..457f79e 100644 --- a/edify/result/__init__.py +++ b/edify/result/__init__.py @@ -1,3 +1,3 @@ from edify.result.regex import Regex -__all__ = ["Regex"]
\ No newline at end of file +__all__ = ["Regex"] diff --git a/edify/result/regex.py b/edify/result/regex.py index 4e177cf..9dbcd63 100644 --- a/edify/result/regex.py +++ b/edify/result/regex.py @@ -35,15 +35,11 @@ class Regex: """The underlying :class:`re.Pattern`; callers that need identity checks read this.""" return self._compiled - def match( - self, string: str, pos: int = 0, endpos: int = sys.maxsize - ) -> re.Match[str] | None: + def match(self, string: str, pos: int = 0, endpos: int = sys.maxsize) -> re.Match[str] | None: """Delegate to :meth:`re.Pattern.match`.""" return self._compiled.match(string, pos, endpos) - def search( - self, string: str, pos: int = 0, endpos: int = sys.maxsize - ) -> re.Match[str] | None: + def search(self, string: str, pos: int = 0, endpos: int = sys.maxsize) -> re.Match[str] | None: """Delegate to :meth:`re.Pattern.search`.""" return self._compiled.search(string, pos, endpos) @@ -99,4 +95,4 @@ class Regex: def __hash__(self) -> int: """Return a hash derived from the source and compiled flags.""" - return hash((self._source, self._compiled.flags))
\ No newline at end of file + return hash((self._source, self._compiled.flags)) diff --git a/tests/builder/builder.test.py b/tests/builder/builder.test.py index ddd1997..82aa1cf 100644 --- a/tests/builder/builder.test.py +++ b/tests/builder/builder.test.py @@ -47,7 +47,7 @@ def regex_equality(regex, rb_expression): def regex_compilation(regex, rb_expression, f=0): rb_expression_c = rb_expression.to_regex() - assert re.compile(regex, flags=f) == rb_expression_c + assert re.compile(regex, flags=f) == rb_expression_c.compiled def test_empty_regex(): diff --git a/tests/builder/use.test.py b/tests/builder/use.test.py index 10d7855..0253786 100644 --- a/tests/builder/use.test.py +++ b/tests/builder/use.test.py @@ -21,7 +21,7 @@ def test_use_drops_the_pattern_flag_snapshot_by_default(): case_insensitive_pattern = Pattern().ignore_case().string("hello") expression = RegexBuilder().use(case_insensitive_pattern) compiled = expression.to_regex() - assert compiled.flags & 2 == 0 + assert compiled.compiled.flags & 2 == 0 def test_use_accepts_another_regex_builder_as_the_source(): diff --git a/tests/pattern/composition.test.py b/tests/pattern/composition.test.py index f58df57..18a6bc2 100644 --- a/tests/pattern/composition.test.py +++ b/tests/pattern/composition.test.py @@ -20,7 +20,7 @@ def test_pattern_exposes_to_regex_string_terminal(): def test_pattern_exposes_to_regex_terminal(): pattern = Pattern().digit() compiled = pattern.to_regex() - assert compiled.pattern == "\\d" + assert compiled.source == "\\d" def test_pattern_supports_nested_use_composition(): diff --git a/tests/result/regex.test.py b/tests/result/regex.test.py index 8f1ed38..d6f2a64 100644 --- a/tests/result/regex.test.py +++ b/tests/result/regex.test.py @@ -94,4 +94,4 @@ def test_hash_matches_when_equal(): def test_equality_with_non_regex_returns_not_implemented(): a = Regex("\\d+", re.compile("\\d+")) - assert a.__eq__("foo") is NotImplemented
\ No newline at end of file + assert a.__eq__("foo") is NotImplemented |
