Merge ~cjwatson/launchpad:virtualenv-20 into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: a5418e0cb6cdded8fbe1c2f289ec4cc7a625ffb3
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:virtualenv-20
Merge into: launchpad:master
Diff against target: 145 lines (+34/-18)
4 files modified
Makefile (+8/-6)
_pythonpath.py (+7/-8)
setup.py (+16/-2)
utilities/js-deps (+3/-2)
Reviewer Review Type Date Requested Status
Jürgen Gmach Approve
Review via email: mp+418654@code.launchpad.net

Commit message

Make build system compatible with virtualenv 20

Description of the change

virtualenv 20 was a complete rewrite of virtualenv. It's mostly compatible with older versions, but there were some places where Launchpad ran into trouble with it:

 * The handling of the `sitecustomize` module is a little different, and we rely on that to load some Launchpad customizations.

 * `env/bin/python3` is now a symlink (indirectly) to `/usr/bin/python3`, rather than being a copy of it, so we can't rely on touching it for timestamp comparison purposes in the `Makefile`. Use `env/instance_name` (which we were already creating for other reasons in `setup.py`) for this instead.

 * We now have to set the `VIRTUAL_ENV` environment variable in `_pythonpath.py` to activate the virtual environment properly.

 * While initially debugging this, I removed an unnecessary `$(PY)` from the invocation of `utilities/create-lp-wadl-and-apidoc.py`, and tidied up the `#!` line of `utilities/js-deps` to match all our other virtualenv-using scripts. These changes turn out not to be strictly required to support virtualenv 20, but I kept them since they seem like reasonable improvements in passing.

To post a comment you must log in.
Revision history for this message
Jürgen Gmach (jugmac00) wrote :

> `env/bin/python3` is now a symlink (indirectly) to `/usr/bin/python3`, rather than being a copy of it

That is possible one reason why virtualenv is a magnitude faster than venv.

> virtualenv 20 was a complete rewrite of virtualenv.

fun fact - Bernát only rewrote virtualenv as he tried to rewrite tox and hit a wall - so he rewrote virtualenv first.

> * It's wise to be a bit more careful about how we execute scripts in a couple more places, although this turns out not to be critical.

This is a bit vague. Could you elaborate where we need to pay attention and why? Otherwise I'd suggest to remove this sentence.

Do I read the commit message correctly that these changes just made the virtualenv usage compatible with virtualenv 20, but still is compatible with the old version?

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

You're right that that bullet point was a bit vague. I've fleshed it out.

