|
| 1 | +from __future__ import annotations |
| 2 | + |
1 | 3 | from functools import reduce |
| 4 | +from typing import Any, Callable, TYPE_CHECKING, Union |
| 5 | + |
| 6 | +if TYPE_CHECKING: |
| 7 | + from .language import core |
| 8 | + IterableType = Union[list[Any], tuple[Any, ...], core.tuple, core.tuple_type] |
| 9 | + ObjPath = tuple[int, ...] |
2 | 10 |
|
3 | 11 |
|
4 | | -def get_iterable_path(iterable, path): |
5 | | - return reduce(lambda a, idx: a[idx], path, iterable) |
| 12 | +def get_iterable_path(iterable: IterableType, path: ObjPath) -> Any: |
| 13 | + return reduce(lambda a, idx: a[idx], path, iterable) # type: ignore[index] |
6 | 14 |
|
7 | 15 |
|
8 | | -def set_iterable_path(iterable, path, val): |
| 16 | +def set_iterable_path(iterable: IterableType, path: tuple[int, ...], val: Any): |
| 17 | + assert len(path) != 0 |
9 | 18 | prev = iterable if len(path) == 1 else get_iterable_path(iterable, path[:-1]) |
10 | | - prev[path[-1]] = val |
| 19 | + prev[path[-1]] = val # type: ignore[index] |
11 | 20 |
|
12 | 21 |
|
13 | | -def find_paths_if(iterable, pred): |
| 22 | +def find_paths_if(iterable: Union[IterableType, Any], pred: Callable[[ObjPath, Any], bool]) -> list[ObjPath]: |
14 | 23 | from .language import core |
15 | | - is_iterable = lambda x: isinstance(x, (list, tuple, core.tuple, core.tuple_type)) |
16 | | - ret = dict() |
| 24 | + is_iterable: Callable[[Any], bool] = lambda x: isinstance(x, (list, tuple, core.tuple, core.tuple_type)) |
| 25 | + # We need to use dict so that ordering is maintained, while set doesn't guarantee order |
| 26 | + ret: dict[ObjPath, None] = {} |
17 | 27 |
|
18 | | - def _impl(current, path): |
19 | | - path = (path[0], ) if len(path) == 1 else tuple(path) |
| 28 | + def _impl(path: tuple[int, ...], current: Any): |
20 | 29 | if is_iterable(current): |
21 | 30 | for idx, item in enumerate(current): |
22 | | - _impl(item, path + (idx, )) |
| 31 | + _impl((*path, idx), item) |
23 | 32 | elif pred(path, current): |
24 | | - if len(path) == 1: |
25 | | - ret[(path[0], )] = None |
26 | | - else: |
27 | | - ret[tuple(path)] = None |
28 | | - |
29 | | - if is_iterable(iterable): |
30 | | - _impl(iterable, []) |
31 | | - elif pred(list(), iterable): |
32 | | - ret = {tuple(): None} |
33 | | - else: |
34 | | - ret = dict() |
| 33 | + ret[path] = None |
| 34 | + |
| 35 | + _impl((), iterable) |
| 36 | + |
35 | 37 | return list(ret.keys()) |
0 commit comments