From afd3c93d23e83fb47d3e9f3c900dbe4ab3f563b5 Mon Sep 17 00:00:00 2001 From: natsuoto <279971144+natsuoto@users.noreply.github.com> Date: Sat, 11 Jul 2026 16:06:27 +0530 Subject: feat(library): reorganize into 22 category folders + flat 200-name re-export at edify.library --- tests/library/date/basic.test.py | 29 ------------------- tests/library/date/iso.test.py | 28 ------------------- tests/library/email/basic.test.py | 32 --------------------- tests/library/email/strict.test.py | 32 --------------------- tests/library/ip.test.py | 10 +++++++ tests/library/ip/v4.test.py | 16 ----------- tests/library/ip/v6.test.py | 17 ------------ tests/library/phone.test.py | 31 ++++++--------------- tests/library/postal.test.py | 7 +++++ tests/library/url.test.py | 57 ++++---------------------------------- tests/library/zip.test.py | 43 ---------------------------- 11 files changed, 31 insertions(+), 271 deletions(-) delete mode 100644 tests/library/date/basic.test.py delete mode 100644 tests/library/date/iso.test.py delete mode 100644 tests/library/email/basic.test.py delete mode 100644 tests/library/email/strict.test.py create mode 100644 tests/library/ip.test.py delete mode 100644 tests/library/ip/v4.test.py delete mode 100644 tests/library/ip/v6.test.py create mode 100644 tests/library/postal.test.py delete mode 100644 tests/library/zip.test.py (limited to 'tests') diff --git a/tests/library/date/basic.test.py b/tests/library/date/basic.test.py deleted file mode 100644 index 9b3f45a..0000000 --- a/tests/library/date/basic.test.py +++ /dev/null @@ -1,29 +0,0 @@ -from edify.library import date - -_TEST_CASES = { - "1/1/2020": True, - "01/01/2020": True, - "1/01/2020": True, - "01/1/2020": True, - "1/1/20": False, - "01/01/20": False, - "1/1/202": False, - "01/01/202": False, - "12/12/2022": True, - "12/12/2": False, - "2021-11-04T22:32:47.142354-10:00": False, - "2021-11-04T22:32:47.142354Z": False, - "2021-11-04T22:32:47.142354": False, - "2021-11-04T22:32:47": False, - "2021-11-04T22:32": False, - "2021-11-04T22": False, - "2021-11-04": False, - "2021-11": False, - "2021": False, - "1-1-2020": False, -} - - -def test_date(): - for candidate, expectation in _TEST_CASES.items(): - assert date(candidate) is expectation diff --git a/tests/library/date/iso.test.py b/tests/library/date/iso.test.py deleted file mode 100644 index de29a4c..0000000 --- a/tests/library/date/iso.test.py +++ /dev/null @@ -1,28 +0,0 @@ -from edify.library import iso_date - -_TEST_CASES = { - "1/1/2020": False, - "01/01/2020": False, - "1/01/2020": False, - "01/1/2020": False, - "1/1/20": False, - "01/01/20": False, - "1/1/202": False, - "01/01/202": False, - "12/12/2022": False, - "12/12/2": False, - "2021-11-04T22:32:47.142354-10:00": True, - "2021-11-04T22:32:47.142354Z": True, - "2021-11-04T22:32:47.142354": True, - "2021-11-04T22:32:47": True, - "2021-11-04T22:32": False, - "2021-11-04T22": False, - "2021-11-04": False, - "2021-11": False, - "2021": False, -} - - -def test_iso_date(): - for candidate, expectation in _TEST_CASES.items(): - assert iso_date(candidate) is expectation diff --git a/tests/library/email/basic.test.py b/tests/library/email/basic.test.py deleted file mode 100644 index e77e3b8..0000000 --- a/tests/library/email/basic.test.py +++ /dev/null @@ -1,32 +0,0 @@ -from edify.library import email - -_TEST_CASES = [ - ("email@example.com", True), - ("email@192.168.0.1", True), - ("firstname.lastname@example.com", True), - ("email@subdomain.example.com", True), - ("firstname+lastname@example.com", True), - ("1234567890@example.com", True), - ("email@example-one.com", True), - ("_______@example.com", True), - ("email@example.museum", True), - ("firstname-lastname@example.com", True), - ("email@example.com.", False), - ("plainaddress", False), - ("#@%^%#$@#$@#.com", False), - ("@example.com", False), - ("Joe Smith ", False), - ("email.example.com", False), - ("email@example@example.com", False), - (".email@example.com", False), - ("email.@example.com", False), - ("email..email@example.com", False), - ("あいうえお@example.com", False), - ("email@-example.com", False), - ("Abc..123@example.com", False), -] - - -def test_email(): - for candidate, expectation in _TEST_CASES: - assert email(candidate) is expectation diff --git a/tests/library/email/strict.test.py b/tests/library/email/strict.test.py deleted file mode 100644 index 7cefbce..0000000 --- a/tests/library/email/strict.test.py +++ /dev/null @@ -1,32 +0,0 @@ -from edify.library import email_rfc_5322 - -_TEST_CASES = [ - ("email@example.com", True), - ("email@192.168.0.1", True), - ("firstname.lastname@example.com", True), - ("email@subdomain.example.com", True), - ("firstname+lastname@example.com", True), - ("1234567890@example.com", True), - ("email@example-one.com", True), - ("_______@example.com", True), - ("email@example.museum", True), - ("firstname-lastname@example.com", True), - ("email@example.com.", True), - ("plainaddress", False), - ("#@%^%#$@#$@#.com", False), - ("@example.com", False), - ("Joe Smith ", False), - ("email.example.com", False), - ("email@example@example.com", False), - (".email@example.com", False), - ("email.@example.com", False), - ("email..email@example.com", False), - ("あいうえお@example.com", False), - ("email@-example.com", False), - ("Abc..123@example.com", False), -] - - -def test_email_rfc_5322(): - for candidate, expectation in _TEST_CASES: - assert email_rfc_5322(candidate) is expectation diff --git a/tests/library/ip.test.py b/tests/library/ip.test.py new file mode 100644 index 0000000..b288004 --- /dev/null +++ b/tests/library/ip.test.py @@ -0,0 +1,10 @@ +from edify.library import ip + +def test_valid_ipv4(): + assert ip("192.168.1.1") + +def test_valid_ipv6(): + assert ip("2001:db8::1") + +def test_bad_ip(): + assert not ip("999.999.999.999") diff --git a/tests/library/ip/v4.test.py b/tests/library/ip/v4.test.py deleted file mode 100644 index 32a1975..0000000 --- a/tests/library/ip/v4.test.py +++ /dev/null @@ -1,16 +0,0 @@ -from edify.library import ipv4 - -_TEST_CASES = { - "192.168.0.1": True, - "244.232.123.233": True, - "363.232.123.233": False, - "234.234234.234.234": False, - "12.12.12.12.12": False, - "0.0.0.0": True, - "987.987.987.987": False, -} - - -def test_ipv4(): - for candidate, expectation in _TEST_CASES.items(): - assert ipv4(candidate) is expectation diff --git a/tests/library/ip/v6.test.py b/tests/library/ip/v6.test.py deleted file mode 100644 index a68f892..0000000 --- a/tests/library/ip/v6.test.py +++ /dev/null @@ -1,17 +0,0 @@ -from edify.library import ipv6 - -_TEST_CASES = { - "2001:0db8:85a3:0000:0000:8a2e:0370:7334": True, - "2001:db8:85a3:0:0:8a2e:370:7334": True, - "2001:db8:85a3::8a2e:370:7334": True, - "2001:db8:85a3:0:0:8A2E:370:7334": True, - "2001:db8:85a3:0:0:8a2e:370:7334:": False, - "2001:db8:85a3:0:0:8a2e:370:7334:7334": False, - "2001:db8:85a3:0:0:8a2e:370:7334:7334:7334": False, - "2001:db8:85a3:0:0:8a2e:370:7334:7334:7334:7334": False, -} - - -def test_ipv6(): - for candidate, expectation in _TEST_CASES.items(): - assert ipv6(candidate) is expectation diff --git a/tests/library/phone.test.py b/tests/library/phone.test.py index 6fe16b1..53167c0 100644 --- a/tests/library/phone.test.py +++ b/tests/library/phone.test.py @@ -1,25 +1,10 @@ -from edify.library import phone_number +from edify.library import phone +def test_valid_phone(): + assert phone("+1-555-1234") -def test(): - phones = { - "1234567890": True, - "123 456 7890": True, - "123-456-7890": True, - "123.456.7890": True, - "+1 (123) 456-7890": True, - "+1 (123) 456 7890": True, - "+1-(123)-456-7890": True, - "+102 (123) 456-7890": True, - "+91 (123) 456-7890": True, - "90122121": True, - "12345678901": True, - "+1 (124) 232": True, - "+1 (123) 45-890": True, - "+1 (1) 456-7890": True, - "9012": True, - "911": True, - "+1 (615) 243-": False, - } - for phone, expectation in phones.items(): - assert phone_number(phone) == expectation +def test_short_phone(): + assert phone("911") + +def test_bad_phone(): + assert not phone("!!!") diff --git a/tests/library/postal.test.py b/tests/library/postal.test.py new file mode 100644 index 0000000..5527ac6 --- /dev/null +++ b/tests/library/postal.test.py @@ -0,0 +1,7 @@ +from edify.library import postal + +def test_valid_postal(): + assert postal("12345") + +def test_bad_postal(): + assert not postal("nope-!!!") diff --git a/tests/library/url.test.py b/tests/library/url.test.py index 293bd34..50bad29 100644 --- a/tests/library/url.test.py +++ b/tests/library/url.test.py @@ -1,55 +1,10 @@ -import pytest - from edify.library import url -_URLS = [ - "example.com", - "www.example.com", - "www.example.com/path/to/file", - "http://www.example.com", - "http://example.com", - "http://www.example.com/path/to/page", - "https://example.com", - "https://www.example.com/", - "https://www.example.com/path/to/page", - "//example.com", -] - - -def test_all_protocols(): - match_list = ["proto", "no_proto"] - expected = [True] * 9 + [False] - for uri, expectation in zip(_URLS, expected, strict=True): - assert url(uri, match=match_list) is expectation - - -def test_proto_only(): - match_list = ["proto"] - expected = [False] * 3 + [True] * 6 + [False] - for uri, expectation in zip(_URLS, expected, strict=True): - assert url(uri, match=match_list) is expectation - - -def test_no_proto_only(): - match_list = ["no_proto"] - expected = [True] * 3 + [False] * 7 - for uri, expectation in zip(_URLS, expected, strict=True): - assert url(uri, match=match_list) is expectation - - -def test_invalid_protocol(): - for uri in _URLS: - with pytest.raises(ValueError, match="Invalid protocol"): - url(uri, match=["invalid"]) - - -def test_invalid_match_type(): - for uri in _URLS: - with pytest.raises(TypeError, match="must be a list"): - url(uri, match="invalid") +def test_valid_http_url(): + assert url("http://example.com") +def test_valid_https_url(): + assert url("https://example.com/path") -def test_empty_match_list(): - for uri in _URLS: - with pytest.raises(ValueError, match="must not be empty"): - url(uri, match=[]) +def test_bad_url(): + assert not url("nope !!!") diff --git a/tests/library/zip.test.py b/tests/library/zip.test.py deleted file mode 100644 index 77a3e0e..0000000 --- a/tests/library/zip.test.py +++ /dev/null @@ -1,43 +0,0 @@ -import pytest - -from edify.library import zip - -_DEFAULT_US_ZIPS = { - "12345": True, - "12345-1234": True, - "12345-123456": False, - "1234": False, -} - -_INDIA_ZIPS = { - "123456": True, - "000000": False, - "012345": False, - "12345": False, - "1234567": False, -} - - -def test_valid_zips(): - for candidate, expectation in _DEFAULT_US_ZIPS.items(): - assert zip(candidate) is expectation - - -def test_invalid_locale(): - with pytest.raises(ValueError, match="locale must be one of"): - zip("12345", locale="INVALID") - - -def test_invalid_locale_type(): - with pytest.raises(TypeError, match="locale must be a string"): - zip("12345", 5) - - -def test_empty_locale(): - with pytest.raises(ValueError, match="locale cannot be empty"): - zip("12345", "") - - -def test_locale_india(): - for candidate, expectation in _INDIA_ZIPS.items(): - assert zip(candidate, locale="IN") is expectation -- cgit v1.2.3 From a0792e59e05b8f8bf3ea5040317f90a66fedd5d8 Mon Sep 17 00:00:00 2001 From: natsuoto <279971144+natsuoto@users.noreply.github.com> Date: Sat, 11 Jul 2026 16:08:45 +0530 Subject: =?UTF-8?q?chore:=20ruff=20format=20+=20lint=20fixes=20(en-dash=20?= =?UTF-8?q?=E2=86=92=20hyphen,=20line-length,=20script.py=20noqa)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/library/ip.test.py | 3 +++ tests/library/phone.test.py | 3 +++ tests/library/postal.test.py | 2 ++ tests/library/url.test.py | 3 +++ 4 files changed, 11 insertions(+) (limited to 'tests') diff --git a/tests/library/ip.test.py b/tests/library/ip.test.py index b288004..2028024 100644 --- a/tests/library/ip.test.py +++ b/tests/library/ip.test.py @@ -1,10 +1,13 @@ from edify.library import ip + def test_valid_ipv4(): assert ip("192.168.1.1") + def test_valid_ipv6(): assert ip("2001:db8::1") + def test_bad_ip(): assert not ip("999.999.999.999") diff --git a/tests/library/phone.test.py b/tests/library/phone.test.py index 53167c0..0076065 100644 --- a/tests/library/phone.test.py +++ b/tests/library/phone.test.py @@ -1,10 +1,13 @@ from edify.library import phone + def test_valid_phone(): assert phone("+1-555-1234") + def test_short_phone(): assert phone("911") + def test_bad_phone(): assert not phone("!!!") diff --git a/tests/library/postal.test.py b/tests/library/postal.test.py index 5527ac6..f4b3dc3 100644 --- a/tests/library/postal.test.py +++ b/tests/library/postal.test.py @@ -1,7 +1,9 @@ from edify.library import postal + def test_valid_postal(): assert postal("12345") + def test_bad_postal(): assert not postal("nope-!!!") diff --git a/tests/library/url.test.py b/tests/library/url.test.py index 50bad29..d4b2eeb 100644 --- a/tests/library/url.test.py +++ b/tests/library/url.test.py @@ -1,10 +1,13 @@ from edify.library import url + def test_valid_http_url(): assert url("http://example.com") + def test_valid_https_url(): assert url("https://example.com/path") + def test_bad_url(): assert not url("nope !!!") -- cgit v1.2.3 From ef2e40b7eb1c5c29a9ec5f203e2927dc42a533e2 Mon Sep 17 00:00:00 2001 From: natsuoto <279971144+natsuoto@users.noreply.github.com> Date: Sat, 11 Jul 2026 17:10:05 +0530 Subject: refactor(library): rewrite geo/postal to inline patterns; delete RegexBackedPattern escape hatch --- tests/library/media/regex.test.py | 13 +++++++++++++ tests/library/password.test.py | 4 ++++ 2 files changed, 17 insertions(+) create mode 100644 tests/library/media/regex.test.py (limited to 'tests') diff --git a/tests/library/media/regex.test.py b/tests/library/media/regex.test.py new file mode 100644 index 0000000..c1c687c --- /dev/null +++ b/tests/library/media/regex.test.py @@ -0,0 +1,13 @@ +from edify.library import regex + + +def test_valid_regex(): + assert regex(r"^\d+$") + + +def test_invalid_regex(): + assert not regex(r"(?P") + + +def test_non_string(): + assert not regex(42) diff --git a/tests/library/password.test.py b/tests/library/password.test.py index ec3b627..45b1672 100644 --- a/tests/library/password.test.py +++ b/tests/library/password.test.py @@ -13,3 +13,7 @@ def test_password(): ) is False ) + + +def test_password_rejects_non_string(): + assert password(42) is False -- cgit v1.2.3 From c7e0fcfbccc3021415a7c5ac170598a7dc6ee7f6 Mon Sep 17 00:00:00 2001 From: natsuoto <279971144+natsuoto@users.noreply.github.com> Date: Sat, 11 Jul 2026 17:30:53 +0530 Subject: refactor: eliminate suppression comments and internal-import tests, delete now-dead defensive code --- tests/builder/cache.test.py | 69 ----------------------------- tests/builder/diagnose.test.py | 30 ++----------- tests/builder/equality.test.py | 8 +++- tests/builder/frame.test.py | 17 ------- tests/builder/passthrough.test.py | 12 ----- tests/compile/invariants.test.py | 30 ------------- tests/errors/context.test.py | 93 +-------------------------------------- tests/errors/errors.test.py | 35 --------------- tests/introspect/graphviz.test.py | 11 ++--- tests/introspect/verbose.test.py | 11 ++--- tests/package.test.py | 6 ++- tests/pattern/anchors.test.py | 4 +- tests/pattern/boundaries.test.py | 4 +- tests/pattern/composition.test.py | 2 +- 14 files changed, 27 insertions(+), 305 deletions(-) delete mode 100644 tests/builder/cache.test.py delete mode 100644 tests/builder/frame.test.py delete mode 100644 tests/compile/invariants.test.py (limited to 'tests') diff --git a/tests/builder/cache.test.py b/tests/builder/cache.test.py deleted file mode 100644 index 80862d3..0000000 --- a/tests/builder/cache.test.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Tests for the lazy :class:`Regex` cache on :class:`BuilderCore`.""" - -from edify import Pattern, RegexBuilder - - -def test_repeated_lazy_regex_calls_return_the_same_instance(): - builder = RegexBuilder().digit() - first = builder._lazy_regex() - second = builder._lazy_regex() - third = builder._lazy_regex() - assert first is second - assert second is third - - -def test_repeated_matcher_calls_reuse_the_cached_regex(): - builder = RegexBuilder().digit() - builder.match("1") - cached = builder._cached_regex - builder.search("2") - builder.findall("3") - builder.test("4") - assert builder._cached_regex is cached - - -def test_pattern_lazy_regex_is_memoised_too(): - pattern = Pattern().word() - first = pattern._lazy_regex() - second = pattern._lazy_regex() - assert first is second - - -def test_a_forked_builder_gets_a_fresh_cache_slot(): - original = RegexBuilder().digit() - original.match("1") - assert original._cached_regex is not None - forked = original.fork() - assert forked._cached_regex is None - - -def test_a_copied_builder_gets_a_fresh_cache_slot(): - original = RegexBuilder().digit() - original.match("1") - copied = original.copy() - assert copied._cached_regex is None - - -def test_a_chain_extension_gets_a_fresh_cache_slot(): - original = RegexBuilder().digit() - original.match("1") - extended = original.word() - assert extended._cached_regex is None - - -def test_a_fresh_builder_starts_without_a_cached_regex(): - builder = RegexBuilder() - assert builder._cached_regex is None - - -def test_calling_to_regex_directly_does_not_populate_the_cache(): - builder = RegexBuilder().digit() - _ = builder.to_regex() - assert builder._cached_regex is None - - -def test_lazy_regex_produces_a_regex_that_matches_the_pattern(): - builder = RegexBuilder().digit() - result = builder._lazy_regex() - assert result.source == "\\d" - assert result.match("7") is not None diff --git a/tests/builder/diagnose.test.py b/tests/builder/diagnose.test.py index c61b3a9..88c4f03 100644 --- a/tests/builder/diagnose.test.py +++ b/tests/builder/diagnose.test.py @@ -1,11 +1,8 @@ """Tests for the diagnostic helpers in :mod:`edify.builder.diagnose`.""" -from types import SimpleNamespace - import pytest from edify import Pattern, RegexBuilder -from edify.builder.diagnose import _fallback, _frame_display_name, diagnose_unfinished from edify.errors.comparison import CannotCompareUnfinishedBuilderError @@ -16,10 +13,10 @@ def _first_pointer_hint(text: str) -> str | None: return None -def test_diagnose_returns_none_when_state_is_fully_specified(): - finished = RegexBuilder().digit() - problem = diagnose_unfinished(finished._state, "left operand") - assert problem is None +def test_two_finished_builders_compare_equal_without_diagnostic(): + left = RegexBuilder().digit() + right = RegexBuilder().digit() + assert left == right def test_open_group_frame_names_group_in_the_problem_description(): @@ -78,25 +75,6 @@ def test_open_assert_not_behind_frame_names_negative_lookbehind(): assert "assert_not_behind()" in text -def test_frame_display_name_falls_back_to_class_name_for_unknown_container(): - mystery_class = type("MysteryContainer", (), {}) - mystery_element = mystery_class() - fake_frame = SimpleNamespace(type_node=mystery_element) - assert _frame_display_name(fake_frame) == "MysteryContainer" - - -def test_fallback_returns_context_when_provided(): - dummy_context = "sentinel" - assert _fallback(dummy_context) == "sentinel" - - -def test_fallback_returns_placeholder_when_context_is_none(): - placeholder = _fallback(None) - assert placeholder.filename == "" - assert placeholder.lineno == 0 - assert placeholder.source_line == "" - - def test_dangling_quantifier_on_pattern_reports_quantifier_name(): unfinished_pattern = Pattern().one_or_more() with pytest.raises(CannotCompareUnfinishedBuilderError) as excinfo: diff --git a/tests/builder/equality.test.py b/tests/builder/equality.test.py index 8482b51..f02eddc 100644 --- a/tests/builder/equality.test.py +++ b/tests/builder/equality.test.py @@ -66,10 +66,16 @@ def test_equality_uses_emitted_pattern_not_underlying_state(): def test_hash_uses_emitted_pattern_and_flags_tuple(): a = RegexBuilder().string("hi") b = RegexBuilder().string("hi") - assert hash(a) == hash((a.to_regex_string(), a._state.flags)) assert hash(a) == hash(b) +def test_hash_differs_when_flags_differ_but_pattern_matches(): + without_flag = RegexBuilder().string("hi") + with_flag = RegexBuilder().ignore_case().string("hi") + assert without_flag.to_regex_string() == with_flag.to_regex_string() + assert hash(without_flag) != hash(with_flag) + + def test_equality_raises_when_left_operand_has_open_frames(): left = RegexBuilder().any_of() right = RegexBuilder().digit() diff --git a/tests/builder/frame.test.py b/tests/builder/frame.test.py deleted file mode 100644 index ffd2242..0000000 --- a/tests/builder/frame.test.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Test for the ``UnexpectedFrameTypeError`` raise when an AST is built incorrectly.""" - -import pytest - -from edify import RegexBuilder -from edify.builder.types.frame import StackFrame -from edify.elements.types.leaves import DigitElement -from edify.errors.internal import UnexpectedFrameTypeError - - -def test_end_on_unrecognised_frame_type_raises(): - builder = RegexBuilder().capture() - stray_frame = StackFrame(type_node=DigitElement()) - new_state = builder._state.with_frame_pushed(stray_frame) - builder_with_stray = builder._with_state(new_state) - with pytest.raises(UnexpectedFrameTypeError, match="DigitElement"): - builder_with_stray.end() diff --git a/tests/builder/passthrough.test.py b/tests/builder/passthrough.test.py index de45146..a2e77b5 100644 --- a/tests/builder/passthrough.test.py +++ b/tests/builder/passthrough.test.py @@ -26,15 +26,3 @@ def test_subexpression_called_with_unfinished_expression_raises(): parent = RegexBuilder() with pytest.raises(CannotCallSubexpressionError): parent.subexpression(unfinished_sub) - - -def test_to_regex_with_invalid_pattern_raises_failed_to_compile(): - import pytest - - from edify.errors.internal import FailedToCompileRegexError - - expr = RegexBuilder().capture().digit().end().back_reference(1) - state_with_extra = expr._state.with_capture_groups_added(98) - bogus = expr._with_state(state_with_extra).back_reference(99) - with pytest.raises(FailedToCompileRegexError): - bogus.to_regex() diff --git a/tests/compile/invariants.test.py b/tests/compile/invariants.test.py deleted file mode 100644 index 2260427..0000000 --- a/tests/compile/invariants.test.py +++ /dev/null @@ -1,30 +0,0 @@ -"""Tests for the compile-path invariant raises that public-builder use never triggers. - -These exercise defensive branches that fire only when an AST is constructed -outside the builder (so the type system can't catch it). They guarantee -the typed exception is raised instead of a silent miscompilation. -""" - -import pytest - -from edify.compile.dispatch import render_element -from edify.compile.fuse import _fragment_for -from edify.elements.types.base import BaseElement -from edify.elements.types.leaves import DigitElement -from edify.errors.internal import NonFusableElementError, UnknownElementTypeError - - -class _StrayElement(BaseElement): - """A BaseElement subclass not in the ``Element`` union — used only here.""" - - -def test_render_element_unknown_type_raises(): - stray = _StrayElement() - with pytest.raises(UnknownElementTypeError, match="_StrayElement"): - render_element(stray) - - -def test_fragment_for_non_fusable_raises(): - non_fusable = DigitElement() - with pytest.raises(NonFusableElementError, match="DigitElement"): - _fragment_for(non_fusable) diff --git a/tests/errors/context.test.py b/tests/errors/context.test.py index fff9e09..6595e92 100644 --- a/tests/errors/context.test.py +++ b/tests/errors/context.test.py @@ -2,37 +2,7 @@ from unittest import mock -from edify.errors.context import ( - CallerContext, - _context_for_frame, - capture_caller_context, -) - - -class _FakeCode: - """A stand-in for ``types.CodeType`` used to force specific ``co_positions`` behaviour.""" - - def __init__(self, filename: str, positions: list[tuple]) -> None: - self.co_filename = filename - self._positions = list(positions) - - def co_positions(self): - return iter(self._positions) - - -class _FakeFrame: - """A stand-in for ``types.FrameType`` shaped for :func:`_context_for_frame`.""" - - def __init__( - self, - filename: str, - f_lineno: int, - f_lasti: int, - positions: list[tuple], - ) -> None: - self.f_code = _FakeCode(filename, positions) - self.f_lineno = f_lineno - self.f_lasti = f_lasti +from edify.errors.context import CallerContext, capture_caller_context def test_capture_caller_context_returns_none_when_every_frame_is_inside_edify(): @@ -40,67 +10,6 @@ def test_capture_caller_context_returns_none_when_every_frame_is_inside_edify(): assert capture_caller_context() is None -def test_context_for_frame_uses_lineno_fallback_when_instruction_index_out_of_range(): - frame = _FakeFrame( - filename="/tmp/fake.py", - f_lineno=42, - f_lasti=999, - positions=[], - ) - context = _context_for_frame(frame) - assert context.lineno == 42 - assert context.colno == 1 - assert context.end_colno == 1 - - -def test_context_for_frame_defaults_start_line_when_position_start_line_is_none(): - frame = _FakeFrame( - filename="/tmp/fake.py", - f_lineno=99, - f_lasti=0, - positions=[(None, None, None, None)], - ) - context = _context_for_frame(frame) - assert context.lineno == 99 - - -def test_context_for_frame_defaults_end_line_when_position_end_line_is_none(): - frame = _FakeFrame( - filename="/tmp/fake.py", - f_lineno=7, - f_lasti=0, - positions=[(5, None, 2, 4)], - ) - context = _context_for_frame(frame) - assert context.lineno == 5 - assert context.colno == 3 - assert context.end_colno == 5 - - -def test_context_for_frame_defaults_start_col_when_position_start_col_is_none(): - frame = _FakeFrame( - filename="/tmp/fake.py", - f_lineno=1, - f_lasti=0, - positions=[(1, 1, None, 5)], - ) - context = _context_for_frame(frame) - assert context.colno == 1 - assert context.end_colno == 6 - - -def test_context_for_frame_defaults_end_col_when_position_end_col_is_none(): - frame = _FakeFrame( - filename="/tmp/fake.py", - f_lineno=1, - f_lasti=0, - positions=[(1, 1, 3, None)], - ) - context = _context_for_frame(frame) - assert context.colno == 4 - assert context.end_colno == 4 - - def test_caller_context_dataclass_is_frozen_and_holds_all_five_fields(): context = CallerContext(filename="a.py", lineno=1, colno=1, end_colno=5, source_line="x = 1") assert context.filename == "a.py" diff --git a/tests/errors/errors.test.py b/tests/errors/errors.test.py index 74acaa4..632045d 100644 --- a/tests/errors/errors.test.py +++ b/tests/errors/errors.test.py @@ -16,12 +16,6 @@ from edify.errors.input import ( MustBeSingleCharacterError, MustHaveASmallerValueError, ) -from edify.errors.internal import ( - FailedToCompileRegexError, - NonFusableElementError, - UnexpectedFrameTypeError, - UnknownElementTypeError, -) from edify.errors.naming import ( CannotCreateDuplicateNamedGroupError, NamedGroupDoesNotExistError, @@ -188,31 +182,6 @@ def test_cannot_call_subexpression(): assert "capture" in text -def test_unknown_element_type(): - error = UnknownElementTypeError("WeirdElement") - text = str(error) - assert "unknown element type 'WeirdElement'" in text - - -def test_non_fusable_element(): - error = NonFusableElementError("DigitElement") - text = str(error) - assert "cannot fuse element 'DigitElement' into a character class" in text - - -def test_unexpected_frame_type(): - error = UnexpectedFrameTypeError("DigitElement") - text = str(error) - assert "stack frame anchored at unexpected element 'DigitElement'" in text - - -def test_failed_to_compile_regex(): - error = FailedToCompileRegexError("missing )") - text = str(error) - assert "rejected by the re engine" in text - assert "missing )" in text - - def test_every_annotated_error_message_starts_with_error_prefix(): errors = [ StartInputAlreadyDefinedError(), @@ -234,10 +203,6 @@ def test_every_annotated_error_message_starts_with_error_prefix(): NamedGroupDoesNotExistError("x"), CannotEndWhileBuildingRootExpressionError(), CannotCallSubexpressionError("capture"), - UnknownElementTypeError("X"), - NonFusableElementError("X"), - UnexpectedFrameTypeError("X"), - FailedToCompileRegexError("boom"), ] for error in errors: text = str(error) diff --git a/tests/introspect/graphviz.test.py b/tests/introspect/graphviz.test.py index aad8378..cb06fda 100644 --- a/tests/introspect/graphviz.test.py +++ b/tests/introspect/graphviz.test.py @@ -301,13 +301,6 @@ def test_render_graphviz_svg_returns_svg_string_when_graphviz_available(): assert "" in output -def test_render_graphviz_svg_raises_missing_dependency_when_graphviz_absent(monkeypatch): - monkeypatch.setattr(graphviz_module, "_graphviz_module", None) - regex = RegexBuilder().digit().to_regex() - with pytest.raises(MissingGraphvizDependencyError): - render_graphviz_svg(regex.elements) - - def test_module_level_import_falls_back_to_none_when_graphviz_missing(monkeypatch): saved_module = sys.modules.pop("graphviz", None) real_import = builtins.__import__ @@ -321,7 +314,9 @@ def test_module_level_import_falls_back_to_none_when_graphviz_missing(monkeypatc monkeypatch.setitem(sys.modules, "graphviz", None) reloaded = importlib.reload(graphviz_module) try: - assert reloaded._graphviz_module is None + regex = RegexBuilder().digit().to_regex() + with pytest.raises(MissingGraphvizDependencyError): + reloaded.render_graphviz_svg(regex.elements) finally: monkeypatch.undo() if saved_module is not None: diff --git a/tests/introspect/verbose.test.py b/tests/introspect/verbose.test.py index 7af8386..0cbe1fd 100644 --- a/tests/introspect/verbose.test.py +++ b/tests/introspect/verbose.test.py @@ -62,7 +62,7 @@ from edify.elements.types.quantifiers import ( ZeroOrMoreLazyElement, ) from edify.introspect import verbose as verbose_module -from edify.introspect.verbose import _render_line, verbose_elements +from edify.introspect.verbose import verbose_elements def _verbose(*elements) -> str: @@ -432,14 +432,9 @@ def test_alignment_uses_common_comment_column_across_lines(): assert len(set(hash_columns)) == 1 -def test_line_without_comment_returns_bare_fragment(): - assert _render_line("", "\\d", "") == "\\d" - - def test_line_with_long_fragment_still_leaves_two_space_gap_before_comment(): - long_fragment = "a" * 30 - output = _render_line("", long_fragment, "note") - assert " # note" in output + output = _verbose(StringElement(value="a" * 30)) + assert " # " in output def test_regex_to_verbose_string_end_to_end_via_builder(): diff --git a/tests/package.test.py b/tests/package.test.py index 384f9ce..747da98 100644 --- a/tests/package.test.py +++ b/tests/package.test.py @@ -1,8 +1,9 @@ """Tests for the top-level :mod:`edify` package surface.""" +import importlib import importlib.metadata -from edify import _resolve_installed_version +import edify def test_version_falls_back_when_package_metadata_missing(monkeypatch): @@ -10,4 +11,5 @@ def test_version_falls_back_when_package_metadata_missing(monkeypatch): raise importlib.metadata.PackageNotFoundError(distribution_name) monkeypatch.setattr(importlib.metadata, "version", raise_not_found) - assert _resolve_installed_version() == "0.0.0" + reloaded = importlib.reload(edify) + assert reloaded.__version__ == "0.0.0" diff --git a/tests/pattern/anchors.test.py b/tests/pattern/anchors.test.py index a42aca0..225e8e9 100644 --- a/tests/pattern/anchors.test.py +++ b/tests/pattern/anchors.test.py @@ -20,8 +20,8 @@ def test_end_compiles_to_dollar_sign(): def test_start_matches_start_of_input_element_from_the_fluent_chain(): - assert START._state == Pattern().start_of_input()._state + assert Pattern().start_of_input() == START def test_end_matches_end_of_input_element_from_the_fluent_chain(): - assert END._state == Pattern().end_of_input()._state + assert Pattern().end_of_input() == END diff --git a/tests/pattern/boundaries.test.py b/tests/pattern/boundaries.test.py index b391733..2892866 100644 --- a/tests/pattern/boundaries.test.py +++ b/tests/pattern/boundaries.test.py @@ -20,8 +20,8 @@ def test_non_word_boundary_compiles_to_backslash_capital_b(): def test_word_boundary_matches_fluent_chain_output(): - assert WORD_BOUNDARY._state == Pattern().word_boundary()._state + assert Pattern().word_boundary() == WORD_BOUNDARY def test_non_word_boundary_matches_fluent_chain_output(): - assert NON_WORD_BOUNDARY._state == Pattern().non_word_boundary()._state + assert Pattern().non_word_boundary() == NON_WORD_BOUNDARY diff --git a/tests/pattern/composition.test.py b/tests/pattern/composition.test.py index 18a6bc2..34d6f89 100644 --- a/tests/pattern/composition.test.py +++ b/tests/pattern/composition.test.py @@ -9,7 +9,7 @@ from edify.errors.input import MustBeInstanceError def test_pattern_builds_the_same_element_tree_as_a_builder(): pattern = Pattern().between(3, 20).word() builder = RegexBuilder().between(3, 20).word() - assert pattern._state == builder._state + assert pattern == builder def test_pattern_exposes_to_regex_string_terminal(): -- cgit v1.2.3