Skip to content

Commit

Permalink
Some more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mar10 committed Oct 29, 2024
1 parent e53478a commit 76b34c9
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 111 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@

Handle multiple references of single objects ('clones') <br>
Search by name pattern, id, or object reference <br>
Unobtrusive handling of arbitrary objects <br>
Compare two trees and calculate patches <br>
Unobtrusive handling of arbitrary objects <br>
Save as DOT file and graphwiz diagram <br>
Nodes can be plain strings or objects <br>
(De)Serialize to (compressed) JSON <br>
Save as Mermaid flow diagram <br>
Different traversal methods <br>
Generate random trees <br>
Fully type annotated <br>
Convert to RDF graph <br>
Fully type annotated <br>
Typed child nodes <br>
Pretty print <br>
Navigation <br>
Expand Down
54 changes: 1 addition & 53 deletions docs/sphinx/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,66 +32,14 @@ nutree
**Note:** Run ``pip install "nutree[graph]"`` or ``pip install "nutree[all]"``
instead, in order to install additional graph support.

.. ::

.. from nutree import Tree, Node
.. tree = Tree("Store")
.. n = tree.add("Records")
.. n.add("Let It Be")
.. n.add("Get Yer Ya-Ya's Out!")
.. n = tree.add("Books")
.. n.add("The Little Prince")
.. tree.print()
.. ::

.. Tree<'Store'>
.. ├─── 'Records'
.. │ ├─── 'Let It Be'
.. │ ╰─── "Get Yer Ya-Ya's Out!"
.. ╰─── 'Books'
.. ╰─── 'The Little Prince'
.. Tree nodes wrap the data and also expose methods for navigation, searching,
.. iteration, ... ::
.. records_node = tree["Records"]
.. assert isinstance(records_node, Node)
.. assert records_node.name == "Records"
.. print(records_node.first_child())
.. ::

.. Node<'Let It Be', data_id=510268653885439170>
.. Nodes may be strings or arbitrary objects::
.. alice = Person("Alice", age=23, guid="{123-456}")
.. tree.add(alice)
.. # Lookup nodes by object, data_id, name pattern, ...
.. alice_node = tree[alice]
.. assert isinstance(alice_node.data, Person)
.. assert alice_node.data is alice
.. del tree[alice]

Nutree Facts
============

* :ref:`Handle multiple references of single objects ('clones') <clones>`
* :ref:`Search by name pattern, id, or object reference <searching>`
* :ref:`Unobtrusive handling of arbitrary objects <objects>`
* :ref:`Compare two trees and calculate patches <diff-and-merge>`
* :ref:`Unobtrusive handling of arbitrary objects <objects>`
* :ref:`Save as DOT file and graphwiz diagram <save-dot>`
* :ref:`Nodes can be plain strings or objects <objects>`
* :ref:`(De)Serialize to (compressed) JSON <serialize>`
Expand Down
11 changes: 5 additions & 6 deletions nutree/tree_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from fabulist import Fabulist