Indeed, this also remains compatible with the old virtualenv version. I successfully ran the full test suite against this commit.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/Makefile b/Makefile
2index 69ad953..5d59a25 100644
3--- a/Makefile
4+++ b/Makefile
5@@ -30,6 +30,7 @@ PIP_ENV += PIP_FIND_LINKS="file://$(WD)/wheels/ file://$(WD)/download-cache/dist
6 VIRTUALENV := $(PIP_ENV) /usr/bin/virtualenv
7 PIP := PYTHONPATH= $(PIP_ENV) env/bin/pip --cache-dir=$(WD)/download-cache/
8
9+VENV_INSTANCE_NAME := env/instance_name
10 VENV_PYTHON := env/bin/$(PYTHON)
11
12 SITE_PACKAGES := \
13@@ -77,7 +78,7 @@ API_INDEX = $(APIDOC_DIR)/index.html
14 # NB: It's important PIP_BIN only mentions things genuinely produced by pip.
15 PIP_BIN = \
16 $(PY) \
17- $(VENV_PYTHON) \
18+ $(VENV_INSTANCE_NAME) \
19 bin/bingtestservice \
20 bin/build-twisted-plugin-cache \
21 bin/harness \
22@@ -131,7 +132,7 @@ hosted_branches: $(PY)
23 $(API_INDEX): $(VERSION_INFO) $(PY)
24 $(RM) -r $(APIDOC_DIR) $(APIDOC_DIR).tmp
25 mkdir -p $(APIDOC_DIR).tmp
26- LPCONFIG=$(LPCONFIG) $(PY) ./utilities/create-lp-wadl-and-apidoc.py \
27+ LPCONFIG=$(LPCONFIG) ./utilities/create-lp-wadl-and-apidoc.py \
28 --force "$(APIDOC_TMPDIR)"
29 mv $(APIDOC_TMPDIR) $(APIDOC_DIR)
30
31@@ -333,8 +334,9 @@ publish-tarball: build-tarball
32 #
33 # If we listed every target on the left-hand side, a parallel make would try
34 # multiple copies of this rule to build them all. Instead, we nominally build
35-# just $(VENV_PYTHON), and everything else is implicitly updated by that.
36-$(VENV_PYTHON): download-cache requirements/combined.txt setup.py
37+# just $(VENV_INSTANCE_NAME), and everything else is implicitly updated by
38+# that.
39+$(VENV_INSTANCE_NAME): download-cache requirements/combined.txt setup.py
40 rm -rf env
41 mkdir -p env
42 $(VIRTUALENV) \
43@@ -350,12 +352,12 @@ $(VENV_PYTHON): download-cache requirements/combined.txt setup.py
44 || { code=$$?; rm -f $@; exit $$code; }
45 touch $@
46
47-$(subst $(VENV_PYTHON),,$(PIP_BIN)): $(VENV_PYTHON)
48+$(subst $(VENV_INSTANCE_NAME),,$(PIP_BIN)): $(VENV_INSTANCE_NAME)
49
50 # Explicitly update version-info.py rather than declaring $(VERSION_INFO) as
51 # a prerequisite, to make sure it's up to date when doing deployments.
52 .PHONY: compile
53-compile: $(VENV_PYTHON)
54+compile: $(VENV_INSTANCE_NAME)
55 ${SHHH} utilities/relocate-virtualenv env
56 $(PYTHON) utilities/link-system-packages.py \
57 "$(SITE_PACKAGES)" system-packages.txt
58diff --git a/_pythonpath.py b/_pythonpath.py
59index a09cb74..1773f89 100644
60--- a/_pythonpath.py
61+++ b/_pythonpath.py
62@@ -25,16 +25,14 @@ python_version = '%s.%s' % sys.version_info[:2]
63 stdlib_dir = os.path.join(env, 'lib', 'python%s' % python_version)
64
65 if ('site' in sys.modules and
66- not sys.modules['site'].__file__.startswith(
67- os.path.join(stdlib_dir, 'site.py'))):
68- # We have the wrong site.py, so our paths are not set up correctly.
69- # We blow up, with a hopefully helpful error message.
70+ 'lp_sitecustomize' not in sys.modules):
71+ # Site initialization has been run but lp_sitecustomize was not loaded,
72+ # so something is set up incorrectly. We blow up, with a hopefully
73+ # helpful error message.
74 raise RuntimeError(
75- 'The wrong site.py is imported (%r imported, %r expected). '
76- 'Scripts should usually be '
77+ 'Python was invoked incorrectly. Scripts should usually be '
78 "started with Launchpad's bin/py, or with a Python invoked with "
79- 'the -S flag.' % (
80- sys.modules['site'].__file__, os.path.join(stdlib_dir, 'site.py')))
81+ 'the -S flag.')
82
83 # Ensure that the virtualenv's standard library directory is in sys.path;
84 # activate_this will not put it there.
85@@ -53,6 +51,7 @@ if not sys.executable.startswith(top + os.sep) or 'site' not in sys.modules:
86 sys.prefix = env
87 os.environ['PATH'] = (
88 os.path.join(env, 'bin') + os.pathsep + os.environ.get('PATH', ''))
89+ os.environ['VIRTUAL_ENV'] = env
90 site_packages = os.path.join(
91 env, 'lib', 'python%s' % python_version, 'site-packages')
92 import site
93diff --git a/setup.py b/setup.py
94index 88f502c..e26320d 100644
95--- a/setup.py
96+++ b/setup.py
97@@ -98,10 +98,18 @@ class lp_develop(develop):
98 """)
99 self.write_script("py", py_header + py_script_text)
100
101+ # Install site customizations for this virtualenv. In principle
102+ # we just want to install sitecustomize and have site load it,
103+ # but this doesn't work with virtualenv 20.x
104+ # (https://github.com/pypa/virtualenv/issues/1703). Note that
105+ # depending on the resolution of
106+ # https://bugs.python.org/issue33944 we may need to change this
107+ # again in future.
108 env_top = os.path.join(os.path.dirname(__file__), "env")
109- stdlib_dir = get_python_lib(standard_lib=True, prefix=env_top)
110+ site_packages_dir = get_python_lib(prefix=env_top)
111 orig_sitecustomize = self._get_orig_sitecustomize()
112- sitecustomize_path = os.path.join(stdlib_dir, "sitecustomize.py")
113+ sitecustomize_path = os.path.join(
114+ site_packages_dir, "_sitecustomize.py")
115 with open(sitecustomize_path, "w") as sitecustomize_file:
116 sitecustomize_file.write(dedent("""\
117 import os
118@@ -114,6 +122,12 @@ class lp_develop(develop):
119 """))
120 if orig_sitecustomize:
121 sitecustomize_file.write(orig_sitecustomize)
122+ # Awkward naming; this needs to come lexicographically after any
123+ # other .pth files.
124+ sitecustomize_pth_path = os.path.join(
125+ site_packages_dir, "zzz_run_venv_sitecustomize.pth")
126+ with open(sitecustomize_pth_path, "w") as sitecustomize_pth_file:
127+ sitecustomize_pth_file.write("import _sitecustomize\n")
128
129 # Write out the build-time value of LPCONFIG so that it can be
130 # used by scripts as the default instance name.
131diff --git a/utilities/js-deps b/utilities/js-deps
132index e8059f1..30d9b36 100755
133--- a/utilities/js-deps
134+++ b/utilities/js-deps
135@@ -1,7 +1,8 @@
136-#!bin/py
137+#! /usr/bin/python3 -S
138
139 import _pythonpath # noqa: F401
140+
141 from convoy.meta import main
142
143-main()
144
145+main()

Subscribers

People subscribed via source and target branches

to status/vote changes: