diff --git a/pew/pew.py b/pew/pew.py index e8d854b..327d74f 100644 --- a/pew/pew.py +++ b/pew/pew.py @@ -25,6 +25,8 @@ from pythonz.installer.pythoninstaller import PythonInstaller, AlreadyInstalledError from pythonz.commands.list import ListCommand as ListPythons from pythonz.commands.locate import LocateCommand as LocatePython + from pythonz.define import PATH_PYTHONS + from pythonz.util import Package, is_installed else: # Pythonz does not support windows InstallCommand = ListPythons = LocatePython = \ @@ -177,10 +179,39 @@ def shell(env, cwd=None): fork_shell(env, [shell], cwd) +def locate_python_by_version(version, pytype): + """ + Save the user a little typing and attempt to find the installed + Python path given just the version number. + E.g., 3.4.3 -> /home/user/.pythonz/pythons/CPython-3.4.3/bin/python3 + + Only intended to find pythons installed by Pythonz. + Windows is therefore unsupported. + + :param version: numerical version (e.g., 2.7.10, 3.5.1) + :param pytype: Python implementation (e.g., cpython, pypy) + :returns: path string or None + """ + path = None + if windows: + return path + pkg = Package(version, pytype) + if is_installed(pkg): + for bin in ('python3', 'python', 'pypy3', 'pypy'): + guess = os.path.join(PATH_PYTHONS, pkg.name, 'bin', bin) + if os.path.exists(guess): + path = guess + break + return path + + def mkvirtualenv(envname, python=None, packages=[], project=None, - requirements=None, rest=[]): + requirements=None, pytype='cpython', rest=[]): if python: + version_path = locate_python_by_version(python, pytype) + if version_path: + python = version_path rest = ["--python=%s" % python] + rest try: @@ -200,6 +231,8 @@ def mkvirtualenv(envname, python=None, packages=[], project=None, def mkvirtualenv_argparser(): parser = argparse.ArgumentParser() parser.add_argument('-p', '--python') + parser.add_argument('--type', default='cpython', help='Type of Python \ +version: cpython, stackless, pypy, pypy3 or jython.') parser.add_argument('-i', action='append', dest='packages', help='Install \ a package after the environment is created. This option may be repeated.') parser.add_argument('-r', dest='requirements', help='Provide a pip \ @@ -220,9 +253,8 @@ def new_cmd(argv): parser.add_argument('envname') args, rest = parser.parse_known_args(argv) project = expandpath(args.project) if args.project else None - mkvirtualenv(args.envname, args.python, args.packages, project, - args.requirements, rest) + args.requirements, args.type, rest) if args.activate: shell(args.envname) @@ -490,7 +522,7 @@ def mkproject_cmd(argv): sys.exit('Project %s already exists.' % args.envname) mkvirtualenv(args.envname, args.python, args.packages, project.absolute(), - args.requirements, rest) + args.requirements, args.type, rest) project.mkdir() @@ -511,7 +543,7 @@ def mktmpenv_cmd(argv): args, rest = parser.parse_known_args(argv) mkvirtualenv(env, args.python, args.packages, requirements=args.requirements, - rest=rest) + pytype=args.type, rest=rest) print('This is a temporary environment. It will be deleted when you exit') try: if args.activate: diff --git a/tests/test_mkvirtualenv.py b/tests/test_mkvirtualenv.py index 78c8924..deab5b9 100644 --- a/tests/test_mkvirtualenv.py +++ b/tests/test_mkvirtualenv.py @@ -1,3 +1,5 @@ +import re +import sys from os import unlink from subprocess import check_call, CalledProcessError from pathlib import Path @@ -12,6 +14,10 @@ import pytest from pew._utils import invoke_pew as invoke +from utils import skip_windows + +if not sys.platform == 'win32': + from pythonz.define import PATH_PYTHONS def are_we_connected(): @@ -26,6 +32,48 @@ def are_we_connected(): reason="An internet connection is required") +def pythonz_python_found(): + python_path = Path(PATH_PYTHONS) + found = python_path.exists() + if found: + found = sum(1 for __ in python_path.iterdir()) > 0 + return found + + +pythonz_python_required = pytest.mark.skipif(not pythonz_python_found(), + reason='Pythonz is required') + +def get_pythonz_python_version(): + python_path = Path(PATH_PYTHONS) + pattern = '^\w+-(\d+\.?)+?' + for f in python_path.iterdir(): + if re.match(pattern, f.name): + return f.name.split('-')[1] + + +@skip_windows(reason="Pythonz is unsupported") +@pythonz_python_required +def test_good_version_resolved_correctly(workon_home): + version = get_pythonz_python_version() + returncode = invoke('new', 'env', '-d', '-p', version).returncode + invoke('rm', 'env') + assert returncode == 0 + + +@skip_windows(reason="Pythonz is unsupported") +@pythonz_python_required +def test_bad_version_not_resolved(workon_home): + version = get_pythonz_python_version() + ".broken" + returncode = invoke('new', 'env', '-d', '-p', version).returncode + assert returncode != 0 + + +def test_explict_path_accepted(workon_home): + returncode = invoke('new', 'env', '-d', '-p', 'python').returncode + invoke('rm', 'env') + assert returncode == 0 + + def test_create(workon_home): envs = set(invoke('ls').out.split()) invoke('new', 'env', '-d')