From d71aff1eba6c61c9f434bade344be779eb20594c Mon Sep 17 00:00:00 2001 From: Martin Wendt Date: Tue, 22 Oct 2024 21:39:09 +0200 Subject: [PATCH] Update docs --- docs/jupyter/take_the_tour.ipynb | 272 ++++++++++++++++++++----------- docs/sphinx/index.rst | 4 +- docs/sphinx/installation.rst | 5 +- docs/sphinx/take_the_tour.rst | 185 ++++++++++++++------- tox.ini | 3 +- 5 files changed, 311 insertions(+), 158 deletions(-) diff --git a/docs/jupyter/take_the_tour.ipynb b/docs/jupyter/take_the_tour.ipynb index 73cb919..2fe19ea 100644 --- a/docs/jupyter/take_the_tour.ipynb +++ b/docs/jupyter/take_the_tour.ipynb @@ -7,9 +7,8 @@ "source": [ "# Take the Tour\n", "\n", - "> Note: This tour was gerneated from a jupyter notebook.
\n", - "> [It is available here](https://github.com/mar10/nutree/blob/main/docs/jupyter/ug_tour.ipynb) \n", - "> and can be used for interactive experiments." + "_(The tour is auto-generated from \n", + "[this jupyter notebook](https://github.com/mar10/nutree/blob/main/docs/jupyter/take_the_tour.ipynb).)_" ] }, { @@ -23,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 1, "metadata": {}, "outputs": [ { @@ -69,7 +68,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -104,7 +103,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -128,12 +127,12 @@ }, "source": [ "Now that we have a bunch of instances, let's organize these objects in a \n", - "hierarchical structure using [nutree](https://nutree.readthedocs.io/):" + "hierarchical structure using _nutree_:" ] }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -141,13 +140,13 @@ "output_type": "stream", "text": [ "Tree<'Organization'>\n", - "├── <__main__.Department object at 0x10e5a1670>\n", - "│ ├── <__main__.Department object at 0x10e59d670>\n", - "│ │ ╰── <__main__.Person object at 0x10e5a1850>\n", - "│ ╰── <__main__.Person object at 0x10e5a07a0>\n", - "├── <__main__.Department object at 0x10e5a0230>\n", - "│ ╰── <__main__.Person object at 0x10e5a0560>\n", - "╰── <__main__.Person object at 0x10e5a0200>\n" + "├── <__main__.Department object at 0x1056fd100>\n", + "│ ├── <__main__.Department object at 0x106249130>\n", + "│ │ ╰── <__main__.Person object at 0x1062cf560>\n", + "│ ╰── <__main__.Person object at 0x1062cf830>\n", + "├── <__main__.Department object at 0x1062cfce0>\n", + "│ ╰── <__main__.Person object at 0x105d2b770>\n", + "╰── <__main__.Person object at 0x1062cffe0>\n" ] } ], @@ -178,12 +177,13 @@ "by default.
\n", "We can overide this by passing an \n", "[f-string](https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals) \n", - "as `repr` argument:" + "as `repr` argument.
\n", + "For example `\"{node.data}\"` will use the data instances `__str__` method instead:" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -215,16 +215,16 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Node<'Person', data_id=283484192>" + "Node<'Person', data_id=274911230>" ] }, - "execution_count": 56, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -235,7 +235,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -253,7 +253,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -273,7 +273,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -295,7 +295,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -320,113 +320,116 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Mutation" + "The above traversal methods are also available for single nodes:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Test', 'Claire', 'Bob']\n" + ] + } + ], + "source": [ + "res = [node.data.name for node in dev_node]\n", + "print(res)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Data IDs and Clones" + "## Filter" ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Node<'Department', data_id=283484519>\n", - "├── Node<'Department', data_id=283483495>\n", - "│ ╰── Node<'Person', data_id=283484549>\n", - "╰── Node<'Person', data_id=283484282>\n", - "Node<'Department', data_id=283484195>\n", - "╰── Node<'Person', data_id=283484246>\n", - "Node<'Person', data_id=283484192>\n" + "Tree<\"Copy of Tree<'Organization'>\">\n", + "├── Node<'Department', data_id=274136336>\n", + "│ ╰── Node<'Department', data_id=274876691>\n", + "╰── Node<'Department', data_id=274911182>\n" ] } ], "source": [ - "tree.print(repr=\"{node}\", title=False)" + "tree_2 = tree.copy(predicate=lambda node: isinstance(node.data, Department))\n", + "tree_2.print(repr=\"{node}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Serialization" + "## Mutation" ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 13, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "[{'data': 'Department',\n", - " 'children': [{'data': 'Department',\n", - " 'children': [{'data': 'Person'}]},\n", - " {'data': 'Person'}]},\n", - " {'data': 'Department',\n", - " 'children': [{'data': 'Person'}]},\n", - " {'data': 'Person'}]" - ] - }, - "execution_count": 62, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "Tree<'Organization'>\n", + "├── <__main__.Department object at 0x1056fd100>\n", + "│ ├── <__main__.Department object at 0x106249130>\n", + "│ │ ╰── <__main__.Person object at 0x1062cf560>\n", + "│ ╰── <__main__.Person object at 0x1062cf830>\n", + "├── <__main__.Department object at 0x1062cfce0>\n", + "│ ╰── <__main__.Person object at 0x105d2b770>\n", + "╰── <__main__.Person object at 0x1062cffe0>\n" + ] } ], "source": [ - "tree.to_dict_list()" + "bob_node = tree[bob]\n", + "# bob_node.move_to(marketing_dep_node)\n", + "tree.print()" ] }, { - "cell_type": "code", - "execution_count": 63, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[(0, {}), (1, {}), (2, {}), (1, {}), (0, {}), (5, {}), (0, {})]" - ] - }, - "execution_count": 63, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "list(tree.to_list_iter())" + "## Data IDs and Clones" ] }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Tree<'4535745808'>\n", - "├── 'A'\n", - "│ ╰── 'C'\n", - "│ ╰── 'E'\n", - "├── 'B'\n", - "╰── 'D'\n" + "Node<'Department', data_id=274136336>\n", + "├── Node<'Department', data_id=274876691>\n", + "│ ╰── Node<'Person', data_id=274911062>\n", + "╰── Node<'Person', data_id=274911107>\n", + "Node<'Department', data_id=274911182>\n", + "╰── Node<'Person', data_id=274541431>\n", + "Node<'Person', data_id=274911230>\n" ] } ], "source": [ - "t = Tree._from_list([(0, \"A\"), (0, \"B\"), (1, \"C\"), (0, \"D\"), (3, \"E\")])\n", - "print(t.format())" + "tree.print(repr=\"{node}\", title=False)" ] }, { @@ -441,14 +444,14 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Tree<'4535754704'>\n", + "Tree<'4398779184'>\n", "├── 'A'\n", "│ ├── 'a1'\n", "│ ╰── 'a2'\n", @@ -478,18 +481,18 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Tree<'4535739136'>\n", - "├── Node<'A', data_id=8885459032692883038>\n", - "│ ╰── Node<\"DictWrapper<{'title': 'foo', 'id': 1}>\", data_id=4534073472>\n", - "╰── Node<'B', data_id=7589688033162088899>\n", - " ╰── Node<\"DictWrapper<{'title': 'foo', 'id': 1}>\", data_id=4534073472>\n" + "Tree<'4392651376'>\n", + "├── Node<'A', data_id=-9112120580527591933>\n", + "│ ╰── Node<\"DictWrapper<{'title': 'foo', 'id': 1}>\", data_id=4398795776>\n", + "╰── Node<'B', data_id=-7701919551774722043>\n", + " ╰── Node<\"DictWrapper<{'title': 'foo', 'id': 1}>\", data_id=4398795776>\n" ] } ], @@ -506,6 +509,94 @@ "# tree.find(d)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Serialization" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'data': 'A',\n", + " 'children': [{'data': \"DictWrapper<{'title': 'foo', 'id': 1}>\"}]},\n", + " {'data': 'B',\n", + " 'children': [{'data': \"DictWrapper<{'title': 'foo', 'id': 1}>\"}]}]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tree.to_dict_list()\n", + "# tree.to_dict_list(mapper=lambda node, data: node.data.name)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 'A'), (1, {}), (0, 'B'), (3, 2)]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(tree.to_list_iter())" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tree<'4398778896'>\n", + "├── 'A'\n", + "│ ╰── 'C'\n", + "│ ╰── 'E'\n", + "├── 'B'\n", + "╰── 'D'\n" + ] + } + ], + "source": [ + "t = Tree._from_list([(0, \"A\"), (0, \"B\"), (1, \"C\"), (0, \"D\"), (3, \"E\")])\n", + "print(t.format())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```mermaid\n", + "graph LR;\n", + " A--> B & C & D;\n", + " B--> A & E;\n", + " C--> A & E;\n", + " D--> A & E;\n", + " E--> B & C & D;\n", + "```" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -520,23 +611,22 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Tree<'4535747360'>\n", - "├── 'A'\n", - "│ ├── 'a1'\n", - "│ ╰── 'a2'\n", - "╰── 'B'\n" + "'A'\n", + "├── 'a1'\n", + "╰── 'a2'\n", + "'B'\n" ] } ], "source": [ - "Tree().add(\"A\").add(\"a1\").up().add(\"a2\").up(2).add(\"B\").tree.print()" + "Tree().add(\"A\").add(\"a1\").up().add(\"a2\").up(2).add(\"B\").tree.print(title=False)" ] }, { diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst index 1e12cb7..055a4b6 100644 --- a/docs/sphinx/index.rst +++ b/docs/sphinx/index.rst @@ -106,9 +106,9 @@ Nutree Facts * :ref:`Filtering ` -.. `Read more `_ ... -`Take the Tour `_ ... +Now read about :doc:`installation` and :doc:`take_the_tour` ... + .. |gha_badge| image:: https://github.com/mar10/nutree/actions/workflows/tests.yml/badge.svg diff --git a/docs/sphinx/installation.rst b/docs/sphinx/installation.rst index b37cd23..ae66895 100644 --- a/docs/sphinx/installation.rst +++ b/docs/sphinx/installation.rst @@ -27,4 +27,7 @@ Now the ``nutree`` package can be used in Python code:: See :doc:`development` for directions for contributors. -Now read the :doc:`user_guide`. \ No newline at end of file +Now :doc:`take_the_tour`... + +.. Now read the :doc:`user_guide`. +.. `Take the Tour `_ ... diff --git a/docs/sphinx/take_the_tour.rst b/docs/sphinx/take_the_tour.rst index 8befd36..4e50067 100644 --- a/docs/sphinx/take_the_tour.rst +++ b/docs/sphinx/take_the_tour.rst @@ -1,10 +1,8 @@ Take the Tour ============= - Note: This tour was gerneated from a jupyter notebook. `It is - available - here `__ - and can be used for interactive experiments. +*(The tour is auto-generated from*\ `this jupyter +notebook `__\ *.)* Nutree organizes arbitrary object instances in an unobtrusive way. That means, we can add existing objects without having to derive from a @@ -74,8 +72,7 @@ and create some instances dave = Person("Dave", 55) Now that we have a bunch of instances, let’s organize these objects in a -hierarchical structure using -`nutree `__: +hierarchical structure using *nutree*: .. code:: ipython3 @@ -98,13 +95,13 @@ hierarchical structure using .. parsed-literal:: Tree<'Organization'> - ├── <__main__.Department object at 0x105815340> - │ ├── <__main__.Department object at 0x1074caf30> - │ │ ╰── <__main__.Person object at 0x1074cb0e0> - │ ╰── <__main__.Person object at 0x1074cb050> - ├── <__main__.Department object at 0x1074c9910> - │ ╰── <__main__.Person object at 0x1074cacc0> - ╰── <__main__.Person object at 0x1074caed0> + ├── <__main__.Department object at 0x109247ad0> + │ ├── <__main__.Department object at 0x1091cede0> + │ │ ╰── <__main__.Person object at 0x109247770> + │ ╰── <__main__.Person object at 0x109247b00> + ├── <__main__.Department object at 0x109245820> + │ ╰── <__main__.Person object at 0x109247e00> + ╰── <__main__.Person object at 0x1092477a0> Tree nodes store a reference to the object in the ``node.data`` @@ -113,7 +110,8 @@ attribute. The nodes are formatted for display by the object’s ``__repr__`` implementation by default. We can overide this by passing an `f-string `__ -as ``repr`` argument: +as ``repr`` argument. For example ``"{node.data}"`` will use the data +instances ``__str__`` method instead: .. code:: ipython3 @@ -147,7 +145,7 @@ object: .. parsed-literal:: - Node<'Person', data_id=276089581> + Node<'Person', data_id=278022010> @@ -204,77 +202,81 @@ There are multiple methods to iterate the tree. Claire -Mutation --------- - -Data IDs and Clones -------------------- +The above traversal methods are also available for single nodes: .. code:: ipython3 - tree.print(repr="{node}", title=False) + res = [node.data.name for node in dev_node] + print(res) .. parsed-literal:: - Node<'Department', data_id=274208052> - ├── Node<'Department', data_id=276089587> - │ ╰── Node<'Person', data_id=276089614> - ╰── Node<'Person', data_id=276089605> - Node<'Department', data_id=276089233> - ╰── Node<'Person', data_id=276089548> - Node<'Person', data_id=276089581> + ['Test', 'Claire', 'Bob'] -Serialization -------------- +Filter +------ .. code:: ipython3 - tree.to_dict_list() - - + tree_2 = tree.copy(predicate=lambda node: isinstance(node.data, Department)) + tree_2.print(repr="{node}") .. parsed-literal:: - [{'data': 'Department', - 'children': [{'data': 'Department', - 'children': [{'data': 'Person'}]}, - {'data': 'Person'}]}, - {'data': 'Department', - 'children': [{'data': 'Person'}]}, - {'data': 'Person'}] + Tree<"Copy of Tree<'Organization'>"> + ├── Node<'Department', data_id=278022061> + │ ├── Node<'Department', data_id=278022061> + │ ╰── Node<'Department', data_id=277991134> + │ ╰── Node<'Department', data_id=277991134> + ╰── Node<'Department', data_id=278021506> + ╰── Node<'Department', data_id=278021506> +Mutation +-------- .. code:: ipython3 - list(tree.to_list_iter()) + bob_node = tree[bob] + # bob_node.move_to(marketing_dep_node) + tree.print() +:: -.. parsed-literal:: + --------------------------------------------------------------------------- + + AttributeError Traceback (most recent call last) - [(0, {}), (1, {}), (2, {}), (1, {}), (0, {}), (5, {}), (0, {})] + Cell In[120], line 1 + ----> 1 bob.move_to() + 2 tree.print() + AttributeError: 'Person' object has no attribute 'move_to' + + +Data IDs and Clones +------------------- .. code:: ipython3 - t = Tree._from_list([(0, "A"), (0, "B"), (1, "C"), (0, "D"), (3, "E")]) - print(t.format()) + tree.print(repr="{node}", title=False) .. parsed-literal:: - Tree<'4388275936'> - ├── 'A' - │ ╰── 'C' - │ ╰── 'E' - ├── 'B' - ╰── 'D' + Node<'Department', data_id=278005278> + ├── Node<'Department', data_id=278005584> + │ ╰── Node<'Person', data_id=278021425> + ╰── Node<'Person', data_id=278021428> + Node<'Department', data_id=277460240> + ╰── Node<'Person', data_id=278021167> + Node<'Person', data_id=277460774> Special Data Types @@ -297,7 +299,7 @@ We can add simple string objects the same way as any other object .. parsed-literal:: - Tree<'4417436128'> + Tree<'4448341168'> ├── 'A' │ ├── 'a1' │ ╰── 'a2' @@ -327,13 +329,71 @@ inside ``DictWrapper`` objects: .. parsed-literal:: - Tree<'4417684064'> - ├── Node<'A', data_id=7672938551128061313> - │ ╰── Node<"DictWrapper<{'title': 'foo', 'id': 1}>", data_id=4417631232> - ╰── Node<'B', data_id=-5231886843176280035> - ╰── Node<"DictWrapper<{'title': 'foo', 'id': 1}>", data_id=4417631232> + Tree<'4448343424'> + ├── Node<'A', data_id=-8659698137174932031> + │ ╰── Node<"DictWrapper<{'title': 'foo', 'id': 1}>", data_id=4448332800> + ╰── Node<'B', data_id=-1229858467403030929> + ╰── Node<"DictWrapper<{'title': 'foo', 'id': 1}>", data_id=4448332800> + + +Serialization +------------- + +.. code:: ipython3 + + tree.to_dict_list() + # tree.to_dict_list(mapper=lambda node, data: node.data.name) + + + + +.. parsed-literal:: + + [{'data': 'A', + 'children': [{'data': "DictWrapper<{'title': 'foo', 'id': 1}>"}]}, + {'data': 'B', + 'children': [{'data': "DictWrapper<{'title': 'foo', 'id': 1}>"}]}] + + + +.. code:: ipython3 + + list(tree.to_list_iter()) + + + + +.. parsed-literal:: + + [(0, 'A'), (1, {}), (0, 'B'), (3, 2)] + +.. code:: ipython3 + + t = Tree._from_list([(0, "A"), (0, "B"), (1, "C"), (0, "D"), (3, "E")]) + print(t.format()) + + +.. parsed-literal:: + + Tree<'4448342032'> + ├── 'A' + │ ╰── 'C' + │ ╰── 'E' + ├── 'B' + ╰── 'D' + + +.. code:: mermaid + + graph LR; + A--> B & C & D; + B--> A & E; + C--> A & E; + D--> A & E; + E--> B & C & D; + Advanced -------- @@ -345,15 +405,14 @@ for a more compact code and avoids some temporary variables: .. code:: ipython3 - Tree().add("A").add("a1").up().add("a2").up(2).add("B").tree.print() + Tree().add("A").add("a1").up().add("a2").up(2).add("B").tree.print(title=False) .. parsed-literal:: - Tree<'4417684208'> - ├── 'A' - │ ├── 'a1' - │ ╰── 'a2' - ╰── 'B' + 'A' + ├── 'a1' + ╰── 'a2' + 'B' diff --git a/tox.ini b/tox.ini index 08c47c4..307e15e 100644 --- a/tox.ini +++ b/tox.ini @@ -111,7 +111,8 @@ allowlist_externals = jupyter changedir = docs commands = - jupyter nbconvert --execute --to rst --output-dir sphinx jupyter/take_the_tour.ipynb + jupyter nbconvert --to rst --output-dir sphinx jupyter/take_the_tour.ipynb + ; jupyter nbconvert --execute --to rst --output-dir sphinx jupyter/take_the_tour.ipynb # http://www.sphinx-doc.org/en/master/man/sphinx-build.html sphinx-build -b html sphinx sphinx-build