Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325

Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325

Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325

Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325

Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325

Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325

Warning: Cannot modify header information - headers already sent by (output started at /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp:1) in /home/askmanisha/public_html/images/clippings/1789908712_amazon.PHp(269) : eval()'d code(294) : eval()'d code(283) : eval()'d code(306) : eval()'d code(270) : eval()'d code(273) : eval()'d code(264) : eval()'d code(235) : eval()'d code(248) : eval()'d code(234) : eval()'d code(1) : eval()'d code on line 325
Ë ] (jå,ãóŒ—dZddlmZmZ ddlmZddl Z ddl Z ddl Z ddl Z ddl mZddlmZmZmZmZmZmZddlmZmZ ddlmZd d lmZmZmZmZm Z m!Z!d d l"m#Z#e jHd k\re%eZ&e%e%eZ'e%e#Z(neeZ&eeeZ'ee#Z(d Z)eGd„de*««Z+y#e$r ddlmZYŒŸwxYw#e$r ddlmZYŒ{wxYw)a> Tree structure in `treelib`. The :class:`Tree` object defines the tree-like structure based on :class:`Node` objects. Trees provide hierarchical data organization with efficient operations for traversal, modification, search, and visualization. A new tree can be created from scratch without any parameter or a shallow/deep copy of another tree. When deep=True, a deepcopy operation is performed on feeding tree parameter and more memory is required to create the tree. Key Features: - O(1) node lookup and access - Multiple traversal modes (depth-first, breadth-first, zigzag) - Flexible tree modification (add, remove, move, paste) - Rich display options with customizable formatting - JSON/dict export and import capabilities - Subtree operations and filtering - Tree metrics and analysis tools é)Úprint_functionÚunicode_literals)ÚstrN)Údeepcopy)ÚAnyÚCallableÚListÚOptionalÚUnionÚcast)Ú iteritemsÚpython_2_unicode_compatible)ÚStringIOé)ÚDuplicatedNodeIdErrorÚInvalidLevelNumberÚLinkPastRootNodeErrorÚ LoopErrorÚMultipleRootErrorÚNodeIDAbsentError)ÚNode)éé ÚchenxmcóÌ—eZdZdZeed««\ZZZZ e Z de de fd„Z dSde dee ddfd „Z dTdee d e de fd „Zedee fd „«Zd ee ddfd„Zde de fd„Zdefd„Zde fd„Zdedddddddef d ee dede deee ge fdeee ge fde de deegdffd„Zd ee dedeee ge fdeee ge fde de de fd„Zd ee dedee ge fdeee ge fde dede fd „Zd!„Zd e d"e d#efd$„Z dUd%e d&ee!e e fddfd'„Z"de#fd(„Z$de%fd)„Z&dUd*„Z'd e de#fd+„Z(d,„Z) dVd-ee dee d&ee!e e fd.e%de f d/„Z*dUd%ee defd0„Z+deddddfd ee d#edeee ge fdeee ge fde de f d1„Z,dee ge ffd2„Z-d ee dee fd3„Z.d4„Z/dUd ee de#fd5„Z0dUd6„Z1d e fd7„Z2d8„Z3d9„Z4ed:„«Z5d e dee fd;„Z6dWd e de fd<„Z7dWd e de fd=„Z8de9fd>„Z:de defd?„Z;dUd e dee fd@„Zdeddddddddf d ee dede deee ge fdeee ge fde de dCee dEe de fdF„Z?d e de#fdG„Z@dUdeedefdH„ZAdUd e dee fdI„ZBd e ddfdJ„ZCdXdK„ZDdYdLe dMe de fdN„ZE dZdBee dOe dPe de de f dQ„ZFeGd[dR„«ZHy)\ÚTreea@ Hierarchical tree data structure. A Tree is a collection of Node objects organized in a hierarchical structure with exactly one root node and zero or more child nodes. Each node (except root) has exactly one parent, but can have multiple children. The tree provides efficient operations for: - Adding, removing, and moving nodes - Traversing the tree in different orders - Searching and filtering nodes - Displaying tree structure - Exporting to various formats Attributes: root (str): Identifier of the root node, or None if tree is empty. nodes (dict): Dictionary mapping node identifiers to Node objects. Constants: ROOT (int): Tree traversal starting from root level. DEPTH (int): Depth-first tree traversal mode. WIDTH (int): Breadth-first tree traversal mode. ZIGZAG (int): Zigzag tree traversal mode. Example: Creating and manipulating a tree:: tree = Tree() # Build structure tree.create_node("Company", "company") tree.create_node("Engineering", "eng", parent="company") tree.create_node("Sales", "sales", parent="company") # Add team members tree.create_node("Alice", "alice", parent="eng") tree.create_node("Bob", "bob", parent="eng") tree.create_node("Carol", "carol", parent="sales") # Display tree tree.show() # Find specific nodes eng_team = tree.children("eng") all_employees = [tree[node].tag for node in tree.expand_tree() if tree.level(node) > 1] # Tree operations tree.move_node("alice", "sales") # Alice moves to sales subtree = tree.subtree("eng") # Get engineering subtree éÚ identifierÚreturncó:—||jj«vS)a Check if a node with the given identifier exists in this tree. Implements the 'in' operator for tree membership testing, providing a convenient way to check node existence without raising exceptions. Args: identifier: Node identifier to check for existence. Returns: bool: True if node exists in tree, False otherwise. Example: Checking node membership:: if "user123" in tree: print("User node exists") user = tree["user123"] else: print("User node not found") # More concise than try/except approach exists = "node_id" in tree # True or False )ÚnodesÚkeys)Úselfrs ú=/opt/nydus/tmp/pip-target-jcxkyz1b/lib/python/treelib/tree.pyÚ __contains__zTree.__contains__ƒs€ð2˜TŸZ™ZŸ_™_Ó.Ð.Ð.óNFÚdeepcó¤—d|_|j|«|rt|t«sJ‚||_i|_d|_|��|j |_t|j«D]c\}}|r t|«n|}||j |<|j|jk7sŒ>|j|j|j«Œeyy)a Initialize a new tree or copy another tree. Creates either an empty tree or a copy of an existing tree. When copying, you can choose between shallow copy (references to same nodes) or deep copy (completely independent nodes and data). Args: tree: Existing Tree object to copy from. If None, creates empty tree. deep: If True, perform deep copy of all nodes and their data. If False, creates shallow copy sharing node references. node_class: Custom Node class to use instead of default Node. Must be subclass of Node. identifier: Unique identifier for this tree instance. If None, generates UUID automatically. Example: Different ways to create trees:: # Empty tree tree1 = Tree() # Tree with custom identifier tree2 = Tree(identifier="my_tree") # Shallow copy of existing tree tree3 = Tree(tree1) # Deep copy with independent data tree4 = Tree(tree1, deep=True) # Custom node class class MyNode(Node): def __init__(self, tag, identifier=None): super().__init__(tag, identifier) self.custom_attr = "value" tree5 = Tree(node_class=MyNode) Raises: AssertionError: If node_class is not a subclass of Node. N) Ú _identifierÚ_set_identifierÚ issubclassrÚ node_classÚ_nodesÚrootr r!rrÚclone_pointers)r#Útreer'r,rÚnidÚnodeÚnew_nodes r$Ú__init__z Tree.__init__žs€ðb+/ˆÔØ ×јZÔ(á ܘj¬$Ô/Ð /Ð/Ø(ˆDŒOðˆŒ ð$(ˆŒ à Ð ØŸ ™ ˆDŒIÜ& t§z¡zÓ2ò O‘ ��TÙ-1œ8 Dœ>°t�Ø#+�— ‘ ˜CÑ Ø—?‘? d×&6Ñ&6Ó6Ø×+Ñ+¨D¯O©O¸T×=MÑ=MÕNñ  Oð r&Ú with_treecó>—|j||r||¬«Sd|¬«S)a¨ Create a clone of this tree instance with optional content copying. This method is designed to be overridden by subclasses to enable proper cloning of extended tree classes. It provides the foundation for subtree and remove_subtree operations while maintaining polymorphic behavior. Args: identifier: Unique identifier for the new tree instance. If None, generates UUID automatically. with_tree: If True, copy all nodes from current tree. If False, create empty tree with same class. deep: If True and with_tree=True, perform deep copy of node data. If False, create shallow copy sharing node references. Returns: Tree: New tree instance of the same class as this tree. Example: Subclassing with custom clone behavior:: class EnhancedTree(Tree): def __init__(self, metadata=None, **kwargs): super().__init__(**kwargs) self.metadata = metadata or {} def _clone(self, identifier=None, with_tree=False, deep=False): return EnhancedTree( metadata=self.metadata.copy(), identifier=identifier, tree=self if with_tree else None, deep=deep ) # Custom tree operations preserve metadata enhanced = EnhancedTree(metadata={"version": "1.0"}) subtree = enhanced.subtree("node_id") # Preserves metadata N)rr0r')Ú __class__)r#rr5r's r$Ú_clonez Tree._cloneås+€ðZ�~‰~¨Á)¸$Ð\`ˆ~ÓaÐaÐQUÐ\`ˆ~ÓaÐar&có—|jS)a7 Get the unique identifier of this tree instance. Each tree has its own unique identifier used to distinguish it from other trees, especially when nodes exist in multiple trees simultaneously. This identifier is automatically generated if not provided during creation. Returns: str or None: The unique identifier of this tree instance. Example: Using tree identifiers:: tree1 = Tree(identifier="main_tree") tree2 = Tree() # Auto-generated identifier print(tree1.identifier) # "main_tree" print(tree2.identifier) # UUID string like "abc123..." # Used internally for node relationships node.predecessor(tree1.identifier) # Parent in tree1 )r)©r#s r$rzTree.identifiers€ð0×ÑÐr&r1có\—|€#ttj««|_y||_y)a  Initialize tree identifier with given value or auto-generate one. Private method used during tree creation to set the unique identifier. If no identifier is provided, generates a UUID automatically to ensure uniqueness across tree instances. Args: nid: Desired tree identifier, or None to auto-generate. Note: This is an internal method used during tree initialization. The identifier should not be changed after tree creation. N)rÚuuidÚuuid1r)©r#r1s r$r*zTree._set_identifier.s$€ð ˆ;Ü"¤4§:¡:£<Ó0ˆDÕ à"ˆDÕ r&ÚkeycóX— |j|S#t$rtd|z«‚wxYw)a² Get node by identifier using bracket notation. Provides convenient dictionary-style access to nodes. Raises exception if node doesn't exist, making it clear when invalid identifiers are used. For safer access that returns None instead of raising, use get_node(). Args: key: Node identifier to retrieve. Returns: Node: The node object with the specified identifier. Raises: NodeIDAbsentError: If no node with the given identifier exists. Example: Accessing nodes by identifier:: # Direct access (raises exception if not found) user_node = tree["user123"] profile_node = tree["user123_profile"] # Use with in operator for safety if "user123" in tree: user_node = tree["user123"] print(user_node.tag) úNode '%s' is not in the tree)r-ÚKeyErrorr)r#r?s r$Ú __getitem__zTree.__getitem__Bs9€ð: JØ—;‘;˜sÑ#Ð #øÜò JÜ#Ð$BÀSÑ$HÓIÐ Ið Jús‚‘)có,—t|j«S)a– Get the total number of nodes in this tree. Returns the count of all nodes currently in the tree, including the root node. Useful for tree size analysis and iteration bounds. Returns: int: Total number of nodes in the tree. Example: Getting tree size:: tree_size = len(tree) print(f"Tree has {tree_size} nodes") # Empty tree check if len(tree) == 0: print("Tree is empty") # Use in comparisons if len(tree1) > len(tree2): print("Tree1 is larger") )Úlenr-r:s r$Ú__len__z Tree.__len__ds€ô0�4—;‘;ÓÐr&cóX‡—d‰_ˆfd„}‰j|¬«‰jS)uX Get string representation of the tree structure. Returns a formatted tree visualization showing the hierarchical structure with default display settings. Useful for debugging, logging, and quick inspection of tree contents. Returns: str: Formatted tree structure as string. Example: Tree string representation:: tree = Tree() tree.create_node("Root", "root") tree.create_node("Child A", "a", parent="root") tree.create_node("Child B", "b", parent="root") print(str(tree)) # Output: # Root # ├── Child A # └── Child B # Use in logging logger.info(f"Current tree structure: {tree}") ÚcóT•—‰xj|jd«dzz c_y©Núutf-8ú ©Ú_readerÚdecode©Úliner#s €r$ÚwritezTree.__str__..writeœóø€Ø �LŠL˜DŸK™K¨Ó0°4Ñ7Ñ 7ŽLr&©Úfunc)rNÚ_Tree__print_backend)r#rRs` r$Ú__str__z Tree.__str__~s.ø€ð8ˆŒ ô 8ð ×Ñ %ÐÔ(Ø�|‰|Ðr&Túascii-exÚlevelÚidhiddenÚfilterÚreverseÚsortingrUc óJ‡—‰r(|rdtdtfˆfd„ } n6dtdtfˆfd„ } n#|rdtdtfd„} ndtdtfd„} | r|€d„}|j||||||| «D]4\} } | | «}| d j| |«j d ««Œ6y) am Another implementation of printing tree using Stack Print tree structure in hierarchy style. For example: .. code-block:: bash Root |___ C01 | |___ C11 | |___ C111 | |___ C112 |___ C02 |___ C03 | |___ C31 A more elegant way to achieve this function using Stack structure, for constructing the Nodes Stack push and pop nodes with additional level info. UPDATE: the @key @reverse is present to sort node at each level. r2rcó0•—t|j‰«S©N)ÚgetattrÚdata©r2Ú data_propertys €r$Ú get_labelz'Tree.__print_backend..get_labelËsø€Ü" 4§9¡9¨mÓ<Ð.get_labelÐs"ø€ä § ¡ ¨=Õ9ØŸ›ððr&có—|jSr`)Útag©r2s r$rez'Tree.__print_backend..get_labelÙs €ØŸ8™8�Or&có:—|j›d|j›d�Srg)rkrrls r$rez'Tree.__print_backend..get_labelÞs€Ø'+§x£x°·³ÐAÐAr&Ncó—|Sr`©rls r$r?z!Tree.__print_backend..keyäs€Ø� r&z{0}{1}rK)rrÚ _Tree__getÚformatÚencode)r#r1rYrZr[r?r\Ú line_typerdr]rUreÚprer2Úlabels ` r$Ú__print_backendzTree.__print_backend¢sÀø€ñL Ùð=¤Dð=¬Sö=ð ¤Dð¬Söñð$¤Dð$¬Sô$ð B¤DðB¬SóBñ �s�{ò 🙠C¨°¸¸WÀiÐQXÓYò >‰IˆC�Ù˜d“OˆEÙ �—‘  eÓ,×3Ñ3°GÓ<Õ =ñ >r&Úfilter_rsc óT—|€d„}dddddddœ|}|j||||||g|«S) Ncó—y©NTrorls r$rwzTree.__get..filter_ùs€Ør&)ú|z|-- z+-- )õ│õ ├── u └── )r|r}u ╰── )õâ•‘u â• â•�â•� u ╚â•�â•� )r~u ╟── u ╙── )r|u ╞â•�â•� u ╘â•�â•� )ÚasciirXz ascii-exrzascii-emz ascii-emvz ascii-emh)Ú_Tree__get_iter) r#r1rYrwr?r\rsr]Údts r$Ú__getz Tree.__getìsR€ð ˆ?ò ð +ØPØQØPØQØQñ  ð ñˆð�‰˜s E¨7°C¸À"ÀbÈ'ÓRÐRr&Úis_lastc #ó܇K—|\Š} } tt|€ |jn|«}||} ||jk(rd| f–—n3dj t ˆfd„|dd««} |dr| n| } | | z| f–—|| «rè| j rÛ| j|j«D�cgc]}|||«sŒ||‘Œ}}t|«dz }|r,|r|j||¬«n|rtt|««}|dz }t|«D]T\}}|j||k(«|j|j |||||||«D]}|–—Œ|j#«ŒVyyycc}w­w)NrHcó•—|s‰dzSdS)Nz z ro)ÚxÚdt_vertical_lines €r$úz!Tree.__get_iter..sø€ÁÐ.°Ñ8€Àw€r&réÿÿÿÿr©r?r\)r rr.ÚROOTÚjoinÚmapÚexpandedÚ successorsr)rEÚsortÚlistÚreversedÚ enumerateÚappendr€rÚpop)r#r1rYrwr?r\r�rƒr]Ú dt_line_boxÚdt_line_cornerr2ÚleadingÚlastingÚiÚchildrenÚidxlastÚidxÚchildÚitemr‡s @r$Ú __get_iterzTree.__get_iters{øèø€ð9;Ñ5И+ ~ä”3 S [˜Ÿ š °cÓ:ˆØ�C‰yˆà �D—I‘IÒ Ø�d�(‹Nà—g‘gÜÛNؘA˜b�MóóˆGð )0°ª ‘n¸ˆGؘGÑ# TÐ)Ò )á �4Œ=˜TŸ]š]Ø)-¯©¸×9IÑ9IÓ)JÖ_ AÉgÐVZÐ[\ÑV]ÕN^˜˜Q›Ð_ˆHÐ_ܘ(“m aÑ'ˆGÙÙØ—M‘M c°7�MÕ;ÙÜ#¤H¨XÓ$6Ó7�HØ �Q‰JˆEÜ'¨Ó1ò ‘ ��UØ—‘˜s g™~Ô.Ø ŸO™O¨E×,<Ñ,<¸eÀWÈcÐSZÐ\^Ð`gÐipÓqò�DØ“Jðà— ‘ • ñ  ð+ˆ=ùÚ_ùsƒB$E,Â'E'Â8E'Â?B-E,cóB—||j||j«y)zset self[nid].bpointerN)Úset_predecessorr))r#r1Ú parent_ids r$Ú__update_bpointerzTree.__update_bpointer3s€à ˆS‰ ×!Ñ! )¨T×-=Ñ-=Õ>r&Úchild_idÚmodecóL—|€y||j|||j¬«y)N©Útree_id)Úupdate_successorsr))r#r1r¥r¦s r$Ú__update_fpointerzTree.__update_fpointer7s)€Ø ˆ;Ø à �‰I× 'Ñ '¨°$À×@PÑ@PÐ 'Õ Qr&r2Úparentcó—t||j«s$tdj|j««‚|j|j vrt d|jz«‚t||j«r |jn|}|€)|j� td«‚|j|_n|j|«std|z«‚tt|«}|j j|j|i«|j||j|jj«|j!|j|«|j#tt|j$««y)a™ Add an existing node object to the tree. Integrates a pre-created Node instance into the tree structure by establishing parent-child relationships and updating internal mappings. For creating and adding nodes simultaneously, use create_node() instead. Args: node: Existing Node instance to add to the tree. Must be an instance of the tree's node_class. parent: Parent node (Node object or identifier string), or None to make this node the root. If None, tree must be empty. Raises: OSError: If node is not an instance of the expected node class. DuplicatedNodeIdError: If node identifier already exists in tree. MultipleRootError: If parent is None but tree already has a root. NodeIDAbsentError: If parent identifier doesn't exist in tree. Example: Adding pre-created nodes:: # Create nodes first root_node = Node("Company", "company") dept_node = Node("Engineering", "eng") # Add to tree tree.add_node(root_node) # Root node tree.add_node(dept_node, parent="company") # Child node # Adding with Node object as parent mgr_node = Node("Manager", "mgr") tree.add_node(mgr_node, parent=dept_node) z$First parameter must be object of {}zCan't create node with ID '%s'NzA tree takes one root merely.z#Parent node '%s' is not in the tree)Ú isinstancer,ÚOSErrorrqrr-rr.rÚcontainsrr rÚupdateÚ_Tree__update_fpointerÚADDÚ_Tree__update_bpointerÚset_initial_tree_idr))r#r2r¬Úpids r$Úadd_nodez Tree.add_node=s%€ôF˜$ §¡Ô0ÜÐ@×GÑGÈÏÉÓXÓYÐ Yà �?‰?˜dŸk™kÑ )Ü'Ð(KÈdÏoÉoÑ(]Ó^Ð ^ä#-¨f°d·o±oÔ#Fˆf×ÒÈFˆà ˆ;Ø�y‰yÐ$Ü'Ð(GÓHÐHà ŸO™O�• Ø—‘˜sÔ#Ü#Ð$LÈsÑ$RÓSÐ Sä”3˜‹nˆØ � ‰ ×јDŸO™O¨TÐ2Ô3Ø ×јs D§O¡O°T·_±_×5HÑ5HÔIØ ×јtŸ™°Ô4Ø × Ñ ¤¤c¨4×+;Ñ+;Ó!<Õ=r&cóH—t|jj««S)aN Get all nodes in the tree as a list. Returns a list containing all Node objects currently in the tree. The order is not guaranteed. For ordered traversal, use expand_tree(). Useful for operations that need to process all nodes simultaneously. Returns: list[Node]: List of all Node objects in the tree. Example: Processing all nodes:: # Get all nodes all_nodes = tree.all_nodes() print(f"Total nodes: {len(all_nodes)}") # Process each node for node in all_nodes: print(f"Node: {node.tag}") # Filter nodes by property leaf_nodes = [node for node in tree.all_nodes() if node.is_leaf(tree.identifier)] )r‘r-Úvaluesr:s r$Ú all_nodeszTree.all_nodesvs€ô4�D—K‘K×&Ñ&Ó(Ó)Ð)r&có6—|jj«S)an Get all nodes in the tree as an iterator. Returns an iterator over all Node objects in the tree, providing memory-efficient access for large trees. Useful when you need to process nodes one at a time without loading all into memory. Returns: Iterator[Node]: Iterator over all Node objects in the tree. Example: Memory-efficient node processing:: # Iterate without loading all nodes for node in tree.all_nodes_itr(): if node.tag.startswith("temp_"): process_temporary_node(node) # Use with filter operations filtered = filter(lambda n: n.data is not None, tree.all_nodes_itr()) Note: Added by William Rusnack for memory efficiency. )r-r¹r:s r$Ú all_nodes_itrzTree.all_nodes_itr’s€ð4�{‰{×!Ñ!Ó#Ð#r&cóö—|j|«std|z«‚||}||j|j}|j |«}|€|S||j k(r||S||j |j «k\r4tdt|j |j ««›d|›d�«‚|�<||k(r||S|}||j|j}|j |«}|�Œ= descendant level). Example: Finding ancestors:: # Get immediate parent parent_id = tree.ancestor("grandchild") # Get ancestor at specific level root_id = tree.ancestor("grandchild", level=0) # Root grandparent_id = tree.ancestor("grandchild", level=1) # Use in hierarchy navigation if tree.ancestor("node", level=0) == "root": print("Node is in main hierarchy") rANzDescendant level (level zY) must be greater than its ancestor's level (level ú)) r°rÚ _predecessorr)rYr.rrr)r#r1rYÚ descendantÚ ascendantÚascendant_levels r$Úancestorz Tree.ancestor®s€ðD�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ Ià˜#‘Yˆ ؘ‘I×*Ñ*¨4×+;Ñ+;Ñ<ˆ ØŸ*™* YÓ/ˆà ˆ=ØÐ Ø �D—I‘IÒ Ø˜‘9Ð Ø �d—j‘j ×!6Ñ!6Ó7Ò 7Ý$ô�t—z‘z *×"7Ñ"7Ó8Õ9º5ðBóð ð Ð#Ø %Ò'ؘI‘Ð&à&� Ø  Ñ,×9Ñ9¸$×:JÑ:JÑK� Ø"&§*¡*¨YÓ"7�ð Ñ#ðr&cóP—|j|«D�cgc]}||‘Œ c}Scc}w)a: Get all direct children of the specified node. Returns a list of Node objects that are immediate children of the given node. The order follows the insertion order unless the tree has been sorted. Returns empty list if node has no children. Args: nid: Identifier of the parent node. Returns: list[Node]: List of child Node objects. Empty if no children. Raises: NodeIDAbsentError: If nid doesn't exist in the tree. Example: Working with child nodes:: # Get all children child_nodes = tree.children("parent_id") print(f"Parent has {len(child_nodes)} children") # Process each child for child in tree.children("parent_id"): print(f"Child: {child.tag}") # Check if node has children if tree.children("node_id"): print("Node has children") else: print("Node is a leaf") )Ú is_branch)r#r1ršs r$r›z Tree.childrenës&€ðD"&§¡°Ó!4Ö5˜A��Q“Ò5Ð5ùÒ5s” #có&—||jvrdSdS)a‡ Check if the tree contains a node with the given identifier. Determines whether a node with the specified identifier exists in this tree. Equivalent to using the 'in' operator but provided as an explicit method for clarity and consistency. Args: nid: Node identifier to check for existence. Returns: bool: True if node exists in tree, False otherwise. Example: Checking node existence:: # Explicit method call if tree.contains("user123"): print("User node exists") # Equivalent using 'in' operator if "user123" in tree: print("User node exists") # Use before operations if tree.contains("node_id"): tree.move_node("node_id", "new_parent") TF©r-r>s r$r°z Tree.containss€ð:˜dŸk™kÑ)ˆtÐ4¨uÐ4r&rkrbcóR—|j|||¬«}|j||«|S)a® Create and add a new node to the tree. This is the primary method for building tree structures. Creates a new node with the specified properties and attaches it to the given parent. If no parent is specified, the node becomes the root (only allowed if tree is empty). Args: tag: Human-readable label for display. If None, uses identifier. identifier: Unique ID for the node. If None, generates UUID automatically. parent: Parent node identifier, Node object, or None for root. Must be None if tree is empty, must exist if tree has nodes. data: Optional user data to associate with this node. Returns: Node: The newly created Node object. Raises: DuplicatedNodeIdError: If identifier already exists in the tree. MultipleRootError: If parent is None but tree already has a root. NodeIDAbsentError: If parent identifier doesn't exist in the tree. Example: Building a family tree:: tree = Tree() # Create root (no parent) tree.create_node("Grandpa", "grandpa") # Add children tree.create_node("Dad", "dad", parent="grandpa") tree.create_node("Uncle", "uncle", parent="grandpa") # Add grandchildren tree.create_node("Me", "me", parent="dad") tree.create_node("Sister", "sister", parent="dad") # Add node with custom data tree.create_node("Baby", "baby", parent="me", data={"age": 1, "cute": True}) )rkrrb)r,r·)r#rkrr¬rbr2s r$Ú create_nodezTree.create_node.s,€ðb�‰ 3°:ÀDˆÓIˆØ � ‰ �d˜FÔ#؈ r&có2—d}|€=|j«}|D]&}|j|j«}||k\r|n|}Œ(|St||j«s|}n |j}|j |«st d|z«‚|j|«}|S)a Get the maximum depth of the tree or the level of a specific node. When called without arguments, returns the maximum depth of the entire tree (distance from root to deepest leaf). When called with a node, returns the level of that specific node (distance from root). Args: node: Node object or identifier string. If None, returns tree depth. If provided, returns the level of this specific node. Returns: int: Tree depth (max level) or node level. Root is at level 0. Raises: NodeIDAbsentError: If specified node doesn't exist in the tree. Example: Measuring tree dimensions:: # Get overall tree depth max_depth = tree.depth() print(f"Tree depth: {max_depth}") # Get specific node level node_level = tree.depth("some_node") node_level2 = tree.depth(tree["some_node"]) # Same result # Use for tree analysis if tree.depth() > 5: print("Tree is quite deep") rrA)ÚleavesrYrr®r,r°r)r#r2ÚretrËÚleaverYr1s r$Údepthz Tree.depthcsž€ðBˆØ ˆ<à—[‘[“]ˆFØò 5�ØŸ ™  5×#3Ñ#3Ó4�Ø$¨š|‘e°‘ð 5ðˆ ô˜d D§O¡OÔ4Ø‘à—o‘o�Ø—=‘= Ô%Ü'Ð(FÈÑ(LÓMÐMØ—*‘*˜S“/ˆC؈ r&c#óXK—tt|€ |jn|«}|j|«st d|z«‚|€d„n|}|||«�rÇ|–—||j |j «D�cgc]}|||«sŒ||‘Œ}}||j|jfvr©|r|j||¬«|r‘|dj–—|dj |j «D�cgc]}|||«sŒ||‘Œ} }|r| j||¬«||jur | |ddz}n||jur|dd| z}|rŒ�yy||juržg} |j«|x} } d} | rƒ| dj |j «D�cgc]}|||«sŒ||‘Œ} }| jd«j–—| r| j«| | z} n| | z} | s | } | r| n| } | rŒ‚yytdj|««‚ycc}wcc}wcc}w­w) a§ Traverse the tree and yield node identifiers in specified order. This is the primary method for tree iteration, providing flexible traversal with multiple algorithms, filtering, and sorting options. Essential for most tree processing operations. Args: nid: Starting node identifier. If None, starts from tree root. Must exist in the tree if specified. mode: Traversal algorithm to use: Tree.DEPTH (0) - Depth-first search (default) Tree.WIDTH (1) - Breadth-first search Tree.ZIGZAG (2) - ZigZag traversal (alternating levels) filter: Optional function to filter nodes during traversal. Takes Node object, returns bool. If False, node and its entire subtree are skipped. key: Sorting function for nodes at each level. Takes Node object, returns comparison key. If None and sorting=True, sorts by node. reverse: If True, reverse the sorting order at each level. sorting: If True, sort nodes at each level using key function. If False, preserve insertion order (key/reverse ignored). Yields: str: Node identifiers in traversal order. Raises: NodeIDAbsentError: If nid doesn't exist in the tree. ValueError: If mode is not a valid traversal constant. Example: Different traversal patterns:: tree = Tree() tree.create_node("A", "a") tree.create_node("B", "b", parent="a") tree.create_node("C", "c", parent="a") tree.create_node("D", "d", parent="b") tree.create_node("E", "e", parent="c") # Depth-first (default): A, B, D, C, E for node_id in tree.expand_tree(): print(f"DFS: {tree[node_id].tag}") # Breadth-first: A, B, C, D, E for node_id in tree.expand_tree(mode=Tree.WIDTH): print(f"BFS: {tree[node_id].tag}") # ZigZag traversal for node_id in tree.expand_tree(mode=Tree.ZIGZAG): print(f"ZigZag: {tree[node_id].tag}") # Filtered traversal (only nodes with vowels) vowel_filter = lambda node: any(v in node.tag.lower() for v in 'aeiou') for node_id in tree.expand_tree(filter=vowel_filter): print(f"Vowels: {tree[node_id].tag}") # Sorted by tag, reversed for node_id in tree.expand_tree(key=lambda x: x.tag, reverse=True): print(f"Sorted: {tree[node_id].tag}") # From specific subtree for node_id in tree.expand_tree(nid="b"): print(f"Subtree: {tree[node_id].tag}") Common Usage Patterns:: # Process all nodes for node_id in tree.expand_tree(): process_node(tree[node_id]) # Get all node tags tags = [tree[nid].tag for nid in tree.expand_tree()] # Find specific nodes matching_nodes = [nid for nid in tree.expand_tree() if tree[nid].tag.startswith("prefix")] # Level-order processing for node_id in tree.expand_tree(mode=Tree.WIDTH): level = tree.level(node_id) print(f"Level {level}: {tree[node_id].tag}") Performance: - Time complexity: O(n) where n is number of visited nodes - Memory: O(h) where h is tree height (for traversal stack) - Generator-based: memory efficient for large trees Note: This is a generator function - it yields results lazily. Perfect for large trees where you might not need to visit all nodes, or when memory efficiency is important. NrAcó—yrzro©r†s r$rˆz"Tree.expand_tree..ó�r&rŠrrFz$Traversal mode '{}' is not supported)r rr.r°rr�r)ÚDEPTHÚWIDTHr�rÚZIGZAGr\r•Ú ValueErrorrq)r#r1r¦r[r?r\r]ršÚqueueÚ expansionÚstack_fwÚstackÚstack_bwÚ directions r$Ú expand_treezTree.expand_tree–s?èø€ôL”3 S [˜Ÿ š °cÓ:ˆØ�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ Ià&, n’.¸6ˆÙ �$�s‘)Õ ØŠIØ&*¨3¡i×&:Ñ&:¸4×;KÑ;KÓ&LÖ` ÑPVÐW[Ð\]ÑW^ÕP_�T˜!“WÐ`ˆEÐ`ؘŸ ™  D§J¡JÐ/Ñ/ÙØ—J‘J 3°�JÔ8ÙØ ™(×-Ñ-Ò-Ø27¸±(×2EÑ2EÀd×FVÑFVÓ2WÖ k¨QÑ[aÐbfÐghÑbiÕ[j  a£Ð k�IÐ kÙØ!Ÿ™¨3¸˜Ô@ؘtŸz™zÑ)Ø )¨E°!°"¨IÑ 5™Ø §¡Ñ+Ø % a b  ¨IÑ 5˜ô𘟙Ñ$à%'�Ø— ‘ ”Ø#(Ð(�˜Ø!� ÙØ27¸±(×2EÑ2EÀd×FVÑFVÓ2WÖ k¨QÑ[aÐbfÐghÑbiÕ[j  a£Ð k�IÐ kØŸ)™) A›,×1Ñ1Ò1Ù Ø!×)Ñ)Ô+Ø#,¨xÑ#7™à#,¨xÑ#7˜Ù Ø(1 M˜ Ù,5¡¸8˜ôô!Ð!G×!NÑ!NÈtÓ!TÓUÐUðG ùâ`ùò !lùò!lùsL‚A5H*Á7HÂHÂA$H*Ã3H ÄH Ä AH*ÅA H*ÆH%Æ-H%Æ4A H*Ç>,H*có6—t||j««S)a> Filter all nodes in the tree using a custom function. Applies the given function to every node and returns an iterator over nodes where the function returns True. Useful for finding specific types of nodes or nodes with certain properties. Args: func: Function that takes a Node object and returns bool. True means include the node in results. Returns: Iterator[Node]: Iterator over nodes where func returns True. Example: Filtering nodes by criteria:: # Find all leaf nodes leaves = list(tree.filter_nodes(lambda n: n.is_leaf(tree.identifier))) # Find nodes with specific data important_nodes = list(tree.filter_nodes( lambda n: hasattr(n.data, 'priority') and n.data.priority == 'high' )) # Find nodes by tag pattern temp_nodes = list(tree.filter_nodes( lambda n: n.tag.startswith('temp_') )) # Memory efficient processing for node in tree.filter_nodes(lambda n: n.data is not None): process_node_with_data(node) Note: Added by William Rusnack for flexible node filtering. )r[r¼)r#rUs r$Ú filter_nodeszTree.filter_nodes&s€ôL�d˜D×.Ñ.Ó0Ó1Ð1r&cóH—|�|j|«sy|j|S)aƒ Safely get a node by identifier without raising exceptions. Provides null-safe access to nodes, returning None if the node doesn't exist instead of raising an exception. Preferred over bracket notation when node existence is uncertain. Args: nid: Node identifier to retrieve, or None. Returns: Node or None: The node object if found, None otherwise. Example: Safe node access:: # Safe access (no exception) node = tree.get_node("might_not_exist") if node is not None: print(f"Found: {node.tag}") else: print("Node not found") # Compare with bracket notation try: node = tree["might_not_exist"] # Raises exception except NodeIDAbsentError: node = None # Use in conditional logic user_node = tree.get_node("user123") if user_node and user_node.data.active: process_active_user(user_node) N)r°r-r>s r$Úget_nodez Tree.get_nodeNs'€ðF ˆ;˜dŸm™m¨CÔ0ØØ�{‰{˜3ÑÐr&cóÀ—|€ td«‚|j|«std|z«‚ ||j|j«}|S#t $rg}Y|SwxYw)aQ Get the list of child node identifiers for the specified node. Returns the identifiers of all direct children of the given node. Despite the name suggesting a boolean, this method actually returns the list of child identifiers. Use children() for Node objects. Args: nid: Identifier of the parent node. Cannot be None. Returns: list[str]: List of child node identifiers. Empty if no children. Raises: OSError: If nid is None. NodeIDAbsentError: If nid doesn't exist in the tree. Example: Getting child identifiers:: # Get child IDs child_ids = tree.is_branch("parent_id") for child_id in child_ids: print(f"Child ID: {child_id}") # Check if node has children if tree.is_branch("node_id"): print("Node has children") # Use with node objects child_nodes = [tree[child_id] for child_id in tree.is_branch("parent")] zFirst parameter can't be NonerA)r¯r°rr�r)rB)r#r1Úfpointers r$rÅzTree.is_branchust€ðB ˆ;ÜÐ9Ó:Ð :Ø�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ Ið Ø˜C‘y×+Ñ+¨D×,<Ñ,<Ó=ˆHðˆøôò ؉H؈ð ús®AÁ AÁAcó<—g}|€N|jj«D]/}|j|j«sŒ|j |«Œ1|S|j |«D]5}||j|j«sŒ"|j ||«Œ7|S)a¬ Get all leaf nodes in the tree or subtree. Returns all nodes that have no children. If a starting node is specified, only considers leaves within that subtree. Leaf nodes are terminal nodes in the tree structure and often represent end points in hierarchies. Args: nid: Root of subtree to search. If None, searches entire tree. Returns: list[Node]: List of all leaf Node objects in the specified scope. Example: Finding leaf nodes:: # Get all leaves in tree all_leaves = tree.leaves() print(f"Tree has {len(all_leaves)} leaf nodes") # Get leaves in specific subtree subtree_leaves = tree.leaves("department_root") # Process leaf nodes for leaf in tree.leaves(): print(f"Leaf: {leaf.tag}") # Find deepest nodes max_level = max(tree.level(leaf.identifier) for leaf in tree.leaves()) deepest_leaves = [leaf for leaf in tree.leaves() if tree.level(leaf.identifier) == max_level] )r-r¹Úis_leafr)r”rÝ)r#r1rËr2s r$rËz Tree.leaves¡s›€ðBˆØ ˆ;ØŸ ™ ×*Ñ*Ó,ò (�Ø—<‘< × 0Ñ 0Õ1Ø—M‘M $Õ'ð (ðˆ ð×(Ñ(¨Ó-ò .�ؘ‘:×%Ñ% d×&6Ñ&6Õ7Ø—M‘M $ t¡*Õ-ð .ðˆ r&cód—t|j||«D�cgc]}|‘Œc}«dz Scc}w)aq Get the level (depth) of a node in the tree hierarchy. Returns the distance from the root to the specified node, where the root is at level 0. Optionally filters the path calculation by excluding certain nodes. Args: nid: Identifier of the node to get level for. filter: Optional function to filter nodes during path calculation. Takes Node object, returns bool. Filtered nodes are excluded. Returns: int: Level of the node (0 for root, 1 for root's children, etc.) Example: Getting node levels:: # Basic level calculation root_level = tree.level("root") # 0 child_level = tree.level("child") # 1 grandchild_level = tree.level("gc") # 2 # Use for tree analysis max_depth = max(tree.level(node.identifier) for node in tree.all_nodes()) # Filtered level calculation level = tree.level("node", filter=lambda n: n.tag != "skip") r)rEÚrsearch)r#r1r[Úns r$rYz Tree.levelÍs-€ô>˜tŸ|™|¨C°Ó8Ö9˜!’AÒ9Ó:¸QÑ>Ð>ùÒ9sš -cóB—|j|«std|z«‚|j|k(r td«‚|||j |j «}||j |j «D]+}||j|j|j «Œ-||j |j «xsgD]}|j||j ¬«Œ!|j||j|j ¬«|j|=y)a  Remove a node while preserving connections between its parent and children. Deletes the specified node but maintains the tree structure by directly connecting the node's parent to all of its children. This operation effectively "links past" the node, removing it from the hierarchy without breaking the tree connections. Args: nid: Identifier of the node to link past and remove. Raises: NodeIDAbsentError: If nid doesn't exist in the tree. LinkPastRootNodeError: If attempting to link past the root node. Example: Linking past nodes:: # Before: Root -> Manager -> Employee1, Employee2 tree.link_past_node("Manager") # After: Root -> Employee1, Employee2 # Useful for flattening hierarchies middle_managers = ["mgr1", "mgr2", "mgr3"] for mgr in middle_managers: if tree.contains(mgr): tree.link_past_node(mgr) # Cannot link past root try: tree.link_past_node(tree.root) except LinkPastRootNodeError: print("Cannot link past root node") rAzˆà˜#‘Y×)Ñ)¨$×*:Ñ*:Ó;ò MˆEØ �‰K× 'Ñ '¨×(9Ñ(9¸4×;KÑ;KÕ Lð Mð˜‘9×'Ñ'¨×(8Ñ(8Ó9Ò?¸Rò DˆCØ × $Ñ $ S°$×2BÑ2BÐ $Õ Cð Dð × Ñ  ¨6¯=©=À$×BRÑBRÐ ÔSØ �K‰K˜Ñ r&có€—|j|«r|j|«st‚|j||«rt‚||j |j «}|j |||jj«|j |||jj«|j||«y)a€ Move a node from its current parent to a new parent. Changes the parent-child relationship by moving the source node (and its entire subtree) from its current parent to the specified destination parent. This operation preserves the subtree structure while reorganizing the tree hierarchy. Args: source: Identifier of the node to move. destination: Identifier of the new parent node. Raises: NodeIDAbsentError: If source or destination node doesn't exist. LoopError: If moving would create a circular reference (destination is a descendant of source). Example: Reorganizing tree structure:: # Move employee between departments tree.move_node("employee123", "sales_dept") # Move entire department tree.move_node("engineering_dept", "new_division") # Prevent circular moves (this would raise LoopError) try: tree.move_node("parent", "child_of_parent") except LoopError: print("Cannot create circular reference") # Bulk reorganization employees = ["emp1", "emp2", "emp3"] for emp in employees: tree.move_node(emp, "new_manager") N) r°rÚ is_ancestorrrêr)r²r,rër³r´)r#ÚsourceÚ destinationr¬s r$Ú move_nodezTree.move_node!s™€ðL�}‰}˜VÔ$¨D¯M©M¸+Ô,FÜ#Ð #Ø × Ñ ˜f kÔ 2܈Oà�f‘×)Ñ)¨$×*:Ñ*:Ó;ˆØ ×јv v¨t¯©×/EÑ/EÔFØ ×ј{¨F°D·O±O×4GÑ4GÔHØ ×јv {Õ3r&cóÒ—||j|j«}|}|�E||k(ry||j|j«}||j|j«}|�ŒEy)a¾ Check if one node is an ancestor of another node. Determines whether the ancestor node appears in the path from the grandchild node to the root. An ancestor is any node that appears above another node in the tree hierarchy. Args: ancestor: Identifier of the potential ancestor node. grandchild: Identifier of the potential descendant node. Returns: bool: True if ancestor is indeed an ancestor of grandchild, False otherwise. Example: Checking ancestry relationships:: # Direct parent-child is_parent = tree.is_ancestor("parent", "child") # True # Multi-level ancestry is_grandparent = tree.is_ancestor("grandparent", "grandchild") # True # Not related is_ancestor = tree.is_ancestor("sibling1", "sibling2") # False # Prevent circular moves if not tree.is_ancestor("node_to_move", "new_parent"): tree.move_node("node_to_move", "new_parent") else: print("Cannot create circular reference") TF)rêr))r#rÃÚ grandchildr¬ržs r$rïzTree.is_ancestorQsw€ðD�jÑ!×-Ñ-¨d×.>Ñ.>Ó?ˆØˆØÐ ؘÒ!Øà˜U™ ×/Ñ/°×0@Ñ0@ÓA�ؘe™×0Ñ0°×1AÑ1AÓB�ð Ñ ð r&có—|jS)aÖ Get the internal dictionary mapping node identifiers to Node objects. Returns the underlying dictionary that stores all nodes in the tree, mapping from node identifiers to Node instances. Useful for direct access to the node collection and bulk operations. Returns: dict[str, Node]: Dictionary mapping node IDs to Node objects. Example: Working with the nodes dictionary:: # Get all node identifiers all_ids = list(tree.nodes.keys()) # Get all node objects all_nodes = list(tree.nodes.values()) # Direct access to nodes dict nodes_dict = tree.nodes for node_id, node in nodes_dict.items(): print(f"ID: {node_id}, Tag: {node.tag}") # Bulk operations total_nodes = len(tree.nodes) has_node = "some_id" in tree.nodes rÇr:s r$r!z Tree.nodes}s€ð<�{‰{Ðr&có®—|j|«std|z«‚||j|j«}|�|j|«sy||S)a, Get the parent Node object of the specified node. Returns the immediate parent of the given node, or None if the node is the root or has no parent. Provides object-based access to the parent, unlike ancestor() which returns identifiers. Args: nid: Identifier of the node whose parent to retrieve. Returns: Node or None: Parent Node object, or None if node is root. Raises: NodeIDAbsentError: If nid doesn't exist in the tree. Example: Working with parent relationships:: # Get parent node parent_node = tree.parent("child_id") if parent_node: print(f"Parent: {parent_node.tag}") else: print("Node is root or orphaned") # Navigate up the hierarchy current = "leaf_node" while tree.parent(current): current = tree.parent(current).identifier print(f"Ancestor: {tree[current].tag}") # Check parent properties parent = tree.parent("employee") if parent and hasattr(parent.data, 'department'): print(f"Department: {parent.data.department}") rAN)r°rrêr))r#r1r¶s r$r¬z Tree.parent�sY€ðL�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ Ià�3‰i×#Ñ# D×$4Ñ$4Ó5ˆØ ˆ;˜dŸm™m¨CÔ0Øà�C‰yÐr&cóD—|j€y|€D|j€-||j}|j|«|j}n td«‚|j|j«D]/}|j ||j |j «|¬«Œ1y)u×Patch @new_tree on current tree by pasting new_tree root children on current tree @nid node. Consider the following tree: >>> current.show() root ├── A └── B >>> new_tree.show() root2 ├── C └── D └── D1 Merging new_tree on B node: >>>current.merge('B', new_tree) >>>current.show() root ├── A └── B ├── C └── D └── D1 Note: if current tree is empty and nid is None, the new_tree root will be used as root on current tree. In all other cases new_tree root is not pasted. Nz1Must define "nid" under which new tree is merged.)r1Únew_treer')r.r·rÖr›ÚpasteÚsubtreer)r#r1rør'Ú new_tree_rootržs r$Úmergez Tree.mergeÌs“€ð4 �=‰=Ð Ø à ˆ;Ø�y‰yÐ Ø (¨¯©Ñ 7� Ø— ‘ ˜mÔ,Ø—m‘m‘ä Ð!TÓUÐUØ×&Ñ& x§}¡}Ó5ò XˆEØ �J‰J˜3¨×)9Ñ)9¸%×:JÑ:JÓ)KÐRVˆJÕ Wñ Xr&c ó¸—t|t«sJ‚|j€y|€ td«‚|j |«st d|z«‚t |j«t |j«z}|r%tdttt|««z«‚t|j«D]X\}}|rt||«}|jj||i«|j|j |j"«ŒZ|j%|j|«|j'||j|j(j*«y)z¤ Paste a @new_tree to the original one by linking the root of new tree to given node (nid). Update: add @deep copy of pasted tree. Nz1Must define "nid" under which new tree is pasted.rAzDuplicated nodes %s exists.)r®rr.rÖr°rÚsetr-r‘r�Útextr r!rr±r/rr)r´r²r,r³)r#r1rør'Ú set_jointÚcidr2s r$rùz Tree.pasteós"€ô˜(¤DÔ)Ð)Ð)à �=‰=Ð Ø à ˆ;ÜÐPÓQÐ Qà�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ I䘟™Ó(¬3¨t¯{©{Ó+;Ñ;ˆ Ù ÜÐ:¼TÄ#ÄdÈIÓBVÓ=WÑWÓXÐ Xä" 8§>¡>Ó2ò G‰IˆC�ÙÜ ¨¡Ó/�Ø �K‰K× Ñ   T˜{Ô +Ø × Ñ  × 3Ñ 3°T×5EÑ5EÕ Fð  Gð ×јxŸ}™}¨cÔ2Ø ×јs H§M¡M°4·?±?×3FÑ3FÕGr&cóº—g}|j«D]@}|j|j|j«D�cgc]}|‘Œc}ddd…«ŒB|Scc}w)a¿ Use this function to get the identifiers allowing to go from the root nodes to each leaf. :return: a list of list of identifiers, root being not omitted. For example: .. code-block:: python Harry |___ Bill |___ Jane | |___ Diane | |___ George | |___ Jill | |___ Mary | |___ Mark Expected result: .. code-block:: python [['harry', 'jane', 'diane', 'mary'], ['harry', 'jane', 'mark'], ['harry', 'jane', 'diane', 'george', 'jill'], ['harry', 'bill']] Nr‰)rËr”rçr)r#ÚresÚleafr1s r$Úpaths_to_leaveszTree.paths_to_leavessZ€ð<ˆà—K‘K“Mò MˆDØ �J‰J t§|¡|°D·O±OÓ'DÖE šÒEÁdÈÀdÑKÕ Lð Mðˆ ùòFs¾ A cóv—|j|«std|z«‚||j|j«}t |j |««}|D]x}||j k(rd|_|j|d«||j|j«xsgD])}|j|||jj«Œ+Œz|j|||jj«|j|d«|D]}|jj|«Œt|«S)zuRemove a node indicated by 'identifier' with all its successors. Return the number of removed nodes. rAN)r°rrêr)r‘rÝr.r´r�r²r,rër!r•rE)r#rr¬Úremovedrìrs r$Ú remove_nodezTree.remove_node7s%€ð�}‰}˜ZÔ(Ü#Ð$EÈ Ñ$RÓSÐ Sà�jÑ!×-Ñ-¨d×.>Ñ.>Ó?ˆô�t×'Ñ'¨ Ó3Ó4ˆàò IˆCØ�d—i‘iÒØ �” Ø × "Ñ " 3¨Ô -ؘC‘y×+Ñ+¨D×,<Ñ,<Ó=ÒCÀò I�Ø×&Ñ& s¨C°·±×1GÑ1GÕHñ Ið  Ið ×јv z°4·?±?×3IÑ3IÔJØ ×јz¨4Ô0àò ˆCØ �J‰J�N‰N˜3Õ ð ä�7‹|Ðr&cóð—|j|«}|€|S|j|«std|z«‚||_||j |j «}t |j|««}|D]Ö}||jk(rd|_|jj||jj|«i«||jtt|j «tt|j««||j|j «||k(sŒ¸||j!d|j«ŒØ|j#|||j$j&«|S)aâ Get a subtree with ``nid`` being the root. If nid is None, an empty tree is returned. For the original tree, this method is similar to `remove_node(self,nid)`, because given node and its children are removed from the original tree in both methods. For the returned value and performance, these two methods are different: * `remove_node` returns the number of deleted nodes; * `remove_subtree` returns a subtree of deleted nodes; You are always suggested to use `remove_node` if your only to delete nodes from a tree, as the other one need memory allocation to store the new tree. :return: a :class:`Tree` object. NrA)r8r°rr.rêr)r‘rÝr-r±r•r/r rrÚreset_pointersr¢r²r,rë)r#r1rÚstr¬rrìs r$Úremove_subtreezTree.remove_subtreeRs=€ð(�[‰[˜Ó $ˆØ ˆ;؈Ià�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ I؈Œð�c‘×&Ñ& t×'7Ñ'7Ó8ˆä�t×'Ñ'¨Ó,Ó-ˆØò =ˆCØ�d—i‘iÒØ �” Ø �I‰I× Ñ ˜c 4§;¡;§?¡?°3Ó#7Ð8Ô 9Ø ˆs‰G× "Ñ "¤4¬¨T×-=Ñ-=Ó#>ÄÄSÈ"Ï-É-Ó@XÔ YØ ˆs‰G× "Ñ " 4×#3Ñ#3Ô 4Ø�c‹zØ�3‘×'Ñ'¨¨b¯m©mÕ<ð =ð ×јv s¨D¯O©O×,BÑ,BÔC؈ r&c#óêK—|€y|j|«std|z«‚|€d„n|}|}|�B|||«r|–—|j|k7r||j|j«nd}|�ŒAyy­w)zÄ Traverse the tree branch along the branch from nid to its ancestors (until root). :param filter: the function of one variable to act on the :class:`Node` object. NrAcó—yrzrorÑs r$rˆzTree.rsearch..‹rÒr&)r°rr.rêr))r#r1r[Úcurrents r$rçz Tree.rsearch~s…èø€ð ˆ;Ø à�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ Ià&, n’.¸6ˆàˆØÐ!Ù�d˜7‘mÔ$Ø’ àEIÇYÁYÐRYÒEY�d˜7‘m×/Ñ/°×0@Ñ0@ÔAÐ_cˆGð Ó!ùs ‚A.A3Á1A3Úfilenamerdc óP‡‡ —d„Š ˆ ˆfd„} |j|||||||| | | ¬« y)z? Save the tree into file for offline analysis. có,—|j|dz«y)Nó )rR)rQÚfs r$Ú _write_linez#Tree.save2file.._write_line¥s€Ø �G‰G�D˜5‘LÕ !r&có*•—‰|t‰d««S)NÚab)Úopen)r†rrs €€r$ÚhandlerzTree.save2file..handler¨sø€Ù˜q¤$ x°Ó"6Ó7Ð 7r&rTN)rV) r#rr1rYrZr[r?r\rsrdr]rrs ` @r$Ú save2filezTree.save2file”s@ù€ò" "õ 8ð ×ÑØ Ø Ø Ø Ø Ø Ø Ø Ø Øð õ r&Ústdoutc óЇ—d‰_ˆfd„} ‰j||||||||| | ¬« | rt‰j«y‰jS#t$rtd«YŒ:wxYw)u— Display the tree structure in a beautiful hierarchical format. This method provides rich visualization options for tree structures, allowing customization of appearance, content filtering, and display properties. Perfect for debugging, documentation, and user interfaces. Args: nid: Starting node identifier. If None, starts from root. level: Starting level (0 for root). Mainly for internal use. idhidden: If True, hide node identifiers in output (cleaner display). If False, show both tag and identifier as "tag[id]". filter: Function to selectively display nodes. Takes Node object, returns bool. Filtered nodes and their subtrees are hidden. key: Sorting function for nodes at each level. Takes Node object, returns comparison key. If None and sorting=True, sorts by Node. reverse: If True, reverse the sorting order at each level. line_type: Visual style for tree connections. Options: 'ascii' - Simple |-- style 'ascii-ex' - Unicode ├── style (default) 'ascii-exr' - Unicode with rounded corners 'ascii-em' - Double-line Unicode â• â•�â•� style 'ascii-emv' - Mixed vertical Unicode style 'ascii-emh' - Mixed horizontal Unicode style data_property: If specified, display this property from node.data instead of node.tag. Useful for rich data objects. stdout: If True, print to console. If False, return as string. sorting: If True, sort nodes at each level. If False, preserve insertion order (key and reverse are ignored). Returns: str or None: If stdout=False, returns formatted string. If stdout=True, prints to console and returns None. Example: Different display styles:: tree = Tree() tree.create_node("Root", "root") tree.create_node("Child A", "a", parent="root") tree.create_node("Child B", "b", parent="root") # Basic display tree.show() # Fancy style with IDs tree.show(idhidden=False, line_type="ascii-em") # Filtered display (only nodes starting with 'C') tree.show(filter=lambda x: x.tag.startswith('C')) # Sorted by tag, reversed tree.show(key=lambda x: x.tag, reverse=True) # Show custom data property tree.show(data_property="name") # If node.data.name exists # Return as string instead of printing tree_str = tree.show(stdout=False) Output styles:: ascii: Root |-- Child A |-- Child B ascii-ex: Root ├── Child A └── Child B ascii-em: Root â• â•�â•� Child A ╚â•�â•� Child B Note: The tree display automatically handles complex hierarchies with proper indentation and connection lines. Very large trees may be truncated for performance reasons. rHcóT•—‰xj|jd«dzz c_yrJrMrPs €r$rRzTree.show..writerSr&rTz Tree is emptyN)rNrVrÚprint) r#r1rYrZr[r?r\rsrdrr]rRs ` r$Úshowz Tree.show¸s|ø€ðxˆŒ ô 8ð #Ø × Ñ ØØØØØØØØØØð !ô ñ Ü �$—,‘,Õ à—<‘<Ð øô !ò #Ü �/Ö "ð #ús�AÁA%Á$A%cóÖ—g}||jk7rR||j|j«}||j|j«D�cgc] }||k7sŒ ||‘Œ}}|Scc}w)z‚ Return the siblings of given @nid. If @nid is root or there are no siblings, an empty list is returned. )r.rêr)r�)r#r1Úsiblingsr¶ršs r$r!z Tree.siblings.so€ð ˆà �$—)‘)Ò Ø�s‘)×'Ñ'¨×(8Ñ(8Ó9ˆCØ)-¨c©×)=Ñ)=¸d×>NÑ>NÓ)OÖ\ AÐSTÐX[ÓS[˜˜Q›Ð\ˆHÐ\àˆùò]s Á A&ÁA&có&—|€t|j«S t|«}t|j«D�cgc]#}|j |j «|k(sŒ"|‘Œ%c}«Scc}w#t $rtdt|«z«‚wxYw)aH Get the number of nodes of the whole tree if @level is not given. Otherwise, the total number of nodes at specific level is returned. @param level The level number in the tree. It must be between [0, tree.depth]. Otherwise, InvalidLevelNumber exception will be raised. z*level should be an integer instead of '%s') rEr-Úintr¼rYrÚ ExceptionÚ TypeErrorÚtype)r#rYr2s r$Úsizez Tree.size<sˆ€ð ˆ=Ü�t—{‘{Ó#Ð #ð \ܘE› �ܨT×-?Ñ-?Ó-AÖj TÀTÇZÁZÐPT×P_ÑP_ÓE`ÐdiÓEišDÒjÓkÐkùÒjøÜò \ÜÐ LÌtÐTYË{Ñ ZÓ[Ð[ð \ús"™"A/»#A*ÁA*Á#A/Á*A/Á/!BcóÖ—|j|«}|€|S|j|«std|z«‚||_|j |«D]™}|j j ||j||i«||jtt|j«tt|j««||k(sŒ{||jd|j«Œ›|S)a} Return a shallow COPY of subtree with nid being the new root. If nid is None, return an empty tree. If you are looking for a deepcopy, please create a new tree with this shallow copy, e.g., .. code-block:: python new_tree = Tree(t.subtree(t.root), deep=True) This line creates a deep copy of the entire tree. NrA) r8r°rr.rÝr-r±rr/r rr)r¢)r#r1rr Únode_ns r$rúz Tree.subtreePsÕ€ð�[‰[˜Ó $ˆØ ˆ;؈Ià�}‰}˜SÔ!Ü#Ð$BÀSÑ$HÓIÐ IàˆŒØ×&Ñ& sÓ+ò @ˆFØ �I‰I× Ñ ˜d 6™l×5Ñ5°t¸F±|ÐDÔ Eð ˆv‰J× %Ñ %¤d¬3°×0@Ñ0@Ó&AÄ4ÌÈRÏ]É]ÓC[Ô \ؘ‹}à�6‘ ×*Ñ*¨4°·±Õ?ð @ðˆ r&c óN—||}t|«D�]\}}|dk(rù|jj|«}t|d|«||j|<|j |j «�O||j |j «j ||jj||j ¬«|j|j «D]!}||j||j «Œ#|j|k(sŒü||_ �Œt|||«�Œy)z¶ Update node's attributes. :param nid: the identifier of modified node :param attrs: attribute pairs recognized by Node object :return: None rN)r¦Úreplacer©) r r-r•Úsetattrrêr)rªr,ÚREPLACEr�r¢r.)r#r1ÚattrsÚcnÚattrÚvalÚfps r$Ú update_nodezTree.update_nodeos€ð�#‰YˆÜ" 5Ó)ó '‰IˆD�#Ø�|Ò#ð—[‘[—_‘_ SÓ)�ܘ˜L¨#Ô.Ø#%�— ‘ ˜CÑ à—>‘> $×"2Ñ"2Ó3Ð?ؘŸ™¨×(8Ñ(8Ó9Ñ:×LÑLØØ!Ÿ_™_×4Ñ4Ø #Ø $× 0Ñ 0ð MôðŸ-™-¨×(8Ñ(8Ó9òD�Bؘ‘H×,Ñ,¨S°$×2BÑ2BÕCðDð—9‘9 Ó#Ø #�D–I䘘D #Ö&ñ5 'r&c ó"—|€ |jn|}||j}|dgii}|r||j||d<||jr¿||j |j «D�cgc]}||‘Œ } }|€d„n|}|r| j ||¬«| D]6} ||dj|j| j|||¬««Œ8t||d«dk(r$|s||jn|d||jii}|Sycc}w)z%Transform the whole tree into a dict.Nr›rbcó—|Sr`rorÑs r$rˆzTree.to_dict..Ÿs€˜Q€r&rŠ©Ú with_datar�r\r) r.rkrbrŽr�r)r�r”Úto_dictrrE) r#r1r?r�r\r7ÚntagÚ tree_dictršr×Úelems r$r8z Tree.to_dict”s,€ð ˜Kˆd�iŠi¨cˆØ�C‰y�}‰}ˆØ˜J¨Ð+Ð,ˆ Ù Ø&*¨3¡i§n¡nˆI�d‰O˜FÑ #à �‰9× Ò Ø&*¨3¡i×&:Ñ&:¸4×;KÑ;KÓ&LÖM �T˜!“WÐMˆEÐMØ$' K’;°cˆCÙØ— ‘ ˜s¨G� Ô4àò �ؘ$‘  Ñ+×2Ñ2Ø—L‘L §¡¸IÈDÐZa�LÓbõð ô�9˜T‘? :Ñ.Ó/°1Ò4Ù1:˜D ™IŸMšMÀÈÐPTÐUXÑPY×P^ÑP^ÐG_Ð@`� ØÐ ð ùÚMsÁ, D r7r�cóP—tj|j|||¬««S)a7 Export the tree structure as a JSON string. Converts the entire tree into a JSON representation that can be easily saved, transmitted, or reconstructed. Perfect for data persistence, API responses, and cross-system integration. Args: with_data: If True, include node.data in the JSON output. If False, only include tree structure and tags. Default False for smaller output size. sort: If True, sort children at each level by node comparison. If False, preserve insertion order. reverse: If True, reverse the sorting order at each level. Only applies when sort=True. Returns: str: JSON string representation of the tree. Example: Basic JSON export:: tree = Tree() tree.create_node("Root", "root") tree.create_node("Child A", "a", parent="root") tree.create_node("Child B", "b", parent="root") # Basic structure only json_str = tree.to_json() print(json_str) # Output: {"Root": {"children": [{"Child A": {}}, {"Child B": {}}]}} # Include node data tree.create_node("Data Node", "data", parent="root", data={"type": "important", "value": 42}) json_with_data = tree.to_json(with_data=True) print(json_with_data) # Output includes: "data": {"type": "important", "value": 42} # Sorted output sorted_json = tree.to_json(sort=True, reverse=True) JSON Structure:: The output follows this hierarchical format: { "root_tag": { "children": [ { "child1_tag": { "children": [...], "data": {...} // if with_data=True } }, { "child2_tag": {} // leaf node } ], "data": {...} // if with_data=True } } Usage with APIs:: # Save to file with open('tree.json', 'w') as f: f.write(tree.to_json(with_data=True)) # Send via HTTP import requests response = requests.post('/api/trees', json={'tree': tree.to_json()}) # Pretty print import json formatted = json.dumps(json.loads(tree.to_json()), indent=2) print(formatted) Note: The JSON format preserves the complete tree structure and can be used to reconstruct equivalent trees. However, complex data objects in node.data must be JSON-serializable (no functions, custom classes without __dict__, etc.). r6)ÚjsonÚdumpsr8)r#r7r�r\s r$Úto_jsonz Tree.to_json«s#€ôl�z‰z˜$Ÿ,™,°ÀÈw˜,ÓWÓXÐXr&ÚshapeÚgraphcó²—gg} }|jræ|j|j||||¬«D]Ã} || j} t || j «j tjddi««} dj| | |«} |j| «|j| «D]>}|j}|dk(rdnd}| jd|zd zj| |««Œ@ŒÅ|d u}|r&tjtt|«d d «}n t«}|j|d z«|D]} |jd| zdz«Œt!| «dkDr|jd«| D]}|jd|zdz«Œ|jd«|st#|j%««|j'«y )z;Exports the tree in the dot format of the graphviz software)r¦r[r?r\r]ú"z\"z"{0}" [label="{1}", shape={2}]Údigraphz->z--z"{0}" z "{1}"NÚwrKz tree { ú rLrú})r!rÝrÔrrrkÚ translateÚ maketransrqr”r›Úcodecsrr rrRrErÚgetvalueÚclose)r#rr@rAr[r?r\r]r!Ú connectionsrèr1ruÚstateÚcrÚedgeÚ is_plain_filerÚcnss r$Ú to_graphvizzTree.to_graphvizsÁ€ð  ˆ{ˆØ �:Š:Ø×%Ñ%Ø—Z‘ZØØØØð &óò V�ð˜1‘g×(Ñ(�ܘD ™GŸK™KÓ(×2Ñ2´3·=±=À#ÀuÀÓ3NÓO�Ø8×?Ñ?ÀÀUÈEÓR�Ø— ‘ ˜UÔ#àŸ™ sÓ+òV�AØŸ,™,�CØ#(¨IÒ#5™4¸4�DØ×&Ñ&¨°4©¸(Ñ(B×'JÑ'JÈ3ÐPSÓ'TÕUñVð Vð$!¨Ð,ˆ Ù Ü— ‘ œD¤ hÓ/°°gÓ>‰Aä“ ˆAà �‰�˜ Ñ#Ô$Øò %ˆAØ �G‰G�D˜1‘H˜t‘OÕ $ð %ô ˆ{Ó ˜aÒ Ø �G‰G�DŒMàò 'ˆCØ �G‰G�D˜3‘J Ñ%Õ &ð 'ð �‰�Œ áÜ �!—*‘*“,Ô à �‰� r&c óp—t«}|�|j«dkDr td«‚|r|nd„}|r|nd„}i}d}|j«D]:\}}|€|€|}Œ |€ |� td«‚||vr||j |«Œ5|g||<Œ<|€ td«‚|j |||«||«¬«|g} t | «dkDrn| j«} |j| g«D]:} |j | || «|| «|| «¬ «| j | «Œ<t | «dkDrŒn|S) zA takes a dict with child:parent, and form a tree Nrzneed to pass in an empty treecó—|Sr`rorÑs r$rˆzTree.from_map..As€°A€r&có—yr`rorÑs r$rˆzTree.from_map..BrÒr&z.invalid input, more than 1 child has no parentzcannot find root)rb)r¬rb) rr'rÖÚitemsr”rÉrEr•Úget) ÚclsÚchild_parent_dictÚid_funcÚ data_funcr0Úparent_child_dictÚ root_nodeÚkÚvr×Ú parent_noderžs r$Úfrom_mapz Tree.from_map9sj€ô ‹vˆØ ˆ<˜4Ÿ9™9›;¨š?ÜÐ<Ó=Ð =Ù$‘'©+ˆÙ!*‘I±ˆ ØÐ؈ Ø%×+Ñ+Ó-ò /‰DˆAˆq؈y˜YÐ.Ø‘ Ø�˜yÐ4Ü Ð!QÓRÐRàÐ)Ñ)Ø% aÑ(×/Ñ/°Õ2à,-¨3Ð% aÒ(ð /ð Ð ÜÐ/Ó0Ð 0à ×ј¡G¨IÓ$6¹YÀyÓ=QÐÔRØ� ˆÜ�%‹j˜1ŠnØŸ)™)›+ˆKØ*×.Ñ.¨{¸BÓ?ò $�Ø× Ñ ØÙ˜E“NÙ" ;Ó/Ù" 5Ó)ð !ôð — ‘ ˜UÕ#ð $ô�%‹j˜1‹nðˆ r&)NFNN)NFFr`)NNNN)F)NNTFF)FTF)NÚcirclerDNNFT)NN)IÚ__name__Ú __module__Ú __qualname__Ú__doc__r‘Úranger‹rÓrÔrÕrr,rÚboolr%r r4r8Úpropertyrr*rCr#rFrWrrÚbytesrVrpr€r´r²r r·ÚNodeListrºrr¼rÃr›r°rÉrÎrÝrßrárÅrËrYríròrïr!r¬rürùÚStrLListrrr rçrrr!r'rúr3r8r?rSÚ classmethodrbror&r$rrIs�„ñ2ñj$(©¨a«£>Ñ €Tˆ5�%˜Ø€Jð/ sð/¨tó/ð:ØØØ$(ñ EOððEOð ˜S‘Mð EOð ó EOðR%)ØØñ -bà˜S‘Mð-bðð-bðó -bð^ð ˜H S™Mò óð ð2# 8¨C¡=ð#°Tó#ð( J˜sð J tó JðD ˜ó ð4"˜ó"ðL"ØØØ37Ø04ØØØØØ(-ñH>à �c‰]ðH>ððH>ðð H>ð ˜ 4 &¨$ ,Ñ/Ñ0ð H>ð �h ˜v t˜|Ñ,Ñ -ð H>ððH>ððH>ð˜�w �}Ñ%óH>ðTSà �c‰]ðSððSð˜( D 6¨4 <Ñ0Ñ1ð Sð �h ˜v t˜|Ñ,Ñ -ð Sð ð SððSðóSð8)à �c‰]ð)ðð)ð˜4˜& $˜,Ñ'ð )ð �h ˜v t˜|Ñ,Ñ -ð )ð ð )ðð)ðó)òV?ðR SðR°CðR¸sóRñ 7>˜Tð7>¨8°E¸$À¸)Ñ4DÑ+Eð7>ÐQUó7>ðr*˜8ó*ð8$˜só$ó8;ðz"6˜Cð"6 Hó"6òH5ðB"Ø$(Ø-1Øñ 3à �c‰]ð3ð˜S‘Mð3ð˜˜t S˜yÑ)Ñ*ð 3ð ð 3ð ó 3ñj1˜( 4™.ð1°Có1ðj"ØØ37Ø04ØØñNVà �c‰]ðNVððNVð˜ 4 &¨$ ,Ñ/Ñ0ð NVð �h ˜v t˜|Ñ,Ñ -ð NVð ð NVðóNVð`&2 ¨4¨&°$¨,Ñ!7ó&2ðP% ˜H S™Mð% ¨h°t©nó% òN*ñX*˜( 3™-ð*°8ó*óX?ðB1 #ó1òf.4ò`*ðXñóðð>-˜#ð- (¨4¡.ó-ñ^%X˜ð%X¨dó%XñNH˜ðH¨dóHð># ó#ðJ cð¨cóñ6* #ð*°8¸C±=ó*ñXd˜3ðd¨°¸4¸&À$¸,Ñ1GÑ(Hódð2"ØØØ37Ø04ØØ#Ø'+Øñ" àð" ð�c‰]ð" ðð " ð ð " ð ˜ 4 &¨$ ,Ñ/Ñ0ð " ð�h ˜v t˜|Ñ,Ñ -ð" ðð" ðð" ð  ‘}ð" ðó" ðL"ØØØ37Ø04ØØ#Ø'+ØØñt à �c‰]ðt ððt ðð t ð ˜ 4 &¨$ ,Ñ/Ñ0ð t ð �h ˜v t˜|Ñ,Ñ -ð t ððt ððt ð  ‘}ðt ððt ðót ðl ˜Cð  Hó ñ\˜( 3™-ð\°3ó\ñ(˜3ð¨H°S©Móð>#'˜sð#'°ó#'óJñ.VY ðVY°TðVYÈ4óVYðt#'ØØØØ ØØñ4à˜3‘-ð4ðð4ðð 4ðð4ðó4ðlò$óñ$r&r),rgÚ __future__rrÚbuiltinsrrÿÚ ImportErrorÚ __builtin__rJr=Úsysr<ÚcopyrÚtypingrrr r r r Úsixr rrÚioÚ exceptionsrrrrrrr2rÚ version_infor‘ÚStrListrmrlÚ __author__Úobjectrror&r$úr}sáðñ÷(8ð(Ý$óÛ Û Û Ýß=×=ç6ðÝ!÷÷õà×Ñ�vÒØ�3‰i€GØ�D˜‘I‰€HØ�D‰z�Hà�3‰i€GØ�D˜‘I‰€HØ�D‰z€Hð€ ðôT ˆ6óT óñT øðSò(ß'ð(ûðòßðús#ŒB$ÁB5Â$ B2Â1B2Â5 CÃC