Skip to content

Commit

Permalink
Merge branch '2.x.x' of github.com:c0fec0de/anytree into 3.x.x
Browse files Browse the repository at this point in the history
  • Loading branch information
c0fec0de committed Oct 11, 2023
2 parents 0dba4d4 + e55d024 commit cd1c4c8
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 73 deletions.
17 changes: 1 addition & 16 deletions anytree/exporter/dictexporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,12 @@ class DictExporter:
>>> s1 = AnyNode(a="sub1", parent=root)
>>> exporter = DictExporter()
>>> pprint(exporter.export(root)) # order within dictionary might vary!
>>> pprint(exporter.export(root))
{'a': 'root',
'children': [{'a': 'sub0',
'children': [{'a': 'sub0A', 'b': 'foo'}, {'a': 'sub0B'}]},
{'a': 'sub1'}]}
Pythons dictionary `dict` does not preserve order.
:any:`collections.OrderedDict` does.
In this case attributes can be ordered via `attriter`.
>>> from collections import OrderedDict
>>> exporter = DictExporter(dictcls=OrderedDict, attriter=sorted)
>>> pprint(exporter.export(root))
OrderedDict([('a', 'root'),
('children',
[OrderedDict([('a', 'sub0'),
('children',
[OrderedDict([('a', 'sub0A'), ('b', 'foo')]),
OrderedDict([('a', 'sub0B')])])]),
OrderedDict([('a', 'sub1')])])])
The attribute iterator `attriter` may be used for filtering too.
For example, just dump attributes named `a`:
Expand Down
67 changes: 36 additions & 31 deletions anytree/exporter/dotexporter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import codecs
import itertools
import logging
import re
from os import path, remove
Expand Down Expand Up @@ -361,26 +362,26 @@ def __init__(
>>> s1ca = Node("sub1Ca", parent=s1c)
>>> from anytree.exporter import UniqueDotExporter
>>> for line in UniqueDotExporter(root): # doctest: +SKIP
>>> for line in UniqueDotExporter(root):
... print(line)
digraph tree {
"0x7f1bf2c9c510" [label="root"];
"0x7f1bf2c9c5a0" [label="sub0"];
"0x7f1bf2c9c630" [label="s0"];
"0x7f1bf2c9c6c0" [label="s0"];
"0x7f1bf2c9c750" [label="sub1"];
"0x7f1bf2c9c7e0" [label="s1"];
"0x7f1bf2c9c870" [label="s1"];
"0x7f1bf2c9c900" [label="s1"];
"0x7f1bf2c9c990" [label="sub1Ca"];
"0x7f1bf2c9c510" -> "0x7f1bf2c9c5a0";
"0x7f1bf2c9c510" -> "0x7f1bf2c9c750";
"0x7f1bf2c9c5a0" -> "0x7f1bf2c9c630";
"0x7f1bf2c9c5a0" -> "0x7f1bf2c9c6c0";
"0x7f1bf2c9c750" -> "0x7f1bf2c9c7e0";
"0x7f1bf2c9c750" -> "0x7f1bf2c9c870";
"0x7f1bf2c9c750" -> "0x7f1bf2c9c900";
"0x7f1bf2c9c900" -> "0x7f1bf2c9c990";
"0x0" [label="root"];
"0x1" [label="sub0"];
"0x2" [label="s0"];
"0x3" [label="s0"];
"0x4" [label="sub1"];
"0x5" [label="s1"];
"0x6" [label="s1"];
"0x7" [label="s1"];
"0x8" [label="sub1Ca"];
"0x0" -> "0x1";
"0x0" -> "0x4";
"0x1" -> "0x2";
"0x1" -> "0x3";
"0x4" -> "0x5";
"0x4" -> "0x6";
"0x4" -> "0x7";
"0x7" -> "0x8";
}
The resulting graph:
Expand All @@ -396,16 +397,16 @@ def __init__(
>>> s0a = AnyNode(id="s0", parent=s0)
>>> from anytree.exporter import UniqueDotExporter
>>> for line in UniqueDotExporter(root, nodeattrfunc=lambda n: 'label="%s"' % (n.id)): # doctest: +SKIP
>>> for line in UniqueDotExporter(root, nodeattrfunc=lambda n: 'label="%s"' % (n.id)):
... print(line)
digraph tree {
"0x7f5c70449af8" [label="root"];
"0x7f5c70449bd0" [label="sub0"];
"0x7f5c70449c60" [label="s0"];
"0x7f5c70449cf0" [label="s0"];
"0x7f5c70449af8" -> "0x7f5c70449bd0";
"0x7f5c70449bd0" -> "0x7f5c70449c60";
"0x7f5c70449bd0" -> "0x7f5c70449cf0";
"0x0" [label="root"];
"0x1" [label="sub0"];
"0x2" [label="s0"];
"0x3" [label="s0"];
"0x0" -> "0x1";
"0x1" -> "0x2";
"0x1" -> "0x3";
}
"""
super(UniqueDotExporter, self).__init__(
Expand All @@ -418,12 +419,16 @@ def __init__(
nodeattrfunc=nodeattrfunc,
edgeattrfunc=edgeattrfunc,
edgetypefunc=edgetypefunc,
maxlevel=maxlevel,
)

@staticmethod
def _default_nodenamefunc(node):
return hex(id(node))
self.node_ids = {}
self.node_counter = itertools.count()

# pylint: disable=arguments-differ
def _default_nodenamefunc(self, node):
node_id = id(node)
if node_id not in self.node_ids:
self.node_ids[node_id] = next(self.node_counter)
return hex(self.node_ids[id(node)])

@staticmethod
def _default_nodeattrfunc(node):
Expand Down
2 changes: 1 addition & 1 deletion anytree/iterators/zigzaggroupiter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ZigZagGroupIter(AbstractIter):
(children of `node`) in reversed order.
The next level contains the children of the children in forward order, and so on.
>>> from anytree import Node, RenderTree, AsciiStyle
>>> from anytree import Node, RenderTree, AsciiStyle, ZigZagGroupIter
>>> f = Node("f")
>>> b = Node("b", parent=f)
>>> a = Node("a", parent=b)
Expand Down
34 changes: 19 additions & 15 deletions anytree/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ def commonancestors(*nodes):
"""
Determine common ancestors of `nodes`.
>>> from anytree import Node
>>> from anytree import Node, util
>>> udo = Node("Udo")
>>> marc = Node("Marc", parent=udo)
>>> lian = Node("Lian", parent=marc)
Expand All @@ -14,13 +14,13 @@ def commonancestors(*nodes):
>>> jan = Node("Jan", parent=dan)
>>> joe = Node("Joe", parent=dan)
>>> commonancestors(jet, joe)
>>> util.commonancestors(jet, joe)
(Node('/Udo'), Node('/Udo/Dan'))
>>> commonancestors(jet, marc)
>>> util.commonancestors(jet, marc)
(Node('/Udo'),)
>>> commonancestors(jet)
>>> util.commonancestors(jet)
(Node('/Udo'), Node('/Udo/Dan'))
>>> commonancestors()
>>> util.commonancestors()
()
"""
ancestors = [node.ancestors for node in nodes]
Expand All @@ -38,16 +38,18 @@ def leftsibling(node):
"""
Return Left Sibling of `node`.
>>> from anytree import Node
>>> from anytree import Node, util
>>> dan = Node("Dan")
>>> jet = Node("Jet", parent=dan)
>>> jan = Node("Jan", parent=dan)
>>> joe = Node("Joe", parent=dan)
>>> leftsibling(dan)
>>> leftsibling(jet)
>>> leftsibling(jan)
>>> print(util.leftsibling(dan))
None
>>> print(util.leftsibling(jet))
None
>>> print(util.leftsibling(jan))
Node('/Dan/Jet')
>>> leftsibling(joe)
>>> print(util.leftsibling(joe))
Node('/Dan/Jan')
"""
if node.parent:
Expand All @@ -62,17 +64,19 @@ def rightsibling(node):
"""
Return Right Sibling of `node`.
>>> from anytree import Node
>>> from anytree import Node, util
>>> dan = Node("Dan")
>>> jet = Node("Jet", parent=dan)
>>> jan = Node("Jan", parent=dan)
>>> joe = Node("Joe", parent=dan)
>>> rightsibling(dan)
>>> rightsibling(jet)
>>> print(util.rightsibling(dan))
None
>>> print(util.rightsibling(jet))
Node('/Dan/Jan')
>>> rightsibling(jan)
>>> print(util.rightsibling(jan))
Node('/Dan/Joe')
>>> rightsibling(joe)
>>> print(util.rightsibling(joe))
None
"""
if node.parent:
pchildren = node.parent.children
Expand Down
2 changes: 1 addition & 1 deletion docs/tricks/consistencychecks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Consistency Checks vs. Speed
============================

Anytree can run some *costly* internal consistency checks.
With version 2.9.2 these got disabled by default.
With version 2.9.1 these got disabled by default.
In case of any concerns about the internal data consistency or just for safety, either

* set the environment variable ``ANYTREE_ASSERTIONS=1``, or
Expand Down
14 changes: 5 additions & 9 deletions tests/test_uniquedotexporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,16 @@ def test_tree1():
s0 = Node("sub0", parent=root)
s0b = Node("sub0B", parent=s0)

id_root = hex(id(root))
id_s0 = hex(id(s0))
id_s0b = hex(id(s0b))

lines = tuple(UniqueDotExporter(root))
eq_(
lines,
(
"digraph tree {",
' "{id_root}" [label="root"];'.format(id_root=id_root),
' "{id_s0}" [label="sub0"];'.format(id_s0=id_s0),
' "{id_s0b}" [label="sub0B"];'.format(id_s0b=id_s0b),
' "{id_root}" -> "{id_s0}";'.format(id_root=id_root, id_s0=id_s0),
' "{id_s0}" -> "{id_s0b}";'.format(id_s0=id_s0, id_s0b=id_s0b),
' "0x0" [label="root"];',
' "0x1" [label="sub0"];',
' "0x2" [label="sub0B"];',
' "0x0" -> "0x1";',
' "0x1" -> "0x2";',
"}",
),
)

0 comments on commit cd1c4c8

Please sign in to comment.