aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornatsuoto <[email protected]>2026-07-15 11:36:16 +0530
committernatsuoto <[email protected]>2026-07-15 11:36:16 +0530
commitb91d28b669974abdcebacf0c8c13abaa5b124f9a (patch)
treec1603efc7dec936895cb74a50fe9d20c407a74f1
parentec570ac862110c7f3445750d7122e1cea7029980 (diff)
downloadedify-b91d28b669974abdcebacf0c8c13abaa5b124f9a.tar.xz
edify-b91d28b669974abdcebacf0c8c13abaa5b124f9a.zip
feat(builder): typed engine=Literal kwarg on to_regex with EngineNotWiredError
-rw-r--r--edify/builder/mixins/terminals.py9
-rw-r--r--edify/builder/types/engine.py8
-rw-r--r--edify/errors/engine.py31
-rw-r--r--tests/builder/engine.test.py19
4 files changed, 67 insertions, 0 deletions
diff --git a/edify/builder/mixins/terminals.py b/edify/builder/mixins/terminals.py
index 1b83c10..25d8191 100644
--- a/edify/builder/mixins/terminals.py
+++ b/edify/builder/mixins/terminals.py
@@ -11,10 +11,12 @@ from __future__ import annotations
import re
+from edify.builder.types.engine import Engine
from edify.builder.types.flags import Flags
from edify.builder.types.protocol import BuilderProtocol
from edify.compile.dispatch import render_element
from edify.elements.types.root import RootElement
+from edify.errors.engine import EngineNotWiredError
from edify.errors.quantifier import DanglingQuantifierError
from edify.errors.structure import CannotCallSubexpressionError
from edify.result import Regex
@@ -51,6 +53,7 @@ class TerminalsMixin(BuilderProtocol):
multiline: bool = False,
dotall: bool = False,
verbose: bool = False,
+ engine: Engine = "re",
) -> Regex:
"""Return the pattern + flags compiled and wrapped in :class:`edify.result.Regex`.
@@ -62,8 +65,14 @@ class TerminalsMixin(BuilderProtocol):
already carries — passing ``ignore_case=True`` here is equivalent to
having called ``.ignore_case()`` in the chain. Flags never turn off,
only on.
+
+ The ``engine`` kwarg selects the compilation backend. Only ``"re"`` is
+ wired today; ``"regex"`` is reserved for the opt-in third-party engine
+ and raises :class:`NotImplementedError` until that dispatch lands.
"""
pattern_string = self.to_regex_string()
+ if engine != "re":
+ raise EngineNotWiredError(engine)
kwarg_flags = Flags(
ascii_only=ascii_only,
debug=debug,
diff --git a/edify/builder/types/engine.py b/edify/builder/types/engine.py
new file mode 100644
index 0000000..9af27e6
--- /dev/null
+++ b/edify/builder/types/engine.py
@@ -0,0 +1,8 @@
+"""Type alias for the ``engine`` kwarg accepted by every builder terminal."""
+
+from __future__ import annotations
+
+from typing import Literal
+
+Engine = Literal["re", "regex"]
+"""The compilation-backend identifier accepted by :meth:`edify.RegexBuilder.to_regex`."""
diff --git a/edify/errors/engine.py b/edify/errors/engine.py
new file mode 100644
index 0000000..5a9e6cb
--- /dev/null
+++ b/edify/errors/engine.py
@@ -0,0 +1,31 @@
+"""Exception classes raised when the ``engine`` kwarg selects an unavailable backend."""
+
+from __future__ import annotations
+
+from edify.errors.formatting import compose_annotated_message
+from edify.errors.syntax import EdifySyntaxError
+
+
+class EngineNotWiredError(EdifySyntaxError):
+ """Raised when ``.to_regex(engine=...)`` selects a backend this build does not wire.
+
+ Args:
+ requested_engine: The value the caller passed for the ``engine`` kwarg.
+ """
+
+ def __init__(self, requested_engine: str) -> None:
+ message = compose_annotated_message(
+ summary=(
+ f"engine={requested_engine!r} is not wired in this build; only 're' is available"
+ ),
+ trigger_hint=".to_regex(engine=…) called here",
+ note=(
+ f"the {requested_engine!r} backend is a planned optional dependency "
+ "and its dispatch has not shipped yet."
+ ),
+ help_line=(
+ "help: call .to_regex() without the engine kwarg, or pin edify to a "
+ "release that wires the requested backend."
+ ),
+ )
+ super().__init__(message)
diff --git a/tests/builder/engine.test.py b/tests/builder/engine.test.py
new file mode 100644
index 0000000..e569c71
--- /dev/null
+++ b/tests/builder/engine.test.py
@@ -0,0 +1,19 @@
+import pytest
+
+from edify import RegexBuilder
+from edify.errors.engine import EngineNotWiredError
+
+
+def test_engine_defaults_to_re_and_compiles():
+ compiled = RegexBuilder().digit().to_regex()
+ assert compiled.source == "\\d"
+
+
+def test_engine_re_explicit_is_accepted():
+ compiled = RegexBuilder().digit().to_regex(engine="re")
+ assert compiled.source == "\\d"
+
+
+def test_engine_regex_raises_until_wired():
+ with pytest.raises(EngineNotWiredError, match="engine='regex'"):
+ RegexBuilder().digit().to_regex(engine="regex")