Skip to content

Commit cbd848d

Browse files
committed
Drop Python 3.9
1 parent 1d5cb2b commit cbd848d

7 files changed

Lines changed: 29 additions & 31 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ jobs:
1111
- macOS
1212
- Windows
1313
python-version:
14-
- '3.9'
1514
- '3.10'
1615
- '3.11'
1716
- '3.12'

CHANGES.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Version 1.12
66

77
* Add support for Free Threading.
88
* Support Python 3.14.
9+
* Drop Python 3.9 support.
910

1011

1112
Version 1.11

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Installation
3030
Requirements
3131
------------
3232

33-
- Python 3.9+
33+
- Python 3.10+
3434

3535

3636
Usage

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ classifiers = [
2121
"Programming Language :: C",
2222
"Programming Language :: C++",
2323
"Programming Language :: Python :: 3",
24-
"Programming Language :: Python :: 3.9",
2524
"Programming Language :: Python :: 3.10",
2625
"Programming Language :: Python :: 3.11",
2726
"Programming Language :: Python :: 3.12",
@@ -31,7 +30,7 @@ classifiers = [
3130
"Topic :: System :: Archiving :: Compression",
3231
"Typing :: Typed",
3332
]
34-
requires-python = ">= 3.9"
33+
requires-python = ">= 3.10"
3534
dynamic = [
3635
"version",
3736
]

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tox]
22
minversion = 3.3
3-
envlist = py3{9-14}, py3{13-14}t
3+
envlist = py3{10-14}, py3{13-14}t
44
isolated_build = True
55

66
[testenv]

zopfli/__init__.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#
22
# zopfli
33
#
4-
# Copyright (c) 2015-2024 Akinori Hattori <hattya@gmail.com>
4+
# Copyright (c) 2015-2025 Akinori Hattori <hattya@gmail.com>
55
#
66
# SPDX-License-Identifier: Apache-2.0
77
#
@@ -14,7 +14,7 @@
1414
import struct
1515
import sys
1616
import threading
17-
from typing import cast, Any, AnyStr, IO, Literal, Optional, Union
17+
from typing import cast, Any, AnyStr, IO, Literal, TypeAlias
1818
import zipfile
1919
import zlib
2020

@@ -31,7 +31,7 @@
3131
except ImportError:
3232
__version__ = 'unknown'
3333

34-
P = Union[str, os.PathLike[str]]
34+
P: TypeAlias = str | os.PathLike[str]
3535

3636

3737
class ZopfliDecompressor:
@@ -72,8 +72,8 @@ class ZipFile(zipfile.ZipFile):
7272
compression: int
7373
_lock: threading.RLock
7474

75-
def __init__(self, file: Union[P, IO[bytes]], mode: Literal['r', 'w', 'x', 'a'] = 'r', compression: int = zipfile.ZIP_DEFLATED, allowZip64: bool = True,
76-
compresslevel: Optional[int] = None, *, strict_timestamps: bool = True, encoding: str = 'cp437', **kwargs: Any) -> None:
75+
def __init__(self, file: P | IO[bytes], mode: Literal['r', 'w', 'x', 'a'] = 'r', compression: int = zipfile.ZIP_DEFLATED, allowZip64: bool = True,
76+
compresslevel: int | None = None, *, strict_timestamps: bool = True, encoding: str = 'cp437', **kwargs: Any) -> None:
7777
self.encoding = encoding
7878
self._options = kwargs
7979
super().__init__(file, mode, compression, allowZip64, compresslevel)
@@ -91,7 +91,7 @@ def _RealGetContents(self) -> None:
9191
zi.filename = n
9292
self.NameToInfo[zi.filename] = zi
9393

