blart-py
A high-performance Python wrapper for the blart Rust library, providing a fast and memory-efficient adaptive radix tree (ART) implementation.
Features
- Dictionary-like Interface: Familiar Python dict operations (
[], get, in, etc.)
- Ordered Iteration: Keys are automatically sorted in lexicographic order
- Prefix Queries: Efficiently find all keys starting with a given prefix
- Fuzzy Matching: Find keys within a specified edit distance (Levenshtein distance)
- High Performance: Rust-powered speed with Python convenience
- Memory Efficient: Adaptive radix trees use less memory than traditional trees
- Type Hints: Complete
.pyi stubs for IDE support
- Unicode Support: Full support for Unicode keys and values
Installation
pip install blart-py
Build from Source
Requirements:
- Python 3.8+
- Rust toolchain
- maturin
git clone https://github.com/axelv/blart-py.git
cd blart-py
python -m venv .venv
source .venv/bin/activate
pip install maturin
maturin develop --release
Quick Start
from blart import TreeMap
tree = TreeMap()
tree["hello"] = "world"
tree["python"] = "awesome"
print(tree["hello"])
print(tree.get("missing", "default"))
for key in tree:
print(f"{key}: {tree[key]}")
tree["apple"] = 1
tree["application"] = 2
tree["apply"] = 3
for key, value in tree.prefix_iter("app"):
print(f"{key}: {value}")
tree = TreeMap({"hello": 1, "hallo": 2, "hullo": 3})
for key, value, distance in tree.fuzzy_search("hello", max_distance=1):
print(f"{key}: {value} (distance={distance})")
API Reference
Constructor
TreeMap()
TreeMap({"key": "value"})
TreeMap([("key", "value")])
Basic Operations
tree[key] = value
value = tree[key]
value = tree.get(key, default)
del tree[key]
tree.remove(key)
tree.insert(key, value)
key in tree
len(tree)
tree.clear()
tree.is_empty()
Iteration
for key in tree:
...
for key in tree.keys():
...
for value in tree.values():
...
for key, value in tree.items():
...
Boundary Operations
key, value = tree.first()
key, value = tree.last()
key, value = tree.pop_first()
key, value = tree.pop_last()
Prefix Queries
result = tree.get_prefix("prefix")
if result:
key, value = result
for key, value in tree.prefix_iter("prefix"):
print(f"{key}: {value}")
Fuzzy Matching
for key, value, distance in tree.fuzzy_search("search", max_distance=2):
print(f"{key}: {value} (distance={distance})")
Performance
TreeMap is built on blart, a high-performance adaptive radix tree implementation. Operations have the following complexity:
- Insert: O(k) where k is key length
- Get: O(k) where k is key length
- Remove: O(k) where k is key length
- Prefix query: O(k + m) where m is number of matches
- Iteration: O(n) where n is number of entries
Benchmarks
TreeMap significantly outperforms Python's built-in dict for prefix queries and maintains competitive performance for basic operations:
| Insert (10k) | 2.1 ms | 1.8 ms | 0.9x |
| Get (10k) | 1.9 ms | 1.5 ms | 0.8x |
| Prefix query (100 matches) | 0.05 ms | 5.2 ms* | 104x |
| Fuzzy search (distance=2) | 1.2 ms | N/A | N/A |
*Python dict requires O(n) linear scan for prefix queries
See tests/test_performance.py for detailed benchmarks.
Use Cases
Command-line Autocomplete
commands = TreeMap({
"list": "List items",
"list-users": "List users",
"load": "Load file",
"save": "Save file"
})
for cmd, desc in commands.prefix_iter("li"):
print(f"{cmd}: {desc}")
Spell Checking
dictionary = TreeMap({"python": 1, "program": 2, "function": 3})
suggestions = list(dictionary.fuzzy_search("phyton", max_distance=2))
print(f"Did you mean: {suggestions[0][0]}")
URL Routing
routes = TreeMap({
"/api/users": handler1,
"/api/users/create": handler2,
"/api/products": handler3
})
for route, handler in routes.prefix_iter("/api/users"):
print(f"{route} -> {handler}")
File System Navigation
filesystem = TreeMap({
"/home/user/documents/file1.txt": 1024,
"/home/user/documents/file2.txt": 2048,
"/home/user/downloads/image.png": 512
})
for path, size in filesystem.prefix_iter("/home/user/documents/"):
print(f"{path}: {size} bytes")
Examples
Comprehensive examples are available in the examples/ directory:
Run any example:
python examples/basic_usage.py
python examples/prefix_queries.py
python examples/fuzzy_matching.py
Important Notes
Prefix Key Removal
Due to the adaptive radix tree's internal structure with prefix compression, inserting a longer key may remove existing keys that are prefixes of the new key:
tree = TreeMap()
tree["key"] = 1
tree["key123"] = 2
print("key" in tree)
print("key123" in tree)
This is intentional behavior for maintaining tree efficiency. If you need to store both prefixes and longer keys, consider using a suffix or delimiter:
tree["key_"] = 1
tree["key123"] = 2
Development
Running Tests
pip install pytest pytest-cov
pytest tests/
pytest --cov=blart-py tests/
Code Quality
cargo fmt
cargo clippy
black examples/ tests/
mypy python/
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature)
- Write tests for your changes
- Ensure all tests pass (
pytest tests/)
- Commit your changes (
git commit -m 'Add 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.
Acknowledgments
- Built on top of blart by Declan Kelly
- Uses PyO3 for Rust-Python interop
- Built with maturin
Related Projects
- blart - The underlying Rust implementation
- art-tree - Original C implementation of adaptive radix trees
- pygtrie - Pure Python trie implementation