Skip to content

Commit

Permalink
Implements TagNode.__delitem__ and .__setitem__
Browse files Browse the repository at this point in the history
They complement .__getitem__ for the same convenience.
  • Loading branch information
funkyfuture committed Nov 11, 2024
1 parent 48dc58d commit d45e3f7
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 4 deletions.
47 changes: 44 additions & 3 deletions _delb/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1695,10 +1695,9 @@ class TagNode(_ElementWrappingNode, NodeBase):
Attributes and nodes can be tested for membership in a node.
>>> root = Document('<root ham="spam"><child/></root>').root
>>> child = root.first_child
>>> "ham" in root
True
>>> child in root
>>> root.first_child in root
True
Nodes can be copied. Note that this relies on :meth:`TagNode.clone`.
Expand All @@ -1718,7 +1717,8 @@ class TagNode(_ElementWrappingNode, NodeBase):
>>> root[0] == root[2]
False
Attribute values and child nodes can be obtained with the subscript notation.
Attribute values and child nodes can be obtained, set and deleted with the subscript
notation.
>>> root = Document('<root x="y"><child_1/>child_2<child_3/></root>').root
>>> print(root["x"])
Expand Down Expand Up @@ -1765,6 +1765,24 @@ def __contains__(self, item: AttributeAccessor | NodeBase) -> bool:
+ ATTRIBUTE_ACCESSOR_MSG
)

def __delitem__(self, item: AttributeAccessor | int):
if isinstance(item, (str, tuple)):
del self.attributes[item]
elif isinstance(item, int):
self[item].detach(retain_child_nodes=False)
elif isinstance(item, slice):
if isinstance(item.start, int) or isinstance(item.stop, int):
raise NotImplementedError(
"This will be implemented in a later version."
)
else:
del self.attributes[(item.start, item.stop)]
else:
raise TypeError(
"Argument must be an integer or an attribute name. "
+ ATTRIBUTE_ACCESSOR_MSG
)

def __eq__(self, other: Any) -> bool:
if not isinstance(other, TagNode):
return False
Expand Down Expand Up @@ -1819,6 +1837,29 @@ def __repr__(self) -> str:
f"{self.attributes}, {self.location_path}) [{hex(id(self))}]>"
)

@overload
def __setitem__(self, item: int, value: NodeSource): ...

@overload
def __setitem__(self, item: AttributeAccessor, value: str | Attribute): ...

def __setitem__(self, item, value):
if isinstance(item, (slice, str, tuple)):
self.attributes[item] = value
elif isinstance(item, int):
children_size = len(self)
if not children_size and item == 0:
self.__add_first_child(value)
else:
if not 0 <= item < children_size:
raise IndexError
self[item].replace_with(value)
else:
raise TypeError(
"Argument must be an integer or an attribute name. "
+ ATTRIBUTE_ACCESSOR_MSG
)

def __add_first_child(self, node: NodeBase):
assert not len(self)
if isinstance(node, _ElementWrappingNode):
Expand Down
38 changes: 37 additions & 1 deletion tests/test_tag_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ def test_deepcopy():
assert clone[0] is not node[0]


def test_delitem():
namespace = "https://foo"
root = Document(f"<root xmlns='{namespace}' A='' B='' C=''><a/></root>").root
del root["A"]
del root[namespace:"B"]
del root[(namespace, "C")]
del root[0]
assert str(root) == f'<root xmlns="{namespace}"/>'
with pytest.raises(IndexError):
del root[0]
with pytest.raises(KeyError):
del root["A"]


def test_depth():
document = Document("<root><a><b/></a></root>")

Expand Down Expand Up @@ -267,10 +281,14 @@ def test_full_text():


def test_getitem():
document = Document('<root ham="spam"><a/><b/><c/><d/></root>')
namespace = "http://foo"
document = Document(f'<root xmlns="{namespace}" ham="spam"><a/><b/><c/><d/></root>')
root = document.root

assert root["ham"] == "spam"
assert root[namespace:"ham"] == "spam"
assert root[(namespace, "ham")] == "spam"

assert root["foo"] is None

assert root[-1].local_name == "d"
Expand All @@ -281,6 +299,9 @@ def test_getitem():

assert "".join(x.local_name for x in root[::-1]) == "dcba"

with pytest.raises(IndexError):
root[4]


def test_id_property(files_path):
document = Document(files_path / "marx_manifestws_1848.TEI-P5.xml")
Expand Down Expand Up @@ -646,6 +667,21 @@ def test_set_tag_components():
assert str(root) == '<prfx:root xmlns:prfx="https://name.space"/>'


def test_set_item():
namespace = "http://foo"
root = Document("<root/>").root
root[0] = tag("b")
root[0] = tag("a")
root["A"] = "1"
root[namespace:"B"] = "2"
root[(namespace, "C")] = "3"
assert str(root) == '<root xmlns:ns0="http://foo" A="1" ns0:B="2" ns0:C="3"/>'
with pytest.raises(IndexError):
root[-1] = "text"
with pytest.raises(IndexError):
root[1] = "text"


def test_tag_definition_copies_attributes():
root = Document('<root foo="bar"/>').root
definition = tag("test", root.attributes)
Expand Down

0 comments on commit d45e3f7

Please sign in to comment.