Skip to content
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

python module: migrate away from the use of distutils #11133

Merged
merged 6 commits into from
Oct 3, 2023

Conversation

eli-schwartz
Copy link
Member

Fixes #7702

See python/cpython#99942 though.

@codecov
Copy link

codecov bot commented Dec 2, 2022

Codecov Report

Patch coverage: 85.71% and project coverage change: -1.66% ⚠️

Comparison is base (586bd11) 70.33% compared to head (c5b7595) 68.67%.

❗ Current head c5b7595 differs from pull request most recent head 3e20f08. Consider uploading reports for the commit 3e20f08 to get more accurate results

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #11133      +/-   ##
==========================================
- Coverage   70.33%   68.67%   -1.66%     
==========================================
  Files         218      414     +196     
  Lines       48336    88040   +39704     
  Branches    11463    20784    +9321     
==========================================
+ Hits        33996    60463   +26467     
- Misses      11809    23064   +11255     
- Partials     2531     4513    +1982     
Files Changed Coverage Δ
mesonbuild/modules/python.py 73.15% <80.00%> (-5.74%) ⬇️
mesonbuild/scripts/python_info.py 90.19% <90.19%> (+1.55%) ⬆️

... and 399 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@dnicolodi
Copy link
Member

Thanks for the investigative work @eli-schwartz

While considering the issue of determining whether linking to libpython is required, it may be worth checking if we have all the pieces required to support the stable ABI that defines slightly different rules for linking https://peps.python.org/pep-0384/#linkage

I think python_installation.extension_module() could grow a abi kwarg that can be used to request the "stable ABI special treatment": linking with the correct dll on Windows and probably defining the Py_LIMITED_API preprocessor variable appropriately https://docs.python.org/3/c-api/stable.html


with importlib.resources.path('mesonbuild.scripts', 'python_info.py') as f:
cmd = self.get_command() + [f]
p, stdout, stderr = mesonlib.Popen_safe(cmd)
Copy link
Member

Choose a reason for hiding this comment

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

importlib.resources.path() returns pathlib.Path object. The subprocess modules (used under the hood in mesonlib.Popen_safe()) accepts path objects as part of the command line only starting with Python 3.8. This should probably be

cmd = self.get_command() + [os.fspath(f)]

or something similar.

# on versions supporting python-embed.pc, this is the non-embed lib
if sys.version_info >= (3, 8):
variables = sysconfig.get_config_vars()
return bool(variables.get('LIBPYTHON', 'yes'))
Copy link
Member

Choose a reason for hiding this comment

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

Just nitpicking, but

bool(variables.get('LIBPYTHON', True))

seems more idiomatic to me.

Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately this does not work on PyPy. On PyPy 3.9 and 3.10 sysconfig.get_config_vars() does not contain a LIBPYTHON key, thus .get() returns the default value also on Linux and macOS. Either the fallback to distutils is applied for Python versions up to 3.10, or it is also made conditional on the interpreter being PyPy.

Copy link
Member

Choose a reason for hiding this comment

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

The current code assumes that if the LIBPYTHON variable is not present, the code is being executed on Windows, and thus libpython needs to be linked. Therefore, another possibility would be to do not make this assumption and make the check explicit by replacing

return bool(variables.get('LIBPYTHON', 'yes'))

with

return sys.platform == 'win32' or bool(variables.get('LIBPYTHON'))

Copy link
Member Author

Choose a reason for hiding this comment

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

Do we know what PyPy did across various different historic versions?

Copy link
Member

Choose a reason for hiding this comment

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

I've tested only pypy3.9 and pypy3.10

Copy link
Contributor

Choose a reason for hiding this comment

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

So how are consumers meant to know the name of the import lib and its path on windows? The discussion at the top of the sysconfig rewrite suggests

Is a shared libpython available? What's its name? Location?

  • definition: Yes if built with --enable-shared
  • currently:
    • (available?) bool(sysconfig.get_config_var('LDLIBRARY'))
    • (name?) sysconfig.get_config_var('LDLIBRARY')
    • (location?) sysconfig.get_config_var('LIBDIR')

but I don't think reality concurs with the "currently" status. Or maybe I don't understand the intent.

Copy link
Member

Choose a reason for hiding this comment

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

Please note that the variable names in the text you quote from the CPython PR and the one used here are different, thus I'm not sure how that fits here. A bit of magic goes into obtaining the library name to use and its location. This is because there isn't a reliable way to get that information that works on all architectures across all versions of Python supported by Meson. This is unfortunate and a bit more complex that what is inferred from the CPython PR you link to.

