diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a45d1810..5f302995 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,11 +20,11 @@ jobs: outputs: noxenvs: ${{ steps.noxenvs-matrix.outputs.noxenvs }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false - name: Set up uv - uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba + uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 with: enable-cache: ${{ github.ref_type != 'tag' }} # zizmor: ignore[cache-poisoning] - id: noxenvs-matrix @@ -75,7 +75,7 @@ jobs: noxenv: "docs(style)" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: persist-credentials: false - name: Install dependencies @@ -100,7 +100,7 @@ jobs: if: runner.os == 'Windows' && startsWith(matrix.noxenv, 'tests') - name: Set up uv - uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba + uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 with: enable-cache: true @@ -119,12 +119,12 @@ jobs: id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 persist-credentials: false - name: Set up uv - uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba + uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 with: enable-cache: true diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 17f2bd11..4bebbe96 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -15,12 +15,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: persist-credentials: false - name: Install uv - uses: astral-sh/setup-uv@bd01e18f51369d5a26f1651c3cb451d3417e3bba + uses: astral-sh/setup-uv@d9e0f98d3fc6adb07d1e3d37f3043649ddad06a1 - name: Run zizmor 🌈 run: uvx zizmor --format=sarif .github > results.sarif diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77b36341..dbfdf001 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ exclude: json/ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-ast - id: check-json @@ -16,7 +16,7 @@ repos: args: [--fix, lf] - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.12.3" + rev: "v0.12.8" hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0d59e4e9..836fdcdc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,8 @@ +v4.25.1 +======= + +* Fix an incorrect required argument in the ``Validator`` protocol's type annotations (#1396). + v4.25.0 ======= diff --git a/jsonschema/protocols.py b/jsonschema/protocols.py index 0fd993ee..b6288dcc 100644 --- a/jsonschema/protocols.py +++ b/jsonschema/protocols.py @@ -108,10 +108,11 @@ class Validator(Protocol): def __init__( self, schema: Mapping | bool, - registry: referencing.jsonschema.SchemaRegistry, + resolver: Any = None, # deprecated format_checker: jsonschema.FormatChecker | None = None, - ) -> None: - ... + *, + registry: referencing.jsonschema.SchemaRegistry = ..., + ) -> None: ... @classmethod def check_schema(cls, schema: Mapping | bool) -> None: diff --git a/jsonschema/tests/typing/__init__.py b/jsonschema/tests/typing/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py b/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py new file mode 100644 index 00000000..63e8bd40 --- /dev/null +++ b/jsonschema/tests/typing/test_all_concrete_validators_match_protocol.py @@ -0,0 +1,38 @@ +""" +This module acts as a test that type checkers will allow each validator +class to be assigned to a variable of type `type[Validator]` + +The assignation is only valid if type checkers recognize each Validator +implementation as a valid implementer of the protocol. +""" +from jsonschema.protocols import Validator +from jsonschema.validators import ( + Draft3Validator, + Draft4Validator, + Draft6Validator, + Draft7Validator, + Draft201909Validator, + Draft202012Validator, +) + +my_validator: type[Validator] + +my_validator = Draft3Validator +my_validator = Draft4Validator +my_validator = Draft6Validator +my_validator = Draft7Validator +my_validator = Draft201909Validator +my_validator = Draft202012Validator + + +# in order to confirm that none of the above were incorrectly typed as 'Any' +# ensure that each of these assignments to a non-validator variable requires an +# ignore +none_var: None + +none_var = Draft3Validator # type: ignore[assignment] +none_var = Draft4Validator # type: ignore[assignment] +none_var = Draft6Validator # type: ignore[assignment] +none_var = Draft7Validator # type: ignore[assignment] +none_var = Draft201909Validator # type: ignore[assignment] +none_var = Draft202012Validator # type: ignore[assignment] diff --git a/jsonschema/validators.py b/jsonschema/validators.py index b8ca3bd4..dbc029fc 100644 --- a/jsonschema/validators.py +++ b/jsonschema/validators.py @@ -147,7 +147,7 @@ def create( applicable_validators: _typing.ApplicableValidators = methodcaller( "items", ), -): +) -> type[Validator]: """ Create a new validator class. @@ -511,7 +511,7 @@ def is_valid(self, instance, _schema=None): Validator.__name__ = Validator.__qualname__ = f"{safe}Validator" Validator = validates(version)(Validator) # type: ignore[misc] - return Validator + return Validator # type: ignore[return-value] def extend( diff --git a/noxfile.py b/noxfile.py index 3a306c38..05a23845 100644 --- a/noxfile.py +++ b/noxfile.py @@ -6,6 +6,7 @@ ROOT = Path(__file__).parent PACKAGE = ROOT / "jsonschema" +TYPING_TESTS= ROOT / "jsonschema" / "tests" / "typing" BENCHMARKS = PACKAGE / "benchmarks" PYPROJECT = ROOT / "pyproject.toml" CHANGELOG = ROOT / "CHANGELOG.rst" @@ -128,6 +129,7 @@ def license_check(session): "jsonschema", "jsonschema-specifications", "referencing", + "rpds-py", "types-python-dateutil", "--allow-only", @@ -180,6 +182,9 @@ def typing(session): """ session.install("mypy", "types-requests", ROOT) session.run("mypy", "--config", PYPROJECT, PACKAGE) + session.run( + "mypy", "--config", PYPROJECT, "--warn-unused-ignores", TYPING_TESTS, + ) @session(tags=["docs"])