Closed Shrews closed 7 months ago
This looks good, but was just playing with it while testing some other core PEP668 work Matt C and I were doing and tripped over a nasty problem that's only apparent when actually running in a PEP668-marked environment: it doesn't look like ensurepip
can respect the envvar, and will thus refuse to work at all.
It doesn't look like any RHELish OSs are marking things yet (only recent Debian/Ubuntu), but it's easy to fake it up for testing using the following as your base image:
FROM fedora:39
RUN touch /usr/lib64/python3.12/EXTERNALLY-MANAGED
Build that locally, then use the resultant image as your base in a vanilla EE with the default Python settings, and it'll :bomb: hard on ensurepip
. It explicitly blocks inheritance of all PIP_
envvars when pip is launched to bootstrap itself, and there's no other apparent way to pass that through.
So either we'll need to look up, zap (and possibly re-create?) the marker file, not use ensurepip
, or ... something else.
Well that's a bunch of poo... For reference, this builder EE is enough show the error:
---
version: 3
images:
base_image:
name: docker.io/library/fedora:39
additional_build_steps:
prepend_base:
- RUN touch /usr/lib64/python3.12/EXTERNALLY-MANAGED
I have no idea if this is intended behavior, but you can also bypass EXTERNALLY-MANAGED
by just supplying --root
, with either pip
or ensurepip
. If you set --root /
that works just like we do now.
[root@8f8a1523eade /]# touch /usr/lib64/python3.12/EXTERNALLY-MANAGED
[root@8f8a1523eade /]# python3 -m ensurepip
error: externally-managed-environment
× This environment is externally managed
╰─> The Python environment under /usr is managed externally, and may not be
manipulated by the user. Please use specific tooling from the distributor of
the Python installation to interact with this environment instead.
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
[SNIP]
[root@8f8a1523eade /]# python3 -m ensurepip --root /
Looking in links: /tmp/tmpn6uk_n0x
Processing /tmp/tmpn6uk_n0x/pip-23.2.1-py3-none-any.whl
Installing collected packages: pip
Successfully installed pip-23.2.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
Ok, using --root
is expected to bypass the check:
# Check whether the environment we're installing into is externally
# managed, as specified in PEP 668. Specifying --root, --target, or
# --prefix disables the check, since there's no reliable way to locate
# the EXTERNALLY-MANAGED file for those cases. An exception is also
# made specifically for "--dry-run --report" for convenience.
I'm going to verify one more thing to be sure, but after messing around with ensurepip --root /
, I think it's basically working for us by accident. I didn't really like adding ensurepip
in the first place, but it's kind of a necessary evil for common vanilla base images that wouldn't require users to always manually add a prepend_base
and figure out how to get pip in there.
Much as I hate to say it, I think we're going to want to hide the ensurepip
stuff behind a reusable script indirection where we can do a little more work to make sure we've got a pip available that can do what we want. The risk is that that script indirection becomes another place where everyone wants to hang "magic" to silently make pip work for $random_base_image_with_unique_busted_pip, but I think it's preferable to the game of distributed whac-a-mole I can see this one becoming down the road :laughing: .
Per internal discussion, going to move the ensurepip call to a new target script to encapsulate future changes that might be needed for non-RHEL distros, and add a flag to disable the call.
pip_install
target script to encapsulate the pip install logic. This will be called to install pip in thebase
image, and thus inherited by the other images. For v1 EE schemas, we always copy this script to thebuilder
image and call it since it will be separate frombase
. For v2, we only copypip_install
to the builder image and call it when one is defined.ensurepip
calls from inside target scripts so we can control pip installation external to those scripts. Also, those calls are unnecessary since we install pip in thebase
image (caveat: see the builder image hoops outlined in previous step).skip_pip_install
option to EE schema to manage pip installation.Fixes #604 Fixes #646