Merge ~cjwatson/launchpad:no-system-site-packages into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 54684b3d58b280e1f2e8c85d61a64be4fbc5510b
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:no-system-site-packages
Merge into: launchpad:master
Diff against target: 188 lines (+88/-17)
6 files modified
Makefile (+8/-7)
doc/pip.txt (+0/-1)
setup-requirements.txt (+0/-7)
setup.py (+1/-2)
system-packages.txt (+32/-0)
utilities/link-system-packages.py (+47/-0)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+378946@code.launchpad.net

Commit message

Stop using --system-site-packages

Description of the change

We've been using "virtualenv --system-site-packages" to ensure that Launchpad has access to some system-installed packages that can't or shouldn't be installed as a normal Python dependency for various reasons. This strategy has always had some problems, but we've been able to deal with them.

However, with recent versions of pip and Python 3, the situation gets worse. https://github.com/pypa/pip/issues/6264 describes the problem: pip attempts to isolate itself from site-packages when building wheels, leaving only its temporary one; but within a virtualenv created with --system-site-packages it removes the virtualenv's site-packages but not the system one (because virtualenv patches distutils.sysconfig.get_python_lib). It's not easy to see how pip might reliably and portably escape the virtualenv in order to find the system's site-packages directory.

(On Python 2 we get away with this by luck, because distutils.sysconfig.get_python_lib returns '.../env/lib/python2.7/site-packages' but '.../env/local/lib/python2.7/site-packages' is on sys.path, and pip doesn't realise that they're the same thing due to symlinks; we don't get this stroke of luck on Python 3.)

Installing everything properly in the virtualenv is also difficult. Several of the ones that remain don't exist as wheels or in another form that pip can install. We could fake this up (I went so far as to prepare a script to convert the installed version of python-apt into a wheel), but this would incur a significant maintenance burden.

Instead, symlink packages from the system site-packages directory into the virtualenv in "make compile". It's conceivable that this might confuse pip slightly, but so far it doesn't seem to, and everything else works fine.

This shouldn't be landed until https://portal.admin.canonical.com/C124200 has been resolved and all production deployment targets have the python-tdb package installed.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) wrote :

LGTM

review: Approve
Revision history for this message
Colin Watson (cjwatson) wrote :

Currently blocked on https://portal.admin.canonical.com/C124836 to ensure that python-tdb is installed everywhere.

Revision history for this message
Colin Watson (cjwatson) wrote :

launchpad-dependencies is up to date everywhere relevant now, so landing.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/Makefile b/Makefile
index 14a6f31..418cf98 100644
--- a/Makefile
+++ b/Makefile
@@ -16,15 +16,14 @@ PIP_ENV := LC_ALL=C.UTF-8
16# be reviewed/merged/deployed.16# be reviewed/merged/deployed.
17PIP_NO_INDEX := 117PIP_NO_INDEX := 1
18PIP_ENV += PIP_NO_INDEX=$(PIP_NO_INDEX)18PIP_ENV += PIP_NO_INDEX=$(PIP_NO_INDEX)
19# Although --ignore-installed is slower, we need it to avoid confusion with
20# system-installed Python packages. If we ever manage to remove the need
21# for virtualenv --system-site-packages, then we can remove this too.
22PIP_ENV += PIP_IGNORE_INSTALLED=1
23PIP_ENV += PIP_FIND_LINKS="file://$(WD)/wheelhouse/ file://$(WD)/download-cache/dist/"19PIP_ENV += PIP_FIND_LINKS="file://$(WD)/wheelhouse/ file://$(WD)/download-cache/dist/"
2420
25VIRTUALENV := $(PIP_ENV) virtualenv21VIRTUALENV := $(PIP_ENV) virtualenv
26PIP := PYTHONPATH= $(PIP_ENV) env/bin/pip --cache-dir=$(WD)/download-cache/22PIP := PYTHONPATH= $(PIP_ENV) env/bin/pip --cache-dir=$(WD)/download-cache/
2723
24SITE_PACKAGES := \
25 $$(env/bin/python -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())')
26
28TESTFLAGS=-p $(VERBOSITY)27TESTFLAGS=-p $(VERBOSITY)
29TESTOPTS=28TESTOPTS=
3029
@@ -250,7 +249,7 @@ $(PY): download-cache constraints.txt setup.py
250 rm -rf env249 rm -rf env
251 mkdir -p env250 mkdir -p env
252 $(VIRTUALENV) \251 $(VIRTUALENV) \
253 --python=$(PYTHON) --system-site-packages --never-download \252 --python=$(PYTHON) --never-download \
254 --extra-search-dir=$(WD)/download-cache/dist/ \253 --extra-search-dir=$(WD)/download-cache/dist/ \
255 --extra-search-dir=$(WD)/wheelhouse/ \254 --extra-search-dir=$(WD)/wheelhouse/ \
256 env255 env
@@ -270,6 +269,8 @@ compile: $(PY)
270 ${SHHH} utilities/relocate-virtualenv env269 ${SHHH} utilities/relocate-virtualenv env
271 ${SHHH} $(MAKE) -C sourcecode build PYTHON=${PYTHON} \270 ${SHHH} $(MAKE) -C sourcecode build PYTHON=${PYTHON} \
272 LPCONFIG=${LPCONFIG}271 LPCONFIG=${LPCONFIG}
272 $(PYTHON) utilities/link-system-packages.py \
273 "$(SITE_PACKAGES)" system-packages.txt
273 ${SHHH} bin/build-twisted-plugin-cache274 ${SHHH} bin/build-twisted-plugin-cache
274 scripts/update-version-info.sh275 scripts/update-version-info.sh
275276
@@ -477,13 +478,13 @@ reload-apache: enable-apache-launchpad
477TAGS: compile478TAGS: compile
478 # emacs tags479 # emacs tags
479 ctags -R -e --languages=-JavaScript --python-kinds=-i -f $@.new \480 ctags -R -e --languages=-JavaScript --python-kinds=-i -f $@.new \
480 $(CURDIR)/lib $(CURDIR)/env/lib/$(PYTHON)/site-packages481 $(CURDIR)/lib "$(SITE_PACKAGES)"
481 mv $@.new $@482 mv $@.new $@
482483
483tags: compile484tags: compile
484 # vi tags485 # vi tags
485 ctags -R --languages=-JavaScript --python-kinds=-i -f $@.new \486 ctags -R --languages=-JavaScript --python-kinds=-i -f $@.new \
486 $(CURDIR)/lib $(CURDIR)/env/lib/$(PYTHON)/site-packages487 $(CURDIR)/lib "$(SITE_PACKAGES)"
487 mv $@.new $@488 mv $@.new $@
488489
489PYDOCTOR = pydoctor490PYDOCTOR = pydoctor
diff --git a/doc/pip.txt b/doc/pip.txt
index b570ab8..23c024b 100644
--- a/doc/pip.txt
+++ b/doc/pip.txt
@@ -372,6 +372,5 @@ Possible Future Goals
372=====================372=====================
373373
374- Use wheels.374- Use wheels.
375- No longer use system site-packages.
376- No longer use make.375- No longer use make.
377- Get rid of the sourcecode directory.376- Get rid of the sourcecode directory.
diff --git a/setup-requirements.txt b/setup-requirements.txt
index 11ef58a..e7472fc 100644
--- a/setup-requirements.txt
+++ b/setup-requirements.txt
@@ -1,10 +1,3 @@
1pip==20.0.21pip==20.0.2
2setuptools==44.0.02setuptools==44.0.0
3wheel==0.34.23wheel==0.34.2
4
5# Install these early to avoid mismatches with a system-installed
6# python-cffi-backend package.
7# XXX cjwatson 2020-02-06: This would all be much easier if we could remove
8# the need for virtualenv --system-site-packages.
9cffi==1.12.2
10pycparser==2.19
diff --git a/setup.py b/setup.py
index 84f146a..565163b 100644
--- a/setup.py
+++ b/setup.py
@@ -187,8 +187,7 @@ setup(
187 'lpjsmin',187 'lpjsmin',
188 'Markdown',188 'Markdown',
189 'meliae',189 'meliae',
190 # Pin version for now to avoid confusion with system site-packages.190 'mock',
191 'mock==1.0.1',
192 'oauth',191 'oauth',
193 'oops',192 'oops',
194 'oops_amqp',193 'oops_amqp',
diff --git a/system-packages.txt b/system-packages.txt
195new file mode 100644194new file mode 100644
index 0000000..1737888
--- /dev/null
+++ b/system-packages.txt
@@ -0,0 +1,32 @@
1# System-installed Python packages to link into our virtualenv. This
2# facility should be reserved for cases where installing them as a normal
3# Python dependency is impossible or unreliable (perhaps due to frequent ABI
4# changes in system libraries they depend on, or frequent security updates
5# managed by the distribution's security team).
6
7# Used by launchpad-buildd.
8apt
9
10# Used by Soyuz and other related code to parse Debian packages and
11# repository index files, and to compare Debian version numbers.
12apt_inst
13apt_pkg
14
15# utilities/js-deps
16convoy
17
18# lp.services.geoip.model
19GeoIP
20
21# lp.testing.html5browser
22gi
23
24# lp.services.fields, lp.services.spriteutils
25PIL
26
27# Dependency of cscvs.
28sqlite
29_sqlite
30
31# Optional dependency of bzr-git and bzr-svn.
32tdb
diff --git a/utilities/link-system-packages.py b/utilities/link-system-packages.py
0new file mode 10075533new file mode 100755
index 0000000..07d0f9b
--- /dev/null
+++ b/utilities/link-system-packages.py
@@ -0,0 +1,47 @@
1#! /usr/bin/python2
2
3# Copyright 2020 Canonical Ltd. This software is licensed under the
4# GNU Affero General Public License version 3 (see the file LICENSE).
5
6"""Link system-installed Python modules into Launchpad's virtualenv."""
7
8from __future__ import absolute_import, print_function, unicode_literals
9
10from argparse import ArgumentParser
11from distutils.sysconfig import get_python_lib
12import importlib
13import os.path
14import re
15
16
17def link_module(name, virtualenv_libdir):
18 module = importlib.import_module(name)
19 path = module.__file__
20 if os.path.basename(path).startswith("__init__."):
21 path = os.path.dirname(path)
22 system_libdir = get_python_lib(plat_specific=path.endswith(".so"))
23 if os.path.commonprefix([path, system_libdir]) != system_libdir:
24 raise RuntimeError(
25 "%s imported from outside %s (%s)" % (name, system_libdir, path))
26 target_path = os.path.join(
27 virtualenv_libdir, os.path.relpath(path, system_libdir))
28 if os.path.lexists(target_path) and os.path.islink(target_path):
29 os.unlink(target_path)
30 os.symlink(path, target_path)
31
32
33def main():
34 parser = ArgumentParser()
35 parser.add_argument("virtualenv_libdir")
36 parser.add_argument("module_file", type=open)
37 args = parser.parse_args()
38
39 for line in args.module_file:
40 line = re.sub(r"#.*", "", line).strip()
41 if not line:
42 continue
43 link_module(line, args.virtualenv_libdir)
44
45
46if __name__ == "__main__":
47 main()

Subscribers

People subscribed via source and target branches

to status/vote changes: