Skip to content

Add CI to test scikit-image against free-threaded Python 3.13 #7463

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

Merged
merged 10 commits into from
Jul 31, 2024

Conversation

andfoy
Copy link
Contributor

@andfoy andfoy commented Jul 11, 2024

Description

See #7464

This PR enables testing scikit-image against free-threaded Python, this is part of the ongoing effort to test projects against the changes proposed in PEP703

For more information see the following related PRs in other projects:

Checklist

Release note

For maintainers and optionally contributors, please refer to the instructions on how to document this PR for the release notes.

...

@stefanv
Copy link
Member

stefanv commented Jul 11, 2024

Thanks @andfoy, that's super helpful!

@andfoy
Copy link
Contributor Author

andfoy commented Jul 11, 2024

Tests are now passing locally in my fork: https://github.com/andfoy/scikit-image/actions/runs/9900010082/job/27349985531

@andfoy andfoy marked this pull request as ready for review July 11, 2024 23:59
@stefanv
Copy link
Member

stefanv commented Jul 12, 2024

Thanks again, this looks good to me.

Now, how do we shore up our test suite to actually create parallel faults if they are present? I suspect our current suite simply won't catch those.

@andfoy
Copy link
Contributor Author

andfoy commented Jul 12, 2024

Well, the usual first step is to identify which classes or functions may have a shared state (usually a big culprit for concurrency issues).

I did a similar exercise throughout scipy.interpolate scipy/scipy#20671 and scipy.spatial scipy/scipy#20655, where we found several cases of classes that had a shared state.

However, this is a very time consuming exercise that should be conducted with planning ahead, i.e., first making sure that the most popular functionality of scikit-image is tested for concurrency first.

I don't know how much budget we can spend here from the current Quansight labs project, maybe @rgommers knows more about how we can help the project to improve its parallelism support.

@rgommers
Copy link

Well, the usual first step is to identify which classes or functions may have a shared state (usually a big culprit for concurrency issues).

Agreed. I'm not sure that there is much shared/global state - if there is, maintainers often know where it's located.

In scikit-image I believe there's a lot of Cython and Pythran code, and no C/C++ code (right?). Pythran code is fairly constrained in how it can be written - I believe individual numerical kernels are safe. For Cython code that's written in C-like ways, it can have thread-safety issues. I suggest auditing it quickly for odd patterns. And maybe @stefanv can suggest a couple of the most heavily used and complex functions to write some new parallel tests for? That tends to be pretty quick to do, and if they all pass that gives a good amount of confidence (plus it's a test pattern that's then easy for future contributors to emulate).

At that point I would stop unless scikit-image-specific bugs show up. I'm hoping that that won't be the case. Given the amount of Cython code and there being both known and unknown issues in Cython, I think it's perhaps more likely to hit Cython (or NumPy, or CPython) issues than problems in scikit-image itself.

I'd also suggest opening a tracking issue to capture the plans/progress, that's easier to find that a merged PR.

Co-authored-by: Stefan van der Walt <sjvdwalt@gmail.com>
@stefanv
Copy link
Member

stefanv commented Jul 13, 2024

Should we also publish a nightly wheel for packages that test against us?

@stefanv
Copy link
Member

stefanv commented Jul 13, 2024

Thanks for guidance on testing! We do have parallel tests for some of our Cython code, and hopefully that catches the worst of it. All of our C++ code comes via Cython, but we do have some C code for phase unwrapping (unwrap_2d_ljmu.c and unwrap_3d_ljmu.c); I'll add some parallel testing to those functions.

Copy link
Member

@stefanv stefanv left a comment

Choose a reason for hiding this comment

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

Tentatively approved, with two (minor) questions.

@@ -129,7 +129,7 @@ def __len__(self):
"""Return one more than the maximum label value being remapped."""
return np.max(self.in_values) + 1

def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=False):
Copy link
Member

Choose a reason for hiding this comment

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

Does this function not only handle the copy=True case?

Copy link
Contributor Author

@andfoy andfoy Jul 15, 2024

Choose a reason for hiding this comment

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

According to the documentation, it should be copy=None, I've just updated it

r'\*\*(\w+)\*\* \:.*?\n(.*?)(?=\n [\*\S]+)', doc, flags=re.DOTALL
)
arg_regex = r'\*\*(\w+)\*\* \:.*?\n(.*?)(?=\n [\*\S]+)'
if sys.version_info >= (3, 13):
Copy link
Member

