{ "info": { "author": "James Hazlett", "author_email": "james.hazlett.python@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "-----DRAFT-----\n-----DRAFT-----\n-----DRAFT-----\n-----DRAFT-----\nJames Hazlett\njames.hazlett.python@gmail.com\n\nWelcome to the Data Tree!\n\nA practical tree for those short on time.\n\nNote: I\u2019m treating this as a living project, and definitely in its alpha stage.\n\nThe mission of this tree is to provide a user friendly interface, balancing ease of use, performance,\nand flexibility. This is a Python-based dictionary tree designed to exploit the language\u2019s strengths and\navoid design approaches which would work well in other languages, but not Python.\n\nThis library grew out of a project that needed a data tree that could work in parallel with a GUI on mobile\ndevices without significant slowdown. After investigating several options, each brought about its own series\nof risks which distracted from the core development of the app. As a result, I conducted a series of timed\ntests and memory to see if there was a Python tree that could be both fast and not require a data-intensive\nbackground to pick up and run with.\n\nTo accomplish this, the public-facing methods use plain language to describe their actions. They also benefit\nfrom extensive documentation. Behind the scenes, the tree dumps traditional method recursion in favor of stack\nrecursion, avoids generators, unnecessary method calls, and uses list comprehensions whenever possible.\n\nThe tree isn\u2019t meant to replace existing solutions, so much as occupy the niche for the \u201cI need convenience\u201d crowd.\n\n\n\nFEATURES\n\n-Pure Python data tree\n\n-Prioritizes explicit, plain language for all methods and variables\n\n-Extensively documented\n\n-Supports dict-like interaction ( i.e. _data_tree[ \u201c1.2.3\u201d ] = _node )\n\n-Flexible pathing logic supports both strings, lists, and even nested lists\n\n-Optimized code for fastest average performance\n\n-Supports functional programming\n\n-No hard-coded limits; the tree can keep expanding as long as the hardware supports it\n\n-Supports Cython and compiling\n\n-Can convert to and from native data structures\n\nExamples Python libraries tested with this tree:\njson\nxml.etree cElementTree (xml)\n\n-Modularized and easily inherited by other classes\n\n-Designed for safe and easy memory management\n\n-Bi-directional traversal support\n\n-Intelligent nodes, which can dynamically take on tree management responsibilities, removing the need for additional support classes\n\n-Comprehensive exception handling for easier debugging\n\n\n\n\n\n\n\n\n\nTABLE OF CONTENTS\n\nCONTACT INFORMATION\nDISCLAIMERS\nINSTALLATION\nCOMPATIBILITY\nTERMINOLOGY\nMETHODS EXPLAINED\nNAMING CONVENTIONS\nRECURRING OPTIONAL PARAMETERS\nPUBLIC METHODS AND DESCRIPTIONS\n-APPEND\n-CLEAR\n-DELETE\n-GET\n-LOGIC\n-POP\n-PRINT\n-SET\n-SETUP\nINHERITANCE\nPERFORMANCE NOTES\nPERFORMANCE NOTES - TUPLES\nPERFORMANCE NOTES - GENERATORS\nKNOWN ISSUES\nFUTURE PLANS\nLICENSE (MIT)\nHELPFUL LINKS\n\n\nCONTACT INFORMATION\n\nJames Hazlett\njames.hazlett.python@gmail.com\n\nI plan to add a github repository after this initial post.\n\nFeel free to reach out via email with library-related questions, suggestions, feature requests, cool ideas,\nstories about how it helped you, something I might find interesting (wildly subjective, I know), etc.\n\nAlso, once I get this up on github, I want it open to community input.\n\nFair warning (only because its necessary): Any emails with easily Google-able questions about the Python language\nwill be ignored outright.\n\n\n\nDISCLAIMERS\n\nAs covered in the MIT license I make no guarantees about this library. Its still considered in an 'alpha' state,\nand will stay that way for the foreseeable future (yes... mostly for plausible deniability reasons).\n\nI'd like to open it up to collaborative development once the github page is up and running.\n\nI encourage cautious coding. The tree is setup to be as flexible as possible, with what came to mind. I'm certain\nthere are more than a few ways to break it that I haven't thought of yet.\n\nThe detailed exception handling *should* help keep the pain factor down to a minimum, but I can only think of\nso many ways to break things.\n\n\n\nINSTALLATION\n\nThe easiest approach is: pip install data_tree\n\nYou can also download the source from github (link and directions to be provided later).\n\n\n\n\n\nCOMPATIBILITY\n\nEnvironment support:\n\n Debian Linux - tested\n\n Mac OS - tested\n\n Windows - untested, but should work\n\nPython versions:\n\n Python 3.7 - tested\n Python 3.5 - tested\n\n Python 3.4 - should work\n\n No compatibility for versions less than 3\n\nCython support:\n\n Recommended version: >0.27\n\n The modules in this library use PEP-484/526 annotations since...\n A. They're standardized (in theory)\n B. In timed tests, binaries compiled from these were actually faster than using the classic Cython approach\n\n\n\nTERMINOLOGY\n\nPATHS VS KEYS\n\n\"Keys\" pretty much refers to \"dictionary keys.\" Each node tracks its child nodes via dictionary.\n\n\"Paths\" are a collection of keys used to travers across multiple nodes.\n\n\nPATHS DETAILED\n\nThe most flexible path format is a list of keys. You can also use a delimited string, but if you go that route,\nI recommend making all the keys, string data types.\n\nExamples:\n\nIn a situation where the has a simple tree setup like this:\n\n{ \u20181\u2019 : { \u20182\u2019 : { \u20183\u2019 : \u2018test\u2019 } } }\n\nFor a path made out of a list of keys, you would use this:\n\nitem_node.get_node_at_path( [ \u201c1\u201d, \u201c2\u201d, \u201c3\u201d, ] )\n\nNested lists will also work:\n\nitem_node.get_node_at_path( [ \u201c1\u201d, [ \u201c2\u201d, \u201c3\u201d, ], ] )\n\nIn this case, since all the keys are strings, a delimited string path is a reliable option:\n\nitem_node.get_node_at_path( \u201c1.2.3\u201d )\n\nNote: By default, string-based paths use a period character \u201c.\u201d as their delimiter. This can be overridden only at the\ntime of an object\u2019s ( tree or node ) instantiation. This restriction is a safety measure, to mitigate having multiple\ndelimiters used in a single tree at different times.\n\nPathing: absolute vs sub-tree\n\nFor simplicity, each node can take either a path meant for a child node, or an absolute path for the entire tree. The user\ncan tell the node which type of path it is via a boolean flag.\n\n\n\nPathing and data types: recommended coding practices\n\nI recommend cautious coding when it comes to key data types within the tree. To preserve the tree\u2019s speed, I didn\u2019t include\nany automatic data type reconciliation in the pathing algorithms.\n\nTo help protect the user from this type of issue, I included data type-specific exception messaging for KeyExceptions.\n\nHere is an example KeyException exception message:\n\n_data_tree = Data_tree_node()\n\n_data_tree.append_path( [ 1, 2, 3, ] )\n\n_node = _data_tree[ \u201c1.2.3\u201d ]\n\nThis will cause an exception with the following message:\n\nError: arg_key_or_path failed.\n\narg_key_or_path = 1.2.3\n\ntype( arg_key_or_path ) = \n\nPath parts present in tree =\n[ , ]\n\nPath parts missing from tree =\n[ '1',\n '2',\n '3', ]\n\nList of pairs, path_parts : ( bool ) if they failed due to data type discrepancy...\n\n1 : True\n2 : True\n3 : True\n\n\n\n\nThe classes defined in this project are:\n\n-Data_tree_node\n-Data_tree_node_with_quick_lookup\n\nEach of these have the same public methods. Rather than have dedicated support classes for managing the trees,\nthe nodes track the root of their own tree, and that root node takes on any tree-wide management responsibilities.\n\n\n\nData_tree_node\n\nThis node type is meant to have the smallest memory footprint. It handles all requests by traversing the tree, and\ndoesn\u2019t cache anything for quick referencing, aside from the node\u2019s own parent, and the root node of the tree.\n\n\n\nData_tree_node_with_quick_lookup\n\nThis node type is meant to speed up actions involving either the root node, or absolute paths.\n\nAny root node of a tree automatically caches the absolute paths to all nodes, node leaves, node instances,\nand objects stored within each node.\n\nIf the node looks up an absolute path, then it does so via dict lookup, rather than iterating across its node\nchildren. In cases where the node isn\u2019t the root node, and it\u2019s not referencing an absolute path, it will traverse its\nchild nodes instead.\n\nFor looking up node instances, or objects stored within the tree, this node type refers to the node\u2019s / object\u2019s\nunique integer id provided by Python\u2019s id() method. This way, all the keys are integer values, and guaranteed to be\nunique over the lifetime of the node / object in question.\n\nWhen a tree pops a node, the node automatically becomes the root node for its own tree, and stores all the data for the\nchildren under it. Conversely, if the tree adds a node, that node is no longer considered a root node, and hands its\ndata off to the root node of the tree that absorbed it.\nMethods\n\nThis section provides a detailed list of methods available in each class. The classes are setup to have the exact same\ninterface (in theory). I\u2019ll document if I break this rule.\n\n\n\n-----METHODS EXPLAINED-----\n\n\nNAMING CONVENTIONS\n\nTo keep with (mostly) plain language, context, and predictability, this convention makes it easier to guess a method\u2019s\nname well enough for the IDE to suggest the correct one, or to support iterating through class attributes, while filtering\nfor key words. Typically, each of the methods follow:\n\n\u201cverb\u201d_\u201dsubject / data type\u201d_\u201dpreposition\u201d_\u201dindirect object\u201d\n\ni.e. get_node_at_path()\n\nAll parameters start with \u201carg\u201d at the beginning. This way, if you forget the parameters, and want *something* to show up,\ntype \u2018arg\u2019 and if you are using an IDE with the functionality, it will list out the arguments available.\n\ni.e. \u201carg_key\u201d and \u201carg_path\u201d\n\nThe only exception to this rule is \u2018kwargs\u2019 because its already kind of standard, but all the arguments packed\nwithin it, start with \u2018arg.\u2019\n\nEach parameter has the following general layout:\n\narg_\u201ddata type ( if relevant )\u201d_\u201dtopic\u201d\n\nThe data type isn\u2019t mentioned if the parameter can tolerate more than one data type, or the data\ntype is already implied by the topic.\n\nExamples:\n\narg_path can take a string or a list, so no data type is given in the parameter name.\n\narg_bool_is_sub_tree_path mentions bool, since it\u2019s exclusively used as a True / False statement.\nSince \u201carg_is_sub_tree_path\u201d could still be a little ambiguous about the exactly its value is used,\n\u2018bool\u2019 is added to the name to make it clear.\n\n\n\nRECURRING OPTIONAL PARAMETERS\n\narg_bool_search_entire_tree / arg_bool_path_is_absolute\n\nIf False ( default value ), the node calling the method is treated like the root node, and only its child nodes\nare considered.\n\nIf True, then the actual root node of the entire tree is considered, and this accounts for all nodes within it.\n\narg_default_value_to_return\n\nThis is similar to the optional default value you would pass to a method like _dict.get( \u201ckey\u201d, \u201cdefault value\u201d, )\nexcept it\u2019s explicitly named parameter.\n\nI tried simulating original way, but it cut down on parameter flexibility, so I prioritized the flexibility, and\nexplicit naming as a trade-off.\n\n\n\nRECURRING OPTIONAL PARAMETERS - Parameters that begin with \"arg_callable\"\n\nSome methods support functional programming and can accept callable objects as arguments. They bring multiple\nbenefits when used:\n\n-Any value editing can take place at the time the information is collected from the data tree. This means you\ncan do a lot in just one traversal of the data tree.\n\n-This adds support for functionality not explicitly defined in the data tree.\n\n-They're only considered during traversals if passed, so if they're unused, they don't impact performance.\n\nThese arguments can take \"callable\" objects ( functions / methods / lambdas ). These are ways to 'insert' custom\ncode into data collection loops.\n\nExample:\n\nLet's say, you call get_list_of_pairs_paths_and_node_children(), but instead of pairs of \"paths and nodes,\"\nyou actually want \"paths and the objects stored within the nodes...\"\n\nOne way you can do it is declare a method:\n\ndef get_pair_path_and_object_stored_in_node( arg_path, arg_node ) :\n\n return [ arg_path, arg_node._object_stored_within_node, ]\n\n...then pass the method, without arguments to get_list_of_pairs_paths_and_node_children.\n\nIn this case, since you're 'formatting' the returned data, you would pass this via 'arg_callable_formatter.'\n\nExample:\n\nget_list_of_pairs_paths_and_node_children( arg_callable_formatter = get_pair_path_and_object_stored_in_node )\n\nThen, let's say you have a node located at key \"1\" and the object stored in that node is \"TEST\".\n\nInstead of getting:\n\n[ [ \"1\" : ] ]\n\nYou would get this instead:\n\n[ [ \"1\" : \"TEST\" ] ]\n\nFor developers no already aware, lambdas are also a good choice for this:\n\nInstead of...\n\ndef get_pair_path_and_object_stored_in_node( arg_path, arg_node ) :\n\n return [ arg_path, arg_node._object_stored_within_node, ]\n\nYou'd create a lambda object\n\nget_pair_path_and_object_stored_in_node = lambda arg_path, arg_node : [ arg_path, arg_node._object_stored_within_node, ]\n\nEither of these will work.\n\n\nThe methods currently supported are:\n\narg_callable_filter - If passed, the method will interpret this callable's result\nas True or False. If True, then that path / node / pairing are added to the returned results.\nIf False, then they are not included in the returned results.\n\narg_callable_formatter - If passed, this allows you to edit the returned information as\nits collected from the data tree.\n\nNote: Each method handles callables slightly differently based on the objects they return.\n\nFor additional details:\n\nLook at the docstrings for methods that being with \"_template_example_for_arg_callable\". These\nare essentially 'example methods' which explain how their associated public method uses them.\n\n\nPUBLIC METHODS AND DESCRIPTIONS\n\nappend_key( arg_key, arg_node = None )\n\n Returns the node stored at arg_key.\n If the node doesn't exist, this method creates on and stores it at the key.\n If the node already exists, return the one that already exists.\n\n Argument:\n\n arg_key - obeys the same rules as a regular dictionary key.\n\n arg_node - If this equals None, the method auto-generates a new node of the same type as the one calling this method. Otherwise,\n this argument is another instanced node.\n\n\n\nappend_path( arg_path, arg_bool_path_is_absolute = False, arg_node = None )\n\n Returns the node at the path.\n If the path already exists, the method returns the node at the path.\n If the path doesn't exist, the method creates it and adds it to the tree.\n\n Arguments:\n\n arg_path - can be either a list of keys, or a delimited string.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n arg_node - If this equals None, the method auto-generates a new node of the same type as the one calling this method. Otherwise,\n this argument is another instanced node.\n\n arg_bool_raise_error_if_node_child_is_not_same_as_type_used_for_node_children - If True, the method will guard itself against adding\n potentially incompatible nodes. If False, the validation doesn't happen.\n\n\n\nclear( **kwargs )\n\n Resets the node.\n\n Clears both the object stored within it, and clears all\n objects stored in self._list_of_attributes_to_clear.\n\n For this class specifically, the only item in\n self._list_of_attributes_to_clear is self._dict_of_keys_and_node_children.\n\n Note: This method does not attempt to sever the connection between this node\n and its parent tree. This can only happen if the tree removes the node.\n\n Arguments:\n\n kwargs is used here for specialized data clearing. If a class inherits this\n node, and passes variable names to this method, then its seeking to clear\n specific information, rather than the standard process.\n\n\n\ncopy( )\n\n Returns a new instance of a tree, consisting of new node instances.\n\n WARNING: This process only changes the reference for _object_stored_in_node. It does not attempt to do deep copies.\n\n Reasons:\n\n -Python's default process for deep copying many objects is prohibitively slow at run-time\n\n -This keeps with the general \"pass by value\" approach\n\n\n\ndelete_key_to_node_child( arg_key )\n\n This method deletes the key, and severs the connection to the child.\n\n This does not return a value.\n\n This will raise an exception if the key isn\u2019t found.\n\n Argument:\n\n arg_key - This has the same behavior as a native dict key\n\n\n\ndelete_node_child( arg_node_child, arg_bool_search_sub_tree = True, arg_bool_search_entire_tree = False )\n\n This method looks for and then breaks the connection between the tree and the child node.\n\n By default, this method only looks for the arg_node_child in the host node\u2019s immediate children.\n\n If the node is not found, the method will throw an exception.\n\n This method does not return a value.\n\n Arguments:\n\n arg_node_child - This is a node instance which already exists within the tree.\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Note: a True value for this parameter supersedes arg_bool_search_sub_tree's value\n\n arg_bool_search_sub_tree - If True, searches sub tree. If False, searches only immediate node children.\n\n\n\ndelete_path_to_node_child( arg_path, arg_bool_path_is_absolute = False )\n\n This method breaks all internal references to the node located at the path. This does not affect the other nodes along the path.\n\n This raises an exception if the path is not found.\n\n This method does not return a value.\n\n Arguments:\n\n arg_path - can be either a list of keys, or a delimited string.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nget( arg_path, arg_bool_search_entire_tree = False, arg_default_value_to_return = None )\n\n Returns the node located at arg_path if the node has children. Otherwise, method returns the object stored\n within the node.\n\n If the path does not exist, method returns arg_default_value_to_return\n\n Arguments:\n\n arg_path - can be either a list of keys, or a delimited string.\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n arg_default_value_to_return - The value returned of the path does not exist.\n\n\n\nget_data_in_format_for_export( arg_bool_search_entire_tree = False )\n\n Default behavior:\n\n Returns a nested dict that mimics the tree\u2019s design, as opposed to the class-specific nodes.\n If the node is a leaf node, the method will add the object stored within it, rather than a dict.\n\n Note: This is meant to put the data structure in a format that can be easily converted to other\n data formats through built-in parsers.\n\n Arguments:\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Example use with json:\n\n import json\n\n _data_tree = Data_tree_node()\n\n _node = _data_tree.append_path( \"1.2.3\" )\n\n _node.set_object_stored_in_node( \"TEST\" )\n\n print( json.dumps( _data_tree.get_data_in_format_for_export() )\n\n # Output: {\"1\": {\"2\": {\"3\": \"TEST\"}}}\n\n\n\nget_deque_of_nodes( arg_bool_search_entire_tree = False, arg_bool_search_sub_tree = False, arg_bool_first_item_popped_is_leaf_node = False )\n\n Returns a stack / deque of nodes within the tree.\n\n Arguments:\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Note: a True value for this parameter supersedes arg_bool_search_sub_tree's value\n\n arg_bool_search_sub_tree - If True, searches sub tree. If False, searches only immediate node children.\n\n\n\nget_dict_of_keys_and_node_children( )\n\n Returns a dict of the current node\u2019s children and their associated keys.\n\n Note: This is intentionally meant to be a super simple method. For advanced\n functionality, use get_dict_of_string_paths_and_node_children()\n\n\n\nget_dict_of_paths_and_node_children( arg_bool_search_sub_tree = True, arg_bool_search_entire_tree = False, arg_bool_get_paths_as_strings = False, arg_callable_filter = None, arg_callable_formatter = None )\n\n Returns a dict, with paths as keys and nodes as values.\n\n Arguments:\n\n arg_bool_search_sub_tree - If True, this method searches all nodes within this node's sub-tree.\n Otherwise, it only searches immediate children.\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Note: If arg_bool_search_entire_tree is True, this method ignored arg_bool_search_sub_tree's value\n\n\n arg_callable_filter - This is a callable object that returns a value which will be interpreted as a boolean\n value.\n\n For additional details, review...\n template_for_arg_callable_filter_in_get_dict_of_paths_and_node_children( self, arg_iterable_path, arg_node )\n\n\n arg_callable_formatter - It a callable object which formats the returned data.\n\n For additional details, review...\n template_for_arg_callable_formatter_in_get_dict_of_paths_and_node_children( self, arg_iterable_path, arg_node )\n\n\n\nget_id_for_object_stored_in_node( arg_object )\n\n RECOMMENDED_FOR_CUSTOM_OVERRIDES\n\n This method is used to generate and return unique ids for arg_object.\n\n This definition exists as a place holder, and not used in Data_tree_node.\n\n To see a functional definition for this method, check its definition in\n Data_tree_node_with_quick_lookup.\n\n\n\nget_int_count_for_node_children( arg_bool_search_sub_tree = True, arg_bool_search_entire_tree = False, arg_callable_filter = None, arg_callable_formatter = None )\n\n Returns the number of nodes in the tree.\n\n This does not count the root node of either search type.\n\n Arguments:\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Note: a True value for this parameter supersedes arg_bool_search_sub_tree's value\n\n arg_bool_search_sub_tree - If True, searches sub tree. If False, searches only immediate node children.\n\n arg_callable_filter - This method interprets the returned value of this callable as True / False.\n\n arg_callable_formatter - This method returns an int value, which is added to the total count returned by\n get_int_count_for_node_children()\n\n\n\nget_key_for_node_child( arg_node_child )\n\n Returns the key linking to the node if it\u2019s found. If not, the method returns None.\n\n Argument:\n\n arg_node_child - This is a pre-existing node instance in the current node's immediate children.\n\n\n\nget_list_of_keys_from_path( *args )\n\n This returns a flat list comprised of the keys to navigate from this node to\n the node at the path.\n\n I settled on this approach to balance execution time and flexibility.\n\n Reminder about competing approaches and performance:\n\n List with basic append requires a list reversal at the end, followed by\n a reconversion to a list. This makes the approach slower.\n\n List with insert( 0, item, ) is slightly faster than above, but slower than\n the stack option actually implemented here.\n\n\n\nget_list_of_pairs_paths_and_node_children( arg_bool_search_sub_tree = True, arg_bool_search_entire_tree = False, arg_bool_get_paths_as_strings = False, arg_callable_filter = None, arg_callable_formatter = None )\n\n Returns a list of pairs: [ path, node, ]\n\n Arguments:\n\n arg_bool_search_sub_tree - If True, method searches the nodes within the current node's sub-tree\n\n Note: arg_bool_search_sub_tree is True by default.\n\n arg_bool_search_entire_tree - If True, searches the whole tree\n\n If both arg_bool_search_sub_tree and arg_bool_search_entire_tree are False, method only\n searches the immediate child nodes.\n\n arg_bool_get_paths_as_strings - If set to True, the method formats paths into delimited strings\n\n arg_callable_filtering - If passed, this needs to be a callable object. This method\n passes two arguments to the callable: path, and node.\n\n For additional details, review...\n template_example_for_arg_callable_filter_in_get_list_of_pairs_paths_and_node_children( self, arg_iterable_path, arg_node )\n\n arg_callable_formatter - This callable edits the returned values to a custom format described within it.\n\n For additional details, review...\n template_example_for_arg_callable_formatter_in_get_list_of_pairs_paths_and_node_children( self, arg_iterable_path, arg_node )\n\n Example of a valid callable:\n\n # Causes this method to return all leaf nodes\n _example_lambda = lambda arg_path, arg_node : not arg_node._dict_of_keys_and_node_children\n\n\n\nget_list_of_pairs_paths_and_node_children_relevant_to_object( arg_object, arg_bool_search_sub_tree = True, arg_bool_search_entire_tree = False, arg_bool_get_paths_as_strings = False, arg_callable_filter = None, arg_callable_formatter = None )\n\n Returns a list of pairs [ path, node, ] consisting of nodes containing objects matching arg_object.\n\n Arguments:\n\n arg_object - The method matches this argument against the object stored in each node. It does so\n by calling get_id_for_object_stored_in_node(). This returns a hash value if possible, or an instance\n id.\n\n arg_bool_search_sub_tree - If True, method searches the nodes within the current node's sub-tree\n\n Note: arg_bool_search_sub_tree is True by default.\n\n arg_bool_search_entire_tree - If True, searches the whole tree\n\n If both arg_bool_search_sub_tree and arg_bool_search_entire_tree are False, method only\n searches the immediate child nodes.\n\n arg_bool_get_paths_as_strings - If set to True, the method formats paths into delimited strings\n\n arg_callable_filtering - If passed, this needs to be a callable object. This method\n passes two arguments to the callable: path, and node.\n\n\n template_example_for_arg_callable_filter_in_get_list_of_pairs_paths_and_node_children_relevant_to_object( self, arg_iterable_path, arg_node )\n\n Example of a valid callable:\n\n # Causes this method to return all leaf nodes\n _example_lambda = lambda arg_path, arg_node : not arg_node._dict_of_keys_and_node_children\n\n\n\nget_list_path_from_arguments( *args )\n\n Returns args in the form of a list based on args' contents.\n\n\n\nget_node_child_at_key( arg_key, arg_default_value_to_return = None )\n\n Returns the child node located at arg_key if it exists; otherwise, returns arg_default_value_to_return.\n\n arg_key - The target key for the node child in question.\n\n arg_default_value_to_return - This is the automatically returned value if the key does not exist in the tree.\n\n\n\nget_node_child_at_path( arg_path, arg_bool_path_is_absolute = False, arg_default_value_to_return = None )\n\n Returns the node located at arg_path.\n\n If the path doesn\u2019t exist in the tree, then the method will return arg_default_value_to_return.\n\n Arguments:\n\n arg_path - can be either a list of keys, or a delimited string.\n\n arg_default_value_to_return - This is the automatically returned value if the path does not exist in the tree.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree of the current node.\n\n\n\nget_node_new_instance( )\n\n IMPORTANT_FOR_BUILDING_TREE\n\n Typically called by the following methods if no arg_node provided...\n append_key()\n append_path()\n\n Creates a new node, which uses the same delimiter as the node creating it.\n\n Reminder: This specific call happens enough to justify its own method for safety reasons.\n\n\n\nget_node_parent( )\n\n Returns the node\u2019s parent node, if it exists, otherwise, the returned value is None.\n\n\n\nget_node_root( )\n\n Returns the root node for the entire tree.\n\n Note: If the root node calls this method, it will return itself.\n\n\n\nget_object_at_key( arg_key, arg_default_value_to_return = None )\n\n Returns the object stored within the child node located at the key.\n\n If the key doesn\u2019t exist, then the method will return arg_default_value_to_return.\n\n\n\nget_object_at_path( arg_path, arg_bool_path_is_absolute = False, arg_default_value_to_return = None )\n\n Returns the object stored within the node located at the path.\n\n If the path does not exist, then the method will return arg_default_value_to_return.\n\n Arguments:\n\n arg_path - can be either a list of keys, or a delimited string.\n\n arg_default_value_to_return - This is the default value returned if the path does not exist.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nget_object_stored_in_node( arg_path = None, arg_bool_path_is_absolute = False, arg_default_value_to_return = None )\n\n Returns the object stored within node.\n\n If arg_path is None, then the method returns the object stored in the current node.\n\n Arguments:\n\n arg_path - can either a list of keys, a delimited string, or None. If the value is None, this method returns the value in\n arg_default_value_to_return.\n\n Note: arg_default_value_to_return is not honored if arg_path is None.\n\n If arg_path is defined, then the method will retrieve the object stored in the node at the path.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nget_path_to_node_child( arg_node_child, arg_bool_search_entire_tree = False, arg_bool_raise_error_if_node_is_not_in_tree = True, arg_default_value_to_return = None, arg_bool_get_path_as_string = False )\n\n Returns a list of keys in the path to the node by default, since not all tree instances are guaranteed to have exclusively strings as keys.\n\n Arguments:\n\n arg_node_child - This needs to be a node which already exists in the tree.\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n arg_bool_raise_error_if_node_is_not_in_tree - If True, then the method will raise an exception if its unable to find the node. If False,\n the method will raise an exception and display the details for why arg_node_child wasn't found.\n\n arg_default_value_to_return is the value returned if arg_bool_raise_error_if_node_is_not_in_tree is False.\n\n Note: If arg_bool_raise_error_if_node_is_not_in_tree is True, then arg_default_value_to_return is ignored.\n\n Note: If arg_bool_raise_error_if_node_is_not_in_tree is True, then arg_default_value_to_return will not be honored.\n\n arg_bool_get_path_as_string - If True, returns path as a string. If False, returns path as a list of keys.\n\n\n\nget_string_delimiter_for_string_paths( )\n\n Returns self._string_delimiter_for_path so users can use without\n explicitly having to know what it is.\n\n\n\nget_string_path_from_arguments( *args )\n\n This takes *args in whatever format, and attempts to form a delimited string out of it.\n\n This method can handle nested data structures.\n\n Examples of valid arguments (not comprehensive):\n\n get_string_path_from_arguments( 1, 2, 3 )\n\n get_string_path_from_arguments( \"1\", \"2\", \"3\" )\n\n get_string_path_from_arguments( [ 1, 2, 3, ] )\n\n get_string_path_from_arguments( [ 1, [[[ 2 ]], [ 3 ]], ] )\n\n\n\nget_tuple_of_keys_from_path( *args )\n\n Returns a tuple compiled from args.\n\n args can be multiple objects, even nested within each other.\n\n Data_tree_node_with_quick_lookup makes use of this method for hashing.\n\n\n\nget_type_to_use_for_node_children( )\n\n RECOMMENDED_FOR_CUSTOM_OVERRIDES\n\n Returns the type used for new child nodes generated within this tree.\n\n\n\nitems( arg_bool_search_sub_tree = False, arg_bool_search_entire_tree = False, arg_bool_get_paths_as_strings = False )\n\n This method is designed to behave similarly to dict.items(), and yields a list pair, with the path in index 0, and the\n corresponding node in index 1.\n\n Arguments:\n\n arg_bool_search_sub_tree - If True, searches sub tree. If False, searches only immediate node children.\n\n Note: a True value for this parameter supersedes arg_bool_search_sub_tree's value\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n arg_bool_get_paths_as_strings - If True, yields the path as a string, otherwise, the method returns the path\n as a list of keys.\n\n\n\nkeys( arg_bool_search_sub_tree = False, arg_bool_search_entire_tree = False, arg_bool_get_paths_as_strings = False )\n\n This method supports iterating across the tree, similarly to dict.keys()\n\n Arguments:\n\n arg_bool_search_sub_tree - If True, searches sub tree. If False, searches only immediate node children.\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Note: a True value for this parameter supersedes arg_bool_search_sub_tree's value\n\n arg_bool_get_paths_as_strings - If True, the method returns paths as delimited strings. If False, this returns\n the paths as lists of keys.\n\n\n\nlogic_data_is_in_correct_format( arg_data )\n\n Returns True if arg_data is in one of the three formats setup_tree_based_on_data_structure() takes.\n\n This is here primarily for debugging and data validation.\n\n For this method to return True, arg_data needs to be one of the following:\n\n -A simple dict\n -A nested dict\n -A list of nested dicts\n -Another data_tree_node\n\n\n\nlogic_key_exists( arg_key )\n\n Returns True, if the key exists for a child node.\n\n Argument:\n\n arg_key - obeys the same rules as regular dictionary keys.\n\n\n\nlogic_objects_match( arg_object_one, arg_object_two )\n\n RECOMMENDED_FOR_CUSTOM_OVERRIDES\n\n Typically called by...\n _get_list_of_pairs_paths_and_node_children_relevant_to_object()\n _get_list_of_pairs_paths_and_node_children_relevant_to_object_with_filter()\n _get_list_of_pairs_paths_as_strings_and_node_children_relevant_to_object()\n _get_list_of_pairs_paths_as_strings_and_node_children_relevant_to_object_with_filter()\n get_list_of_pairs_paths_and_node_children_relevant_to_object()\n\n This is a drop-in method for potential custom comparisons in inheriting classes.\n\n\n\nlogic_path_exists( arg_path, arg_bool_path_is_absolute = False )\n\n Returns True, if the path exists within the tree, and False if not.\n\n Arguments:\n\n arg_path - Can be a delimited string or list of keys.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nlogic_path_is_leaf_node( arg_path, arg_bool_path_is_absolute = False )\n\n Returns True, if the path exists in the tree, and the node at the path has no children.\n\n Arguments:\n\n arg_path - This can be a delimited string, or a list of keys.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nnodes( arg_bool_search_sub_tree = True, arg_bool_search_entire_tree = False )\n\n This method supports iteration similarly to dict.values().\n\n Arguments:\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Note: a True value for this parameter supersedes arg_bool_search_sub_tree's value\n\n arg_bool_search_sub_tree - If True, searches sub tree. If False, searches only immediate node children.\n\n\n\npaths( arg_bool_search_sub_tree = True, arg_bool_search_entire_tree = False, arg_bool_get_paths_as_strings = False )\n\n This method supports iteration similarly to dict.keys()\n\n Arguments:\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n arg_bool_get_paths_as_strings - If True, returns each path as a delimited string. If False, the\n returned path is a list of keys.\n\n\n\npop( arg_path, arg_default_value_to_return = _OBJECT_FOR_RAISING_ERRORS, arg_bool_path_is_absolute = False )\n\n This method behaves similarly to dict.pop()\n\n Returns the object stored in the node located at arg_path; then removes the node and all its\n sub nodes. If arg_path doesn\u2019t exist, returns arg_default_value_to_return.\n\n Arguments:\n\n arg_default_value_to_return - This is the returned value, if arg_path isn't in the tree.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n Note: This is a wrapper to mimic dict\u2019s pop method.\n\n\n\npop_key( arg_key, arg_default_value_to_return = _OBJECT_FOR_RAISING_ERRORS )\n\n Returns the object stored in the child node at arg_key; and removes the node and all its sub nodes as well. If arg_key doesn\u2019t exist,\n returns arg_default_value_to_return.\n\n If arg_key does not exist, and arg_default_value_to_return isn't set, then this method will raise an error.\n\n Arguments:\n\n arg_key - This obeys the same rules as a key used in a dict.\n\n arg_default_value_to_return - This is the default value returned if the key does not exist.\n\n\n\npop_key_to_node_child( arg_key, arg_default_value_to_return = _OBJECT_FOR_RAISING_ERRORS )\n\n Returns the child node located at arg_key; and removes the node and all its sub nodes.\n If arg_key doesn\u2019t exist, returns arg_default_value_to_return.\n\n Note: This method is an explicit request for the node, instead of the object stored within it.\n\n Arguments:\n\n arg_key - This obeys the same rules as a normal dict key.\n\n arg_default_value_to_return - This is the default value returned if arg_key doesn't exist.\n\n\n\npop_node_child( arg_node_child, arg_bool_search_entire_tree = False, arg_default_value_to_return = None )\n\n Returns the path to the node; and removes the node. If arg_node doesn\u2019t exist, returns arg_default_value_to_return.\n\n Arguments:\n\n arg_default_value_to_return - This is the default value returns if arg_node_child doesn't exist within the searched area.\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Rational behind returning the path:\n\n If the dev is using the node object, then they already have access to the object stored within the node. The info\n that isn't necessarily easy to come by is what gets returned, which in this case would be the path.\n\n\n\npop_path( arg_path, arg_bool_path_is_absolute = False, arg_default_value_to_return = _OBJECT_FOR_RAISING_ERRORS )\n\n This method is an explicit wrapper for pop()\n\n If the node has no children, then this method return's the node stored at that location.\n\n Otherwise, it returns the node itself.\n\n Arguments:\n\n arg_path - This is the path to the node. It can either be a delimited string, or a list of keys.\n\n arg_default_value_to_return - This is the default value returned if arg_path does not exist in the searched area.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\npop_path_to_node_child( arg_path, arg_bool_path_is_absolute = False, arg_default_value_to_return = _OBJECT_FOR_RAISING_ERRORS )\n\n Returns the node located at the path, and removes this node.\n\n Any child nodes remain attached to popped node, and no longer considered\n part of the original data tree.\n\n Example:\n\n If the tree has the address: \"1.2.3.4\" and the user pops the node \"1.2.3\"\n then \"3\" is removed from the tree. The node at \"4\" remains attached to the node\n at \"3\", and becomes inaccessible to the tree popping the path.\n\n Node at address \"1.2\" remains unaffected, other than its connection to node \"3\"\n is severed.\n\n Arguments:\n\n arg_path - This is the path to the node. It can either be a delimited string, or a list of keys.\n\n arg_default_value_to_return - This is the default value returned if arg_path does not exist in the searched area.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nprint_object( arg_object, arg_name_for_object = None )\n\n This method prints information in a reasonably easy to read format, and\n compensates for some formatting challenges in pprint.\n\n Reminder: Processes like Cythonize do not like a self.print() method, so this\n had to be changed to print_object.\n\n Arguments:\n\n arg_object - This can be pretty much anything.\n\n arg_name_for_object - If this contains a value, then the name provided\n is displayed above arg_object's printed information. If this value is None\n then only arg_object's info will print.\n\n\n\nprint_tree( arg_bool_search_entire_tree = False, arg_names_for_attributes_to_print = None )\n\n Prints output for the data tree.\n\n Example code and output\u2026\n\n Code:\n\n _dict_tree = Dict_tree_node()\n\n _dict_tree.append_path( [ 1, 2, 3, ] )\n\n _dict_tree.print_tree()\n\n Output:\n\n ---PRINTING TREE---\n\n --- PATH: (root) ---\n\n --- PATH: 1 ---\n\n --- PATH: 1.2 ---\n\n --- PATH: 1.2.3 \u2014\n\n Code:\n\n _dict_tree = Dict_tree_node()\n\n _node = _dict_tree.append_path( [ 1, 2, 3, ] )\n\n _node.set_object_stored_in_node( \"EXAMPLE\" )\n\n _dict_tree.print_tree( arg_names_for_attributes_to_print = \"_object_stored_in_node\" )\n\n Output:\n\n ---PRINTING TREE---\n\n --- PATH: (root) ---\n\n _object_stored_in_node = None\n\n --- PATH: 1 ---\n\n _object_stored_in_node = None\n\n --- PATH: 1.2 ---\n\n _object_stored_in_node = None\n\n --- PATH: 1.2.3 ---\n\n _object_stored_in_node = EXAMPLE\n\n Arguments:\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n arg_names_for_attributes_to_print can be a single string, or a list of strings. This will include the attributes in the print output.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nset_object_stored_in_node( arg_object, arg_path = None, arg_bool_path_is_absolute = False )\n\n Stores arg_object in a node within the tree. If arg_path is not defined, arg_object is stored in the\n current node. If arg_path is defined, then the method will store arg_object in the node located at the path.\n\n Note: In keeping with dict\u2019s regular behavior, if the path doesn\u2019t exist, then the method will create create\n the path.\n\n Arguments:\n\n arg_object - This can be any object that could be stored in a variable.\n\n arg_path - This can be a delimited string, list of keys, or None. If the value is None, then the object set is an\n attribute of the current node.\n\n arg_bool_path_is_absolute - If True, starts from the entire tree's root node. If False, the method focuses on the children in\n the sub tree.\n\n\n\nset_string_delimiter_for_path_default_for_all_classes( cls, arg_string_delimiter_for_path_default, **kwargs ) \n\n This method sets the global default delimiter for Data_tree_node and all inheriting classes.\n\n CAUTION: This method should only really run before any nodes exists.\n\n It will raise errors if used after first node created, or ran a 2nd time. These errors\n can be overridden in the arguments by setting either of these arguments to True:\n\n -arg_bool_override_safety_against_multiple_assignments\n -arg_bool_override_safety_against_setting_global_value_after_first_node_creation\n\n\n\nsetup_tree_based_on_data_structure( arg_data, arg_keys_for_categorizing_nodes = None, arg_bool_search_entire_tree = False )\n\n This method takes arg_data, and builds the data tree based on arg_data\u2019s contents.\n\n arg_data can be one of the following:\n -dict\n -nested dict\n -list of dicts\n -another tree node\n\n Note:\n\n For users who want to use this library for importing data, I recommend Python\u2019s built-in json\n library for json data, and xml.etree cElementTree ( xml ) for actual parsing. Produce a nested dict,\n or list of dicts from the data using these, and then pass it to setup_tree_based_on_data_structure\n as arg_data.\n\n Arguments:\n\n arg_data - This can be any of the following types:\n\n -Simple dict\n -Nested dict\n -List of dicts\n -Another data_tree\n\n arg_keys_for_categorizing_nodes - This can be either a list, or a single key.\n\n This argument is meant for \"lists of dicts.\"\n\n If the code calling this method passes a list of dicts to arg_data, this method will use the arg_keys_for_categorizing_nodes\n to reference the corresponding values in the dicts. If arg_keys_for_categorizing_nodes is a list, the method will continue\n sub-dividing the categories until there are no keys left.\n\n Example:\n\n arg_data = [\n\n # Exists at index 0 of list\n { \"key_1\" : \"value_1\",\n \"key_2\" : \"value_2\",\n \"key_3\" : \"value_3\", },\n\n # Exists at index 1 of list\n { \"key_1\" : \"value_4\",\n \"key_2\" : \"value_5\",\n \"key_3\" : \"value_6\", },\n ]\n\n arg_keys_for_categorizing_nodes = [ \"key_1\", \"key_2\", ]\n\n Resulting tree:\n\n { \"value_1\" : { \"value_2\" : arg_data[ 0 ] },\n \"value_4\" : { \"value_5\" : arg_data[ 1 ] }, }\n\n Note: There's no hard limit to the resulting tree's depth, aside from the hardware running this library.\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n\n Example uses with python parsing libraries...\n\n Example use for xml with cElementTree:\n\n from xml.etree import cElementTree\n\n tree = cElementTree.parse('your_file.xml')\n root = tree.getroot()\n xmldict = XmlDictConfig(root)\n\n Or, if you want to use an XML string:\n\n root = cElementTree.XML(xml_string)\n xml_dict = XmlDictConfig(root)\n\n setup_tree_based_on_data_structure( arg_data = xml_dict )\n\n\n Example use with json\n\n import json\n\n _list_of_dicts = json.loads( json_string )\n\n setup_tree_based_on_data_structure( arg_data = _list_of_dicts, arg_keys_for_categorizing_nodes = [ \"key_0\", \"key_1\", ], )\n\n\n\ntemplate_example_for_arg_callable_filter_in_get_int_count_for_node_children( arg_node )\n\n This is a support method for get_int_count_for_node_children(), and provides a working example\n of a valid object for arg_callable_filter.\n\n Callable requirements:\n\n -Accepts one argument: arg_node\n\n arg_node is any node that exists within the scope of get_int_count_for_node_children()'s actions.\n\n -Returned value interpreted as boolean logic\n\n This callable's value is assessed in an if statement.\n\n If this callable returns True value, then the counter increments by 1. If False, the count does\n not increment.\n\n Action specific to this template:\n\n This callable will cause get_int_count_for_node_children() to provide a count for only nodes which\n contain data other than None.\n\n Equivalent lambda:\n\n lambda arg_node : not arg_node._object_stored_in_node == None\n\n\n\ntemplate_example_for_arg_callable_filter_in_get_list_of_pairs_paths_and_node_children( *args )\n\n This method is a template / example for what to pass via arg_callable_filter to get_list_of_pairs_paths_and_node_children()\n\n get_list_of_pairs_paths_and_node_children() interprets this method's returned value as boolean logic.\n\n Any callable passed this way has the following requirements:\n\n -Must accommodate two arguments: arg_iterable_path and arg_node\n\n First argument - This is the iterable of keys leading to arg_node when traversing the tree\n\n 2nd argument - This is the node located at the end of arg_list_path\n\n -Returned value is always assessed as boolean logic\n\n Details specific to this example:\n\n This callable causes get_list_of_pairs_paths_and_node_children() to return only leaf nodes\n\n Equivalent lambda:\n lambda *args : not args[ 1 ]._dict_of_keys_and_node_children\n\n\n\ntemplate_example_for_arg_callable_filter_in_get_list_of_pairs_paths_and_node_children_relevant_to_object( *args )\n\n This method is a template / example for what to pass via arg_callable_filter to\n get_list_of_pairs_paths_and_node_children_relevant_to_object()\n\n get_list_of_pairs_paths_and_node_children_relevant_to_object() interprets this method's returned value as boolean logic.\n\n Any callable passed this way has the following requirements:\n\n -Must accommodate two arguments\n\n First argument - This is the iterable of keys leading to arg_node when traversing the tree\n\n 2nd argument - This is the node located at the end of arg_list_path\n\n -Returned value is always assessed as boolean logic\n\n Details specific to this example:\n\n This callable causes get_list_of_pairs_paths_and_node_children_relevant_to_object() to return only leaf nodes\n\n Equivalent lambda:\n lambda arg_iterable_path, arg_node : not arg_node._dict_of_keys_and_node_children\n\n\n\ntemplate_example_for_arg_callable_formatter_in_get_int_count_for_node_children( arg_node )\n\n This is a support method for get_int_count_for_node_children(), and provides a working example\n of a valid object for arg_callable_filter.\n\n Callable requirements:\n\n -Accepts one argument: arg_node\n\n arg_node is any node that exists within the scope of get_int_count_for_node_children()'s actions.\n\n -Returned value is an int\n\n This callable's value is the count returned by get_int_count_for_node_children().\n\n Action specific to this template:\n\n This callable causes get_int_count_for_node_children() to sum the lengths of each node's _object_stored_in_node\n attribute.\n\n Nodes with iterable objects have the lengths of the iterables counted instead.\n\n Nodes storing only None, aren't counted.\n\n All other values for _object_stored_in_node return 1.\n\n\n\ntemplate_example_for_arg_callable_formatter_in_get_list_of_pairs_paths_and_node_children( *args )\n\n This method is a template / example for what to pass via arg_callable_formatter to get_list_of_pairs_paths_and_node_children()\n\n Any callable passed this way has the following requirements:\n\n -Must accommodate two arguments: arg_iterable_path and arg_node\n\n First argument - This is the iterable of keys leading to arg_node when traversing the tree\n\n 2nd argument - This is the node located at the end of arg_list_path\n\n -Returned value: despite get_list_of_pairs_paths_and_node_children()'s name, the returned value can be pretty much\n anything. Despite this, its still recommended that a callable passed this way returns a pair consisting of a path and node.\n\n Details specific to this example:\n\n This callable causes get_list_of_pairs_paths_and_node_children() to return a list of pairs:\n -String paths\n\n -Objects stored in the nodes at each path\n\n Equivalent lambda:\n lambda arg_iterable_path, arg_node : [ self._string_delimiter_for_path.join( [ str( item_path_part ) for item_path_part in arg_iterable_path ] ), arg_node._object_stored_in_node, ]\n\n\n\ntemplate_example_for_arg_callable_formatter_in_get_list_of_pairs_paths_and_node_children_relevant_to_object( *args )\n\n This method is a template / example for what to pass via arg_callable_formatter to\n get_list_of_pairs_paths_and_node_children_relevant_to_object()\n\n Any callable passed this way has the following requirements:\n\n -Must accommodate two arguments\n\n First argument - This is the iterable of keys leading to arg_node when traversing the tree\n\n 2nd argument - This is the node located at the end of arg_list_path\n\n -Returned value: despite get_list_of_pairs_paths_and_node_children_relevant_to_object()'s name, the returned value can be\n pretty much anything. Despite this, its still recommended that a callable passed this way returns a pair consisting of a\n path and node.\n\n Details specific to this example:\n\n This callable causes get_list_of_pairs_paths_and_node_children_relevant_to_object() to return a list of pairs:\n -String paths\n\n -Objects stored in the nodes at each path\n\n Equivalent lambda:\n lambda arg_iterable_path, arg_node : [ self._string_delimiter_for_path.join( [ str( item_path_part ) for item_path_part in arg_iterable_path ] ), arg_node._object_stored_in_node, ]\n\n\n\ntemplate_for_arg_callable_filter_in_get_dict_of_paths_and_node_children( arg_iterable_path, arg_node )\n\n This method is a template / example of the type of callable to pass to get_dict_of_string_paths_and_node_children()\n via arg_callable_filter\n\n General requirements for any callable passed this way:\n -Must be able to support two arguments. The arguments can have any name, but the first will always be\n a path ( list of keys ) to reach arg_node within the tree.\n\n -get_dict_of_string_paths_and_node_children() will always interpret this callable's returned value as boolean logic\n\n Details specific to this example:\n\n This filter returns True, if arg_node has no child nodes, causing get_list_of_node_children() to return\n only leaf nodes.\n\n Equivalent lambda:\n\n lambda arg_iterable_path, arg_node : not arg_node._dict_of_keys_and_node_children\n\n\n\ntemplate_for_arg_callable_formatter_in_get_dict_of_paths_and_node_children( arg_iterable_path, arg_node )\n\n This method is a template / example of the type of callable to pass to arg_callable_formatter\n in get_dict_of_string_paths_and_node_children()\n\n It determines what values are actually stored in the returned dict.\n\n In order for this type of callable to work in get_dict_of_string_paths_and_node_children(), it\n must satisfy the following requirements:\n\n -Accept two arguments: arg_list_path and arg_node\n\n arg_iterable_path - This is always an iterable of keys to traverse the tree to reach arg_node\n\n arg_node - This is always the node stored at the end of arg_list_path\n\n -This must always return a pair, and the value at index 0, must be a hashable type, the value at index 1 can be anything.\n\n Note: since arg_list_path is always a list, it can't be returned as-is without triggering an error.\n\n Notes for this specific example:\n\n This callable causes get_dict_of_string_paths_and_node_children() to return a dict of paths and the objects stored in\n each corresponding node.\n\n [ _string_path_hashable, arg_node._object_stored_in_node, ]\n\n Equivalent lambda:\n\n lambda arg_iterable_path, arg_node : [ self._string_delimiter_for_path.join( [ str( item_path_part ) for item_path_part in arg_iterable_path ] ), arg_node._object_stored_in_node, ]\n\n\n\nvalues( arg_bool_search_sub_tree = False, arg_bool_search_entire_tree = False )\n\n Supports iteration similarly to dict.values()\n\n Arguments:\n\n arg_bool_search_entire_tree - Searches entire tree if True, and only the sub-tree if False.\n\n Note: a True value for this parameter supersedes arg_bool_search_sub_tree's value\n\n arg_bool_search_sub_tree - If True, searches sub tree. If False, searches only immediate node children.\n\n\n\n\nINHERITANCE\n\nTo help with adapting the library to more custom uses, the docstrings contain labels to help identify\nsafe methods for overriding, as well as understanding how each method factors into actions such as tree\nbuilding.\n\nIMPORTANT_FOR_BUILDING_TREE\n\nThis marker exists in docstrings for methods which play active roles in adding and removing nodes.\n\nRECOMMENDED_FOR_CUSTOM_OVERRIDES\n\nMethods with this tag carry out common operations, and are low risk to change.\n\nExample: The code typically calls logic_objects_match() when comparing objects stored in nodes. Instead of doing\na direct comparison, the code calls this method.\n\n\n\n\nPERFORMANCE NOTES\n\nDisclaimer: all of these design decisions are based off timed tests across Python 3.4 - 3.7. None of it\nis 'set in stone,' since the performance advantages could change in a future Python version.\n\n\nPython versions\n\nPython 3.7 definitely has the fastest overall execution, so a lot of the research focused on this.\n\n\nCompiling\n\n-Packaging employs wheels to support compiling to binary format\n\n-Cython builds C files from annotated Python source\n\nNote: Annotations don't exist in the basic Python source (.py) since in speed tests, they appeared to\nslow down \"pure python\" execution.\n\nNote: In timed tests, binaries produced from Python-annotated files had higher average speeds than binaries\nproduced using Cython's original code\n\n\nLoops\n\n-There's no method recursion. Its impractically slow, and runs the risk of colliding with one of Python's\nhard-limits.\n\n-The code handles recursion through deques / stacks since they benefit from quick append / pop methods.\n\n-The minimalist recursion loops exist in four variations:\n--Basic - no callables or logic checks. This is the fastest execution.\n--Filter only / Formatter only - both of these only make one call to their callable object, each iteration\n--Both filter and formatter - these loops call both callable once per iteration\n\n-Since there are four combinations, all logic checks for optional parameters occur only once, to select\nwhich loop to use\n\n-The methods prioritize list comprehensions whenever possible / reasonable.\n\n-All paths exist as lists internally, except where hashable keys are necessary. In cases involving hashes,\nlist-to-string conversions still had the fastest average execution.\n\n-While the library can support them as arguments, the library doesn't use tuples or generators internally,\nsince they both come with significant speed penalties.\n\n--Note: Generators are only practical when a single-instance list can threaten to hit the memory threshold.\nSince Python 3+ deletes local variables as soon as they fall out of scope, the chances of actually needing\na generator is incredibly unlikely, and not worth the speed penalty.\n\n--Note: Tuples, in timed tests, are only slightly faster than converting a list to a string, and not reliably so.\nAdditionally, converting a list to a tuple is slower than a list to a string, so in that case, they actually\nbecome a performance liability. Leaving them out provides the best average performance.\n\n--Note: The library uses careful coding practices to avoid unnecessary list duplicates, or modifications.\n\nData structures\n\n-Generally, all things key-related are handled by dicts\n\n-Lists handle everything that doesn't benefit from a hash\n\n-Tuples exist exclusively to support hashes\n\n\nException handling\n\n-Try / excepts exist only where actual exceptions can occur. Except rare single instances like importing modules,\nthere is no point where exceptions can happen during an 'error free' run.\n\n\n\n\nNotes on testing criteria:\n\n-Time comparisons were calculated as 'averages' across 50-100 automatic repetitions of\nvarious tests\n\n-Each test, when possible, involved hundreds of the same actions with all independent\nvariables removed when the time recording started.\n\n\n\n\n\n\n\n\n\n\n\n\nMemory leak mitigation\u2026\n\nSince this is a tree, there are safeguards against unintentionally preserved references.\n\nAll methods responsible for adding or removing nodes from the tree have \"_integrate\" as their\nprefix. This narrows the potential internal causes for memory leaks to the following...\n\nData_tree_node - 4 methods ( 2 of 6 \"_integrate\" methods are simple wrappers )\n\nData_tree_node_with_quick_lookup - 7 methods\n\n\n\nGarbage collection\u2026\n\nThe tree works off Python\u2019s existing process, and focuses on removing key references within the tree.\n\nExamples:\n\nData_tree_node: If you pop a node with child nodes, the tree severs the links to and from its parent.\n\nData_tree_fast_absolute_paths: This class severs the links like Data_tree_node, and also removes all absolute path\nreferences to both the popped node and all its children.\n\nNote: weakref is never used to prevent broken links.\n\n\n\nKNOWN ISSUES\n\n#Silenced deprecation warning\n\nThe actual message is: \u201cDeprecationWarning: Deprecated since Python 3.4. Use importlib.util.find_spec() instead.\u201d\n\nThis is a known issue with using setuptools for compiling. I\u2019ve only seen this on Mac OS, using Python 3.7, with binaries. Under all other conditions\n( i.e. Debian Linux, pure python, etc. ), this warning doesn\u2019t appear.\n\nTo work around this issue, I left __init__.py out of list of files to compile and added code to the module to silence deprecation warnings. Since\nthis library only uses official packages, this shouldn\u2019t escalate beyond just the warning message.\n\n\n#Support for Windows OS is theoretical, but untested\n\nSince all the libraries used in this package\u2019s creation are official, and the binaries are compiled from pure python modules, this shouldn\u2019t be a problem.\n\n\n\nFUTURE PLANS\n\nThe future plans are still to focus on the main three areas: usability, flexibility, and performance.\n\nGeneral notes:\n\n-Parallel processing: Need to investigate further. It shows promise on paper, but in practice, each processor call causes\nsignificant slow-downs during execution (processors tested: 4).\n\n-Annotations: Although a lot of the code is already annotated, there's still plenty of room for enhancements here.\n\n\n\n\nNotes on features considered out of scope:\n\n-Data parsing from sources such as files / strings / binary\n\nThere are no plans on integrating this tree with parsers directly because that requires a lot of assumptions about the incoming parse-able data,\nand every library user's situation is different.\n\n-Additional data science-related features\n\nThere aren't any explicit plans to add specifically \"data science\" features. That doesn't mean it won't happen, but this tree isn't designed with\n\"data science\" in mind. There are already-existing trees better tailored for that.\n\n-Python 2.7 compatibility\n\nNo plans for retroactive compatibility. This library is designed more for exploiting performance boosts from each consecutive version of Python,\nand in order to do so reliably, it needs to stay future-facing.\n\nLICENSE (MIT)\n\nMIT License\n\nCopyright (c) 2019 James Hazlett\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\nHELPFUL LINKS\n\nLambdas\nhttps://www.w3schools.com/python/python_lambda.asp\n\nGarbage collector\nhttps://docs.python.org/3/library/gc.html\n\n\n\n\n\n-----DRAFT-----\n-----DRAFT-----\n-----DRAFT-----\n-----DRAFT-----\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/jrhazlett/data_tree", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "data-tree", "package_url": "https://pypi.org/project/data-tree/", "platform": "", "project_url": "https://pypi.org/project/data-tree/", "project_urls": { "Homepage": "https://github.com/jrhazlett/data_tree" }, "release_url": "https://pypi.org/project/data-tree/0.0.1/", "requires_dist": null, "requires_python": "", "summary": "", "version": "0.0.1" }, "last_serial": 5369495, "releases": { "0.0.1": [ { "comment_text": "", "digests": { "md5": "c907e59406cacb0ec11a993d66d5ae38", "sha256": "904cc0545cb3c8bc3fb81928c754b10510c1ae8ca9229de0c5cc8817eb301113" }, "downloads": -1, "filename": "data_tree-0.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", "has_sig": false, "md5_digest": "c907e59406cacb0ec11a993d66d5ae38", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": null, "size": 909689, "upload_time": "2019-06-06T23:17:04", "url": "https://files.pythonhosted.org/packages/65/d7/2d9e375d9f2dc3e932c6d6ded1ec18f95be493d070d19aebd071d177746d/data_tree-0.0.1-cp37-cp37m-macosx_10_9_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "c2790b68d23fea5b9391c37e799b3e1d", "sha256": "06f2a18b372cf2451166d426591f6e6fc73a7aabcad97255d50927aa3c3d5a0e" }, "downloads": -1, "filename": "data_tree-0.0.1.tar.gz", "has_sig": false, "md5_digest": "c2790b68d23fea5b9391c37e799b3e1d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 504094, "upload_time": "2019-06-06T23:17:08", "url": "https://files.pythonhosted.org/packages/f5/20/34af987bb524bb7e008cb2bcf82a43f5b10678165455bb298fb38d18d293/data_tree-0.0.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c907e59406cacb0ec11a993d66d5ae38", "sha256": "904cc0545cb3c8bc3fb81928c754b10510c1ae8ca9229de0c5cc8817eb301113" }, "downloads": -1, "filename": "data_tree-0.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", "has_sig": false, "md5_digest": "c907e59406cacb0ec11a993d66d5ae38", "packagetype": "bdist_wheel", "python_version": "cp37", "requires_python": null, "size": 909689, "upload_time": "2019-06-06T23:17:04", "url": "https://files.pythonhosted.org/packages/65/d7/2d9e375d9f2dc3e932c6d6ded1ec18f95be493d070d19aebd071d177746d/data_tree-0.0.1-cp37-cp37m-macosx_10_9_x86_64.whl" }, { "comment_text": "", "digests": { "md5": "c2790b68d23fea5b9391c37e799b3e1d", "sha256": "06f2a18b372cf2451166d426591f6e6fc73a7aabcad97255d50927aa3c3d5a0e" }, "downloads": -1, "filename": "data_tree-0.0.1.tar.gz", "has_sig": false, "md5_digest": "c2790b68d23fea5b9391c37e799b3e1d", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 504094, "upload_time": "2019-06-06T23:17:08", "url": "https://files.pythonhosted.org/packages/f5/20/34af987bb524bb7e008cb2bcf82a43f5b10678165455bb298fb38d18d293/data_tree-0.0.1.tar.gz" } ] }