Skip to content

Commit a7a39fe

Browse files
committed
Drop Python 3.8 and 3.9 support, add Python 3.13
- Bump version to 1.5.0.dev - Update requires-python from >=3.8 to >=3.10 - Update classifiers in pyproject.toml - Update tox envlist and gh-actions configuration - Update GitHub Actions workflow matrix - Update pyupgrade to use --py310-plus - Remove unused typing imports (Dict, List, Optional, Tuple) now that Python 3.10+ built-in generics are available - Update changelog with version 1.5.0 release notes
1 parent 97f4938 commit a7a39fe

9 files changed

Lines changed: 38 additions & 42 deletions

File tree

.github/workflows/main.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919

2020
strategy:
2121
matrix:
22-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
22+
python-version: ["3.10", "3.11", "3.12", "3.13"]
2323

2424
# Steps represent a sequence of tasks that will be executed as part of the job
2525
steps:
@@ -39,8 +39,8 @@ jobs:
3939
run: "python -m tox"
4040

4141
- name: "Report to coveralls"
42-
# coverage is only created in the py39 environment
43-
if: "matrix.python-version == '3.9'"
42+
# coverage is only created in the py311 environment
43+
if: "matrix.python-version == '3.12'"
4444
run: |
4545
pip install coveralls
4646
coveralls --service=github

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ repos:
1919
rev: v3.19.0
2020
hooks:
2121
- id: pyupgrade
22-
args: [--py36-plus]
22+
args: [--py310-plus]
2323

2424
- repo: https://github.com/mgedmin/check-python-versions
2525
rev: 0.22.1

CHANGES.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
=========
33

4+
1.5.0 (unreleased)
5+
------------------
6+
- drop support for Python 3.8 and 3.9
7+
- add support for Python 3.13
8+
49
1.4.1 (unreleased)
510
------------------
611
- migrate from setup.py to pyproject.toml configuration

