Skip to main content

Contributors Forks Stargazers Issues MIT License


Weaktree

Model Data Trees Without Worrying About Lifetimes
Explore the docs »

Report Bug · Request Feature

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. API Reference
  5. License
  6. Contact

About The Project

Weaktree provides tree nodes that don't make strong references to their data, and can clean themselves up when their data expires. WeakTreeNodes also provide several handy iteration method for easy traversal.

(back to top)

Getting Started

Weaktree is written in pure python, with no system dependencies, and should be OS-agnostic.

Installation

Weaktree can be installed from the PyPI using pip:

pip install weaktree

and can be imported for use with:

import weaktree

(back to top)

Usage

Trees are built out of WeakTreeNodes. WeakTreeNodes possess a trunk and branches, which are used to define the hierarchy of the tree. Nodes with a trunk of None are considered root nodes.

Creating WeakTrees

Root nodes can be created with the WeakTreeNode constructor.

root = WeakTreeNode(some_object)

Branches can be added either by creating new nodes the same way and passing another node as the trunk, or by using WeakTreeNode.add_branch().

root.add_branch(another_object)

WeakTreeNode.add_branch() returns the new node, so they can be chained.

root.add_branch(third_object).add_branch(fourth_object).add_branch(fifth_object)

Trees can also be reorganized by assigning to the trunk property.

(back to top)

Accessing Data

The stored data of a Node is accessed by the data property. This will dereference the weakref, and return either the data object, or None if the object has expired.

Alternatively, if iterating over the tree by the values() method, the iterand will be the value from data.

(back to top)

Cleanup

WeakTreeNodes possess the cleanup_mode property. This is used to how the tree modifies itself when a Node's data reference expires. These values are accessible as constants in the WeakTreeNode class.

Prune

When the node expires, all descending nodes are removed from the tree.

Note: If there are strong references to those nodes elsewhere, they will be kept alive and the branches will not be fully unwound. However, they will no longer be reachable from other nodes in the tree.

Reparent

When the node expires, shift its branches up to its trunk. All descending nodes will be shifted up in the hierarchy.

No cleanup

When a node expires, leave the tree intact. Instead, the node will be "empty" and report a value of None.

Default

A node will do whatever its trunk node would do when it expires. For root nodes, this will be prune.

Callbacks

WeakTreeNodes feature an optional callback parameter for each node. The callback must take a weakreference object as its parameter. This will be the reference to the data value, just before it expires.

(back to top)

Tree Iteration

WeakTreeNodes provide several features to simplify traversal. All branch iteration follows insertion order of the branch nodes.

You can control the type of data provided in the iteration:

By Nodes

By using WeakTreeNode.nodes() or by directly iteration over the tree, you can traverse over the nodes themselves. This is comparable to the keys() method of dictionaries.

Example:

root = WeakTreeNode(some_object)

root.add_branch(one_fish).add_branch(two_fish)

root.add_branch(red_fish).add_branch(blue_fish)

for node in root.nodes():  # <- Same as `for node in root:`
    print(node)

    # Expected order: Node(some_object), Node(one_fish), Node(red_fish), Node(two_fish), Node(blue_fish)

By Values

By using WeakTreeNode.values(), you can iterate over the values of the nodes. This is comparable to the method of the same name in dictionaries.

Example:

root = WeakTreeNode(some_object)

root.add_branch(one_fish).add_branch(two_fish)

root.add_branch(red_fish).add_branch(blue_fish)

for node in root.values():
    print(node)

    # Expected output: some_object, one_fish, red_fish, two_fish, blue_fish

By Items

WeakTreeNode.items() provides an iterable that gives the pairs of nodes and values. This is comparable to the method of the same name in dictionaries.

You can also control how the iteration traverses the tree.

By Breadth

By using the breadth() method of any iterable, or directly iterating over one, you can specify that the traversal should happen in a breadth-first order, i.e. all nodes of the same level in the hierarchy are processed before moving on to the next level.

Example:

root = WeakTreeNode(some_object)

root.add_branch(one_fish).add_branch(two_fish)

root.add_branch(red_fish).add_branch(blue_fish)

for node in root.nodes().breadth():  # <- Same as `for node in root:`
    print(node)

    # Expected order: Node(some_object), Node(one_fish), Node(red_fish), Node(two_fish), Node(blue_fish)

By Depth

By using the depth() method of any iterable, you can specify that the traversal should happen in a dpeth-first order, i.e. branches are followed to their furthest reaches before moving on to side branches.

