diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51aea73..9bca556 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,8 @@ name: CI on: push: + tags: + - "v*" pull_request: release: types: [published] @@ -14,15 +16,6 @@ env: PIP_NO_PYTHON_VERSION_WARNING: "1" jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - uses: pre-commit/action@v3.0.0 - list: runs-on: ubuntu-latest outputs: @@ -30,7 +23,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up nox - uses: wntrblm/nox@2023.04.22 + uses: wntrblm/nox@2024.03.02 - id: noxenvs-matrix run: | echo >>$GITHUB_OUTPUT noxenvs=$( @@ -70,8 +63,9 @@ jobs: 3.12 pypy3.10 allow-prereleases: true + cache: pip - name: Set up nox - uses: wntrblm/nox@2023.04.22 + uses: wntrblm/nox@2024.03.02 - name: Run nox run: nox -s "${{ matrix.noxenv }}" -- ${{ matrix.posargs }} @@ -92,6 +86,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: + cache: pip python-version: "3.x" - name: Install dependencies run: python -m pip install build @@ -102,7 +97,7 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 - name: Create a Release if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: | dist/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 80c0c3e..07eac3c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,16 +13,12 @@ repos: args: [--fix, lf] - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.1.14" + rev: "v0.3.3" hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - repo: https://github.com/psf/black - rev: 23.12.1 + rev: 24.3.0 hooks: - name: black id: black diff --git a/docs/changes.rst b/docs/changes.rst index 6a43ab8..9575f40 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -2,6 +2,12 @@ Changelog ========= +v0.34.0 +------- + +* Also look inside ``definitions`` keywords even on newer dialects. + The specification recommends doing so regardless of the rename to ``$defs``. + v0.33.0 ------- diff --git a/docs/requirements.txt b/docs/requirements.txt index 8ad07f9..b72dedc 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -12,13 +12,13 @@ babel==2.14.0 # via sphinx beautifulsoup4==4.12.3 # via furo -certifi==2023.11.17 +certifi==2024.2.2 # via requests charset-normalizer==3.3.2 # via requests docutils==0.20.1 # via sphinx -furo==2023.9.10 +furo==2024.1.29 # via -r docs/requirements.in idna==3.6 # via requests @@ -28,7 +28,7 @@ jinja2==3.1.3 # via sphinx lxml==5.1.0 # via sphinx-json-schema-spec -markupsafe==2.1.4 +markupsafe==2.1.5 # via jinja2 packaging==23.2 # via sphinx @@ -84,5 +84,5 @@ sphinxext-opengraph==0.9.1 # via -r docs/requirements.in url-py==0.10.0 # via -r docs/requirements.in -urllib3==2.1.0 +urllib3==2.2.0 # via requests diff --git a/noxfile.py b/noxfile.py index 2434617..52b949c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -17,8 +17,8 @@ path.parent / f"{path.stem}.in" for path in REQUIREMENTS.values() ] -SUPPORTED = ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.10"] -LATEST = "3.12" +SUPPORTED = ["3.8", "3.9", "3.10", "pypy3.10", "3.11", "3.12"] +LATEST = SUPPORTED[-1] nox.options.sessions = [] @@ -89,7 +89,7 @@ def style(session): Check Python code style. """ session.install("ruff") - session.run("ruff", "check", ROOT) + session.run("ruff", "check", ROOT, __file__) @session() @@ -97,8 +97,17 @@ def typing(session): """ Check static typing. """ - session.install("pyright", ROOT) - session.run("pyright", REFERENCING) + session.install("pyright!=1.1.354", ROOT) + session.run("pyright", *session.posargs, REFERENCING) + + +@session() +def mypy(session): + """ + Check that mypy runs with no blocking errors. + """ + session.install("mypy", ROOT) + session.run("mypy", REFERENCING) @session(tags=["docs"]) @@ -154,7 +163,9 @@ def docs_style(session): @session(default=False) def requirements(session): """ - Update the project's pinned requirements. Commit the result. + Update the project's pinned requirements. + + You should commit the result afterwards. """ session.install("pip-tools") for each in REQUIREMENTS_IN: diff --git a/pyproject.toml b/pyproject.toml index 32b99be..1557af1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,11 +10,9 @@ name = "referencing" description = "JSON Referencing + Python" requires-python = ">=3.8" readme = "README.rst" -license = {text = "MIT"} keywords = ["json", "referencing", "jsonschema", "openapi", "asyncapi"] authors = [ - {email = "Julian+referencing@GrayVines.com"}, - {name = "Julian Berman"}, + { name = "Julian Berman", email = "Julian+referencing@GrayVines.com" }, ] classifiers = [ "Development Status :: 3 - Alpha", @@ -44,6 +42,7 @@ Homepage = "https://github.com/python-jsonschema/referencing" Issues = "https://github.com/python-jsonschema/referencing/issues/" Funding = "https://github.com/sponsors/Julian" Tidelift = "https://tidelift.com/subscription/pkg/pypi-referencing?utm_source=pypi-referencing&utm_medium=referral&utm_campaign=pypi-link" +Changelog = "https://referencing.readthedocs.io/en/stable/changes/" Source = "https://github.com/python-jsonschema/referencing" [tool.coverage.html] @@ -78,6 +77,10 @@ include_trailing_comma = true multi_line_output = 3 use_parentheses = true +[tool.mypy] +follow_imports = "skip" +ignore_errors = true + [tool.pyright] reportUnnecessaryTypeIgnoreComment = true strict = ["**/*"] @@ -88,6 +91,9 @@ exclude = [ [tool.ruff] line-length = 79 +extend-exclude = ["suite"] + +[tool.ruff.lint] select = ["ALL"] ignore = [ "A001", # It's fine to shadow builtins @@ -95,6 +101,7 @@ ignore = [ "A003", "ARG", # This is all wrong whenever an interface is involved "ANN", # Just let the type checker do this + "B006", # Mutable arguments require care but are OK if you don't abuse them "B008", # It's totally OK to call functions for default arguments. "B904", # raise SomeException(...) is fine. "B905", # No need for explicit strict, this is simply zip's default behavior @@ -114,7 +121,6 @@ ignore = [ "EM102", "FBT", # It's worth avoiding boolean args but I don't care to enforce it "FIX", # Yes thanks, if I could it wouldn't be there - "I001", # We can't yet use ruff's isort "N", # These naming rules are silly "PLR0912", # These metrics are fine to be aware of but not to enforce "PLR0913", @@ -132,19 +138,18 @@ ignore = [ "TD", # These TODO style rules are also silly "UP007", # We support 3.8 + 3.9 ] -extend-exclude = ["suite"] [tool.ruff.lint.flake8-pytest-style] mark-parentheses = false -[tool.ruff.flake8-quotes] +[tool.ruff.lint.flake8-quotes] docstring-quotes = "double" [tool.ruff.lint.isort] combine-as-imports = true from-first = true -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "noxfile.py" = ["ANN", "D100", "S101", "T201"] "docs/*" = ["ANN", "D", "INP001"] "referencing/tests/*" = ["ANN", "D", "RUF012", "S", "PLR", "TRY"] diff --git a/referencing/__init__.py b/referencing/__init__.py index c4476d4..e09207d 100644 --- a/referencing/__init__.py +++ b/referencing/__init__.py @@ -1,6 +1,7 @@ """ Cross-specification, implementation-agnostic JSON referencing. """ + from referencing._core import Anchor, Registry, Resource, Specification __all__ = ["Anchor", "Registry", "Resource", "Specification"] diff --git a/referencing/_core.py b/referencing/_core.py index 9443715..e6da867 100644 --- a/referencing/_core.py +++ b/referencing/_core.py @@ -33,8 +33,7 @@ def __call__( segments: Sequence[int | str], resolver: Resolver[D], subresource: Resource[D], - ) -> Resolver[D]: - ... + ) -> Resolver[D]: ... def _detect_or_error(contents: D) -> Specification[D]: @@ -192,8 +191,9 @@ class Resource(Generic[D]): def from_contents( cls, contents: D, - default_specification: type[Specification[D]] - | Specification[D] = Specification, + default_specification: ( + type[Specification[D]] | Specification[D] + ) = Specification, ) -> Resource[D]: """ Create a resource guessing which specification applies to the contents. @@ -205,6 +205,7 @@ def from_contents( if the given contents don't have any discernible information which could be used to guess which specification they identify as + """ specification = default_specification.detect(contents) return specification.create_resource(contents=contents) @@ -254,6 +255,7 @@ def pointer(self, pointer: str, resolver: Resolver[D]) -> Resolved[D]: `exceptions.PointerToNowhere` if the pointer points to a location not present in the document + """ contents = self.contents segments: list[int | str] = [] @@ -369,6 +371,7 @@ def __rmatmul__( `NoInternalID` if the resource(s) in fact do not have IDs + """ if isinstance(new, Resource): new = (new,) @@ -549,8 +552,8 @@ def combine(self, *registries: Registry[D]) -> Registry[D]: uncrawled = self._uncrawled retrieve = self._retrieve for registry in registries: - resources = resources.update(registry._resources) # type: ignore[reportUnknownMemberType] - anchors = anchors.update(registry._anchors) # type: ignore[reportUnknownMemberType] + resources = resources.update(registry._resources) + anchors = anchors.update(registry._anchors) uncrawled = uncrawled.update(registry._uncrawled) if registry._retrieve is not _fail_to_retrieve: @@ -658,6 +661,7 @@ def lookup(self, ref: URI) -> Resolved[D]: if the reference is to a URI where a resource exists but contains a JSON pointer to a location within the resource that does not exist + """ if ref.startswith("#"): uri, fragment = self._base_uri, ref[1:] diff --git a/referencing/exceptions.py b/referencing/exceptions.py index a893d45..3267fc7 100644 --- a/referencing/exceptions.py +++ b/referencing/exceptions.py @@ -1,6 +1,7 @@ """ Errors, oh no! """ + from __future__ import annotations from typing import TYPE_CHECKING, Any diff --git a/referencing/jsonschema.py b/referencing/jsonschema.py index 564f4d2..fb8668a 100644 --- a/referencing/jsonschema.py +++ b/referencing/jsonschema.py @@ -9,9 +9,12 @@ from referencing import Anchor, Registry, Resource, Specification, exceptions from referencing._attrs import frozen -from referencing._core import _UNSET # type: ignore[reportPrivateUsage] -from referencing._core import _Unset # type: ignore[reportPrivateUsage] -from referencing._core import Resolved as _Resolved, Resolver as _Resolver +from referencing._core import ( + _UNSET, # type: ignore[reportPrivateUsage] + Resolved as _Resolved, + Resolver as _Resolver, + _Unset, # type: ignore[reportPrivateUsage] +) from referencing.typing import URI, Anchor as AnchorType, Mapping #: A JSON Schema which is a JSON object @@ -374,6 +377,7 @@ def maybe_in_subresource( in_subarray={"allOf", "anyOf", "oneOf", "prefixItems"}, in_subvalues={ "$defs", + "definitions", "dependentSchemas", "patternProperties", "properties", @@ -397,6 +401,7 @@ def maybe_in_subresource( in_subarray={"allOf", "anyOf", "oneOf", "prefixItems"}, in_subvalues={ "$defs", + "definitions", "dependentSchemas", "patternProperties", "properties", @@ -424,12 +429,13 @@ def maybe_in_subresource( in_subarray={"allOf", "anyOf", "oneOf"}, in_subvalues={ "$defs", + "definitions", "dependentSchemas", "patternProperties", "properties", }, ), - anchors_in=_anchor_2019, # type: ignore[reportGeneralTypeIssues] TODO: check whether this is real + anchors_in=_anchor_2019, # type: ignore[reportGeneralTypeIssues] # TODO: check whether this is real maybe_in_subresource=_maybe_in_subresource_crazy_items( in_value={ "additionalItems", @@ -447,6 +453,7 @@ def maybe_in_subresource( in_subarray={"allOf", "anyOf", "oneOf"}, in_subvalues={ "$defs", + "definitions", "dependentSchemas", "patternProperties", "properties", @@ -471,7 +478,7 @@ def maybe_in_subresource( in_subarray={"allOf", "anyOf", "oneOf"}, in_subvalues={"definitions", "patternProperties", "properties"}, ), - anchors_in=_legacy_anchor_in_dollar_id, # type: ignore[reportGeneralTypeIssues] TODO: check whether this is real + anchors_in=_legacy_anchor_in_dollar_id, # type: ignore[reportGeneralTypeIssues] # TODO: check whether this is real maybe_in_subresource=_maybe_in_subresource_crazy_items_dependencies( in_value={ "additionalItems", @@ -502,7 +509,7 @@ def maybe_in_subresource( in_subarray={"allOf", "anyOf", "oneOf"}, in_subvalues={"definitions", "patternProperties", "properties"}, ), - anchors_in=_legacy_anchor_in_dollar_id, # type: ignore[reportGeneralTypeIssues] TODO: check whether this is real + anchors_in=_legacy_anchor_in_dollar_id, # type: ignore[reportGeneralTypeIssues] # TODO: check whether this is real maybe_in_subresource=_maybe_in_subresource_crazy_items_dependencies( in_value={ "additionalItems", @@ -575,6 +582,7 @@ def specification_with( `UnknownDialect` if the given ``dialect_id`` isn't known + """ resource = _SPECIFICATIONS.get(dialect_id.rstrip("#")) if resource is not None: diff --git a/referencing/retrieval.py b/referencing/retrieval.py index 589f1f8..e3719cf 100644 --- a/referencing/retrieval.py +++ b/referencing/retrieval.py @@ -1,6 +1,7 @@ """ Helpers related to (dynamic) resource retrieval. """ + from __future__ import annotations from functools import lru_cache diff --git a/referencing/tests/test_referencing_suite.py b/referencing/tests/test_referencing_suite.py index a3d0783..4b8ae91 100644 --- a/referencing/tests/test_referencing_suite.py +++ b/referencing/tests/test_referencing_suite.py @@ -46,6 +46,9 @@ def test_referencing_suite(test_path, subtests): ) for test in loaded["tests"]: with subtests.test(test=test): + if "normalization" in test_path.stem: + pytest.xfail("APIs need to change for proper URL support.") + resolver = registry.resolver(base_uri=test.get("base_uri", "")) if test.get("error"): diff --git a/referencing/typing.py b/referencing/typing.py index ad807a3..d00b018 100644 --- a/referencing/typing.py +++ b/referencing/typing.py @@ -1,6 +1,7 @@ """ Type-annotation related support for the referencing library. """ + from __future__ import annotations from typing import TYPE_CHECKING, Protocol, TypeVar diff --git a/suite b/suite index 9153b05..e3fe0aa 160000 --- a/suite +++ b/suite @@ -1 +1 @@ -Subproject commit 9153b054a9e936587eb514e4466c1886645278c4 +Subproject commit e3fe0aa59667bcc34b3c1be42aaed7ee685df846