Choose a reason for hiding this comment

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

What is this change for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After Python 3.13, the docstrings extracted via the __doc__ attribute now have their initial indentation offsets stripped. Thus this regex now needs to be modified in order for it to work without considering the initial indentation. See: https://docs.python.org/3.13/whatsnew/3.13.html#other-language-changes

Copy link
Member

@lagru lagru left a comment

Choose a reason for hiding this comment

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

@andfoy, thanks for getting this process started!

@stefanv
Copy link
Member

stefanv commented Jul 13, 2024

How does Cython's freethreading_compatible flag interact with the PYTHON_GIL variable; do we need to modify our Meson build? Maybe @ngoldbaum has thoughts.

@andfoy
Copy link
Contributor Author

andfoy commented Jul 15, 2024

Should we also publish a nightly wheel for packages that test against us?

I can open a follow up PR to upload free-threaded nightly wheels to the Scientific Python Anaconda distribution channel

@rgommers
Copy link

How does Cython's freethreading_compatible flag interact with the PYTHON_GIL variable; do we need to modify our Meson build?

The freethreading-compatible sets the default. PYTHON_GIL=0 overrides the default (so if an extension is not yet marked as safe, it's forced to run without the GIL anyway). Using PYTHON_GIL=0 may be useful for the next little while, until all extension modules in all test dependencies are marked as safe and those nightly wheels have propagated.

xref numpy/numpy#26950 for the pattern to use the new freethreading-compatible directive.

@andfoy
Copy link
Contributor Author

andfoy commented Jul 17, 2024

@stefanv @lagru 140a4dc preemptively marks all scikit-image modules as free-threading safe, however, for this to be true, it would require an extensive lookup on each of scikit-image's modules to guarantee that they are indeed safe. I don't know if you prefer to that to be done in a separate PR?

@stefanv
Copy link
Member

stefanv commented Jul 17, 2024

We only have one source of C code:

./restoration/unwrap_2d_ljmu.c
./restoration/unwrap_3d_ljmu.c

If, in addition, we can guarantee that Cython and Pythran codes are safe, then I think we're good (I don't currently have enough knowledge to perform such a check).

@andfoy
Copy link
Contributor Author

andfoy commented Jul 22, 2024

@stefanv, thanks for the references! After looking at them, they should not have any issues regarding C module registration, since they are being included as part of other Cython source.

@andfoy
Copy link
Contributor Author

andfoy commented Jul 22, 2024

From my side, this PR is ready

Copy link
Member

@stefanv stefanv left a comment

Choose a reason for hiding this comment

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

Let's give it a shot!

# TODO: delete when scipy, numpy, cython and pywavelets free-threaded wheels are available on PyPi
FREE_THREADED_BUILD="$(python -c"import sysconfig; print(bool(sysconfig.get_config_var('Py_GIL_DISABLED')))")"
if [[ $FREE_THREADED_BUILD == "True" ]]; then
pip install --pre -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple cython numpy scipy pywavelets
Copy link
Member

Choose a reason for hiding this comment

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

We should keep an eye on whether this actually provides pre-releases in our CI for FREE_THREADED_BUILD=True. We've had issues with pip updating indirect dependencies despite the upgrade strategy being "only-if-needed".

Copy link
Member

@lagru lagru left a comment

Choose a reason for hiding this comment

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

Looks good otherwise provided the CI passes.

@rgommers
Copy link

Two approvals and the one CI failure is unrelated. Can this PR be merged?

@rgommers
Copy link

Should we also publish a nightly wheel for packages that test against us?

That does seem like a good idea. In other projects we've done that as a next step after adding regular CI. And in NumPy we've now merged the default and free-threaded wheel builds; 2.1.0 should publish cp313t wheels to PyPI.

@lagru lagru merged commit ee41933 into scikit-image:main Jul 31, 2024
25 of 27 checks passed
@stefanv stefanv added this to the 0.25 milestone Jul 31, 2024
@lagru
Copy link
Member

lagru commented Jul 31, 2024

Thanks for helping us get this testing in! And sorry for the small stall at the end. It's easy to forget PRs when waiting on the CI. 🙈

@andfoy andfoy deleted the add_py13t_ci branch July 31, 2024 17:26
@andfoy
Copy link
Contributor Author

andfoy commented Jul 31, 2024

Let me open a followup PR to upload nightly wheels

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🤖 type: Infrastructure CI, packaging, tools and automation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants