Skip to content

Commit

Permalink
feat(common): also traverse nodes used as dictionary keys (#9041)
Browse files Browse the repository at this point in the history
This change allows repr-ing node mappings, like the dereference mappings
for easier inspection.

Inevitably this is slowing down the traversals, but changing the
instance checks to use a plain dict instead of `(dict, frozendict)`
maintains the previous traversal speed.
  • Loading branch information
kszucs authored Apr 25, 2024
1 parent 68dfe39 commit 02c6607
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 7 deletions.
14 changes: 9 additions & 5 deletions ibis/common/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

from __future__ import annotations

import itertools
from abc import abstractmethod
from collections import deque
from collections.abc import Iterable, Iterator, KeysView, Mapping, Sequence
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union

from ibis.common.bases import Hashable
from ibis.common.collections import frozendict
from ibis.common.patterns import NoMatch, Pattern
from ibis.common.typing import _ClassInfo
from ibis.util import experimental, promote_list
Expand Down Expand Up @@ -66,8 +66,9 @@ def _flatten_collections(node: Any) -> Iterator[N]:
yield item
elif isinstance(item, (tuple, list)):
yield from _flatten_collections(item)
elif isinstance(item, (dict, frozendict)):
yield from _flatten_collections(item.values())
elif isinstance(item, dict):
items = itertools.chain.from_iterable(item.items())
yield from _flatten_collections(items)


def _recursive_lookup(obj: Any, dct: dict) -> Any:
Expand All @@ -89,6 +90,7 @@ def _recursive_lookup(obj: Any, dct: dict) -> Any:
Examples
--------
>>> from ibis.common.collections import frozendict
>>> from ibis.common.grounds import Concrete
>>> from ibis.common.graph import Node
>>>
Expand Down Expand Up @@ -117,8 +119,10 @@ def _recursive_lookup(obj: Any, dct: dict) -> Any:
return dct.get(obj, obj)
elif isinstance(obj, (tuple, list)):
return tuple(_recursive_lookup(o, dct) for o in obj)
elif isinstance(obj, (dict, frozendict)):
return {k: _recursive_lookup(v, dct) for k, v in obj.items()}
elif isinstance(obj, dict):
return {
_recursive_lookup(k, dct): _recursive_lookup(v, dct) for k, v in obj.items()
}
else:
return obj

Expand Down
12 changes: 10 additions & 2 deletions ibis/common/tests/test_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def copy(self, name=None, children=None):
E = MyNode(name="E", children=[])
B = MyNode(name="B", children=[D, E])
A = MyNode(name="A", children=[B, C])
F = MyNode(name="F", children=[{C: D, E: None}])


def test_bfs():
Expand All @@ -68,8 +69,8 @@ def test_construction():


def test_graph_nodes():
g = Graph(A)
assert g.nodes() == {A, B, C, D, E}
assert Graph(A).nodes() == {A, B, C, D, E}
assert Graph(F).nodes() == {F, C, D, E}


def test_graph_repr():
Expand Down Expand Up @@ -286,6 +287,10 @@ def test_flatten_collections():
)
assert list(result) == [A, C, D]

# test that dictionary keys are also flattened
result = _flatten_collections([0.0, {A: B, C: [D]}, frozendict({E: 6})])
assert list(result) == [A, B, C, D, E]


def test_recursive_lookup():
results = {A: "A", B: "B", C: "C", D: "D"}
Expand All @@ -312,6 +317,9 @@ def test_recursive_lookup():
my_map,
)

# test that dictionary nodes as dictionary keys are also looked up
assert _recursive_lookup({A: B, C: D}, results) == {"A": "B", "C": "D"}


def test_coerce_finder():
f = _coerce_finder(int)
Expand Down

0 comments on commit 02c6607

Please sign in to comment.