-
Notifications
You must be signed in to change notification settings - Fork 170
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
Check for pip configuration when an externally managed environment is detected. #979
Changes from all commits
696be7b
c121579
e9ad440
8ce4a75
2c76686
29ec176
48b9da2
a5e10e7
f042758
a2adfe8
5b2cc64
73ed28a
f1dffc6
f685769
65c7152
f8472d2
70775f4
25ac0e0
8c57dba
56a745a
a2e9a1e
f14c6de
8b48f13
8e94ef1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,4 @@ Contents | |
sources_list | ||
developers_guide | ||
rosdep2_api | ||
pip_and_pep_668 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
Pip installation after PEP 668 | ||
============================== | ||
|
||
`PEP-668`_ introduced `externally managed environments <externally-managed-environments>`_ to Python packaging. | ||
|
||
rosdep is designed to use pip as an alternative system package manager, rosdep installation of pip packages requires installing packages globally as root. | ||
Starting with Python 3.11, `PEP-668`_ compliance requires you to allow pip to install alongside externally managed packages using the ``break-system-packages`` option. | ||
|
||
There are multiple ways to configure pip so that rosdep will succeed. | ||
|
||
|
||
Configure using environment variable | ||
------------------------------------ | ||
|
||
This is the way that we recommend configuring pip for rosdep usage. | ||
We recommend configuring pip using the system environment. | ||
Setting environment variables in your login profile, ``PIP_BREAK_SYSTEM_PACKAGES`` in your environment. | ||
The value of the environment variable can be any of ``1``, ``yes``, or ``true``. | ||
The string values are not case sensitive. | ||
|
||
rosdep is designed to use ``sudo`` in order to gain root privileges for installation when not run as root. | ||
If your system's sudo configuration prohibits the passing of environment variables use the :ref:`pip.conf <configure-using-pip.conf>` method below. | ||
|
||
|
||
.. _configure-using-pip.conf: | ||
|
||
Configure using pip.conf | ||
------------------------ | ||
|
||
`Pip configuration files <pip-configuration>`_ can be used to set the desired behavior. | ||
Pip checks for global configuration files in ``XDG_CONFIG_DIRS``, as well as ``/etc/pip.conf``. | ||
For details on ``XDG_CONFIG_DIRS`` refer to the `XDG base directories specification <xdg-base-dirs>`_. | ||
If you're unsure which configuration file is in use by your system, ``/etc/pip.conf`` seems like the most generic. | ||
|
||
.. code-block:: ini | ||
|
||
[install] | ||
break-system-packages = true | ||
|
||
|
||
.. warning:: Creating a pip.conf in your user account's ``XDG_CONFIG_HOME`` (e.g. ``~/.config/pip/pip.conf``) does not appear to be sufficent when installing packages globally. | ||
|
||
|
||
Configuring for CI setup | ||
------------------------ | ||
|
||
Either environment variables or configuration files can be used with your CI system. | ||
Which one you choose will depend on how your CI environment is configured. | ||
Perhaps the most straightforward will be to set the environent variable in the shell or script execution context before invoking ``rosdep``. | ||
|
||
.. code-block:: bash | ||
|
||
sudo rosdep init | ||
rosdep update | ||
PIP_BREAK_SYSTEM_PACKAGES=1 rosdep install -r rolling --from-paths src/ | ||
|
||
If ``rosdep`` is invoked by internal processes in your CI and you need to set the configuration without having direct control over how ``rosdep install`` is run, setting the environment variable globally would also work. | ||
|
||
.. code-block:: bash | ||
|
||
export PIP_BREAK_SYSTEM_PACKAGES=1 | ||
./path/to/ci-script.sh | ||
|
||
|
||
If you cannot set environment variables but you can create configuration files, you can set ``/etc/pip.conf`` with the necessary configuration. | ||
|
||
.. code-block:: bash | ||
|
||
printf "[install]\nbreak-system-packages = true\n" | sudo tee -a /etc/pip.conf | ||
|
||
.. _PEP-668: https://peps.python.org/pep-0668/ | ||
.. _pip-configuration: https://pip.pypa.io/en/stable/topics/configuration/ | ||
.. _externally-managed-environments: https://packaging.python.org/en/latest/specifications/externally-managed-environments/ | ||
.. _xdg-base-dirs: https://specifications.freedesktop.org/basedir-spec/latest/ |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -67,6 +67,23 @@ def test_PipInstaller_get_depends(): | |||||||||||||||||
assert ['foo'] == installer.get_depends(dict(depends=['foo'])) | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
@patch('rosdep2.platforms.pip.externally_managed_installable') | ||||||||||||||||||
def test_PipInstaller_handles_externally_managed_environment(externally_managed_installable): | ||||||||||||||||||
from rosdep2 import InstallFailed | ||||||||||||||||||
from rosdep2.platforms.pip import EXTERNALLY_MANAGED_EXPLAINER, PipInstaller | ||||||||||||||||||
Comment on lines
+72
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not super up on why imports are done per test function here but I am propagating the convention until I understand it better. |
||||||||||||||||||
|
||||||||||||||||||
externally_managed_installable.return_value = False | ||||||||||||||||||
installer = PipInstaller() | ||||||||||||||||||
try: | ||||||||||||||||||
installer.get_install_command(['whatever']) | ||||||||||||||||||
assert False, 'should have raised' | ||||||||||||||||||
except InstallFailed as e: | ||||||||||||||||||
assert e.failures == [('pip', EXTERNALLY_MANAGED_EXPLAINER)] | ||||||||||||||||||
Comment on lines
+77
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want, a more Pythonic way could be:
Suggested change
and make sure to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the suggestion. I'm actually going to keep this as is for now, because I don't believe the infrastructure packages ever explicitly require pytest. I think pytest is supposed to be compatible with stdlib unittest as well and utilizing that may be the preferred approach. |
||||||||||||||||||
externally_managed_installable.return_value = True | ||||||||||||||||||
assert installer.get_install_command(['whatever'], interactive=False) | ||||||||||||||||||
|
||||||||||||||||||
|
||||||||||||||||||
@patch.dict(os.environ, {'PIP_BREAK_SYSTEM_PACKAGES': '1'}) | ||||||||||||||||||
nuclearsandwich marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
def test_PipInstaller(): | ||||||||||||||||||
from rosdep2 import InstallFailed | ||||||||||||||||||
from rosdep2.platforms.pip import PipInstaller | ||||||||||||||||||
|
@@ -104,7 +121,7 @@ def test(expected_prefix, mock_method, mock_get_pip_command): | |||||||||||||||||
try: | ||||||||||||||||||
if hasattr(os, 'geteuid'): | ||||||||||||||||||
with patch('rosdep2.installers.os.geteuid', return_value=1): | ||||||||||||||||||
test(['sudo', '-H']) | ||||||||||||||||||
test(['sudo', '-H', '--preserve-env=PIP_BREAK_SYSTEM_PACKAGES']) | ||||||||||||||||||
with patch('rosdep2.installers.os.geteuid', return_value=0): | ||||||||||||||||||
test([]) | ||||||||||||||||||
else: | ||||||||||||||||||
|
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.
Note that this documentation won't actually be live until the PR is merged and docs rebuild the next day. This will only affect those using rosdep from mainline or this branch. I'll wait until the docs rebuild to make a new rosdep release with this change.