From 5203c997af9a6d068908ac800506f3de23122376 Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Thu, 23 Jun 2022 14:05:59 -0700 Subject: [PATCH] Use sitecustomize to emulate venv during python install (#512) During initialization, the `site` module probes for a `sitecustomize` module which performs site-specific initialization of various paths. Python virtual environments modify sys.prefix in order to ensure that any attempted package installation ends up using the virtual environment instead of the system locations. This approach should prevent various Linux distributions' attempts to inject `local` into the installation locations for python packages. They seem to have special cases for detecting virtual environments and omit the custom logic when detected. --- colcon_core/task/python/build.py | 22 ++++++++++++++----- colcon_core/task/python/template/__init__.py | 4 ++++ .../task/python/template/sitecustomize.py.em | 3 +++ setup.cfg | 1 + test/spell_check.words | 1 + 5 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 colcon_core/task/python/template/__init__.py create mode 100644 colcon_core/task/python/template/sitecustomize.py.em diff --git a/colcon_core/task/python/build.py b/colcon_core/task/python/build.py index 54868764..1ae2bd03 100644 --- a/colcon_core/task/python/build.py +++ b/colcon_core/task/python/build.py @@ -21,6 +21,7 @@ from colcon_core.task import TaskExtensionPoint from colcon_core.task.python import get_data_files_mapping from colcon_core.task.python import get_setup_data +from colcon_core.task.python.template import expand_template logger = colcon_logger.getChild(__name__) @@ -46,14 +47,23 @@ async def build(self, *, additional_hooks=None): # noqa: D102 return 1 setup_py_data = get_setup_data(self.context.pkg, env) + # override installation locations + prefix_override = Path(args.build_base) / 'prefix_override' + expand_template( + Path(__file__).parent / 'template' / 'sitecustomize.py.em', + prefix_override / 'sitecustomize.py', + { + 'site_prefix': args.install_base, + }) + # `setup.py develop|install` requires the python lib path to exist python_lib = os.path.join( args.install_base, self._get_python_lib(args)) os.makedirs(python_lib, exist_ok=True) # and being in the PYTHONPATH env = dict(env) - env['PYTHONPATH'] = python_lib + os.pathsep + \ - env.get('PYTHONPATH', '') + env['PYTHONPATH'] = str(prefix_override) + os.pathsep + \ + python_lib + os.pathsep + env.get('PYTHONPATH', '') # determine if setuptools specific commands are available available_commands = await self._get_available_commands( @@ -79,7 +89,7 @@ async def build(self, *, additional_hooks=None): # noqa: D102 cmd += [ 'build', '--build-base', os.path.join( args.build_base, 'build'), - 'install', '--prefix', args.install_base, + 'install', '--record', os.path.join(args.build_base, 'install.log')] if 'egg_info' in available_commands: # prevent installation of dependencies specified in setup.py @@ -102,14 +112,14 @@ async def build(self, *, additional_hooks=None): # noqa: D102 # easy-install.pth file cmd = [ executable, 'setup.py', - 'develop', '--prefix', args.install_base, + 'develop', '--editable', '--build-directory', os.path.join(args.build_base, 'build'), '--no-deps', ] if setup_py_data.get('data_files'): - cmd += ['install_data', '--install-dir', args.install_base] + cmd += ['install_data'] completed = await run( self.context, cmd, cwd=args.build_base, env=env) finally: @@ -163,7 +173,7 @@ async def _undo_develop(self, pkg, args, env): if os.path.exists(egg_info) and os.path.islink(setup_py_build_space): cmd = [ executable, 'setup.py', - 'develop', '--prefix', args.install_base, + 'develop', '--uninstall', '--editable', '--build-directory', os.path.join(args.build_base, 'build') ] diff --git a/colcon_core/task/python/template/__init__.py b/colcon_core/task/python/template/__init__.py new file mode 100644 index 00000000..b100a937 --- /dev/null +++ b/colcon_core/task/python/template/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2022 Open Source Robotics Foundation, Inc. +# Licensed under the Apache License, Version 2.0 + +from colcon_core.shell.template import expand_template # noqa: F401 diff --git a/colcon_core/task/python/template/sitecustomize.py.em b/colcon_core/task/python/template/sitecustomize.py.em new file mode 100644 index 00000000..5ed6f35e --- /dev/null +++ b/colcon_core/task/python/template/sitecustomize.py.em @@ -0,0 +1,3 @@ +import sys +sys.real_prefix = sys.prefix +sys.prefix = sys.exec_prefix = @repr(site_prefix) diff --git a/setup.cfg b/setup.cfg index bdcb900d..78fb1707 100644 --- a/setup.cfg +++ b/setup.cfg @@ -145,6 +145,7 @@ pytest11 = [options.package_data] colcon_core.shell.template = *.em +colcon_core.task.python.template = *.em [flake8] import-order-style = google diff --git a/test/spell_check.words b/test/spell_check.words index 52eb1ced..279e35c8 100644 --- a/test/spell_check.words +++ b/test/spell_check.words @@ -101,6 +101,7 @@ setupscript setuptools shlex sigint +sitecustomize sloretz stacklevel staticmethod