diff --git a/docs/tutorial.rst b/docs/tutorial.rst index 619392a175..5d977c48a5 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -330,10 +330,10 @@ representation of the hierarchy, e.g.:: >>> root.tree() / - └── foo - └── bar - ├── baz (10000, 10000) int32 - └── quux (10000, 10000) int32 + └── foo + └── bar + ├── baz (10000, 10000) int32 + └── quux (10000, 10000) int32 The :func:`zarr.convenience.open` function provides a convenient way to create or re-open a group stored in a directory on the file-system, with sub-groups stored in @@ -424,6 +424,12 @@ Groups also have the :func:`zarr.hierarchy.Group.tree` method, e.g.:: ├── bar (1000000,) int64 └── baz (1000, 1000) float32 + +.. note:: + + :func:`zarr.Group.tree` requires the optional `rich `_ + dependency. It can be installed with the ``[tree]`` extra. + If you're using Zarr within a Jupyter notebook (requires `ipytree `_), calling ``tree()`` will generate an interactive tree representation, see the `repr_tree.ipynb notebook diff --git a/notebooks/repr_tree.ipynb b/notebooks/repr_tree.ipynb deleted file mode 100644 index dde8bbb9a0..0000000000 --- a/notebooks/repr_tree.ipynb +++ /dev/null @@ -1,1388 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'2.3.3.dev87'" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import zarr\n", - "zarr.__version__" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "g1 = zarr.group()\n", - "g2 = g1.create_group('foo')\n", - "g3 = g1.create_group('bar')\n", - "g3.create_group('baz')\n", - "g3.create_dataset('xxx', shape=100)\n", - "g3.create_dataset('yyy', shape=(100, 100), dtype='i4')\n", - "g5 = g3.create_group('quux')\n", - "g5.create_dataset('aaa', shape=100)\n", - "g5.create_dataset('bbb', shape=(100, 100), dtype='i4')\n", - "g7 = g3.create_group('zoo')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Generate text (unicode) tree:" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/\n", - " ├── bar\n", - " │ ├── baz\n", - " │ ├── quux\n", - " │ │ ├── aaa (100,) float64\n", - " │ │ └── bbb (100, 100) int32\n", - " │ ├── xxx (100,) float64\n", - " │ ├── yyy (100, 100) int32\n", - " │ └── zoo\n", - " └── foo\n" - ] - } - ], - "source": [ - "print(g1.tree())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The ``level`` parameter controls how deep the tree is." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/\n", - " ├── bar\n", - " └── foo\n" - ] - } - ], - "source": [ - "print(g1.tree(level=1))" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/\n", - " ├── bar\n", - " │ ├── baz\n", - " │ ├── quux\n", - " │ ├── xxx (100,) float64\n", - " │ ├── yyy (100, 100) int32\n", - " │ └── zoo\n", - " └── foo\n" - ] - } - ], - "source": [ - "print(g1.tree(level=2))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Alternative plain ASCII tree:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "/\n", - " +-- bar\n", - " | +-- baz\n", - " | +-- quux\n", - " | | +-- aaa (100,) float64\n", - " | | +-- bbb (100, 100) int32\n", - " | +-- xxx (100,) float64\n", - " | +-- yyy (100, 100) int32\n", - " | +-- zoo\n", - " +-- foo\n" - ] - } - ], - "source": [ - "print(bytes(g1.tree()).decode())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "HTML trees:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "dc848c2195a54f1386f67cc631871449", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tree(nodes=(Node(disabled=True, name='/', nodes=(Node(disabled=True, name='bar', nodes=(Node(disabled=True, na…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1.tree()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use ``expand=True`` to have all groups automatically expanded." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "9459567637cf48cca67ba0145cc3a626", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tree(nodes=(Node(disabled=True, name='/', nodes=(Node(disabled=True, name='bar', nodes=(Node(disabled=True, na…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1.tree(expand=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "1c635a91c6a444fab7bcb33a33c73097", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tree(nodes=(Node(disabled=True, name='/', nodes=(Node(disabled=True, name='bar', nodes=(Node(disabled=True, na…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1.tree(expand=True, level=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "0a32522e7c3b4f95b2258027502167f7", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tree(nodes=(Node(disabled=True, name='/', nodes=(Node(disabled=True, name='bar'), Node(disabled=True, name='fo…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1.tree(expand=True, level=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The ``expand`` parameter can also be an integer, giving the depth to expand to." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2c9c017eebf94d2aa785439e94cfa533", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tree(nodes=(Node(disabled=True, name='/', nodes=(Node(disabled=True, name='bar', nodes=(Node(disabled=True, na…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1.tree(expand=1)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "edcb8650759d44a995538257d637a17e", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tree(nodes=(Node(disabled=True, name='/', nodes=(Node(disabled=True, name='bar', nodes=(Node(disabled=True, na…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1.tree(expand=2)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "ee03f431a4fa4395bb913c7107b8f82d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Tree(nodes=(Node(disabled=True, name='/', nodes=(Node(disabled=True, name='bar', nodes=(Node(disabled=True, na…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "g1.tree(expand=3)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.2" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "state": { - "00266d06794e4879b5f200a6da77a8b5": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": {} - }, - "005396454d8a4ccdbd078066066abb25": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "4a5cee4c-65aa-4837-977c-ca04e7be6608", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "foo" - } - }, - "094a129d9ef04f56bbd9e2ef440cdf9f": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "ef9fee92-e6cb-4362-b095-715fa4bff3bc", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "aaa (100,) float64", - "opened": false - } - }, - "0a32522e7c3b4f95b2258027502167f7": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "TreeModel", - "state": { - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "layout": "IPY_MODEL_24a5ef087e4546aeb18b5bd4ea4106d9", - "nodes": [ - "IPY_MODEL_a91c0ac9a6644f18a6ec657cc3c2eb95" - ] - } - }, - "120c8e8cb7664fa0aa0a6ca27a1c5f3f": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "e6f11c15-cf73-4ac9-9737-0f539be27858", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "bar", - "nodes": [ - "IPY_MODEL_741dc4fd91c346f9882b87e9c927362d", - "IPY_MODEL_a6bd734a89044570af371e3f8d4c9fc7", - "IPY_MODEL_a443244f855e40e5bf02be971f470aad", - "IPY_MODEL_9d69e6ca94ae40de8103a7aa51a79a15", - "IPY_MODEL_71ae557f54c944b08874833dbc540339" - ] - } - }, - "14b81f73c9e6404bab187635805952ad": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": {} - }, - "167b0ac3d49b4602900182c8580546eb": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "b7891bd2-6ef7-4d27-ad27-0f017548d302", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "xxx (100,) float64", - "opened": false - } - }, - "1b5ecaaed52a47d6b9846e8626dded36": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "a2ca1d10-8196-491a-898b-cb7ba4578157", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "zoo", - "opened": false - } - }, - "1c635a91c6a444fab7bcb33a33c73097": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "TreeModel", - "state": { - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "layout": "IPY_MODEL_231c730e6b154dd699e528a04e3bceac", - "nodes": [ - "IPY_MODEL_e4c46e3a01564d2986c3a0687e859251" - ] - } - }, - "2004cbc5c93142678c0443ab3260fc76": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "90ee3b0d-847e-4577-a7c3-9b21c666c292", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "/", - "nodes": [ - "IPY_MODEL_2fef457b9982499f87599dfc8404a768", - "IPY_MODEL_98eb0a6675c440ce9672156423051909" - ] - } - }, - "231c730e6b154dd699e528a04e3bceac": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": {} - }, - "2376f63399664a9cb6fcb80112ebd316": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "0120995a-5972-45eb-94a2-4b4480395aa0", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "aaa (100,) float64", - "opened": false - } - }, - "23f8520500534a16903b1d760ee3c1b0": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "d6a76614-eafd-4059-83c0-7dc725969785", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "zoo" - } - }, - "24364dd05e21498a9ee6148d7069470c": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "775d02ae-3e15-4001-9680-89cdda964afe", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "foo" - } - }, - "24a5ef087e4546aeb18b5bd4ea4106d9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": {} - }, - "2bf13d8729f543c3b62c1dff0bc75f24": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "c994f1f5-1c0d-4a10-b056-54eb6e3654b0", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "quux", - "nodes": [ - "IPY_MODEL_859d15d7e1fb460fa9e1607835028d2f", - "IPY_MODEL_530d9c8872a34f68a5f203c490abea8c" - ] - } - }, - "2c9c017eebf94d2aa785439e94cfa533": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "TreeModel", - "state": { - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "layout": "IPY_MODEL_00266d06794e4879b5f200a6da77a8b5", - "nodes": [ - "IPY_MODEL_2004cbc5c93142678c0443ab3260fc76" - ] - } - }, - "2dd1565f8b5747f59bb0787235310fca": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "e5ac34b6-872f-41cf-84b2-0a2db45bf585", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "/", - "nodes": [ - "IPY_MODEL_c5c799513c214d878e6d9798e1418028", - "IPY_MODEL_7e51196f842148a59de5285587f7cdb3" - ] - } - }, - "2fef457b9982499f87599dfc8404a768": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "a1aad2fe-b5c5-4151-82a7-b681b524f55e", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "bar", - "nodes": [ - "IPY_MODEL_fda99f8dbbbe4636a0bab906c5267b81", - "IPY_MODEL_953f8443d806452aa6009c8d8cd6a00b", - "IPY_MODEL_167b0ac3d49b4602900182c8580546eb", - "IPY_MODEL_3aa51d78e7c14aa58818339ff8583364", - "IPY_MODEL_1b5ecaaed52a47d6b9846e8626dded36" - ], - "opened": false - } - }, - "3aa51d78e7c14aa58818339ff8583364": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "68f3fef0-f0a9-4549-80db-9c8f49e3c7c2", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "yyy (100, 100) int32", - "opened": false - } - }, - "3c31fa266b124cc7baa9572e10fff7d5": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "467cb368-3758-41ac-a037-d901001fedb8", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "foo", - "opened": false - } - }, - "3f686ef60c3742498467169845340486": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "46413eeb-ff88-4c8e-81f3-75175174511c", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "yyy (100, 100) int32", - "opened": false - } - }, - "41537120a09b49d7aef63cf97d3bcbdc": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "7bff3ed2-1676-4dc7-8865-8294df17bdf3", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "quux", - "nodes": [ - "IPY_MODEL_094a129d9ef04f56bbd9e2ef440cdf9f", - "IPY_MODEL_80da3a55f04d4ebfb5199c50553268a8" - ], - "opened": false - } - }, - "530d9c8872a34f68a5f203c490abea8c": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "ce5627ee-6b00-431f-8e0b-3e5f45e3228f", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "bbb (100, 100) int32" - } - }, - "57ce4623088643a18e8eec3ba6003fd7": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "9da1285e-bced-44b8-af47-95272401bcef", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "bar", - "nodes": [ - "IPY_MODEL_8bc5d72953c34131961f747b2cfac812", - "IPY_MODEL_a4b87dfad842423e88c5eeeba8817539", - "IPY_MODEL_81e1f7fa28b8482f9f6ec5694f99233f", - "IPY_MODEL_f394ec7d09a2410a8e19905a6b59751f", - "IPY_MODEL_9c89de1bc6f542bf8c16810dcfd5d703" - ] - } - }, - "585c788c4e60434295e208470bf2b9db": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "961d4b35-c7ad-4503-b212-18d2b5394cac", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "aaa (100,) float64", - "opened": false - } - }, - "5b06653afbb04bcd979f5390326b8115": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "9c26687d-6dab-4daa-95cc-bbbfab950e08", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "xxx (100,) float64", - "opened": false - } - }, - "60e395b510714a92b02b331dedac2c22": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "ec0906d5-3c8e-4c02-89fa-19f052ecb448", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "foo" - } - }, - "6390bb65bfd945aba4993fb82e052b13": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "23323321-dfbd-4fb1-b970-db209ff7dbdf", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "yyy (100, 100) int32" - } - }, - "6e160ea17f5c43248a8ef50ca08b3af4": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "185ee6a2-9064-4e40-a493-cd1fa2039510", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "bbb (100, 100) int32", - "opened": false - } - }, - "71ae557f54c944b08874833dbc540339": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "7442b769-5dd9-466e-bf71-d850dc4d864b", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "zoo" - } - }, - "741dc4fd91c346f9882b87e9c927362d": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "25667d21-0738-4b33-82a6-43cead9dcee3", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "baz" - } - }, - "7aed7c2c71fe4261bfee71d4e13b611a": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "b690530c-9414-47a6-ba1a-47b8582bb18e", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "baz" - } - }, - "7cc19caed25f418d8724b6d61cd40196": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "1ea2d350-3f19-4417-be0f-a8836eeb6015", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "bar", - "nodes": [ - "IPY_MODEL_d12eeab848e548869a02e76ee7deedcb", - "IPY_MODEL_41537120a09b49d7aef63cf97d3bcbdc", - "IPY_MODEL_5b06653afbb04bcd979f5390326b8115", - "IPY_MODEL_3f686ef60c3742498467169845340486", - "IPY_MODEL_d686caad9af343a6ad01cb98d7d46be9" - ], - "opened": false - } - }, - "7e51196f842148a59de5285587f7cdb3": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "66a2f671-756a-403b-b96a-78a4abca32c6", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "foo" - } - }, - "7f23d5608a9b4ae397e32c70bd7897bc": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "556be9fc-a7ff-45bb-b756-6494f58238a4", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "/", - "nodes": [ - "IPY_MODEL_7cc19caed25f418d8724b6d61cd40196", - "IPY_MODEL_3c31fa266b124cc7baa9572e10fff7d5" - ] - } - }, - "80da3a55f04d4ebfb5199c50553268a8": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "bdc7445a-2bdc-4191-bc18-d2e55227c338", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "bbb (100, 100) int32", - "opened": false - } - }, - "81e1f7fa28b8482f9f6ec5694f99233f": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "c4646b6f-fb0a-416e-817a-f52edb067149", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "xxx (100,) float64", - "opened": false - } - }, - "82f52a3353be4a368db1f71e15892a5c": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "92392f3d-ac69-48a2-be99-99d663baf567", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "bbb (100, 100) int32", - "opened": false - } - }, - "84f56fbf5c384e16b409ee918f6d7855": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "3ea55be4-a339-4798-af2e-e37ee07d0c3d", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "yyy (100, 100) int32" - } - }, - "8524ddcc608d42fcaa3ae6384ef98a25": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": {} - }, - "859d15d7e1fb460fa9e1607835028d2f": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "74776201-7656-4b01-a9a4-723b0ec56602", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "aaa (100,) float64" - } - }, - "86f72ec1e4764ff1863e7daef9476476": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "2313648b-d4b1-47d1-a2ee-b07fbda43308", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "baz" - } - }, - "8818d09311f143f28672e8f75be2df01": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "bb7b5a89-cb39-4860-8d61-9419d59514e5", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "xxx (100,) float64" - } - }, - "896a87da7c2e4cd894c0e8bcc03e80fe": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "c73b4900-c1e4-4d60-814b-524de689373a", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "quux" - } - }, - "8bc5d72953c34131961f747b2cfac812": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "db5dcd99-6e88-40d0-b068-af436c365c8d", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "baz", - "opened": false - } - }, - "9459567637cf48cca67ba0145cc3a626": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "TreeModel", - "state": { - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "layout": "IPY_MODEL_14b81f73c9e6404bab187635805952ad", - "nodes": [ - "IPY_MODEL_2dd1565f8b5747f59bb0787235310fca" - ] - } - }, - "953f8443d806452aa6009c8d8cd6a00b": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "18140127-9a50-432d-98bf-297b8677e37e", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "quux", - "nodes": [ - "IPY_MODEL_2376f63399664a9cb6fcb80112ebd316", - "IPY_MODEL_deba2782b4c1464f81652d5f03920084" - ], - "opened": false - } - }, - "98eb0a6675c440ce9672156423051909": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "c89b7b7b-c3a3-4ac5-b2f6-9b938b2f7f98", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "foo", - "opened": false - } - }, - "9c89de1bc6f542bf8c16810dcfd5d703": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "b70bf77b-2424-478d-b009-67dd51fdfa5f", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "zoo", - "opened": false - } - }, - "9cb02d82f4964b9bb80202eb1e1965f0": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": {} - }, - "9d69e6ca94ae40de8103a7aa51a79a15": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "530496d0-18ca-4d43-a6e4-b03d3816a567", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "yyy (100, 100) int32" - } - }, - "a443244f855e40e5bf02be971f470aad": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "cf32dfd6-d234-498c-821a-63424fe24b03", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "xxx (100,) float64" - } - }, - "a4b87dfad842423e88c5eeeba8817539": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "448a6ae8-e4b4-468d-8db1-a92092a5b255", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "quux", - "nodes": [ - "IPY_MODEL_585c788c4e60434295e208470bf2b9db", - "IPY_MODEL_82f52a3353be4a368db1f71e15892a5c" - ], - "opened": false - } - }, - "a6bd734a89044570af371e3f8d4c9fc7": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "36e727be-a1b7-4a0a-9941-aae4f617b6a7", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "quux", - "nodes": [ - "IPY_MODEL_eba07ec14a46420ab8f00565e998424f", - "IPY_MODEL_6e160ea17f5c43248a8ef50ca08b3af4" - ] - } - }, - "a91c0ac9a6644f18a6ec657cc3c2eb95": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "3b5d5c34-8fc1-4e50-be15-16cbae22778a", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "/", - "nodes": [ - "IPY_MODEL_ea998265f10549a1be0fea9579ab5ebd", - "IPY_MODEL_24364dd05e21498a9ee6148d7069470c" - ] - } - }, - "b612bbe40f3045cb9bce7662872d2194": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "5e418578-9312-4ce6-933a-c6d2606d0603", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "bar", - "nodes": [ - "IPY_MODEL_86f72ec1e4764ff1863e7daef9476476", - "IPY_MODEL_896a87da7c2e4cd894c0e8bcc03e80fe", - "IPY_MODEL_e7dd9b21300d4d84bf173916c6b05ccc", - "IPY_MODEL_6390bb65bfd945aba4993fb82e052b13", - "IPY_MODEL_df525d63b58649658b18437078be0b6c" - ] - } - }, - "c5c799513c214d878e6d9798e1418028": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "d8173610-bb78-45eb-9f56-a1e1f404d1e9", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "bar", - "nodes": [ - "IPY_MODEL_7aed7c2c71fe4261bfee71d4e13b611a", - "IPY_MODEL_2bf13d8729f543c3b62c1dff0bc75f24", - "IPY_MODEL_8818d09311f143f28672e8f75be2df01", - "IPY_MODEL_84f56fbf5c384e16b409ee918f6d7855", - "IPY_MODEL_23f8520500534a16903b1d760ee3c1b0" - ] - } - }, - "d00c74f3a7714efb9d89829e16ff4cd8": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": {} - }, - "d12eeab848e548869a02e76ee7deedcb": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "f23a5051-caf9-4527-951d-61bb352a1c4f", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "baz", - "opened": false - } - }, - "d686caad9af343a6ad01cb98d7d46be9": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "ce8441db-4518-4d0d-b9e3-085f662b4c77", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "zoo", - "opened": false - } - }, - "db4c5c98a1bb49afa022d0009d5f4247": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "f6d98bfd-d8bb-4cd1-b714-e4ed7574f0a0", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "/", - "nodes": [ - "IPY_MODEL_57ce4623088643a18e8eec3ba6003fd7", - "IPY_MODEL_de6fc66267e449dd988127f5fecef78d" - ] - } - }, - "dc848c2195a54f1386f67cc631871449": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "TreeModel", - "state": { - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "layout": "IPY_MODEL_8524ddcc608d42fcaa3ae6384ef98a25", - "nodes": [ - "IPY_MODEL_7f23d5608a9b4ae397e32c70bd7897bc" - ] - } - }, - "de6fc66267e449dd988127f5fecef78d": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "1d94bc9b-a7d6-430c-a159-88c7848d2554", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "foo" - } - }, - "deba2782b4c1464f81652d5f03920084": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "c830b45a-298b-42df-a3db-7e5170e612a2", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "bbb (100, 100) int32", - "opened": false - } - }, - "df525d63b58649658b18437078be0b6c": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "2d8dbe4d-b683-452b-987b-84775b0b95db", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "zoo" - } - }, - "e4c46e3a01564d2986c3a0687e859251": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "8f477a24-5aa9-4d1f-8104-947cf2cca963", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "/", - "nodes": [ - "IPY_MODEL_b612bbe40f3045cb9bce7662872d2194", - "IPY_MODEL_005396454d8a4ccdbd078066066abb25" - ] - } - }, - "e7dd9b21300d4d84bf173916c6b05ccc": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "aec6a6ff-3944-4ff5-941d-51bcf07f7bcf", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "xxx (100,) float64" - } - }, - "ea998265f10549a1be0fea9579ab5ebd": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "0bc70d5d-6730-4265-a0f0-9c14c0ea868e", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "bar" - } - }, - "eba07ec14a46420ab8f00565e998424f": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "167e56c3-594e-4629-82b9-c71a12519d04", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "aaa (100,) float64", - "opened": false - } - }, - "edcb8650759d44a995538257d637a17e": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "TreeModel", - "state": { - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "layout": "IPY_MODEL_9cb02d82f4964b9bb80202eb1e1965f0", - "nodes": [ - "IPY_MODEL_db4c5c98a1bb49afa022d0009d5f4247" - ] - } - }, - "ee03f431a4fa4395bb913c7107b8f82d": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "TreeModel", - "state": { - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "layout": "IPY_MODEL_d00c74f3a7714efb9d89829e16ff4cd8", - "nodes": [ - "IPY_MODEL_fc9683e8600b461dbd4edaa9726179ad" - ] - } - }, - "f394ec7d09a2410a8e19905a6b59751f": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "900c0ef5-07f0-45be-9d4f-05a9e08d7709", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "icon": "table", - "name": "yyy (100, 100) int32", - "opened": false - } - }, - "fc9683e8600b461dbd4edaa9726179ad": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "26731003-f7ac-45fa-955d-181a02ba2b3d", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "/", - "nodes": [ - "IPY_MODEL_120c8e8cb7664fa0aa0a6ca27a1c5f3f", - "IPY_MODEL_60e395b510714a92b02b331dedac2c22" - ] - } - }, - "fda99f8dbbbe4636a0bab906c5267b81": { - "model_module": "ipytree", - "model_module_version": "0.1.3", - "model_name": "NodeModel", - "state": { - "_id": "093bd8d7-6f8d-4017-85eb-edf3260c4a30", - "_model_module_version": "0.1.3", - "_view_module_version": "0.1.3", - "disabled": true, - "name": "baz", - "opened": false - } - } - }, - "version_major": 2, - "version_minor": 0 - } - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/pyproject.toml b/pyproject.toml index dc0e4730eb..1fe4558caf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,6 +97,10 @@ extra = [ ] optional = [ 'universal-pathlib>=0.0.22', + 'rich' +] +tree = [ + 'rich', ] [project.urls] diff --git a/src/zarr/core/_tree.py b/src/zarr/core/_tree.py new file mode 100644 index 0000000000..8e3b0c306d --- /dev/null +++ b/src/zarr/core/_tree.py @@ -0,0 +1,64 @@ +import io +from collections.abc import Sequence +from typing import Any + +from zarr.core.group import AsyncGroup + +try: + import rich + import rich.console + import rich.tree +except ImportError as e: + raise ImportError("'rich' is required for Group.tree") from e + + +class TreeRepr: + """ + A simple object with a tree-like repr for the Zarr Group. + + Note that this object and it's implementation isn't considered part + of Zarr's public API. + """ + + def __init__(self, tree: rich.tree.Tree) -> None: + self._tree = tree + + def __repr__(self) -> str: + terminal = rich.get_console() + console = rich.console.Console(file=io.StringIO(), color_system=terminal.color_system) + console.print(self._tree) + return str(console.file.getvalue()) + + def _repr_mimebundle_( + self, + include: Sequence[str], + exclude: Sequence[str], + **kwargs: Any, + ) -> dict[str, str]: + # For jupyter support. + # Unsure why mypy infers the return type to by Any + return self._tree._repr_mimebundle_(include=include, exclude=exclude, **kwargs) # type: ignore[no-any-return] + + +async def group_tree_async(group: AsyncGroup, max_depth: int | None = None) -> TreeRepr: + tree = rich.tree.Tree(label=f"[bold]{group.name}[/bold]") + nodes = {"": tree} + members = sorted([x async for x in group.members(max_depth=max_depth)]) + + for key, node in members: + if key.count("/") == 0: + parent_key = "" + else: + parent_key = key.rsplit("/", 1)[0] + parent = nodes[parent_key] + + # We want what the spec calls the node "name", the part excluding all leading + # /'s and path segments. But node.name includes all that, so we build it here. + name = key.rsplit("/")[-1] + if isinstance(node, AsyncGroup): + label = f"[bold]{name}[/bold]" + else: + label = f"[bold]{name}[/bold] {node.shape} {node.dtype}" + nodes[key] = parent.add(label) + + return TreeRepr(tree) diff --git a/src/zarr/core/group.py b/src/zarr/core/group.py index 9a54b346b0..de4c272fb9 100644 --- a/src/zarr/core/group.py +++ b/src/zarr/core/group.py @@ -1282,8 +1282,30 @@ async def array_values( async for _, array in self.arrays(): yield array - async def tree(self, expand: bool = False, level: int | None = None) -> Any: - raise NotImplementedError + async def tree(self, expand: bool | None = None, level: int | None = None) -> Any: + """ + Return a tree-like representation of a hierarchy. + + This requires the optional ``rich`` dependency. + + Parameters + ---------- + expand : bool, optional + This keyword is not yet supported. A NotImplementedError is raised if + it's used. + level : int, optional + The maximum depth below this Group to display in the tree. + + Returns + ------- + TreeRepr + A pretty-printable object displaying the hierarchy. + """ + from zarr.core._tree import group_tree_async + + if expand is not None: + raise NotImplementedError("'expanded' is not yet implemented.") + return await group_tree_async(self, max_depth=level) async def empty( self, *, name: str, shape: ChunkCoords, **kwargs: Any @@ -1519,7 +1541,25 @@ def array_values(self) -> Generator[Array, None]: for _, array in self.arrays(): yield array - def tree(self, expand: bool = False, level: int | None = None) -> Any: + def tree(self, expand: bool | None = None, level: int | None = None) -> Any: + """ + Return a tree-like representation of a hierarchy. + + This requires the optional ``rich`` dependency. + + Parameters + ---------- + expand : bool, optional + This keyword is not yet supported. A NotImplementedError is raised if + it's used. + level : int, optional + The maximum depth below this Group to display in the tree. + + Returns + ------- + TreeRepr + A pretty-printable object displaying the hierarchy. + """ return self._sync(self._async_group.tree(expand=expand, level=level)) def create_group(self, name: str, **kwargs: Any) -> Group: diff --git a/tests/test_tree.py b/tests/test_tree.py new file mode 100644 index 0000000000..e8bcae0ee3 --- /dev/null +++ b/tests/test_tree.py @@ -0,0 +1,59 @@ +import textwrap +from typing import Any + +import pytest + +import zarr + +pytest.importorskip("rich") + + +@pytest.mark.parametrize("root_name", [None, "root"]) +def test_tree(root_name: Any) -> None: + g = zarr.group(path=root_name) + A = g.create_group("A") + B = g.create_group("B") + C = B.create_group("C") + D = C.create_group("C") + + A.create_array(name="x", shape=(2), dtype="float64") + A.create_array(name="y", shape=(0,), dtype="int8") + B.create_array(name="x", shape=(0,)) + C.create_array(name="x", shape=(0,)) + D.create_array(name="x", shape=(0,)) + + result = repr(g.tree()) + root = root_name or "" + + BOPEN = "\x1b[1m" + BCLOSE = "\x1b[0m" + + expected = textwrap.dedent(f"""\ + {BOPEN}/{root}{BCLOSE} + ├── {BOPEN}A{BCLOSE} + │ ├── {BOPEN}x{BCLOSE} (2,) float64 + │ └── {BOPEN}y{BCLOSE} (0,) int8 + └── {BOPEN}B{BCLOSE} + ├── {BOPEN}C{BCLOSE} + │ ├── {BOPEN}C{BCLOSE} + │ │ └── {BOPEN}x{BCLOSE} (0,) float64 + │ └── {BOPEN}x{BCLOSE} (0,) float64 + └── {BOPEN}x{BCLOSE} (0,) float64 + """) + + assert result == expected + + result = repr(g.tree(level=0)) + expected = textwrap.dedent(f"""\ + {BOPEN}/{root}{BCLOSE} + ├── {BOPEN}A{BCLOSE} + └── {BOPEN}B{BCLOSE} + """) + + assert result == expected + + +def test_expand_not_implemented() -> None: + g = zarr.group() + with pytest.raises(NotImplementedError): + g.tree(expand=True)