Skip to content

Commit d7c69ff

Browse files
igorcodingclaude
andcommitted
Add Python 3.14 support with free-threaded (no-GIL) compatibility
Python 3.14 compatibility: - Add PyUnicodeWriter compatibility macros for Python 3.14+ (Python 3.14 introduced public PyUnicodeWriter API, deprecating private _PyUnicodeWriter) - Add PyHASH_MULTIPLIER compatibility macro for Python 3.13+ (Python 3.13 introduced public PyHASH_MULTIPLIER constant) - Update ttuple_repr() and ttuple_hash() to use new compatibility macros Free-threaded Python support: - Add `freethreading_compatible=True` Cython directive to protocol.pyx - Disable C freelist in tupleobj.c when Py_GIL_DISABLED is defined (global static arrays are not thread-safe without the GIL) Code cleanup (Python 3.9+ minimum): - Simplify trashcan macros (always use Py_TRASHCAN_BEGIN/END for Python 3.9+) - Remove obsolete Python 2.x and <3.7 backports from datetime handling - Remove python.pxd (no longer needed, use standard cpython.datetime) - Update datetime.pyx to use cpython.datetime's timezone_new and datetime_from_timestamp - Remove dateutil dependency from tests (datetime.fromisoformat available since 3.7) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent b1472fd commit d7c69ff

24 files changed

+350
-349
lines changed

.github/workflows/actions.yaml

Lines changed: 78 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,25 @@ jobs:
77
strategy:
88
matrix:
99
os: [ ubuntu-latest, macos-latest ]
10-
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', 'pypy3.10']
10+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t', 'pypy3.10', 'pypy3.11']
1111
tarantool: ['1.10', '2', '3']
1212
exclude:
1313
- os: macos-latest
1414
tarantool: '1.10'
1515
- os: macos-latest
1616
tarantool: '2'
17-
- os: macos-latest
18-
python-version: '3.7'
1917
- python-version: 'pypy3.10'
2018
tarantool: '1.10'
19+
- python-version: 'pypy3.11'
20+
tarantool: '1.10'
2121

2222
runs-on: ${{ matrix.os }}
2323

2424
steps:
2525
- uses: actions/checkout@v4
2626
with:
2727
submodules: recursive
28+
2829
- name: Set up Python ${{ matrix.python-version }}
2930
uses: actions/setup-python@v5
3031
with:
@@ -40,24 +41,41 @@ jobs:
4041
if: matrix.os == 'macos-latest'
4142
run: brew install tarantool
4243

44+
- name: Install uv
45+
uses: astral-sh/setup-uv@v4
46+
4347
- name: Install dependencies
44-
run: |
45-
python -m pip install --upgrade pip setuptools wheel coveralls
48+
run: uv sync --extra test
49+
50+
- name: Run ruff check
51+
run: uv run ruff check .
52+
53+
- name: Run ruff format check
54+
run: uv run ruff format --check .
55+
4656
- name: Run tests
47-
run: |
48-
if [[ "$RUNNER_OS" == "Linux" && ${{ matrix.python-version }} == "3.12" && ${{ matrix.tarantool }} == "3" ]]; then
49-
make build && make test
50-
make clean && make debug && make coverage
51-
# coveralls
52-
else
53-
make build && make lint && make quicktest
54-
fi
57+
run: uv run pytest .
58+
59+
- name: Run tests with uvloop
60+
if: "!contains(matrix.python-version, 'pypy')"
61+
env:
62+
USE_UVLOOP: "1"
63+
run: uv run pytest .
64+
65+
- name: Run coverage tests
66+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.14' && matrix.tarantool == '3'
5567
env:
68+
ASYNCTNT_DEBUG: "1"
5669
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}"
70+
run: |
71+
make clean
72+
uv pip install -e '.[test]'
73+
uv run pytest --cov
74+
./scripts/run_until_success.sh uv run coverage report -m
75+
./scripts/run_until_success.sh uv run coverage html
5776
5877
build-wheels:
5978
name: Build wheels on ${{ matrix.os }}
60-
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
6179
runs-on: ${{ matrix.os }}
6280
strategy:
6381
matrix:
@@ -71,75 +89,100 @@ jobs:
7189
submodules: recursive
7290

7391
- uses: actions/setup-python@v5
92+
with:
93+
python-version: '3.14'
94+
95+
- name: Install uv
96+
uses: astral-sh/setup-uv@v4
7497

7598
- name: Install cibuildwheel
76-
run: python -m pip install --upgrade cibuildwheel
99+
run: uv tool install cibuildwheel
100+
101+
- name: Install pyproject-build
102+
run: uv tool install build
103+
104+
- name: Build source archive
105+
if: matrix.os == 'ubuntu-latest'
106+
run: |
107+
pyproject-build -s .
77108
78109
- name: Build wheels
79-
run: python -m cibuildwheel --output-dir wheelhouse
110+
run: cibuildwheel --output-dir dist
80111
env:
81-
CIBW_BUILD: "cp37-* cp38-* cp39-* cp310-* cp311-* cp312-* cp313-* pp310-*"
112+
CIBW_ENABLE: pypy pypy-eol
113+
CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-* cp314-* cp314t-* pp310-* pp311-*"
82114

