diff --git a/CHANGELOG.md b/CHANGELOG.md index 2eedfc5..4ae7d26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file. * Function `is_valid_syntax_scheme` (via [#48]) * Function `is_valid_syntax_non_zero` (via [#48]) * Function `is_valid_syntax_pct_encoded` (via [#48]) + * Dict `RFC3987_SYNTAX_TERM_VALIDATORS` mapping `RFC3987_SYNTAX_TERMS` to validator function (via [#49]) * Performance * Public symbols use lazy loading (via [#43]) * Tests @@ -26,6 +27,7 @@ All notable changes to this project will be documented in this file. [#43]: https://github.com/jkowalleck/rfc3987-syntax2/pull/43 [#45]: https://github.com/jkowalleck/rfc3987-syntax2/pull/45 [#48]: https://github.com/jkowalleck/rfc3987-syntax2/pull/48 +[#49]: https://github.com/jkowalleck/rfc3987-syntax2/pull/49 ## v1.2.0 - 2026-07-16 diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index bbf821f..8592598 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -19,6 +19,7 @@ "parse", "is_valid_syntax", "make_syntax_validator", + "RFC3987_SYNTAX_TERM_VALIDATORS", "is_valid_syntax_iri", "is_valid_syntax_iri_reference", "is_valid_syntax_absolute_iri", @@ -120,8 +121,9 @@ def is_valid_syntax(term: str, value: str) -> bool: except exceptions.LarkError: return False +_T_SYNTAX_VALIDATOR = Callable[[str], bool] -def make_syntax_validator(rule_name: str) -> Callable[[str], bool]: +def make_syntax_validator(rule_name: str) -> _T_SYNTAX_VALIDATOR: parser: Optional[Lark] = None parser_lock = Lock() @@ -252,6 +254,51 @@ def _get_syntax_parser() -> Lark: is_valid_syntax_pct_encoded = make_syntax_validator("pct_encoded") +RFC3987_SYNTAX_TERM_VALIDATORS: dict[str, _T_SYNTAX_VALIDATOR] = { # frozendict + "iri": is_valid_syntax_iri, + "iri_reference": is_valid_syntax_iri_reference, + "absolute_iri": is_valid_syntax_absolute_iri, + "scheme": is_valid_syntax_scheme, + "ihier_part": is_valid_syntax_ihier_part, + "irelative_ref": is_valid_syntax_irelative_ref, + "irelative_part": is_valid_syntax_irelative_part, + "iauthority": is_valid_syntax_iauthority, + "iuserinfo": is_valid_syntax_iuserinfo, + "ihost": is_valid_syntax_ihost, + "ireg_name": is_valid_syntax_ireg_name, + "ipath": is_valid_syntax_ipath, + "ipath_abempty": is_valid_syntax_ipath_abempty, + "ipath_absolute": is_valid_syntax_ipath_absolute, + "ipath_noscheme": is_valid_syntax_ipath_noscheme, + "ipath_rootless": is_valid_syntax_ipath_rootless, + "ipath_empty": is_valid_syntax_ipath_empty, + "isegment": is_valid_syntax_isegment, + "isegment_nz": is_valid_syntax_isegment_nz, + "isegment_nz_nc": is_valid_syntax_isegment_nz_nc, + "ipchar": is_valid_syntax_ipchar, + "iquery": is_valid_syntax_iquery, + "ifragment": is_valid_syntax_ifragment, + "iunreserved": is_valid_syntax_iunreserved, + "ucschar": is_valid_syntax_ucschar, + "iprivate": is_valid_syntax_iprivate, + "sub_delims": is_valid_syntax_sub_delims, + "ip_literal": is_valid_syntax_ip_literal, + "ipvfuture": is_valid_syntax_ipvfuture, + "ipv6address": is_valid_syntax_ipv6address, + "h16": is_valid_syntax_h16, + "ls32": is_valid_syntax_ls32, + "ipv4address": is_valid_syntax_ipv4address, + "dec_octet": is_valid_syntax_dec_octet, + "digit": is_valid_syntax_digit, + "non_zero": is_valid_syntax_non_zero, + "unreserved": is_valid_syntax_unreserved, + "alpha": is_valid_syntax_alpha, + "hexdig": is_valid_syntax_hexdig, + "port": is_valid_syntax_port, + "pct_encoded": is_valid_syntax_pct_encoded, +} + + # region lazy loaded attrs if TYPE_CHECKING: # types for lazy-loaded symbols diff --git a/tests/test_syntax.py b/tests/test_syntax.py index a3c30f2..85298e7 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -2,14 +2,13 @@ # Copyright (c) 2026 Jan Kowalleck - modifications and maintenance # SPDX-License-Identifier: MIT -from typing import TYPE_CHECKING, Any, Callable +from typing import Any, Callable import pytest import rfc3987_syntax2 as sut from . import ( - TESTS_DATA_FILES, valid_syntax_data, invalid_syntax_data, SyntaxCase, @@ -25,7 +24,7 @@ def syntax_data_as_params(src_cb: Callable[[], T_syntax_file]) -> Any: @pytest.mark.parametrize("term,valid_example", syntax_data_as_params(valid_syntax_data)) def test_is_valid_syntax(term: str, valid_example: SyntaxCase) -> None: - actual = getattr(sut, f'is_valid_syntax_{term}')(valid_example["value"]) + actual = sut.RFC3987_SYNTAX_TERM_VALIDATORS[term](valid_example["value"]) print("") print( f"Testing {term} with {valid_example['value']} : {valid_example['reason']}" @@ -37,7 +36,7 @@ def test_is_valid_syntax(term: str, valid_example: SyntaxCase) -> None: @pytest.mark.parametrize("term,invalid_example", syntax_data_as_params(invalid_syntax_data)) def test_not_is_valid_syntax(term: str, invalid_example: SyntaxCase) -> None: - actual = getattr(sut, f'is_valid_syntax_{term}')(invalid_example["value"]) + actual = sut.RFC3987_SYNTAX_TERM_VALIDATORS[term](invalid_example["value"]) print("") print( f"Testing {term} with {invalid_example['value']} : {invalid_example['reason']}" diff --git a/tests/test_term_validators.py b/tests/test_term_validators.py new file mode 100644 index 0000000..3bc0c94 --- /dev/null +++ b/tests/test_term_validators.py @@ -0,0 +1,14 @@ +# Copyright (c) 2025 Will Riley - original author +# Copyright (c) 2026 Jan Kowalleck - modifications and maintenance +# SPDX-License-Identifier: MIT + +import pytest + +import rfc3987_syntax2 as sut + + +@pytest.mark.parametrize("term", sut.RFC3987_SYNTAX_TERMS) +def test_term_validator_as_expected(term: str) -> None: + expected = getattr(sut, f'is_valid_syntax_{term}') + actual = sut.RFC3987_SYNTAX_TERM_VALIDATORS[term] + assert (actual is expected), f"unexpected func for {term}"