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

Isolated build environment is corrupted on Homebrew Python 3.10 without active virtual environment #11539

Closed
1 task done
RazerM opened this issue Oct 24, 2022 · 17 comments · Fixed by #11598
Closed
1 task done
Labels
project: <downstream> When the cause/effect is related to redistributors type: bug A confirmed bug or unintended behavior

Comments

@RazerM
Copy link

RazerM commented Oct 24, 2022

Description

I experienced the following error in a CI environment

  Getting requirements to build wheel: started
  Running command Getting requirements to build wheel
  Error in sitecustomize; set PYTHONVERBOSE for traceback:
  AssertionError:
  Traceback (most recent call last):
    File "/usr/local/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 351, in <module>
      main()
    File "/usr/local/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 333, in main
      json_out['return_val'] = hook(**hook_input['kwargs'])
    File "/usr/local/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 118, in get_requires_for_build_wheel
      return hook(config_settings)
    File "/usr/local/lib/python3.10/site-packages/setuptools/build_meta.py", line 338, in get_requires_for_build_wheel
      return self._get_build_requires(config_settings, requirements=['wheel'])
    File "/usr/local/lib/python3.10/site-packages/setuptools/build_meta.py", line 320, in _get_build_requires
      self.run_setup()
    File "/usr/local/lib/python3.10/site-packages/setuptools/build_meta.py", line 335, in run_setup
      exec(code, locals())
    File "<string>", line 2, in <module>
  ModuleNotFoundError: No module named 'skbuild'
  error: subprocess-exited-with-error
  
  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> See above for output.

Since I know from the pip install -v log above this truncated output that skbuild was definitely picked up from pyproject.toml and installed, it's clear that the assertion error prevents the skbuild package from being found.

Enabling PYTHONVERBOSE=1:

  Adding directory: '/usr/local/lib/python3.10/site-packages'
  Processing .pth file: '/usr/local/lib/python3.10/site-packages/distutils-precedence.pth'
  Traceback (most recent call last):
    File "/usr/local/Cellar/python@3.10/3.10.8/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site.py", line 549, in execsitecustomize
      import sitecustomize
    File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
    File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
    File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
    File "<frozen importlib._bootstrap_external>", line 883, in exec_module
    File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
    File "/private/var/folders/cb/yqyhmsb136xcxm64h9vrswcw0000gn/T/pip-build-env-f7sm6np4/site/sitecustomize.py", line 22, in <module>
      assert not path in sys.path
  AssertionError

I see that it comes from

assert not path in sys.path

This is not my project so I don't know if this is something wrong with the package itself, the CI environment where this happens, or something else.

Changing the CI job to use a venv gets rid of the issue (which is of course a good idea anyway), but it would be good to understand why this assertion is in place and what I should do to fix it.

I think the message should at least include path and sys.path, which I could open a PR for, but I would also like some context from someone familiar with the implementation about what one should do in this case.

Expected behavior

The AssertionError would include actionable information

pip version

22.3

Python version

3.10.8

OS

macOS 10.14.6

How to Reproduce

I don't yet know why this happens, and since this issue about improving the message I think it's ok.

Output

No response

Code of Conduct

@RazerM RazerM added S: needs triage Issues/PRs that need to be triaged type: bug A confirmed bug or unintended behavior labels Oct 24, 2022
@RazerM
Copy link
Author

RazerM commented Oct 24, 2022

FWIW /usr/local/lib/python3.10/site-packages is the path which is also the last item in sys.path

@uranusjr
Copy link
Member

How did you install the Python 3.10.8? The assert checks that a path is not in sys.path (because the environment is supposed to be isolated) but something in the installation is breaking the promise. There’s likely some configuration issues.

@RazerM
Copy link
Author

RazerM commented Oct 24, 2022

brew

@dnicolodi
Copy link
Contributor

dnicolodi commented Nov 14, 2022

I'm hitting the same issue. This happens only with Python 3.9 and 3.10 installed via Homebrew. It works on earlier Python versions. I'm debugging the issue but the first actionable thing could be to make the assertion error more verbose and detailed: understanding that something is going wrong with how pip setups the build venv took me many hours of head scratching.

Mostly for reference to other maybe bumping into the same problem, the symptoms look like this:

