Skip to content

Commit

Permalink
fix: future method on ASyncGenericBase conflict (#74)
Browse files Browse the repository at this point in the history
* fix: future method on ASyncGenericBase conflict

* fix: kwargs

* fix: missing *

* fix(test): try smth

* fix: future deco

* fix: missing import

* fix: unexpected kwarg

* fix: more conflict

* fix: missing import

* fix: circular import

* chore: remove prints
  • Loading branch information
BobTheBuidler authored Oct 3, 2023
1 parent 5e249d7 commit bd528e5
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 22 deletions.
14 changes: 9 additions & 5 deletions a_sync/_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any, Dict, Tuple

from a_sync import ENVIRONMENT_VARIABLES, _bound, modifiers
from a_sync.future import _ASyncFutureWrappedFn
from a_sync.modified import ASyncFunction, Modified
from a_sync.property import PropertyDescriptor

Expand All @@ -14,20 +15,23 @@ class ASyncMeta(ABCMeta):
"""Any class with metaclass ASyncMeta will have its functions wrapped with a_sync upon class instantiation."""
def __new__(cls, new_class_name, bases, attrs):
_update_logger(new_class_name)
logger.debug(f"woah, you're defining a new ASync class `{new_class_name}`! let's walk thru it together")
logger.debug(f"first, I check whether you've defined any modifiers on `{new_class_name}`")
logger.debug(f"woah, you're defining a new ASync class `%s`! let's walk thru it together", new_class_name)
logger.debug(f"first, I check whether you've defined any modifiers on `%s`", new_class_name)
# NOTE: Open uesion: what do we do when a parent class and subclass define the same modifier differently?
# Currently the parent value is used for functions defined on the parent,
# and the subclass value is used for functions defined on the subclass.
class_defined_modifiers = modifiers.get_modifiers_from(attrs)
logger.debug(f'found modifiers: {class_defined_modifiers}')
logger.debug(f'found modifiers: %s', class_defined_modifiers)
logger.debug("now I inspect the class definition to figure out which attributes need to be wrapped")
for attr_name, attr_value in list(attrs.items()):
if attr_name.startswith("_"):
logger.debug(f"`{new_class_name}.{attr_name}` starts with an underscore, skipping")
logger.debug(f"`%s.%s` starts with an underscore, skipping", new_class_name, attr_name)
continue
elif "__" in attr_name:
logger.debug(f"`{new_class_name}.{attr_name}` incluldes a double-underscore, skipping")
logger.debug(f"`%s.%s` incluldes a double-underscore, skipping", new_class_name, attr_name)
continue
elif isinstance(attr_value, _ASyncFutureWrappedFn):
logger.debug(f"`%s.%s` is a %s, skipping", new_class_name, attr_name, attr_value.__class__.__name__)
continue
logger.debug(f"inspecting `{new_class_name}.{attr_name}` of type {attr_value.__class__.__name__}")
fn_modifiers = dict(class_defined_modifiers)
Expand Down
39 changes: 22 additions & 17 deletions a_sync/future.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@

import asyncio
from decimal import Decimal
from functools import wraps
from functools import partial, wraps
from typing import (Any, Awaitable, Callable, List, Set, TypeVar, Union,
overload)

from typing_extensions import ParamSpec, Unpack

from a_sync import a_sync
from a_sync._typing import ModifierKwargs

T = TypeVar('T')
P = ParamSpec('P')
MaybeMeta = Union[T, "ASyncFuture[T]"]

def future(callable: Union[Callable[P, Awaitable[T]], Callable[P, T]], **kwargs) -> Callable[P, "ASyncFuture[T]"]:
return ASyncFuture.wrap_callable(callable, **kwargs)
def future(callable: Union[Callable[P, Awaitable[T]], Callable[P, T]] = None, **kwargs: Unpack[ModifierKwargs]) -> Callable[P, "ASyncFuture[T]"]:
return _ASyncFutureWrappedFn(callable, **kwargs)

async def _gather_check_and_materialize(*things: Unpack[MaybeMeta[T]]) -> List[T]:
return await asyncio.gather(*[_check_and_materialize(thing) for thing in things])
Expand All @@ -33,8 +33,6 @@ def _materialize(meta: "ASyncFuture[T]") -> T:
retval = asyncio.get_event_loop().run_until_complete(meta._awaitable)
meta.set_result(retval)
meta._done.set()
print(f'dependencies: {meta.dependencies}')
print(f'dependants: {meta.dependants}')
return retval
except RuntimeError as e:
raise RuntimeError(f"{meta} result is not set and the event loop is running, you will need to await it first") from e
Expand All @@ -43,7 +41,6 @@ def _materialize(meta: "ASyncFuture[T]") -> T:

class ASyncFuture(asyncio.Future, Awaitable[T]):
def __init__(self, awaitable: Awaitable[T], dependencies: List["ASyncFuture"] = []) -> None:
#print(awaitable)
self._awaitable = awaitable
self._dependencies = dependencies
for dependency in dependencies:
Expand Down Expand Up @@ -87,13 +84,6 @@ def result(self) -> Union[Callable[[], T], Any]:
return r.result
# the result should be callable like an asyncio.Future
return super().result
@classmethod
def wrap_callable(cls, callable: Union[Callable[P, Awaitable[T]], Callable[P, T]], **kwargs) -> Callable[P, "ASyncFuture[T]"]:
callable = a_sync(callable, **kwargs)
@wraps(callable)
def future_wrap(*args: P.args, **kwargs: P.kwargs) -> "ASyncFuture[T]":
return cls(callable(*args, **kwargs, sync=False))
return future_wrap
def __getattr__(self, attr: str) -> Any:
return getattr(_materialize(self), attr)
def __getitem__(self, key) -> Any:
Expand Down Expand Up @@ -121,9 +111,6 @@ async def __await(self) -> T:
self._started = True
self.set_result(await self._awaitable)
self._done.set()
print(f'self {self.__repr__()}')
print(f'dependencies: {self.dependencies}')
print(f'dependants: {self.dependants}')
return self._result
def __iter__(self):
return _materialize(self).__iter__()
Expand Down Expand Up @@ -496,3 +483,21 @@ def __int__(self) -> int:
return int(_materialize(self))
def __float__(self) -> float:
return float(_materialize(self))

class _ASyncFutureWrappedFn(Callable[P, ASyncFuture[T]]):
__slots__ = "callable", "wrapped"
def __init__(self, callable: Union[Callable[P, Awaitable[T]], Callable[P, T]] = None, **kwargs: Unpack[ModifierKwargs]):
from a_sync import a_sync
if callable:
self.callable = callable
a_sync_callable = a_sync(callable, default="async", **kwargs)
@wraps(callable)
def future_wrap(*args: P.args, **kwargs: P.kwargs) -> "ASyncFuture[T]":
return ASyncFuture(a_sync_callable(*args, **kwargs, sync=False))
self.wrapped = future_wrap
else:
self.wrapped = partial(_ASyncFutureWrappedFn, **kwargs)
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> ASyncFuture[T]:
return self.wrapped(*args, **kwargs)
def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.callable}>"

0 comments on commit bd528e5

Please sign in to comment.