From b0f64729a02fbf04ea6a55bee71e517fd1d10ba0 Mon Sep 17 00:00:00 2001 From: frostming Date: Thu, 4 Apr 2019 10:33:10 +0800 Subject: [PATCH] Add windows support --- setup.py | 2 +- trypackage/__main__.py | 11 ++++--- trypackage/core.py | 73 ++++++++++++++++++++++++++++++------------ 3 files changed, 60 insertions(+), 26 deletions(-) diff --git a/setup.py b/setup.py index fafc0e3..4470421 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ def get_version(): packages=find_packages(), include_package_data=True, - install_requires=["click"], + install_requires=["click", "virtualenv"], entry_points={ "console_scripts": [ diff --git a/trypackage/__main__.py b/trypackage/__main__.py index 79fbead..ab196b9 100644 --- a/trypackage/__main__.py +++ b/trypackage/__main__.py @@ -20,9 +20,12 @@ def normalize_python_version(ctx, param, value): # pylint: disable=unused-argument """Normalize given python version.""" if value is None: - return "python{major}.{minor}".format( - major=sys.version_info.major, - minor=sys.version_info.minor) + if os.name == "nt": + return "python" + else: + return "python{major}.{minor}".format( + major=sys.version_info.major, + minor=sys.version_info.minor) if re.match(r"\d\.\d", value): return "python{0}".format(value) @@ -40,7 +43,7 @@ def resolve_package(value): match = re.match(r"([^/]+?/[^/:]+)(?::(.+))?", value) if match: # install from github repository name = match.group(1) - url = "git+git://github.com/{0}".format(match.group(1)) + url = "git+https://github.com/{0}".format(match.group(1)) import_name = match.group(2) if match.group(2) else match.group(1).split("/")[-1] else: # install from PyPI if ":" in value: diff --git a/trypackage/core.py b/trypackage/core.py index 04fba3d..1f7bc20 100644 --- a/trypackage/core.py +++ b/trypackage/core.py @@ -8,11 +8,12 @@ """ import os +import click import shutil import tempfile import contextlib import threading -from subprocess import Popen +from subprocess import Popen, call from collections import namedtuple @@ -48,7 +49,7 @@ def try_packages(packages, virtualenv=None, python_version=None, shell=None, use for package in packages: pip_install(package.url, index) - if shell and not shell == "python": + if shell and shell != "python": # shell could contain cli options: only take first word. pip_install(shell.split()[0]) @@ -88,17 +89,15 @@ def use_virtualenv(virtualenv, python_version): """Use specific virtualenv.""" try: if virtualenv: - # check if given directory is a virtualenv - if not os.path.join(virtualenv, "bin/activate"): - raise TryError("Given directory {0} is not a virtualenv.".format(virtualenv)) - context.virtualenv_path = virtualenv - yield True else: - proc = Popen("virtualenv env -p {0} >> {1}".format(python_version, context.logfile), - shell=True, cwd=context.tempdir_path) + p = Popen("virtualenv env -p {0} >> {1}".format(python_version, context.logfile), + shell=True, cwd=context.tempdir_path) + if p.wait() != 0: + raise RuntimeError("Failed to create virtualenv!") context.virtualenv_path = os.path.join(context.tempdir_path, "env") - yield proc.wait() == 0 + inline_activate_virtualenv(context.virtualenv_path) + yield finally: context.virtualenv_path = None @@ -140,24 +139,56 @@ def use_template(packages): def pip_install(package, index=None): """Install given package in virtualenv.""" - exec_in_virtualenv("python -m pip install {2} {0} >> {1}".format( + exec_command("python -m pip install {2} {0} >> {1}".format( package, context.logfile, "-i {0}".format(index) if index else '')) def run_shell(shell, startup_script): """Run specific python shell.""" - exec_in_virtualenv("PYTHONSTARTUP={0} {1}".format(startup_script, shell)) + exec_command(shell, {"PYTHONSTARTUP": startup_script}) def run_editor(template_path): """Run editor and open the given template file.""" - editor = os.environ.get("EDITOR", "editor") - exec_in_virtualenv("{0} {1} && python {1}".format(editor, template_path)) - - -def exec_in_virtualenv(command): - """Execute command in virtualenv.""" - proc = Popen(". {0}/bin/activate && {1}".format(context.virtualenv_path, command), shell=True) - if proc.wait() != 0: + click.edit(filename=template_path, env=os.environ.copy()) + exec_command("python {}".format(template_path)) + + +def find_virtualenv_file(virtualenv_path, file_path): + """Find activate_this.py script inside a virtualenv_path.""" + for bin_path in ("Scripts", "bin"): + try_path = os.path.join(virtualenv_path, bin_path, file_path) + if os.path.isfile(try_path): + return try_path + return None + + +def exec_command(command, extra_env=None): + """Execute given command in subprocess.""" + kwargs = {"shell": True} + if extra_env: + env = os.environ.copy() + env.update(extra_env) + kwargs.update({"env": env}) + retcode = call(command, **kwargs) + if retcode != 0: raise TryError("Command '{0}' exited with error code: {1}. See {2}".format( - command, proc.returncode, context.logfile)) + command, retcode, context.logfile)) + + +def inline_activate_virtualenv(virtualenv): + """Activate a virtualenv in current process.""" + if not find_virtualenv_file(virtualenv, "activate"): + raise TryError("Given directory {0} is not a virtualenv.".format(virtualenv)) + activate_this = find_virtualenv_file(virtualenv, "activate.py") + if activate_this: # virtualenv + with open(activate_this) as f: + code = compile(f.read(), activate_this, "exec") + exec(code, dict(__file__=activate_this)) + else: # venv module, just prepend to the PATH + os.environ["VIRTUAL_ENV"] = virtualenv + bin_dir = os.path.abspath(os.path.dirname(find_virtualenv_file(virtualenv, "activate"))) + if "PATH" in os.environ: + os.environ["PATH"] = os.path.pathsep.join([bin_dir, os.environ["PATH"]]) + else: + os.environ["PATH"] = bin_dir