FlexTree - A flexible and intuitive Python library for creating and manipulating tree data structures
Project description
FlexTree
A flexible and intuitive Python library for creating and manipulating tree data structures.
Warning Some code or documentation in this project may be generated by AI. While the developer has tested the code, please review and validate all outputs before using them in production. Use at your own risk.
Features
- TreeNode: Individual nodes with name, content, and parent-child relationships
- Tree: Complete tree structure with operations for insertion, deletion, and modification
- JSON Serialization: Save and load trees to/from JSON files
- Tree Visualization: ASCII art tree drawing functionality
- Flexible Content: Store any Python object as node content
- Search Operations: Find nodes by name or index
- Tree Statistics: Calculate depth, width, and node count metrics
Installation
Install using pip:
pip install flextree
Or, you can install by cloning this repository:
git clone https://github.com/znzhao/flextree.git
cd flextree
pip install -e .
Quick Start
Creating a Flex Tree
from flextree import TreeNode, Tree, draw_tree
# Create nodes
root = TreeNode("Company", "Acme Corp")
engineering = TreeNode("Engineering", "Tech Department")
marketing = TreeNode("Marketing", "Marketing Department")
backend = TreeNode("Backend", "Server Team")
frontend = TreeNode("Frontend", "UI Team")
# Build tree structure
root.add_child(engineering)
root.add_child(marketing)
engineering.add_child(backend)
engineering.add_child(frontend)
# Create a tree object
company_tree = Tree(root)
# Visualize the tree
draw_tree(root)
Output:
└── Company: Acme Corp
├── Engineering: Tech Department
│ ├── Backend: Server Team
│ └── Frontend: UI Team
└── Marketing: Marketing Department
Working with Tree Operations
# Insert a new department
new_dept = TreeNode("HR", "Human Resources")
company_tree.insert("Company", new_dept)
# Modify existing content
company_tree.alter("Backend", "Backend Development Team")
# Get a subtree
eng_subtree = company_tree.get("Engineering")
print(eng_subtree.summary())
# Delete a node
company_tree.delete("HR")
Pythonic Tree Navigation with getitem
Flex Tree supports intuitive Python-style indexing for tree navigation using square bracket notation. This makes working with trees feel natural and pythonic.
# Tree objects support getitem operations
company_tree = Tree(root)
# Access child by name - returns Tree object
eng_dept = company_tree["Engineering"]
print(f"Department: {eng_dept.root.name}")
# Multiple selection using string lists
departments = company_tree[["Engineering", "Marketing"]]
print(f"Selected: {[tree.root.name for tree in departments]}")
# Chain operations for deep navigation
if eng_dept:
# Further navigation on the subtree
print(f"Engineering has {len(eng_dept.root.children)} teams")
Important Notes:
- Only
Treeobjects support getitem operations (tree[key]) TreeNodeobjects do not support getitem - usenode.childrenfor direct access- Tree getitem returns
Treeobjects (single) orList[Tree](multiple) - Multiple selection only supports string-only lists:
tree[["name1", "name2"]] - For other access patterns (by index, slicing), work with
TreeNode.childrendirectly
Storing Complex Data
# Nodes can store any Python object
employee_data = {
"name": "John Doe",
"role": "Senior Developer",
"skills": ["Python", "JavaScript", "Docker"],
"start_date": "2023-01-15"
}
employee_node = TreeNode("john_doe", employee_data)
backend.add_child(employee_node)
JSON Serialization
# Save tree to JSON file
company_tree.save_json("company_structure.json")
# Load tree from JSON file
loaded_tree = Tree.load_json("company_structure.json")
Tree Statistics
# Get tree dimensions
print(f"Max depth: {company_tree.max_depth()}")
print(f"Max width: {company_tree.max_width()}")
print(f"Node count: {company_tree.count()}")
# Get node summary
print(company_tree.summary())
Tree Visualization with draw_tree
The draw_tree function provides a beautiful ASCII art representation of your tree structure using Unicode box-drawing characters. This is perfect for debugging, documentation, or simply understanding your tree's structure.
Basic Usage
from flextree import TreeNode, draw_tree
# Create a Flex tree
root = TreeNode("root", "I am root")
child1 = TreeNode("child1", "First child")
child2 = TreeNode("child2", "Second child")
grandchild = TreeNode("grandchild", "I'm nested!")
root.add_child(child1)
root.add_child(child2)
child1.add_child(grandchild)
# Draw the tree
draw_tree(root)
Output:
└── root: I am root
├── child1: First child
│ └── grandchild: I'm nested!
└── child2: Second child
Drawing Subtrees
You can draw any subtree by passing any node as the starting point:
# Draw only the subtree starting from child1
draw_tree(child1)
Output:
└── child1: First child
└── grandchild: I'm nested!
Special Content Handling
The draw_tree function has special handling for dictionary content. By default, if your node's content is a dictionary containing a 'definition' key, it will display that value instead of the entire dictionary:
# Create nodes with dictionary content
root = TreeNode("concept", {"definition": "A tree data structure", "type": "data_structure"})
child = TreeNode("node", {"definition": "A single element in the tree", "properties": ["name", "content"]})
root.add_child(child)
draw_tree(root)
Output:
└── concept: A tree data structure
└── node: A single element in the tree
Custom Dictionary Key
You can specify a custom key to display from dictionary content using the key parameter:
# Create nodes with custom dictionary keys
root = TreeNode("product", {"name": "Flex Tree Library", "description": "Tree data structure", "version": "1.0"})
child = TreeNode("feature", {"name": "JSON Export", "type": "functionality"})
root.add_child(child)
# Use 'name' key instead of 'definition'
draw_tree(root, key="name")
Output:
└── product: Flex Tree Library
└── feature: JSON Export
Without a matching key, the entire dictionary would be displayed:
root = TreeNode("data", {"key": "value", "number": 42})
draw_tree(root, key="description") # Key doesn't exist
Output:
└── data: {'key': 'value', 'number': 42}
Complex Tree Visualization
Here's a more complex example showing different content types:
# Create a knowledge tree
knowledge = TreeNode("Programming", "Software Development")
# Programming languages
languages = TreeNode("Languages", {"definition": "Programming languages", "count": 3})
python = TreeNode("Python", {"definition": "High-level programming language", "year": 1991})
javascript = TreeNode("JavaScript", {"definition": "Web programming language", "year": 1995})
java = TreeNode("Java", {"definition": "Object-oriented programming language", "year": 1995})
# Concepts
concepts = TreeNode("Concepts", "Core programming concepts")
oop = TreeNode("OOP", {"definition": "Object-Oriented Programming", "paradigm": "object-oriented"})
functional = TreeNode("Functional", {"definition": "Functional Programming", "paradigm": "functional"})
# Build the tree
knowledge.add_child(languages)
knowledge.add_child(concepts)
languages.add_child(python)
languages.add_child(javascript)
languages.add_child(java)
concepts.add_child(oop)
concepts.add_child(functional)
# Visualize
draw_tree(knowledge)
Output:
└── Programming: Software Development
├── Languages: Programming languages
│ ├── Python: High-level programming language
│ ├── JavaScript: Web programming language
│ └── Java: Object-oriented programming language
└── Concepts: Core programming concepts
├── OOP: Object-Oriented Programming
└── Functional: Functional Programming
Using with Tree Objects
You can also use draw_tree with Tree objects by accessing their root:
tree = Tree(root)
draw_tree(tree.root)
# Or use the convenient .draw() method
tree.draw()
# With custom key
tree.draw(key="name")
Convenient .draw() Methods
Both TreeNode and Tree objects have a convenient .draw() method:
# Draw from any TreeNode
node.draw()
node.draw(key="custom_key")
# Draw entire Tree
tree.draw()
tree.draw(key="custom_key")
Tips for Better Visualization
- Keep node names concise: Long names can make the tree hard to read
- Use meaningful content: The content appears after the colon, so make it descriptive
- Use the 'definition' key: For dictionary content, use a 'definition' key for cleaner output
- Consider tree depth: Very deep trees might be hard to read in console output
Unicode Characters Used
The draw_tree function uses these Unicode box-drawing characters:
└──(U+2514 U+2500 U+2500): For the last child at each level├──(U+251C U+2500 U+2500): For intermediate children│(U+2502): For vertical continuation lines(4 spaces): For padding after the last child
API Reference
TreeNode
Constructor
TreeNode(name: str, content: Any = None): Create a new tree node
Methods
add_child(child: TreeNode): Add a child noderemove_child(child: Union[TreeNode, str, int]): Remove a child by node, name, or indexget_child(key: Union[str, int]): Get child by name or indexset_content(content: Any): Update node contentget_subtree(name: str): Find and return subtree by node nameto_dict(): Convert node and its subtree to dictionaryfrom_dict(data: Dict): Create node from dictionary (static method)max_depth(): Calculate maximum depth from this nodemax_width(): Calculate maximum width from this nodesummary(): Get a formatted summary of the nodedraw(key: str = "definition"): Print ASCII art representation of the node and its subtree
Tree
Constructor
Tree(root: TreeNode): Create a tree with the given root node
Methods
insert(parent_name: str, node: TreeNode): Insert node under specified parentdelete(node_name: str): Delete node by namealter(node_name: str, new_content: Any): Change content of specified nodeget(key: Union[str, int]): Get subtree by name or indexsave_json(filepath: str): Save tree to JSON fileload_json(filepath: str): Load tree from JSON file (static method)max_depth(): Get maximum depth of entire treemax_width(): Get maximum width of entire treesummary(): Get formatted summary of the treedraw(key: str = "definition"): Print ASCII art representation of the entire tree
Utility Functions
draw_tree
draw_tree(node: TreeNode, prefix: str = "", is_last: bool = True, key: str = "definition"): Print ASCII art representation of tree
Parameters:
node(TreeNode): The root node of the tree/subtree to visualizeprefix(str, optional): Internal parameter for indentation. Leave as default.is_last(bool, optional): Internal parameter for formatting. Leave as default.key(str, optional): Dictionary key to display for dict content. Defaults to "definition".
Special Features:
- Uses Unicode box-drawing characters for clean tree visualization
- Customizable handling for dictionary content with specified key
- Recursively displays entire subtree structure
- Works with any TreeNode as starting point
Usage Examples:
# Basic usage - draw entire tree
draw_tree(root_node)
# Draw subtree starting from any node
draw_tree(some_child_node)
# Use custom dictionary key
draw_tree(root_node, key="name")
# The function automatically handles the formatting
Examples
The Flex Tree package includes ready-to-run example files that demonstrate all the key features:
Running the Examples
from flextree import examples
examples()
Requirements
- Python 3.6+
- No external dependencies for core functionality
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
Version 0.1.6
- Maintained backward compatibility with all existing features
- Prepared for future enhancements and optimizations
- Consolidated changelog documentation
Version 0.1.5
- Added pythonic getitem functionality for Tree objects
- Enhanced examples with copy-paste ready CODE/OUTPUT format
- Improved documentation with comprehensive getitem usage guide
- Updated TreeNode behavior: removed getitem support (use Tree objects instead)
- Added comprehensive test coverage for count and getitem operations
- Refined Tree getitem to support string-only lists for multiple selection
- Updated package documentation and README with getitem introduction
Version 0.1.4
- Added count() methods to TreeNode and Tree classes
- Enhanced tree statistics functionality
- Updated examples and documentation
Version 0.1.3
- Package name changed to "Flex Tree"
- Improved package structure and imports
- Added quick_examples and examples functions to public API
Version 0.1.2
- Bug fixes and improvements
- Enhanced tree manipulation methods
Version 0.1.1
- Minor updates and documentation improvements
Version 0.1.0
- Initial release
- Basic tree node and tree operations
- JSON serialization support
- Tree visualization
- Comprehensive test suite
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flextree-0.1.7.tar.gz.
File metadata
- Download URL: flextree-0.1.7.tar.gz
- Upload date:
- Size: 23.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d7b837ca28ba14a93350af59085ccc25ce8741925bc832a250c6e219b4a6403
|
|
| MD5 |
b99c20c05fecc7b8979e82052ccf3e57
|
|
| BLAKE2b-256 |
45c683c4503250c40af4bef826ac42fe981d1dbfad7b76ecfb7a3e014b85bf30
|
File details
Details for the file flextree-0.1.7-py3-none-any.whl.
File metadata
- Download URL: flextree-0.1.7-py3-none-any.whl
- Upload date:
- Size: 19.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c0729c25a20f7b2308cc235531c76b617a2162ce39d322f71c3037102258b2a
|
|
| MD5 |
2e2d1f8e8a0e77edfb7e1cf7e9f87f36
|
|
| BLAKE2b-256 |
3f3fe884e20478805e54a726acf7f5f6bd8b9e348bc542622f16e8c600879fe2
|