Skip to content

Require pyperformance to be installed before running #151

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pyperformance.egg-info/

# Created by the pyperformance script
venv/
.venvs/

# Created by the tox program
.tox/
2 changes: 0 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ include README.rst
include TODO.rst
include requirements.in
include requirements.txt
include runtests.py
include tox.ini

include doc/*.rst doc/images/*.png doc/images/*.jpg
include doc/conf.py doc/Makefile doc/make.bat
Expand Down
80 changes: 80 additions & 0 deletions dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# A script for running pyperformance out of the repo in dev-mode.

import os.path
import sys


REPO_ROOT = os.path.dirname(os.path.abspath(__file__))
VENVS = os.path.join(REPO_ROOT, '.venvs')


def ensure_venv_ready(venvroot=None, kind='dev'):
if sys.prefix != sys.base_prefix:
assert os.path.exists(os.path.join(sys.prefix, 'pyvenv.cfg'))
venvroot = sys.prefix
python = sys.executable
readyfile = os.path.join(sys.prefix, 'READY')
isready = os.path.exists(readyfile)
else:
import venv
if not venvroot:
import sysconfig
if sysconfig.is_python_build():
sys.exit('please install your built Python first (or pass it using --python)')
# XXX Handle other implementations too?
base = os.path.join(VENVS, kind or 'dev')
major, minor = sys.version_info[:2]
pyloc = ((os.path.abspath(sys.executable)
).partition(os.path.sep)[2].lstrip(os.path.sep)
).replace(os.path.sep, '-')
venvroot = f'{base}-{major}.{minor}-{pyloc}'
# Make sure the venv exists.
readyfile = os.path.join(venvroot, 'READY')
isready = os.path.exists(readyfile)
if not isready:
relroot = os.path.relpath(venvroot)
if not os.path.exists(venvroot):
print(f'creating venv at {relroot}...')
else:
print(f'venv {relroot} not ready, re-creating...')
venv.create(venvroot, with_pip=True, clear=True)
else:
assert os.path.exists(os.path.join(venvroot, 'pyvenv.cfg'))
# Return the venv's Python executable.
binname = 'Scripts' if os.name == 'nt' else 'bin'
exename = os.path.basename(sys.executable)
python = os.path.join(venvroot, binname, exename)

# Now make sure the venv has pyperformance installed.
if not isready:
import subprocess
relroot = os.path.relpath(venvroot)
print(f'venv {relroot} not ready, installing dependencies...')
proc = subprocess.run(
[python, '-m', 'pip', 'install',
'--upgrade',
'--editable', REPO_ROOT],
)
if proc.returncode != 0:
sys.exit('ERROR: install failed')
with open(readyfile, 'w'):
pass
print('...venv {relroot} ready!')

return python


def main(venvroot=None):
python = ensure_venv_ready(venvroot)
if python != sys.executable:
# Now re-run using the venv.
os.execv(python, [python, *sys.argv])
# <unreachable>

# Now run pyperformance.
import pyperformance.cli
pyperformance.cli.main()


if __name__ == '__main__':
main()
8 changes: 6 additions & 2 deletions pyperformance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,18 @@


def is_installed():
parent = os.path.dirname(PKG_ROOT)
if not os.path.exists(os.path.join(parent, 'setup.py')):
if not is_dev():
return True
if _is_venv():
return True
return _is_devel_install()


def is_dev():
parent = os.path.dirname(PKG_ROOT)
return os.path.exists(os.path.join(parent, 'setup.py'))


def _is_venv():
if sys.base_prefix == sys.prefix:
return False
Expand Down
66 changes: 24 additions & 42 deletions pyperformance/cli.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import argparse
import contextlib
import logging
import os.path
import sys

from pyperformance import _utils, is_installed
from pyperformance.venv import exec_in_virtualenv, cmd_venv
from pyperformance import _utils, is_installed, is_dev
from pyperformance.venv import cmd_venv


def comma_separated(values):
Expand Down Expand Up @@ -136,19 +135,21 @@ def parse_args():
cmds.append(cmd)

# venv
cmd = subparsers.add_parser('venv',
venv_common = argparse.ArgumentParser(add_help=False)
venv_common.add_argument("--venv", help="Path to the virtual environment")
cmd = subparsers.add_parser('venv', parents=[venv_common],
help='Actions on the virtual environment')
cmd.set_defaults(venv_action='show')
venvsubs = cmd.add_subparsers(dest="venv_action")
cmd = venvsubs.add_parser('show')
cmd = venvsubs.add_parser('show', parents=[venv_common])
cmds.append(cmd)
cmd = venvsubs.add_parser('create')
cmd = venvsubs.add_parser('create', parents=[venv_common])
filter_opts(cmd, allow_no_benchmarks=True)
cmds.append(cmd)
cmd = venvsubs.add_parser('recreate')
cmd = venvsubs.add_parser('recreate', parents=[venv_common])
filter_opts(cmd, allow_no_benchmarks=True)
cmds.append(cmd)
cmd = venvsubs.add_parser('remove')
cmd = venvsubs.add_parser('remove', parents=[venv_common])
cmds.append(cmd)

for cmd in cmds:
Expand All @@ -158,15 +159,9 @@ def parse_args():
"names that are inherited from the parent "
"environment when running benchmarking "
"subprocesses."))
cmd.add_argument("--inside-venv", action="store_true",
help=("Option for internal usage only, don't use "
"it directly. Notice that we are already "
"inside the virtual environment."))
cmd.add_argument("-p", "--python",
help="Python executable (default: use running Python)",
default=sys.executable)
cmd.add_argument("--venv",
help="Path to the virtual environment")

options = parser.parse_args()

Expand Down Expand Up @@ -198,21 +193,6 @@ def parse_args():
return (parser, options)


@contextlib.contextmanager
def _might_need_venv(options):
try:
if not is_installed():
# Always force a local checkout to be installed.
assert not options.inside_venv
raise ModuleNotFoundError
yield
except ModuleNotFoundError:
if not options.inside_venv:
print('switching to a venv.', flush=True)
exec_in_virtualenv(options)
raise # re-raise


def _manifest_from_options(options):
from pyperformance import _manifest
return _manifest.load_manifest(options.manifest)
Expand Down Expand Up @@ -253,12 +233,18 @@ def _select_benchmarks(raw, manifest):


def _main():
if not is_installed():
# Always require a local checkout to be installed.
print('ERROR: pyperformance should not be run without installing first')
if is_dev():
print('(consider using the dev.py script)')
sys.exit(1)

parser, options = parse_args()

if options.action == 'venv':
if options.venv_action in ('create', 'recreate'):
with _might_need_venv(options):
benchmarks = _benchmarks_from_options(options)
benchmarks = _benchmarks_from_options(options)
else:
benchmarks = None
cmd_venv(options, benchmarks)
Expand All @@ -280,23 +266,19 @@ def _main():
cmd_show(options)
sys.exit()
elif options.action == 'run':
with _might_need_venv(options):
from pyperformance.cli_run import cmd_run
benchmarks = _benchmarks_from_options(options)
from pyperformance.cli_run import cmd_run
benchmarks = _benchmarks_from_options(options)
cmd_run(options, benchmarks)
elif options.action == 'compare':
with _might_need_venv(options):
from pyperformance.compare import cmd_compare
from pyperformance.compare import cmd_compare
cmd_compare(options)
elif options.action == 'list':
with _might_need_venv(options):
from pyperformance.cli_run import cmd_list
benchmarks = _benchmarks_from_options(options)
from pyperformance.cli_run import cmd_list
benchmarks = _benchmarks_from_options(options)
cmd_list(options, benchmarks)
elif options.action == 'list_groups':
with _might_need_venv(options):
from pyperformance.cli_run import cmd_list_groups
manifest = _manifest_from_options(options)
from pyperformance.cli_run import cmd_list_groups
manifest = _manifest_from_options(options)
cmd_list_groups(manifest)
else:
parser.print_help()
Expand Down
26 changes: 14 additions & 12 deletions pyperformance/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,6 @@ def compile_install(self):
# First: remove everything
self.safe_rmdir(self.conf.build_dir)
self.safe_rmdir(self.conf.prefix)
self.safe_rmdir(self.conf.venv)

self.python.patch(self.patch)
self.python.compile_install()
Expand All @@ -522,18 +521,22 @@ def create_venv(self):
if not python or not exists:
python = sys.executable
cmd = [python, '-u', '-m', 'pyperformance', 'venv', 'recreate',
'--benchmarks', '<NONE>']
if self.conf.venv:
cmd.extend(('--venv', self.conf.venv))
'--venv', self.conf.venv,
'--benchmarks', '<NONE>',
]
if self.options.inherit_environ:
cmd.append('--inherit-environ=%s' % ','.join(self.options.inherit_environ))
exitcode = self.run_nocheck(*cmd)
if exitcode:
sys.exit(EXIT_VENV_ERROR)
binname = 'Scripts' if os.name == 'nt' else 'bin'
base = os.path.basename(python)
return os.path.join(self.conf.venv, binname, base)

def run_benchmark(self):
def run_benchmark(self, python=None):
self.safe_makedirs(os.path.dirname(self.filename))
python = self.python.program
if not python:
python = self.python.program
if self._dryrun:
python = sys.executable
cmd = [python, '-u',
Expand All @@ -549,8 +552,6 @@ def run_benchmark(self):
cmd.append('--benchmarks=%s' % self.conf.benchmarks)
if self.conf.affinity:
cmd.extend(('--affinity', self.conf.affinity))
if self.conf.venv:
cmd.extend(('--venv', self.conf.venv))
if self.conf.debug:
cmd.append('--debug-single-value')
exitcode = self.run_nocheck(*cmd)
Expand Down Expand Up @@ -709,9 +710,12 @@ def compile_bench(self):
except SystemExit:
sys.exit(EXIT_COMPILE_ERROR)

self.create_venv()
if self.conf.venv:
python = self.create_venv()
else:
python = None

failed = self.run_benchmark()
failed = self.run_benchmark(python)
if not failed and self.conf.upload:
self.upload()
return failed
Expand Down Expand Up @@ -992,8 +996,6 @@ def cmd_compile(options):
conf.update = False
if options.no_tune:
conf.system_tune = False
if options.venv:
conf.venv = options.venv
bench = BenchmarkRevision(conf, options.revision, options.branch,
patch=options.patch, options=options)
bench.main()
Expand Down
13 changes: 4 additions & 9 deletions pyperformance/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,15 @@ def run_benchmarks(should_run, python, options):

benchmarks = {}
venvs = set()
if options.venv:
venv = _venv.VirtualEnvironment(
options.python,
options.venv,
inherit_environ=options.inherit_environ,
)
venv.ensure(refresh=False)
venvs.add(venv.get_path())
if sys.prefix != sys.base_prefix:
venvs.add(sys.prefix)
common_venv = None # XXX Add the ability to combine venvs.
for i, bench in enumerate(to_run):
bench_runid = runid._replace(bench=bench)
assert bench_runid.name, (bench, bench_runid)
venv = _venv.VirtualEnvironment(
options.python,
options.venv,
common_venv,
inherit_environ=options.inherit_environ,
name=bench_runid.name,
usebase=True,
Expand Down
5 changes: 1 addition & 4 deletions pyperformance/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,7 @@ def tearDownClass(cls):
def venv_python(self):
return resolve_venv_python(self._VENV)

def run_pyperformance(self, cmd, *args, invenv=True):
if invenv:
assert self._VENV
args += ('--venv', self._VENV)
def run_pyperformance(self, cmd, *args):
run_cmd(
sys.executable, '-u', '-m', 'pyperformance',
cmd, *args,
Expand Down
Loading