aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornatsuoto <[email protected]>2026-07-01 17:38:09 +0530
committernatsuoto <[email protected]>2026-07-01 17:38:09 +0530
commit060b4089f93ea7add1ca70a2b63e6411967f9a97 (patch)
treebf9051c6478b532f79b8855886092384f6acfdb4
parent7e5df3512b2a00fb225caf45979350395b40a823 (diff)
downloadedify-060b4089f93ea7add1ca70a2b63e6411967f9a97.tar.xz
edify-060b4089f93ea7add1ca70a2b63e6411967f9a97.zip
feat!: to_regex() returns the Regex wrapper instead of raw re.Pattern
-rw-r--r--edify/__init__.py2
-rw-r--r--edify/builder/mixins/terminals.py16
-rw-r--r--edify/result/__init__.py2
-rw-r--r--edify/result/regex.py10
-rw-r--r--tests/builder/builder.test.py2
-rw-r--r--tests/builder/use.test.py2
-rw-r--r--tests/pattern/composition.test.py2
-rw-r--r--tests/result/regex.test.py2
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