diff --git a/pyproject.toml b/pyproject.toml index cf8f1cd..c3f2cba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ classifiers = [ "Intended Audience :: Developers", ] name = "type-lens" -version = "0.2.3" +version = "0.2.4" description = "type-lens is a Python template project designed to simplify the setup of a new project." readme = "README.md" license = { text = "MIT" } @@ -127,6 +127,7 @@ warn_unused_configs = true warn_unused_ignores = true [tool.pyright] +typeCheckingMode = "strict" disableBytesTypePromotions = true exclude = [ "tools", diff --git a/tests/test_callable_view.py b/tests/test_callable_view.py index 0b67ba9..92425c0 100644 --- a/tests/test_callable_view.py +++ b/tests/test_callable_view.py @@ -44,9 +44,9 @@ def fn() -> int: def test_untyped_param() -> None: def fn(foo): # type: ignore[no-untyped-def] - return foo + return foo # pyright: ignore - function_view = CallableView.from_callable(fn) + function_view = CallableView.from_callable(fn) # pyright: ignore assert function_view.parameters == (ParameterView("foo", TypeView(Any), has_annotation=False),) @@ -127,7 +127,7 @@ def method(self, c: bool) -> bool: def test_parameters_with_none_default(hint: Any) -> None: def fn(plain: hint = None, annotated: Annotated[hint, ...] = None) -> None: ... # pyright: ignore - fn_view = CallableView.from_callable(fn, localns=locals(), include_extras=True) + fn_view = CallableView.from_callable(fn, localns=locals(), include_extras=True) # pyright: ignore plain_param, annotated_param = fn_view.parameters assert plain_param.type_view.annotation == annotated_param.type_view.annotation diff --git a/tests/test_type_view.py b/tests/test_type_view.py index b61b675..7101b42 100644 --- a/tests/test_type_view.py +++ b/tests/test_type_view.py @@ -31,7 +31,7 @@ T = TypeVar("T") -def _check_parsed_type(type_lens: TypeView, expected: dict[str, Any]) -> None: +def _check_parsed_type(type_lens: TypeView[Any], expected: dict[str, Any]) -> None: __tracebackhide__ = True for key, expected_value in expected.items(): lens_value = getattr(type_lens, key) @@ -334,8 +334,8 @@ def test_tuple() -> None: assert TypeView(Tuple[int, ...]).is_tuple is True assert TypeView(Tuple[int, ...]).is_variadic_tuple is True - assert TypeView(...).is_tuple is False - assert TypeView(...).is_variadic_tuple is False + assert TypeView(...).is_tuple is False # pyright: ignore + assert TypeView(...).is_variadic_tuple is False # pyright: ignore def test_strip_optional() -> None: diff --git a/type_lens/callable_view.py b/type_lens/callable_view.py index 291639a..b1de659 100644 --- a/type_lens/callable_view.py +++ b/type_lens/callable_view.py @@ -16,7 +16,7 @@ class CallableView: - def __init__(self, fn: Callable, type_hints: dict[str, type]): + def __init__(self, fn: Callable[..., Any], type_hints: dict[str, type]): self.callable = fn self.signature = getattr(fn, "__signature__", None) or inspect.signature(fn) @@ -45,7 +45,7 @@ def __repr__(self) -> str: @classmethod def from_callable( cls: type[Self], - fn: Callable, + fn: Callable[..., Any], *, globalns: dict[str, Any] | None = None, localns: dict[str, Any] | None = None, @@ -61,17 +61,3 @@ def from_callable( result = get_type_hints(hint_fn, globalns=globalns, localns=localns, include_extras=include_extras) return cls(fn, result) - - -def _fix_annotated_optional_type_hints( - hints: dict[str, Any], -) -> dict[str, Any]: # pragma: no cover - """Normalize `Annotated` interacting with `get_type_hints` in versions <3.11. - - https://github.com/python/cpython/issues/90353. - """ - for param_name, hint in hints.items(): - type_view = TypeView(hint) - if type_view.is_union and type_view.inner_types[0].is_annotated: - hints[param_name] = type_view.inner_types[0].raw - return hints diff --git a/type_lens/parameter_view.py b/type_lens/parameter_view.py index 22dd39d..3ccc783 100644 --- a/type_lens/parameter_view.py +++ b/type_lens/parameter_view.py @@ -32,7 +32,7 @@ class ParameterView: def __init__( self, name: str, - type_view: TypeView = _any_type_view, + type_view: TypeView[Any] = _any_type_view, *, default: Any | EmptyType = Empty, has_annotation: bool = True, diff --git a/type_lens/type_view.py b/type_lens/type_view.py index 84f935e..67a5729 100644 --- a/type_lens/type_view.py +++ b/type_lens/type_view.py @@ -2,7 +2,17 @@ from collections import abc from collections.abc import Collection, Mapping -from typing import Any, AnyStr, Final, ForwardRef, Generic, Literal, TypeVar, Union, _SpecialForm +from typing import ( + Any, + AnyStr, + Final, + ForwardRef, + Generic, + Literal, + TypeVar, + Union, + _SpecialForm, # pyright: ignore[reportPrivateUsage] +) from typing_extensions import Annotated, NotRequired, Required, get_args, get_origin from typing_extensions import Literal as ExtensionsLiteral @@ -44,12 +54,12 @@ def __init__(self, annotation: T) -> None: unwrapped, metadata, wrappers = unwrap_annotation(annotation) origin = get_origin(unwrapped) - args: tuple[Any, ...] = () if origin is abc.Callable else get_args(unwrapped) + args: tuple[Any, ...] = () if origin is abc.Callable else get_args(unwrapped) # pyright: ignore self.raw: Final[T] = annotation self.annotation: Final = unwrapped - self.origin: Final = origin - self.fallback_origin: Final = origin or unwrapped + self.origin: Final[Any] = origin + self.fallback_origin: Final[Any] = origin or unwrapped self.args: Final[tuple[Any, ...]] = args self.metadata: Final = metadata self._wrappers: Final = wrappers @@ -79,15 +89,15 @@ def repr_type(self) -> str: """ # Literal/Union both appear to have no name on some versions of python. if self.is_literal: - name = "Literal" + name: str = "Literal" elif self.is_union: name = "Union" elif isinstance(self.annotation, (type, _SpecialForm)) or self.origin: try: - name = self.annotation.__name__ # pyright: ignore[reportAttributeAccessIssue] + name = str(self.annotation.__name__) # pyright: ignore except AttributeError: # Certain _SpecialForm items have no __name__ python 3.8. - name = self.annotation._name # pyright: ignore[reportAttributeAccessIssue] + name = str(self.annotation._name) # pyright: ignore else: name = repr(self.annotation) @@ -95,7 +105,7 @@ def repr_type(self) -> str: inner_types = ", ".join(t.repr_type for t in self.inner_types) name = f"{name}[{inner_types}]" - return str(name) + return name @property def allows_none(self) -> bool: @@ -192,7 +202,7 @@ def is_variadic_tuple(self) -> bool: Tuples like `tuple[int, ...]` represent a list-like unbounded sequence of a single type T. """ - return self.is_tuple and len(self.args) == 2 and self.args[1] == ... + return self.is_tuple and len(self.args) == 2 and self.args[1] == ... # pyright: ignore @property def safe_generic_origin(self) -> Any: @@ -249,7 +259,7 @@ def is_subclass_of(self, typ: Any | tuple[Any, ...], /) -> bool: """ return isinstance(self.fallback_origin, type) and issubclass(self.fallback_origin, typ) - def strip_optional(self) -> TypeView: + def strip_optional(self) -> TypeView[Any]: if not self.is_optional: return self diff --git a/type_lens/typing.py b/type_lens/typing.py index 07a4ded..0eda9e0 100644 --- a/type_lens/typing.py +++ b/type_lens/typing.py @@ -44,7 +44,7 @@ def fix_annotated_optional_type_hints( _get_type_hints = typing.get_type_hints # pyright: ignore else: - from eval_type_backport import eval_type_backport + from eval_type_backport import eval_type_backport # pyright: ignore @typing.no_type_check def _get_type_hints( # noqa: C901 diff --git a/type_lens/utils.py b/type_lens/utils.py index c5ad3ac..530a8ef 100644 --- a/type_lens/utils.py +++ b/type_lens/utils.py @@ -94,8 +94,8 @@ def unwrap_annotation(annotation: t.Any) -> tuple[t.Any, tuple[t.Any, ...], set[ A tuple of the unwrapped annotation and any ``Annotated`` metadata, and a set of any wrapper types encountered. """ origin = te.get_origin(annotation) - wrappers = set() - metadata = [] + wrappers: set[t.Any] = set() + metadata: list[t.Any] = [] while origin in _WRAPPER_TYPES: wrappers.add(origin) annotation, *meta = te.get_args(annotation)