94-
def open(self, name: Union[str, zipfile.ZipInfo], mode: Literal['r', 'w'] = 'r', pwd: Optional[bytes] = None,
94+
def open(self, name: str | zipfile.ZipInfo, mode: Literal['r', 'w'] = 'r', pwd: bytes | None = None,
9595
*, force_zip64: bool = False, **kwargs: Any) -> IO[bytes]:
9696
fp = super().open(name, mode, pwd, force_zip64=force_zip64)
9797
if (mode == 'w'
@@ -103,10 +103,10 @@ def open(self, name: Union[str, zipfile.ZipInfo], mode: Literal['r', 'w'] = 'r',
103103
def _open_to_write(self, zinfo: zipfile.ZipInfo, force_zip64: bool = False) -> IO[bytes]:
104104
return cast(IO[bytes], super()._open_to_write(self._convert(zinfo), force_zip64))
105105

106-
def write(self, filename: P, arcname: Optional[P] = None,
107-
compress_type: Optional[int] = None, compresslevel: Optional[int] = None, **kwargs: Any) -> None:
106+
def write(self, filename: P, arcname: P | None = None,
107+
compress_type: int | None = None, compresslevel: int | None = None, **kwargs: Any) -> None:
108108
zopflify = self._zopflify(compress_type)
109-
z: Optional[ZopfliCompressor] = None
109+
z: ZopfliCompressor | None = None
110110
if zopflify:
111111
compress_type = zipfile.ZIP_STORED
112112
z = ZopfliCompressor(ZOPFLI_FORMAT_DEFLATE, **self._options | kwargs)
@@ -129,14 +129,14 @@ def write(self, filename: P, arcname: Optional[P] = None,
129129
self.filelist[-1] = zi
130130
self.NameToInfo[zi.filename] = zi
131131

132-
def writestr(self, zinfo_or_arcname: Union[str, zipfile.ZipInfo], data: AnyStr,
133-
compress_type: Optional[int] = None, compresslevel: Optional[int] = None, **kwargs: Any) -> None:
132+
def writestr(self, zinfo_or_arcname: str | zipfile.ZipInfo, data: AnyStr,
133+
compress_type: int | None = None, compresslevel: int | None = None, **kwargs: Any) -> None:
134134
if isinstance(zinfo_or_arcname, zipfile.ZipInfo):
135135
compress_type = zinfo_or_arcname.compress_type
136136
if isinstance(zinfo_or_arcname, ZipInfo):
137137
zinfo_or_arcname.encoding = self.encoding
138138
zopflify = self._zopflify(compress_type)
139-
z: Optional[ZopfliCompressor] = None
139+
z: ZopfliCompressor | None = None
140140
if zopflify:
141141
compress_type = zipfile.ZIP_STORED
142142
z = ZopfliCompressor(ZOPFLI_FORMAT_DEFLATE, **self._options | kwargs)
@@ -159,7 +159,7 @@ def writestr(self, zinfo_or_arcname: Union[str, zipfile.ZipInfo], data: AnyStr,
159159
self.NameToInfo[zi.filename] = zi
160160

161161
if sys.version_info >= (3, 11):
162-
def mkdir(self, zinfo_or_directory: Union[str, zipfile.ZipInfo], mode: int = 511) -> None:
162+
def mkdir(self, zinfo_or_directory: str | zipfile.ZipInfo, mode: int = 511) -> None:
163163
with self._lock:
164164
fp = self.fp
165165
try:
@@ -184,13 +184,13 @@ def _convert(self, src: zipfile.ZipInfo) -> ZipInfo:
184184
dst.encoding = self.encoding
185185
return dst
186186

187-
def _file(self, z: Optional[ZopfliCompressor]) -> IO[bytes]:
187+
def _file(self, z: ZopfliCompressor | None) -> IO[bytes]:
188188
LFH = '<4s5H3L2H'
189189
EFS = 1 << 11
190190

191191
class ZopfliFile:
192192

193-
def __init__(self, zf: ZipFile, z: Optional[ZopfliCompressor]) -> None:
193+
def __init__(self, zf: ZipFile, z: ZopfliCompressor | None) -> None:
194194
self.size = 0
195195
self._zf = zf
196196
self._fp = zf.fp
@@ -241,7 +241,7 @@ def _zip64(self, zi: zipfile.ZipInfo) -> bool:
241241
return (zi.file_size > zipfile.ZIP64_LIMIT
242242
or zi.compress_size > zipfile.ZIP64_LIMIT)
243243

244-
def _zopflify(self, compression: Optional[int]) -> bool:
244+
def _zopflify(self, compression: int | None) -> bool:
245245
return (compression == zipfile.ZIP_DEFLATED
246246
or (compression is None
247247
and self.compression == zipfile.ZIP_DEFLATED))
@@ -251,7 +251,7 @@ class ZipInfo(zipfile.ZipInfo):
251251

252252
__slots__ = ('encoding',)
253253

254-
encoding: Optional[str]
254+
encoding: str | None
255255
orig_filename: str
256256

257257
def __init__(self, *args: Any, **kwargs: Any) -> None:

zopfli/_zopfli.pyi

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
#
22
# zopfli._zopfli
33
#
4-
# Copyright (c) 2021-2024 Akinori Hattori <hattya@gmail.com>
4+
# Copyright (c) 2021-2025 Akinori Hattori <hattya@gmail.com>
55
#
66
# SPDX-License-Identifier: MIT
77
#
88

99
from collections.abc import Sequence
10-
from typing import Optional
1110

1211

1312
ZOPFLI_FORMAT_GZIP: int
@@ -17,16 +16,16 @@ ZOPFLI_FORMAT_DEFLATE: int
1716

1817
class ZopfliCompressor:
1918

20-
def __init__(self, format: int = ..., verbose: Optional[bool] = ..., iterations: int = ...,
21-
block_splitting: Optional[bool] = ..., block_splitting_max: int = ...) -> None: ...
19+
def __init__(self, format: int = ..., verbose: bool | None = ..., iterations: int = ...,
20+
block_splitting: bool | None = ..., block_splitting_max: int = ...) -> None: ...
2221
def compress(self, data: bytes) -> bytes: ...
2322
def flush(self) -> bytes: ...
2423

2524

2625
class ZopfliDeflater:
2726

28-
def __init__(self, verbose: Optional[bool] = ..., iterations: int = ...,
29-
block_splitting: Optional[bool] = ..., block_splitting_max: int = ...) -> None: ...
27+
def __init__(self, verbose: bool | None = ..., iterations: int = ...,
28+
block_splitting: bool | None = ..., block_splitting_max: int = ...) -> None: ...
3029
def compress(self, data: bytes) -> bytes: ...
3130
def flush(self) -> bytes: ...
3231

@@ -44,7 +43,7 @@ class ZopfliPNG:
4443
iterations: int
4544
iterations_large: int
4645

47-
def __init__(self, verbose: Optional[bool] = ..., lossy_transparent: Optional[bool] = ..., lossy_8bit: Optional[bool] = ..., filter_strategies: str = ...,
48-
auto_filter_strategy: Optional[bool] = ..., keep_color_type: Optional[bool] = ..., keep_chunks: Sequence[str] = ...,
49-
use_zopfli: Optional[bool] = ..., iterations: int = ..., iterations_large: int = ...) -> None: ...
46+
def __init__(self, verbose: bool | None = ..., lossy_transparent: bool | None = ..., lossy_8bit: bool | None = ..., filter_strategies: str = ...,
47+
auto_filter_strategy: bool | None = ..., keep_color_type: bool | None = ..., keep_chunks: Sequence[str] = ...,
48+
use_zopfli: bool | None = ..., iterations: int = ..., iterations_large: int = ...) -> None: ...
5049
def optimize(self, data: bytes) -> bytes: ...

0 commit comments

Comments
 (0)