$ python3.10 -m pip install -vv .[test]
Using pip 22.3.1 from /usr/local/lib/python3.10/site-packages/pip (python 3.10)
Non-user install because site-packages writeable
Created temporary directory: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-tracker-lvays4wb
Initialized build tracking at /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-tracker-lvays4wb
Created build tracker: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-tracker-lvays4wb
Entered build tracker: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-tracker-lvays4wb
Created temporary directory: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-install-e_f9sj5i
Created temporary directory: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-ephem-wheel-cache-m8k1pvpm
Processing /Users/runner/work/meson-python/meson-python
  Added file:///Users/runner/work/meson-python/meson-python to build tracker '/private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-tracker-lvays4wb'
  Running command pip subprocess to install build dependencies
  Created temporary directory: /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-env-zpp5cr1c
  Installing build dependencies: started
  Using pip 22.3.1 from /usr/local/lib/python3.10/site-packages/pip (python 3.10)
  Ignoring typing-extensions: markers 'python_version < "3.8"' don't match your environment
  Collecting meson>=0.63.3
    Downloading meson-0.64.0-py3-none-any.whl (895 kB)
       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 895.2/895.2 kB 21.6 MB/s eta 0:00:00
  Collecting ninja
    Downloading ninja-1.11.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl (270 kB)
       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 270.7/270.7 kB 22.7 MB/s eta 0:00:00
  Collecting pyproject-metadata>=0.5.0
    Downloading pyproject_metadata-0.6.1-py3-none-any.whl (7.4 kB)
  Collecting tomli>=1.0.0
    Downloading tomli-2.0.1-py3-none-any.whl (12 kB)
  Collecting packaging>=19.0
    Downloading packaging-21.3-py3-none-any.whl (40 kB)
       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.8/40.8 kB 5.9 MB/s eta 0:00:00
  Collecting pyparsing!=3.0.5,>=2.0.2
    Downloading pyparsing-3.0.9-py3-none-any.whl (98 kB)
       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 98.3/98.3 kB 13.7 MB/s eta 0:00:00
  Installing collected packages: ninja, tomli, pyparsing, meson, packaging, pyproject-metadata
    Creating /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-env-zpp5cr1c/overlay/bin
    changing mode of /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-env-zpp5cr1c/overlay/bin/ninja to 755
    changing mode of /private/var/folders/24/8k48jl6d249_n_qfxwsl6xvm0000gn/T/pip-build-env-zpp5cr1c/overlay/bin/meson to 755
  Successfully installed meson-0.64.0 ninja-1.11.1 packaging-21.3 pyparsing-3.0.9 pyproject-metadata-0.6.1 tomli-2.0.1
  Running command Getting requirements to build wheel
  Installing build dependencies: finished with status 'done'
  Getting requirements to build wheel: started
  Error in sitecustomize; set PYTHONVERBOSE for traceback:
  AssertionError:
  Getting requirements to build wheel: finished with status 'done'
ERROR: Exception:
Traceback (most recent call last):
  [ImportError because of missing build dependency folllows]

where the interesting part is:

  Error in sitecustomize; set PYTHONVERBOSE for traceback:
  AssertionError:

Setting PYTHONVERBOSE=1 produces the expected torrent of debug messages, but it does not add much to the AssertionError.

@uranusjr
Copy link
Member

uranusjr commented Nov 14, 2022

You might want to reach out to maintainers of Homebrew’s Python formula to get a better idea about the issue. It only happens to their Python installation so it’s likely something wrong with how they build or configurate it.

@dnicolodi
Copy link
Contributor

The sitecustomize.py generated by pip for its isolated environment looks like this with Homebrew's Python 3.10 when not using a virtual environment:

import os, site, sys

# First, drop system-sites related paths.
original_sys_path = sys.path[:]
known_paths = set()
for path in {'/usr/local/lib/python3.10/site-packages'}:
    site.addsitedir(path, known_paths=known_paths)
system_paths = set(
    os.path.normcase(path)
    for path in sys.path[len(original_sys_path):]
)
original_sys_path = [
    path for path in original_sys_path
    if os.path.normcase(path) not in system_paths
]
sys.path = original_sys_path

# Second, add lib directories.
# ensuring .pth file are processed.
for path in ['/usr/local/lib/python3.10/site-packages', '/usr/local/lib/python3.10/site-packages']:
    assert not path in sys.path
    site.addsitedir(path)

The assertion in there obviously fails.

