-
Notifications
You must be signed in to change notification settings - Fork 10
Add Python 3.14 support and remove Python <3.8 compatibility code #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
igorcoding
commented
Jan 18, 2026
- 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
- Simplify trashcan macros (always use Py_TRASHCAN_BEGIN/END for Python 3.8+)
- 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)
d7c69ff to
649748b
Compare
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]>
649748b to
32746ac
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds Python 3.14 support and removes compatibility code for Python versions < 3.8. The changes modernize the codebase by:
- Upgrading Cython from 3.0.11 to 3.2.4
- Adding compatibility macros for Python 3.13+ (
PyHASH_MULTIPLIER) and Python 3.14+ (PyUnicodeWriterAPI) - Adding free-threaded (no-GIL) build support for Python 3.13+
- Removing obsolete Python 2.x and <3.7 compatibility code
- Removing the
dateutiltest dependency (usingdatetime.fromisoformatinstead) - Updating build tooling to use
uv
Changes:
- Add Python 3.14 compatibility macros for PyUnicodeWriter and PyHASH_MULTIPLIER in C code
- Disable C freelist in free-threaded builds to ensure thread safety
- Remove python.pxd and use standard cpython.datetime functions
- Update test code to use datetime.fromisoformat instead of dateutil
- Modernize build configuration (Cython 3.2.4, setuptools>=77, uv tooling)
Reviewed changes
Copilot reviewed 23 out of 24 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| asynctnt/iproto/tupleobj/tupleobj.h | Added compatibility macros for Python 3.13+ (PyHASH_MULTIPLIER) and 3.14+ (PyUnicodeWriter API) |
| asynctnt/iproto/tupleobj/tupleobj.c | Updated to use new compatibility macros and disabled freelist for free-threaded builds |
| asynctnt/iproto/python.pxd | Removed obsolete Python 2.x and <3.7 compatibility code |
| asynctnt/iproto/ext/datetime.pyx | Updated to use cpython.datetime's timezone_new and datetime_from_timestamp |
| asynctnt/iproto/protocol.pyx | Added freethreading_compatible directive |
| asynctnt/iproto/protocol.pxd | Removed python.pxd include |
| tests/test_mp_ext.py | Removed dateutil dependency, using datetime.fromisoformat instead |
| tests/*.py | Fixed string concatenation style (removing implicit concatenation) |
| tests/_testbase.py | Added length assertion to assertResponseEqual and noqa comment for print |
| pyproject.toml | Updated Python version support (3.9-3.14), Cython version, removed black/isort, expanded ruff config |
| setup.py | Updated Cython version to 3.2.4 |
| .github/workflows/actions.yaml | Updated CI matrix for Python 3.9-3.14, PyPy 3.11, and modernized to use uv |
| Makefile | Updated to use uv commands |
| README.md | Updated minimum Python version requirement to 3.9 |
| CHANGELOG.md | Added v2.5.0 release notes |
| bench/benchmark.py | Updated to use uvloop.run and added noqa comments for prints |
| asynctnt/init.py | Version bump to 2.5.0 |
| asynctnt/connection.py | Fixed string concatenation style |
| .gitignore | Added uv-related entries |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "Programming Language :: Python :: 3.13", | ||
| "Programming Language :: Python :: 3.14", | ||
| 'Programming Language :: Python :: Implementation :: CPython', | ||
| "Intended Audience :: Developers", |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "License :: OSI Approved :: Apache Software License" classifier was removed from the classifiers list. This classifier is important for PyPI users to filter packages by license type. Consider restoring this classifier to maintain compatibility with PyPI's license filtering.
| ./scripts/run_until_success.sh uv run coverage html | ||
| build-wheels: | ||
| name: Build wheels on ${{ matrix.os }} |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The build-wheels job is now triggered on every push, whereas previously it was restricted to tag pushes only (via the if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') condition). This means wheel builds will run unnecessarily on every commit, consuming CI resources. Consider restoring the condition to only build wheels when pushing tags.
| name: Build wheels on ${{ matrix.os }} | |
| name: Build wheels on ${{ matrix.os }} | |
| if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') |
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.14' && matrix.tarantool == '3' | ||
| env: | ||
| ASYNCTNT_DEBUG: "1" | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}" |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a syntax error in the GITHUB_TOKEN environment variable - it has an extra double quote at the end. This will cause the token to include a literal quote character, which could lead to authentication issues. Remove the trailing double quote.
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}" | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.14' && matrix.tarantool == '3' | ||
| env: | ||
| ASYNCTNT_DEBUG: "1" | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}" |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The coverage test condition has changed from Python 3.12 to Python 3.14. Since Python 3.14 is newly added and might not be fully stable yet (especially the free-threaded build), consider whether coverage should still be tested on a more stable Python version like 3.12 or 3.13. This ensures consistent coverage reporting even if 3.14 has compatibility issues.
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.14' && matrix.tarantool == '3' | |
| env: | |
| ASYNCTNT_DEBUG: "1" | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}" | |
| if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' && matrix.tarantool == '3' | |
| env: | |
| ASYNCTNT_DEBUG: "1" | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| loop.run_until_complete( | ||
| async_bench( | ||
|
|
||
| async def main(name=name, conn_creator=conn_creator): |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inner async function definition shadows the outer main function name. While this works due to scoping, it's confusing to have two different functions with the same name in close proximity. Consider renaming the inner async function to something like bench_main or run_benchmark to avoid confusion.
| # Аналогично black, двойные кавычки | ||
| quote-style = "double" | ||
|
|
||
| # Аналогично black, пробелы вместо табов | ||
| indent-style = "space" | ||
|
|
||
| # Аналогично black, уважаем trailing commas | ||
| skip-magic-trailing-comma = false | ||
|
|
||
| # Аналогично black, автоматически определяем подходящее окончание строки. | ||
| line-ending = "auto" | ||
|
|
||
| [tool.ruff.lint] | ||
| # Список кодов или префиксов правил, которые следует считать исправляемыми. (https://docs.astral.sh/ruff/settings/#fixable) | ||
| # По умолчанию все правила считаются исправляемыми. | ||
| fixable = ["I", "RUF022", "RUF023", "F401"] | ||
| preview = true | ||
|
|
||
| select = [ | ||
| "E", # pycodestyle errors | ||
| "W", # pycodestyle warnings | ||
| "F", # pyflakes | ||
| "I", # isort | ||
| "C", # flake8-comprehensions | ||
| "B", # flake8-bugbear | ||
| "T20", # flake8-print | ||
| ] | ||
| lint.ignore = [ | ||
| "E501", # line too long, handled by black | ||
| "B008", # do not perform function calls in argument defaults | ||
| "C901", # too complex | ||
| ignore = [ | ||
| "E501", # line too long, handled by black | ||
| "B008", # do not perform function calls in argument defaults | ||
| "C901", # too complex | ||
| ] | ||
|
|
||
| extend-exclude = [ | ||
| "app/store/migrations", | ||
| [tool.ruff.lint.isort] | ||
| # Позволяет использовать as в комбинации с группировкой (https://docs.astral.sh/ruff/settings/#isort-combine-as-imports) | ||
| #from package import ( | ||
| # func1 as foo, | ||
| # func2 as boo, | ||
| #) | ||
| combine-as-imports = true | ||
|
|
||
| # Воспринимать следующие пакеты в качестве stdlib (https://docs.astral.sh/ruff/settings/#isort-extra-standard-library) | ||
| extra-standard-library = ["typing_extensions"] | ||
|
|
||
| section-order = [ | ||
| "future", | ||
| "standard-library", | ||
| "third-party", | ||
| "first-party", | ||
| "local-folder" | ||
| ] | ||
|
|
||
| # Не добавлять пустую строку перед данными секциям (https://docs.astral.sh/ruff/settings/#isort-no-lines-before) | ||
| no-lines-before = [] | ||
|
|
||
| [tool.ruff.lint.pep8-naming] | ||
| # если навесить данные декораторы, то можно использовать cls (https://docs.astral.sh/ruff/settings/#pep8-naming-classmethod-decorators) | ||
| # в качестве первого аргумента. | ||
| classmethod-decorators = ["cached_classproperty", "classproperty"] |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comments in the ruff configuration section are in Russian (Cyrillic). While this doesn't affect functionality, it may be better to use English comments for international collaboration and consistency with the rest of the codebase, which uses English comments.
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip setuptools wheel coveralls | ||
| run: uv sync --extra test |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GitHub Actions workflow uses 'uv sync --extra test' but the Makefile uses 'uv pip install -e' commands. These are different approaches - 'uv sync' expects a uv.lock file (which is in .gitignore), while 'uv pip install' works like pip. This inconsistency could lead to different dependency resolution between CI and local development. Consider either adding uv.lock to version control if you want reproducible builds, or use 'uv pip install' consistently in both places.
| run: uv sync --extra test | |
| run: uv pip install -e '.[test]' |