83115
- uses: actions/upload-artifact@v4
84116
with:
85117
name: wheels-${{ matrix.os }}
86-
path: ./wheelhouse/*.whl
118+
path: ./dist/*
87119

88120
publish:
89121
name: Publish wheels
90122
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
91123
needs:
92124
- build-wheels
93125
runs-on: ubuntu-latest
126+
environment:
127+
name: pypi
128+
url: https://pypi.org/p/asynctnt
129+
permissions:
130+
id-token: write # Required for trusted publishing
131+
contents: write # Required for releases
94132
steps:
95133
- name: Get tag
96134
id: get_tag
97135
run: echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//}
136+
98137
- run: echo "Current tag is ${{ steps.get_tag.outputs.TAG }}"
138+
99139
- uses: actions/checkout@v4
100140
with:
101141
submodules: recursive
142+
102143
- name: Set up Python
103144
uses: actions/setup-python@v5
104145
with:
105-
python-version: '3.12'
146+
python-version: '3.14'
147+
148+
- name: Install uv
149+
uses: astral-sh/setup-uv@v4
106150

107151
- name: Install dependencies
108152
run: |
109-
python -m pip install --upgrade pip setuptools wheel twine build
153+
uv pip install --upgrade build
110154
111155
- uses: actions/download-artifact@v4
112156
with:
113157
name: wheels-ubuntu-latest
114-
path: wheels-ubuntu
158+
path: dist
115159

116160
- uses: actions/download-artifact@v4
117161
with:
118162
name: wheels-macos-latest
119-
path: wheels-macos
163+
path: dist
120164

121165
- uses: actions/download-artifact@v4
122166
with:
123167
name: wheels-windows-latest
124-
path: wheels-windows
168+
path: dist
125169

126-
- name: Publish dist
170+
- name: Build source archive
127171
run: |
128172
python -m build . -s
129-
tree dist wheels-ubuntu wheels-macos wheels-windows
130-
twine upload dist/* wheels-ubuntu/*.whl wheels-macos/*.whl wheels-windows/*.whl
131-
env:
132-
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
133-
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
173+
tree dist
174+
175+
- name: Publish to PyPI
176+
uses: pypa/gh-action-pypi-publish@release/v1
177+
with:
178+
print-hash: true
179+
134180
- uses: marvinpinto/action-automatic-releases@latest
135181
with:
136182
repo_token: "${{ secrets.GITHUB_TOKEN }}"
137183
prerelease: false
138184
title: ${{ steps.get_tag.outputs.TAG }}
139185
files: |
140-
wheels-ubuntu/*.whl
141-
wheels-macos/*.whl
142-
wheels-windows/*.whl
143186
dist/*
144187
145188
docs:
@@ -156,12 +199,10 @@ jobs:
156199
- name: Set up Python
157200
uses: actions/setup-python@v5
158201
with:
159-
python-version: '3.12'
202+
python-version: '3.14'
160203

161-
- name: Install dependencies
162-
run: |
163-
python -m pip install --upgrade pip setuptools wheel build
164-
make build
204+
- name: Install uv
205+
uses: astral-sh/setup-uv@v4
165206

166207
- name: Build docs
167208
run: make docs

.gitignore

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ celerybeat-schedule
8181
.env
8282

8383
# virtualenv
84-
.venv/
84+
.venv*/
8585
venv/
8686
ENV/
8787

@@ -113,3 +113,10 @@ deploy_key*
113113
cython_debug
114114

