Skip to content

Instantly share code, notes, and snippets.

@ask
Last active February 28, 2018 11:57
Show Gist options
  • Save ask/e2a70efd6556ecaa400aabe220f8434f to your computer and use it in GitHub Desktop.
Save ask/e2a70efd6556ecaa400aabe220f8434f to your computer and use it in GitHub Desktop.

Revisions

  1. ask revised this gist Sep 27, 2017. No changes.
  2. ask revised this gist Sep 27, 2017. 1 changed file with 0 additions and 5 deletions.
    5 changes: 0 additions & 5 deletions async_cached_property.py
    Original file line number Diff line number Diff line change
    @@ -15,11 +15,6 @@ def __get__(self, instance: Optional[Any], cls: Type) -> Awaitable:
    if instance is None:
    return self
    try:
    # first time called we create a promise that we cache,
    # this promise will be returned by all future calls.
    # when the user calls `await promise``, it will wait
    # until the result is set, so that also takes care of
    # cache stampede problems.
    followers_future = instance.__dict__[self.name]
    return followers_future
    except KeyError:
  3. ask revised this gist Sep 27, 2017. No changes.
  4. ask revised this gist Sep 27, 2017. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions async_cached_property.py
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,8 @@
    import asyncio
    from typing import Any, Awaitable, Callable, Optional, Type

    # eh, like the @cached_property idiom, but async and you do `await x.y`


    class async_cached_property:
    name: str
  5. ask revised this gist Sep 27, 2017. 1 changed file with 4 additions and 11 deletions.
    15 changes: 4 additions & 11 deletions async_cached_property.py
    Original file line number Diff line number Diff line change
    @@ -1,22 +1,15 @@
    import asyncio
    from typing import Any, Awaitable, Callable, Generic, Optional, Type, TypeVar
    from typing import Any, Awaitable, Callable, Optional, Type

    _T = TypeVar('_T')


    def done_future(result: Any = None) -> asyncio.Future:
    fut = asyncio.Future()
    fut.set_result(result)


    class async_cached_property(Generic[_T]):
    class async_cached_property:
    name: str

    def __init__(self, getter: Callable[[], Awaitable[_T]]) -> None:
    def __init__(self, getter: Callable[[], Awaitable]) -> None:
    self._getter = getter
    self.name = self._getter.__name__

    def __get__(self, instance: Optional[Any], cls: Type) -> Awaitable[_T]:
    def __get__(self, instance: Optional[Any], cls: Type) -> Awaitable:
    if instance is None:
    return self
    try:
  6. ask created this gist Sep 27, 2017.
    56 changes: 56 additions & 0 deletions async_cached_property.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    import asyncio
    from typing import Any, Awaitable, Callable, Generic, Optional, Type, TypeVar

    _T = TypeVar('_T')


    def done_future(result: Any = None) -> asyncio.Future:
    fut = asyncio.Future()
    fut.set_result(result)


    class async_cached_property(Generic[_T]):
    name: str

    def __init__(self, getter: Callable[[], Awaitable[_T]]) -> None:
    self._getter = getter
    self.name = self._getter.__name__

    def __get__(self, instance: Optional[Any], cls: Type) -> Awaitable[_T]:
    if instance is None:
    return self
    try:
    # first time called we create a promise that we cache,
    # this promise will be returned by all future calls.
    # when the user calls `await promise``, it will wait
    # until the result is set, so that also takes care of
    # cache stampede problems.
    followers_future = instance.__dict__[self.name]
    return followers_future
    except KeyError:
    followers_future = instance.__dict__[self.name] = asyncio.Future()
    winners_future = asyncio.ensure_future(self._getter(instance))
    winners_future.add_done_callback(
    lambda fut: followers_future.set_result(fut.result()))
    return winners_future


    class X:

    @async_cached_property
    async def y(self) -> float:
    await asyncio.sleep(1.0)
    return 1.0


    async def test():
    x = X()
    print(await x.y)
    for i in range(100):
    print(await x.y)


    if __name__ == '__main__':
    import asyncio
    loop = asyncio.get_event_loop()
    loop.run_until_complete(test())