From 2ef1091e15eee51627e35da381d39efb5e1a37e4 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:39:36 -0400 Subject: [PATCH] feat: cythonize _helpers.py (#402) --- a_sync/a_sync/_helpers.pxd | 2 ++ a_sync/a_sync/_helpers.pyi | 3 +++ a_sync/a_sync/{_helpers.py => _helpers.pyx} | 14 ++++++++------ a_sync/a_sync/function.pyx | 9 +++------ a_sync/a_sync/method.pyx | 6 ++---- a_sync/a_sync/property.pyx | 7 ++++--- a_sync/task.py | 12 ++++++------ 7 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 a_sync/a_sync/_helpers.pxd create mode 100644 a_sync/a_sync/_helpers.pyi rename a_sync/a_sync/{_helpers.py => _helpers.pyx} (88%) diff --git a/a_sync/a_sync/_helpers.pxd b/a_sync/a_sync/_helpers.pxd new file mode 100644 index 00000000..4366da1c --- /dev/null +++ b/a_sync/a_sync/_helpers.pxd @@ -0,0 +1,2 @@ +cpdef object _await(object awaitable) +cdef object _asyncify(object func, object executor) \ No newline at end of file diff --git a/a_sync/a_sync/_helpers.pyi b/a_sync/a_sync/_helpers.pyi new file mode 100644 index 00000000..48ce098d --- /dev/null +++ b/a_sync/a_sync/_helpers.pyi @@ -0,0 +1,3 @@ +from a_sync._typing import * +from a_sync import exceptions as exceptions +def _await(awaitable: Awaitable[T]) -> T:... \ No newline at end of file diff --git a/a_sync/a_sync/_helpers.py b/a_sync/a_sync/_helpers.pyx similarity index 88% rename from a_sync/a_sync/_helpers.py rename to a_sync/a_sync/_helpers.pyx index 2015bf50..658d73e1 100644 --- a/a_sync/a_sync/_helpers.py +++ b/a_sync/a_sync/_helpers.pyx @@ -5,14 +5,14 @@ import asyncio import functools -from concurrent.futures import Executor import a_sync.asyncio from a_sync import exceptions from a_sync._typing import * +cdef object get_event_loop = a_sync.asyncio.get_event_loop -def _await(awaitable: Awaitable[T]) -> T: +cpdef object _await(object awaitable): """ Await an awaitable object in a synchronous context. @@ -34,14 +34,14 @@ def _await(awaitable: Awaitable[T]) -> T: - :func:`asyncio.run`: For running the main entry point of an asyncio program. """ try: - return a_sync.asyncio.get_event_loop().run_until_complete(awaitable) + return get_event_loop().run_until_complete(awaitable) except RuntimeError as e: if str(e) == "This event loop is already running": raise exceptions.SyncModeInAsyncContextError from None raise -def _asyncify(func: SyncFn[P, T], executor: Executor) -> CoroFn[P, T]: # type: ignore [misc] +cdef object _asyncify(object func, object executor): # type: ignore [misc] """ Convert a synchronous function to a coroutine function using an executor. @@ -80,12 +80,14 @@ def _asyncify(func: SyncFn[P, T], executor: Executor) -> CoroFn[P, T]: # type: if asyncio.iscoroutinefunction(func) or isinstance(func, ASyncFunction): raise exceptions.FunctionNotSync(func) + + cdef object sumbit = executor.submit @functools.wraps(func) async def _asyncify_wrap(*args: P.args, **kwargs: P.kwargs) -> T: return await asyncio.futures.wrap_future( - executor.submit(func, *args, **kwargs), - loop=a_sync.asyncio.get_event_loop(), + submit(func, *args, **kwargs), + loop=get_event_loop(), ) return _asyncify_wrap diff --git a/a_sync/a_sync/function.pyx b/a_sync/a_sync/function.pyx index 75304c54..736a8a8a 100644 --- a/a_sync/a_sync/function.pyx +++ b/a_sync/a_sync/function.pyx @@ -8,8 +8,8 @@ from async_property.base import AsyncPropertyDescriptor # type: ignore [import] from async_property.cached import AsyncCachedPropertyDescriptor # type: ignore [import] from a_sync._typing import * -from a_sync.a_sync import _helpers from a_sync.a_sync cimport _kwargs +from a_sync.a_sync._helpers cimport _asyncify, _await from a_sync.a_sync.flags import VIABLE_FLAGS from a_sync.a_sync.modifiers.manager import ModifierManager @@ -54,11 +54,9 @@ class _ModifiedMixin: The asynchronous version of the function with applied modifiers. See Also: - - :func:`_helpers._asyncify` - :meth:`ModifierManager.apply_async_modifiers` """ - coro_fn = _helpers._asyncify(func, self.modifiers.executor) - return self.modifiers.apply_async_modifiers(coro_fn) + return self.modifiers.apply_async_modifiers(_asyncify(func, self.modifiers.executor)) @functools.cached_property def _await(self) -> Callable[[Awaitable[T]], T]: @@ -69,10 +67,9 @@ class _ModifiedMixin: The modified _await function. See Also: - - :func:`_helpers._await` - :meth:`ModifierManager.apply_sync_modifiers` """ - return self.modifiers.apply_sync_modifiers(_helpers._await) + return self.modifiers.apply_sync_modifiers(_await) @functools.cached_property def default(self) -> DefaultMode: diff --git a/a_sync/a_sync/method.pyx b/a_sync/a_sync/method.pyx index 3c16caac..b36a15e5 100644 --- a/a_sync/a_sync/method.pyx +++ b/a_sync/a_sync/method.pyx @@ -9,16 +9,14 @@ asynchronously based on various conditions and configurations. # mypy: disable-error-code=valid-type # mypy: disable-error-code=misc import functools -import heapq import logging import weakref from inspect import isawaitable -from a_sync import exceptions from a_sync._typing import * -from a_sync.a_sync import _helpers from a_sync.a_sync cimport _kwargs from a_sync.a_sync._descriptor import ASyncDescriptor +from a_sync.a_sync._helpers cimport _await from a_sync.a_sync.function import ( ASyncFunction, ASyncFunctionAsyncDefault, @@ -585,7 +583,7 @@ class ASyncBoundMethod(ASyncFunction[P, T], Generic[I, P, T]): logger.debug( "awaiting %s for %s args: %s kwargs: %s", coro, self, args, kwargs ) - retval = _helpers._await(coro) + retval = _await(coro) logger.debug( "returning %s for %s args: %s kwargs: %s", retval, self, args, kwargs ) diff --git a/a_sync/a_sync/property.pyx b/a_sync/a_sync/property.pyx index 255cacfd..ccd47711 100644 --- a/a_sync/a_sync/property.pyx +++ b/a_sync/a_sync/property.pyx @@ -6,7 +6,8 @@ from typing_extensions import Unpack from a_sync import _smart from a_sync._typing import * -from a_sync.a_sync import _helpers, config +from a_sync.a_sync import config +from a_sync.a_sync._helpers cimport _asyncify, _await from a_sync.a_sync._descriptor import ASyncDescriptor from a_sync.a_sync.function import ( ASyncFunction, @@ -82,7 +83,7 @@ class _ASyncPropertyDescriptorBase(ASyncDescriptor[I, Tuple[()], T]): if asyncio.iscoroutinefunction(_fget): self._fget = self.__wrapped__ else: - self._fget = _helpers._asyncify(self.__wrapped__, self.modifiers.executor) + self._fget = _asyncify(self.__wrapped__, self.modifiers.executor) @overload def __get__(self, instance: None, owner: Type[I]) -> Self: ... @@ -130,7 +131,7 @@ class _ASyncPropertyDescriptorBase(ASyncDescriptor[I, Tuple[()], T]): instance, owner, ) - retval = _helpers._await(awaitable) + retval = _await(awaitable) else: retval = awaitable diff --git a/a_sync/task.py b/a_sync/task.py index 93c67bd8..838870a5 100644 --- a/a_sync/task.py +++ b/a_sync/task.py @@ -738,6 +738,8 @@ class _TaskMappingView(ASyncGenericBase, Iterable[T], Generic[T, K, V]): _get_from_item: Callable[[Tuple[K, V]], T] _pop: bool = False + __slots__ = "__view__", "__mapping__" + def __init__( self, view: Iterable[T], task_mapping: TaskMapping[K, V], pop: bool = False ) -> None: @@ -751,16 +753,11 @@ def __iter__(self) -> Iterator[T]: return iter(self.__view__) def __await__(self) -> Generator[Any, None, List[T]]: - return self._await().__await__() + return self.__await().__await__() def __len__(self) -> int: return len(self.__view__) - async def _await(self) -> List[T]: - return [result async for result in self] - - __slots__ = "__view__", "__mapping__" - async def aiterbykeys(self, reverse: bool = False) -> ASyncIterator[T]: async for tup in ASyncSorter( self.__mapping__.items(pop=self._pop), key=_get_key, reverse=reverse @@ -773,6 +770,9 @@ async def aiterbyvalues(self, reverse: bool = False) -> ASyncIterator[T]: ): yield self._get_from_item(tup) + async def __await(self) -> List[T]: + return [result async for result in self] + class TaskMappingKeys(_TaskMappingView[K, K, V], Generic[K, V]): """