core.common: move listify to core.utils.itertools, use better typing annotations for it

also some minor refactoring of my.rss
This commit is contained in:
Dima Gerasimov 2024-08-14 10:59:47 +03:00
parent 029fa3ae84
commit f4214807a3
6 changed files with 81 additions and 65 deletions

View file

@ -4,8 +4,11 @@ Various helpers/transforms of iterators
Ideally this should be as small as possible and we should rely on stdlib itertools or more_itertools
"""
from typing import Callable, Dict, Iterable, TypeVar, cast
from typing import Callable, Dict, Iterable, Iterator, TypeVar, List, cast, TYPE_CHECKING
from ..compat import ParamSpec
from decorator import decorator
T = TypeVar('T')
K = TypeVar('K')
@ -75,3 +78,39 @@ def test_make_dict() -> None:
# check type inference
d2: Dict[str, int] = make_dict(it, key=lambda i: str(i))
d3: Dict[str, bool] = make_dict(it, key=lambda i: str(i), value=lambda i: i % 2 == 0)
LFP = ParamSpec('LFP')
LV = TypeVar('LV')
@decorator
def _listify(func: Callable[LFP, Iterable[LV]], *args: LFP.args, **kwargs: LFP.kwargs) -> List[LV]:
"""
Wraps a function's return value in wrapper (e.g. list)
Useful when an algorithm can be expressed more cleanly as a generator
"""
return list(func(*args, **kwargs))
# ugh. decorator library has stub types, but they are way too generic?
# tried implementing my own stub, but failed -- not sure if it's possible at all?
# so seems easiest to just use specialize instantiations of decorator instead
if TYPE_CHECKING:
def listify(func: Callable[LFP, Iterable[LV]]) -> Callable[LFP, List[LV]]: ...
else:
listify = _listify
def test_listify() -> None:
@listify
def it() -> Iterator[int]:
yield 1
yield 2
res = it()
from typing_extensions import assert_type # TODO move to compat?
assert_type(res, List[int])
assert res == [1, 2]