From e4a4ddbc7bbac2a74a083dd0bab51129fbc59dff Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 21:31:43 +0000 Subject: [PATCH 1/2] Add type hints via a jsonpointer-stubs package jsonpointer.py stays a single-file module. Type information is shipped as a PEP 561 stub-only package (jsonpointer-stubs) inside the same wheel and sdist. A CI job runs mypy's stubtest to keep the stubs in sync with the implementation. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PewzpkKQeJ6P2vfXcywcMy --- .github/workflows/test.yaml | 21 ++++++++++++ MANIFEST.in | 1 + jsonpointer-stubs/__init__.pyi | 63 ++++++++++++++++++++++++++++++++++ setup.py | 4 +++ 4 files changed, 89 insertions(+) create mode 100644 jsonpointer-stubs/__init__.pyi diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 50df175..6fedd8f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -54,6 +54,27 @@ jobs: - name: Check distributions run: twine check --strict dist/* + typing: + name: "Check type stubs" + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install mypy . + - name: Run stubtest + # run outside the checkout so the installed module and stubs are used + working-directory: ${{ runner.temp }} + run: python -m mypy.stubtest jsonpointer + docs: name: "Build documentation" runs-on: ubuntu-latest diff --git a/MANIFEST.in b/MANIFEST.in index 0f9b9f6..0906a30 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,4 @@ include AUTHORS include LICENSE.txt include README.md include tests.py +recursive-include jsonpointer-stubs *.pyi diff --git a/jsonpointer-stubs/__init__.pyi b/jsonpointer-stubs/__init__.pyi new file mode 100644 index 0000000..a011a88 --- /dev/null +++ b/jsonpointer-stubs/__init__.pyi @@ -0,0 +1,63 @@ +from collections.abc import Iterable, Iterator +from re import Pattern +from typing import Any, Final, TypeVar + +from typing_extensions import Self + +_T = TypeVar("_T") + +__author__: Final[str] +__version__: Final[str] +__website__: Final[str] +__license__: Final[str] + +VERBOSE_EXCEPTIONS: bool + +def set_pointer( + doc: Any, + pointer: str, + value: Any, + inplace: bool = True, + create: bool = False, +) -> Any: ... +def resolve_pointer(doc: Any, pointer: str, default: Any = ...) -> Any: ... +def pairwise(iterable: Iterable[_T]) -> Iterator[tuple[_T, _T]]: ... + +class JsonPointerException(Exception): ... + +class EndOfList: + list_: Any + def __init__(self, list_: Any) -> None: ... + +class JsonPointer: + _RE_ARRAY_INDEX: Pattern[str] + _RE_INVALID_ESCAPE: Pattern[str] + parts: list[str] + def __init__(self, pointer: str) -> None: ... + def to_last(self, doc: Any) -> tuple[Any, str | int | None]: ... + def resolve(self, doc: Any, default: Any = ...) -> Any: ... + def get(self, doc: Any, default: Any = ...) -> Any: ... + def set( + self, + doc: Any, + value: Any, + inplace: bool = True, + create: bool = False, + ) -> Any: ... + @classmethod + def get_part(cls, doc: Any, part: str | int) -> str | int: ... + def get_parts(self) -> list[str]: ... + def walk(self, doc: Any, part: str | int) -> Any: ... + def contains(self, ptr: JsonPointer) -> bool: ... + def __contains__(self, item: JsonPointer) -> bool: ... + def join(self, suffix: JsonPointer | str | Iterable[object]) -> Self: ... + def __truediv__(self, suffix: JsonPointer | str | Iterable[object]) -> Self: ... + @property + def path(self) -> str: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... + @classmethod + def from_parts(cls, parts: Iterable[object]) -> Self: ... + +def escape(s: str) -> str: ... +def unescape(s: str) -> str: ... diff --git a/setup.py b/setup.py index 26f34ee..b13b103 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities', + 'Typing :: Stubs Only', ] setup( @@ -60,6 +61,9 @@ license=LICENSE, url=WEBSITE, py_modules=MODULES, + # PEP 561 stub-only package, shipped in the same distribution + packages=['jsonpointer-stubs'], + package_data={'jsonpointer-stubs': ['__init__.pyi']}, scripts=['bin/jsonpointer'], classifiers=CLASSIFIERS, python_requires='>=3.10', From ab18a64827c2fb0ce7f03dac6d1cadb112825add Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 21:36:06 +0000 Subject: [PATCH 2/2] Use Typing :: Typed classifier The distribution ships the runtime module alongside the stubs, so 'Stubs Only' misdescribes it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01PewzpkKQeJ6P2vfXcywcMy --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b13b103..785d397 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Libraries', 'Topic :: Utilities', - 'Typing :: Stubs Only', + 'Typing :: Typed', ] setup(