1111 cast ,
1212 Dict ,
1313 Any ,
14+ Generic ,
15+ TypeVar ,
16+ Iterator ,
1417)
1518from collections import defaultdict
1619from operator import itemgetter
2225
2326# root has no key (None), keyed node has children with str keys, unkeyed node has children with int keys
2427Key = Union [None , str , int ]
25- KeyedNode = Tuple [Key , Node ]
2628KeyedTree = Tuple [Key , "Tree" ]
2729Path = str
2830
31+ GenericNode = TypeVar ("GenericNode" , bound = Node )
32+ KeyedNode = Tuple [Key , GenericNode ]
2933
30- class Tree (object ):
34+
35+ class Tree (Generic [GenericNode ]):
3136
3237 """Principles:
3338 - each node is identified by an id
@@ -49,7 +54,7 @@ def __init__(self, path_separator: str = ".") -> None:
4954 # nodes references and hierarchy in tree
5055 self .root : Optional [NodeId ] = None
5156 # node identifier -> node
52- self ._nodes_map : Dict [NodeId , Node ] = {}
57+ self ._nodes_map : Dict [NodeId , GenericNode ] = {}
5358 # node identifier -> parent node identifier
5459 self ._nodes_parent : Dict [NodeId , Optional [NodeId ]] = defaultdict (lambda : None )
5560 # "map" node identifier -> map of children nodes identifier -> key
@@ -61,10 +66,6 @@ def __contains__(self, identifier: NodeId) -> bool:
6166 return identifier in self ._nodes_map
6267
6368 def get (self , nid : NodeId ) -> KeyedNode :
64- """Get a node by its id.
65- :param nid: str, identifier of node to fetch
66- :rtype: lighttree.node.Node
67- """
6869 self ._ensure_present (nid )
6970 return self .get_key (nid ), self ._nodes_map [nid ]
7071
@@ -128,13 +129,12 @@ def list(
128129 self ,
129130 id_in : Optional [Sequence [NodeId ]] = None ,
130131 depth_in : Optional [Sequence [int ]] = None ,
131- filter_ : Optional [Callable [[Node ], bool ]] = None ,
132+ filter_ : Optional [Callable [[GenericNode ], bool ]] = None ,
132133 ) -> List [KeyedNode ]:
133134 """List nodes.
134135 :param id_in: list of str, optional, filter nodes among provided identifiers
135136 :param depth_in: list of int, optional, filter nodes whose depth in tree is among provided values
136137 :param filter\_: function, optional, filtering function to apply to each node
137- :rtype: list of lighttree.node.Node
138138 """
139139 return [
140140 (self .get_key (nid ), node )
@@ -167,7 +167,7 @@ def _ensure_present(
167167 raise NotFoundNodeError ("Node id <%s> doesn't exist in tree" % nid )
168168 return nid
169169
170- def _validate_node_insertion (self , node : Node ) -> None :
170+ def _validate_node_insertion (self , node : GenericNode ) -> None :
171171 if node .identifier in self ._nodes_map .keys ():
172172 raise DuplicatedNodeError (
173173 "Can't create node with id '%s'" % node .identifier
@@ -330,24 +330,12 @@ def leaves_ids(self, nid: Optional[NodeId] = None) -> List[NodeId]:
330330
331331 def insert (
332332 self ,
333- item : Union [Node , "Tree" ],
333+ item : Union [GenericNode , "Tree" ],
334334 parent_id : Optional [NodeId ] = None ,
335335 child_id : Optional [NodeId ] = None ,
336336 child_id_below : Optional [NodeId ] = None ,
337337 key : Key = None ,
338338 ) -> "Tree" :
339- if isinstance (item , Node ):
340- if child_id_below is not None :
341- raise ValueError (
342- '"child_id_below" parameter is reserved to Tree insertion.'
343- )
344- self .insert_node (
345- node = item ,
346- parent_id = parent_id ,
347- child_id = child_id ,
348- key = key ,
349- )
350- return self
351339 if isinstance (item , Tree ):
352340 self .insert_tree (
353341 new_tree = item ,
@@ -357,13 +345,22 @@ def insert(
357345 key = key ,
358346 )
359347 return self
360- raise ValueError (
361- '"item" parameter must either be a Node, or a Tree, got <%s>.' % type (item )
348+ # item is GenericNode
349+ if child_id_below is not None :
350+ raise ValueError (
351+ '"child_id_below" parameter is reserved to Tree insertion.'
352+ )
353+ self .insert_node (
354+ node = item ,
355+ parent_id = parent_id ,
356+ child_id = child_id ,
357+ key = key ,
362358 )
359+ return self
363360
364361 def insert_node (
365362 self ,
366- node : Node ,
363+ node : GenericNode ,
367364 parent_id : Optional [NodeId ] = None ,
368365 child_id : Optional [NodeId ] = None ,
369366 key : Key = None ,
@@ -386,7 +383,7 @@ def insert_node(
386383
387384 def _insert_node_below (
388385 self ,
389- node : Node ,
386+ node : GenericNode ,
390387 parent_id : Optional [NodeId ],
391388 key : Key ,
392389 ) -> None :
@@ -433,7 +430,7 @@ def _insert_node_below(
433430 self ._nodes_map [node_id ] = node
434431 self ._nodes_parent [node_id ] = parent_id
435432
436- def _insert_node_above (self , node : Node , child_id : NodeId , key : Key ) -> None :
433+ def _insert_node_above (self , node : GenericNode , child_id : NodeId , key : Key ) -> None :
437434 self ._ensure_present (child_id )
438435 # get parent_id before dropping subtree
439436 try :
@@ -601,10 +598,10 @@ def expand_tree(
601598 self ,
602599 nid : Optional [NodeId ] = None ,
603600 mode : str = "depth" ,
604- filter_ : Optional [Callable [[Union [None , str , int ], Node ], bool ]] = None ,
601+ filter_ : Optional [Callable [[Union [None , str , int ], GenericNode ], bool ]] = None ,
605602 filter_through : bool = False ,
606603 reverse : bool = False ,
607- ) -> Iterable [KeyedNode ]:
604+ ) -> Iterator [KeyedNode ]:
608605 """Python generator traversing the tree (or a subtree) with optional node filtering.
609606
610607 Inspired by treelib implementation https://github.com/caesar0301/treelib/blob/master/treelib/tree.py#L374
@@ -615,7 +612,6 @@ def expand_tree(
615612 :param filter_through: if True, excluded nodes don't exclude their children.
616613 :param reverse: the ``reverse`` param for sorting :class:`Node` objects in the same level
617614 :return: node ids that satisfy the conditions if ``id_only`` is True, else nodes.
618- :rtype: generator
619615 """
620616 if mode not in ("depth" , "width" ):
621617 raise NotImplementedError ("Traversal mode '%s' is not supported" % mode )
@@ -656,7 +652,7 @@ def expand_tree(
656652 def show (
657653 self ,
658654 nid : Optional [NodeId ] = None ,
659- filter_ : Optional [Callable [[Node ], bool ]] = None ,
655+ filter_ : Optional [Callable [[GenericNode ], bool ]] = None ,
660656 display_key : bool = True ,
661657 reverse : bool = False ,
662658 line_type : str = "ascii-ex" ,
@@ -676,8 +672,6 @@ def show(
676672 :param limit: int, truncate tree display to this number of lines
677673 :param kwargs: kwargs params passed to node ``line_repr`` method
678674 :param line_max_length
679- :rtype: unicode in python2, str in python3
680-
681675 """
682676 output = ""
683677
@@ -714,10 +708,10 @@ def show(
714708 def _iter_nodes_with_location (
715709 self ,
716710 nid : Optional [NodeId ],
717- filter_ : Optional [Callable [[Node ], bool ]],
711+ filter_ : Optional [Callable [[GenericNode ], bool ]],
718712 reverse : bool ,
719713 is_last_list : Optional [List [bool ]] = None ,
720- ) -> Iterable [Tuple [Tuple [bool , ...], Key , Node ]]:
714+ ) -> Iterable [Tuple [Tuple [bool , ...], Key , GenericNode ]]:
721715 """Yield nodes with information on how they are placed.
722716 :param nid: starting node identifier
723717 :param filter_: filter function applied on nodes
0 commit comments