@uranusjr
Copy link
Member

Quoting myself above:

The assert checks that a path is not in sys.path (because the environment is supposed to be isolated) but something in the installation is breaking the promise.

The assertion is doing what it is supposed to do, those entries being in there is the problem.

@dnicolodi
Copy link
Contributor

If I read it correctly, the first part of the generated sitecustomize.py removes the system paths from sys.path. The second path then checks that its job correctly. Apparently something fails. I am not yet saying that this is a bug in pip. Since getting to the generated sitecustomize.py is not trivial I wanted to record it here to aid other in understanding what is going on.

@dnicolodi
Copy link
Contributor

dnicolodi commented Nov 14, 2022

Oh, and by the way, adding a thing twice to a list while checking that it is not already there, rarely succeeds.

The last three lines of that sitecustomize.py do the equivalent of

foo = []
for path in ['/usr/local/lib/python3.10/site-packages', '/usr/local/lib/python3.10/site-packages']:
    assert not path in foo
    foo.append(path)

Deduplicating the list solves the problem.

@uranusjr
Copy link
Member

The logic works everywhere except Homebrew’s Python installation, some I’m inclined to say the responsibility to correct this likely lies in Homebrew. pip can modify the heuristic to better support whatever Homebrew is doing, if we can have insight first on what Homebrew is doing with its Python installation (that nobody else is), why it’s doing it, whether it’s a correct thing (if it’s not, maybe fix that instead), and how pip can help without compromising what we (both sides) are trying to achieve.

@uranusjr uranusjr added project: <downstream> When the cause/effect is related to redistributors and removed S: needs triage Issues/PRs that need to be triaged labels Nov 14, 2022
@dnicolodi
Copy link
Contributor

dnicolodi commented Nov 14, 2022

Following the code that generates pip's sitecustomize.py I've found that the cause of the issue is that on Homebrew's Python sysconfig.get_paths(expand=False)['purelib'] returns /usr/local/lib/python{py_version_short}/site-packages. pip expects this path to be resolved relative to a {base} variable, see pip._internal.locations._sysconfig.get_prefixed_libs().

I'm trying to understand why this works as expected in a virtual environment.

@dnicolodi
Copy link
Contributor

dnicolodi commented Nov 14, 2022

@uranusjr Maybe I'm off the wrong track, but shouldn't pip pass scheme='venv' to sysconfig.get_path() in pip._internal.locations._sysconfig.get_prefixed_libs()? What it is trying to do is after all to setup the equivalent of a virtual environment.

Changing this fixes the issue for me:

diff --git a/src/pip/_internal/locations/_sysconfig.py b/src/pip/_internal/locations/_sysconfig.py
index 0bbc9283d..e1071006a 100644
--- a/src/pip/_internal/locations/_sysconfig.py
+++ b/src/pip/_internal/locations/_sysconfig.py
@@ -214,5 +214,5 @@ def get_platlib() -> str:


 def get_prefixed_libs(prefix: str) -> typing.Tuple[str, str]:
-    paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix})
+    paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix}, scheme="venv")
     return (paths["purelib"], paths["platlib"])

With Homebrew's Python the default installation scheme (outside a virtualenv) is osx_framework_library which is defined as

    'osx_framework_library': {
        'stdlib': '{installed_base}/{platlibdir}/python{py_version_short}',
        'platstdlib': '{platbase}/{platlibdir}/python{py_version_short}',
        'purelib': '/usr/local/lib/python{py_version_short}/site-packages',
        'platlib': '/usr/local/{platlibdir}/python{py_version_short}/site-packages',
        'include': '{installed_base}/include/python{py_version_short}{abiflags}',
        'platinclude': '{installed_platbase}/include/python{py_version_short}{abiflags}',
        'scripts': '/usr/local/bin',
        'data': '/usr/local',
        },

@uranusjr
Copy link
Member

Judging from the prefix you showed (/usr/local/lib), you are not in a virtual environment, so pip should not use venv.

Also, the venv scheme is not in Python 3.10, so it should not be relevant? https://github.com/python/cpython/blob/3.10/Lib/sysconfig.py

@dnicolodi
Copy link
Contributor

