diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 72d5a39..e0262eb 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -27,6 +27,34 @@ permissions: contents: read jobs: + static-code-analysis: + name: StaticCodingAnalysis (py${{ matrix.python-version}} ${{ matrix.toxenv-factors }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + include: + - python-version: '3.14' # latest + os: ubuntu-latest + - python-version: '3.9' # lowest + os: ubuntu-latest + steps: + - name: Checkout + # see https://github.com/actions/checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Setup Python Environment + # see https://github.com/actions/setup-python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: ${{ matrix.python-version }} + architecture: 'x64' + - name: Install project + run: pip install --user -e ".[testing]" + - name: Run Mypy + run: python -m mypy -v build-and-test: name: Test (${{ matrix.os }} py${{ matrix.python-version }} ${{ matrix.toxenv-factors }}) runs-on: ${{ matrix.os }} diff --git a/.mypy.ini b/.mypy.ini new file mode 100644 index 0000000..13fb917 --- /dev/null +++ b/.mypy.ini @@ -0,0 +1,34 @@ +[mypy] + +files = src/ + +show_error_codes = True +pretty = True + +warn_unreachable = True +allow_redefinition = False + +# ignore_missing_imports = False +# follow_imports = normal +# follow_imports_for_stubs = True + +### Strict mode ### +warn_unused_configs = True +disallow_subclassing_any = True +disallow_any_generics = True +disallow_untyped_calls = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +check_untyped_defs = True +disallow_untyped_decorators = True +no_implicit_optional = True +warn_redundant_casts = True +warn_unused_ignores = True +warn_return_any = True +no_implicit_reexport = True + +[mypy-pytest.*] +ignore_missing_imports = True + +[mypy-tests.*] +disallow_untyped_decorators = False \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3964458..58fbe21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file. +* Added + * Assert static typing (via [#16]) + +[#16]: https://github.com/jkowalleck/rfc3987-syntax2/pull/16 + ## v1.1.1 - 2026-07-14 ### Fork release `rfc3987-syntax2` diff --git a/pyproject.toml b/pyproject.toml index 43251d8..cbcc0e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,8 @@ classifiers = [ "Programming Language :: Python :: 3", "Topic :: Scientific/Engineering", "Topic :: Software Development", - "Topic :: Utilities" + "Topic :: Utilities", + "Typing :: Typed", ] dependencies = ["lark>=1.2.2"] @@ -41,6 +42,7 @@ Source = "https://github.com/jkowalleck/rfc3987-syntax2" [project.optional-dependencies] testing = [ + "mypy>=1.15,<2", "typing_extensions>=4.16.0", "pytest>=8.3.5", ] diff --git a/src/rfc3987_syntax2/py.typed b/src/rfc3987_syntax2/py.typed new file mode 100644 index 0000000..161a597 --- /dev/null +++ b/src/rfc3987_syntax2/py.typed @@ -0,0 +1 @@ +# see https://peps.python.org/pep-0561/ \ No newline at end of file diff --git a/src/rfc3987_syntax2/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py index 117a2ce..2e3876e 100644 --- a/src/rfc3987_syntax2/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -1,3 +1,5 @@ +from typing import Callable + from lark import Lark, ParseTree, exceptions from pathlib import Path @@ -54,7 +56,7 @@ def parse(term: str, value: str) -> ParseTree: return syntax_parser.parse(value, start=term) -def is_valid_syntax(term: str, value: str): +def is_valid_syntax(term: str, value: str) -> bool: try: parse(term=term, value=value) return True @@ -62,10 +64,10 @@ def is_valid_syntax(term: str, value: str): return False -def make_syntax_validator(rule_name): +def make_syntax_validator(rule_name: str) -> Callable[[str], bool]: parser = Lark(grammar, start=rule_name, parser=RFC3987_SYNTAX_PARSER_TYPE) - def syntax_validator(text): + def syntax_validator(text: str) -> bool: try: parser.parse(text) return True diff --git a/src/rfc3987_syntax2/utils.py b/src/rfc3987_syntax2/utils.py index a310886..af16a5d 100644 --- a/src/rfc3987_syntax2/utils.py +++ b/src/rfc3987_syntax2/utils.py @@ -1,6 +1,6 @@ from pathlib import Path -def load_grammar(path: Path): +def load_grammar(path: Path) -> str: with open(path, "r", encoding="utf-8") as file: return file.read() diff --git a/tests/__init__.py b/tests/__init__.py index d6eb5d2..4585dbd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -23,8 +23,8 @@ class SyntaxCase(TypedDict): def valid_syntax_data() -> T_syntax_file: with open(TESTS_DATA_FILES['valid_syntax'], "r", encoding="utf-8") as f: - return json.load(f) + return json.load(f) # type:ignore[no-any-return] def invalid_syntax_data() -> T_syntax_file: with open(TESTS_DATA_FILES['invalid_syntax'], "r", encoding="utf-8") as f: - return json.load(f) \ No newline at end of file + return json.load(f) # type:ignore[no-any-return] \ No newline at end of file diff --git a/tests/test_syntax.py b/tests/test_syntax.py index e50a767..4262b88 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -1,14 +1,14 @@ from typing import TYPE_CHECKING import pytest -from typing import Callable +from typing import Any, Callable import rfc3987_syntax2 as sut from . import valid_syntax_data, invalid_syntax_data, SyntaxCase, T_syntax_file -def syntax_data_as_params(src_cb: Callable[[], T_syntax_file]): +def syntax_data_as_params(src_cb: Callable[[], T_syntax_file]) -> Any: for term, examples in src_cb().items(): for example in examples: yield pytest.param(term, example, id=f"{term}-{example['value']}")