diff --git a/.github/workflows/sphinx.yml b/.github/workflows/sphinx.yml
new file mode 100644
index 0000000..978038c
--- /dev/null
+++ b/.github/workflows/sphinx.yml
@@ -0,0 +1,25 @@
+name: "Sphinx: Render docs"
+
+on: push
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+ - name: Build HTML
+ uses: ammaraskar/sphinx-action@dev
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: html-docs
+ path: docs/build/html/
+ - name: Deploy
+ uses: peaceiris/actions-gh-pages@v3
+ if: github.ref == 'refs/heads/main'
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: docs/build/html
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d0c3cbf
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..e4b1afb
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,4 @@
+sphinx-rtd-theme>=3.0.2
+myst-parser>=3.0.1
+myst>=1.0.4
+sphinxcontrib-mermaid>=1.0.0
\ No newline at end of file
diff --git a/CHANGES.md b/docs/source/CHANGES.md
similarity index 98%
rename from CHANGES.md
rename to docs/source/CHANGES.md
index 28b52e1..50088a7 100644
--- a/CHANGES.md
+++ b/docs/source/CHANGES.md
@@ -1,3 +1,5 @@
+# Changelog
+
## Version 0.1.1
* Make it possible to pass options like `file_format` to `to_image`.
diff --git a/docs/source/api.rst b/docs/source/api.rst
new file mode 100644
index 0000000..af8fcc0
--- /dev/null
+++ b/docs/source/api.rst
@@ -0,0 +1,45 @@
+API
+==================
+
+Abstract base classes
+---------------------
+
+.. automodule:: abstracttree.tree
+ :members:
+ :show-inheritance:
+
+.. automodule:: abstracttree.binarytree
+ :members:
+ :show-inheritance:
+
+Adapters
+------------------
+
+.. automodule:: abstracttree.adapters
+ :members: astree, convert_tree
+ :show-inheritance:
+
+Export
+------------------
+
+.. automodule:: abstracttree.export
+ :members:
+ :show-inheritance:
+
+Predicates
+------------------
+.. automodule:: abstracttree.predicates
+ :members:
+ :show-inheritance:
+
+HeapTree
+------------------
+.. automodule:: abstracttree.heaptree
+ :members: HeapTree
+ :show-inheritance:
+
+Route
+------------------
+.. automodule:: abstracttree.route
+ :members: Route
+ :show-inheritance:
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 0000000..f02d496
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,46 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+
+project = 'AbstractTree'
+copyright = '2024, Laurent Verweijen'
+author = 'Laurent Verweijen'
+release = 'stable'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.autosummary',
+ 'sphinx.ext.githubpages',
+ 'sphinx.ext.viewcode',
+ 'myst_parser',
+ 'sphinxcontrib.mermaid'
+]
+
+templates_path = ['_templates']
+exclude_patterns = []
+
+
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'sphinx_rtd_theme'
+html_static_path = ['_static']
+
+
+myst_enable_extensions = ["colon_fence"]
+
+# Code is in src. Make sure sphinx can find it
+import sys
+from pathlib import Path
+src_folder = Path(__file__).parent.parent.parent / "src"
+print(src_folder)
+sys.path.insert(0, str(src_folder.resolve(strict=True)))
diff --git a/docs/source/images/latex_img.png b/docs/source/images/latex_img.png
new file mode 100644
index 0000000..134625b
Binary files /dev/null and b/docs/source/images/latex_img.png differ
diff --git a/docs/source/images/str_mermaid.png b/docs/source/images/str_mermaid.png
new file mode 100644
index 0000000..ccccaf5
Binary files /dev/null and b/docs/source/images/str_mermaid.png differ
diff --git a/docs/source/images/tree_calc_plot.png b/docs/source/images/tree_calc_plot.png
new file mode 100644
index 0000000..eaaab7e
Binary files /dev/null and b/docs/source/images/tree_calc_plot.png differ
diff --git a/docs/source/images/tree_dot.png b/docs/source/images/tree_dot.png
new file mode 100644
index 0000000..515b0ac
Binary files /dev/null and b/docs/source/images/tree_dot.png differ
diff --git a/docs/source/img_1.png b/docs/source/img_1.png
new file mode 100644
index 0000000..4c97e90
Binary files /dev/null and b/docs/source/img_1.png differ
diff --git a/docs/source/img_2.png b/docs/source/img_2.png
new file mode 100644
index 0000000..4c97e90
Binary files /dev/null and b/docs/source/img_2.png differ
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 0000000..779f5d7
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,29 @@
+.. AbstractTree documentation master file, created by
+ sphinx-quickstart on Mon Feb 19 18:11:55 2024.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to AbstractTree's documentation!
+========================================
+
+Trees are very common data structure that represents a hierarchy of common nodes.
+This package defines abstract base classes for these data structure in order to make code reusable.
+It also provides an ``astree`` adapter in case it's not possible to inherit from any of these classes.
+Finally, it provides many exports that even work on objects that don't inherit from any of the abstract base classes.
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ installation
+ usage_bundled
+ api
+ CHANGES
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
new file mode 100644
index 0000000..47c29a3
--- /dev/null
+++ b/docs/source/installation.rst
@@ -0,0 +1,17 @@
+Installation
+========================================
+
+Use `pip `_ to install littletree::
+
+ $ pip install --upgrade littletree
+
+In addition you may want to install the following for some export functions::
+
+ $ pip install --upgrade matplotlib
+ $ pip install --upgrade pillow
+
+For some export functions it might help to install:
+
+- `Graphviz `_
+- `Mermaid `_
+- `LaTeX `_
diff --git a/docs/source/usage_bundled.rst b/docs/source/usage_bundled.rst
new file mode 100644
index 0000000..f5dbc75
--- /dev/null
+++ b/docs/source/usage_bundled.rst
@@ -0,0 +1,239 @@
+Usage
+==================
+
+Abstract tree classes
+---------------------
+
+.. mermaid::
+
+ graph TD;
+ Tree[Tree];
+ MutableTree[MutableTree];
+ DownTree[DownTree];
+ Tree[Tree];
+ MutableTree[MutableTree];
+ MutableDownTree[MutableDownTree];
+ MutableTree[MutableTree];
+ BinaryDownTree[BinaryDownTree];
+ BinaryTree[BinaryTree];
+ Tree-->MutableTree;
+ DownTree-->Tree;
+ DownTree-->MutableDownTree;
+ MutableDownTree-->MutableTree;
+ DownTree-->BinaryDownTree;
+ BinaryDownTree-->BinaryTree;
+ Tree-->BinaryTree;
+
+
+A Downtrees is an object that has links to its direct children.
+A Tree is has links to both its children and its parent.
+A binary tree has exactly two children (left and right).
+A mutable tree can change its structure once created.
+
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+| ABC | Inherits from | Abstract Methods | Mixin Methods |
++=====================+===============================+=====================================+====================================================================================+
+| ``AbstractTree`` | | | ``nid``, ``eqv()`` |
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+| ``DownTree`` | ``AbstractTree`` | ``children`` | ``nodes``, ``descendants``, ``leaves``, ``levels``, ``is_leaf``, ``transform()`` |
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+| ``Tree`` | ``DownTree`` | | ``siblings`` |
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+| ``MutableDownTree`` | ``DownTree`` | ``add_child()``, ``remove_child()`` | ``add_children()`` |
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+| ``MutableTree`` | ``Tree``, ``MutableDownTree`` | | ``detach()`` |
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+| ``BinaryDownTree`` | ``DownTree`` | ``left_child``, ``right_child`` | ``children`` |
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+| ``BinaryTree`` | ``BinaryDownTree``, ``Tree`` | | |
++---------------------+-------------------------------+-------------------------------------+------------------------------------------------------------------------------------+
+
+In your own code, you can inherit from these trees.
+For example, if your tree only has links to children::
+
+ import abstracttree
+ from abstracttree import print_tree
+
+ class MyTree(abstracttree.DownTree):
+ def __init__(self, value, children=()):
+ self.value = value
+ self._children = children
+
+ def __str__(self):
+ return "MyTree " + str(self.value)
+
+ @property
+ def children(self):
+ return self._children
+
+
+You can now use this class in the following way to generate output::
+
+ tree = MyTree(1, children=[MyTree(2), MyTree(3)])
+ print_tree(tree)
+
+ # MyTree 1
+ # ├─ MyTree 2
+ # └─ MyTree 3
+
+Adapter
+------------------
+
+In practice, not all existing tree data structures implement one of the abstract classes specified in `Abstract classes `_.
+As a bridge, you can use ``Tree.convert`` to convert these trees to a ``Tree`` instance.
+However, whenever possible, it's recommended to inherit from ``Tree`` directly for minimal overhead.
+
+``Tree.convert`` already does the right thing on many objects of the standard library::
+
+ # Inheritance hierarchy
+ Tree.convert(int)
+
+ # Abstract syntax tree
+ Tree.convert(ast.parse("1 + 1 == 2"))
+
+ # Filesystem
+ Tree.convert(pathlib.Path("abstracttree"))
+
+ # Zipfile
+ Tree.convert(zipfile.ZipFile("eclipse.jar"))
+
+ # Nested list
+ Tree.convert([[1, 2, 3], [4, 5, 6]])
+
+It can also construct a tree by ducktyping on ``parent`` and ``children`` attributes::
+
+ # Works on objects by anytree, bigtree and littletree
+ Tree.convert(anytree.Node('node'))
+
+Alternatively, you can use ``astree`` and explicitly specify how to find ``children`` and ``parent``::
+
+ # Tree from json-data
+ data = {"name": "a",
+ "children": [
+ {"name": "b", "children": []},
+ {"name": "c", "children": []}
+ ]}
+ astree(data, children=operator.itemgetter["children"])
+
+ # pyqt.QtWidget
+ astree(widget, children=lambda w: w.children(), parent = lambda w: w.parent())
+
+ # Tree from treelib
+ astree(tree.root, children=lambda nid: tree.children(nid), parent=lambda nid: tree.parent(nid))
+
+ # itertree
+ astree(tree, children=iter, parent=lambda t: t.parent)
+
+ # Infinite binary tree
+ inf_binary = astree(0, children=lambda n: (2*n + 1, 2*n + 2))
+
+Traversal
+----------------------------------------
+
+There are 3 common ways to traverse a tree:
+
+Pre-order
+ The parent is iterated over before its children.
+
+Post-order
+ The children are iterated over before their parent.
+
+Level-order
+ Nodes closer to root are iterated over before nodes further from the root.
+
+All these are possible by writing one of::
+
+ for node, item in tree.nodes.preorder():
+ ...
+
+ for node, item in tree.nodes.postorder():
+ ...
+
+ for node, item in tree.nodes.levelorder():
+ ...
+
+These methods return an item in addition to a node.
+This item is a tuple of the following fields:
+
+depth
+ This indicates how deep the node is relative to the root of the (sub)tree iterated over.
+ The root of the (sub)tree always has depth 0.
+ To find the absolute depth of a node, use ``node.ancestors.count()``.
+
+index
+ The index of this node among its siblings in relation to its direct parent.
+ The first child of a parent gets index 0, the second gets index 1.
+ The root of the (sub)tree always gets an index of ``0`` even if it has prior siblings.
+
+To iterate over the descendants (the nodes without the root), similar methods are defined::
+
+ for descendant, item in tree.descendants.preorder():
+ ...
+
+If the order of iteration doesn't matter an alternative way to iterate is as follows::
+
+ for node in tree.nodes:
+ ...
+
+ for descendant in tree.descendants:
+ ...
+
+
+Export
+----------------------------------------
+
+Pretty printing::
+
+ print_tree(Path())
+ # .
+ # ├─ abstracttree
+ # │ ├─ abstracttree\conversions.py
+ # │ ├─ abstracttree\export.py
+ # │ ├─ abstracttree\predicates.py
+ # │ ├─ abstracttree\treeclasses.py
+ # │ └─ abstracttree\__init__.py
+ # ├─ LICENSE
+ # ├─ Makefile
+ # ├─ manual.txt
+ # ├─ pyproject.toml
+ # ├─ README.md
+ # └─ tests
+ # ├─ tests\test_downtree.py
+ # ├─ tests\test_export.py
+ # ├─ tests\test_mutabletree.py
+ # ├─ tests\test_tree.py
+ # ├─ tests\test_uptree.py
+ # └─ tests\tree_instances.py
+
+
+
+Plotting with matplotlib::
+
+ import matplotlib.pyplot as plt
+
+ plot_tree(ast.parse("y = x*x + 1"))
+ plt.show()
+
+.. image:: images/tree_calc_plot.png
+
+Export to graphviz::
+
+ tree = astree(seq, children=lambda x: [x[:-2], x[1:]] if x else [])
+ to_graphviz(tree)
+
+
+.. image:: images/tree_dot.png
+
+Export to mermaid::
+
+ to_mermaid(str)
+
+.. image:: images/str_mermaid.png
+
+Export to latex::
+
+ data = [["james", "steve"],
+ ["patrick", "mike", "bod", "piet"]]
+ to_latex(data)
+
+.. image:: images/latex_img.png
diff --git a/pyproject.toml b/pyproject.toml
index 66f8440..61313d6 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -41,8 +41,9 @@ export = [
[project.urls]
Homepage = "https://github.com/lverweijen/abstracttree"
Repository = "https://github.com/lverweijen/abstracttree"
+Documentation = "https://lverweijen.github.io/AbstractTree/"
Issues = "https://github.com/lverweijen/abstracttree/issues"
-Changelog = "https://github.com/lverweijen/abstracttree/blob/main/changes.md"
+Changelog = "https://lverweijen.github.io/AbstractTree/CHANGES.html"
[tool.ruff]
line-length = 100
@@ -58,6 +59,11 @@ preview = true
[dependency-groups]
dev = [
"ruff>=0.8.2",
+ "sphinx-rtd-theme>=3.0.2",
+ "sphinx>=7.4.7",
+ "myst-parser>=3.0.1",
+ "myst>=1.0.4",
+ "sphinxcontrib-mermaid>=1.0.0",
]
[build-system]