fab = Fabulist()
except ImportError:
except ImportError: # pragma: no cover
# We run without fabulist (with reduced functionality in this case)
Fabulist = None
fab = None
Expand Down Expand Up @@ -149,7 +149,6 @@ def __init__(
def generate(self) -> Union[date, float, None]:
# print(self.min, self.max, self.delta_days, self.probability)
if self._skip_value():
# print("SKIP")
return
res = self.min + timedelta(days=random.randrange(self.delta_days))
# print(res)
Expand Down Expand Up @@ -205,7 +204,7 @@ def __init__(
super().__init__(probability=probability)
self.sample_list = sample_list
# TODO: remove this when support for Python 3.8 is removed
if sys.version_info < (3, 9) and counts:
if sys.version_info < (3, 9) and counts: # pragma: no cover
raise RuntimeError("counts argument requires Python 3.9 or later.")

self.counts = counts
Expand All @@ -214,7 +213,7 @@ def generate(self) -> Any:
if self._skip_value():
return
# TODO: remove this when support for Python 3.8 is removed
if sys.version_info < (3, 9) and not self.counts:
if sys.version_info < (3, 9) and not self.counts: # pragma: no cover
return random.sample(self.sample_list, 1)[0]
return random.sample(self.sample_list, 1, counts=self.counts)[0]

Expand Down Expand Up @@ -242,7 +241,7 @@ class TextRandomizer(Randomizer):

def __init__(self, template: str | list[str], *, probability: float = 1.0) -> None:
super().__init__(probability=probability)
if not fab:
if not fab: # pragma: no cover
raise RuntimeError("Need fabulist installed to generate random text.")
self.template = template

Expand Down Expand Up @@ -283,7 +282,7 @@ def __init__(
probability: float = 1.0,
) -> None:
super().__init__(probability=probability)
if not fab:
if not fab: # pragma: no cover
raise RuntimeError("Need fabulist installed to generate random text.")

self.sentence_count = sentence_count
Expand Down
33 changes: 0 additions & 33 deletions nutree/typed_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,39 +411,6 @@ def append_sibling(
node_id=node_id,
)

# def move_to(
# self,
# new_parent: TypedNode | TypedTree,
# *,
# before: TypedNode | bool | int | None = None,
# ):
# """Move this node before or after `new_parent`."""
# raise NotImplementedError

# def copy(self, *, add_self=True, predicate=None) -> TypedTree:
# """Return a new :class:`~nutree.typed_tree.TypedTree` instance from this
# branch.

# See also :meth:`_add_from` and :ref:`iteration-callbacks`.
# """
# return super().copy(add_self=add_self, predicate=predicate)

# def filtered(self, predicate: PredicateCallbackType) -> TypedTree:
# """Return a filtered copy of this node and descendants as tree.

# See also :ref:`iteration-callbacks`.
# """
# return super().filtered(predicate=predicate)

# def iterator(
# self, method: IterMethod = IterMethod.PRE_ORDER, *, add_self=False
# ) -> Iterator[Node]:
# """Generator that walks the hierarchy."""
# return super().iterator(method=method, add_self=add_self)

# #: Implement ``for subnode in node: ...`` syntax to iterate descendant nodes.
# __iter__ = iterator

@classmethod
def _make_list_entry(cls, node: Self) -> dict[str, Any]:
node_data = node._data
Expand Down
2 changes: 2 additions & 0 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ def test_basic(self):

assert records.tree is records._tree

assert len(tree.get_toplevel_nodes()) == 2

assert tree.find(data="Records") is records
# TODO: hashes are salted in Py3, so we can't assume stable keys in tests
# assert tree.find(data_id="1862529381406879915") is records
Expand Down
17 changes: 13 additions & 4 deletions tests/test_tree_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@

class TestBase:
def test_simple(self):
_cb_count = 0

def _calback(data):
nonlocal _cb_count
assert data["title"].startswith("Failure ")
_cb_count += 1

structure_def = {
"name": "fmea",
#: Types define the default properties of the nodes
Expand All @@ -41,28 +48,29 @@ def test_simple(self):
"relations": {
"__root__": {
"function": {
":count": 3,
":count": 30,
"title": "Function {hier_idx}",
"date": DateRangeRandomizer(
datetime.date(2020, 1, 1), datetime.date(2020, 12, 31)
),
"date2": DateRangeRandomizer(
datetime.date(2020, 1, 1), 365, probability=0.99
datetime.date(2020, 1, 1), 365, probability=0.5
),
"value": ValueRandomizer("foo", probability=0.5),
"expanded": SparseBoolRandomizer(probability=0.5),
"state": SampleRandomizer(["open", "closed"], probability=0.99),
"state": SampleRandomizer(["open", "closed"], probability=0.5),
},
},
"function": {
"failure": {
":count": RangeRandomizer(1, 3),
":callback": _calback,
"title": "Failure {hier_idx}",
},
},
"failure": {
"cause": {
":count": RangeRandomizer(1, 3, probability=0.99),
":count": RangeRandomizer(1, 3, probability=0.5),
"title": "Cause {hier_idx}",
},
"effect": {
Expand All @@ -76,6 +84,7 @@ def test_simple(self):
tree.print()
assert type(tree) is Tree
assert tree.calc_height() == 3
assert _cb_count >= 3

tree2 = TypedTree.build_random_tree(structure_def)
tree2.print()
Expand Down
56 changes: 43 additions & 13 deletions tests/test_typed_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
# pyright: reportOptionalMemberAccess=false

import re
from pathlib import Path

from nutree.typed_tree import ANY_KIND, TypedNode, TypedTree, _SystemRootTypedNode

from . import fixture


class TestTypedTree:
# def met
def test_add_child(self):
tree = TypedTree("fixture")

Expand Down Expand Up @@ -58,6 +60,7 @@ def test_add_child(self):
`- function → func2
""",
)
assert tree.last_child(ANY_KIND).name == "func2"

assert len(fail1.children) == 4
assert fail1.get_children(kind=ANY_KIND) == fail1.children
Expand Down Expand Up @@ -139,6 +142,36 @@ def test_add_child(self):
subtree = func2.copy()
assert isinstance(subtree, TypedTree)

def test_add_child_2(self):
tree = TypedTree("fixture")

a = tree.add("A")
a.append_child("a1")
a.prepend_child("a0")
a.append_sibling("A2")
a.prepend_sibling("A0")

b = tree.add("B", kind="letter")
tree_2 = TypedTree("fixture2").add("X").add("x1").up(2).add("Y").add("y1").tree
b.append_child(tree_2)
tree.print()
assert fixture.check_content(
tree,
"""
TypedTree<*>
+- child → A0
+- child → A
| +- child → a0
| `- child → a1
+- child → A2
`- letter → B
+- child → X
| `- child → x1
`- child → Y
`- child → y1
""",
)

def test_graph_product(self):
tree = TypedTree("Pencil")

Expand Down Expand Up @@ -172,16 +205,13 @@ def test_graph_product(self):
# tree.print()
# raise

# with fixture.WritableTempFile("w", suffix=".png") as temp_file:

# tree.to_dotfile(
# # temp_file.name,
# "/Users/martin/Downloads/tree_graph_pencil.png",
# format="png",
# graph_attrs={"rankdir": "LR"},
# # add_root=False,
# # node_mapper=node_mapper,
# # edge_mapper=edge_mapper,
# # unique_nodes=False,
# )
# assert False
def test_graph_product2(self):
tree = fixture.create_typed_tree()
tree.print()
with fixture.WritableTempFile("w", suffix=".gv") as temp_file:
tree.to_dotfile(temp_file.name)

buffer = Path(temp_file.name).read_text()

print(buffer)
assert '[label="func2"]' in buffer

0 comments on commit 76b34c9

Please sign in to comment.