From 7232a5b132975cbbdca682ddd787f1c91e0f4c3c Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Mon, 5 Oct 2026 22:06:59 +0000 Subject: [PATCH] Reject assigning a string to an attribute index AttrsList.__setitem__ with an integer index treated a string value as a (key, value) pair, so attrs[0] = "confirm" renamed the first attribute to "c" and set its value to "o". Raise TypeError instead, and describe the accepted combinations with overloads, so that type checkers report it too. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Anders Kaseorg --- python/markupever/dom.py | 18 +++++++++++++++++- python/tests/test_dom.py | 7 +++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/python/markupever/dom.py b/python/markupever/dom.py index 17ca309..e684c2d 100644 --- a/python/markupever/dom.py +++ b/python/markupever/dom.py @@ -852,6 +852,16 @@ def __delitem__(self, index: int | str | _rustlib.QualName) -> None: self.__raw.remove(index) + @typing.overload + def __setitem__(self, index: int, val: tuple[_rustlib.QualName | str, str]) -> None: ... + + @typing.overload + def __setitem__( + self, + index: str | _rustlib.QualName, + val: str | tuple[_rustlib.QualName | str, str], + ) -> None: ... + def __setitem__( self, index: int | str | _rustlib.QualName, @@ -865,7 +875,10 @@ def __setitem__( a new attribute is pushed. If the key exists, the attribute is updated. - index: An integer index or a string/QualName key to set. - - val: A value to set, either as a string or a (key, value) tuple. + - val: A value to set, either as a string or a (key, value) tuple. With an integer + index, it must be a (key, value) tuple. + + Raises `TypeError` if the index is an integer and the value is a string. """ if not isinstance(index, int): key, value = (index, val) if isinstance(val, str) else val @@ -877,6 +890,9 @@ def __setitem__( val = (key, value) + if isinstance(val, str): + raise TypeError("an attribute index must be assigned a (key, value) tuple, not a str") + self.__raw.update_item(index, val[0], val[1]) @typing.overload diff --git a/python/tests/test_dom.py b/python/tests/test_dom.py index c38725a..87b506c 100644 --- a/python/tests/test_dom.py +++ b/python/tests/test_dom.py @@ -406,6 +406,13 @@ def test_element(): html.attrs.insert(0, "onclick", "alert") assert html.attrs[0] == (markupever.dom.QualName("onclick"), "alert") + # An attribute index must be assigned a (key, value) tuple. + with pytest.raises(TypeError): + html.attrs[0] = "confirm" # type: ignore[call-overload] + assert html.attrs[0] == (markupever.dom.QualName("onclick"), "alert") + html.attrs[0] = ("onload", "init") + assert html.attrs[0] == (markupever.dom.QualName("onload"), "init") + html.attrs = [("id", "id1"), ("id", "id2"), ("data-role", "button")] assert html.id == "id1"