I don't think that the quirks of how the information required for building extension modules can be retrieved from a CPython installation are nice or the best way things could be, but having all Python implementation implement the same interface helps the consumers of such information. At the same time, there is no fixing for the already released PyPy versions, thus Meson will need to find a way to work with what is out there.

Copy link
Member Author

Choose a reason for hiding this comment

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

So how are consumers meant to know the name of the import lib and its path on windows? The discussion at the top of the sysconfig rewrite suggests

You are quite right, CPython doesn't really expose this information at all for Visual Studio solutions file builds and that is partially because those builds are a really odd duck in a whole bunch of ways and partially because there isn't really an official way to get this information at all.

The sysconfig variables we're discussing here are the best we have and they don't really work at all on Windows...

... which is why I suggested that once we are adding stuff manually to PyPy's sysconfigdata, it might be good to go the extra mile and provide this information even on Windows, where CPython doesn't provide it. :D

Copy link
Member

Choose a reason for hiding this comment

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

but I don't think reality concurs with the "currently" status. Or maybe I don't understand the intent.

It tries to show how people generally get that value at the moment, not to suggest how they should do it. It is also very Posix-centric as that's my background and that issue was not meant to be an extensive document — just help to paint the picture. PEP 720 OTOH tries to do that, but it still lacks a lot of information.

This PR is a good example of why the current way is problematic, which is what we want to fix.

So how are consumers meant to know the name of the import lib and its path on windows?

I don't think there is a portable/reliable way, unfortunately.

python/cpython#110049 will help with that, but only on >=3.13.

Copy link
Contributor

Choose a reason for hiding this comment

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

One of the advantanges of PyPy is that I can make changes that will be available in a nightly build of python3.9/3.10, and these can be easily tried out in CI. So if the direction in PR 10049 looks correct, I can add that to PyPy so that, at least on PyPy, meson could get the values from the installed python.

dnicolodi referenced this pull request in mesonbuild/meson-python Dec 21, 2022
dnicolodi added a commit to dnicolodi/meson-python that referenced this pull request Feb 14, 2023
Meson up to version 1.0.0 uses distuitls for introspection of the
Python interpreter it is compiling for. distutils has been removed
from the Python standard library in version 3.12. distutils has been
effectively embedded into setuptools thus depending on setuptools
works around the issue.

See mesonbuild/meson#11133.
dnicolodi added a commit to dnicolodi/meson-python that referenced this pull request Feb 14, 2023
Meson up to version 1.0.0 uses distuitls for introspection of the
Python interpreter it is compiling for. distutils has been removed
from the Python standard library in version 3.12. distutils has been
effectively embedded into setuptools thus depending on setuptools
works around the issue.

See mesonbuild/meson#11133.
@@ -58,10 +58,15 @@ def get_install_paths():
return paths, install_paths

def links_against_libpython():
from distutils.core import Distribution, Extension
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand the reference to "embed" in the commit message. The code using distutils probes the build_ext command, which AFAIU doesn't have anything to do with embedding python.

Copy link
Member Author

@eli-schwartz eli-schwartz Feb 16, 2023

Choose a reason for hiding this comment

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

Right, this is the non-embed probe. The embed: false probe.

That could indeed use rewording.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that is what I meant. I didn't understand what "stop using distutils "embed" probe" in the commit message means, because AFAICT, this does not probe for the build options to embed Python.

@dnicolodi
Copy link
Member

I tried to get some attention to the Python side of the issue https://discuss.python.org/t/building-extensions-modules-in-a-post-distutils-world/23938

@dnicolodi
Copy link
Member

@eli-schwartz Things are moving on the Python side. Now that we have some attention from the Python developers, is there anything else other than the two PRs you already opened on cpython that is needed to remove the dependency on distutils at least for Python 3.12 and later?

@eli-schwartz
Copy link
Member Author

Thanks for getting the ball rolling!

I would still like to make the sysconfig API able to expose the information we are grabbing via heuristics, but that's no longer a merge blocker, just a "good FOSS citizen" thing.

@dnicolodi
Copy link
Member

My understanding is that there is no CPython core developer with the interest and the knowledge to get the required information into a consistent sysconfig API. The only way to get that is to propose patches to sysconfig. Once this PR and #11392 are merged I can work into refactoring some of heuristic into patches for sysconfig (I prefer to wait for thise patches to land because they clean up the implementation quite a bit and they make this job easier). However, Meson would not be able to depend on the improvements on sysconfig for a very long time, thus this would not have high priority.