115115
temp
116+
bin
117+
include
118+
instances.enabled
119+
modules
120+
templates
121+
uv.lock
122+
.DS_Store

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## v2.5.0
4+
**Breaking changes**
5+
* Dropped support for Python 3.7 and 3.8 (minimum required version is now 3.9)
6+
7+
**New features**
8+
* Added support for Python 3.14, including free-threaded (no-GIL) builds
9+
10+
**Other changes**
11+
* Upgraded Cython to 3.2.4
12+
* Declared Cython module as `freethreading_compatible` for Python 3.13+
13+
* Disabled C freelist in free-threaded builds to ensure thread safety
14+
315
## v2.4.0
416
**New features**
517
* Added support for Python 3.13 [#37](https://github.com/igorcoding/asynctnt/issues/37)

Makefile

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,62 @@
11
.PHONY: clean build local debug annotate dist docs style mypy ruff style-check lint test quicktest coverage
22

3-
PYTHON?=python
4-
53
all: local
64

7-
clean:
8-
pip uninstall -y asynctnt
9-
rm -rf asynctnt/*.c asynctnt/*.h asynctnt/*.cpp
10-
rm -rf asynctnt/*.so asynctnt/*.html
11-
rm -rf asynctnt/iproto/*.c asynctnt/iproto/*.h
12-
rm -rf asynctnt/iproto/*.so asynctnt/iproto/*.html asynctnt/iproto/requests/*.html
13-
rm -rf build *.egg-info .eggs dist
14-
find . -name '__pycache__' | xargs rm -rf
15-
rm -rf htmlcov
16-
rm -rf __tnt*
17-
rm -rf tests/__tnt*
18-
19-
205
build:
21-
$(PYTHON) -m pip install -e '.[test,docs]'
6+
uv pip install -e '.[test,docs]'
227

238
local:
24-
$(PYTHON) -m pip install -e .
25-
9+
uv pip install -e .
2610

2711
debug: clean
28-
ASYNCTNT_DEBUG=1 $(PYTHON) -m pip install -e '.[test]'
29-
12+
ASYNCTNT_DEBUG=1 uv pip install -e '.[test]'
3013

3114
annotate:
3215
cython -3 -a asynctnt/iproto/protocol.pyx
3316

34-
dist:
35-
$(PYTHON) -m build .
36-
37-
docs: build
38-
$(MAKE) -C docs html
17+
lint: style-check ruff
3918

4019
style:
41-
$(PYTHON) -m black .
42-
$(PYTHON) -m isort .
20+
uv run --active ruff format .
21+
uv run --active ruff check --select I,F401 --fix .
4322

44-
mypy:
45-
$(PYTHON) -m mypy --enable-error-code ignore-without-code .
23+
style-check:
24+
uv run --active ruff format --check .
4625

4726
ruff:
48-
$(PYTHON) -m ruff check .
49-
50-
style-check:
51-
$(PYTHON) -m black --check --diff .
52-
$(PYTHON) -m isort --check --diff .
27+
uv run --active ruff check .
5328

54-
lint: style-check ruff
29+
mypy:
30+
uv run --active mypy --enable-error-code ignore-without-code .
5531

56-
test: lint
57-
PYTHONASYNCIODEBUG=1 $(PYTHON) -m pytest
58-
$(PYTHON) -m pytest
59-
USE_UVLOOP=1 $(PYTHON) -m pytest
32+
test:
33+
PYTHONASYNCIODEBUG=1 uv run --active pytest
34+
uv run --active pytest
35+
USE_UVLOOP=1 uv run --active pytest
6036

6137
quicktest:
62-
$(PYTHON) -m pytest
38+
uv run --active pytest
6339

6440
coverage:
65-
$(PYTHON) -m pytest --cov
66-
./scripts/run_until_success.sh $(PYTHON) -m coverage report -m
67-
./scripts/run_until_success.sh $(PYTHON) -m coverage html
41+
uv run --active pytest --cov
42+
./scripts/run_until_success.sh uv run --active coverage report -m
43+
./scripts/run_until_success.sh uv run --active coverage html
44+
45+
dist:
46+
uv pip install build
47+
uv run --active python -m build .
48+
49+
docs: build
50+
$(MAKE) -C docs html
51+
52+
clean:
53+
uv pip uninstall asynctnt
54+
rm -rf asynctnt/*.c asynctnt/*.h asynctnt/*.cpp
55+
rm -rf asynctnt/*.so asynctnt/*.html
56+
rm -rf asynctnt/iproto/*.c asynctnt/iproto/*.h
57+
rm -rf asynctnt/iproto/*.so asynctnt/iproto/*.html asynctnt/iproto/requests/*.html
58+
rm -rf build *.egg-info .eggs dist
59+
find . -name '__pycache__' | xargs rm -rf
60+
rm -rf htmlcov
61+
rm -rf __tnt*
62+
rm -rf tests/__tnt*

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
[![Build](https://github.com/igorcoding/asynctnt/actions/workflows/actions.yaml/badge.svg?branch=master)](https://github.com/igorcoding/asynctnt/actions)
44
[![PyPI](https://img.shields.io/pypi/v/asynctnt.svg)](https://pypi.python.org/pypi/asynctnt)
55
[![Maintainability](https://api.codeclimate.com/v1/badges/6cec8adae280cda3e161/maintainability)](https://codeclimate.com/github/igorcoding/asynctnt/maintainability)
6-
<a href="http://tarantool.org">
6+
<a href="https://tarantool.org">
77
<img src="https://avatars2.githubusercontent.com/u/2344919?v=2&s=250" align="right">
88
</a>
99

1010
asynctnt is a high-performance [Tarantool](https://tarantool.org/) database
1111
connector library for Python/asyncio. It was highly inspired by
1212
[asyncpg](https://github.com/MagicStack/asyncpg) module.
1313

14-
asynctnt requires Python 3.7 or later and is supported for Tarantool
14+
asynctnt requires Python 3.9 or later and is supported for Tarantool
1515
versions 1.10+.
1616

1717

asynctnt/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
TarantoolTuple,
1919
)
2020

21-
__version__ = "2.4.0"
21+
__version__ = "2.5.0"

0 commit comments

Comments
 (0)