Skip to content

broken Debian's pypy 2.7 causes virtualenv to fail #1770

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

Closed
stefanor opened this issue Apr 16, 2020 · 10 comments
Closed

broken Debian's pypy 2.7 causes virtualenv to fail #1770

stefanor opened this issue Apr 16, 2020 · 10 comments
Labels

Comments

@stefanor
Copy link
Contributor

Issue

virtualenv 20 doesn't support Debian's pypy (2.7) packages (which I maintain).

I'm completely new to the virtualenv 20 codebase. So, not entirely sure what's going on here. The old version was a horrible mess, but I knew my way around it :)

Environment

Provide at least:

  • OS: Debian unstable
  • pip list of the host python where virtualenv is installed: N/A

Output of the virtual environment creation

Make sure to run the creation with -vvv --with-traceback:

21 setup logging to NOTSET [DEBUG report:43]
31 find interpreter for spec PythonSpec(implementation=pypy) [INFO builtin:44]
32 discover exe for PythonInfo(spec=CPython3.8.2.final.0-64, exe=/home/stefanor/git/virtualenv/.tox/dev/bin/python, platform=linux, version='3.8.2 (default, Apr  1 2020, 15:52:55) \n[GCC 9.3.0]', encoding_fs_io=utf-8-utf-8) in /usr [DEBUG py_info:352]
32 filesystem is case-sensitive [DEBUG info:28]
33 Attempting to acquire lock 140499136607520 on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/df0893f56f349688326838aaeea0de204df53a132722cbd565e54b24a8fec5f6.lock [DEBUG filelock:270]
33 Lock 140499136607520 acquired on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/df0893f56f349688326838aaeea0de204df53a132722cbd565e54b24a8fec5f6.lock [INFO filelock:274]
33 get PythonInfo from /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/df0893f56f349688326838aaeea0de204df53a132722cbd565e54b24a8fec5f6.json for /usr/bin/python3.8 [DEBUG cached_py_info:79]
33 Attempting to release lock 140499136607520 on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/df0893f56f349688326838aaeea0de204df53a132722cbd565e54b24a8fec5f6.lock [DEBUG filelock:315]
34 Lock 140499136607520 released on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/df0893f56f349688326838aaeea0de204df53a132722cbd565e54b24a8fec5f6.lock [INFO filelock:318]
34 proposed PythonInfo(spec=CPython3.8.2.final.0-64, system=/usr/bin/python3.8, exe=/home/stefanor/git/virtualenv/.tox/dev/bin/python, platform=linux, version='3.8.2 (default, Apr  1 2020, 15:52:55) \n[GCC 9.3.0]', encoding_fs_io=utf-8-utf-8) [INFO builtin:50]
34 discover PATH[0]=/home/stefanor/bin [DEBUG builtin:84]
34 discover PATH[1]=/home/stefanor/bin [DEBUG builtin:84]
34 discover PATH[2]=/usr/local/bin [DEBUG builtin:84]
34 discover PATH[3]=/usr/bin [DEBUG builtin:84]
34 Attempting to acquire lock 140499136452496 on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/7930211a46c0ce5bc025cac73adac10e9ad674ccfb0861d555fc87d3b57bbbef.lock [DEBUG filelock:270]
34 Lock 140499136452496 acquired on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/7930211a46c0ce5bc025cac73adac10e9ad674ccfb0861d555fc87d3b57bbbef.lock [INFO filelock:274]
35 remove PythonInfo /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/7930211a46c0ce5bc025cac73adac10e9ad674ccfb0861d555fc87d3b57bbbef.json for /usr/bin/pypy [DEBUG cached_py_info:84]
35 get interpreter info via cmd: /usr/bin/pypy /home/stefanor/git/virtualenv/src/virtualenv/discovery/py_info.py [DEBUG cached_py_info:108]
110 write PythonInfo to /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/7930211a46c0ce5bc025cac73adac10e9ad674ccfb0861d555fc87d3b57bbbef.json for /usr/bin/pypy [DEBUG cached_py_info:94]
111 Attempting to release lock 140499136452496 on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/7930211a46c0ce5bc025cac73adac10e9ad674ccfb0861d555fc87d3b57bbbef.lock [DEBUG filelock:315]
111 Lock 140499136452496 released on /home/stefanor/.local/share/virtualenv/py_info/20.0.18.dev1+g1ed3db1/7930211a46c0ce5bc025cac73adac10e9ad674ccfb0861d555fc87d3b57bbbef.lock [INFO filelock:318]
111 proposed PathPythonInfo(spec=PyPy2.7.13.final.42-64, exe=/usr/bin/pypy, platform=linux2, version='2.7.13 (7.3.1+dfsg-1, Apr 10 2020, 20:05:43)\n[PyPy 7.3.1 with GCC 9.3.0]', encoding_fs_io=UTF-8-None) [INFO builtin:50]
111 accepted PathPythonInfo(spec=PyPy2.7.13.final.42-64, exe=/usr/bin/pypy, platform=linux2, version='2.7.13 (7.3.1+dfsg-1, Apr 10 2020, 20:05:43)\n[PyPy 7.3.1 with GCC 9.3.0]', encoding_fs_io=UTF-8-None) [DEBUG builtin:52]
Traceback (most recent call last):
  File ".tox/dev/bin/virtualenv", line 11, in <module>
    load_entry_point('virtualenv', 'console_scripts', 'virtualenv')()
  File "/home/stefanor/git/virtualenv/src/virtualenv/__main__.py", line 51, in run_with_catch
    run(args, options)
  File "/home/stefanor/git/virtualenv/src/virtualenv/__main__.py", line 20, in run
    session = cli_run(args, options)
  File "/home/stefanor/git/virtualenv/src/virtualenv/run/__init__.py", line 27, in cli_run
    session = session_via_cli(args, options)
  File "/home/stefanor/git/virtualenv/src/virtualenv/run/__init__.py", line 35, in session_via_cli
    parser = build_parser(args, options)
  File "/home/stefanor/git/virtualenv/src/virtualenv/run/__init__.py", line 75, in build_parser
    CreatorSelector(interpreter, parser),
  File "/home/stefanor/git/virtualenv/src/virtualenv/run/plugin/creators.py", line 15, in __init__
    creators, self.key_to_meta, self.describe, self.builtin_key = self.for_interpreter(interpreter)
  File "/home/stefanor/git/virtualenv/src/virtualenv/run/plugin/creators.py", line 40, in for_interpreter
    raise RuntimeError(
RuntimeError: missing required file PathRefToDest(src=/usr/lib/pypy/../../local/lib/pypy2.7/lib-python/os.py) for creators PyPy2Posix

py_info:

{
  "platform": "linux2", 
  "implementation": "PyPy", 
  "pypy_version_info": [
    7, 
    3, 
    1, 
    "final", 
    0
  ], 
  "version_info": {
    "major": 2, 
    "minor": 7, 
    "micro": 13, 
    "releaselevel": "final", 
    "serial": 42
  }, 
  "architecture": 64, 
  "version": "2.7.13 (7.3.1+dfsg-1, Apr 10 2020, 20:05:43)\n[PyPy 7.3.1 with GCC 9.3.0]", 
  "os": "posix", 
  "prefix": "/usr/lib/pypy", 
  "base_prefix": null, 
  "real_prefix": null, 
  "base_exec_prefix": null, 
  "exec_prefix": "/usr/lib/pypy", 
  "executable": "/usr/bin/pypy", 
  "original_executable": "/usr/bin/pypy", 
  "system_executable": "/usr/bin/pypy", 
  "has_venv": false, 
  "path": [
    "/home/stefanor/git/virtualenv/src/virtualenv/discovery", 
    "/usr/lib/pypy/lib_pypy/__extensions__", 
    "/usr/lib/pypy/lib_pypy", 
    "/usr/lib/pypy/lib-python/2.7", 
    "/usr/lib/pypy/lib-python/2.7/lib-tk", 
    "/usr/lib/pypy/lib-python/2.7/plat-linux2", 
    "/usr/local/lib/pypy2.7/dist-packages", 
    "/usr/lib/pypy/dist-packages"
  ], 
  "file_system_encoding": "UTF-8", 
  "stdout_encoding": "UTF-8", 
  "sysconfig_paths": {
    "stdlib": "{base}/../../local/lib/pypy{py_version_short}/lib-python", 
    "platstdlib": "{base}/../../local/lib/pypy{py_version_short}/lib-python", 
    "purelib": "{base}/../../local/lib/pypy{py_version_short}/lib-python", 
    "platlib": "{base}/../../local/lib/pypy{py_version_short}/lib-python", 
    "include": "{base}/../../local/include", 
    "scripts": "{base}/../../local/bin", 
    "data": "{base}/../../local"
  }, 
  "sysconfig_vars": {
    "base": "/usr/lib/pypy", 
    "py_version_short": "2.7", 
    "PYTHONFRAMEWORK": "", 
    "implementation_lower": "python"
  }, 
  "distutils_install": {
    "purelib": "site-packages", 
    "platlib": "site-packages", 
    "headers": "include", 
    "scripts": "bin", 
    "data": ""
  }, 
  "system_stdlib": "/usr/lib/pypy/../../local/lib/pypy2.7/lib-python", 
  "system_stdlib_platform": "/usr/lib/pypy/../../local/lib/pypy2.7/lib-python", 
  "max_size": 9223372036854775807, 
  "_creators": null
}

Presumably this is caused by our virtualenv parsing our deb layout.
To support users installing packages as root, outside a virtualenv, we point the purelib path at /usr/local/lib through the pypy-local sysconfig layout:

https://salsa.debian.org/debian/pypy/-/blob/debian/debian/patches/distutils-install-layout

We do the same thing for PyPy3, but for some reason, virtualenv seems to know about that and handle it correctly.

@stefanor stefanor added the bug label Apr 16, 2020
@gaborbernat gaborbernat added question and removed bug labels Apr 16, 2020
@gaborbernat
Copy link
Contributor

gaborbernat commented Apr 16, 2020

Why are you changing the stdlib entries, not just purelib/platlib? This feels to me like a broken packaging; you're basically pointing the stdlib to a folder that does not exist. Either the stdlib or platstdlib should contain os.py (a standard library module), and at the moment does not hence the error. Furthermore, you're doing overzealous patching here, why patch the sysconfig? distutils is what determines where pip will install packages; tools can freely expect that sysconfig describes the system pythons parameters.

This kind of patching seems the wrong thing to me, if you want to support installing packages without root (which feels to me a bad idea, anyone can install now a bad package with a pth file that will break the system Python 🤔) you should be using the distutils configuration file rather than patching the source code - see https://docs.python.org/3/install/index.html#distutils-configuration-files (works just the same for python 2/ pypy).

PS. Maybe would be nice to review the patches you apply, not sure all of them makes sense to me.

@gaborbernat gaborbernat changed the title virtualenv broken for Debian's pypy 2.7 broken Debian's pypy 2.7 causes virtualenv to fail Apr 16, 2020
@stefanor
Copy link
Contributor Author

Thanks for the reply. I'll admit that I'm a little bit miffed at such hard push-back, and the ticket immediately being closed, with a finger-waving title. There's an issue here we need to resolve, between two systems, and I'd like to have a constructive conversation. So, I'll do my best to do that.

Why are you changing the stdlib entries, not just purelib/platlib

Honestly, this dates back approx 8 years, to when that patch was first written, so I can't remember all of my thinking. But I think I just did that for consistency, and it didn't seem to break anything.

I think it has to be relative to base, for things to still work correctly in the virtualenv. I can tweak things, and see what happens.

The reason I filed this ticket is that everything is just working correctly for our PyPy3 package, with exactly the same patch. So something in virtualenv is treating that differently, and I'm not sure exactly what, so I was asking for your help to understand what's going on.

Either the stdlib or platstdlib should contain os.py (a standard library module), and at the moment does not hence the error.

Fair enough.

Furthermore, you're doing overzealous patching here, why patch the sysconfig? distutils is what determines where pip will install packages; tools can freely expect that sysconfig describes the system pythons parameters.

Two reasons:

  1. sysconfig did not have anything that really matched our installation layout. PyPy essentially uses cpython's local dev layout, and that wasn't really appropriate for a system-wide install.
  2. Following Debian's cpython packaging. If a patch made sense there, there's a good chance it does for PyPy.

I work with the PyPy upstream team, they (at least roughly) know what I'm patching, and I commit whatever makes sense, upstream.

if you want to support installing packages without root (which feels to me a bad idea, anyone can install now a bad package with a pth file that will break the system Python thinking)

Totally agreed that users shouldn't be doing that. Users should almost always use virtualenvs. (That's why this bug exists :) )

