Skip to content
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
- '3.11'
- '3.12'
- '3.13'
- '3.13t'
arch:
- 'arm64'
- 'x86'
Expand Down Expand Up @@ -59,49 +60,62 @@ jobs:
PYTHONDEVMODE: '1'
steps:
- name: Set up Python
uses: actions/setup-python@v5
uses: Quansight-Labs/setup-python@v5
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to adopt a less official action? Does actions/setup-python not support the free-threaded builds?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, GitHub has been quite unresponsive to this: actions/setup-python#771

Though, there seems to be some recent activity: actions/setup-python#973

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fork is tracking upstream and has the open PR to add free-threading support applied.

You could also use setup-uv, which can be used as a drop-in replacement if you install pip into the uv environment.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, my (former) project provides the Python builds for uv. I actually trust those Python builds and the people behind uv. So astral-sh/setup-uv would be my preference.

with:
python-version: ${{ matrix.py }}
architecture: ${{ matrix.arch }}

- name: Install Rust
if: matrix.arch == 'x64'
if: matrix.arch == 'x64' && !startsWith(matrix.py, '3.13')
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable

- uses: actions/checkout@v4

- name: Install Dependencies
if: "!endsWith(matrix.py, 't')"
shell: bash
run: |
python -m pip install --require-hashes -r ci/requirements.txt

- name: Install Dependencies (free-threading)
if: "endsWith(matrix.py, 't')"
shell: bash
run: |
python -m pip install --require-hashes -r ci/requirements.freethreading.txt

# TODO enable once PyO3 supports 3.13.
- name: Build (Rust)
if: matrix.arch == 'x64' && matrix.py != '3.13'
if: matrix.arch == 'x64' && !startsWith(matrix.py, '3.13')
Comment on lines 81 to +83
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this is supported now. But scope bloat to resolve it in this PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened a followup issue to track this #251

env:
PIP_CONSTRAINT: 'ci/constraints.txt'
run: |
python -m pip install --config-settings='--build-option=--rust-backend' -e .

- name: Build (No Rust)
if: matrix.arch != 'x64' || matrix.py == '3.13'
if: matrix.arch != 'x64' || startsWith(matrix.py, '3.13')
run: |
python -m pip install -e .

- name: Test C Backend
run: |
pytest --numprocesses=auto --hypothesis-profile=${HYPOTHESIS_PROFILE} -v tests/

- name: Test in Parallel
if: "endsWith(matrix.py, 't')"
run: |
pytest --numprocesses=auto --hypothesis-profile=${HYPOTHESIS_PROFILE} --parallel-threads=10 -v tests/

- name: Test CFFI Backend
if: "!startsWith(matrix.py, '3.13')" # see pyproject.toml:4
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably this limitation no longer holds.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately no, CFFI is one of the last low-level major dependencies without support. Recently the maintainers asked our team to work on a fork with free-threading support so they can review one big PR:

python-cffi/cffi#143 (comment)

env:
PYTHON_ZSTANDARD_IMPORT_POLICY: 'cffi'
run: |
pytest --numprocesses=auto --hypothesis-profile=${HYPOTHESIS_PROFILE} -v tests/

