Metadata-Version: 2.4
Name: pytreeds
Version: 0.1.0
Summary: Beginner-friendly rooted trees with consistent string navigation and traversal
Author: pytreeds contributors
License-Expression: MIT
Project-URL: Source, https://gitlab.com/ujen.teaching/pytreeds
Project-URL: Issues, https://gitlab.com/ujen.teaching/pytreeds/-/issues
Keywords: tree,data structures,education,dfs,bfs,pyodide
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Education
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Education
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# pytreeds

A dependency-free Python library for beginners to create, inspect, modify, traverse, and print **non-binary trees**. Students keep only one tree variable. Node identifiers are unique strings, and all node-related operations return strings rather than mixed types.

## Install

~~~bash
pip install pytreeds
~~~

Pyodide (from your platform's asynchronous package loader):

~~~python
import micropip
await micropip.install("pytreeds")
~~~

## First example

~~~python
from pytreeds import Tree

org = Tree("CEO")
org.add_nodes(["CTO", "CFO", "COO"], parent="CEO")
org.add_nodes(["Engineering", "DevOps"], parent="CTO")
org.add_node("Accounting", parent="CFO")

org.show()
print(org.height())                # 2 edges
print(org.size())                  # 6
print(org.children("CTO"))         # ['Engineering', 'DevOps']
print(org.parent("DevOps"))        # CTO
print(org.leaves())
print(list(org.dfs()))
print(list(org.bfs()))

for name in org:
    print(name, "->", org.parent(name))
~~~

Text diagram:

~~~text
CEO
├── CTO
│   ├── Engineering
│   └── DevOps
├── CFO
│   └── Accounting
└── COO
~~~

## API reference

### Build and modify

| API | Description |
|---|---|
| Tree(root=None) | Create an empty tree or one with a root |
| add_node(name, parent=None) | Create one node |
| add_nodes(names, parent) | Add several children in insertion order |
| remove_node(name) | Remove a leaf; raises an error for internal nodes |
| remove_subtree(name) | Remove a whole branch; removing root empties tree |
| move_node(name, parent) | Move a whole branch under a different parent |
| rename_node(old, new) | Rename a node without changing its location |
| copy() | Return an independent copy |

### Properties and relationships

| API | Description |
|---|---|
| root | Root name, or None if empty |
| size() | Total number of nodes |
| height(name=None) | Height in edges; empty tree = -1 |
| depth(name) | Number of edges from root; root depth = 0 |
| leaf_count(name=None) | Number of leaves in tree or subtree |
| is_leaf(name) | True if the node has no children |
| contains(name) | True if this name exists |
| parent(name) | Parent name, or None at root |
| children(name) | List of child names |
| siblings(name) | List of sibling names |
| ancestors(name) | Ancestors from root to parent |
| descendants(name) | Descendant names in DFS order |
| leaves(name=None) | Leaf names in DFS order |
| path(name) | Root-to-node list of names |

### Traversal and search

| API | Description |
|---|---|
| dfs(name=None) | Preorder DFS iterator of strings |
| bfs(name=None) | Level-order BFS iterator of strings |
| dfs_postorder(name=None) | Postorder DFS iterator of strings |
| nodes(name=None) | List of names in preorder |
| find(text) | Names containing a case-sensitive substring |
| for name in tree | Default preorder DFS |

### Print text

| API | Description |
|---|---|
| show(name=None, style="unicode") | Print tree or subtree |
| to_text(name=None, style="unicode") | Return a diagram string |
| print(tree) | Print the full diagram |

The style option can also be "ascii".

## Design and errors

- Each node has a unique nonempty string name. No UUIDs, extra node variables, overloaded subscripts, or student-defined classes.
- Parent, children, leaves, DFS, BFS, and ordinary iteration consistently use strings.
- The first node is the root. Further additions need a parent.
- Children and traversals preserve insertion order.
- Height and depth count edges, not levels.
- Duplicate names and invalid moves raise ValueError; missing names raise KeyError.
- Batch insertions validate before modifying the tree.
- Avoid modifying the tree while looping over a traversal.

## Subtree example

~~~python
from pytreeds import Tree
org = Tree("CEO")
org.add_nodes(["CTO", "CFO"], parent="CEO")
org.add_node("DevOps", parent="CTO")
org.move_node("DevOps", parent="CFO")
org.rename_node("DevOps", "Infrastructure")
org.show()
print(org.path("Infrastructure"))
org.remove_subtree("CFO")
~~~

Run tests locally using: python -m unittest discover -s tests -v

This repository intentionally keeps the existing publishing-only GitLab CI workflow; there is no testing CI job.
