diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b5f504..f89a3dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ For the purpose of determining breaking changes:
- Update license metadata as per [PEP 639](https://peps.python.org/pep-0639)
- Add tests for PyPy 3.11
+- Upgrade dev dependencies
## [1.1.0] - 2024-10-10
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 8e11a56..42d2f91 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -2,17 +2,17 @@
-e .
# build
-build==1.2.2
+build==1.3.0
# docs
mkdocs==1.6.1
# lint
-ruff==0.6.9
+ruff==0.14.1
# tests
-pytest==8.3.3
-pytest-cov==5.0.0
+pytest==8.4.2
+pytest-cov==7.0.0
# type
-mypy==1.11.2
+mypy==1.18.2
diff --git a/pyproject.toml b/pyproject.toml
index d686093..189d680 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -84,7 +84,7 @@ ignore_missing_imports = false
follow_imports = "normal"
mypy_path = "stubs"
# Platform configuration
-python_version = 3.13
+python_version = "3.13"
# Disallow dynamic typing
disallow_any_unimported = true
disallow_any_decorated = true
@@ -143,8 +143,6 @@ target-version = "py39"
[tool.ruff.lint]
select = ["ALL"]
ignore = [
- "ANN101",
- "ANN102",
"C901",
"COM812",
"D",
diff --git a/src/bigxml/__init__.py b/src/bigxml/__init__.py
index e5f6690..0133604 100644
--- a/src/bigxml/__init__.py
+++ b/src/bigxml/__init__.py
@@ -9,9 +9,9 @@
"HandlerTypeHelper",
"Parser",
"Streamable",
- "xml_handle_element",
- "xml_handle_text",
"XMLElement",
"XMLElementAttributes",
"XMLText",
+ "xml_handle_element",
+ "xml_handle_text",
)
diff --git a/src/bigxml/handle_mgr.py b/src/bigxml/handle_mgr.py
index ff0ffee..81ddb36 100644
--- a/src/bigxml/handle_mgr.py
+++ b/src/bigxml/handle_mgr.py
@@ -100,7 +100,7 @@ def iter_from(
],
) -> Iterator[Union["XMLElement", T]]: ...
- @overload # type: ignore[misc]
+ @overload
def iter_from(
self,
*handlers: Any, # noqa: ANN401
diff --git a/src/bigxml/handler_creator.py b/src/bigxml/handler_creator.py
index 1b5b285..9e1bdc2 100644
--- a/src/bigxml/handler_creator.py
+++ b/src/bigxml/handler_creator.py
@@ -135,7 +135,7 @@ def handle(
return None
@staticmethod
- def _handle_from_class( # type: ignore[misc]
+ def _handle_from_class(
klass: type[Any], node: Union["XMLElement", "XMLText"]
) -> Optional[Iterable[object]]:
# instantiate class
diff --git a/src/bigxml/handler_marker.py b/src/bigxml/handler_marker.py
index 5f10811..da37f41 100644
--- a/src/bigxml/handler_marker.py
+++ b/src/bigxml/handler_marker.py
@@ -19,7 +19,7 @@
class ___xml_handle_xxx_wrapped(Protocol[T_co]): # noqa: N801
# wrapper for classes
@overload
- def __call__( # type: ignore[misc]
+ def __call__(
self,
obj: K,
) -> K: ...
@@ -49,22 +49,21 @@ def wrapper(obj: F) -> F:
if isinstance(markable, staticmethod):
# staticmethod(xml_handle_element(...)) works as expected
# xml_handle_element(staticmethod(...)) needs special care
- markable = cast(F, markable.__func__)
+ markable = cast("F", markable.__func__)
add_mark(markable, tuple(args))
return obj
return cast(
- ___xml_handle_xxx_wrapped[XMLElement],
+ "___xml_handle_xxx_wrapped[XMLElement]",
wrapper,
)
# @xml_handle_text (for classes)
@overload
-def xml_handle_text(obj: K, /) -> K: # type: ignore[misc]
- ...
+def xml_handle_text(obj: K, /) -> K: ...
# @xml_handle_text (for functions)
diff --git a/src/bigxml/stream.py b/src/bigxml/stream.py
index bc40a12..7584a47 100644
--- a/src/bigxml/stream.py
+++ b/src/bigxml/stream.py
@@ -19,7 +19,7 @@ def _flatten_stream(stream: Streamable) -> Generator[Optional[memoryview], int,
# buffer protocol (bytes, etc.)
try:
# we try-except instead of isinstance(stream, Buffer) for compatibility reasons
- yield memoryview(cast(Buffer, stream))
+ yield memoryview(cast("Buffer", stream))
return # noqa: TRY300
except TypeError:
pass
@@ -28,7 +28,7 @@ def _flatten_stream(stream: Streamable) -> Generator[Optional[memoryview], int,
if hasattr(stream, "read"):
while True:
size = yield None
- data = cast(SupportsRead[Any], stream).read(size)
+ data = cast("SupportsRead[Any]", stream).read(size)
if not data:
break # EOF
try:
@@ -57,7 +57,7 @@ def _flatten_stream(stream: Streamable) -> Generator[Optional[memoryview], int,
# stream iterator (recursive)
try:
- substreams = iter(cast(Iterable[Streamable], stream))
+ substreams = iter(cast("Iterable[Streamable]", stream))
except TypeError:
# other types not supported
raise TypeError(f"Invalid stream type: {type(stream).__name__}") from None
diff --git a/src/bigxml/utils.py b/src/bigxml/utils.py
index 0930820..0b1f3bc 100644
--- a/src/bigxml/utils.py
+++ b/src/bigxml/utils.py
@@ -40,7 +40,7 @@ def __next__(self) -> T:
def extract_namespace_name(name: str) -> tuple[str, str]:
match = _EXTRACT_NAMESPACE_REGEX.match(name)
if match:
- return cast(tuple[str, str], match.groups())
+ return cast("tuple[str, str]", match.groups())
return ("", name)
diff --git a/stubs/defusedxml/ElementTree.pyi b/stubs/defusedxml/ElementTree.pyi
index 45d7a93..d6b250e 100644
--- a/stubs/defusedxml/ElementTree.pyi
+++ b/stubs/defusedxml/ElementTree.pyi
@@ -1,7 +1,8 @@
# ruff: noqa: FBT001
# note: only used items are defined here, with used typing
-from typing import Iterator, Optional, Protocol, Sequence, TypeVar
+from collections.abc import Iterator, Sequence
+from typing import Optional, Protocol, TypeVar
from xml.etree.ElementTree import Element, ParseError
_T_co = TypeVar("_T_co", covariant=True)
@@ -19,4 +20,4 @@ def iterparse(
class DefusedXmlException(ValueError): ... # noqa: N818
-__all__ = ("DefusedXmlException", "Element", "iterparse", "ParseError")
+__all__ = ("DefusedXmlException", "Element", "ParseError", "iterparse")
diff --git a/tests/integration/test_namespaces.py b/tests/integration/test_namespaces.py
index a196807..139741c 100644
--- a/tests/integration/test_namespaces.py
+++ b/tests/integration/test_namespaces.py
@@ -71,7 +71,9 @@ def handle_bbb(node: XMLElement) -> Iterator[tuple[str, str, str]]:
)
# note that a warning is emitted if there are attributes with various
# namespaces but none without namespace
- with pytest.warns(UserWarning):
+ with pytest.warns(
+ UserWarning, match="Several alternatives for attribute name 'yyy'."
+ ):
# current implementation uses "first" attribute in that case
# but you should not rely on it and specify the namespace to use
yield ("bbb", "yyy default", node.attributes["yyy"])
diff --git a/tests/integration/test_ram_usage.py b/tests/integration/test_ram_usage.py
index 782b536..8239c72 100644
--- a/tests/integration/test_ram_usage.py
+++ b/tests/integration/test_ram_usage.py
@@ -9,7 +9,7 @@
@pytest.fixture
def ram_usage() -> Iterator[Callable[[], float]]:
try:
- import tracemalloc
+ import tracemalloc # noqa: PLC0415
except ImportError: # e.g. PyPy
pytest.skip("tracemalloc module not available")
diff --git a/tests/integration/test_security.py b/tests/integration/test_security.py
index e69c451..901d315 100644
--- a/tests/integration/test_security.py
+++ b/tests/integration/test_security.py
@@ -104,13 +104,11 @@ def test_external_entities(xml: bytes, msg: str) -> None:
def test_insecurely_allow_entities() -> None:
- xml = (
- b"\n'
- b"]>\n"
- b"Ω\n"
- )
- with pytest.warns(UserWarning):
+ xml = b']>\nΩ\n'
+ with pytest.warns(
+ UserWarning,
+ match="Using 'insecurely_allow_entities' makes your code vulnerable to some XML attacks.",
+ ):
parser = Parser(xml, insecurely_allow_entities=True)
value = parser.return_from(handler_get_text)
assert value == "Ω"
diff --git a/tests/unit/test_handler_creator.py b/tests/unit/test_handler_creator.py
index f6d6ff0..478e45f 100644
--- a/tests/unit/test_handler_creator.py
+++ b/tests/unit/test_handler_creator.py
@@ -25,7 +25,7 @@ def create_nodes(
for node_name in path:
# the cast below is wrong but makes our life easier
# plus that case is kind of tested in tests below
- parents = tuple(cast(list[XMLElement], nodes))
+ parents = tuple(cast("list[XMLElement]", nodes))
if node_name == ":text:":
node: Union[XMLElement, XMLText] = XMLText(text="text", parents=parents)
else:
@@ -631,7 +631,9 @@ def handle0(node: XMLElement) -> Iterable[tuple[str, XMLElement]]:
nodes = create_nodes("x", "a")
handler = create_handler(Handler)
- with pytest.warns(UserWarning):
+ with pytest.warns(
+ UserWarning, match="Items were yielded by some sub-handler of class Handler."
+ ):
items = list(handler(nodes[0]))
assert len(items) == 1
assert isinstance(items[0], Handler)
@@ -651,7 +653,9 @@ def xml_handler() -> Iterable[tuple[str, None]]:
nodes = create_nodes("x", "a")
handler = create_handler(Handler)
- with pytest.warns(UserWarning):
+ with pytest.warns(
+ UserWarning, match="Items were yielded by some sub-handler of class Handler."
+ ):
assert list(handler(nodes[0])) == [("end", None)]
diff --git a/tests/unit/test_nodes_element_attributes.py b/tests/unit/test_nodes_element_attributes.py
index 6e1b9dd..9e20c82 100644
--- a/tests/unit/test_nodes_element_attributes.py
+++ b/tests/unit/test_nodes_element_attributes.py
@@ -51,7 +51,9 @@
def test_get_without_namespace(key: str, value: str, should_warn: bool) -> None:
context: AbstractContextManager[object] = nullcontext()
if should_warn:
- context = pytest.warns(UserWarning)
+ context = pytest.warns(
+ UserWarning, match=f"Several alternatives for attribute name '{key}'."
+ )
with context:
assert XML_ELEMENT_ATTRIBUTES[key] == value
@@ -89,5 +91,5 @@ def test_eq() -> None:
@pytest.mark.parametrize("key", [r"{aaa", r"{aaa}{bbb"])
def test_invalid_key(key: str) -> None:
- with pytest.raises(ValueError, match="Invalid key: '.*'"):
+ with pytest.raises(ValueError, match=r"Invalid key: '.*'"):
XMLElementAttributes({key: "foo"})
diff --git a/tests/unit/test_parser.py b/tests/unit/test_parser.py
index 11141bc..e62aebf 100644
--- a/tests/unit/test_parser.py
+++ b/tests/unit/test_parser.py
@@ -198,7 +198,10 @@ def root_handler(
) -> Iterator[tuple[str, Union[XMLElement, XMLText]]]:
yield from node.iter_from(handler)
- with pytest.warns(UserWarning):
+ with pytest.warns(
+ UserWarning,
+ match="Using 'insecurely_allow_entities' makes your code vulnerable to some XML attacks.",
+ ):
parser = Parser(xml, insecurely_allow_entities=True)
assert list(parser.iter_from(root_handler)) == [("handler-yield-0", text_pi_node)]
diff --git a/tests/unit/test_stream.py b/tests/unit/test_stream.py
index 39c164d..cf2a34e 100644
--- a/tests/unit/test_stream.py
+++ b/tests/unit/test_stream.py
@@ -119,7 +119,7 @@ def __repr__() -> str:
],
)
def test_types_invalid(stream: object, err_message: str) -> None:
- stream = StreamChain(cast(Streamable, stream))
+ stream = StreamChain(cast("Streamable", stream))
with pytest.raises(TypeError) as excinfo:
stream.read(42)
diff --git a/tests/unit/test_utils_get_mandatory_params.py b/tests/unit/test_utils_get_mandatory_params.py
index b4b683d..0627d5e 100644
--- a/tests/unit/test_utils_get_mandatory_params.py
+++ b/tests/unit/test_utils_get_mandatory_params.py
@@ -1,5 +1,3 @@
-# ruff: noqa: ARG001
-
from typing import Callable
import pytest
@@ -57,9 +55,9 @@ def fct6(
(dict, ()),
],
ids=lambda x: str(x.__name__ if callable(x) else x),
-) # type: ignore[misc]
+)
# Typing note: see https://github.com/python/mypy/issues/13436
-def test_mandatory_params(
+def test_mandatory_params( # type: ignore[misc]
fct: Callable[..., object], expected: tuple[str, ...]
) -> None:
assert get_mandatory_params(fct) == expected
diff --git a/tox.ini b/tox.ini
index 0c3117f..20995d9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,8 +9,8 @@ envlist =
package = wheel
wheel_build_env = .pkg # reuse same wheel across envs
deps =
- pytest==8.3.3
- pytest-cov==5.0.0
+ pytest==8.4.2
+ pytest-cov==7.0.0
passenv = PY_COLORS
setenv =
COVERAGE_FILE = {toxworkdir}/{envname}/.coverage
@@ -22,7 +22,7 @@ commands =
[testenv:build]
skip_install = true
deps =
- build==1.2.2
+ build==1.3.0
commands =
python -m build
@@ -35,15 +35,15 @@ commands =
[testenv:lint]
deps =
- ruff==0.6.9
+ ruff==0.14.1
commands =
ruff check src docs tests
ruff format --check src docs tests
[testenv:type]
deps =
- mypy==1.11.2
- pytest==8.3.3 # for typing
+ mypy==1.18.2
+ pytest==8.4.2 # for typing
commands =
mypy
mypy --explicit-package-bases docs tests