1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
Read-only Tree
==============
**Application**: A read-only tree data structure, which denies modifications.
The `Node._pre_attach` and `Node._pre_detach` hookups can be used
for blocking tree modifications.
If they raise an `Exception`, the tree is not modified.
>>> from anytree import NodeMixin, RenderTree
The exception:
>>> class ReadOnlyError(RuntimeError):
... pass
Permanent
---------
The read-only attribute needs to be set after attaching to parent:
>>> class ReadOnlyNode(NodeMixin):
...
... def __init__(self, foo, parent=None):
... super(ReadOnlyNode, self).__init__()
... self.foo = foo
... self.__readonly = False
... self.parent = parent
... self.__readonly = True
...
... def _pre_attach(self, parent):
... if self.__readonly:
... raise ReadOnlyError()
...
... def _pre_detach(self, parent):
... raise ReadOnlyError()
An example tree:
>>> a = ReadOnlyNode("a")
>>> a0 = ReadOnlyNode("a0", parent=a)
>>> a1 = ReadOnlyNode("a1", parent=a)
>>> a1a = ReadOnlyNode("a1a", parent=a1)
>>> a2 = ReadOnlyNode("a2", parent=a)
>>> print(RenderTree(a).by_attr("foo"))
a
├── a0
├── a1
│ └── a1a
└── a2
Modifications raise an `ReadOnlyError`
>>> a0.parent = a2
Traceback (most recent call last):
...
ReadOnlyError
>>> a.children = [a1]
Traceback (most recent call last):
...
ReadOnlyError
The tree structure is untouched:
>>> print(RenderTree(a).by_attr("foo"))
a
├── a0
├── a1
│ └── a1a
└── a2
.. note::
It is important to use the ``_pre_*`` and **not** the ``_post_*`` methods.
An exception raised by `_pre_detach(parent)` and `_pre_attach(parent)` will **prevent** the tree structure to be updated.
The node keeps the old state.
An exception raised by `_post_detach(parent)` and `_post_attach(parent)` does **not rollback** the tree structure modification.
Temporary
---------
To select the read-only mode temporarily, the root node should provide
an attribute for all child nodes, set *after* construction.
>>> class ReadOnlyNode(NodeMixin):
... def __init__(self, foo, parent=None):
... super(ReadOnlyNode, self).__init__()
... self.readonly = False
... self.foo = foo
... self.parent = parent
... def _pre_attach(self, parent):
... if self.root.readonly:
... raise ReadOnlyError()
... def _pre_detach(self, parent):
... if self.root.readonly:
... raise ReadOnlyError()
An example tree:
>>> a = ReadOnlyNode("a")
>>> a0 = ReadOnlyNode("a0", parent=a)
>>> a1 = ReadOnlyNode("a1", parent=a)
>>> a1a = ReadOnlyNode("a1a", parent=a1)
>>> a2 = ReadOnlyNode("a2", parent=a)
>>> print(RenderTree(a).by_attr("foo"))
a
├── a0
├── a1
│ └── a1a
└── a2
Switch to read-only mode:
>>> a.readonly = True
>>> a0.parent = a2
Traceback (most recent call last):
...
ReadOnlyError
>>> a.children = [a1]
Traceback (most recent call last):
...
ReadOnlyError
Disable read-only mode:
>>> a.readonly = False
Modifications are allowed now:
>>> a0.parent = a2
>>> print(RenderTree(a).by_attr("foo"))
a
├── a1
│ └── a1a
└── a2
└── a0
|