Skip to content

Commit

Permalink
Merge pull request #193 from jodal/pyright
Browse files Browse the repository at this point in the history
Check types with pyright
  • Loading branch information
jodal committed Jun 4, 2023
2 parents 39d2db2 + 258a6c1 commit 952b5ea
Show file tree
Hide file tree
Showing 10 changed files with 85 additions and 54 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
- name: "Lint: mypy"
python: "3.11"
tox: mypy
- name: "Lint: pyright"
python: "3.11"
tox: pyright
- name: "Lint: ruff"
python: "3.11"
tox: ruff
Expand Down
13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ toml = "^0.10.2"
[tool.poetry.group.mypy.dependencies]
mypy = "^1.3.0"

[tool.poetry.group.pyright.dependencies]
pyright = "^1.1.311"

[tool.poetry.group.ruff.dependencies]
ruff = "^0.0.270"

Expand Down Expand Up @@ -133,6 +136,16 @@ target-version = "py38"
[tool.ruff.isort]
known-first-party = ["pykka"]

[tool.pyright]
pythonVersion = "3.8"
venvPath = "."
venv = ".venv"
typeCheckingMode = "strict"
# Already coverd by tests and careful import ordering:
reportImportCycles = false
# Already covered by flake8-self:
reportPrivateUsage = false

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
24 changes: 12 additions & 12 deletions src/pykka/_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import sys
import threading
import uuid
from typing import TYPE_CHECKING, Any, Protocol, Sequence
from typing import TYPE_CHECKING, Any, Optional, Protocol, Sequence

from pykka import ActorDeadError, ActorRef, ActorRegistry, messages

Expand All @@ -20,10 +20,10 @@


class ActorInbox(Protocol):
def put(self, envelope: Envelope) -> None:
def put(self, envelope: Envelope[Any], /) -> None:
...

def get(self) -> Envelope:
def get(self) -> Envelope[Any]:
...

def empty(self) -> bool:
Expand Down Expand Up @@ -128,7 +128,7 @@ def _create_actor_inbox() -> ActorInbox:
raise NotImplementedError("Use a subclass of Actor")

@staticmethod
def _create_future() -> Future:
def _create_future() -> Future[Any]:
"""Create a future for the actor.
Internal method for implementors of new actor types.
Expand Down Expand Up @@ -297,23 +297,23 @@ def on_stop(self) -> None:

def _handle_failure(
self,
exception_type: type[BaseException],
exception_value: BaseException,
traceback: TracebackType,
exception_type: Optional[type[BaseException]],
exception_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
"""Log unexpected failures, unregisters and stops the actor."""
logger.error(
f"Unhandled exception in {self}:",
exc_info=(exception_type, exception_value, traceback),
exc_info=(exception_type, exception_value, traceback), # type: ignore[arg-type] # noqa: E501
)
ActorRegistry.unregister(self.actor_ref)
self.actor_stopped.set()

def on_failure(
self,
exception_type: type[BaseException],
exception_value: BaseException,
traceback: TracebackType,
exception_type: Optional[type[BaseException]],
exception_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
"""Run code when an unhandled exception is raised.
Expand Down Expand Up @@ -391,7 +391,7 @@ def _introspect_attributes(
obj: Any,
) -> dict[str, Any]:
"""Combine ``__dict__`` from ``obj`` and all its superclasses."""
result = {}
result: dict[str, Any] = {}
for cls in reversed(obj.__class__.mro()):
result.update(cls.__dict__)
if hasattr(obj, "__dict__"):
Expand Down
6 changes: 3 additions & 3 deletions src/pykka/_envelope.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Generic, Optional, TypeVar
from typing import TYPE_CHECKING, Any, Generic, Optional, TypeVar

if TYPE_CHECKING:
from pykka import Future
Expand All @@ -23,9 +23,9 @@ class Envelope(Generic[T]):
__slots__ = ["message", "reply_to"]

message: T
reply_to: Optional[Future]
reply_to: Optional[Future[Any]]

def __init__(self, message: T, reply_to: Optional[Future] = None) -> None:
def __init__(self, message: T, reply_to: Optional[Future[Any]] = None) -> None:
self.message = message
self.reply_to = reply_to

Expand Down
49 changes: 26 additions & 23 deletions src/pykka/_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@

__all__ = ["Future", "get_all"]

_T = TypeVar("_T")
T = TypeVar("T")
J = TypeVar("J") # For when T is Iterable[J]

_M = TypeVar("_M") # For Future.map()
_R = TypeVar("_R") # For Future.reduce()
M = TypeVar("M") # For Future.map()
R = TypeVar("R") # For Future.reduce()

GetHookFunc: TypeAlias = Callable[[Optional[float]], _T]
GetHookFunc: TypeAlias = Callable[[Optional[float]], T]


class Future(Generic[_T]):
class Future(Generic[T]):
"""A handle to a value which is available now or in the future.
Typically returned by calls to actor methods or accesses to actor fields.
Expand All @@ -39,15 +39,18 @@ class Future(Generic[_T]):
``await`` the future.
"""

_get_hook: Optional[GetHookFunc[T]]
_get_hook_result: Optional[T]

def __init__(self) -> None:
super().__init__()
self._get_hook: Optional[GetHookFunc] = None
self._get_hook_result: Optional[_T] = None
self._get_hook = None
self._get_hook_result = None

