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
diff --git a/Makefile b/Makefile
index 69ad953..5d59a25 100644
--- a/Makefile
+++ b/Makefile
@@ -30,6 +30,7 @@ PIP_ENV += PIP_FIND_LINKS="file://$(WD)/wheels/ file://$(WD)/download-cache/dist
30VIRTUALENV := $(PIP_ENV) /usr/bin/virtualenv30VIRTUALENV := $(PIP_ENV) /usr/bin/virtualenv
31PIP := PYTHONPATH= $(PIP_ENV) env/bin/pip --cache-dir=$(WD)/download-cache/31PIP := PYTHONPATH= $(PIP_ENV) env/bin/pip --cache-dir=$(WD)/download-cache/
3232
33VENV_INSTANCE_NAME := env/instance_name
33VENV_PYTHON := env/bin/$(PYTHON)34VENV_PYTHON := env/bin/$(PYTHON)
3435
35SITE_PACKAGES := \36SITE_PACKAGES := \
@@ -77,7 +78,7 @@ API_INDEX = $(APIDOC_DIR)/index.html
77# NB: It's important PIP_BIN only mentions things genuinely produced by pip.78# NB: It's important PIP_BIN only mentions things genuinely produced by pip.
78PIP_BIN = \79PIP_BIN = \
79 $(PY) \80 $(PY) \
80 $(VENV_PYTHON) \81 $(VENV_INSTANCE_NAME) \
81 bin/bingtestservice \82 bin/bingtestservice \
82 bin/build-twisted-plugin-cache \83 bin/build-twisted-plugin-cache \
83 bin/harness \84 bin/harness \
@@ -131,7 +132,7 @@ hosted_branches: $(PY)
131$(API_INDEX): $(VERSION_INFO) $(PY)132$(API_INDEX): $(VERSION_INFO) $(PY)
132 $(RM) -r $(APIDOC_DIR) $(APIDOC_DIR).tmp133 $(RM) -r $(APIDOC_DIR) $(APIDOC_DIR).tmp
133 mkdir -p $(APIDOC_DIR).tmp134 mkdir -p $(APIDOC_DIR).tmp
134 LPCONFIG=$(LPCONFIG) $(PY) ./utilities/create-lp-wadl-and-apidoc.py \135 LPCONFIG=$(LPCONFIG) ./utilities/create-lp-wadl-and-apidoc.py \
135 --force "$(APIDOC_TMPDIR)"136 --force "$(APIDOC_TMPDIR)"
136 mv $(APIDOC_TMPDIR) $(APIDOC_DIR)137 mv $(APIDOC_TMPDIR) $(APIDOC_DIR)
137138
@@ -333,8 +334,9 @@ publish-tarball: build-tarball
333#334#
334# If we listed every target on the left-hand side, a parallel make would try335# If we listed every target on the left-hand side, a parallel make would try
335# multiple copies of this rule to build them all. Instead, we nominally build336# multiple copies of this rule to build them all. Instead, we nominally build
336# just $(VENV_PYTHON), and everything else is implicitly updated by that.337# just $(VENV_INSTANCE_NAME), and everything else is implicitly updated by
337$(VENV_PYTHON): download-cache requirements/combined.txt setup.py338# that.
339$(VENV_INSTANCE_NAME): download-cache requirements/combined.txt setup.py
338 rm -rf env340 rm -rf env
339 mkdir -p env341 mkdir -p env
340 $(VIRTUALENV) \342 $(VIRTUALENV) \
@@ -350,12 +352,12 @@ $(VENV_PYTHON): download-cache requirements/combined.txt setup.py
350 || { code=$$?; rm -f $@; exit $$code; }352 || { code=$$?; rm -f $@; exit $$code; }
351 touch $@353 touch $@
352354
353$(subst $(VENV_PYTHON),,$(PIP_BIN)): $(VENV_PYTHON)355$(subst $(VENV_INSTANCE_NAME),,$(PIP_BIN)): $(VENV_INSTANCE_NAME)
354356
355# Explicitly update version-info.py rather than declaring $(VERSION_INFO) as357# Explicitly update version-info.py rather than declaring $(VERSION_INFO) as
356# a prerequisite, to make sure it's up to date when doing deployments.358# a prerequisite, to make sure it's up to date when doing deployments.
357.PHONY: compile359.PHONY: compile
358compile: $(VENV_PYTHON)360compile: $(VENV_INSTANCE_NAME)
359 ${SHHH} utilities/relocate-virtualenv env361 ${SHHH} utilities/relocate-virtualenv env
360 $(PYTHON) utilities/link-system-packages.py \362 $(PYTHON) utilities/link-system-packages.py \
361 "$(SITE_PACKAGES)" system-packages.txt363 "$(SITE_PACKAGES)" system-packages.txt
diff --git a/_pythonpath.py b/_pythonpath.py
index a09cb74..1773f89 100644
--- a/_pythonpath.py
+++ b/_pythonpath.py
@@ -25,16 +25,14 @@ python_version = '%s.%s' % sys.version_info[:2]
25stdlib_dir = os.path.join(env, 'lib', 'python%s' % python_version)25stdlib_dir = os.path.join(env, 'lib', 'python%s' % python_version)
2626
27if ('site' in sys.modules and27if ('site' in sys.modules and
28 not sys.modules['site'].__file__.startswith(28 'lp_sitecustomize' not in sys.modules):
29 os.path.join(stdlib_dir, 'site.py'))):29 # Site initialization has been run but lp_sitecustomize was not loaded,
30 # We have the wrong site.py, so our paths are not set up correctly.30 # so something is set up incorrectly. We blow up, with a hopefully
31 # We blow up, with a hopefully helpful error message.31 # helpful error message.
32 raise RuntimeError(32 raise RuntimeError(
33 'The wrong site.py is imported (%r imported, %r expected). '33 'Python was invoked incorrectly. Scripts should usually be '
34 'Scripts should usually be '
35 "started with Launchpad's bin/py, or with a Python invoked with "34 "started with Launchpad's bin/py, or with a Python invoked with "
36 'the -S flag.' % (35 'the -S flag.')
37 sys.modules['site'].__file__, os.path.join(stdlib_dir, 'site.py')))
3836
39# Ensure that the virtualenv's standard library directory is in sys.path;37# Ensure that the virtualenv's standard library directory is in sys.path;
40# activate_this will not put it there.38# activate_this will not put it there.
@@ -53,6 +51,7 @@ if not sys.executable.startswith(top + os.sep) or 'site' not in sys.modules:
53 sys.prefix = env51 sys.prefix = env
54 os.environ['PATH'] = (52 os.environ['PATH'] = (
55 os.path.join(env, 'bin') + os.pathsep + os.environ.get('PATH', ''))53 os.path.join(env, 'bin') + os.pathsep + os.environ.get('PATH', ''))
54 os.environ['VIRTUAL_ENV'] = env
56 site_packages = os.path.join(55 site_packages = os.path.join(
57 env, 'lib', 'python%s' % python_version, 'site-packages')56 env, 'lib', 'python%s' % python_version, 'site-packages')
58 import site57 import site
diff --git a/setup.py b/setup.py
index 88f502c..e26320d 100644
--- a/setup.py
+++ b/setup.py
@@ -98,10 +98,18 @@ class lp_develop(develop):
98 """)98 """)
99 self.write_script("py", py_header + py_script_text)99 self.write_script("py", py_header + py_script_text)
100100
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.
101 env_top = os.path.join(os.path.dirname(__file__), "env")108 env_top = os.path.join(os.path.dirname(__file__), "env")
102 stdlib_dir = get_python_lib(standard_lib=True, prefix=env_top)109 site_packages_dir = get_python_lib(prefix=env_top)
103 orig_sitecustomize = self._get_orig_sitecustomize()110 orig_sitecustomize = self._get_orig_sitecustomize()
104 sitecustomize_path = os.path.join(stdlib_dir, "sitecustomize.py")111 sitecustomize_path = os.path.join(
112 site_packages_dir, "_sitecustomize.py")
105 with open(sitecustomize_path, "w") as sitecustomize_file:113 with open(sitecustomize_path, "w") as sitecustomize_file:
106 sitecustomize_file.write(dedent("""\114 sitecustomize_file.write(dedent("""\
107 import os115 import os
@@ -114,6 +122,12 @@ class lp_develop(develop):
114 """))122 """))
115 if orig_sitecustomize:123 if orig_sitecustomize:
116 sitecustomize_file.write(orig_sitecustomize)124 sitecustomize_file.write(orig_sitecustomize)
125 # Awkward naming; this needs to come lexicographically after any
126 # other .pth files.
127 sitecustomize_pth_path = os.path.join(
128 site_packages_dir, "zzz_run_venv_sitecustomize.pth")
129 with open(sitecustomize_pth_path, "w") as sitecustomize_pth_file:
130 sitecustomize_pth_file.write("import _sitecustomize\n")
117131
118 # Write out the build-time value of LPCONFIG so that it can be132 # Write out the build-time value of LPCONFIG so that it can be
119 # used by scripts as the default instance name.133 # used by scripts as the default instance name.
diff --git a/utilities/js-deps b/utilities/js-deps
index e8059f1..30d9b36 100755
--- a/utilities/js-deps
+++ b/utilities/js-deps
@@ -1,7 +1,8 @@
1#!bin/py1#! /usr/bin/python3 -S
22
3import _pythonpath # noqa: F4013import _pythonpath # noqa: F401
4
4from convoy.meta import main5from convoy.meta import main
56
6main()
77
8main()

Subscribers

People subscribed via source and target branches

to status/vote changes: