Source code for geocode.field.base_tree

"""Base tree component."""
from typing import Self
from weakref import ref
import pandas as pd
from anytree import Node, RenderTree, AsciiStyle, Resolver, PreOrderIter, find_by_attr

from .base_component import BaseComponent

[docs] class BaseTreeNode(Node): """Tree's node. Parameters ---------- name : str Name of the node. key : str, optional Key used to query node data. is_group : bool, optional Should a node represet a group of nodes. Default False. """ def __init__(self, name, key=None, is_group=False, **kwargs): super().__init__(name, **kwargs) self._key = key self._is_group = is_group def __getattr__(self, attr) -> pd.DataFrame | None: if attr.startswith('_'): raise AttributeError(attr) data = getattr(self.root.component(), attr) if self.is_root: return data return None if data is None else data[data[self.key] == self.name] def __contains__(self, x: str): try: return getattr(self, x) is not None except AttributeError: return False @property def is_group(self): """Check that node is a group of nodes.""" return self._is_group @property def key(self): """Node's type.""" return self._key @property def fullname(self): """Full name from root.""" return self.separator.join([node.name for node in self.path[1:]])
class IterableTree: """Tree iterator excluding group nodes.""" def __init__(self, root): self.iter = PreOrderIter(root) def __next__(self): x = next(self.iter) if x.is_group: return next(self) return x def __iter__(self) -> Self: return self
[docs] class BaseTree(BaseComponent): """Base tree component. Contains nodes and groups in a single tree structure. Parameters ---------- node : TreeSegment, optional Root node for the tree. """ def __init__(self, root=None, **kwargs): super().__init__(**kwargs) self._root = root if root is not None else BaseTreeNode(name='root') self._root.component = ref(self) self._resolver = Resolver() @property def root(self): """Tree root.""" return self._root @property def resolver(self): """Tree resolver.""" return self._resolver @property def names(self): """List of node names excluding group nodes.""" return [node.name for node in self] def __getitem__(self, key): node = find_by_attr(self.root, key) if node is None: raise KeyError(key) return node def __iter__(self): return IterableTree(self.root)
[docs] def glob(self, name): """Return instances at ``name`` supporting wildcards.""" return self.resolver.glob(self.root, name)
[docs] def render_tree(self): """Print tree structure.""" print(RenderTree(self.root, style=AsciiStyle()).by_attr()) return self
[docs] def build_tree(self): """Build tree from component's data.""" raise NotImplementedError()