pip._internal.locations._sysconfig.get_prefixed_libs() is used (maybe among other things) to constructs the entries added to sys.path at the end of the generated sitecustomize.py for an isolated environment. If the environment has to be isolated, they cannot be the system ones. The default installation scheme for Homebrew's Python when not in a virtual environment is osx_framework_library which as in the snippet above, has absolute paths for platlib and purelib. When pip constructs these paths for the isolated environment it therefore ends up with system paths. And everything break.

Homebrew has the venv scheme also in Python 3.10. And if I patch pip as in the diff above, this issue is solved.

@uranusjr
Copy link
Member

But the scheme is not available anywhere else (on 3.10) so we can’t do that. But since that would be available in 3.11, we can adopt it conditionally, and “incidentally” resolve the issue for Homebrew. Would you be able to file a pull request for this? We can work out the details from there.

@dnicolodi
Copy link
Contributor

Sure. Let me double check that this solves my CI issues and then I'll come up with a PR.

@dnicolodi
Copy link
Contributor

dnicolodi commented Nov 14, 2022

Great Scott! This fixes Python 3.10 but not 3.11.

Edit: it was an operator error. The patch above solves the problem for both Python 3.10 and 3.11. I'm preparing a PR.

dnicolodi added a commit to dnicolodi/pip that referenced this issue Nov 15, 2022
get_prefixed_libs() computes the Python path for libraries in a pip
isolation environment. Python 3.11 introduced the "venv" path scheme
to be used in these cases. Use it if available.

This solves a bug on Homebrew's Python 3.10 and later where the
default paths scheme when Python is invoked outside a virtual
environment is "osx_framework_library" and does not relative to the
"{base}" or "{platbase}" variables.

Fixes pypa#11539.
dnicolodi added a commit to dnicolodi/pip that referenced this issue Nov 15, 2022
get_prefixed_libs() computes the Python path for libraries in a pip
isolation environment. Python 3.11 introduced the "venv" path scheme
to be used in these cases. Use it if available.

This solves a bug on Homebrew's Python 3.10 and later where the
default paths scheme when Python is invoked outside a virtual
environment is "osx_framework_library" and does not relative to the
"{base}" or "{platbase}" variables.

Fixes pypa#11539.
dnicolodi added a commit to dnicolodi/pip that referenced this issue Nov 15, 2022
get_prefixed_libs() computes the Python path for libraries in a pip
isolation environment. Python 3.11 introduced the "venv" path scheme
to be used in these cases. Use it if available.

This solves a bug on Homebrew's Python 3.10 and later where the
default paths scheme when Python is invoked outside a virtual
environment is "osx_framework_library" and does not relative to the
"{base}" or "{platbase}" variables.

Fixes pypa#11539.
dnicolodi added a commit to dnicolodi/pip that referenced this issue Nov 15, 2022
get_prefixed_libs() computes the Python path for libraries in a pip
isolation environment. Python 3.11 introduced the "venv" path scheme
to be used in these cases. Use it if available.

This solves a bug on Homebrew's Python 3.10 and later where the
default paths scheme when Python is invoked outside a virtual
environment is "osx_framework_library" and does not relative to the
"{base}" or "{platbase}" variables.

Fixes pypa#11539.
dnicolodi added a commit to dnicolodi/pip that referenced this issue Nov 15, 2022
get_prefixed_libs() computes the Python path for libraries in a pip
isolation environment. Python 3.11 introduced the "venv" path scheme
to be used in these cases. Use it if available.

This solves a bug on Homebrew's Python 3.10 and later where the
default paths scheme when Python is invoked outside a virtual
environment is "osx_framework_library" and does not relative to the
"{base}" or "{platbase}" variables.

Fixes pypa#11539.
@uranusjr uranusjr changed the title Blank AssertionError in pip-generated sitecustomize.py Isolated build environment is corrupted on Homebrew Python 3.10 without active virtual environment Nov 16, 2022
dnicolodi added a commit to dnicolodi/pip that referenced this issue Nov 16, 2022
get_prefixed_libs() computes the Python path for libraries in a pip
isolation environment. Python 3.11 introduced the "venv" path scheme
to be used in these cases. Use it if available.

This solves a bug on Homebrew's Python 3.10 and later where the
default paths scheme when Python is invoked outside a virtual
environment is "osx_framework_library" and does not relative to the
"{base}" or "{platbase}" variables.

Fixes pypa#11539.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
project: <downstream> When the cause/effect is related to redistributors type: bug A confirmed bug or unintended behavior
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants