@@ -101,33 +101,24 @@ def _clone_init(self, deep):
101101 """
102102 return self .__class__ ()
103103
104- def _clone_nodes_with_hierarchy (self , new_tree , deep , new_root = None ):
105- """Clone nodes and node hierarchies from current tree to new tree."""
106- self ._validate_tree_insertion (new_tree )
107- if new_root is not None :
108- self ._ensure_present (new_root )
109- else :
110- new_root = self .root
111- for node in self .expand_tree (new_root , id_only = False ):
112- new_tree ._nodes_map [node .identifier ] = deepcopy (node ) if deep else node
113- new_tree ._nodes_parent [node .identifier ] = self ._nodes_parent [
114- node .identifier
115- ]
116- new_tree ._nodes_children [node .identifier ] = set (
117- self ._nodes_children [node .identifier ]
118- )
119-
120- new_tree .root = new_root
121- new_tree ._nodes_parent [new_root ] = None
122- return new_tree
123-
124104 def clone (self , with_tree = True , deep = False , new_root = None ):
125105 """Clone current instance, with or without tree.
126106 :rtype: :class:`ltree.Tree`
127107 """
128108 new_tree = self ._clone_init (deep )
129- if with_tree :
130- self ._clone_nodes_with_hierarchy (new_tree , new_root = new_root , deep = deep )
109+ if not with_tree :
110+ return new_tree
111+ if new_root is None :
112+ new_tree .insert (self , deep = deep )
113+ return new_tree
114+
115+ for nid in self .expand_tree (nid = new_root ):
116+ node = self .get (nid )
117+ if deep :
118+ node = deepcopy (node )
119+ pid = None if nid == self .root or nid == new_root else self .parent (nid )
120+ # with_children only makes sense when using "node hierarchy" syntax
121+ new_tree .insert_node (node , parent_id = pid , with_children = False )
131122 return new_tree
132123
133124 def parent (self , nid , id_only = True ):
@@ -219,41 +210,45 @@ def insert(
219210 '"item" parameter must either be a Node, or a Tree, got <%s>.' % type (item )
220211 )
221212
222- def insert_node (self , node , parent_id = None , child_id = None , deep = False ):
213+ def insert_node (
214+ self , node , parent_id = None , child_id = None , deep = False , with_children = True
215+ ):
223216 self ._validate_node_insertion (node )
224217 node = deepcopy (node ) if deep else node
225218 if parent_id is not None and child_id is not None :
226219 raise ValueError ('Can declare at most "parent_id" or "child_id"' )
227- if parent_id is None and child_id is None :
228- self ._insert_node_at_root (node )
229- return self
230- if parent_id is not None :
231- self ._insert_node_below (node , parent_id = parent_id )
220+ if child_id is not None :
221+ self ._insert_node_above (node , child_id = child_id )
232222 return self
233- self ._insert_node_above (node , child_id = child_id )
223+ self ._insert_node_below (node , parent_id = parent_id , with_children = with_children )
234224 return self
235225
236- def _insert_node_at_root (self , node ):
237- if not self .is_empty ():
238- raise MultipleRootError ("A tree takes one root merely." )
239- self .root = node .identifier
240- self ._nodes_map [node .identifier ] = node
226+ def _insert_node_below (self , node , parent_id , with_children = True ):
227+ # insertion at root
228+ if parent_id is None :
229+ if not self .is_empty ():
230+ raise MultipleRootError ("A tree takes one root merely." )
231+ self .root = node .identifier
232+ self ._nodes_map [node .identifier ] = node
233+ if with_children and hasattr (node , "_children" ):
234+ for child in node ._children or []:
235+ self .insert (child , parent_id = node .identifier )
236+ return
241237
242- def _insert_node_below (self , node , parent_id ):
243238 self ._ensure_present (parent_id )
244239 node_id = node .identifier
245240 self ._nodes_map [node_id ] = node
246241 self ._nodes_parent [node_id ] = parent_id
247242 self ._nodes_children [parent_id ].add (node_id )
243+ if with_children and hasattr (node , "_children" ):
244+ for child in node ._children or []:
245+ self .insert (child , parent_id = node .identifier )
248246
249247 def _insert_node_above (self , node , child_id ):
250248 self ._ensure_present (child_id )
251249 parent_id = self .parent (child_id )
252250 child_subtree = self .drop_subtree (child_id )
253- if parent_id is None :
254- self ._insert_node_at_root (node )
255- else :
256- self ._insert_node_below (node , parent_id )
251+ self ._insert_node_below (node , parent_id )
257252 self ._insert_tree_below (child_subtree , node .identifier , False )
258253
259254 def insert_tree (
@@ -264,34 +259,32 @@ def insert_tree(
264259 return self
265260 if parent_id is not None and child_id is not None :
266261 raise ValueError ('Can declare at most "parent_id" or "child_id"' )
267- if parent_id is None and child_id is None :
268- self ._insert_tree_at_root (new_tree , deep = deep )
269- return self
270- if parent_id is not None :
271- self ._insert_tree_below (new_tree , parent_id = parent_id , deep = deep )
272- return self
273- self ._insert_tree_above (
274- new_tree , child_id = child_id , child_id_below = child_id_below , deep = deep
275- )
276- return self
277-
278- def _insert_tree_at_root (self , new_tree , deep ):
279- # replace tree, allowed only if initial tree is empty
280- if not self .is_empty ():
281- raise MultipleRootError ("A tree takes one root merely." )
282- new_tree ._clone_nodes_with_hierarchy (self , deep = deep )
262+ if child_id is not None :
263+ return self ._insert_tree_above (
264+ new_tree , child_id = child_id , child_id_below = child_id_below , deep = deep
265+ )
266+ return self ._insert_tree_below (new_tree , parent_id = parent_id , deep = deep )
283267
284268 def _insert_tree_below (self , new_tree , parent_id , deep ):
269+ if parent_id is None :
270+ # insertion at root requires tree to be empty
271+ if not self .is_empty ():
272+ raise MultipleRootError ("A tree takes one root merely." )
273+ else :
274+ self ._ensure_present (parent_id )
285275 self ._validate_tree_insertion (new_tree )
286- self ._ensure_present (parent_id )
287276
288277 if new_tree .is_empty ():
289278 return self
290279
291280 for new_nid in new_tree .expand_tree ():
292281 node = new_tree .get (new_nid )
293282 pid = parent_id if new_nid == new_tree .root else new_tree .parent (new_nid )
294- self .insert_node (deepcopy (node ) if deep else node , parent_id = pid )
283+ # with_children only makes sense when using "node hierarchy" syntax
284+ self .insert_node (
285+ deepcopy (node ) if deep else node , parent_id = pid , with_children = False
286+ )
287+ return self
295288
296289 def _insert_tree_above (self , new_tree , child_id , child_id_below , deep ):
297290 # make all checks before modifying tree
@@ -309,11 +302,7 @@ def _insert_tree_above(self, new_tree, child_id, child_id_below, deep):
309302 child_id_below = new_tree_leaves .pop ()
310303 parent_id = self .parent (child_id )
311304 child_subtree = self .drop_subtree (child_id )
312- if parent_id is None :
313- self ._insert_tree_at_root (new_tree , deep )
314- else :
315- self ._insert_tree_below (new_tree , parent_id , deep )
316-
305+ self ._insert_tree_below (new_tree , parent_id , deep )
317306 self ._insert_tree_below (child_subtree , child_id_below , False )
318307
319308 def _drop_node (self , nid ):
@@ -360,6 +349,7 @@ def expand_tree(
360349 nid = None ,
361350 mode = "depth" ,
362351 filter_ = None ,
352+ filter_through = False ,
363353 key = None ,
364354 reverse = False ,
365355 id_only = True ,
@@ -371,8 +361,8 @@ def expand_tree(
371361
372362 :param nid: Node identifier from which tree traversal will start. If None tree root will be used
373363 :param mode: Traversal mode, may be either "depth" or "width"
374- :param filter_: filter function performed on nodes. Node excluded from filter function nor their children
375- won 't be yielded by generator .
364+ :param filter_: filter function performed on nodes. Node excluded from filter function won't be yielded.
365+ :param filter_through: if True, excluded nodes don 't exclude their children .
376366 :param reverse: the ``reverse`` param for sorting :class:`Node` objects in the same level
377367 :param key: key used to order nodes of same parent
378368 :return: node ids that satisfy the conditions if ``id_only`` is True, else nodes.
@@ -384,23 +374,26 @@ def expand_tree(
384374 key = attrgetter ("identifier" ) if key is None else key
385375 if nid is not None :
386376 node = self .get (nid )
387- if filter_ is None or filter_ (node ):
377+ filter_pass_node = filter_ is None or filter_ (node )
378+ if filter_pass_node :
388379 yield nid if id_only else node
380+ if filter_pass_node or filter_through :
389381 queue = [
390382 child_node
391383 for child_node in self .children (nid , id_only = False )
392- if filter_ is None or filter_ (child_node )
384+ if filter_ is None or filter_through or filter_ (child_node )
393385 ]
394386 queue .sort (key = key , reverse = reverse )
395387 while queue :
396388 current_node = queue .pop (0 )
397- yield current_node .identifier if id_only else current_node
389+ if filter_ is None or filter_ (current_node ):
390+ yield current_node .identifier if id_only else current_node
398391 expansion = [
399392 gchild_node
400393 for gchild_node in self .children (
401394 current_node .identifier , id_only = False
402395 )
403- if filter_ is None or filter_ (gchild_node )
396+ if filter_ is None or filter_through or filter_ (gchild_node )
404397 ]
405398 expansion .sort (key = key , reverse = reverse )
406399 if mode == "depth" :
@@ -549,8 +542,7 @@ def merge(self, new_tree, nid=None, deep=False):
549542 )
550543
551544 if self .is_empty ():
552- new_tree ._clone_nodes_with_hierarchy (new_tree = self , deep = deep )
553- return self
545+ return self ._insert_tree_below (new_tree , parent_id = None , deep = deep )
554546
555547 nid = self ._ensure_present (nid , defaults_to_root = True )
556548
0 commit comments