However, in the modern container world, there is an argument for installing packages system-wide as root. We should support it.

Also, again, I wanted PyPy on Debian to behave as similarly as possible to cPython.

PS. Maybe would be nice to review the patches you apply, not sure all of them makes sense to me.

I sense a hostility to patches :)

I am, of course, painfully aware of every patch I apply. Patches take effort to maintain, and it has to be weighed against value. But, it is pretty common to need to patch at least something in package, to get it to work well with the rest of the system. We live with it. And try to push everything possible upstream.

you should be using the distutils configuration file rather than patching the source code

Interesting. If I knew about those, I'd forgotten. I'll have a play around with them, and see if they can do what I need.

I would like to maintain alignment with our cPython approach. So, I'll have to talk to the maintainer, too.

@gaborbernat
Copy link
Contributor

gaborbernat commented Apr 16, 2020

I'll admit that I'm a little bit miffed at such hard push-back, and the ticket immediately being closed, with a finger-waving title.

I'm sorry if this came across as harsh worded. Wasn't my intention. I'll admit there is some annoyance over how Debian patches tools, mostly because over the years we've got multiple issues opened by annoyed users due to some of the patches. Users tend to come to us and complain before going to Debian in practice (I sense maybe because it's easier to open issues here than in Debian). For another recent example of this see pre-commit/pre-commit#1383 (comment). I'd very much prefer we don't have to do these rounds by addressing incompatible changes early on, rather than users running into it later. I've personally felt the title was finger-pointing as it implied virtualenv is broken; ideally, I've too would have preferred a less finger-pointing title.

The reason I filed this ticket is that everything is just working correctly for our PyPy3 package, with exactly the same patch.

The reason for this is because of how a virtual environment is created differs massively between Python 2 and Python 3, partially due to Python 3 mandates https://www.python.org/dev/peps/pep-0405/ that makes things much easier. Python 3 working does not necessarily mean the patch is correct, it's more due to Python 3 virtual environment not relying at the moment on what the patch changes.

  1. sysconfig did not have anything that really matched our installation layout. PyPy essentially uses cpython's local dev layout, and that wasn't really appropriate for a system-wide install.

It's ok to patch sysconfig but the patch should describe paths for the system python. I don't think this is the case at the moment, as in this case the systdlib and sysplatlib seems to point to unexisting paths.

However, in the modern container world, there is an argument for installing packages system-wide as root. We should support it.

Wait, I thought you've added the patch so users can install without root, but here you're saying you want to support a behaviour under root?

I sense a hostility to patches :)

