Skip to content

Commit

Permalink
Slightly better type coverage on property use
Browse files Browse the repository at this point in the history
  • Loading branch information
mikeshardmind committed Jan 22, 2025
1 parent 7da8ed7 commit 47db770
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/async_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
__author__ = "Michael Hall"
__license__ = "Apache-2.0"
__copyright__ = "Copyright 2020-Present Michael Hall"
__version__ = "2025.01.20"
__version__ = "2025.01.22"

import os
import sys
Expand Down
5 changes: 3 additions & 2 deletions src/async_utils/_typings.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
TYPE_CHECKING = False

if TYPE_CHECKING:
from typing import Any, Literal, Never, Self
from typing import Any, Literal, Never, Self, overload
else:
overload = lambda f: f # noqa: E731

def __getattr__(name: str):
if name in {"Any", "Literal", "Never", "Self"}:
Expand All @@ -42,4 +43,4 @@ def __getattr__(name: str):
raise AttributeError(msg)


__all__ = ["Any", "Literal", "Never", "Self"]
__all__ = ["TYPE_CHECKING", "Any", "Literal", "Never", "Self", "overload"]
3 changes: 1 addition & 2 deletions src/async_utils/gen_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,11 @@ async def __aexit__(
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: object,
) -> bool:
) -> None:
if exc_value is not None:
self.f.set_exception(exc_value)

await self.g.aclose()
return False


def _sync_to_async_gen[**P, Y](
Expand Down
44 changes: 39 additions & 5 deletions src/async_utils/priority_sem.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import contextvars
import heapq
import threading
from collections.abc import Generator
from collections.abc import Callable, Generator
from contextlib import contextmanager
from operator import attrgetter

Expand All @@ -31,6 +31,39 @@

_priority: contextvars.ContextVar[int] = contextvars.ContextVar("_priority", default=0)

type CN = Callable[[], None]
type CNN = Callable[[None], None]

TYPE_CHECKING = False
if TYPE_CHECKING:
# see: https://discuss.python.org/t/compatability-of-descriptor-objects-in-protocols/77998/2
type DunderAwait[T] = t.Any
else:
type DunderAwait[T] = Generator[t.Any, t.Any, T]


class prop[T, R]:
def __init__(self, fget: Callable[[T], R]) -> None:
self.fget = fget

def __call__(self) -> R: ...

def __set__(self, instance: T | None, owner: type[T]) -> t.Never:
raise AttributeError

if TYPE_CHECKING:

@t.overload
def __get__(self, obj: T, objtype: type[T] | None) -> R: ...

@t.overload
def __get__(self, obj: None, objtype: type[T] | None) -> Callable[[t.Any], R]: ...

def __get__(self, obj: T | None, objtype: t.Any):
if obj is None:
return self.fget
return self.fget(obj)


class PriorityWaiter:
__slots__ = ("future", "ord")
Expand All @@ -39,9 +72,10 @@ def __init__(self, priority: int, ts: float, future: asyncio.Future[None], /) ->
self.ord = (priority, ts)
self.future = future

cancelled = property(attrgetter("future.cancelled"))
done = property(attrgetter("future.done"))
__await__: t.Any = property(attrgetter("future.__await__"))
cancelled: prop[t.Self, CN] = prop(attrgetter("future.cancelled"))
done: prop[t.Self, CN] = prop(attrgetter("future.done"))
__await__: prop[t.Self, DunderAwait[None]] = prop(attrgetter("future.__await__"))
set_result: prop[t.Self, CNN] = prop(attrgetter("future.set_result"))

def __lt__(self: t.Self, other: object) -> bool:
if not isinstance(other, PriorityWaiter):
Expand Down Expand Up @@ -176,7 +210,7 @@ def _maybe_wake(self) -> None:

if not (next_waiter.done() or next_waiter.cancelled()):
self._value -= 1
next_waiter.future.set_result(None)
next_waiter.set_result(None)

while self._waiters:
# cleanup maintaining heap invariant
Expand Down

0 comments on commit 47db770

Please sign in to comment.