FFY00 pushed a commit to mesonbuild/meson-python that referenced this pull request Mar 1, 2023
Meson up to version 1.0.0 uses distuitls for introspection of the
Python interpreter it is compiling for. distutils has been removed
from the Python standard library in version 3.12. distutils has been
effectively embedded into setuptools thus depending on setuptools
works around the issue.

See mesonbuild/meson#11133.
mesonbuild/scripts/python_info.py Show resolved Hide resolved
mesonbuild/scripts/python_info.py Show resolved Hide resolved
@FFY00
Copy link
Member

FFY00 commented Mar 1, 2023

My understanding is that there is no CPython core developer with the interest and the knowledge to get the required information into a consistent sysconfig API.

I am working on this.

I would still like to make the sysconfig API able to expose the information we are grabbing via heuristics, but that's no longer a merge blocker, just a "good FOSS citizen" thing.

Which information specifically?

@dnicolodi
Copy link
Member

The release date for Python 3.12 is approaching. Having this merged and have a Meson release before then would be great. @eli-schwartz I can push this through, if you like.

@dnicolodi
Copy link
Member

@eli-schwartz You mentioned having a rebase of this on master. Can you please push it? I would like to test it in mesonbuild/meson-python#492 instead of using my own rebase. Thanks

@dnicolodi
Copy link
Member

@eli-schwartz It took you less than one hour to close my PR with a rebase of this work. You said that you already have the rebases commits and that if I wanted this PR to be rebased I should have simply asked. I've asked multiple times now. It is fine if you don't want to work on this anymore, but I don't understand why you don't want anyone else to work on it either.

@eli-schwartz
Copy link
Member Author

I'd originally hoped to move this forward a long time ago, sorry for the lateness. In the interest of ensuring that this PR can have smooth sailing, I'd like to clarify the goals of the PR, and what is and is not on-topic for it.

In particular, the PR topic is "migrate away from the use of distutils", and strictly speaking, that is all It is intended to do -- take existing code, and port it over, bug for bug, from deprecated APIs onto their non-deprecated equivalent APIs.

(Along the way, I have also staged some generic cleanups for the python module, which are however not the purpose of the PR, merely additional extras.)

What this means is that it is on-topic for this PR, to discuss:

  • matters pertaining to effecting a distutils-to-sysconfig migration in a bug-for-bug manner
  • any additional changes present in the PR can be reviewed for their effectiveness in accomplishing the goals that the commits in question propose to accomplish

Not on-topic for this PR:

  • harping on unrelated bits of functionality that simply happen to share the same python module, or utilize the results of the information that is being bug-for-bug ported from distutils to sysconfig

Obviously, meson is happy to hear your (rhet.) bug reports about any topic at all, whatsoever, which is about "meson". It doesn't have to have anything to do with a particular topic. But if you do so, it had better receive its own distinct bug report. Where it will be, crucially, on-topic.

No useful purpose is served in bogging down a PR that is about bug-for-bug porting of distutils to sysconfig, with tangents that are not blockers for bug-for-bug porting of distutils to sysconfig. Nor in wearing out maintainers who just wanted to fix bugs that run contrary to the intentions of the code ("the thing failed with a ModuleNotFoundError" is a heck of a bug, yes?), did not want to be sidetracked into significantly impactful, wide-ranging policy decisions, and decide their time is better spent doing more enjoyable things.

Again, apologies for the lateness and the lack of communication. Thank you for your patience. <3

Let's see if we can get the ball rolling and merged for python 3.12 final release.

dnicolodi added a commit to dnicolodi/meson-python that referenced this pull request Sep 22, 2023
@dnicolodi
Copy link
Member

Python 3.12 has been released today https://www.python.org/downloads/release/python-3120/ To avoid a flood of bug reports it would be great if this PR could be finished and a new meson release cut ASAP. There are a few ways to solve the PyPy issue. The most conservative is to switch away from the distutils probe for Python 3.12 onward only. Anyhow, any of the proposed fix would work.

eli-schwartz and others added 4 commits October 2, 2023 18:15
…distutils

We do not use setuptools for anything, and only lightly use distutils.
Unpredictable issues can occur due to setuptools monkey-patching, which
interferes with our intended use. Tell setuptools to simply never get
involved.