def get(
self,
timeout: Optional[float] = None,
) -> _T:
) -> T:
"""Get the value encapsulated by the future.
If the encapsulated value is an exception, it is raised instead of
Expand Down Expand Up @@ -76,7 +79,7 @@ def get(

def set(
self,
value: Optional[_T] = None,
value: Optional[T] = None,
) -> None:
"""Set the encapsulated value.
Expand Down Expand Up @@ -107,7 +110,7 @@ def set_exception(

def set_get_hook(
self,
func: GetHookFunc,
func: GetHookFunc[T],
) -> None:
"""Set a function to be executed when :meth:`get` is called.
Expand Down Expand Up @@ -154,7 +157,7 @@ def filter(
return future

def join(
self,
self: Future[Any],
*futures: Future[Any],
) -> Future[Iterable[Any]]:
"""Return a new future with a list of the result of multiple futures.
Expand All @@ -177,14 +180,14 @@ def join(
.. versionadded:: 1.2
"""
future = self.__class__()
future = cast(Future[Iterable[Any]], self.__class__())
future.set_get_hook(lambda timeout: [f.get(timeout) for f in [self, *futures]])
return cast(Future[Iterable[Any]], future)
return future

def map(
self,
func: Callable[[_T], _M],
) -> Future[_M]:
func: Callable[[T], M],
) -> Future[M]:
"""Pass the result of the future through a function.
Example::
Expand Down Expand Up @@ -212,15 +215,15 @@ def map(
behavior has been simplified. Now, the entire result value is
passed to the function.
"""
future = self.__class__()
future = cast(Future[M], self.__class__())
future.set_get_hook(lambda timeout: func(self.get(timeout)))
return cast(Future[_M], future)
return future

def reduce(
self: Future[Iterable[J]],
func: Callable[[_R, J], _R],
*args: _R,
) -> Future[_R]:
func: Callable[[R, J], R],
*args: R,
) -> Future[R]:
"""Reduce a future's iterable result to a single value.
The function of two arguments is applied cumulatively to the items of
Expand Down Expand Up @@ -266,13 +269,13 @@ def reduce(
.. versionadded:: 1.2
"""
future = self.__class__()
future = cast(Future[R], self.__class__())
future.set_get_hook(
lambda timeout: functools.reduce(func, self.get(timeout), *args)
)
return cast(Future[_R], future)
return future

def __await__(self) -> Generator[None, None, _T]:
def __await__(self) -> Generator[None, None, T]:
yield
value = self.get()
return value
Expand Down
9 changes: 3 additions & 6 deletions src/pykka/_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from typing import (
TYPE_CHECKING,
Any,
Callable,
NamedTuple,
Optional,
Sequence,
Expand All @@ -24,6 +23,8 @@
logger = logging.getLogger("pykka")


_T = TypeVar("_T")

AttrPath: TypeAlias = Sequence[str]


Expand Down Expand Up @@ -361,11 +362,7 @@ def defer(
self.actor_ref.tell(message)


FuncType: TypeAlias = Callable[..., Any]
_F = TypeVar("_F", bound=FuncType)


def traversable(obj: _F) -> _F:
def traversable(obj: _T) -> _T:
"""Mark an actor attribute as traversable.
The traversable marker makes the actor attribute's own methods and
Expand Down
8 changes: 6 additions & 2 deletions src/pykka/_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ActorRegistry:
def broadcast(
cls,
message: Any,
target_class: Optional[type[Actor]] = None,
target_class: Union[str, type[Actor], None] = None,
) -> None:
"""Broadcast ``message`` to all actors of the specified ``target_class``.
Expand Down Expand Up @@ -158,7 +158,11 @@ def stop_all(
...

@classmethod
def stop_all(cls, block=True, timeout=None): # type: ignore[no-untyped-def]
def stop_all(
cls,
block: bool = True,
timeout: Optional[float] = None,
) -> Union[list[bool], list[Future[bool]]]:
"""Stop all running actors.
``block`` and ``timeout`` works as for
Expand Down
16 changes: 11 additions & 5 deletions src/pykka/_threading.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
import queue
import sys
import threading
from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, Optional
from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, Optional, TypeVar

from pykka import Actor, Future, Timeout

if TYPE_CHECKING:
from pykka._actor import ActorInbox
from pykka._envelope import Envelope
from pykka._types import OptExcInfo

__all__ = ["ThreadingActor", "ThreadingFuture"]
Expand All @@ -18,7 +20,10 @@ class ThreadingFutureResult(NamedTuple):
exc_info: Optional[OptExcInfo] = None


class ThreadingFuture(Future):
F = TypeVar("F")


class ThreadingFuture(Future[F]):
"""Implementation of :class:`Future` for use with regular Python threads`.
The future is implemented using a :class:`queue.Queue`.
Expand Down Expand Up @@ -101,11 +106,12 @@ class ThreadingActor(Actor):
"""

@staticmethod
def _create_actor_inbox() -> queue.Queue:
return queue.Queue()
def _create_actor_inbox() -> ActorInbox:
inbox: queue.Queue[Envelope[Any]] = queue.Queue()
return inbox

@staticmethod
def _create_future() -> ThreadingFuture:
def _create_future() -> Future[Any]:
return ThreadingFuture()

def _start_actor_loop(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion src/pykka/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from typing import Any, Dict, NamedTuple, Sequence, Tuple


class _ActorStop(NamedTuple):
class _ActorStop(NamedTuple): # pyright: ignore[reportUnusedClass]
"""Internal message."""


Expand Down
9 changes: 7 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ commands =
[testenv:docs]
commands =
poetry install --all-extras --only=main,docs
python -m sphinx -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html
poetry run sphinx-build -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html

[testenv:mypy]
commands =
poetry install --only=main,dev,docs,tests,mypy
poetry run python -m mypy src tests
poetry run mypy src tests

[testenv:pyright]
commands =
poetry install --only=main,dev,docs,tests,pyright
poetry run pyright src

[testenv:ruff]
commands =
Expand Down

0 comments on commit 952b5ea

Please sign in to comment.