diff --git a/tests/test_cqa.py b/tests/test_cqa.py index 5a5a817..f2b04dc 100644 --- a/tests/test_cqa.py +++ b/tests/test_cqa.py @@ -29,7 +29,7 @@ def test_cqa_ruff_check(project_root): def test_cqa_ruff_format_check(project_root): """Test that code is properly formatted according to ruff.""" - # Ruff format respects pyproject.toml exclusions (_vendor is excluded) + # Ruff format respects pyproject.toml exclusions result = subprocess.run( [sys.executable, "-m", "ruff", "format", "--check", "."], capture_output=True, @@ -43,12 +43,11 @@ def test_cqa_ruff_format_check(project_root): ) -@pytest.mark.skip(reason="Mypy is not working yet") def test_cqa_mypy(project_root): """Test that code passes mypy type checking.""" # Mypy uses configuration from pyproject.toml - # Run on the whole project (pi package and tests), excluding _vendor - for subdir in ["pi", "tests/fixtures"]: + # Run on typemap -- tests not ready yet + for subdir in ["typemap"]: result = subprocess.run( [ sys.executable, diff --git a/typemap/__init__.py b/typemap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/typemap/type_eval/_apply_generic.py b/typemap/type_eval/_apply_generic.py index e451dcb..2b54111 100644 --- a/typemap/type_eval/_apply_generic.py +++ b/typemap/type_eval/_apply_generic.py @@ -77,7 +77,7 @@ def _box(cls: type[Any], args: dict[str, Any]) -> Boxed: return Boxed(cls, boxed_bases, args) - if isinstance(cls, (typing._GenericAlias, types.GenericAlias)): + if isinstance(cls, (typing._GenericAlias, types.GenericAlias)): # type: ignore[attr-defined] # XXX this feels out of place, `box()` needs to only accept types. args = dict( zip(cls.__origin__.__parameters__, cls.__args__, strict=True) @@ -184,7 +184,7 @@ def apply(cls: type[Any]) -> dict[str, Any]: annos.update(af) for name, stuff in boxed.cls.__dict__.items(): - if name in typing.EXCLUDED_ATTRIBUTES: + if name in typing.EXCLUDED_ATTRIBUTES: # type: ignore[attr-defined] continue stuff = inspect.unwrap(stuff) diff --git a/typemap/type_eval/_eval_call.py b/typemap/type_eval/_eval_call.py index aff404a..027102f 100644 --- a/typemap/type_eval/_eval_call.py +++ b/typemap/type_eval/_eval_call.py @@ -16,11 +16,11 @@ def eval_call(func: types.FunctionType, /, *args: Any, **kwargs: Any) -> Any: def _eval_call(func: types.FunctionType, /, *args: Any, **kwargs: Any) -> Any: - vars = {} + vars: dict[str, Any] = {} params = func.__type_params__ for p in params: - if p.__bound__ is next.CallSpec: + if hasattr(p, "__bound__") and p.__bound__ is next.CallSpec: vars[p.__name__] = next._CallSpecWrapper(args, kwargs, func) else: vars[p.__name__] = p @@ -29,6 +29,8 @@ def _eval_call(func: types.FunctionType, /, *args: Any, **kwargs: Any) -> Any: af = func.__annotate__ except AttributeError: raise ValueError("func has no __annotate__ attribute") + if not af: + raise ValueError("func has no __annotate__ attribute") af_args = tuple( types.CellType(vars[name]) for name in af.__code__.co_freevars diff --git a/typemap/type_eval/_eval_typing.py b/typemap/type_eval/_eval_typing.py index e1fbdaa..24319c8 100644 --- a/typemap/type_eval/_eval_typing.py +++ b/typemap/type_eval/_eval_typing.py @@ -99,7 +99,7 @@ def _eval_type_type(obj: type, ctx: EvalContext): if isinstance(obj, type) and issubclass(obj, typing.Generic): ret = type( obj.__name__, - (typing.Protocol,), + (typing.cast(type, typing.Protocol),), { "__module__": obj.__module__, "__name__": obj.__name__, @@ -129,6 +129,7 @@ def _eval_type_var(obj: typing.TypeVar, ctx: EvalContext): @_eval_types_impl.register def _eval_type_alias(obj: typing.TypeAliasType, ctx: EvalContext): + assert obj.__module__ # FIXME: or can this really happen? func = obj.evaluate_value mod = sys.modules[obj.__module__] ff = types.FunctionType(func.__code__, mod.__dict__, None, None, ()) @@ -142,7 +143,7 @@ def _eval_generic(obj: types.GenericAlias, ctx: EvalContext): # This is a GenericAlias over a Python class, e.g. `dict[str, int]` # Let's reconstruct it by evaluating all arguments new_args = tuple(_eval_types(arg, ctx) for arg in obj.__args__) - return obj.__origin__[new_args] + return obj.__origin__[new_args] # type: ignore[index] func = obj.evaluate_value @@ -169,5 +170,6 @@ def _eval_generic(obj: types.GenericAlias, ctx: EvalContext): @_eval_types_impl.register def _eval_union(obj: typing.Union, ctx: EvalContext): # type: ignore - new_args = tuple(_eval_types(arg, ctx) for arg in obj.__args__) + args: typing.Sequence[typing.Any] = obj.__args__ + new_args = tuple(_eval_types(arg, ctx) for arg in args) return typing.Union[new_args] diff --git a/typemap/typing.py b/typemap/typing.py index ad31ddf..9146038 100644 --- a/typemap/typing.py +++ b/typemap/typing.py @@ -16,7 +16,8 @@ class CallSpec: class _CallSpecWrapper: _args: tuple[typing.Any] _kwargs: dict[str, typing.Any] - _func: types.FunctionType | types.MethodType + # TODO: Support MethodType! + _func: types.FunctionType # | types.MethodType @property def args(self) -> None: @@ -32,7 +33,7 @@ class _CallKwarg: name: str -@typing._SpecialForm +@typing._SpecialForm # type: ignore[call-arg] def CallSpecKwargs(self, spec: _CallSpecWrapper) -> list[_CallKwarg]: ff = types.FunctionType( spec._func.__code__, @@ -85,7 +86,7 @@ class DirProperties(metaclass=DirPropertiesMeta): class NewProtocolMeta(type): def __getitem__(cls, val: list[Property]): - dct = {} + dct: dict[str, object] = {} dct["__annotations__"] = {prop.name: prop.type for prop in val} module_name = __name__ @@ -99,7 +100,7 @@ def __getitem__(cls, val: list[Property]): dct["__module__"] = module_name - mcls = type(typing.Protocol) + mcls: type = type(typing.cast(type, typing.Protocol)) cls = mcls(name, (typing.Protocol,), dct) return cls