From my side there would be no hostility; I would like to understand the reason for the patch because ideally, I'd prefer to offer a way that makes the patch less intrusive to avoid some of such problems.

@stefanor
Copy link
Contributor Author

I'll admit there is some annoyance over how Debian patches tools, mostly because over the years we've got multiple issues opened by annoyed users due to some of the patches. Users tend to come to us and complain before going to Debian in practice (I sense maybe because it's easier to open issues here than in Debian).

Yeah, not the first time that's been an issue. And won't be the last. :( Some bugs are specific to upstream. Some bugs are specific to a distribution (with or without patches being involved). Sometimes the upstream bug-tracker is more active, sometimes the distribution one is (usually that's a sign of a completely dead upstream). The best path here, that I'm aware of, is good communication between distro package maintainers and their upstreams.

We (the Debian Python community) have a team mailing list [email protected] and hang out on IRC in #debian-python on irc.oftc.net. You're welcome, any time.

Developers are probably patching tools for good reasons, to solve problems. They should be able to motivate those changes to the upstream. Maybe the changes are desirable in a distribution, and undesirable upstream.

For another recent example of this see pre-commit/pre-commit#1383 (comment).

Yeah, de-vendoring is a good example of this. From a simplicity and security PoV, we want <=1 version of every thing we carry in a distribution. It means less work to fix security issues (and it's obvious when the work is complete). Distributions bring code together into a coherent system. The ideal there is again, one version of each thing. We don't always achieve that, we need to be pragmatic. But it's a goal.

Upstream code lives in a wild, uncontrolled environment where such things are impossible.

I've personally felt the title was finger-pointing as it implied virtualenv is broken; ideally, I've too would have preferred a less finger-pointing title.

Oops. Fair enough. I was treating this as a regression. Everything has worked for the last 8 years, and now it doesn't any more. I wanted to contribute a patch to fix it and suddenly found myself in a foreign code-base that I need to get by bearings in.

if you want to support installing packages without root (which feels to me a bad idea, anyone can install now a bad package with a pth file that will break the system Python thinking)

However, in the modern container world, there is an argument for installing packages system-wide as root. We should support it.

Wait, I thought you've added the patch so users can install without root, but here you're saying you want to support a behaviour under root?

Oops. Misread you there. Missed the "without".

We want to support all of these things, ideally, in order of recommendation:

  1. virtualenv
  2. Per-user installation (pip install --user), for users on shared systems, without root. Common in academic computing.
  3. Local installation (sudo python setup.py install) which should go into a separate tree to the distribution library packages.

@gaborbernat
Copy link
Contributor

So what's next to get us moving ahead on this? I don't follow IRC myself but if anyone has any questions about repackaging virtualenv I'm always happy to help and answer.

@gaborbernat
Copy link
Contributor

By the way using a distribution configuration could solve all three of those (virtualenv ignores that config) without needing to patch the source code.

@stefanor
Copy link
Contributor Author

Did some testing and changed the sysconfig patch to be a little more accurate (for 2.7 and 3.6).

https://salsa.debian.org/debian/pypy/-/commit/dcff89a62337630f85922415b5173176d09ea026

We still don't have --system-site-packages working, due to pypy's patch that uses distutils in site.py, but I'll patch that back to cpython's codepath.

There's still probably an opportunity to make better use of distutils.cfg, but I think it'll require reverting some of pypy upstream's stdlib patching. I'm going to ignore that avenue for now.

@gaborbernat
Copy link
Contributor

👍 ok. To be fair I wouldn't say the patch is to support virtualenv 20, it would be more accurate to say to fix a previous faulty patch that pointed system stdlib paths to the wrong folders. Any tool using sysconfig stdlib path would be similarly broken.

@gaborbernat
Copy link
Contributor

By the way why would it require reverting pypy upstream patches? Their sysconfig paths are accurate to reflect the pypy layout. Generally distutils.conf is reserved for downstream distribution customisations, as is the case here with Debian.

@stefanor
Copy link
Contributor Author

To be fair I wouldn't say the patch is to support virtualenv 20, it would be more accurate to say to fix a previous faulty patch that pointed system stdlib paths to the wrong folders. Any tool using sysconfig stdlib path would be similarly broken.

Yep, totally agree with that. But that's far more detail than a Debian changelog needs. From an outsider's (e.g. the user)'s point of view, this patch fixed a regression with virtualenv 20, by changing stdlib paths in sysconfig.

By the way why would it require reverting pypy upstream patches?

You probably don't want to know the answer to this question...

It's because of it's interaction with the check for real_prefix in the distutils part of the patch (which originated in cpython packaging that didn't call into distutils here). I know... :(

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

No branches or pull requests

2 participants