11# piker: trading gear for hackers
2- # Copyright (C) Tyler Goodlet (in stewardship for piker0 )
2+ # Copyright (C) Tyler Goodlet (in stewardship for pikers )
33
44# This program is free software: you can redistribute it and/or modify
55# it under the terms of the GNU Affero General Public License as published by
1414# You should have received a copy of the GNU Affero General Public License
1515# along with this program. If not, see <https://www.gnu.org/licenses/>.
1616
17- """
17+ '''
1818Cacheing apis and toolz.
1919
20- """
20+ '''
2121
2222from collections import OrderedDict
2323from contextlib import (
24- asynccontextmanager ,
24+ asynccontextmanager as acm ,
25+ )
26+ from typing import (
27+ Awaitable ,
28+ Callable ,
29+ ParamSpec ,
30+ TypeVar ,
2531)
2632
2733from tractor .trionics import maybe_open_context
3238
3339log = get_logger (__name__ )
3440
35-
36- def async_lifo_cache (maxsize = 128 ):
37- """Async ``cache`` with a LIFO policy.
41+ T = TypeVar ("T" )
42+ P = ParamSpec ("P" )
43+
44+
45+ # TODO: move this to `tractor.trionics`..
46+ # - egs. to replicate for tests: https://github.com/aio-libs/async-lru#usage
47+ # - their suite as well:
48+ # https://github.com/aio-libs/async-lru/tree/master/tests
49+ # - asked trio_util about it too:
50+ # https://github.com/groove-x/trio-util/issues/21
51+ def async_lifo_cache (
52+ maxsize = 128 ,
53+
54+ # NOTE: typing style was learned from:
55+ # https://stackoverflow.com/a/71132186
56+ ) -> Callable [
57+ Callable [P , Awaitable [T ]],
58+ Callable [
59+ Callable [P , Awaitable [T ]],
60+ Callable [P , Awaitable [T ]],
61+ ],
62+ ]:
63+ '''
64+ Async ``cache`` with a LIFO policy.
3865
3966 Implemented my own since no one else seems to have
4067 a standard. I'll wait for the smarter people to come
4168 up with one, but until then...
42- """
69+
70+ NOTE: when decorating, due to this simple/naive implementation, you
71+ MUST call the decorator like,
72+
73+ .. code:: python
74+
75+ @async_lifo_cache()
76+ async def cache_target():
77+
78+ '''
4379 cache = OrderedDict ()
4480
45- def decorator (fn ):
81+ def decorator (
82+ fn : Callable [P , Awaitable [T ]],
83+ ) -> Callable [P , Awaitable [T ]]:
4684
47- async def wrapper (* args ):
85+ async def decorated (
86+ * args : P .args ,
87+ ** kwargs : P .kwargs ,
88+ ) -> T :
4889 key = args
4990 try :
5091 return cache [key ]
@@ -53,16 +94,20 @@ async def wrapper(*args):
5394 # discard last added new entry
5495 cache .popitem ()
5596
56- # do it
57- cache [key ] = await fn (* args )
97+ # call underlying
98+ cache [key ] = await fn (
99+ * args ,
100+ ** kwargs ,
101+ )
58102 return cache [key ]
59103
60- return wrapper
104+ return decorated
61105
62106 return decorator
63107
64108
65- @asynccontextmanager
109+ # TODO: move this to `.brokers.utils`..
110+ @acm
66111async def open_cached_client (
67112 brokername : str ,
68113) -> 'Client' : # noqa
0 commit comments