- name: Test Rust Backend
if: matrix.arch == 'x64'
if: matrix.arch == 'x64' && !startsWith(matrix.py, '3.13')
# Rust backend is currently experimental. So ignore failures in it.
continue-on-error: true
env:
Expand Down
6 changes: 5 additions & 1 deletion c-ext/backend_c.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ void zstd_module_init(PyObject *m) {
Py_DECREF(feature);
#endif

#ifdef Py_GIL_DISABLED
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif

if (PyObject_SetAttrString(m, "backend_features", features) == -1) {
return;
}
Expand Down Expand Up @@ -313,7 +317,7 @@ size_t roundpow2(size_t i) {
int safe_pybytes_resize(PyObject **obj, Py_ssize_t size) {
PyObject *tmp;

if ((*obj)->ob_refcnt == 1) {
if (Py_REFCNT(*obj) == 1) {
return _PyBytes_Resize(obj, size);
}

Expand Down
12 changes: 12 additions & 0 deletions ci/requirements.freethreading.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# This is a dependency of pytest on Windows but isn't picked up by pip-compile.
atomicwrites
cibuildwheel
#cffi
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given that we're disabling cffi with setup.py maybe it makes sense to uncomment this line.
probably less headaches in the future.

IIRC this was the only difference, so these special requirements files for free threading can be removed.
(I'm on my phone, sorry I can't check this right now.)

colorama
hypothesis
mypy
pycparser
pytest-xdist
pytest-run-parallel
pytest
wheel
132 changes: 132 additions & 0 deletions ci/requirements.freethreading.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#
# This file is autogenerated by pip-compile with Python 3.13
# by the following command:
#
# pip-compile --generate-hashes --output-file=ci/requirements.freethreading.txt --pre ci/requirements.freethreading.in
#
atomicwrites==1.4.1 \
--hash=sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11
# via -r ci/requirements.freethreading.in
attrs==24.2.0 \
--hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \
--hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2
# via hypothesis
bashlex==0.18 \
--hash=sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee \
--hash=sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa
# via cibuildwheel
bracex==2.5.post1 \
--hash=sha256:12c50952415bfa773d2d9ccb8e79651b8cdb1f31a42f6091b804f6ba2b4a66b6 \
--hash=sha256:13e5732fec27828d6af308628285ad358047cec36801598368cb28bc631dbaf6
# via cibuildwheel
certifi==2024.8.30 \
--hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \
--hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9
# via cibuildwheel
cibuildwheel==2.21.3 \
--hash=sha256:3ce23a9e5406b3eeb80039d7a6fdb218a2450932a8037c0bf76511cd88dfb74e \
--hash=sha256:f1d036a13603a6ce4019d8b1bd52c296cf32461a3b3be8441434b60b8b378b80
# via -r ci/requirements.freethreading.in
colorama==0.4.6 \
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
# via -r ci/requirements.freethreading.in
execnet==2.1.1 \
--hash=sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc \
--hash=sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3
# via pytest-xdist
filelock==3.16.1 \
--hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \
--hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435
# via cibuildwheel
hypothesis==6.116.0 \
--hash=sha256:9c1ac9a2edb77aacae1950d8ded6b3f40dbf8483097c88336265c348d2132c71 \
--hash=sha256:d30271214eae0d4758b72b408e9777405c7c7f687e14e8a42853adea887b2891
# via -r ci/requirements.freethreading.in
iniconfig==2.0.0 \
--hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
--hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
# via pytest
mypy==1.13.0 \
--hash=sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc \
--hash=sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e \
--hash=sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f \
--hash=sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74 \
--hash=sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a \
--hash=sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2 \
--hash=sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b \
--hash=sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73 \
--hash=sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e \
--hash=sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d \
--hash=sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d \
--hash=sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6 \
--hash=sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca \
--hash=sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d \
--hash=sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5 \
--hash=sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62 \
--hash=sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a \
--hash=sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc \
--hash=sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7 \
--hash=sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb \
--hash=sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7 \
--hash=sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732 \
--hash=sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80 \
--hash=sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a \
--hash=sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc \
--hash=sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2 \
--hash=sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0 \
--hash=sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24 \
--hash=sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7 \
--hash=sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b \
--hash=sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372 \
--hash=sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8
# via -r ci/requirements.freethreading.in
mypy-extensions==1.0.0 \
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
--hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782
# via mypy
packaging==24.1 \
--hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \
--hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124
# via
# cibuildwheel
# pytest
platformdirs==4.3.6 \
--hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \
--hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb
# via cibuildwheel
pluggy==1.5.0 \
--hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \
--hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669
# via pytest
pycparser==2.22 \
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
--hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
# via -r ci/requirements.freethreading.in
pytest==8.3.3 \
--hash=sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181 \
--hash=sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2
# via
# -r ci/requirements.freethreading.in
# pytest-run-parallel
# pytest-xdist
pytest-run-parallel==0.1.0 \
--hash=sha256:13d8579d39d60d5d77695e6bc292daa3352a5974eb446819f52fba4e20bb0d0f \
--hash=sha256:271854a2919aaff4e2a39bc2094bd2f96aa32fba9e51a995405ead35b74cc062
# via -r ci/requirements.freethreading.in
pytest-xdist==3.6.1 \
--hash=sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7 \
--hash=sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d
# via -r ci/requirements.freethreading.in
sortedcontainers==2.4.0 \
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
# via hypothesis
typing-extensions==4.12.2 \
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
# via mypy
wheel==0.44.0 \
--hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \
--hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49
# via -r ci/requirements.freethreading.in
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Documentation = "https://python-zstandard.readthedocs.io/en/latest/"

[build-system]
requires = [
"cffi>=1.17.0",
# "cffi>=1.17.0", # ok for default, nok for free-threading
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is safe to unconditionally disable. Since cffi is used in setup.py, I believe removing this will have the effect of not producing the cffi backend when you build the package from source unless you disable build isolation and install the cffi dependency yourself.

But, the requires entries can't be dynamic and pyproject.toml provides no mechanism for conditional dependencies short of swapping in a custom build backend (I think).

Ugh, Python packaging tooling.

Copy link
Contributor

@ngoldbaum ngoldbaum Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can just disable this dynamically in setup.py on the free-threaded build - we'd still depend on cffi, but we wouldn't actually use it. There are ways to check for the free-threaded build at runtime. It's only really a headache if you don't have a setup.py and can't do dynamic things. I'll try this along with getting the rust bindings working.

Copy link
Contributor

@ngoldbaum ngoldbaum Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

along with getting the rust bindings working

Oh oops, missed that rust isn't enabled by default yet, so I'll ignore it for now.

"setuptools",
]
# Need to use legacy backend because setup_zstd.py breaks build isolation.
Expand Down
Loading