From 905969637891f87eb84c8bee56fd1a4ab08bf910 Mon Sep 17 00:00:00 2001 From: Abraham Murciano Date: Tue, 13 Jan 2026 19:52:01 +0200 Subject: [PATCH 1/3] Add type stubs for punq punq is an IOC (Inversion of Control) Container for Python 3.8+ --- pyrightconfig.stricter.json | 1 + stubs/punq/METADATA.toml | 4 ++ stubs/punq/punq/__init__.pyi | 82 ++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 stubs/punq/METADATA.toml create mode 100644 stubs/punq/punq/__init__.pyi diff --git a/pyrightconfig.stricter.json b/pyrightconfig.stricter.json index c1513a3016ea..37f6b55294bd 100644 --- a/pyrightconfig.stricter.json +++ b/pyrightconfig.stricter.json @@ -74,6 +74,7 @@ "stubs/protobuf", "stubs/psutil/psutil/__init__.pyi", "stubs/psycopg2", + "stubs/punq", "stubs/pyasn1", "stubs/pycurl", "stubs/Pygments", diff --git a/stubs/punq/METADATA.toml b/stubs/punq/METADATA.toml new file mode 100644 index 000000000000..99e472d4bbd4 --- /dev/null +++ b/stubs/punq/METADATA.toml @@ -0,0 +1,4 @@ +version = "0.7.*" +upstream_repository = "https://github.com/bobthemighty/punq" + +[tool.stubtest] diff --git a/stubs/punq/punq/__init__.pyi b/stubs/punq/punq/__init__.pyi new file mode 100644 index 000000000000..95ed27f00219 --- /dev/null +++ b/stubs/punq/punq/__init__.pyi @@ -0,0 +1,82 @@ +from collections.abc import Callable +from enum import Enum +from typing import Any, Generic, NamedTuple, TypeVar, overload + +__version__: str + +class MissingDependencyException(Exception): ... +class MissingDependencyError(MissingDependencyException): ... +class InvalidRegistrationException(Exception): ... +class InvalidRegistrationError(InvalidRegistrationException): ... +class InvalidForwardReferenceException(Exception): ... +class InvalidForwardReferenceError(InvalidForwardReferenceException): ... + +class Scope(Enum): + transient = 0 + singleton = 1 + +_T = TypeVar("_T") +_TOpt = TypeVar("_TOpt", default=object) + +class _Registration(NamedTuple, Generic[_T]): + service: type[_T] | str + scope: Scope + builder: Callable[[], _T] + needs: dict[str, object] + args: dict[str, object] + +empty: Any + +class _Registry: + def register_service_and_impl( + self, service: type | str, scope: Scope, impl: Callable[..., object], resolve_args: dict[str, object] + ) -> None: ... + def register_service_and_instance(self, service: type[_T] | str, instance: _T) -> None: ... + def register_concrete_service(self, service: type | str, scope: Scope) -> None: ... + def build_context(self, key: type | str, existing: _ResolutionContext | None = None) -> _ResolutionContext: ... + def register( + self, service: type[_T] | str, factory: Callable[..., _T] = ..., instance: _T = ..., scope: Scope = ..., **kwargs: object + ) -> None: ... + def __getitem__(self, service: type[_T] | str) -> list[_Registration[_T]]: ... + +class _ResolutionTarget(Generic[_T]): + service: type[_T] | str + impls: list[_Registration[_T]] + def __init__(self, key: type[_T] | str, impls: list[_Registration[_T]]) -> None: ... + def is_generic_list(self) -> bool: ... + @property + def generic_parameter(self) -> Any: ... + def next_impl(self) -> _Registration[_T]: ... + +class _ResolutionContext: + targets: dict[type | str, _ResolutionTarget[object]] + cache: dict[type | str, object] + service: type | str + def __init__(self, key: type | str, impls: list[_Registration[object]]) -> None: ... + def target(self, key: type[_T] | str) -> _ResolutionTarget[_T]: ... + def has_cached(self, key: type | str) -> bool: ... + def __getitem__(self, key: type | str) -> object: ... + def __setitem__(self, key: type | str, value: object) -> None: ... + def all_registrations(self, service: type[_T] | str) -> list[_Registration[_T]]: ... + +class Container: + registrations: _Registry + def __init__(self) -> None: ... + @overload + def register(self, service: type[_TOpt] | str, *, instance: _TOpt, **kwargs: Any) -> Container: ... + @overload + def register( + self, service: type[_TOpt] | str, factory: Callable[..., _TOpt] = ..., *, scope: Scope = ..., **kwargs: Any + ) -> Container: ... + @overload + def register( + self, + service: type[_TOpt] | str, + factory: Callable[..., _TOpt] = ..., + instance: _TOpt = ..., + scope: Scope = Scope.transient, + **kwargs: Any, + ): ... + def resolve_all(self, service: type[_TOpt] | str, **kwargs: Any) -> list[_TOpt]: ... + def resolve(self, service_key: type[_TOpt] | str, **kwargs: Any) -> _TOpt: ... + def instantiate(self, service_key: type[_TOpt] | str, **kwargs: Any) -> _TOpt: ... From d5e71abcfaa7d7d9c64927d63559a3341f311fff Mon Sep 17 00:00:00 2001 From: Abraham Murciano Date: Thu, 15 Jan 2026 14:22:32 +0200 Subject: [PATCH 2/3] Improve type hints in punq stubs - Replace object with Any where appropriate - Clarify the use of **kwargs in method signatures - Make _T default to Any and use it in more places instead of Any/object - Include _Empty as a default parameter where applicable --- stubs/punq/@test/stubtest_allowlist.txt | 1 + stubs/punq/punq/__init__.pyi | 61 +++++++++++++++---------- 2 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 stubs/punq/@test/stubtest_allowlist.txt diff --git a/stubs/punq/@test/stubtest_allowlist.txt b/stubs/punq/@test/stubtest_allowlist.txt new file mode 100644 index 000000000000..b4bd7cb73492 --- /dev/null +++ b/stubs/punq/@test/stubtest_allowlist.txt @@ -0,0 +1 @@ + punq._Empty.__init__ \ No newline at end of file diff --git a/stubs/punq/punq/__init__.pyi b/stubs/punq/punq/__init__.pyi index 95ed27f00219..e96d53c0bb50 100644 --- a/stubs/punq/punq/__init__.pyi +++ b/stubs/punq/punq/__init__.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable from enum import Enum -from typing import Any, Generic, NamedTuple, TypeVar, overload +from typing import Any, Final, Generic, NamedTuple, NewType, TypeVar, overload __version__: str @@ -15,27 +15,36 @@ class Scope(Enum): transient = 0 singleton = 1 -_T = TypeVar("_T") -_TOpt = TypeVar("_TOpt", default=object) +_T = TypeVar("_T", default=Any) class _Registration(NamedTuple, Generic[_T]): service: type[_T] | str scope: Scope - builder: Callable[[], _T] - needs: dict[str, object] - args: dict[str, object] + builder: Callable[..., _T] + needs: dict[str, Any] # the type hints of the builder's parameters + args: dict[str, Any] # passed to builder at instantiation time -empty: Any +_Empty = NewType("_Empty", object) # a class at runtime +empty: Final[_Empty] class _Registry: def register_service_and_impl( - self, service: type | str, scope: Scope, impl: Callable[..., object], resolve_args: dict[str, object] + self, + service: type[_T] | str, + scope: Scope, + impl: type[_T], + resolve_args: dict[str, Any], # forwarded to _Registration.builder ) -> None: ... def register_service_and_instance(self, service: type[_T] | str, instance: _T) -> None: ... def register_concrete_service(self, service: type | str, scope: Scope) -> None: ... def build_context(self, key: type | str, existing: _ResolutionContext | None = None) -> _ResolutionContext: ... def register( - self, service: type[_T] | str, factory: Callable[..., _T] = ..., instance: _T = ..., scope: Scope = ..., **kwargs: object + self, + service: type[_T] | str, + factory: Callable[..., _T] | _Empty = ..., + instance: _T | _Empty = ..., + scope: Scope = Scope.transient, + **kwargs: Any, # forwarded to _Registration.builder ) -> None: ... def __getitem__(self, service: type[_T] | str) -> list[_Registration[_T]]: ... @@ -45,38 +54,44 @@ class _ResolutionTarget(Generic[_T]): def __init__(self, key: type[_T] | str, impls: list[_Registration[_T]]) -> None: ... def is_generic_list(self) -> bool: ... @property - def generic_parameter(self) -> Any: ... + def generic_parameter(self) -> Any: ... # returns the first annotated generic parameter of the service def next_impl(self) -> _Registration[_T]: ... class _ResolutionContext: - targets: dict[type | str, _ResolutionTarget[object]] - cache: dict[type | str, object] + targets: dict[type | str, _ResolutionTarget[Any]] + cache: dict[type | str, Any] # resolved objects during this resolution service: type | str - def __init__(self, key: type | str, impls: list[_Registration[object]]) -> None: ... + def __init__(self, key: type | str, impls: list[_Registration[Any]]) -> None: ... def target(self, key: type[_T] | str) -> _ResolutionTarget[_T]: ... def has_cached(self, key: type | str) -> bool: ... - def __getitem__(self, key: type | str) -> object: ... - def __setitem__(self, key: type | str, value: object) -> None: ... + def __getitem__(self, key: type[_T] | str) -> _T: ... + def __setitem__(self, key: type[_T] | str, value: _T) -> None: ... def all_registrations(self, service: type[_T] | str) -> list[_Registration[_T]]: ... class Container: registrations: _Registry def __init__(self) -> None: ... + # all kwargs are forwarded to _Registration.builder @overload - def register(self, service: type[_TOpt] | str, *, instance: _TOpt, **kwargs: Any) -> Container: ... + def register(self, service: type[_T] | str, *, instance: _T, **kwargs: Any) -> Container: ... @overload def register( - self, service: type[_TOpt] | str, factory: Callable[..., _TOpt] = ..., *, scope: Scope = ..., **kwargs: Any + self, + service: type[_T] | str, + factory: Callable[..., _T] | _Empty = ..., + *, + scope: Scope = Scope.transient, + **kwargs: Any, ) -> Container: ... @overload def register( self, - service: type[_TOpt] | str, - factory: Callable[..., _TOpt] = ..., - instance: _TOpt = ..., + service: type[_T] | str, + factory: Callable[..., _T] | _Empty = ..., + instance: _T | _Empty = ..., scope: Scope = Scope.transient, **kwargs: Any, ): ... - def resolve_all(self, service: type[_TOpt] | str, **kwargs: Any) -> list[_TOpt]: ... - def resolve(self, service_key: type[_TOpt] | str, **kwargs: Any) -> _TOpt: ... - def instantiate(self, service_key: type[_TOpt] | str, **kwargs: Any) -> _TOpt: ... + def resolve_all(self, service: type[_T] | str, **kwargs: Any) -> list[_T]: ... + def resolve(self, service_key: type[_T] | str, **kwargs: Any) -> _T: ... + def instantiate(self, service_key: type[_T] | str, **kwargs: Any) -> _T: ... From 7c3742183dc309dda0f7eccbe53246f40bfbd06f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 12:46:29 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stubs/punq/@test/stubtest_allowlist.txt | 1 - stubs/punq/@tests/stubtest_allowlist.txt | 1 + stubs/punq/punq/__init__.pyi | 7 +------ 3 files changed, 2 insertions(+), 7 deletions(-) delete mode 100644 stubs/punq/@test/stubtest_allowlist.txt create mode 100644 stubs/punq/@tests/stubtest_allowlist.txt diff --git a/stubs/punq/@test/stubtest_allowlist.txt b/stubs/punq/@test/stubtest_allowlist.txt deleted file mode 100644 index b4bd7cb73492..000000000000 --- a/stubs/punq/@test/stubtest_allowlist.txt +++ /dev/null @@ -1 +0,0 @@ - punq._Empty.__init__ \ No newline at end of file diff --git a/stubs/punq/@tests/stubtest_allowlist.txt b/stubs/punq/@tests/stubtest_allowlist.txt new file mode 100644 index 000000000000..e2c11ed54e4d --- /dev/null +++ b/stubs/punq/@tests/stubtest_allowlist.txt @@ -0,0 +1 @@ + punq._Empty.__init__ diff --git a/stubs/punq/punq/__init__.pyi b/stubs/punq/punq/__init__.pyi index e96d53c0bb50..196d64445f06 100644 --- a/stubs/punq/punq/__init__.pyi +++ b/stubs/punq/punq/__init__.pyi @@ -76,12 +76,7 @@ class Container: def register(self, service: type[_T] | str, *, instance: _T, **kwargs: Any) -> Container: ... @overload def register( - self, - service: type[_T] | str, - factory: Callable[..., _T] | _Empty = ..., - *, - scope: Scope = Scope.transient, - **kwargs: Any, + self, service: type[_T] | str, factory: Callable[..., _T] | _Empty = ..., *, scope: Scope = Scope.transient, **kwargs: Any ) -> Container: ... @overload def register(