Skip to content

Commit d2a2a52

Browse files
committed
Remove Python 3.9 support
Fixes #584
1 parent 7989b45 commit d2a2a52

16 files changed

Lines changed: 27 additions & 161 deletions

File tree

.github/workflows/build-pypi.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-14]
13-
pyver: [cp39, cp310, cp311, cp312, cp313, cp314, cp314t]
14-
exclude:
15-
- os: windows-11-arm
16-
pyver: cp39
17-
13+
pyver: [cp310, cp311, cp312, cp313, cp314, cp314t]
1814

1915
steps:
2016
- uses: actions/checkout@v5

.github/workflows/build-testpypi.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-14]
13-
pyver: [cp39, cp310, cp311, cp312, cp313, cp314, cp314t]
14-
exclude:
15-
- os: windows-11-arm
16-
pyver: cp39
17-
13+
pyver: [cp310, cp311, cp312, cp313, cp314, cp314t]
1814

1915
steps:
2016
- uses: actions/checkout@v5

Makefile

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,6 @@ compile-win: ## Builds and tests against all the Python versions on Windows
210210
$(MAKE) compile-win-one PYTHON=c:/python311-32/python
211211
$(MAKE) compile-win-one PYTHON=c:/python310-32/python
212212
$(MAKE) compile-win-one PYTHON=c:/python310/python
213-
$(MAKE) compile-win-one PYTHON=c:/python39-32/python
214-
$(MAKE) compile-win-one PYTHON=c:/python39/python
215213

216214
# I did try to make this use venv but then the pip inside the venv and
217215
# other packages were skipped due to metadata issues

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
:target: https://rogerbinns.github.io/apsw/
66

77
APSW stands for **A**\ nother **P**\ ython **S**\ QLite **W**\ rapper. APSW
8-
supports CPython 3.9 onwards.
8+
supports CPython 3.10 onwards.
99

1010
About
1111
=====

apsw/ext.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import abc
66
import collections
7-
import collections.abc
87
import contextvars
98
import dataclasses
109
import enum
@@ -24,12 +23,11 @@
2423
from dataclasses import dataclass, is_dataclass, make_dataclass
2524
from fractions import Fraction
2625
from typing import Any, Callable, Generator, Iterable, Iterator, Literal, Sequence, TextIO, Union
26+
from types import NoneType
2727

2828
import apsw
2929
import apsw.unicode
3030

31-
NoneType = types.NoneType if sys.version_info > (3, 10) else type(None)
32-
3331

