Skip to content

Commit cd96906

Browse files
committed
chore: drop support for Python 3.9
1 parent 7cc96fb commit cd96906

23 files changed

+197
-240
lines changed

.github/workflows/build.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ jobs:
1818
strategy:
1919
matrix:
2020
python:
21-
- "3.9"
2221
- "3.10"
2322
- "3.11"
2423
- "3.12"
2524
- "3.13"
2625
- "3.14"
27-
- "pypy-3.9"
2826
- "pypy-3.10"
2927
- "pypy-3.11"
3028
steps:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ For the purpose of determining breaking changes:
2121
### :house: Internal
2222

2323
- Update license metadata as per [PEP 639](https://peps.python.org/pep-0639)
24+
- End of Python 3.9 support
2425
- Add tests for CPython 3.14 and PyPy 3.11
2526
- Use CPython 3.14 for misc. tests
2627
- Upgrade dev dependencies

pyproject.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ classifiers = [
1313
"Programming Language :: Python",
1414
"Programming Language :: Python :: 3",
1515
"Programming Language :: Python :: 3 :: Only",
16-
"Programming Language :: Python :: 3.9",
1716
"Programming Language :: Python :: 3.10",
1817
"Programming Language :: Python :: 3.11",
1918
"Programming Language :: Python :: 3.12",
@@ -22,7 +21,7 @@ classifiers = [
2221
"Topic :: Utilities",
2322
"Topic :: Text Processing :: Markup :: XML",
2423
]
25-
requires-python = ">=3.9"
24+
requires-python = ">=3.10"
2625
dependencies = [
2726
"defusedxml>=0.7.1",
2827
"typing-extensions>=4.6.0 ; python_version<'3.12'",
@@ -139,7 +138,7 @@ testpaths = ["docs", "tests"]
139138

140139
[tool.ruff]
141140
src = ["src"]
142-
target-version = "py39"
141+
target-version = "py310"
143142

144143
[tool.ruff.lint]
145144
select = ["ALL"]

src/bigxml/handle_mgr.py

Lines changed: 70 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from collections.abc import Iterable, Iterator
1+
from collections.abc import Callable, Iterable, Iterator
22
import sys
3-
from typing import TYPE_CHECKING, Any, Callable, Optional, Union, overload
3+
from typing import TYPE_CHECKING, Any, Optional, Union, overload
44

55
from bigxml.handler_creator import create_handler
66
from bigxml.typing import (
@@ -20,12 +20,12 @@
2020

2121

2222
class HandleMgr:
23-
_handle: Optional[
23+
_handle: (
2424
Callable[
25-
[Callable[[Union["XMLElement", "XMLText"]], Iterator[Any]]],
26-
Iterator[Any],
25+
[Callable[[Union["XMLElement", "XMLText"]], Iterator[Any]]], Iterator[Any]
2726
]
28-
] = None
27+
| None
28+
) = None
2929

3030
# iter_from
3131

@@ -37,67 +37,55 @@ def iter_from(
3737
@overload
3838
def iter_from(
3939
self,
40-
*handlers: Union[
41-
str,
42-
list[str],
43-
tuple[str, ...],
44-
],
40+
*handlers: str | list[str] | tuple[str, ...],
4541
) -> Iterator["XMLElement"]: ...
4642

4743
@overload
4844
def iter_from(
4945
self,
50-
*handlers: Union[
51-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
52-
ClassHandlerWithCustomWrapper0[T],
53-
ClassHandlerWithCustomWrapper1[T],
54-
type[ClassHandlerWithCustomWrapper0[T]],
55-
type[ClassHandlerWithCustomWrapper1[T]],
56-
],
46+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
47+
| ClassHandlerWithCustomWrapper0[T]
48+
| ClassHandlerWithCustomWrapper1[T]
49+
| type[ClassHandlerWithCustomWrapper0[T]]
50+
| type[ClassHandlerWithCustomWrapper1[T]],
5751
) -> Iterator[T]: ...
5852

5953
@overload
6054
def iter_from(
6155
self,
62-
*handlers: Union[
63-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
64-
ClassHandlerWithCustomWrapper0[T],
65-
ClassHandlerWithCustomWrapper1[T],
66-
type[ClassHandlerWithCustomWrapper0[T]],
67-
type[ClassHandlerWithCustomWrapper1[T]],
68-
type[T],
69-
],
56+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
57+
| ClassHandlerWithCustomWrapper0[T]
58+
| ClassHandlerWithCustomWrapper1[T]
59+
| type[ClassHandlerWithCustomWrapper0[T]]
60+
| type[ClassHandlerWithCustomWrapper1[T]]
61+
| type[T],
7062
) -> Iterator[T]: ...
7163

7264
@overload
7365
def iter_from(
7466
self,
75-
*handlers: Union[
76-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
77-
ClassHandlerWithCustomWrapper0[T],
78-
ClassHandlerWithCustomWrapper1[T],
79-
type[ClassHandlerWithCustomWrapper0[T]],
80-
type[ClassHandlerWithCustomWrapper1[T]],
81-
str,
82-
list[str],
83-
tuple[str, ...],
84-
],
67+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
68+
| ClassHandlerWithCustomWrapper0[T]
69+
| ClassHandlerWithCustomWrapper1[T]
70+
| type[ClassHandlerWithCustomWrapper0[T]]
71+
| type[ClassHandlerWithCustomWrapper1[T]]
72+
| str
73+
| list[str]
74+
| tuple[str, ...],
8575
) -> Iterator[Union["XMLElement", T]]: ...
8676

8777
@overload
8878
def iter_from(
8979
self,
90-
*handlers: Union[
91-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
92-
ClassHandlerWithCustomWrapper0[T],
93-
ClassHandlerWithCustomWrapper1[T],
94-
type[ClassHandlerWithCustomWrapper0[T]],
95-
type[ClassHandlerWithCustomWrapper1[T]],
96-
type[T],
97-
str,
98-
list[str],
99-
tuple[str, ...],
100-
],
80+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
81+
| ClassHandlerWithCustomWrapper0[T]
82+
| ClassHandlerWithCustomWrapper1[T]
83+
| type[ClassHandlerWithCustomWrapper0[T]]
84+
| type[ClassHandlerWithCustomWrapper1[T]]
85+
| type[T]
86+
| str
87+
| list[str]
88+
| tuple[str, ...],
10189
) -> Iterator[Union["XMLElement", T]]: ...
10290

10391
@overload
@@ -122,74 +110,62 @@ def return_from(
122110
@overload
123111
def return_from(
124112
self,
125-
*handlers: Union[
126-
str,
127-
list[str],
128-
tuple[str, ...],
129-
],
113+
*handlers: str | list[str] | tuple[str, ...],
130114
) -> Optional["XMLElement"]: ...
131115

132116
@overload
133117
def return_from(
134118
self,
135-
*handlers: Union[
136-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
137-
ClassHandlerWithCustomWrapper0[T],
138-
ClassHandlerWithCustomWrapper1[T],
139-
type[ClassHandlerWithCustomWrapper0[T]],
140-
type[ClassHandlerWithCustomWrapper1[T]],
141-
],
142-
) -> Optional[T]: ...
119+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
120+
| ClassHandlerWithCustomWrapper0[T]
121+
| ClassHandlerWithCustomWrapper1[T]
122+
| type[ClassHandlerWithCustomWrapper0[T]]
123+
| type[ClassHandlerWithCustomWrapper1[T]],
124+
) -> T | None: ...
143125

144126
@overload
145127
def return_from(
146128
self,
147-
*handlers: Union[
148-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
149-
ClassHandlerWithCustomWrapper0[T],
150-
ClassHandlerWithCustomWrapper1[T],
151-
type[ClassHandlerWithCustomWrapper0[T]],
152-
type[ClassHandlerWithCustomWrapper1[T]],
153-
type[T],
154-
],
155-
) -> Optional[T]: ...
129+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
130+
| ClassHandlerWithCustomWrapper0[T]
131+
| ClassHandlerWithCustomWrapper1[T]
132+
| type[ClassHandlerWithCustomWrapper0[T]]
133+
| type[ClassHandlerWithCustomWrapper1[T]]
134+
| type[T],
135+
) -> T | None: ...
156136

157137
@overload
158138
def return_from(
159139
self,
160-
*handlers: Union[
161-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
162-
ClassHandlerWithCustomWrapper0[T],
163-
ClassHandlerWithCustomWrapper1[T],
164-
type[ClassHandlerWithCustomWrapper0[T]],
165-
type[ClassHandlerWithCustomWrapper1[T]],
166-
str,
167-
list[str],
168-
tuple[str, ...],
169-
],
170-
) -> Optional[Union["XMLElement", T]]: ...
140+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
141+
| ClassHandlerWithCustomWrapper0[T]
142+
| ClassHandlerWithCustomWrapper1[T]
143+
| type[ClassHandlerWithCustomWrapper0[T]]
144+
| type[ClassHandlerWithCustomWrapper1[T]]
145+
| str
146+
| list[str]
147+
| tuple[str, ...],
148+
) -> Union["XMLElement", T] | None: ...
171149

172150
@overload
173151
def return_from(
174152
self,
175-
*handlers: Union[
176-
Callable[[Union["XMLElement", "XMLText"]], Optional[Iterable[T]]],
177-
ClassHandlerWithCustomWrapper0[T],
178-
ClassHandlerWithCustomWrapper1[T],
179-
type[ClassHandlerWithCustomWrapper0[T]],
180-
type[ClassHandlerWithCustomWrapper1[T]],
181-
str,
182-
list[str],
183-
tuple[str, ...],
184-
type[T],
185-
],
186-
) -> Optional[Union["XMLElement", T]]: ...
153+
*handlers: Callable[[Union["XMLElement", "XMLText"]], Iterable[T] | None]
154+
| ClassHandlerWithCustomWrapper0[T]
155+
| ClassHandlerWithCustomWrapper1[T]
156+
| type[ClassHandlerWithCustomWrapper0[T]]
157+
| type[ClassHandlerWithCustomWrapper1[T]]
158+
| str
159+
| list[str]
160+
| tuple[str, ...]
161+
| type[T],
162+
) -> Union["XMLElement", T] | None: ...
187163

188164
@overload
189165
def return_from(
190166
self,
191167
*handlers: object,
192-
) -> Optional[object]: ...
168+
) -> object | None: ...
193169

194-
def return_from(self, *handlers: Any) -> Optional[Any]:
170+
def return_from(self, *handlers: Any) -> Any | None:
195171
return last_item_or_none(self.iter_from(*handlers))

src/bigxml/handler_creator.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from collections.abc import Iterable, Iterator
1+
from collections.abc import Callable, Iterable, Iterator
22
from dataclasses import is_dataclass
33
from inspect import getmembers, isclass
4-
from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast
4+
from typing import TYPE_CHECKING, Any, Union, cast
55
import warnings
66

77
from bigxml.marks import get_marks, has_marks
@@ -27,7 +27,7 @@ def _assert_one_mandatory_param(
2727

2828
def _assert_iterable_or_none(
2929
item: object, klass: type[Any], method_name: str
30-
) -> Optional[Iterable[object]]:
30+
) -> Iterable[object] | None:
3131
if item is None or isinstance(item, Iterable):
3232
return item
3333
raise TypeError(
@@ -44,7 +44,7 @@ class _HandlerTree:
4444
def __init__(self, path: tuple[str, ...] = ()) -> None:
4545
self.path: tuple[str, ...] = path
4646
self.children: dict[str, _HandlerTree] = {}
47-
self.handler: Optional[Callable[..., Iterable[object]]] = None
47+
self.handler: Callable[..., Iterable[object]] | None = None
4848

4949
def add_handler(
5050
self,
@@ -111,15 +111,13 @@ def add_handler_callable(
111111
self.handler = handler
112112

113113
@transform_to_iterator
114-
def handle(
115-
self, node: Union["XMLElement", "XMLText"]
116-
) -> Optional[Iterable[object]]:
114+
def handle(self, node: Union["XMLElement", "XMLText"]) -> Iterable[object] | None:
117115
if self.handler:
118116
if isclass(self.handler):
119117
return self._handle_from_class(self.handler, node)
120118
return self.handler(node)
121119

122-
child: Optional[_HandlerTree] = None
120+
child: _HandlerTree | None = None
123121
namespace = getattr(node, "namespace", None)
124122
if namespace is not None:
125123
child = self.children.get(f"{{{namespace}}}{node.name}")
@@ -137,7 +135,7 @@ def handle(
137135
@staticmethod
138136
def _handle_from_class(
139137
klass: type[Any], node: Union["XMLElement", "XMLText"]
140-
) -> Optional[Iterable[object]]:
138+
) -> Iterable[object] | None:
141139
# instantiate class
142140
init_mandatory_params = get_mandatory_params(klass)
143141
try:

src/bigxml/handler_marker.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from collections.abc import Iterable
2-
from typing import Any, Callable, Generic, Optional, Union, cast, overload
1+
from collections.abc import Callable, Iterable
2+
from typing import Any, Generic, cast, overload
33

44
from bigxml.marks import add_mark
55
from bigxml.nodes import XMLElement, XMLText
@@ -28,15 +28,15 @@ def __call__(
2828
@overload
2929
def __call__(
3030
self,
31-
obj: Callable[[T_co], Optional[Iterable[T]]],
32-
) -> Callable[[Union[XMLElement, XMLText]], Optional[Iterable[T]]]: ...
31+
obj: Callable[[T_co], Iterable[T] | None],
32+
) -> Callable[[XMLElement | XMLText], Iterable[T] | None]: ...
3333

3434
# wrapper for methods
3535
@overload
3636
def __call__(
3737
self,
38-
obj: Callable[[U, T_co], Optional[Iterable[T]]],
39-
) -> Callable[[U, Union[XMLElement, XMLText]], Optional[Iterable[T]]]: ...
38+
obj: Callable[[U, T_co], Iterable[T] | None],
39+
) -> Callable[[U, XMLElement | XMLText], Iterable[T] | None]: ...
4040

4141

4242
def xml_handle_element(*args: str) -> ___xml_handle_xxx_wrapped[XMLElement]:
@@ -69,15 +69,15 @@ def xml_handle_text(obj: K, /) -> K: ...
6969
# @xml_handle_text (for functions)
7070
@overload
7171
def xml_handle_text(
72-
obj: Callable[[XMLText], Optional[Iterable[T]]], /
73-
) -> Callable[[Union[XMLElement, XMLText]], Optional[Iterable[T]]]: ...
72+
obj: Callable[[XMLText], Iterable[T] | None], /
73+
) -> Callable[[XMLElement | XMLText], Iterable[T] | None]: ...
7474

7575

7676
# @xml_handle_text (for methods)
7777
@overload
7878
def xml_handle_text(
79-
obj: Callable[[U, XMLText], Optional[Iterable[T]]], /
80-
) -> Callable[[U, Union[XMLElement, XMLText]], Optional[Iterable[T]]]: ...
79+
obj: Callable[[U, XMLText], Iterable[T] | None], /
80+
) -> Callable[[U, XMLElement | XMLText], Iterable[T] | None]: ...
8181

8282

8383
# @xml_handle_text(...) (for functions & methods)

0 commit comments

Comments
 (0)