-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Run pip in isolated env by building zip #9689
Conversation
96c02ff
to
f53ccfb
Compare
In get-pip, we do some clever stuff to extract the certificate bundle, because openssl needs it to be an actual file. Is that likely to be a problem here? I've never actually had an issue where that was necessary myself, so I don't know why it's important, maybe someone who worked on get-pip would know? |
There are some tests failing due to unexpected temp files at |
Ah, looks like certifi these days uses Which probably also means we can simplify the get-pip bootstrap, and maybe even just ship pip as a pyz for people who want that... |
I read the implementation and unfortunately the logic only works on Python 3.7. So unless we add |
I tried simply copying the whole tree into a temp directory, the overhead is about twice as much (slightly under 2s per distribution), which could be a bit much? Since we already have a real-file certificate bundle (in the original installation), I’m thinking we probably can just point |
That sounds like a better idea. I'm not keen on a 2s overhead, by the time you start adding in things like virus checkers, slow non-SSD disks, temp on NFS disks, old hardware, etc, these things get a lot worse than we expect, very fast 🙁 |
Huh, the test failures are about forking bomb pervention. It seems to me (I’m not very familiar with this part of pip and may be wrong) the requirement tracker is not correctly communicated between the parent and child pip, but can’t tell why making the child a zip would affect this 😞 |
I managed to fix the fork bomb, but the tests are still failing due to unexpected temp files. This is because I think the only way to work around this is to patch |
It’s ugly, but it’s working. |
Hmm... I wonder if a better mechanism would be to have a variable somewhere that we patch for the child process? That way we can have a branch that does things only in the subprocess. |
So you're suggesting that the actual pip code carries a vendor patch here? That sounds reasonable, we could patch certifi to recognise when it's being run under an isolated pip. Maybe something like replacing try:
from importlib.resources import path as get_path, read_text
...
except ImportError:
... with class EarlyExit(Exception):
"""Hack to do an early exit from the try-except"""
try:
if "PIP__ISOLATED__CERT__" in os.environ:
def where():
return os.environ["PIP__ISOLATED__CERT__"]
raise EarlyExit
from importlib.resources import path as get_path, read_text
...
except ImportError:
...
except EarlyExit:
pass The Edit: Use an environment variable, so we don't need to hack pip's main function. |
I'd even start the environment variable with an underscore, to make it clearer that it's not meant for "external users". |
Sounds like a good idea, let me try this out. |
Tests seem to pass (finally). |
@@ -190,8 +234,9 @@ def install_requirements( | |||
args.append('--prefer-binary') | |||
args.append('--') | |||
args.extend(requirements) | |||
extra_environ = {"_PIP_STANDALONE_CERT": where()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works, and I'm not suggesting that you try updating this PR, but I do wonder: can we use the --cert option that we have on the CLI to achieve this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works in practice, but some tests will fail because requests
always triggers temp file creation even when the default cert is never used. The environ + certifi patch solution is the only way I can think of to prevent that temp file from being created entirely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I managed to fix the fork bomb, but the tests are still failing due to unexpected temp files. This is because requests calls certifi.where() unconditionally, and a temporary file is created to read the cert bundle even if we pass in --cert explicitly.
From #9689 (comment) in this thread
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming that there's a reason we can't just mark these as "expected temp files" in the test suite, or a reason why the temp files are bad in real usage - and we're not introducing a complicated hack just to cover over a test issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are tests on temp files being correctly cleaned up after build, and allowing temp files would defeat their purpose. Another way to do this is to ensure the copied cert file is also cleaned up by the PEP 517 process, but that would require even more patching in certifi.
Hello! I am an automated bot and I have noticed that this pull request is not currently able to be merged. If you are able to either merge the |
The standalone pip doesn't have a correct sys.path when __main__.py is invoked, and we'd be patching the wrong certifi at that point. The sys.path is guaranteed to be correct when __init__.py is loaded (otherwise we wouldn't be able to import it in the first place).
The following error happens on my mac m1 when building docker image for system tests. Collecting pynacl Using cached PyNaCl-1.4.0.tar.gz (3.4 MB) Installing build dependencies ... error ERROR: Command errored out with exit status 1: command: /usr/bin/python3 /usr/local/lib/python3.8/dist-packages/pip install --ignore-installed --no-user --prefix /tmp/pip-build-env-k867aac0/overlay --no-warn-script-location --no-binary :none: --only-binary :none: -i https://pypi.org/simple -- 'setuptools>=40.8.0' wheel 'cffi>=1.4.1; python_implementation != '"'"'PyPy'"'"'' cwd: None Complete output (14 lines): Traceback (most recent call last): File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main return _run_code(code, main_globals, None, File "/usr/lib/python3.8/runpy.py", line 87, in _run_code exec(code, run_globals) File "/usr/local/lib/python3.8/dist-packages/pip/__main__.py", line 23, in <module> from pip._internal.cli.main import main as _main # isort:skip # noqa File "/usr/local/lib/python3.8/dist-packages/pip/_internal/cli/main.py", line 5, in <module> import locale File "/usr/lib/python3.8/locale.py", line 16, in <module> import re File "/usr/lib/python3.8/re.py", line 145, in <module> class RegexFlag(enum.IntFlag): AttributeError: module 'enum' has no attribute 'IntFlag' ---------------------------------------- ERROR: Command errored out with exit status 1: /usr/bin/python3 /usr/local/lib/python3.8/dist-packages/pip install --ignore-installed --no-user --prefix /tmp/pip-build-env-k867aac0/overlay --no-warn-script-location --no-binary :none: --only-binary :none: -i https://pypi.org/simple -- 'setuptools>=40.8.0' wheel 'cffi>=1.4.1; python_implementation != '"'"'PyPy'"'"'' Check the logs for full command output. There was a related issue: pypa/pip#9689 and it is already fixed by pypa/pip#9689 (included by pip 21.1.1). I test the pip 21.1.1 and it works well on mac m1. Reviewers: Ismael Juma <[email protected]>
This puts pip into a zip file, and change the isolated environment to run that instead of the pip installation in site-packages. This avoids other packages in site-packages to be injected into the isolated environment.
Fix #8214.
As discussed in #8214 (comment), this has a ~1s overhead for each call, so an sdist build can be slowed down for up to 2s (one for
pyproject.toml
and another forget_requires_for_build_wheel
). There are possible optimisations, e.g. creating only one pip.zip for each pip invocation, but the globally managed state would introduce much complexity, so I’m not getting into them unless we decide this is the way to go.