Example:

root = WeakTreeNode(some_object)

root.add_branch(one_fish).add_branch(two_fish)

root.add_branch(red_fish).add_branch(blue_fish)

for node in root.nodes().depth():
    print(node)

    # Expected order: Node(some_object), Node(one_fish), Node(two_fish), Node(red_fish), Node(blue_fish)

Towards the Root

By using the towards_root() method of any iterable, you can iterate towards the root of the tree.

Example:

root = WeakTreeNode(some_object)

two_fish_node = root.add_branch(one_fish).add_branch(two_fish)

root.add_branch(red_fish).add_branch(blue_fish)

for node in two_fish_node.nodes().towards_root():
    print(node)

    # Expected order: Node(two_fish), Node(one_fish), Node(some_object)

(back to top)

API Reference

class weaktree.WeakTreeNode(data: Any, trunk: WeakTreeNode | None, cleanup_mode: CleanupMode, callback: Callable | None)

The base unit of a weak tree. Stores a weak reference to its data, and cleans itself up per the cleanup mode when that data expires. If a callback is provided, that will be called when the reference expires as well.

method __init__(data: Any, trunk: WeakTreeNode | None, cleanup_mode: CleanupMode, callback: Callable | None) -> None

Creates a new WeakTreeNode, storing the passed data, and as a branch of trunk, if trunk is provided. Optionally, cleanup mode can be specified to determine how the node will behave when the data expires. Additionally, the optional callback can allow further customization of cleanup behavior.

property branches: set[WeakTreeNode]

Read-only.

A set representing any branches that descend from the node.

property cleanup_mode: CleanupMode

An enum value that determines how the node will clenaup after itself when its data expires.

property data: Any | None

The stored data. When called, dereferences and returns either a strong reference to the data, or None if the data has expired.

property trunk: WeakTreeNode | Node

The previous node in the tree. If None, the node is considered a root.

add_branch(data: Any, cleanup_mode: CleanupMode, callback: Callable | None) -> WeakTreeNode

Creates a new node as a branch of the calling node.

breadth() -> Iterator[WeakTreeNode]

Returns an iterator that will traverse the tree by nodes, in a breadth-first pattern, starting at the calling node. Branches will be traversed in insertion order.

depth() -> Iterator[WeakTreeNode]

Returns an iterator that will traverse the tree by nodes, in a depth-first pattern, starting at the calling node. Branches will be traversed in insertion order.

towards_root() -> Iterator[WeakTreeNode]

Returns an iterator that will traverse the tree by nodes, up to the root, starting at the calling node.

nodes() -> NodeIterable

Creates an iterable that allows for traversing the tree by nodes. Has the same breadth(), depth(), and towards_root() methods as WeakTreeNode

values() -> ValueIterable

Creates an iterable that allows for traversing the tree by data values. Has the same breadth(), depth(), and towards_root() methods as WeakTreeNode

items() -> ItemsIterable

Creates an iterable that allows for traversing the tree by node/value pairs. Has the same breadth(), depth(), and towards_root() methods as WeakTreeNode

(back to top)

License

Distributed under the MIT License. See LICENSE.txt for more information.

(back to top)

Contact

Better Built Fool - betterbuiltfool@gmail.com

Bluesky - @betterbuiltfool.bsky.social

Project Link: https://github.com/BetterBuiltFool/weaktree

(back to top)

Metadata

Release files for weaktree 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for weaktree 0.1.0
File Size Uploaded
weaktree-0.1.0.tar.gz 16.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for weaktree 0.1.0
File Interpreter ABI Platform
weaktree-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.3 kB

Release files / weaktree-0.1.0.tar.gz

Download URL weaktree-0.1.0.tar.gz
Size 16.8 kB
Tags Source
SHA-256 checksum
How to use checksums
29d3f09d4d2d7b1e86a7cdd912a402c48f5fabe82f8e7670885e3ca01a136ac4
BLAKE2b-256 checksum
How to use checksums
078f4cfbac1541dfee59ff227ad7098a42071418cc02c908622d14a27099ac9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.4

Release files / weaktree-0.1.0-py3-none-any.whl

Download URL weaktree-0.1.0-py3-none-any.whl
Size 10.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bdbf656df14a88bc38c32b166082e2b0b3d29974822e81029aeb5b27181ec695
BLAKE2b-256 checksum
How to use checksums
248b8161c8b60b5017dd49daeb91b105ca102eaa44b875716513b37331835c22
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.4

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page