3432
def result_string(code: int) -> str:
3533
"""Turns a result or extended result code into a string.

apsw/fts5.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,11 +1391,7 @@ def key_tokens(
13911391
for token in self.tokenize(utf8, include_colocated=False, include_offsets=False, locale=locale):
13921392
token_counter[token] += 1
13931393

1394-
try:
1395-
row_token_count = token_counter.total()
1396-
except AttributeError:
1397-
# Py <= 3.9 doesn't have total so do it the hard way
1398-
row_token_count = sum(token_counter.values())
1394+
row_token_count = token_counter.total()
13991395

14001396
# calculate per token score
14011397
scores: list[tuple[float, str]] = []

apsw/fts5query.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,13 +106,8 @@
106106
import sys
107107

108108
import dataclasses
109-
from wsgiref.simple_server import sys_version
110109

111-
try:
112-
from typing import Union, Any, Sequence, NoReturn, Literal, Iterator, TypeAlias
113-
except ImportError:
114-
# TypeAlias is not in Python <= 3.9
115-
pass
110+
from typing import Union, Any, Sequence, NoReturn, Literal, Iterator, TypeAlias
116111

117112
import apsw
118113

@@ -583,15 +578,8 @@ def quote(text: str | QueryTokens) -> str:
583578
COLUMNFILTER: ("query",),
584579
}
585580

586-
if sys.version_info >= (3, 10):
587-
588-
def _is_QUERY(obj):
589-
return isinstance(obj, QUERY)
590-
else:
591-
# py 3.9 can't do the above so we always return True. Providing a
592-
# non-query will result in an inscrutable error lower in walk
593-
def _is_QUERY(obj):
594-
return True
581+
def _is_QUERY(obj):
582+
return isinstance(obj, QUERY)
595583

596584

597585
def walk(start: QUERY) -> Iterator[tuple[tuple[QUERY, ...], QUERY]]:

apsw/shell.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ def handle_exception(self):
864864
if self.bail:
865865
raise
866866

867-
@dataclasses.dataclass(**({"slots": True, "frozen": True} if sys.version_info >= (3, 10) else {}))
867+
@dataclasses.dataclass(slots=True, frozen=True)
868868
class _qd:
869869
query: str | None
870870
remaining: str | None
@@ -3464,7 +3464,7 @@ def complete_command(self, line, token, beg, end):
34643464
return [v for v in sorted(completions) if v.startswith(token) and v != token]
34653465

34663466
### Output helpers
3467-
@dataclasses.dataclass(**({"slots": True, "frozen": True} if sys.version_info >= (3, 10) else {}))
3467+
@dataclasses.dataclass(slots=True, frozen=True)
34683468
class Row:
34693469
"Returned by :class:`Shell.PositionRow`"
34703470

apsw/tests/__main__.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10844,9 +10844,8 @@ def testFunctionFlags(self) -> None:
1084410844

1084510845
def testBestPractice(self) -> None:
1084610846
"apsw.bestpractice module"
10847-
if sys.version_info >= (3, 10):
10848-
with self.assertNoLogs():
10849-
apsw.log(apsw.SQLITE_NOMEM, "Zebras are striped")
10847+
with self.assertNoLogs():
10848+
apsw.log(apsw.SQLITE_NOMEM, "Zebras are striped")
1085010849
apsw.bestpractice.apply(apsw.bestpractice.recommended)
1085110850
with self.assertLogs() as l:
1085210851
apsw.log(apsw.SQLITE_NOMEM, "Zebras are striped")
@@ -10863,14 +10862,12 @@ def testBestPractice(self) -> None:
1086310862
con = apsw.Connection("")
1086410863
# now fail
1086510864
apsw.config(apsw.SQLITE_CONFIG_LOG, None)
10866-
if sys.version_info >= (3, 10):
10867-
with self.assertNoLogs():
10868-
self.assertRaises(apsw.SQLError, con.execute, dqs)
10865+
with self.assertNoLogs():
10866+
self.assertRaises(apsw.SQLError, con.execute, dqs)
1086910867

1087010868
# can't optimize or WAL readonly databases
10871-
if sys.version_info >= (3, 10):
10872-
with self.assertNoLogs():
10873-
apsw.Connection(self.db.filename, flags=apsw.SQLITE_OPEN_READONLY)
10869+
with self.assertNoLogs():
10870+
apsw.Connection(self.db.filename, flags=apsw.SQLITE_OPEN_READONLY)
1087410871

1087510872
def testExtTracing(self) -> None:
1087610873
"apsw.ext Tracing and Resource usage"
@@ -11224,13 +11221,9 @@ def stuff_namedtuple():
1122411221
(stuff_stat_struct, apsw.ext.VTColumnAccess.By_Attr, None),
1122511222
):
1122611223
if func is stuff_stat_struct:
11227-
if sys.version_info < (3, 10):
11228-
a = apsw.ext.VTColumnAccess.By_Attr
11229-
n = names = tuple(member for member in dir(next(func())) if member.startswith("st_"))
11230-
else:
11231-
n, a = apsw.ext.get_column_names(next(func()))
11232-
names = n
11233-
self.assertEqual(names, os.stat(".").__match_args__)
11224+
n, a = apsw.ext.get_column_names(next(func()))
11225+
names = n
11226+
self.assertEqual(names, os.stat(".").__match_args__)
1123411227
else:
1123511228
n, a = apsw.ext.get_column_names(next(func()))
1123611229
self.assertEqual(access, a)
@@ -12104,10 +12097,6 @@ def setup():
1210412097
if not getattr(memdb, "enableloadextension", None):
1210512098
del APSW.testLoadExtension
1210612099

12107-
# earlier py versions make recursion error fatal
12108-
if sys.version_info < (3, 10):
12109-
del APSW.testIssue425
12110-
1211112100
# Fork checker is becoming less usefull on newer Pythons because
1211212101
# multiprocessing really doesn't want you to use fork and does
1211312102
# alternate methods instead. We also run sanitizers on most

apsw/tests/ftstests.py

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,6 @@
3131
import apsw.fts5query
3232
import apsw.unicode
3333

34-
try:
35-
itertools.pairwise
36-
except AttributeError:
37-
# Py <= 3.9 doesn't have it. We monkeypatch because only
38-
# referenced in this test code
39-
40-
# from the docs
41-
def pairwise(iterable):
42-
# pairwise('ABCDEFG') → AB BC CD DE EF FG
43-
44-
iterator = iter(iterable)
45-
a = next(iterator, None)
46-
47-
for b in iterator:
48-
yield a, b
49-
a = b
50-
51-
itertools.pairwise = pairwise
52-
5334

5435
class BecauseWindowsTempfile:
5536
"Work around Windows preventing concurrent access to a file opened for writing"
@@ -1526,8 +1507,7 @@ def mq(s):
15261507
# key tokens
15271508
def token_check(rowid, expected, **kwargs):
15281509
kt = t.key_tokens(rowid, **kwargs)
1529-
zip_kwargs = {"strict": True} if sys.version_info >= (3, 10) else {}
1530-
for got, expected in zip(kt, expected, **zip_kwargs):
1510+
for got, expected in zip(kt, expected, strict=True):
15311511
# float values
15321512
self.assertAlmostEqual(got[0], expected[0])
15331513
self.assertEqual(got[1], expected[1])
@@ -2889,11 +2869,9 @@ def testErrors(self):
28892869

28902870
def testWalk(self):
28912871
"walk and related functions"
2892-
if sys.version_info >= (3, 10):
2893-
# py 3.9 is unable to type check and give a nicer error
2894-
self.assertRaises(TypeError, list, apsw.fts5query.walk({1: 2}))
2895-
self.assertRaises(TypeError, apsw.fts5query.extract_with_column_filters, {1: 2}, 1)
2896-
self.assertRaises(TypeError, apsw.fts5query.applicable_columns, {1: 2}, 2, 3)
2872+
self.assertRaises(TypeError, list, apsw.fts5query.walk({1: 2}))
2873+
self.assertRaises(TypeError, apsw.fts5query.extract_with_column_filters, {1: 2}, 1)
2874+
self.assertRaises(TypeError, apsw.fts5query.applicable_columns, {1: 2}, 2, 3)
28972875

28982876
def n(v):
28992877
# turns instance into class basename

0 commit comments

Comments
 (0)