Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Mypy config #179

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,25 @@ repos:
# Run the formatter
- id: ruff-format
types_or: [python, pyi]

- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.10.0"
hooks:
- id: mypy
files: plum
additional_dependencies:
- pytest
exclude: |
(?x)
^(
| plum/alias\.py
| plum/function\.py
| plum/method\.py
| plum/parametric\.py
| plum/promotion\.py
| plum/repr\.py
| plum/resolver\.py
| plum/signature\.py
| plum/type\.py
| plum/util\.py
)$
22 changes: 17 additions & 5 deletions plum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
# versions from `typing`. To not break backward compatibility, we still export these
# types.
from functools import partial
from typing import Dict, List, Tuple, Union # noqa: F401
from typing import Dict, List, Tuple, Type, TypeVar, Union # noqa: F401

from typing_extensions import TypeGuard # noqa: F401

from beartype import BeartypeConf as _BeartypeConf
from beartype import BeartypeStrategy as _BeartypeStrategy
Expand Down Expand Up @@ -31,8 +33,14 @@
# actually is not yet available, but we can already opt in to use it.
_is_bearable = partial(_is_bearable, conf=_BeartypeConf(strategy=_BeartypeStrategy.On))

T = TypeVar("T")
T2 = TypeVar("T2")


def isinstance(instance, c):
# TODO: TypeGuard or
def isinstance(
instance: object, c: Union[Type[T], _TypeHint[T]]
) -> TypeGuard[Union[T, _TypeHint[T]]]:
"""Check if `instance` is of type or type hint `c`.

This is a drop-in replace for the built-in :func:`ininstance` which supports type
Expand All @@ -45,10 +53,13 @@ def isinstance(instance, c):
Returns:
bool: Whether `instance` is of type or type hint `c`.
"""
return _is_bearable(instance, resolve_type_hint(c))
pred: bool = _is_bearable(instance, resolve_type_hint(c))
return pred


def issubclass(c1, c2):
def issubclass(
c1: Union[Type[T], _TypeHint[T]], c2: Union[Type[T2], _TypeHint[T2]]
) -> TypeGuard[Union[T2, _TypeHint[T2]]]:
"""Check if `c1` is a subclass or sub-type hint of `c2`.

This is a drop-in replace for the built-in :func:`issubclass` which supports type
Expand All @@ -61,4 +72,5 @@ def issubclass(c1, c2):
Returns:
bool: Whether `c1` is a subtype or sub-type hint of `c2`.
"""
return _TypeHint(resolve_type_hint(c1)) <= _TypeHint(resolve_type_hint(c2))
pred: bool = _TypeHint(resolve_type_hint(c1)) <= _TypeHint(resolve_type_hint(c2))
return pred
12 changes: 6 additions & 6 deletions plum/autoreload.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
__all__ = ["activate_autoreload", "deactivate_autoreload"]


def _update_instances(old, new):
def _update_instances(old: type, new: type) -> None:
"""First call the original implementation of Autoreload's :meth:`update_instances`,
and then use :obj:`.type._type_mapping` to map type `old` to the type `new`.

Args:
old (type): Old type.
new (type): New type.
"""
_update_instances_original(old, new)
_update_instances_original(old, new) # type: ignore[misc]

type_mapping[old] = new

Expand All @@ -33,10 +33,10 @@ def _update_instances(old, new):
"""function: Original implementation of :func:`update_instances`."""


def activate_autoreload():
def activate_autoreload() -> None:
"""Pirate Autoreload's `update_instance` function to have Plum work with
Autoreload."""
from IPython.extensions import autoreload # type: ignore
from IPython.extensions import autoreload

# First, cache the original method so we can deactivate ourselves.
global _update_instances_original
Expand All @@ -47,7 +47,7 @@ def activate_autoreload():
autoreload.update_instances = _update_instances


def deactivate_autoreload():
def deactivate_autoreload() -> None:
"""Disable Plum's autoreload hack. This undoes what
:func:`.autoreload.activate_autoreload` did."""
global _update_instances_original
Expand All @@ -66,7 +66,7 @@ def deactivate_autoreload():
try:
# Try to load iPython and get the iPython session, but don't crash if
# this does not work (for example IPython not installed, or python shell)
from IPython import get_ipython # type: ignore
from IPython import get_ipython

ip = get_ipython()
ext_loaded = "IPython.extensions.storemagic" in ip.extension_manager.loaded
Expand Down
8 changes: 4 additions & 4 deletions plum/dispatcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class Dispatcher:
classes: Dict[str, Dict[str, Function]] = field(default_factory=dict)

@overload
def __call__(self, method: T, precedence: int = ...) -> T: ...
def __call__(self, method: None, precedence: int) -> Callable[[T], T]: ...

@overload
def __call__(self, method: None, precedence: int) -> Callable[[T], T]: ...
def __call__(self, method: T, precedence: int = ...) -> T: ...

def __call__(
self, method: Optional[T] = None, precedence: int = 0
Expand Down Expand Up @@ -130,13 +130,13 @@ def _add_method(
f.register(method, signature, precedence)
return f

def clear_cache(self):
def clear_cache(self) -> None:
"""Clear cache."""
for f in self.functions.values():
f.clear_cache()


def clear_all_cache():
def clear_all_cache() -> None:
"""Clear all cache, including the cache of subclass checks. This should be called
if types are modified."""
for f in Function._instances:
Expand Down
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ branch = true
command_line = "-m pytest --verbose test"
source = ["plum"]


[tool.mypy]
disable_error_code = ["no-redef"]
enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
files = ["plum"]
strict = true
follow_imports = "skip"


[tool.pytest.ini_options]
testpaths = ["tests/", "plum", "docs"]
addopts = [
Expand Down
2 changes: 1 addition & 1 deletion tests/typechecked/test_overload.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def f(x): # E: pyright(overloaded implementation is not consistent)
def test_overload() -> None:
assert f(1) == 1
assert f("1") == "1"
with pytest.raises(NotFoundLookupError):
with pytest.raises(NotFoundLookupError, match="no overload variant"):
# E: pyright(argument of type "float" cannot be assigned to parameter "x")
# E: pyright(no overloads for "f" match the provided arguments)
# E: mypy(no overload variant of "f" matches argument type "float")
Expand Down
Loading