Note: while it's otherwise possible to check if the probe is run using
sys.executable and avoid forking, setuptools unconditionally injects
itself at startup in a way that requires subprocess isolation to
disable.
Since 3.10.3, Debian finally started patching sysconfig with custom
paths, instead of just distutils. This means we can now go use that
instead. It reduces our reliance on the deprecated distutils module.

Partial fix for mesonbuild#7702
…nt python

On python >=3.8, this information is expected to be encoded in the
sysconfig vars.

In distutils, it is always necessary to link to libpython on Windows;
for posix platforms, it depends on the value of LIBPYTHON (which is the
library to link to, possibly the empty string) as generated by
configure.ac and embedded into python.pc and python-config.sh, and then
coded a second time in the distutils python sources.

There are a couple of caveats which have ramifications for Cygwin and
Android:

- python.pc and python-config.sh disagree with distutils when python is
  not built shared. In that case, the former act the same as a shared
  build, while the latter *never* links to libpython

- python.pc disagrees with python-config.sh and distutils when python is
  built shared. The former never links to libpython, while the latter do

The disagreement is resolved in favor of distutils' behavior in all
cases, and python.pc is correct for our purposes on python 3.12; see:
python/cpython#100356
python/cpython#100967

Although it was not backported to older releases, Cygwin at least has
always patched in a fix for python.pc, which behavior is now declared
canonical. We can reliably assume it is always correct.

This is the other half of the fix for mesonbuild#7702
@eli-schwartz
Copy link
Member Author

There are a few ways to solve the PyPy issue. The most conservative is to switch away from the distutils probe for Python 3.12 onward only.

I opted instead to try handling it by using the previous logic not just on cpython <=3.7, but also on pypy-any-version. Please take a look.

dnicolodi added a commit to dnicolodi/meson-python that referenced this pull request Oct 2, 2023
dnicolodi added a commit to dnicolodi/meson-python that referenced this pull request Oct 2, 2023
@dnicolodi
Copy link
Member

I opted instead to try handling it by using the previous logic not just on cpython <=3.7, but also on pypy-any-version. Please take a look.

Thanks @eli-schwartz It looks good to me. I've run the changes through the meson-python CI and everything works as expected also on PyPy.

I think this can be merged. The pylint error is clearly unrelated.

@eli-schwartz
Copy link
Member Author

I submitted the pylint error as pylint-dev/pylint#9095 since it seems to be incorrectly detected.

Testing the correctness of the `modules: ` kwarg can be done with other
guaranteed stdlib modules that are even more guaranteed since they
didn't get deprecated for removal.
meson itself runs okay on 3.12, and the last issue for *probing* against
3.12 is solved. Tests pass here locally.
@dnicolodi
Copy link
Member

@eli-schwartz I think this the "work in progress" status may be removed now

@dcbaker dcbaker marked this pull request as ready for review October 3, 2023 15:07
@dcbaker dcbaker requested a review from jpakkane as a code owner October 3, 2023 15:07
Copy link
Member

@dcbaker dcbaker left a comment

Choose a reason for hiding this comment

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

I've looked this over, and while there's sime minor things that could be cleaner (I think the scheme code could be compacted a bit), this has been sitting a long time and is needed to get python 3.12 working reliably.

@dnicolodi
Copy link
Member

is needed to get python 3.12 working reliably

Well, it is needed to get Python 3.12 working at all.

@eli-schwartz
Copy link
Member Author

Well, it is needed to get Python 3.12 working at all.

pip install setuptools has entered the chat

Okay okay I jest. In fact, that won't even work in some cases like spack, because... that only works when setuptools is in a site-packages directory, not when it is added via $PYTHONPATH. Those .pth files, huh.

@eli-schwartz eli-schwartz merged commit 880f212 into mesonbuild:master Oct 3, 2023
33 of 34 checks passed
@eli-schwartz eli-schwartz deleted the pep632 branch October 3, 2023 15:30
@eli-schwartz eli-schwartz modified the milestones: 1.3.0, 1.2.3 Oct 3, 2023
@eli-schwartz
Copy link
Member Author

Marking for the point release milestone. This is needed ASAP for python 3.12, and I think it has very low chance of regression ;) as its effects are essentially entirely within the introspection script.

@dnicolodi
Copy link
Member

pip install setuptools has entered the chat

Sure, that works (with the caveats you highlighted) but then meson would need to depend on setuptools when installed on Python 3.12 (which is doable and is what meson-python has been doing for a while now).

@eli-schwartz
Copy link
Member Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PEP 632: Proposed deprectation of distutils
7 participants