There are different cases here:
- Package does not provide wheels with cp313t ABI.
This means that you can’t install on a free-threading build from a wheel. It may or may not be the case that building from source will result in a package however:
- The package may or may not have the
Py_MOD_GIL_NOT_USED flag set in its extension modules after building.
- It may or may not be the case that arguments to the build backend can be used to choose whether
Py_MOD_GIL_NOT_USED gets set.
- It may or may not be the case that the package has been tested in any of these configurations.
Another possibility is:
- The package provides wheels with cp313t ABI.
However caveats abound:
- The package may or may not
Py_MOD_GIL_NOT_USED flag set in its extension modules after building.
- It may or may not be the case that all features of the package can be used safely without the GIL.
- You can probably assume that the cp313t wheels have had some testing before being uploaded but…
- It may or may not be the case that free-threading has actually been stress tested i.e. the package was actually tested with many threads in high contention rather than just the normal package test suite.
(As an example, python-flint’s test suite does not have any tests that involve Python’s threads at all: it has never been relevant to consider them before.)
A third possibility is:
- The package has been fully tested and made compatible with a free-threaded build. All operations that previously relied on the GIL for thread-safety have been changed through either per-object locks or some other mechanism. The extension modules will have
Py_MOD_GIL_NOT_USED set and so CPython will run without the GIL after importing the package. Downstream users can simply use the package as they already have done without worrying about thread-safety at all.
However a fourth possibility is:
- The package has been fully tested and made compatible with a free-threaded build. All APIs that previously relied on the GIL for thread-safety have been replaced with new APIs that do not. The package is GIL-free but is only thread-safe if downstream code updates to use the new APIs or stops using features that are now deprecated.
Note an important caveat about the third and fourth cases:
- The fact that the package has been fully tested and made compatible with a free-threaded build does not imply that any cp313t wheels have been uploaded.
Many projects that upload wheels to PyPI are concerned about storage limits so a decision to upload wheels for any given OS/ABI etc is affected by how large the files are and expectations around how likely they are to be used. Some projects may test the free-threaded build in CI to confirm that it works but then may choose not to upload any wheels to PyPI. Currently a decision to upload cp313t wheels would double the size of the wheels for CPython 3.13. There are also other things like Linux aarch64, Windows on ARM etc that threaten to more or less double the number of wheels in other ways.
You can discuss having a classifier or a badge etc but I don’t see how you can reliably use that to distinguish all of the different cases above. Note that the most likely scenario here is actually the second case with the most likely caveat being:
- Not all features of the package can be used safely without the GIL.
No badge or classifier is going to help you if you don’t read the docs about what can be used safely without the GIL.
To quote from the guide linked above:
For NumPy, we are generally assuming users will not do pathological things like resizing an array while another thread is reading from or writing to it and do not explicitly account for this. Eventually we will need to add locking around data structures to avoid races caused by issues like this, but in this early stage of porting we are not planning to add locking on every operation exposed to users that mutates data. Locking will likely need to be added in the future, but that should be done carefully and with experience informed by real-world multithreaded scaling.
The situation for python-flint will likely be identical: don’t resize a polynomial that is shared between threads. Having thought about this a bit more I realise a more problematic case which is that the elements of some python-flint matrices can live on the heap and so potentially setting an element of a matrix is not thread-safe:
M[0,0] = large_integer # may trigger realloc
I will want to test this sort of thing in a multithreaded scenario before uploading wheels so that at least I have some idea how likely this is to be a problem.
So the situation will be that we upload wheels with the cp313t flag and the Py_MOD_GIL_NOT_USED but you have to read the docs to know which operations can be used safely in a multithreaded context. In future this may change and it may be that non-thread-safe operations are deprecated or it may be that per-object locks are introduced so that those operations become thread-safe at least in the “no segfaults” sense.
The most useful information for downstream users here is not a classifier or a badge but a link to the place in the docs where the status of testing, thread-safety, new/deprecated APIs, future plans etc can be found.