From 6d01afea45906a444db15b7035889618ccc55bf5 Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Tue, 29 Sep 2026 01:09:46 +0200 Subject: [PATCH 1/2] typing: add repoze.lru stub file and clean up cache,py --- pyproject.toml | 6 ++- reg/cache.py | 46 ++++++----------------- stubs/repoze/lru/__init__.pyi | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 36 deletions(-) create mode 100644 stubs/repoze/lru/__init__.pyi diff --git a/pyproject.toml b/pyproject.toml index 84300d3..8a83ca4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,7 +75,7 @@ show_missing = true [tool.flake8] show-source = true -ignore = ["E203", "E301", "E501", "E704", "E731", "W503"] +ignore = ["E203", "E301", "E302", "E501", "E704", "E731", "W503"] extend-select = ["TC", "TC1"] max-line-length = 88 @@ -83,11 +83,15 @@ max-line-length = 88 python_version = "3.10" strict = true files = ["."] +mypy_path = "stubs" +explicit_package_bases = true warn_unreachable = true warn_unused_ignores = true +warn_unused_configs = true [tool.pyright] typeCheckingMode = "strict" +stubPath = "stubs" [tool.tox] requires = ["tox>=4"] diff --git a/reg/cache.py b/reg/cache.py index a89c108..0a89d50 100644 --- a/reg/cache.py +++ b/reg/cache.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any, Generic -from repoze.lru import lru_cache # type: ignore[import-untyped] +from repoze.lru import lru_cache if TYPE_CHECKING: from collections.abc import Callable, Sequence @@ -49,27 +49,13 @@ class DictCachingKeyLookup(Generic[_ValueT]): def __init__(self, key_lookup: KeyLookup[_ValueT]) -> None: self.key_lookup = key_lookup - self.component = Cache(key_lookup.component).__getitem__ # type: ignore - self.fallback = Cache(key_lookup.fallback).__getitem__ # type: ignore + self.component = Cache(key_lookup.component).__getitem__ + self.fallback = Cache(key_lookup.fallback).__getitem__ def _all(key: Sequence[Any]) -> list[_ValueT]: return list(key_lookup.all(key)) - self.all = Cache(_all).__getitem__ # type: ignore - - if TYPE_CHECKING: - # NOTE: For pyright's sake we declare these callable instance attributes - # as methods, even though they're not, since pyright does not seem - # to be able to match protocols against them. mypy can deal with - # it just fine - def component(self, key: Sequence[Any], /) -> _ValueT | None: - raise NotImplementedError - - def fallback(self, key: Sequence[Any], /) -> _ValueT | None: - raise NotImplementedError - - def all(self, key: Sequence[Any], /) -> list[_ValueT]: - raise NotImplementedError + self.all = Cache(_all).__getitem__ class LruCachingKeyLookup(Generic[_ValueT]): @@ -100,20 +86,10 @@ def __init__( fallback_cache_size: int, ) -> None: self.key_lookup = key_lookup - self.component = lru_cache(component_cache_size)(key_lookup.component) # type: ignore - self.fallback = lru_cache(fallback_cache_size)(key_lookup.fallback) # type: ignore - self.all = lru_cache(all_cache_size)(lambda key: list(key_lookup.all(key))) # type: ignore - - if TYPE_CHECKING: - # NOTE: For pyright's sake we declare these callable instance attributes - # as methods, even though they're not, since pyright does not seem - # to be able to match protocols against them. mypy can deal with - # it just fine - def component(self, key: Sequence[Any], /) -> _ValueT | None: - raise NotImplementedError - - def fallback(self, key: Sequence[Any], /) -> _ValueT | None: - raise NotImplementedError - - def all(self, key: Sequence[Any], /) -> list[_ValueT]: - raise NotImplementedError + self.component = lru_cache(component_cache_size)(key_lookup.component) + self.fallback = lru_cache(fallback_cache_size)(key_lookup.fallback) + + def _all(key: Sequence[Any]) -> list[_ValueT]: + return list(key_lookup.all(key)) + + self.all = lru_cache(all_cache_size)(_all) diff --git a/stubs/repoze/lru/__init__.pyi b/stubs/repoze/lru/__init__.pyi new file mode 100644 index 0000000..df68ae1 --- /dev/null +++ b/stubs/repoze/lru/__init__.pyi @@ -0,0 +1,69 @@ +from collections.abc import Callable +from typing import Any, TypeVar, overload + +_F = TypeVar("_F", bound=Callable[..., Any]) +_T = TypeVar("_T") + +class CacheSizeMustBeGreaterThanZero(ValueError): + def __init__(self, size: int) -> None: ... + +class CacheMaxsizeRequired(ValueError): + def __init__(self) -> None: ... + +class CacheAlreadyInUse(KeyError): + def __init__(self, name: str) -> None: ... + +class Cache: + def clear(self) -> None: ... + @overload + def get(self, key: Any) -> Any | None: ... + @overload + def get(self, key: Any, default: _T) -> Any | _T: ... + def put(self, key: Any, val: Any) -> None: ... + def invalidate(self, key: Any) -> None: ... + +class UnboundedCache(Cache): + def __init__(self) -> None: ... + +class LRUCache(Cache): + size: int + evictions: int + hits: int + misses: int + lookups: int + def __init__(self, size: int) -> None: ... + +class ExpiringLRUCache(Cache): + size: int + default_timeout: float + evictions: int + hits: int + misses: int + lookups: int + def __init__(self, size: int, default_timeout: float = ...) -> None: ... + def put(self, key: Any, val: Any, timeout: float | None = None) -> None: ... + +class lru_cache: + cache: Cache + def __init__( + self, + maxsize: int | None, + cache: Cache | None = None, + timeout: float | None = None, + ignore_unhashable_args: bool = False, + ) -> None: ... + def __call__(self, func: _F) -> _F: ... + +class CacheMaker: + def __init__(self, maxsize: int | None = None, timeout: float = ...) -> None: ... + def memoized(self, name: str | None = None) -> lru_cache: ... + def lrucache( + self, name: str | None = None, maxsize: int | None = None + ) -> lru_cache: ... + def expiring_lrucache( + self, + name: str | None = None, + maxsize: int | None = None, + timeout: float | None = None, + ) -> lru_cache: ... + def clear(self, *names: str) -> None: ... From 2e32dabcf615abbd2bda6890392eab407251fb5a Mon Sep 17 00:00:00 2001 From: Henri Hulski Date: Tue, 29 Sep 2026 01:11:11 +0200 Subject: [PATCH 2/2] docs: adjust developing.rst and update Makefile --- doc/Makefile | 2 +- doc/developing.rst | 64 ++++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 01e701a..c8755bc 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -5,7 +5,7 @@ SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = -BUILDDIR = build +BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 diff --git a/doc/developing.rst b/doc/developing.rst index 2ee3558..e85787d 100644 --- a/doc/developing.rst +++ b/doc/developing.rst @@ -54,19 +54,19 @@ install the `pre-commit hook`_ for Black integration before committing:: Running the tests ----------------- -You can run the tests using `py.test`_:: +You can run the tests using `pytest`_:: - $ py.test + $ pytest To generate test coverage information as HTML do:: - $ py.test --cov --cov-report html + $ pytest --cov --cov-report html You can then point your web browser to the ``htmlcov/index.html`` file in the project directory and click on modules to see detailed coverage information. -.. _`py.test`: https://pytest.org/latest/ +.. _`pytest`: https://pytest.org Black ----- @@ -80,6 +80,30 @@ Black has also integration_ for the most popular editors. .. _`Black Code Formatter`: https://black.readthedocs.io .. _integration: https://black.readthedocs.io/en/stable/editor_integration.html +flake8 +------ + +flake8_ is a tool that can do various checks for common Python +mistakes using pyflakes_, checks for PEP8_ style compliance and +can do `cyclomatic complexity`_ checking. To do pyflakes and pep8 +checking do:: + + $ flake8 reg + +To also show cyclomatic complexity, use this command:: + + $ flake8 --max-complexity=10 reg + + +.. _flake8: https://pypi.org/project/flake8 + +.. _pyflakes: https://pypi.org/project/pyflakes + +.. _pep8: https://peps.python.org/pep-0008 + +.. _`cyclomatic complexity`: https://en.wikipedia.org/wiki/Cyclomatic_complexity + + Running the documentation tests ------------------------------- @@ -113,44 +137,22 @@ Or from the Reg project directory:: $ make -C doc html -Various checking tools ----------------------- - -flake8_ is a tool that can do various checks for common Python -mistakes using pyflakes_, check for PEP8_ style compliance and -can do `cyclomatic complexity`_ checking. To do pyflakes and pep8 -checking do:: - - $ flake8 reg - -To also show cyclomatic complexity, use this command:: - - $ flake8 --max-complexity=10 reg - -.. _flake8: https://pypi.org/project/flake8 - -.. _pyflakes: https://pypi.org/project/pyflakes - -.. _pep8: https://peps.python.org/pep-0008 - -.. _`cyclomatic complexity`: https://en.wikipedia.org/wiki/Cyclomatic_complexity - Tox --- -With tox you can test Morepath under different Python environments. +With tox you can test Reg under different Python environments. -We have Travis continuous integration installed on Morepath's github +We have gh-actions continuous integration installed on Reg's github repository and it runs the same tox tests after each checkin. First you should install all Python versions which you want to test. The versions which are not installed will be skipped. You should -at least install Python 3.7 which is required by flake8, coverage and -doctests. +at least install Python 3.14 which is required by flake8, coverage, +doctests, mypy and pyright. One tool you can use to install multiple versions of Python is pyenv_. -To find out which test environments are defined for Morepath in tox.ini run:: +To find out which test environments are defined for Reg in tox.ini run:: $ tox -l