Skip to content

Commit 098ffc9

Browse files
Share a venv, if possible (#155)
This also includes a few small fixes.
1 parent b13bb1e commit 098ffc9

File tree

4 files changed

+119
-88
lines changed

4 files changed

+119
-88
lines changed

pyperformance/_pip.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,26 +36,32 @@ def run_pip(cmd, *args, **kwargs):
3636

3737
def is_pip_installed(python, *, env=None):
3838
"""Return True if pip is installed on the given Python executable."""
39-
ec, _, _ = run_pip('--version', env=env, capture=True, verbose=False)
39+
ec, _, _ = run_pip(
40+
'--version',
41+
python=python,
42+
env=env,
43+
capture=True,
44+
verbose=False,
45+
)
4046
return ec == 0
4147

4248

4349
def install_pip(python=sys.executable, *,
4450
info=None,
4551
downloaddir=None,
4652
env=None,
53+
upgrade=True,
4754
**kwargs
4855
):
4956
"""Install pip on the given Python executable."""
5057
if not python:
5158
python = getattr(info, 'executable', None) or sys.executable
5259

5360
# python -m ensurepip
54-
res = _utils.run_python(
55-
'-m', 'ensurepip', '--verbose',
56-
python=python,
57-
**kwargs
58-
)
61+
args = ['-m', 'ensurepip', '-v'] # --verbose
62+
if upgrade:
63+
args.append('-U') # --upgrade
64+
res = _utils.run_python(*args, python=python, **kwargs)
5965
ec, _, _ = res
6066
if ec == 0 and is_pip_installed(python, env=env):
6167
return res
@@ -111,16 +117,20 @@ def upgrade_pip(python=sys.executable, *,
111117

112118
if installer:
113119
# Upgrade installer dependencies (setuptools, ...)
114-
reqs = [
115-
f'setuptools>={OLD_SETUPTOOLS}',
116-
# install wheel so pip can cache binary wheel packages locally,
117-
# and install prebuilt wheel packages from PyPI.
118-
'wheel',
119-
]
120-
res = install_requirements(*reqs, python=python, upgrade=True, **kwargs)
120+
res = ensure_installer(python, upgrade=True, **kwargs)
121121
return res
122122

123123

124+
def ensure_installer(python=sys.executable, **kwargs):
125+
reqs = [
126+
f'setuptools>={OLD_SETUPTOOLS}',
127+
# install wheel so pip can cache binary wheel packages locally,
128+
# and install prebuilt wheel packages from PyPI.
129+
'wheel',
130+
]
131+
return install_requirements(*reqs, python=python, **kwargs)
132+
133+
124134
def install_requirements(reqs, *extra,
125135
upgrade=True,
126136
**kwargs

pyperformance/_venv.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,18 +197,41 @@ def base(self):
197197
self._base = _pythoninfo.get_info(base_exe)
198198
return self._base
199199

200-
def ensure_pip(self, downloaddir=None):
200+
def ensure_pip(self, downloaddir=None, *, installer=True, upgrade=True):
201+
if not upgrade and _pip.is_pip_installed(self.python, env=self._env):
202+
return
201203
ec, _, _ = _pip.install_pip(
202204
self.python,
203205
info=self.info,
204206
downloaddir=downloaddir or self.root,
205207
env=self._env,
208+
upgrade=upgrade,
206209
)
207210
if ec != 0:
208211
raise VenvPipInstallFailedError(root, ec)
209212
elif not _pip.is_pip_installed(self.python, env=self._env):
210213
raise VenvPipInstallFailedError(root, 0, "pip doesn't work")
211214

215+
if installer:
216+
# Upgrade installer dependencies (setuptools, ...)
217+
ec, _, _ = _pip.ensure_installer(
218+
self.python,
219+
env=self._env,
220+
upgrade=True,
221+
)
222+
if ec != 0:
223+
raise RequirementsInstallationFailedError('wheel')
224+
225+
def upgrade_pip(self, *, installer=True):
226+
ec, _, _ = _pip.upgrade_pip(
227+
self.python,
228+
info=self.info,
229+
env=self._env,
230+
installer=installer,
231+
)
232+
if ec != 0:
233+
raise RequirementsInstallationFailedError('pip')
234+
212235
def ensure_reqs(self, *reqs, upgrade=True):
213236
print("Installing requirements into the virtual environment %s" % self.root)
214237
ec, _, _ = _pip.install_requirements(

pyperformance/run.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,16 @@ def run_benchmarks(should_run, python, options):
6060
info = _pythoninfo.get_info(python)
6161
runid = get_run_id(info)
6262

63+
unique = getattr(options, 'unique_venvs', False)
64+
if not unique:
65+
common = VenvForBenchmarks.ensure(
66+
_venv.get_venv_root(runid.name, python=info),
67+
info,
68+
inherit_environ=options.inherit_environ,
69+
)
70+
6371
benchmarks = {}
6472
venvs = set()
65-
if sys.prefix != sys.base_prefix:
66-
venvs.add(sys.prefix)
6773
for i, bench in enumerate(to_run):
6874
bench_runid = runid._replace(bench=bench)
6975
assert bench_runid.name, (bench, bench_runid)
@@ -73,12 +79,20 @@ def run_benchmarks(should_run, python, options):
7379
print('='*50)
7480
print(f'({i+1:>2}/{len(to_run)}) creating venv for benchmark ({bench.name})')
7581
print()
76-
alreadyseen = venv_root in venvs
82+
if not unique:
83+
print('(trying common venv first)')
84+
# Try the common venv first.
85+
try:
86+
common.ensure_reqs(bench)
87+
except _venv.RequirementsInstallationFailedError:
88+
print('(falling back to unique venv)')
89+
else:
90+
benchmarks[bench] = (common, bench_runid)
91+
continue
7792
venv = VenvForBenchmarks.ensure(
7893
venv_root,
7994
info,
8095
inherit_environ=options.inherit_environ,
81-
refresh=not alreadyseen,
8296
)
8397
try:
8498
# XXX Do not override when there is a requirements collision.
@@ -89,6 +103,7 @@ def run_benchmarks(should_run, python, options):
89103
venv = None
90104
venvs.add(venv_root)
91105
benchmarks[bench] = (venv, bench_runid)
106+
print()
92107

93108
suite = None
94109
run_count = str(len(to_run))

pyperformance/venv.py

Lines changed: 53 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class VenvForBenchmarks(_venv.VirtualEnvironment):
120120
@classmethod
121121
def create(cls, root=None, python=None, *,
122122
inherit_environ=None,
123-
install=True,
123+
upgrade=False,
124124
):
125125
env = _get_envvars(inherit_environ)
126126
try:
@@ -131,7 +131,7 @@ def create(cls, root=None, python=None, *,
131131
self.inherit_environ = inherit_environ
132132

133133
try:
134-
self.ensure_pip()
134+
self.ensure_pip(upgrade=upgrade)
135135
except _venv.VenvPipInstallFailedError as exc:
136136
print(f'ERROR: {exc}')
137137
_utils.safe_rmtree(self.root)
@@ -140,92 +140,64 @@ def create(cls, root=None, python=None, *,
140140
_utils.safe_rmtree(self.root)
141141
raise
142142

143-
try:
144-
self.prepare(install)
145-
except BaseException:
146-
print()
147-
_utils.safe_rmtree(venv.root)
148-
raise
143+
# Display the pip version
144+
_pip.run_pip('--version', python=self.python, env=self._env)
149145

150146
return self
151147

152148
@classmethod
153149
def ensure(cls, root, python=None, *,
154-
refresh=True,
155-
install=True,
150+
upgrade=False,
156151
**kwargs
157152
):
158153
if _venv.venv_exists(root):
159154
self = super().ensure(root)
160-
if refresh:
161-
self.prepare(install)
155+
if upgrade:
156+
self.upgrade_pip()
157+
else:
158+
self.ensure_pip(upgrade=False)
162159
return self
163160
else:
164-
return cls.create(root, python, install=install, **kwargs)
161+
return cls.create(
162+
root,
163+
python,
164+
upgrade=upgrade,
165+
**kwargs
166+
)
165167

166168
def __init__(self, root, *, base=None, inherit_environ=None):
167169
super().__init__(root, base=base)
168170
self.inherit_environ = inherit_environ or None
169-
self._prepared = False
170171

171172
@property
172173
def _env(self):
173174
# Restrict the env we use.
174175
return _get_envvars(self.inherit_environ)
175176

176-
def prepare(self, install=True):
177-
print("Installing the virtual environment %s" % self.root)
178-
if self._prepared or (self._prepared is None and not install):
179-
print('(already installed)')
180-
return
181-
182-
if not self._prepared:
183-
# parse requirements
177+
def install_pyperformance(self):
178+
print("installing pyperformance in the venv at %s" % self.root)
179+
# Install pyperformance inside the virtual environment.
180+
if pyperformance.is_dev():
184181
basereqs = Requirements.from_file(REQUIREMENTS_FILE, ['psutil'])
182+
self.ensure_reqs(basereqs)
185183

186-
# Upgrade pip
187-
ec, _, _ = _pip.upgrade_pip(
188-
self.python,
189-
info=self.info,
184+
root_dir = os.path.dirname(pyperformance.PKG_ROOT)
185+
ec, _, _ = _pip.install_editable(
186+
root_dir,
187+
python=self.info,
190188
env=self._env,
191-
installer=True,
192189
)
193-
if ec != 0:
194-
sys.exit(ec)
195-
196-
# XXX not for benchmark venvs
197-
if install:
198-
# install pyperformance inside the virtual environment
199-
# XXX This isn't right...
200-
if pyperformance.is_installed():
201-
root_dir = os.path.dirname(pyperformance.PKG_ROOT)
202-
ec, _, _ = _pip.install_editable(
203-
root_dir,
204-
python=self.info,
205-
env=self._env,
206-
)
207-
else:
208-
version = pyperformance.__version__
209-
ec, _, _ = _pip.install_requirements(
210-
f'pyperformance=={version}',
211-
python=self.info,
212-
env=self._env,
213-
)
214-
if ec != 0:
215-
sys.exit(ec)
216-
self._prepared = True
217190
else:
218-
self._prepared = None
219-
220-
# Display the pip version
221-
_pip.run_pip('--version', python=self.python, env=self._env)
222-
223-
# Dump the package list and their versions: pip freeze
224-
_pip.run_pip('freeze', python=self.python, env=self._env)
191+
version = pyperformance.__version__
192+
ec, _, _ = _pip.install_requirements(
193+
f'pyperformance=={version}',
194+
python=self.info,
195+
env=self._env,
196+
)
197+
if ec != 0:
198+
sys.exit(ec)
225199

226200
def ensure_reqs(self, requirements=None, *, exitonerror=False):
227-
print("Installing requirements into the virtual environment %s" % self.root)
228-
229201
# parse requirements
230202
bench = None
231203
if requirements is None:
@@ -235,20 +207,17 @@ def ensure_reqs(self, requirements=None, *, exitonerror=False):
235207
requirements = Requirements.from_benchmarks([bench])
236208

237209
# Every benchmark must depend on pyperf.
238-
if requirements and bench is not None:
239-
if not requirements.get('pyperf'):
240-
basereqs = Requirements.from_file(REQUIREMENTS_FILE, ['psutil'])
241-
pyperf_req = basereqs.get('pyperf')
242-
if not pyperf_req:
243-
raise NotImplementedError
244-
requirements.specs.append(pyperf_req)
245-
# XXX what about psutil?
210+
if bench is not None and not requirements.get('pyperf'):
211+
basereqs = Requirements.from_file(REQUIREMENTS_FILE, ['psutil'])
212+
pyperf_req = basereqs.get('pyperf')
213+
if not pyperf_req:
214+
raise NotImplementedError
215+
requirements.specs.append(pyperf_req)
216+
# XXX what about psutil?
246217

247218
if not requirements:
248219
print('(nothing to install)')
249220
else:
250-
self.prepare(install=bench is None)
251-
252221
# install requirements
253222
try:
254223
super().ensure_reqs(
@@ -287,6 +256,12 @@ def cmd_venv(options, benchmarks=None):
287256
info = None
288257
exists = _venv.venv_exists(root)
289258

259+
def install(venv):
260+
try:
261+
venv.install_pyperformance()
262+
except _venv.RequirementsInstallationFailedError:
263+
sys.exit(1)
264+
290265
action = options.venv_action
291266
if action == 'create':
292267
requirements = Requirements.from_benchmarks(benchmarks)
@@ -297,6 +272,8 @@ def cmd_venv(options, benchmarks=None):
297272
info,
298273
inherit_environ=options.inherit_environ,
299274
)
275+
venv.ensure_pip()
276+
install(venv)
300277
venv.ensure_reqs(requirements, exitonerror=True)
301278
if not exists:
302279
print("The virtual environment %s has been created" % root)
@@ -313,6 +290,8 @@ def cmd_venv(options, benchmarks=None):
313290
info,
314291
inherit_environ=options.inherit_environ,
315292
)
293+
venv.ensure_pip()
294+
install(venv)
316295
venv.ensure_reqs(requirements, exitonerror=True)
317296
else:
318297
print("The virtual environment %s already exists" % root)
@@ -324,6 +303,8 @@ def cmd_venv(options, benchmarks=None):
324303
info,
325304
inherit_environ=options.inherit_environ,
326305
)
306+
venv.ensure_pip()
307+
install(venv)
327308
venv.ensure_reqs(requirements, exitonerror=True)
328309
print("The virtual environment %s has been recreated" % root)
329310
else:
@@ -332,6 +313,8 @@ def cmd_venv(options, benchmarks=None):
332313
info,
333314
inherit_environ=options.inherit_environ,
334315
)
316+
venv.ensure_pip()
317+
install(venv)
335318
venv.ensure_reqs(requirements, exitonerror=True)
336319
print("The virtual environment %s has been created" % root)
337320

0 commit comments

Comments
 (0)