verysimpletree 0.1.0b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
File without changes
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
from unittest import TestCase
|
|
2
|
+
|
|
3
|
+
from verysimpletree.tree import Tree, ChildNotFoundError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class A(Tree):
|
|
7
|
+
def __init__(self, name, parent=None, *args, **keyword):
|
|
8
|
+
super().__init__(*args, **keyword)
|
|
9
|
+
self._children = []
|
|
10
|
+
self._parent = parent
|
|
11
|
+
self.name = name
|
|
12
|
+
|
|
13
|
+
def _check_child_to_be_added(self, child):
|
|
14
|
+
if not isinstance(child, self.__class__):
|
|
15
|
+
raise TypeError
|
|
16
|
+
|
|
17
|
+
def add_child(self, name):
|
|
18
|
+
child = type(self)(parent=self, name=name)
|
|
19
|
+
return super().add_child(child)
|
|
20
|
+
|
|
21
|
+
def __str__(self):
|
|
22
|
+
return self.name
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
return self.__str__()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TestTree(TestCase):
|
|
29
|
+
def setUp(self) -> None:
|
|
30
|
+
self.root = A(name='root')
|
|
31
|
+
self.child1 = self.root.add_child('child1')
|
|
32
|
+
self.child2 = self.root.add_child('child2')
|
|
33
|
+
self.child3 = self.root.add_child('child3')
|
|
34
|
+
self.child4 = self.root.add_child('child4')
|
|
35
|
+
self.grandchild1 = self.child2.add_child('grandchild1')
|
|
36
|
+
self.grandchild2 = self.child2.add_child('grandchild2')
|
|
37
|
+
self.grandchild3 = self.child4.add_child('grandchild3')
|
|
38
|
+
self.greatgrandchild1 = self.grandchild2.add_child('greatgrandchild1')
|
|
39
|
+
|
|
40
|
+
def test_get_root(self):
|
|
41
|
+
assert self.greatgrandchild1.get_root() == self.root
|
|
42
|
+
assert self.child4.get_root() == self.root
|
|
43
|
+
assert self.root.get_root() == self.root
|
|
44
|
+
|
|
45
|
+
def test_is_leaf(self):
|
|
46
|
+
assert self.greatgrandchild1.is_leaf is True
|
|
47
|
+
assert self.child4.is_leaf is False
|
|
48
|
+
assert self.child3.is_leaf is True
|
|
49
|
+
|
|
50
|
+
def test_traverse(self):
|
|
51
|
+
assert list(self.root.traverse()) == [self.root, self.child1, self.child2, self.grandchild1, self.grandchild2,
|
|
52
|
+
self.greatgrandchild1, self.child3, self.child4, self.grandchild3]
|
|
53
|
+
|
|
54
|
+
def test_iterate_leaves(self):
|
|
55
|
+
assert list(self.root.iterate_leaves()) == [self.child1, self.grandchild1, self.greatgrandchild1,
|
|
56
|
+
self.child3, self.grandchild3]
|
|
57
|
+
|
|
58
|
+
def test_level(self):
|
|
59
|
+
assert [node.level for node in self.root.traverse()] == [0, 1, 1, 2, 2, 3, 1, 1, 2]
|
|
60
|
+
assert self.greatgrandchild1.level == 3
|
|
61
|
+
assert self.grandchild2.level == 2
|
|
62
|
+
assert self.child4.level == 1
|
|
63
|
+
assert self.root.level == 0
|
|
64
|
+
|
|
65
|
+
def test_tree_repr(self):
|
|
66
|
+
expected = """root
|
|
67
|
+
child1
|
|
68
|
+
child2
|
|
69
|
+
grandchild1
|
|
70
|
+
grandchild2
|
|
71
|
+
greatgrandchild1
|
|
72
|
+
child3
|
|
73
|
+
child4
|
|
74
|
+
grandchild3
|
|
75
|
+
"""
|
|
76
|
+
assert self.root.tree_representation(lambda x: x.name) == expected
|
|
77
|
+
|
|
78
|
+
def test_reversed_path_to_root(self):
|
|
79
|
+
assert list(self.greatgrandchild1.reversed_path_to_root()) == [self.greatgrandchild1, self.grandchild2, self.child2, self.root]
|
|
80
|
+
|
|
81
|
+
def test_remove_child(self):
|
|
82
|
+
with self.assertRaises(ChildNotFoundError):
|
|
83
|
+
self.child2.remove(self.child1)
|
|
84
|
+
|
|
85
|
+
self.child2.remove(self.grandchild2)
|
|
86
|
+
assert self.child2.get_children() == [self.grandchild1]
|
|
87
|
+
assert self.grandchild2.get_parent() is None
|
|
88
|
+
assert self.greatgrandchild1.get_parent() == self.grandchild2
|
|
89
|
+
|
|
90
|
+
def test_get_coordinates(self):
|
|
91
|
+
assert self.greatgrandchild1.get_coordinates_in_tree() == '2.2.1'
|
|
92
|
+
assert self.child2.get_coordinates_in_tree() == '2'
|
|
93
|
+
assert self.root.get_coordinates_in_tree() == '0'
|
|
94
|
+
|
|
95
|
+
def test_replace_child(self):
|
|
96
|
+
self.child2.replace_child(self.grandchild1, A(name='new_grand_child'))
|
|
97
|
+
assert [ch.name for ch in self.child2.get_children()] == ['new_grand_child', 'grandchild2']
|
|
98
|
+
self.child1.add_child('grandchild')
|
|
99
|
+
self.child1.add_child('grandchild')
|
|
100
|
+
new = A(name='other_new_grand_child')
|
|
101
|
+
self.child1.replace_child(lambda x: x.name == 'grandchild', new, 1)
|
|
102
|
+
assert new.get_parent() == self.child1
|
|
103
|
+
assert [ch.name for ch in self.child1.get_children()] == ['grandchild', 'other_new_grand_child']
|
|
104
|
+
with self.assertRaises(ValueError):
|
|
105
|
+
self.child2.replace_child(None, None)
|
|
106
|
+
with self.assertRaises(TypeError):
|
|
107
|
+
self.root.replace_child(self.child1, 34)
|
|
108
|
+
|
|
109
|
+
def test_previous(self):
|
|
110
|
+
assert self.child4.previous == self.child3
|
|
111
|
+
assert self.child3.previous == self.child2
|
|
112
|
+
assert self.child2.previous == self.child1
|
|
113
|
+
assert self.child1.previous is None
|
|
114
|
+
|
|
115
|
+
def test_next(self):
|
|
116
|
+
assert self.child1.next == self.child2
|
|
117
|
+
assert self.child2.next == self.child3
|
|
118
|
+
assert self.child3.next == self.child4
|
|
119
|
+
assert self.child4.next is None
|
|
120
|
+
|
|
121
|
+
def test_get_leaves(self):
|
|
122
|
+
assert self.root.get_leaves(key=lambda x: x.name) == ['child1', ['grandchild1', ['greatgrandchild1']], 'child3', ['grandchild3']]
|
|
123
|
+
|
|
124
|
+
def test_get_layer(self):
|
|
125
|
+
assert self.root.get_layer(0) == [self.root]
|
|
126
|
+
assert self.root.get_layer(1) == [self.child1, self.child2, self.child3, self.child4]
|
|
127
|
+
assert self.root.get_layer(2) == [self.child1, self.grandchild1, self.grandchild2, self.child3, self.grandchild3]
|
|
128
|
+
assert self.root.get_layer(3) == [self.child1, self.grandchild1, self.greatgrandchild1, self.child3, self.grandchild3]
|
|
129
|
+
assert self.root.get_layer(4) == [self.child1, self.grandchild1, self.greatgrandchild1, self.child3, self.grandchild3]
|
|
130
|
+
|
|
131
|
+
def test_find_grandchild(self):
|
|
132
|
+
assert [n for n in self.root.traverse() if n.level == 2] == [self.grandchild1, self.grandchild2, self.grandchild3]
|
|
133
|
+
for n in self.root.traverse():
|
|
134
|
+
if n.level == 2:
|
|
135
|
+
print(n)
|
|
136
|
+
break
|
verysimpletree/tree.py
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Optional, Callable, List, Iterator
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TreeException(Exception):
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ChildNotFoundError(TreeException):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Tree(ABC):
|
|
14
|
+
"""
|
|
15
|
+
An abstract lightweight tree class for managing tree structures in MusicXML and musicscore packages.
|
|
16
|
+
"""
|
|
17
|
+
_TREE_ATTRIBUTES = {'compact_repr', 'is_leaf', 'level', '_parent', '_children', 'up'}
|
|
18
|
+
|
|
19
|
+
def __init__(self, *args, **kwargs):
|
|
20
|
+
super().__init__(*args, **kwargs)
|
|
21
|
+
self._parent = None
|
|
22
|
+
self._children = []
|
|
23
|
+
self._traversed = None
|
|
24
|
+
self._is_leaf = True
|
|
25
|
+
self._iterated_leaves = None
|
|
26
|
+
self._reversed_path_to_root = None
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def _check_child_to_be_added(self, child):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
def _raw_traverse(self):
|
|
33
|
+
yield self
|
|
34
|
+
for child in self.get_children():
|
|
35
|
+
for node in child._raw_traverse():
|
|
36
|
+
yield node
|
|
37
|
+
|
|
38
|
+
def _raw_reversed_path_to_root(self):
|
|
39
|
+
yield self
|
|
40
|
+
if self.get_parent():
|
|
41
|
+
for node in self.get_parent().reversed_path_to_root():
|
|
42
|
+
yield node
|
|
43
|
+
|
|
44
|
+
def _reset_iterators(self):
|
|
45
|
+
"""
|
|
46
|
+
This method is used to reset both parent's and this class's iterators for :obj:'~traverse', obj:'~iterate_leaves' and obj:'~reversed_path_to_root'
|
|
47
|
+
"""
|
|
48
|
+
if self.up:
|
|
49
|
+
self.up._reset_iterators()
|
|
50
|
+
self._traversed = None
|
|
51
|
+
self._iterated_leaves = None
|
|
52
|
+
self._reversed_path_to_root = None
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def compact_repr(self) -> str:
|
|
56
|
+
"""
|
|
57
|
+
:obj:`~tree.tree.Tree` property
|
|
58
|
+
|
|
59
|
+
:return: compact representation of a node. Default is the string representation. This property is used as default in the :obj:`~tree_representation` method and can be
|
|
60
|
+
customized in subclasses to get the most appropriate representation.
|
|
61
|
+
:rtype: str
|
|
62
|
+
"""
|
|
63
|
+
return self.__str__()
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def is_leaf(self) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
:obj:`~tree.tree.Tree` property
|
|
69
|
+
|
|
70
|
+
:return: ``True`` if self has no children. ``False`` if self has one or more children.
|
|
71
|
+
:rtype: bool
|
|
72
|
+
"""
|
|
73
|
+
return self._is_leaf
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def is_root(self) -> bool:
|
|
77
|
+
"""
|
|
78
|
+
:obj:`~tree.tree.Tree` property
|
|
79
|
+
|
|
80
|
+
:return: ``True`` if self has no parent, else ``False``.
|
|
81
|
+
:rtype: bool
|
|
82
|
+
"""
|
|
83
|
+
return True if self.get_parent() is None else False
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def level(self) -> int:
|
|
87
|
+
"""
|
|
88
|
+
:obj:`~tree.tree.Tree` property
|
|
89
|
+
|
|
90
|
+
:return: ``0`` for ``root``, ``1, 2 etc.`` for each layer of children
|
|
91
|
+
:rtype: nonnegative int
|
|
92
|
+
|
|
93
|
+
>>> class TestTree(Tree):
|
|
94
|
+
... def _check_child_to_be_added(self, child):
|
|
95
|
+
... return True
|
|
96
|
+
>>> root = TestTree()
|
|
97
|
+
>>> root.level
|
|
98
|
+
0
|
|
99
|
+
>>> ch = root.add_child(TestTree()).add_child(TestTree()).add_child(TestTree())
|
|
100
|
+
>>> ch.level
|
|
101
|
+
3
|
|
102
|
+
"""
|
|
103
|
+
if self.get_parent() is None:
|
|
104
|
+
return 0
|
|
105
|
+
else:
|
|
106
|
+
return self.get_parent().level + 1
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def next(self) -> Optional['Tree']:
|
|
110
|
+
"""
|
|
111
|
+
:obj:`~tree.tree.Tree` property
|
|
112
|
+
|
|
113
|
+
:return: next sibling. ``None`` if this is the last current child of the parent.
|
|
114
|
+
:rtype: :obj:`~tree.tree.Tree`
|
|
115
|
+
"""
|
|
116
|
+
if self.up and self != self.up.get_children()[-1]:
|
|
117
|
+
return self.up.get_children()[self.up.get_children().index(self) + 1]
|
|
118
|
+
else:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def previous(self) -> Optional['Tree']:
|
|
123
|
+
"""
|
|
124
|
+
:obj:`~tree.tree.Tree` property
|
|
125
|
+
|
|
126
|
+
:return: previous sibling. ``None`` if this is the first child of the parent.
|
|
127
|
+
:rtype: :obj:`~tree.tree.Tree`
|
|
128
|
+
"""
|
|
129
|
+
if self.up and self != self.up.get_children()[0]:
|
|
130
|
+
return self.up.get_children()[self.up.get_children().index(self) - 1]
|
|
131
|
+
else:
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def up(self) -> 'Tree':
|
|
136
|
+
"""
|
|
137
|
+
:obj:`~tree.tree.Tree` property
|
|
138
|
+
|
|
139
|
+
:return: :obj:`get_parent()`
|
|
140
|
+
:rtype: :obj:`~tree.tree.Tree`
|
|
141
|
+
"""
|
|
142
|
+
return self.get_parent()
|
|
143
|
+
|
|
144
|
+
def add_child(self, child: 'Tree') -> 'Tree':
|
|
145
|
+
"""
|
|
146
|
+
:obj:`~tree.tree.Tree` method
|
|
147
|
+
|
|
148
|
+
Check and add child to list of children. Child's parent is set to self.
|
|
149
|
+
|
|
150
|
+
:param child:
|
|
151
|
+
:return: child
|
|
152
|
+
:rtype: :obj:`~tree.tree.Tree`
|
|
153
|
+
"""
|
|
154
|
+
self._check_child_to_be_added(child)
|
|
155
|
+
child._parent = self
|
|
156
|
+
self._children.append(child)
|
|
157
|
+
self._reset_iterators()
|
|
158
|
+
if self._is_leaf is True:
|
|
159
|
+
self._is_leaf = False
|
|
160
|
+
return child
|
|
161
|
+
|
|
162
|
+
def get_children(self) -> List['Tree']:
|
|
163
|
+
"""
|
|
164
|
+
:obj:`~tree.tree.Tree` method
|
|
165
|
+
|
|
166
|
+
:return: list of added children.
|
|
167
|
+
:rtype: List[:obj:`~tree.tree.Tree`]
|
|
168
|
+
"""
|
|
169
|
+
return self._children
|
|
170
|
+
|
|
171
|
+
def get_children_of_type(self, type) -> List['Tree']:
|
|
172
|
+
"""
|
|
173
|
+
:obj:`~tree.tree.Tree` method
|
|
174
|
+
|
|
175
|
+
:return: list of added children of type.
|
|
176
|
+
:rtype: List[:obj:`~tree.tree.Tree`]
|
|
177
|
+
"""
|
|
178
|
+
return [ch for ch in self.get_children() if isinstance(ch, type)]
|
|
179
|
+
|
|
180
|
+
def get_coordinates_in_tree(self) -> str:
|
|
181
|
+
"""
|
|
182
|
+
:obj:`~tree.tree.Tree` method
|
|
183
|
+
|
|
184
|
+
:return: 0 for ``root``. 1, 2, ... for layer 1. Other layers: x.y.z.... Example: 3.2.2 => third child of secod child of second child
|
|
185
|
+
of the root.
|
|
186
|
+
:rtype: str
|
|
187
|
+
|
|
188
|
+
>>> class TestTree(Tree):
|
|
189
|
+
... def _check_child_to_be_added(self, child):
|
|
190
|
+
... return True
|
|
191
|
+
>>> root = TestTree()
|
|
192
|
+
>>> root.get_coordinates_in_tree()
|
|
193
|
+
'0'
|
|
194
|
+
>>> child1 = root.add_child(TestTree())
|
|
195
|
+
>>> child2 = root.add_child(TestTree())
|
|
196
|
+
>>> grandchild1 = child2.add_child(TestTree())
|
|
197
|
+
>>> grandchild2 = child2.add_child(TestTree())
|
|
198
|
+
>>> child1.get_coordinates_in_tree()
|
|
199
|
+
'1'
|
|
200
|
+
>>> child2.get_coordinates_in_tree()
|
|
201
|
+
'2'
|
|
202
|
+
>>> grandchild1.get_coordinates_in_tree()
|
|
203
|
+
'2.1'
|
|
204
|
+
>>> grandchild2.get_coordinates_in_tree()
|
|
205
|
+
'2.2'
|
|
206
|
+
"""
|
|
207
|
+
if self.level == 0:
|
|
208
|
+
return '0'
|
|
209
|
+
elif self.level == 1:
|
|
210
|
+
return str(self.get_parent().get_children().index(self) + 1)
|
|
211
|
+
else:
|
|
212
|
+
return f"{self.get_parent().get_coordinates_in_tree()}.{self.get_parent().get_children().index(self) + 1}"
|
|
213
|
+
|
|
214
|
+
def get_indentation(self) -> str:
|
|
215
|
+
"""
|
|
216
|
+
:obj:`~tree.tree.Tree` method
|
|
217
|
+
|
|
218
|
+
:return: indentation according to ``level`` (layer number). As default it is used for creating tabs in :obj:`tree_representation`
|
|
219
|
+
:rtype: str
|
|
220
|
+
"""
|
|
221
|
+
indentation = ''
|
|
222
|
+
for i in range(self.level):
|
|
223
|
+
indentation += ' '
|
|
224
|
+
return indentation
|
|
225
|
+
|
|
226
|
+
def get_parent(self) -> 'Tree':
|
|
227
|
+
"""
|
|
228
|
+
:obj:`~tree.tree.Tree` method
|
|
229
|
+
|
|
230
|
+
:return: parent. ``None`` for ``root``.
|
|
231
|
+
:rtype: :obj:`~tree.tree.Tree`
|
|
232
|
+
"""
|
|
233
|
+
return self._parent
|
|
234
|
+
|
|
235
|
+
def get_leaves(self, key: Optional[Callable] = None) -> list:
|
|
236
|
+
"""
|
|
237
|
+
:obj:`~tree.tree.Tree` method
|
|
238
|
+
|
|
239
|
+
:param key: An optional callable to be called on each leaf.
|
|
240
|
+
:return: nested list of leaves or values of key(leaf) for each leaf
|
|
241
|
+
:rtype: nested list of :obj:`~tree.tree.Tree`
|
|
242
|
+
"""
|
|
243
|
+
output = []
|
|
244
|
+
for child in self.get_children():
|
|
245
|
+
if not child.is_leaf:
|
|
246
|
+
output.append(child.get_leaves(key=key))
|
|
247
|
+
else:
|
|
248
|
+
if key is not None:
|
|
249
|
+
output.append(key(child))
|
|
250
|
+
else:
|
|
251
|
+
output.append(child)
|
|
252
|
+
|
|
253
|
+
return output
|
|
254
|
+
|
|
255
|
+
def get_root(self) -> 'Tree':
|
|
256
|
+
"""
|
|
257
|
+
:obj:`~tree.tree.Tree` method
|
|
258
|
+
|
|
259
|
+
:return: ``root`` (upmost node of a tree which has no parent)
|
|
260
|
+
:rtype: :obj:`~tree.tree.Tree`
|
|
261
|
+
"""
|
|
262
|
+
node = self
|
|
263
|
+
parent = node.get_parent()
|
|
264
|
+
while parent is not None:
|
|
265
|
+
node = parent
|
|
266
|
+
parent = node.get_parent()
|
|
267
|
+
return node
|
|
268
|
+
|
|
269
|
+
def get_layer(self, level: int, key: Optional[Callable] = None) -> list:
|
|
270
|
+
"""
|
|
271
|
+
:obj:`~tree.tree.Tree` method
|
|
272
|
+
|
|
273
|
+
:param level: layer number where 0 is the ``root``.
|
|
274
|
+
:param key: An optional callable for each node in the layer.
|
|
275
|
+
:return: All nodes on this level. The leaves of branches which are shorter than the given level will be repeated on this and all
|
|
276
|
+
following layers.
|
|
277
|
+
:rtype: list
|
|
278
|
+
"""
|
|
279
|
+
if level == 0:
|
|
280
|
+
output = [self]
|
|
281
|
+
elif level == 1:
|
|
282
|
+
output = self.get_children()
|
|
283
|
+
else:
|
|
284
|
+
output = []
|
|
285
|
+
for child in self.get_layer(level - 1):
|
|
286
|
+
if child.is_leaf:
|
|
287
|
+
output.append(child)
|
|
288
|
+
else:
|
|
289
|
+
output.extend(child.get_children())
|
|
290
|
+
if key is None:
|
|
291
|
+
return output
|
|
292
|
+
else:
|
|
293
|
+
return [key(child) for child in output]
|
|
294
|
+
|
|
295
|
+
def iterate_leaves(self) -> Iterator['Tree']:
|
|
296
|
+
"""
|
|
297
|
+
:obj:`~tree.tree.Tree` method
|
|
298
|
+
|
|
299
|
+
:return: A generator iterating over all leaves.
|
|
300
|
+
"""
|
|
301
|
+
if self._iterated_leaves is None:
|
|
302
|
+
self._iterated_leaves = [n for n in self.traverse() if n.is_leaf]
|
|
303
|
+
return iter(self._iterated_leaves)
|
|
304
|
+
|
|
305
|
+
def remove(self, child: 'Tree') -> None:
|
|
306
|
+
"""
|
|
307
|
+
:obj:`~tree.tree.Tree` method
|
|
308
|
+
|
|
309
|
+
Child's parent will be set to ``None`` and child will be removed from list of children.
|
|
310
|
+
|
|
311
|
+
:param child:
|
|
312
|
+
:return: None
|
|
313
|
+
"""
|
|
314
|
+
if child not in self.get_children():
|
|
315
|
+
raise ChildNotFoundError
|
|
316
|
+
child._parent = None
|
|
317
|
+
self.get_children().remove(child)
|
|
318
|
+
self._reset_iterators()
|
|
319
|
+
|
|
320
|
+
def remove_children(self) -> None:
|
|
321
|
+
"""
|
|
322
|
+
:obj:`~tree.tree.Tree` method
|
|
323
|
+
|
|
324
|
+
Calls :obj:`remove()` on all children.
|
|
325
|
+
|
|
326
|
+
:return: None
|
|
327
|
+
"""
|
|
328
|
+
for child in self.get_children()[:]:
|
|
329
|
+
child.up.remove(child)
|
|
330
|
+
|
|
331
|
+
def replace_child(self, old, new, index: int = 0) -> None:
|
|
332
|
+
"""
|
|
333
|
+
:obj:`~tree.tree.Tree` method
|
|
334
|
+
|
|
335
|
+
:param old: child or function
|
|
336
|
+
:param new: child
|
|
337
|
+
:param index: index of old child in the list of its appearances
|
|
338
|
+
:return: None
|
|
339
|
+
"""
|
|
340
|
+
if hasattr(old, '__call__'):
|
|
341
|
+
list_of_olds = [ch for ch in self.get_children() if old(ch)]
|
|
342
|
+
else:
|
|
343
|
+
list_of_olds = [ch for ch in self.get_children() if ch == old]
|
|
344
|
+
if not list_of_olds:
|
|
345
|
+
raise ValueError(f"{old} not in list.")
|
|
346
|
+
self._check_child_to_be_added(new)
|
|
347
|
+
old_index = self.get_children().index(list_of_olds[index])
|
|
348
|
+
old_child = self.get_children()[old_index]
|
|
349
|
+
self.get_children().remove(old_child)
|
|
350
|
+
self.get_children().insert(old_index, new)
|
|
351
|
+
old_child._parent = None
|
|
352
|
+
self._reset_iterators()
|
|
353
|
+
new._parent = self
|
|
354
|
+
|
|
355
|
+
def reversed_path_to_root(self) -> Iterator['Tree']:
|
|
356
|
+
"""
|
|
357
|
+
:obj:`~tree.tree.Tree` method
|
|
358
|
+
|
|
359
|
+
:return: path from self upwards through all ancestors up to the ``root``.
|
|
360
|
+
"""
|
|
361
|
+
if self._reversed_path_to_root is None:
|
|
362
|
+
self._reversed_path_to_root = list(self._raw_reversed_path_to_root())
|
|
363
|
+
return self._reversed_path_to_root
|
|
364
|
+
|
|
365
|
+
def traverse(self) -> Iterator['Tree']:
|
|
366
|
+
"""
|
|
367
|
+
:obj:`~tree.tree.Tree` method
|
|
368
|
+
|
|
369
|
+
Traverse all tree nodes.
|
|
370
|
+
|
|
371
|
+
:return: generator
|
|
372
|
+
"""
|
|
373
|
+
if self._traversed is None:
|
|
374
|
+
self._traversed = list(self._raw_traverse())
|
|
375
|
+
return iter(self._traversed)
|
|
376
|
+
|
|
377
|
+
def tree_representation(self, key: Optional[Callable] = None, tab: Optional[Callable] = None) -> str:
|
|
378
|
+
"""
|
|
379
|
+
:obj:`~tree.tree.Tree` method
|
|
380
|
+
|
|
381
|
+
:param key: An optional callable if ``None`` :obj:`~compact_repr` property of each node is called.
|
|
382
|
+
:param tab: An optional callable if ``None`` :obj:`get_indentation()` method of each node is called.
|
|
383
|
+
:return: a representation of all nodes as string in tree form.
|
|
384
|
+
:rtype: str
|
|
385
|
+
"""
|
|
386
|
+
if not key:
|
|
387
|
+
key = lambda x: x.compact_repr
|
|
388
|
+
|
|
389
|
+
if not tab:
|
|
390
|
+
tab = lambda x: x.get_indentation()
|
|
391
|
+
|
|
392
|
+
output = ''
|
|
393
|
+
for node in self.traverse():
|
|
394
|
+
output += tab(node) + key(node)
|
|
395
|
+
output += '\n'
|
|
396
|
+
return output
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: verysimpletree
|
|
3
|
+
Version: 0.1.0b0
|
|
4
|
+
Summary: lightweight tree data structure for musicxml and musicscore
|
|
5
|
+
Home-page: https://github.com/alexgorji/verysimpletree.git
|
|
6
|
+
Author: Alex Gorji
|
|
7
|
+
Author-email: aligorji@hotmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
An abstract lightweight tree class for managing tree structures in musicxml and musicscore packages.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
verysimpletree/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
verysimpletree/test_tree.py,sha256=RSp-Vxmfs0UoTO00a_l0f4u0YRqDcJlTbkbGOAfCht0,5587
|
|
3
|
+
verysimpletree/tree.py,sha256=Xpi-8fxuz3j4yW1dmvrP13BW_GYpYqYVtxthITkoS-8,12382
|
|
4
|
+
verysimpletree-0.1.0b0.dist-info/METADATA,sha256=aLkosPmNUPiEf7fZFrSUczzXGFYeAiQsp5jaePJmhmY,479
|
|
5
|
+
verysimpletree-0.1.0b0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
6
|
+
verysimpletree-0.1.0b0.dist-info/top_level.txt,sha256=zEghJjeoIBa8XOrU-H1FPmlyq1LkKWoxmVBEdhrzGtk,15
|
|
7
|
+
verysimpletree-0.1.0b0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
verysimpletree
|