From 41f9780db15367f1bfc2738a7ef296ec399ea782 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 16:32:32 +0200 Subject: [PATCH 01/10] rotate Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/__init__.py | 331 +++++++++++++++++++++++++- src/rfc3987_syntax2/syntax_helpers.py | 331 +------------------------- 2 files changed, 331 insertions(+), 331 deletions(-) diff --git a/src/rfc3987_syntax2/__init__.py b/src/rfc3987_syntax2/__init__.py index 3de7209..9b82840 100644 --- a/src/rfc3987_syntax2/__init__.py +++ b/src/rfc3987_syntax2/__init__.py @@ -2,22 +2,335 @@ # Copyright (c) 2026 Jan Kowalleck - modifications and maintenance # SPDX-License-Identifier: MIT -from typing import Any +from pathlib import Path +from threading import Lock +from typing import Any, Callable, Optional, TYPE_CHECKING, Literal +from warnings import warn -from . import syntax_helpers as _syntax_helpers +from lark import Lark, ParseTree, exceptions -__all__ = _syntax_helpers.__all__ +from .utils import load_grammar -_EXPORTS_SH = frozenset(_syntax_helpers.__all__) + +__all__ = [ + "RFC3987_SYNTAX_PARSER_TYPE", + "RFC3987_SYNTAX_GRAMMAR_PATH", + "RFC3987_SYNTAX_TERMS", + "grammar", + "syntax_parser", + "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", + "is_valid_syntax_scheme", + "is_valid_syntax_ihier_part", + "is_valid_syntax_irelative_ref", + "is_valid_syntax_irelative_part", + "is_valid_syntax_iauthority", + "is_valid_syntax_iuserinfo", + "is_valid_syntax_ihost", + "is_valid_syntax_ireg_name", + "is_valid_syntax_ipath", + "is_valid_syntax_ipath_abempty", + "is_valid_syntax_ipath_absolute", + "is_valid_syntax_ipath_noscheme", + "is_valid_syntax_ipath_rootless", + "is_valid_syntax_ipath_empty", + "is_valid_syntax_isegment", + "is_valid_syntax_isegment_nz", + "is_valid_syntax_isegment_nz_nc", + "is_valid_syntax_ipchar", + "is_valid_syntax_iquery", + "is_valid_syntax_ifragment", + "is_valid_syntax_iunreserved", + "is_valid_syntax_ucschar", + "is_valid_syntax_iprivate", + "is_valid_syntax_sub_delims", + "is_valid_syntax_ip_literal", + "is_valid_syntax_ipvfuture", + "is_valid_syntax_ipv6address", + "is_valid_syntax_h16", + "is_valid_syntax_ls32", + "is_valid_syntax_ipv4address", + "is_valid_syntax_dec_octet", + "is_valid_syntax_digit", + "is_valid_syntax_non_zero", + "is_valid_syntax_unreserved", + "is_valid_syntax_alpha", + "is_valid_syntax_hexdig", + "is_valid_syntax_port", + "is_valid_syntax_pct_encoded", +] + +RFC3987_SYNTAX_PARSER_TYPE: str = "earley" +RFC3987_SYNTAX_GRAMMAR_PATH: Path = Path(__file__).parent / "syntax_rfc3987.lark" +RFC3987_SYNTAX_TERMS: list[str] = [ + "iri", + "iri_reference", + "absolute_iri", + "scheme", + "ihier_part", + "irelative_ref", + "irelative_part", + "iauthority", + "iuserinfo", + "ihost", + "ireg_name", + "ipath", + "ipath_abempty", + "ipath_absolute", + "ipath_noscheme", + "ipath_rootless", + "ipath_empty", + "isegment", + "isegment_nz", + "isegment_nz_nc", + "ipchar", + "iquery", + "ifragment", + "iunreserved", + "ucschar", + "iprivate", + "sub_delims", + "ip_literal", + "ipvfuture", + "ipv6address", + "h16", + "ls32", + "ipv4address", + "dec_octet", + "digit", + "non_zero", + "unreserved", + "alpha", + "hexdig", + "port", + "pct_encoded", +] + +_T_SYNTAX_PARSER_TERM = Literal["iri", "iri_reference", "absolute_iri"] +_SYNTAX_PARSER_STARTS: list[_T_SYNTAX_PARSER_TERM] = ["iri", "iri_reference", "absolute_iri"] + +def parse(term: _T_SYNTAX_PARSER_TERM, value: str) -> 'ParseTree': + return _get_syntax_parser().parse(value, start=term) + + +def is_valid_syntax(term: _T_SYNTAX_PARSER_TERM, value: str) -> bool: + try: + parse(term=term, value=value) + except exceptions.UnexpectedInput: + # from Lark.parse() + return False + except exceptions.LarkError as err: + # from Lark internals / initialization (non-UnexpectedInput) + warn("Unexpected LarkError (non-UnexpectedInput) " + f"for term={term!r}: {type(err).__name__}: {err}", + RuntimeWarning, + stacklevel=2) + return False + return True + +_T_SYNTAX_VALIDATOR = Callable[[str], bool] + +def make_syntax_validator(rule_name: str) -> _T_SYNTAX_VALIDATOR: + parser: Optional[Lark] = None + parser_lock = Lock() + + def syntax_validator(text: str) -> bool: + nonlocal parser + with parser_lock: + if parser is None: + parser = Lark(_get_grammar(), + start=rule_name, + parser=RFC3987_SYNTAX_PARSER_TYPE) + try: + parser.parse(text) + except exceptions.LarkError: + return False + else: + return True + + return syntax_validator + + +_grammar: Optional[str] = None +_grammar_lock = Lock() + +def _get_grammar() -> str: + """this is private API""" + global _grammar + with _grammar_lock: + if _grammar is None: + _grammar = load_grammar(RFC3987_SYNTAX_GRAMMAR_PATH) + return _grammar + + +_syntax_parser: Optional[Lark] = None +_syntax_parser_lock = Lock() + +def _get_syntax_parser() -> Lark: + """this is private API""" + global _syntax_parser + if _syntax_parser is not None: + return _syntax_parser + grammar = _get_grammar() + with _syntax_parser_lock: + if _syntax_parser is None: + _syntax_parser = Lark(grammar, + start=_SYNTAX_PARSER_STARTS, + parser=RFC3987_SYNTAX_PARSER_TYPE) + return _syntax_parser + + +is_valid_syntax_iri = make_syntax_validator("iri") + +is_valid_syntax_iri_reference = make_syntax_validator("iri_reference") + +is_valid_syntax_absolute_iri = make_syntax_validator("absolute_iri") + +is_valid_syntax_scheme = make_syntax_validator("scheme") + +is_valid_syntax_ihier_part = make_syntax_validator("ihier_part") + +is_valid_syntax_irelative_ref = make_syntax_validator("irelative_ref") + +is_valid_syntax_irelative_part = make_syntax_validator("irelative_part") + +is_valid_syntax_iauthority = make_syntax_validator("iauthority") + +is_valid_syntax_iuserinfo = make_syntax_validator("iuserinfo") + +is_valid_syntax_ihost = make_syntax_validator("ihost") + +is_valid_syntax_ireg_name = make_syntax_validator("ireg_name") + +is_valid_syntax_ipath = make_syntax_validator("ipath") + +is_valid_syntax_ipath_abempty = make_syntax_validator("ipath_abempty") + +is_valid_syntax_ipath_absolute = make_syntax_validator("ipath_absolute") + +is_valid_syntax_ipath_noscheme = make_syntax_validator("ipath_noscheme") + +is_valid_syntax_ipath_rootless = make_syntax_validator("ipath_rootless") + +is_valid_syntax_ipath_empty = make_syntax_validator("ipath_empty") + +is_valid_syntax_isegment = make_syntax_validator("isegment") + +is_valid_syntax_isegment_nz = make_syntax_validator("isegment_nz") + +is_valid_syntax_isegment_nz_nc = make_syntax_validator("isegment_nz_nc") + +is_valid_syntax_ipchar = make_syntax_validator("ipchar") + +is_valid_syntax_iquery = make_syntax_validator("iquery") + +is_valid_syntax_ifragment = make_syntax_validator("ifragment") + +is_valid_syntax_iunreserved = make_syntax_validator("iunreserved") + +is_valid_syntax_ucschar = make_syntax_validator("ucschar") + +is_valid_syntax_iprivate = make_syntax_validator("iprivate") + +is_valid_syntax_sub_delims = make_syntax_validator("sub_delims") + +is_valid_syntax_ip_literal = make_syntax_validator("ip_literal") + +is_valid_syntax_ipvfuture = make_syntax_validator("ipvfuture") + +is_valid_syntax_ipv6address = make_syntax_validator("ipv6address") + +is_valid_syntax_h16 = make_syntax_validator("h16") + +is_valid_syntax_ls32 = make_syntax_validator("ls32") + +is_valid_syntax_ipv4address = make_syntax_validator("ipv4address") + +is_valid_syntax_dec_octet = make_syntax_validator("dec_octet") + +is_valid_syntax_digit = make_syntax_validator("digit") + +is_valid_syntax_non_zero = make_syntax_validator("non_zero") + +is_valid_syntax_unreserved = make_syntax_validator("unreserved") + +is_valid_syntax_alpha = make_syntax_validator("alpha") + +is_valid_syntax_hexdig = make_syntax_validator("hexdig") + +is_valid_syntax_port = make_syntax_validator("port") + +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 + grammar: str + syntax_parser: Lark def __getattr__(name: str) -> Any: - if name in _EXPORTS_SH: - value = getattr(_syntax_helpers, name) - globals()[name] = value - return value + if name == "grammar": + global grammar + grammar = _get_grammar() + return grammar + if name == "syntax_parser": + global syntax_parser + syntax_parser = _get_syntax_parser() + return syntax_parser raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - def __dir__() -> list[str]: return sorted(set(globals().keys()) | set(__all__)) + +# endregion lazy loaded attrs diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index 9b82840..3de7209 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -2,335 +2,22 @@ # Copyright (c) 2026 Jan Kowalleck - modifications and maintenance # SPDX-License-Identifier: MIT -from pathlib import Path -from threading import Lock -from typing import Any, Callable, Optional, TYPE_CHECKING, Literal -from warnings import warn +from typing import Any -from lark import Lark, ParseTree, exceptions +from . import syntax_helpers as _syntax_helpers -from .utils import load_grammar +__all__ = _syntax_helpers.__all__ - -__all__ = [ - "RFC3987_SYNTAX_PARSER_TYPE", - "RFC3987_SYNTAX_GRAMMAR_PATH", - "RFC3987_SYNTAX_TERMS", - "grammar", - "syntax_parser", - "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", - "is_valid_syntax_scheme", - "is_valid_syntax_ihier_part", - "is_valid_syntax_irelative_ref", - "is_valid_syntax_irelative_part", - "is_valid_syntax_iauthority", - "is_valid_syntax_iuserinfo", - "is_valid_syntax_ihost", - "is_valid_syntax_ireg_name", - "is_valid_syntax_ipath", - "is_valid_syntax_ipath_abempty", - "is_valid_syntax_ipath_absolute", - "is_valid_syntax_ipath_noscheme", - "is_valid_syntax_ipath_rootless", - "is_valid_syntax_ipath_empty", - "is_valid_syntax_isegment", - "is_valid_syntax_isegment_nz", - "is_valid_syntax_isegment_nz_nc", - "is_valid_syntax_ipchar", - "is_valid_syntax_iquery", - "is_valid_syntax_ifragment", - "is_valid_syntax_iunreserved", - "is_valid_syntax_ucschar", - "is_valid_syntax_iprivate", - "is_valid_syntax_sub_delims", - "is_valid_syntax_ip_literal", - "is_valid_syntax_ipvfuture", - "is_valid_syntax_ipv6address", - "is_valid_syntax_h16", - "is_valid_syntax_ls32", - "is_valid_syntax_ipv4address", - "is_valid_syntax_dec_octet", - "is_valid_syntax_digit", - "is_valid_syntax_non_zero", - "is_valid_syntax_unreserved", - "is_valid_syntax_alpha", - "is_valid_syntax_hexdig", - "is_valid_syntax_port", - "is_valid_syntax_pct_encoded", -] - -RFC3987_SYNTAX_PARSER_TYPE: str = "earley" -RFC3987_SYNTAX_GRAMMAR_PATH: Path = Path(__file__).parent / "syntax_rfc3987.lark" -RFC3987_SYNTAX_TERMS: list[str] = [ - "iri", - "iri_reference", - "absolute_iri", - "scheme", - "ihier_part", - "irelative_ref", - "irelative_part", - "iauthority", - "iuserinfo", - "ihost", - "ireg_name", - "ipath", - "ipath_abempty", - "ipath_absolute", - "ipath_noscheme", - "ipath_rootless", - "ipath_empty", - "isegment", - "isegment_nz", - "isegment_nz_nc", - "ipchar", - "iquery", - "ifragment", - "iunreserved", - "ucschar", - "iprivate", - "sub_delims", - "ip_literal", - "ipvfuture", - "ipv6address", - "h16", - "ls32", - "ipv4address", - "dec_octet", - "digit", - "non_zero", - "unreserved", - "alpha", - "hexdig", - "port", - "pct_encoded", -] - -_T_SYNTAX_PARSER_TERM = Literal["iri", "iri_reference", "absolute_iri"] -_SYNTAX_PARSER_STARTS: list[_T_SYNTAX_PARSER_TERM] = ["iri", "iri_reference", "absolute_iri"] - -def parse(term: _T_SYNTAX_PARSER_TERM, value: str) -> 'ParseTree': - return _get_syntax_parser().parse(value, start=term) - - -def is_valid_syntax(term: _T_SYNTAX_PARSER_TERM, value: str) -> bool: - try: - parse(term=term, value=value) - except exceptions.UnexpectedInput: - # from Lark.parse() - return False - except exceptions.LarkError as err: - # from Lark internals / initialization (non-UnexpectedInput) - warn("Unexpected LarkError (non-UnexpectedInput) " - f"for term={term!r}: {type(err).__name__}: {err}", - RuntimeWarning, - stacklevel=2) - return False - return True - -_T_SYNTAX_VALIDATOR = Callable[[str], bool] - -def make_syntax_validator(rule_name: str) -> _T_SYNTAX_VALIDATOR: - parser: Optional[Lark] = None - parser_lock = Lock() - - def syntax_validator(text: str) -> bool: - nonlocal parser - with parser_lock: - if parser is None: - parser = Lark(_get_grammar(), - start=rule_name, - parser=RFC3987_SYNTAX_PARSER_TYPE) - try: - parser.parse(text) - except exceptions.LarkError: - return False - else: - return True - - return syntax_validator - - -_grammar: Optional[str] = None -_grammar_lock = Lock() - -def _get_grammar() -> str: - """this is private API""" - global _grammar - with _grammar_lock: - if _grammar is None: - _grammar = load_grammar(RFC3987_SYNTAX_GRAMMAR_PATH) - return _grammar - - -_syntax_parser: Optional[Lark] = None -_syntax_parser_lock = Lock() - -def _get_syntax_parser() -> Lark: - """this is private API""" - global _syntax_parser - if _syntax_parser is not None: - return _syntax_parser - grammar = _get_grammar() - with _syntax_parser_lock: - if _syntax_parser is None: - _syntax_parser = Lark(grammar, - start=_SYNTAX_PARSER_STARTS, - parser=RFC3987_SYNTAX_PARSER_TYPE) - return _syntax_parser - - -is_valid_syntax_iri = make_syntax_validator("iri") - -is_valid_syntax_iri_reference = make_syntax_validator("iri_reference") - -is_valid_syntax_absolute_iri = make_syntax_validator("absolute_iri") - -is_valid_syntax_scheme = make_syntax_validator("scheme") - -is_valid_syntax_ihier_part = make_syntax_validator("ihier_part") - -is_valid_syntax_irelative_ref = make_syntax_validator("irelative_ref") - -is_valid_syntax_irelative_part = make_syntax_validator("irelative_part") - -is_valid_syntax_iauthority = make_syntax_validator("iauthority") - -is_valid_syntax_iuserinfo = make_syntax_validator("iuserinfo") - -is_valid_syntax_ihost = make_syntax_validator("ihost") - -is_valid_syntax_ireg_name = make_syntax_validator("ireg_name") - -is_valid_syntax_ipath = make_syntax_validator("ipath") - -is_valid_syntax_ipath_abempty = make_syntax_validator("ipath_abempty") - -is_valid_syntax_ipath_absolute = make_syntax_validator("ipath_absolute") - -is_valid_syntax_ipath_noscheme = make_syntax_validator("ipath_noscheme") - -is_valid_syntax_ipath_rootless = make_syntax_validator("ipath_rootless") - -is_valid_syntax_ipath_empty = make_syntax_validator("ipath_empty") - -is_valid_syntax_isegment = make_syntax_validator("isegment") - -is_valid_syntax_isegment_nz = make_syntax_validator("isegment_nz") - -is_valid_syntax_isegment_nz_nc = make_syntax_validator("isegment_nz_nc") - -is_valid_syntax_ipchar = make_syntax_validator("ipchar") - -is_valid_syntax_iquery = make_syntax_validator("iquery") - -is_valid_syntax_ifragment = make_syntax_validator("ifragment") - -is_valid_syntax_iunreserved = make_syntax_validator("iunreserved") - -is_valid_syntax_ucschar = make_syntax_validator("ucschar") - -is_valid_syntax_iprivate = make_syntax_validator("iprivate") - -is_valid_syntax_sub_delims = make_syntax_validator("sub_delims") - -is_valid_syntax_ip_literal = make_syntax_validator("ip_literal") - -is_valid_syntax_ipvfuture = make_syntax_validator("ipvfuture") - -is_valid_syntax_ipv6address = make_syntax_validator("ipv6address") - -is_valid_syntax_h16 = make_syntax_validator("h16") - -is_valid_syntax_ls32 = make_syntax_validator("ls32") - -is_valid_syntax_ipv4address = make_syntax_validator("ipv4address") - -is_valid_syntax_dec_octet = make_syntax_validator("dec_octet") - -is_valid_syntax_digit = make_syntax_validator("digit") - -is_valid_syntax_non_zero = make_syntax_validator("non_zero") - -is_valid_syntax_unreserved = make_syntax_validator("unreserved") - -is_valid_syntax_alpha = make_syntax_validator("alpha") - -is_valid_syntax_hexdig = make_syntax_validator("hexdig") - -is_valid_syntax_port = make_syntax_validator("port") - -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 - grammar: str - syntax_parser: Lark +_EXPORTS_SH = frozenset(_syntax_helpers.__all__) def __getattr__(name: str) -> Any: - if name == "grammar": - global grammar - grammar = _get_grammar() - return grammar - if name == "syntax_parser": - global syntax_parser - syntax_parser = _get_syntax_parser() - return syntax_parser + if name in _EXPORTS_SH: + value = getattr(_syntax_helpers, name) + globals()[name] = value + return value raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + def __dir__() -> list[str]: return sorted(set(globals().keys()) | set(__all__)) - -# endregion lazy loaded attrs From f59bf4ea7350ccb9622f95cbf6aaa069f170b7b5 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:16:14 +0200 Subject: [PATCH 02/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/__init__.py | 59 +---------------------- src/rfc3987_syntax2/syntax_helpers.py | 69 ++++++++++++++++++++++++--- src/rfc3987_syntax2/utils.py | 4 ++ 3 files changed, 69 insertions(+), 63 deletions(-) diff --git a/src/rfc3987_syntax2/__init__.py b/src/rfc3987_syntax2/__init__.py index 9b82840..a6956c9 100644 --- a/src/rfc3987_syntax2/__init__.py +++ b/src/rfc3987_syntax2/__init__.py @@ -9,62 +9,6 @@ from lark import Lark, ParseTree, exceptions -from .utils import load_grammar - - -__all__ = [ - "RFC3987_SYNTAX_PARSER_TYPE", - "RFC3987_SYNTAX_GRAMMAR_PATH", - "RFC3987_SYNTAX_TERMS", - "grammar", - "syntax_parser", - "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", - "is_valid_syntax_scheme", - "is_valid_syntax_ihier_part", - "is_valid_syntax_irelative_ref", - "is_valid_syntax_irelative_part", - "is_valid_syntax_iauthority", - "is_valid_syntax_iuserinfo", - "is_valid_syntax_ihost", - "is_valid_syntax_ireg_name", - "is_valid_syntax_ipath", - "is_valid_syntax_ipath_abempty", - "is_valid_syntax_ipath_absolute", - "is_valid_syntax_ipath_noscheme", - "is_valid_syntax_ipath_rootless", - "is_valid_syntax_ipath_empty", - "is_valid_syntax_isegment", - "is_valid_syntax_isegment_nz", - "is_valid_syntax_isegment_nz_nc", - "is_valid_syntax_ipchar", - "is_valid_syntax_iquery", - "is_valid_syntax_ifragment", - "is_valid_syntax_iunreserved", - "is_valid_syntax_ucschar", - "is_valid_syntax_iprivate", - "is_valid_syntax_sub_delims", - "is_valid_syntax_ip_literal", - "is_valid_syntax_ipvfuture", - "is_valid_syntax_ipv6address", - "is_valid_syntax_h16", - "is_valid_syntax_ls32", - "is_valid_syntax_ipv4address", - "is_valid_syntax_dec_octet", - "is_valid_syntax_digit", - "is_valid_syntax_non_zero", - "is_valid_syntax_unreserved", - "is_valid_syntax_alpha", - "is_valid_syntax_hexdig", - "is_valid_syntax_port", - "is_valid_syntax_pct_encoded", -] - RFC3987_SYNTAX_PARSER_TYPE: str = "earley" RFC3987_SYNTAX_GRAMMAR_PATH: Path = Path(__file__).parent / "syntax_rfc3987.lark" RFC3987_SYNTAX_TERMS: list[str] = [ @@ -164,7 +108,7 @@ def _get_grammar() -> str: global _grammar with _grammar_lock: if _grammar is None: - _grammar = load_grammar(RFC3987_SYNTAX_GRAMMAR_PATH) + _grammar = RFC3987_SYNTAX_GRAMMAR_PATH.read_text(encoding='utf-8') return _grammar @@ -321,6 +265,7 @@ def _get_syntax_parser() -> Lark: def __getattr__(name: str) -> Any: if name == "grammar": + print('get_grammar') global grammar grammar = _get_grammar() return grammar diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index 3de7209..4f5300a 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -2,18 +2,75 @@ # Copyright (c) 2026 Jan Kowalleck - modifications and maintenance # SPDX-License-Identifier: MIT -from typing import Any +""" +Deprecated re-export of package attributes. +""" -from . import syntax_helpers as _syntax_helpers +from importlib import import_module +from typing import Any, TYPE_CHECKING -__all__ = _syntax_helpers.__all__ +__all__ = [ + "RFC3987_SYNTAX_PARSER_TYPE", + "RFC3987_SYNTAX_GRAMMAR_PATH", + "RFC3987_SYNTAX_TERMS", + "grammar", + "syntax_parser", + "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", + "is_valid_syntax_scheme", + "is_valid_syntax_ihier_part", + "is_valid_syntax_irelative_ref", + "is_valid_syntax_irelative_part", + "is_valid_syntax_iauthority", + "is_valid_syntax_iuserinfo", + "is_valid_syntax_ihost", + "is_valid_syntax_ireg_name", + "is_valid_syntax_ipath", + "is_valid_syntax_ipath_abempty", + "is_valid_syntax_ipath_absolute", + "is_valid_syntax_ipath_noscheme", + "is_valid_syntax_ipath_rootless", + "is_valid_syntax_ipath_empty", + "is_valid_syntax_isegment", + "is_valid_syntax_isegment_nz", + "is_valid_syntax_isegment_nz_nc", + "is_valid_syntax_ipchar", + "is_valid_syntax_iquery", + "is_valid_syntax_ifragment", + "is_valid_syntax_iunreserved", + "is_valid_syntax_ucschar", + "is_valid_syntax_iprivate", + "is_valid_syntax_sub_delims", + "is_valid_syntax_ip_literal", + "is_valid_syntax_ipvfuture", + "is_valid_syntax_ipv6address", + "is_valid_syntax_h16", + "is_valid_syntax_ls32", + "is_valid_syntax_ipv4address", + "is_valid_syntax_dec_octet", + "is_valid_syntax_digit", + "is_valid_syntax_non_zero", + "is_valid_syntax_unreserved", + "is_valid_syntax_alpha", + "is_valid_syntax_hexdig", + "is_valid_syntax_port", + "is_valid_syntax_pct_encoded", +] -_EXPORTS_SH = frozenset(_syntax_helpers.__all__) +if TYPE_CHECKING: + from . import * +_pkg = import_module(".", package=__package__) + def __getattr__(name: str) -> Any: - if name in _EXPORTS_SH: - value = getattr(_syntax_helpers, name) + if name in __all__: + value = getattr(_pkg, name) globals()[name] = value return value raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/src/rfc3987_syntax2/utils.py b/src/rfc3987_syntax2/utils.py index 32f05dc..024f36a 100644 --- a/src/rfc3987_syntax2/utils.py +++ b/src/rfc3987_syntax2/utils.py @@ -2,6 +2,10 @@ # Copyright (c) 2026 Jan Kowalleck - modifications and maintenance # SPDX-License-Identifier: MIT +""" +deprecated function set. +""" + from pathlib import Path From bcb2a01e82cb46c73e25cd1832035ece9465340f Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:16:48 +0200 Subject: [PATCH 03/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/__init__.py | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/rfc3987_syntax2/__init__.py b/src/rfc3987_syntax2/__init__.py index a6956c9..9f18026 100644 --- a/src/rfc3987_syntax2/__init__.py +++ b/src/rfc3987_syntax2/__init__.py @@ -9,6 +9,60 @@ from lark import Lark, ParseTree, exceptions + +__all__ = [ + "RFC3987_SYNTAX_PARSER_TYPE", + "RFC3987_SYNTAX_GRAMMAR_PATH", + "RFC3987_SYNTAX_TERMS", + "grammar", + "syntax_parser", + "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", + "is_valid_syntax_scheme", + "is_valid_syntax_ihier_part", + "is_valid_syntax_irelative_ref", + "is_valid_syntax_irelative_part", + "is_valid_syntax_iauthority", + "is_valid_syntax_iuserinfo", + "is_valid_syntax_ihost", + "is_valid_syntax_ireg_name", + "is_valid_syntax_ipath", + "is_valid_syntax_ipath_abempty", + "is_valid_syntax_ipath_absolute", + "is_valid_syntax_ipath_noscheme", + "is_valid_syntax_ipath_rootless", + "is_valid_syntax_ipath_empty", + "is_valid_syntax_isegment", + "is_valid_syntax_isegment_nz", + "is_valid_syntax_isegment_nz_nc", + "is_valid_syntax_ipchar", + "is_valid_syntax_iquery", + "is_valid_syntax_ifragment", + "is_valid_syntax_iunreserved", + "is_valid_syntax_ucschar", + "is_valid_syntax_iprivate", + "is_valid_syntax_sub_delims", + "is_valid_syntax_ip_literal", + "is_valid_syntax_ipvfuture", + "is_valid_syntax_ipv6address", + "is_valid_syntax_h16", + "is_valid_syntax_ls32", + "is_valid_syntax_ipv4address", + "is_valid_syntax_dec_octet", + "is_valid_syntax_digit", + "is_valid_syntax_non_zero", + "is_valid_syntax_unreserved", + "is_valid_syntax_alpha", + "is_valid_syntax_hexdig", + "is_valid_syntax_port", + "is_valid_syntax_pct_encoded", +] + RFC3987_SYNTAX_PARSER_TYPE: str = "earley" RFC3987_SYNTAX_GRAMMAR_PATH: Path = Path(__file__).parent / "syntax_rfc3987.lark" RFC3987_SYNTAX_TERMS: list[str] = [ From 325e68922c15c16929771b7bfcfa22f3964fd6ab Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:18:45 +0200 Subject: [PATCH 04/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/__init__.py | 53 --------------------------------- 1 file changed, 53 deletions(-) diff --git a/src/rfc3987_syntax2/__init__.py b/src/rfc3987_syntax2/__init__.py index 9f18026..c380acc 100644 --- a/src/rfc3987_syntax2/__init__.py +++ b/src/rfc3987_syntax2/__init__.py @@ -10,59 +10,6 @@ from lark import Lark, ParseTree, exceptions -__all__ = [ - "RFC3987_SYNTAX_PARSER_TYPE", - "RFC3987_SYNTAX_GRAMMAR_PATH", - "RFC3987_SYNTAX_TERMS", - "grammar", - "syntax_parser", - "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", - "is_valid_syntax_scheme", - "is_valid_syntax_ihier_part", - "is_valid_syntax_irelative_ref", - "is_valid_syntax_irelative_part", - "is_valid_syntax_iauthority", - "is_valid_syntax_iuserinfo", - "is_valid_syntax_ihost", - "is_valid_syntax_ireg_name", - "is_valid_syntax_ipath", - "is_valid_syntax_ipath_abempty", - "is_valid_syntax_ipath_absolute", - "is_valid_syntax_ipath_noscheme", - "is_valid_syntax_ipath_rootless", - "is_valid_syntax_ipath_empty", - "is_valid_syntax_isegment", - "is_valid_syntax_isegment_nz", - "is_valid_syntax_isegment_nz_nc", - "is_valid_syntax_ipchar", - "is_valid_syntax_iquery", - "is_valid_syntax_ifragment", - "is_valid_syntax_iunreserved", - "is_valid_syntax_ucschar", - "is_valid_syntax_iprivate", - "is_valid_syntax_sub_delims", - "is_valid_syntax_ip_literal", - "is_valid_syntax_ipvfuture", - "is_valid_syntax_ipv6address", - "is_valid_syntax_h16", - "is_valid_syntax_ls32", - "is_valid_syntax_ipv4address", - "is_valid_syntax_dec_octet", - "is_valid_syntax_digit", - "is_valid_syntax_non_zero", - "is_valid_syntax_unreserved", - "is_valid_syntax_alpha", - "is_valid_syntax_hexdig", - "is_valid_syntax_port", - "is_valid_syntax_pct_encoded", -] - RFC3987_SYNTAX_PARSER_TYPE: str = "earley" RFC3987_SYNTAX_GRAMMAR_PATH: Path = Path(__file__).parent / "syntax_rfc3987.lark" RFC3987_SYNTAX_TERMS: list[str] = [ From 8d6c4857ba155edc550a5ccd469d59ffabcd0260 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:20:57 +0200 Subject: [PATCH 05/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rfc3987_syntax2/__init__.py b/src/rfc3987_syntax2/__init__.py index c380acc..126720d 100644 --- a/src/rfc3987_syntax2/__init__.py +++ b/src/rfc3987_syntax2/__init__.py @@ -277,6 +277,6 @@ def __getattr__(name: str) -> Any: raise AttributeError(f"module {__name__!r} has no attribute {name!r}") def __dir__() -> list[str]: - return sorted(set(globals().keys()) | set(__all__)) + return sorted(set(globals().keys()) | {"grammar", "syntax_parser"}) # endregion lazy loaded attrs From 88a72d78e6add8e3aaf8236a2685efb295f0a978 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:23:39 +0200 Subject: [PATCH 06/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/syntax_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index 4f5300a..a992a96 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -64,7 +64,7 @@ if TYPE_CHECKING: from . import * - +# cannot simply import everything, since we need to be aware of lazy-loading _pkg = import_module(".", package=__package__) From cc5a009ec34dac6c69d3677f34ccc680fecaddaa Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:25:28 +0200 Subject: [PATCH 07/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/syntax_helpers.py | 74 ++------------------------- 1 file changed, 3 insertions(+), 71 deletions(-) diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index a992a96..f25016a 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -5,76 +5,8 @@ """ Deprecated re-export of package attributes. """ - +import sys from importlib import import_module -from typing import Any, TYPE_CHECKING - -__all__ = [ - "RFC3987_SYNTAX_PARSER_TYPE", - "RFC3987_SYNTAX_GRAMMAR_PATH", - "RFC3987_SYNTAX_TERMS", - "grammar", - "syntax_parser", - "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", - "is_valid_syntax_scheme", - "is_valid_syntax_ihier_part", - "is_valid_syntax_irelative_ref", - "is_valid_syntax_irelative_part", - "is_valid_syntax_iauthority", - "is_valid_syntax_iuserinfo", - "is_valid_syntax_ihost", - "is_valid_syntax_ireg_name", - "is_valid_syntax_ipath", - "is_valid_syntax_ipath_abempty", - "is_valid_syntax_ipath_absolute", - "is_valid_syntax_ipath_noscheme", - "is_valid_syntax_ipath_rootless", - "is_valid_syntax_ipath_empty", - "is_valid_syntax_isegment", - "is_valid_syntax_isegment_nz", - "is_valid_syntax_isegment_nz_nc", - "is_valid_syntax_ipchar", - "is_valid_syntax_iquery", - "is_valid_syntax_ifragment", - "is_valid_syntax_iunreserved", - "is_valid_syntax_ucschar", - "is_valid_syntax_iprivate", - "is_valid_syntax_sub_delims", - "is_valid_syntax_ip_literal", - "is_valid_syntax_ipvfuture", - "is_valid_syntax_ipv6address", - "is_valid_syntax_h16", - "is_valid_syntax_ls32", - "is_valid_syntax_ipv4address", - "is_valid_syntax_dec_octet", - "is_valid_syntax_digit", - "is_valid_syntax_non_zero", - "is_valid_syntax_unreserved", - "is_valid_syntax_alpha", - "is_valid_syntax_hexdig", - "is_valid_syntax_port", - "is_valid_syntax_pct_encoded", -] - -if TYPE_CHECKING: - from . import * -# cannot simply import everything, since we need to be aware of lazy-loading - -_pkg = import_module(".", package=__package__) - -def __getattr__(name: str) -> Any: - if name in __all__: - value = getattr(_pkg, name) - globals()[name] = value - return value - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") - -def __dir__() -> list[str]: - return sorted(set(globals().keys()) | set(__all__)) +pkg = import_module(".", package=__package__) +sys.modules[__name__] = pkg From a6044a9f36810914f15271ca15eb4baf277a88c0 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:26:06 +0200 Subject: [PATCH 08/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/syntax_helpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index f25016a..a3b9bf0 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -3,10 +3,10 @@ # SPDX-License-Identifier: MIT """ -Deprecated re-export of package attributes. +Deprecated alias of package attributes. """ + import sys from importlib import import_module -pkg = import_module(".", package=__package__) -sys.modules[__name__] = pkg +sys.modules[__name__] = import_module(".", package=__package__) From fdd4b8df84a7f6a9fa048804fde18c05ecf0422f Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:28:10 +0200 Subject: [PATCH 09/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/syntax_helpers.py | 74 +++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index a3b9bf0..a992a96 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -3,10 +3,78 @@ # SPDX-License-Identifier: MIT """ -Deprecated alias of package attributes. +Deprecated re-export of package attributes. """ -import sys from importlib import import_module +from typing import Any, TYPE_CHECKING -sys.modules[__name__] = import_module(".", package=__package__) +__all__ = [ + "RFC3987_SYNTAX_PARSER_TYPE", + "RFC3987_SYNTAX_GRAMMAR_PATH", + "RFC3987_SYNTAX_TERMS", + "grammar", + "syntax_parser", + "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", + "is_valid_syntax_scheme", + "is_valid_syntax_ihier_part", + "is_valid_syntax_irelative_ref", + "is_valid_syntax_irelative_part", + "is_valid_syntax_iauthority", + "is_valid_syntax_iuserinfo", + "is_valid_syntax_ihost", + "is_valid_syntax_ireg_name", + "is_valid_syntax_ipath", + "is_valid_syntax_ipath_abempty", + "is_valid_syntax_ipath_absolute", + "is_valid_syntax_ipath_noscheme", + "is_valid_syntax_ipath_rootless", + "is_valid_syntax_ipath_empty", + "is_valid_syntax_isegment", + "is_valid_syntax_isegment_nz", + "is_valid_syntax_isegment_nz_nc", + "is_valid_syntax_ipchar", + "is_valid_syntax_iquery", + "is_valid_syntax_ifragment", + "is_valid_syntax_iunreserved", + "is_valid_syntax_ucschar", + "is_valid_syntax_iprivate", + "is_valid_syntax_sub_delims", + "is_valid_syntax_ip_literal", + "is_valid_syntax_ipvfuture", + "is_valid_syntax_ipv6address", + "is_valid_syntax_h16", + "is_valid_syntax_ls32", + "is_valid_syntax_ipv4address", + "is_valid_syntax_dec_octet", + "is_valid_syntax_digit", + "is_valid_syntax_non_zero", + "is_valid_syntax_unreserved", + "is_valid_syntax_alpha", + "is_valid_syntax_hexdig", + "is_valid_syntax_port", + "is_valid_syntax_pct_encoded", +] + +if TYPE_CHECKING: + from . import * +# cannot simply import everything, since we need to be aware of lazy-loading + +_pkg = import_module(".", package=__package__) + +def __getattr__(name: str) -> Any: + if name in __all__: + value = getattr(_pkg, name) + globals()[name] = value + return value + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def __dir__() -> list[str]: + return sorted(set(globals().keys()) | set(__all__)) From 2b57a8921009fabac2d6e6b52417dd65c8660e84 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Tue, 21 Jul 2026 17:32:14 +0200 Subject: [PATCH 10/10] refactor: move public api Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/syntax_helpers.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index a992a96..7c926b2 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -75,6 +75,13 @@ def __getattr__(name: str) -> Any: return value raise AttributeError(f"module {__name__!r} has no attribute {name!r}") +def __setattr__(name: str, value: Any) -> Any: + # for monkey patching + if name in __all__: + setattr(_pkg, name, value) + globals()[name] = value + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + def __dir__() -> list[str]: return sorted(set(globals().keys()) | set(__all__))