pyproject.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "Flask-Reuploaded"
7-
version = "1.4.1.dev"
7+
version = "1.5.0.dev"
88
description = "Flexible and efficient upload handling for Flask"
99
license = {text = "MIT"}
1010
authors = [
@@ -13,7 +13,7 @@ authors = [
1313
maintainers = [
1414
{name = "Jürgen Gmach", email = "juergen.gmach@googlemail.com"},
1515
]
16-
requires-python = ">= 3.8"
16+
requires-python = ">= 3.10"
1717
dynamic = ["readme"]
1818
classifiers = [
1919
"Development Status :: 5 - Production/Stable",
@@ -22,11 +22,10 @@ classifiers = [
2222
"Operating System :: OS Independent",
2323
"Programming Language :: Python",
2424
"Programming Language :: Python :: 3",
25-
"Programming Language :: Python :: 3.8",
26-
"Programming Language :: Python :: 3.9",
2725
"Programming Language :: Python :: 3.10",
2826
"Programming Language :: Python :: 3.11",
2927
"Programming Language :: Python :: 3.12",
28+
"Programming Language :: Python :: 3.13",
3029
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
3130
"Topic :: Software Development :: Libraries :: Python Modules",
3231
"Framework :: Flask",

src/flask_uploads/extensions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Extension presets and extension configuration."""
22
import os
3-
from typing import Iterable
3+
from collections.abc import Iterable
44
from typing import cast
55

66
# This contains archive and compression formats (.gz, .bz2, .zip, .tar,

src/flask_uploads/flask_uploads.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,9 @@
1313
import os
1414
import os.path
1515
import posixpath
16+
from collections.abc import Callable
17+
from collections.abc import Iterable
1618
from typing import Any
17-
from typing import Callable
18-
from typing import Dict
19-
from typing import Iterable
20-
from typing import Optional
21-
from typing import Tuple
2219
from typing import Union
2320

2421
from flask import Blueprint
@@ -45,7 +42,7 @@ def addslash(url: str) -> str:
4542
def config_for_set(
4643
uset: 'UploadSet',
4744
app: Flask,
48-
defaults: Optional[Dict[str, Optional[str]]] = None
45+
defaults: dict[str, str | None] | None = None
4946
) -> 'UploadConfiguration':
5047
"""
5148
This is a helper function for `configure_uploads` that extracts the
@@ -146,20 +143,20 @@ class UploadConfiguration:
146143
def __init__(
147144
self,
148145
destination: str,
149-
base_url: Optional[str] = None,
150-
allow: Union[Tuple[()], Tuple[str, ...]] = (),
151-
deny: Union[Tuple[()], Tuple[str, ...]] = ()
146+
base_url: str | None = None,
147+
allow: tuple[()] | tuple[str, ...] = (),
148+
deny: tuple[()] | tuple[str, ...] = ()
152149
) -> None:
153150
self.destination = destination
154151
self.base_url = base_url
155152
self.allow = allow
156153
self.deny = deny
157154

158155
@property
159-
def tuple(self) -> Tuple[
160-
str, Optional[str],
161-
Union[Tuple[()], Tuple[str, ...]],
162-
Union[Tuple[()], Tuple[str, ...]]
156+
def tuple(self) -> tuple[
157+
str, str | None,
158+
tuple[()] | tuple[str, ...],
159+
tuple[()] | tuple[str, ...]
163160
]:
164161
return (self.destination, self.base_url, self.allow, self.deny)
165162

@@ -193,13 +190,13 @@ def __init__(
193190
self,
194191
name: str = 'files',
195192
extensions: Iterable[str] = DEFAULTS,
196-
default_dest: Optional[Callable[[Flask], str]] = None
193+
default_dest: Callable[[Flask], str] | None = None
197194
) -> None:
198195
if not name.isalnum():
199196
raise ValueError("Name must be alphanumeric (no underscores)")
200197
self.name = name
201198
self.extensions = extensions
202-
self._config: Optional[UploadConfiguration] = None
199+
self._config: UploadConfiguration | None = None
203200
self.default_dest = default_dest
204201

205202
@property
@@ -238,7 +235,7 @@ def url(self, filename: str) -> str:
238235
else:
239236
return base + filename
240237

241-
def path(self, filename: str, folder: Optional[str] = None) -> str:
238+
def path(self, filename: str, folder: str | None = None) -> str:
242239
"""
243240
This returns the absolute path of a file uploaded to this set. It
244241
doesn't actually check whether said file exists.
@@ -288,8 +285,8 @@ def get_basename(self, filename: str) -> str:
288285
def save(
289286
self,
290287
storage: FileStorage,
291-
folder: Optional[str] = None,
292-
name: Optional[str] = None
288+
folder: str | None = None,
289+
name: str | None = None
293290
) -> str:
294291
"""This saves the `storage` into this upload set.
295292

src/flask_uploads/test_helper.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"""
88
from typing import Any
99
from typing import BinaryIO
10-
from typing import Optional
1110

1211
from werkzeug.datastructures import FileStorage
1312

@@ -32,12 +31,12 @@ class TestingFileStorage(FileStorage):
3231
"""
3332
def __init__(
3433
self,
35-
stream: Optional[BinaryIO] = None,
36-
filename: Optional[str] = None,
37-
name: Optional[str] = None,
34+
stream: BinaryIO | None = None,
35+
filename: str | None = None,
36+
name: str | None = None,
3837
content_type: str = 'application/octet-stream',
3938
content_length: int = -1,
40-
headers: Optional[Any] = None
39+
headers: Any | None = None
4140
) -> None:
4241
super().__init__(
4342
stream,

tests/test_flask_reuploaded.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
import os
88
import os.path
9-
from typing import Dict
10-
from typing import List
119
from unittest.mock import Mock
1210
from unittest.mock import patch
1311

@@ -82,7 +80,7 @@ def teardown_method(self) -> None:
8280

8381
def configure(
8482
self, *sets: 'UploadSet', **options: str
85-
) -> Dict[str, UploadConfiguration]:
83+
) -> dict[str, UploadConfiguration]:
8684
self.app.config.update(options)
8785
configure_uploads(self.app, sets)
8886
return self.app.upload_set_config # type: ignore
@@ -306,7 +304,7 @@ def test_file_not_allowed(self) -> None:
306304
@patch("os.makedirs", Mock(return_value=None))
307305
class TestConflictResolution:
308306
def setup_method(self) -> None:
309-
self.extant_files: List[str] = []
307+
self.extant_files: list[str] = []
310308
self.old_exists = os.path.exists
311309
os.path.exists = self.exists # type: ignore
312310

tox.ini

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
[tox]
22
envlist =
3-
py38,
4-
py39,
53
py310,
64
py311,
75
py312,
6+
py313,
87
lint,
98
mypy,
109
coverage,
@@ -56,11 +55,10 @@ python_classes = Test[A-Z]*
5655

5756
[gh-actions]
5857
python =
59-
3.8: py38, mypy
60-
3.9: py39, coverage
61-
3.10: py310, pre-commit
58+
3.10: py310
6259
3.11: py311
63-
3.12: py312
60+
3.12: py312, mypy, coverage, pre-commit
61+
3.13: py313
6462

6563
[testenv:docs]
6664
description = generate docs in HTML format

0 commit comments

Comments
 (0)