Merge pull request #1605 from pypa/feature/vendor-pew

Vendor pew under script name `pewtwo`
This commit is contained in:
2018-03-08 12:42:20 -05:00
committed by GitHub
20 changed files with 1342 additions and 9 deletions
+1 -1
View File
@@ -975,7 +975,7 @@ def do_create_virtualenv(python=None, site_packages=False):
click.echo(crayons.normal(u'Making site-packages available…', bold=True), err=True)
os.environ['VIRTUAL_ENV'] = project.virtualenv_location
delegator.run('pipenv run pew toggleglobalsitepackages')
delegator.run('pipenv run pewtwo toggleglobalsitepackages')
del os.environ['VIRTUAL_ENV']
# Say where the virtualenv is.
+4
View File
@@ -0,0 +1,4 @@
from __future__ import absolute_import
from . import pew
__all__ = ['pew']
+3
View File
@@ -0,0 +1,3 @@
from pew.pew import pew
pew()
+55
View File
@@ -0,0 +1,55 @@
from __future__ import division, print_function
import os
from math import ceil
try:
from itertools import zip_longest
except ImportError:
from itertools import izip_longest as zip_longest
try:
from shutil import get_terminal_size
except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size
SEP = ' '
L = len(SEP)
def get_rows(venvs, columns_number):
lines_number = int(ceil(len(venvs) / columns_number))
for i in range(lines_number):
yield venvs[i::lines_number]
def row_len(names):
return sum(map(len, names)) + L * len(names) - L
def get_best_columns_number(venvs):
max_width, _ = get_terminal_size()
columns_number = 1
for columns_number in range(1, len(venvs) + 1):
rows = get_rows(venvs, columns_number)
if max(map(row_len, rows)) > max_width:
return (columns_number - 1) or 1
else:
return columns_number
def align_column(column):
m = max(map(len, column))
return [name.ljust(m) for name in column]
def columnize(venvs):
columns_n = get_best_columns_number(venvs)
columns = map(align_column, zip_longest(*get_rows(venvs, columns_n), fillvalue=''))
return map(SEP.join, zip(*columns))
def print_virtualenvs(*venvs):
venvs = sorted(venvs)
if os.isatty(1):
print(*columnize(venvs), sep='\n')
else:
print(*venvs, sep=' ')
+89
View File
@@ -0,0 +1,89 @@
import os
import sys
import locale
from codecs import getwriter
from contextlib import contextmanager
from subprocess import check_call, Popen, PIPE
from collections import namedtuple
from functools import partial, wraps
from pathlib import Path
from tempfile import NamedTemporaryFile as _ntf
try:
from shutil import which
except ImportError:
from shutilwhich import which
py2 = sys.version_info[0] == 2
windows = sys.platform == 'win32'
if py2 or windows:
locale.setlocale(locale.LC_CTYPE, '')
encoding = locale.getlocale()[1] or 'ascii'
if py2:
@wraps(_ntf)
def NamedTemporaryFile(mode):
return getwriter(encoding)(_ntf(mode))
def to_unicode(x):
return x.decode(encoding)
else:
NamedTemporaryFile = _ntf
to_unicode = str
def check_path():
parent = os.path.dirname
return parent(parent(which('python'))) == os.environ['VIRTUAL_ENV']
def resolve_path(f):
def call(cmd, **kwargs):
ex = cmd[0]
ex = which(ex) or ex
return f([ex] + list(cmd[1:]), **kwargs) # list-conversion is required in case `cmd` is a tuple
return call
if windows:
check_call = resolve_path(check_call)
Popen = resolve_path(Popen)
Result = namedtuple('Result', 'returncode out err')
# TODO: it's better to fail early, and thus I'd need to check the exit code, but it'll
# need a refactoring of a couple of tests
def invoke(*args, **kwargs):
inp = kwargs.pop('inp', '').encode(encoding)
popen = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, **kwargs)
out, err = [o.strip().decode(encoding) for o in popen.communicate(inp)]
return Result(popen.returncode, out, err)
invoke_pew = partial(invoke, 'pew')
env_bin_dir = 'bin' if sys.platform != 'win32' else 'Scripts'
def expandpath(path):
return Path(os.path.expanduser(os.path.expandvars(path)))
def own(path):
if sys.platform == 'win32':
# Even if run by an administrator, the permissions will be set
# correctly on Windows, no need to check
return True
while not path.exists():
path = path.parent
return path.stat().st_uid == os.getuid()
@contextmanager
def temp_environ():
environ = dict(os.environ)
try:
yield
finally:
os.environ.clear()
os.environ.update(environ)
+107
View File
@@ -0,0 +1,107 @@
# -*- coding=utf-8 -*-
# psutil is painfully slow in win32. So to avoid adding big
# dependencies like pywin32 a ctypes based solution is preferred
# Code based on the winappdbg project http://winappdbg.sourceforge.net/
# (BSD License) - adapted from Celery
# https://github.com/celery/celery/blob/2.5-archived/celery/concurrency/processes/_win.py
import os
from ctypes import (
byref, sizeof, windll, Structure, WinError, POINTER,
c_size_t, c_char, c_void_p
)
from ctypes.wintypes import DWORD, LONG
ERROR_NO_MORE_FILES = 18
INVALID_HANDLE_VALUE = c_void_p(-1).value
class PROCESSENTRY32(Structure):
_fields_ = [
('dwSize', DWORD),
('cntUsage', DWORD),
('th32ProcessID', DWORD),
('th32DefaultHeapID', c_size_t),
('th32ModuleID', DWORD),
('cntThreads', DWORD),
('th32ParentProcessID', DWORD),
('pcPriClassBase', LONG),
('dwFlags', DWORD),
('szExeFile', c_char * 260),
]
LPPROCESSENTRY32 = POINTER(PROCESSENTRY32)
def CreateToolhelp32Snapshot(dwFlags=2, th32ProcessID=0):
hSnapshot = windll.kernel32.CreateToolhelp32Snapshot(
dwFlags,
th32ProcessID
)
if hSnapshot == INVALID_HANDLE_VALUE:
raise WinError()
return hSnapshot
def Process32First(hSnapshot):
pe = PROCESSENTRY32()
pe.dwSize = sizeof(PROCESSENTRY32)
success = windll.kernel32.Process32First(hSnapshot, byref(pe))
if not success:
if windll.kernel32.GetLastError() == ERROR_NO_MORE_FILES:
return
raise WinError()
return pe
def Process32Next(hSnapshot, pe=None):
if pe is None:
pe = PROCESSENTRY32()
pe.dwSize = sizeof(PROCESSENTRY32)
success = windll.kernel32.Process32Next(hSnapshot, byref(pe))
if not success:
if windll.kernel32.GetLastError() == ERROR_NO_MORE_FILES:
return
raise WinError()
return pe
def get_all_processes():
"""Return a dictionary of properties about all processes.
>>> get_all_processes()
{
1509: {
'parent_pid': 1201,
'executable': 'C:\\Program\\\\ Files\\Python36\\python.exe'
}
}
"""
h_process = CreateToolhelp32Snapshot()
pids = {}
pe = Process32First(h_process)
while pe:
pids[pe.th32ProcessID] = {
'executable': str(pe.szExeFile.decode('utf-8'))
}
if pe.th32ParentProcessID:
pids[pe.th32ProcessID]['parent_pid'] = pe.th32ParentProcessID
pe = Process32Next(h_process, pe)
return pids
def get_grandparent_process(pid=None):
"""Get grandparent process name of the supplied pid or os.getpid().
:param int pid: The pid to track.
:return: Name of the grandparent process.
"""
if not pid:
pid = os.getpid()
processes = get_all_processes()
ppid = processes[pid]['parent_pid']
parent = processes[ppid]
grandparent = processes[parent['parent_pid']]
return grandparent['executable']
+768
View File
@@ -0,0 +1,768 @@
from __future__ import print_function, absolute_import, unicode_literals
import os
import sys
import argparse
import shutil
import random
import textwrap
from functools import partial
from subprocess import CalledProcessError
from pathlib import Path
try:
from shutil import get_terminal_size
except ImportError:
from backports.shutil_get_terminal_size import get_terminal_size
windows = sys.platform == 'win32'
from clonevirtualenv import clone_virtualenv
if not windows:
try:
# Try importing these packages if avaiable
from pythonz.commands.install import InstallCommand
from pythonz.commands.uninstall import UninstallCommand
from pythonz.installer.pythoninstaller import PythonInstaller, AlreadyInstalledError
from pythonz.commands.list import ListCommand as ListPythons
from pythonz.define import PATH_PYTHONS
from pythonz.commands.locate import LocateCommand as LocatePython
except:
# create mock commands
InstallCommand = ListPythons = LocatePython = UninstallCommand = \
lambda : sys.exit('You need to install the pythonz extra. pip install pew[pythonz]')
else:
# Pythonz does not support windows
InstallCommand = ListPythons = LocatePython = UninstallCommand = \
lambda : sys.exit('Command not supported on this platform')
from ._win_utils import get_grandparent_process
from pew._utils import (check_call, invoke, expandpath, own, env_bin_dir,
check_path, temp_environ, NamedTemporaryFile, to_unicode)
from pew._print_utils import print_virtualenvs
if sys.version_info[0] == 2:
input = raw_input
err = partial(print, file=sys.stderr)
if windows:
default_home = '~/.virtualenvs'
else:
default_home = os.path.join(
os.environ.get('XDG_DATA_HOME', '~/.local/share'), 'virtualenvs')
workon_home = expandpath(
os.environ.get('WORKON_HOME', default_home))
def makedirs_and_symlink_if_needed(workon_home):
if not workon_home.exists() and own(workon_home):
workon_home.mkdir(parents=True)
link = expandpath('~/.virtualenvs')
if os.name == 'posix' and 'WORKON_HOME' not in os.environ and \
'XDG_DATA_HOME' not in os.environ and not link.exists():
link.symlink_to(str(workon_home))
return True
else:
return False
pew_site = Path(__file__).parent
def supported_shell():
shell = Path(os.environ.get('SHELL', '')).stem
if shell in ('bash', 'zsh', 'fish'):
return shell
def shell_config_cmd(argv):
"Prints the path for the current $SHELL helper file"
shell = supported_shell()
if shell:
print(pew_site / 'shell_config' / ('init.' + shell))
else:
err('Completions and prompts are unavailable for %s' %
repr(os.environ.get('SHELL', '')))
def deploy_completions():
completions = {'complete.bash': Path('/etc/bash_completion.d/pew'),
'complete.zsh': Path('/usr/local/share/zsh/site-functions/_pew'),
'complete.fish': Path('/etc/fish/completions/pew.fish')}
for comp, dest in completions.items():
if not dest.parent.exists():
dest.parent.mkdir(parents=True)
shutil.copy(str(pew_site / 'shell_config' / comp), str(dest))
def get_project_dir(env):
project_file = workon_home / env / '.project'
if project_file.exists():
with project_file.open() as f:
project_dir = f.readline().strip()
if os.path.exists(project_dir):
return project_dir
else:
err('Corrupted or outdated:', project_file, '\nDirectory',
project_dir, "doesn't exist.")
def unsetenv(key):
if key in os.environ:
del os.environ[key]
def compute_path(env):
envdir = workon_home / env
return os.pathsep.join([
str(envdir / env_bin_dir),
os.environ['PATH'],
])
def inve(env, command, *args, **kwargs):
"""Run a command in the given virtual environment.
Pass additional keyword arguments to ``subprocess.check_call()``."""
# we don't strictly need to restore the environment, since pew runs in
# its own process, but it feels like the right thing to do
with temp_environ():
os.environ['VIRTUAL_ENV'] = str(workon_home / env)
os.environ['PATH'] = compute_path(env)
unsetenv('PYTHONHOME')
unsetenv('__PYVENV_LAUNCHER__')
try:
return check_call([command] + list(args), shell=windows, **kwargs)
# need to have shell=True on windows, otherwise the PYTHONPATH
# won't inherit the PATH
except OSError as e:
if e.errno == 2:
err('Unable to find', command)
else:
raise
def fork_shell(env, shellcmd, cwd):
or_ctrld = '' if windows else "or 'Ctrl+D' "
err("Launching subshell in virtual environment. Type 'exit' ", or_ctrld,
"to return.", sep='')
if 'VIRTUAL_ENV' in os.environ:
err("Be aware that this environment will be nested on top "
"of '%s'" % Path(os.environ['VIRTUAL_ENV']).name)
inve(env, *shellcmd, cwd=cwd)
def fork_bash(env, cwd):
# bash is a special little snowflake, and prevent_path_errors cannot work there
# https://github.com/berdario/pew/issues/58#issuecomment-102182346
bashrcpath = expandpath('~/.bashrc')
if bashrcpath.exists():
with NamedTemporaryFile('w+') as rcfile:
with bashrcpath.open() as bashrc:
rcfile.write(bashrc.read())
rcfile.write('\nexport PATH="' + to_unicode(compute_path(env)) + '"')
rcfile.flush()
fork_shell(env, ['bash', '--rcfile', rcfile.name], cwd)
else:
fork_shell(env, ['bash'], cwd)
def fork_cmder(env, cwd):
shell_cmd = ['cmd']
cmderrc_path = r'%CMDER_ROOT%\vendor\init.bat'
if expandpath(cmderrc_path).exists():
shell_cmd += ['/k', cmderrc_path]
if cwd:
os.environ['CMDER_START'] = cwd
fork_shell(env, shell_cmd, cwd)
def _detect_shell():
shell = os.environ.get('SHELL', None)
if not shell:
if 'CMDER_ROOT' in os.environ:
shell = 'Cmder'
elif windows:
shell = get_grandparent_process(os.getpid())
else:
shell = 'sh'
return shell
def shell(env, cwd=None):
env = str(env)
shell = _detect_shell()
shell_name = Path(shell).stem
if shell_name not in ('Cmder', 'bash', 'elvish', 'powershell', 'klingon', 'cmd'):
# On Windows the PATH is usually set with System Utility
# so we won't worry about trying to check mistakes there
shell_check = (sys.executable + ' -c "from pew.pew import '
'prevent_path_errors; prevent_path_errors()"')
try:
inve(env, shell, '-c', shell_check)
except CalledProcessError:
return
if shell_name == 'bash':
fork_bash(env, cwd)
elif shell_name == 'Cmder':
fork_cmder(env, cwd)
else:
fork_shell(env, [shell], cwd)
def mkvirtualenv(envname, python=None, packages=[], project=None,
requirements=None, rest=[]):
if python:
rest = ["--python=%s" % python] + rest
path = (workon_home / envname).absolute()
try:
check_call([sys.executable, "-m", "virtualenv", str(path)] + rest)
except (CalledProcessError, KeyboardInterrupt):
rmvirtualenvs([envname])
raise
else:
if project:
setvirtualenvproject(envname, project.absolute())
if requirements:
inve(envname, 'pip', 'install', '-r', str(expandpath(requirements)))
if packages:
inve(envname, 'pip', 'install', *packages)
def mkvirtualenv_argparser():
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--python')
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 \
requirements file to install a base set of packages into the new environment.')
parser.add_argument('-d', '--dont-activate', action='store_false',
default=True, dest='activate', help="After \
creation, continue with the existing shell (don't \
activate the new environment).")
return parser
def new_cmd(argv):
"""Create a new environment, in $WORKON_HOME."""
parser = mkvirtualenv_argparser()
parser.add_argument('-a', dest='project', help='Provide a full path to a \
project directory to associate with the new environment.')
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)
if args.activate:
shell(args.envname)
def rmvirtualenvs(envs):
error_happened = False
for env in envs:
env = workon_home / env
if os.environ.get('VIRTUAL_ENV') == str(env):
err("ERROR: You cannot remove the active environment (%s)." % env)
error_happened = True
break
try:
shutil.rmtree(str(env))
except OSError as e:
err("Error while trying to remove the {0} env: \n{1}".format
(env, e.strerror))
error_happened = True
return error_happened
def rm_cmd(argv):
"""Remove one or more environment, from $WORKON_HOME."""
if len(argv) < 1:
sys.exit("Please specify an environment")
return rmvirtualenvs(argv)
def packages(site_packages):
nodes = site_packages.iterdir()
return set([x.stem.split('-')[0] for x in nodes]) - set(['__pycache__'])
def showvirtualenv(env):
columns, _ = get_terminal_size()
pkgs = sorted(packages(sitepackages_dir(env)))
env_python = workon_home / env / env_bin_dir / 'python'
l = len(env) + 2
version = invoke(str(env_python), '-V')
version = ' - '.join((version.out + version.err).splitlines())
print(env, ': ', version, sep='')
print(textwrap.fill(' '.join(pkgs),
width=columns-l,
initial_indent=(l * ' '),
subsequent_indent=(l * ' ')), '\n')
def show_cmd(argv):
try:
showvirtualenv(argv[0])
except IndexError:
if 'VIRTUAL_ENV' in os.environ:
showvirtualenv(Path(os.environ['VIRTUAL_ENV']).name)
else:
sys.exit('pew show [env]')
def lsenvs():
return sorted(set(env.parts[-3] for env in
workon_home.glob(os.path.join('*', env_bin_dir, 'python*'))))
def lsvirtualenv(verbose):
envs = lsenvs()
if not verbose:
print_virtualenvs(*envs)
else:
for env in envs:
showvirtualenv(env)
def ls_cmd(argv):
"""List available environments."""
parser = argparse.ArgumentParser()
p_group = parser.add_mutually_exclusive_group()
p_group.add_argument('-b', '--brief', action='store_false')
p_group.add_argument('-l', '--long', action='store_true')
args = parser.parse_args(argv)
lsvirtualenv(args.long)
def parse_envname(argv, no_arg_callback):
if len(argv) < 1:
no_arg_callback()
env = argv[0]
if env.startswith('/'):
sys.exit("ERROR: Invalid environment name '{0}'.".format(env))
if not (workon_home / env).exists():
sys.exit("ERROR: Environment '{0}' does not exist. Create it with \
'pew new {0}'.".format(env))
else:
return env
def workon_cmd(argv):
"""List or change working virtual environments."""
def list_and_exit():
lsvirtualenv(False)
sys.exit(0)
env = parse_envname(argv, list_and_exit)
# Check if the virtualenv has an associated project directory and in
# this case, use it as the current working directory.
project_dir = get_project_dir(env) or os.getcwd()
shell(env, cwd=project_dir)
def sitepackages_dir(env=os.environ.get('VIRTUAL_ENV')):
if not env:
sys.exit('ERROR: no virtualenv active')
else:
env_python = workon_home / env / env_bin_dir / 'python'
return Path(invoke(str(env_python), '-c', 'import distutils; \
print(distutils.sysconfig.get_python_lib())').out)
def add_cmd(argv):
"""Add the specified directories to the Python path for the currently active virtualenv.
This will be done by placing the directory names in a path file named
"virtualenv_path_extensions.pth" inside the virtualenv's site-packages
directory; if this file does not exists, it will be created first.
"""
parser = argparse.ArgumentParser()
parser.add_argument('-d', dest='remove', action='store_true')
parser.add_argument('dirs', nargs='+')
args = parser.parse_args(argv)
extra_paths = sitepackages_dir() / '_virtualenv_path_extensions.pth'
new_paths = [os.path.abspath(d) + "\n" for d in args.dirs]
if not extra_paths.exists():
with extra_paths.open('w') as extra:
extra.write('''import sys; sys.__plen = len(sys.path)
import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)
''')
def rewrite(f):
with extra_paths.open('r+') as extra:
to_write = f(extra.readlines())
extra.seek(0)
extra.truncate()
extra.writelines(to_write)
if args.remove:
rewrite(lambda ls: [line for line in ls if line not in new_paths])
else:
rewrite(lambda lines: lines[0:1] + new_paths + lines[1:])
def sitepackages_dir_cmd(argv):
print(sitepackages_dir())
def lssitepackages_cmd(argv):
"""Show the content of the site-packages directory of the current virtualenv."""
site = sitepackages_dir()
print(*sorted(site.iterdir()), sep=os.linesep)
extra_paths = site / '_virtualenv_path_extensions.pth'
if extra_paths.exists():
print('from _virtualenv_path_extensions.pth:')
with extra_paths.open() as extra:
print(''.join(extra.readlines()))
def toggleglobalsitepackages_cmd(argv):
"""Toggle the current virtualenv between having and not having access to the global site-packages."""
quiet = argv == ['-q']
site = sitepackages_dir()
ngsp_file = site.parent / 'no-global-site-packages.txt'
if ngsp_file.exists():
ngsp_file.unlink()
if not quiet:
print('Enabled global site-packages')
else:
with ngsp_file.open('w'):
if not quiet:
print('Disabled global site-packages')
def cp_cmd(argv):
"""Duplicate the named virtualenv to make a new one."""
parser = argparse.ArgumentParser()
parser.add_argument('source')
parser.add_argument('target', nargs='?')
parser.add_argument('-d', '--dont-activate', action='store_false',
default=True, dest='activate', help="After \
creation, continue with the existing shell (don't \
activate the new environment).")
args = parser.parse_args(argv)
target_name = copy_virtualenv_project(args.source, args.target)
if args.activate:
shell(target_name)
def copy_virtualenv_project(source, target):
source = expandpath(source)
if not source.exists():
source = workon_home / source
if not source.exists():
sys.exit('Please provide a valid virtualenv to copy')
target_name = target or source.name
target = workon_home / target_name
if target.exists():
sys.exit('%s virtualenv already exists in %s.' % (
target_name, workon_home
))
print('Copying {0} in {1}'.format(source, target_name))
clone_virtualenv(str(source), str(target))
return target_name
def rename_cmd(argv):
"""Rename a virtualenv"""
parser = argparse.ArgumentParser()
parser.add_argument('source')
parser.add_argument('target')
pargs = parser.parse_args(argv)
copy_virtualenv_project(pargs.source, pargs.target)
return rmvirtualenvs([pargs.source])
def setvirtualenvproject(env, project):
print('Setting project for {0} to {1}'.format(env, project))
with (workon_home / env / '.project').open('wb') as prj:
prj.write(str(project).encode())
def setproject_cmd(argv):
"""Given a virtualenv directory and a project directory, set the
virtualenv up to be associated with the project."""
args = dict(enumerate(argv))
project = os.path.abspath(args.get(1, '.'))
env = args.get(0, os.environ.get('VIRTUAL_ENV'))
if not env:
sys.exit('pew setproject [virtualenv] [project_path]')
if not (workon_home / env).exists():
sys.exit("Environment '%s' doesn't exist." % env)
if not os.path.isdir(project):
sys.exit('pew setproject: %s does not exist' % project)
setvirtualenvproject(env, project)
def mkproject_cmd(argv):
"""Create a new project directory and its associated virtualenv."""
if '-l' in argv or '--list' in argv:
templates = [t.name[9:] for t in workon_home.glob("template_*")]
print("Available project templates:", *templates, sep='\n')
return
parser = mkvirtualenv_argparser()
parser.add_argument('envname')
parser.add_argument(
'-t', action='append', default=[], dest='templates', help='Multiple \
templates may be selected. They are applied in the order specified on the \
command line.')
parser.add_argument(
'-l', '--list', action='store_true', help='List available templates.')
args, rest = parser.parse_known_args(argv)
projects_home = Path(os.environ.get('PROJECT_HOME', '.'))
if not projects_home.exists():
sys.exit('ERROR: Projects directory %s does not exist. \
Create it or set PROJECT_HOME to an existing directory.' % projects_home)
project = (projects_home / args.envname).absolute()
if project.exists():
sys.exit('Project %s already exists.' % args.envname)
mkvirtualenv(args.envname, args.python, args.packages, project.absolute(),
args.requirements, rest)
project.mkdir()
for template_name in args.templates:
template = workon_home / ("template_" + template_name)
inve(args.envname, str(template), args.envname, str(project))
if args.activate:
shell(args.envname, cwd=str(project))
def mktmpenv_cmd(argv):
"""Create a temporary virtualenv."""
parser = mkvirtualenv_argparser()
env = '.'
while (workon_home / env).exists():
env = hex(random.getrandbits(64))[2:-1]
args, rest = parser.parse_known_args(argv)
mkvirtualenv(env, args.python, args.packages, requirements=args.requirements,
rest=rest)
print('This is a temporary environment. It will be deleted when you exit')
try:
if args.activate:
# only used for testing on windows
shell(env)
finally:
return rmvirtualenvs([env])
def wipeenv_cmd(argv):
"""Remove all installed packages from the current (or supplied) env."""
env = argv[0] if argv else os.environ.get('VIRTUAL_ENV')
if not env:
sys.exit('ERROR: no virtualenv active')
elif not (workon_home / env).exists():
sys.exit("ERROR: Environment '{0}' does not exist.".format(env))
else:
env_pip = str(workon_home / env / env_bin_dir / 'pip')
all_pkgs = set(invoke(env_pip, 'freeze').out.splitlines())
pkgs = set(p for p in all_pkgs if len(p.split("==")) == 2)
ignored = sorted(all_pkgs - pkgs)
pkgs = set(p.split("==")[0] for p in pkgs)
to_remove = sorted(pkgs - set(['distribute', 'wsgiref']))
if to_remove:
print("Ignoring:\n %s" % "\n ".join(ignored))
print("Uninstalling packages:\n %s" % "\n ".join(to_remove))
inve(env, 'pip', 'uninstall', '-y', *to_remove)
else:
print("Nothing to remove")
def inall_cmd(argv):
"""Run a command in each virtualenv."""
envs = lsenvs()
errors = False
for env in envs:
print("\n%s:" % env)
try:
inve(env, *argv)
except CalledProcessError as e:
errors = True
err(e)
sys.exit(errors)
def in_cmd(argv):
"""Run a command in the given virtualenv."""
if len(argv) == 1:
return workon_cmd(argv)
parse_envname(argv, lambda : sys.exit('You must provide a valid virtualenv to target'))
inve(*argv)
def restore_cmd(argv):
"""Try to restore a broken virtualenv by reinstalling the same python version on top of it"""
if len(argv) < 1:
sys.exit('You must provide a valid virtualenv to target')
env = argv[0]
path = workon_home / env
py = path / env_bin_dir / ('python.exe' if windows else 'python')
exact_py = py.resolve().name
check_call([sys.executable, "-m", "virtualenv", str(path.absolute()), "--python=%s" % exact_py])
def dir_cmd(argv):
"""Print the path for the virtualenv directory"""
env = parse_envname(argv, lambda : sys.exit('You must provide a valid virtualenv to target'))
print(workon_home / env)
def install_cmd(argv):
'''Use Pythonz to download and build the specified Python version'''
installer = InstallCommand()
options, versions = installer.parser.parse_args(argv)
if len(versions) != 1:
installer.parser.print_help()
sys.exit(1)
else:
try:
actual_installer = PythonInstaller.get_installer(versions[0], options)
actual_installer.install()
except AlreadyInstalledError as e:
print(e)
def uninstall_cmd(argv):
'''Use Pythonz to uninstall the specified Python version'''
UninstallCommand().run(argv)
def list_pythons_cmd(argv):
'''List the pythons installed by Pythonz (or all the installable ones)'''
try:
Path(PATH_PYTHONS).mkdir(parents=True)
except OSError:
pass
ListPythons().run(argv)
def locate_python_cmd(argv):
'''Locate the path for the python version installed by Pythonz'''
LocatePython().run(argv)
def version_cmd(argv):
"""Prints current pew version"""
import pkg_resources
try:
__version__ = pkg_resources.get_distribution('pew').version
except pkg_resources.DistributionNotFound:
__version__ = 'unknown'
print('Setuptools has some issues here, failed to get our own package.', file=sys.stderr)
print(__version__)
def prevent_path_errors():
if 'VIRTUAL_ENV' in os.environ and not check_path():
sys.exit('''ERROR: The virtualenv hasn't been activated correctly.
Either the env is corrupted (try running `pew restore env`),
Or an upgrade of your Python version broke your env,
Or check the contents of your $PATH. You might be adding new directories to it
from inside your shell's configuration file.
In this case, for further details please see: https://github.com/berdario/pew#the-environment-doesnt-seem-to-be-activated''')
def first_run_setup():
shell = supported_shell()
if shell:
if shell == 'fish':
source_cmd = 'source (pew shell_config)'
else:
source_cmd = 'source $(pew shell_config)'
rcpath = expandpath({'bash': '~/.bashrc'
, 'zsh': '~/.zshrc'
, 'fish': '~/.config/fish/config.fish'}[shell])
if rcpath.exists():
update_config_file(rcpath, source_cmd)
else:
print("It seems that you're running pew for the first time\n"
"If you want source shell competions and update your prompt, "
"Add the following line to your shell config file:\n %s" % source_cmd)
print('\nWill now continue with the command:', *sys.argv[1:])
input('[enter]')
def update_config_file(rcpath, source_cmd):
with rcpath.open('r+') as rcfile:
if source_cmd not in (line.strip() for line in rcfile.readlines()):
choice = 'X'
while choice not in ('y', '', 'n'):
choice = input("It seems that you're running pew for the first time\n"
"do you want to modify %s to source completions and"
" update your prompt? [y/N]\n> " % rcpath).lower()
if choice == 'y':
rcfile.write('\n# added by Pew\n%s\n' % source_cmd)
print('Done')
else:
print('\nOk, if you want to do it manually, just add\n %s\nat'
' the end of %s' % (source_cmd, rcpath))
def print_commands(cmds):
longest = max(map(len, cmds)) + 3
columns, _ = get_terminal_size()
print('Available commands:\n')
for cmd, fun in sorted(cmds.items()):
if fun.__doc__:
print(textwrap.fill(
fun.__doc__.splitlines()[0],
columns or 1000,
initial_indent=(' {0}: '.format(cmd)).ljust(longest),
subsequent_indent=longest * ' '))
else:
print(' ' + cmd)
def pew():
first_run = makedirs_and_symlink_if_needed(workon_home)
if first_run and sys.stdin.isatty():
first_run_setup()
cmds = dict((cmd[:-4], fun)
for cmd, fun in globals().items() if cmd.endswith('_cmd'))
if sys.argv[1:]:
if sys.argv[1] in cmds:
command = cmds[sys.argv[1]]
try:
return command(sys.argv[2:])
except CalledProcessError as e:
return e.returncode
except KeyboardInterrupt:
pass
else:
err("ERROR: command", sys.argv[1], "does not exist.")
print_commands(cmds)
sys.exit(1)
else:
print_commands(cmds)
+1
View File
@@ -0,0 +1 @@
complete.zsh
@@ -0,0 +1,46 @@
# Homebrew on Macs have version 1.3 of bash-completion which doesn't include
# _init_completion. This is a very minimal version of that function.
__my_init_completion()
{
COMPREPLY=()
_get_comp_words_by_ref cur prev words cword
}
_pew()
{
local cur prev words cword args commands
if declare -F _init_completions >/dev/null 2>&1; then
_init_completion || return
else
__my_init_completion || return
fi
args="--help --python -i -a -r"
commands="ls add mkproject rm lssitepackages cp workon new mktmpenv setproject show wipeenv sitepackages_dir inall toggleglobalsitepackages rename restore install list_pythons locate_python"
case $prev in
ls|show|rm|workon|cp|setproject|rename|wipeenv)
COMPREPLY=( $(compgen -W "$(pew ls)" -- ${cur}) )
return 0
;;
inall)
_command_offset 2
return 0
;;
mktmpenv|new)
COMPREPLY=( $(compgen -W "${args}" -- ${cur}) )
return 0
;;
mkproject)
COMPREPLY=( $(compgen -W "${args} -t --list" -- ${cur}) )
return 0
;;
add)
COMPREPLY=( $(compgen -W "--help -d" -- ${cur}) )
return 0
;;
esac
COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) )
} &&
complete -o default -F _pew pew
@@ -0,0 +1,116 @@
set pew pew
function __pew_needs_command
set cmd (commandline -opc)
if [ (count $cmd) -eq 1 -a $cmd[1] = "$pew" ]
return 0
end
return 1
end
function __pew_list_envs
eval "$pew ls" | tr " " "\n"
end
function __pew_using_command
set cmd (commandline -opc)
if test (count $cmd) -gt 1
if test $argv[1] = $cmd[2]
return 0
end
end
return 1
end
#### new
complete -f -c $pew -n '__pew_needs_command' -a new -d 'Create a new environment'
complete -f -c $pew -n '__pew_using_command new' -s h -d 'Show help'
complete -f -c $pew -n '__pew_using_command new' -s p -l python -d 'Python executable'
complete -f -c $pew -n '__pew_using_command new' -s i -d 'Install a package after the environment is created'
complete -f -c $pew -n '__pew_using_command new' -s a -a '(__fish_complete_directories (commandline -ct))' -d 'Project directory to associate'
complete -f -c $pew -n '__pew_using_command new' -s r -d 'Pip requirements file'
#### workon
complete -f -c $pew -n '__pew_needs_command' -a workon -d 'Activates an existing virtual environment'
complete -f -c $pew -n '__pew_using_command workon' -a '(__pew_list_envs)' -d 'Virtual env'
#### mktmpenv
complete -f -c $pew -n '__pew_needs_command' -a mktmpenv -d 'Create a temporary virtualenv'
complete -f -c $pew -n '__pew_using_command mktmpenv' -s h -d 'Show help'
complete -f -c $pew -n '__pew_using_command mktmpenv' -s p -l python -d 'Python executable'
complete -f -c $pew -n '__pew_using_command mktmpenv' -s i -d 'Install a package after the environment is created'
complete -f -c $pew -n '__pew_using_command mktmpenv' -s a -d 'Project directory to associate'
complete -f -c $pew -n '__pew_using_command mktmpenv' -s r -d 'Pip requirements file'
#### ls
complete -f -c $pew -n '__pew_needs_command' -a ls -d 'List all existing virtual environments'
complete -f -c $pew -n '__pew_using_command ls' -s l -l long -d 'Verbose ls'
complete -f -c $pew -n '__pew_using_command ls' -s b -l brief -d 'One line ls'
#### show
complete -f -c $pew -n '__pew_needs_command' -a show -d 'Show'
complete -f -c $pew -n '__pew_using_command show' -a '(__pew_list_envs)' -d 'Virtual env'
#### rm
complete -f -c $pew -n '__pew_needs_command' -a rm -d 'Remove one or more environments'
complete -f -c $pew -n '__pew_using_command rm' -a '(__pew_list_envs)' -d 'Virtual env'
#### cp
complete -f -c $pew -n '__pew_needs_command' -a cp -d 'Duplicate an existing virtualenv environment'
#### sitepackages_dir
complete -f -c $pew -n '__pew_needs_command' -a sitepackages_dir -d 'Location of the currently active site-packages'
#### lssitepackages
complete -f -c $pew -n '__pew_needs_command' -a lssitepackages -d 'List currently active site-packages'
#### add
complete -f -c $pew -n '__pew_needs_command' -a add -d 'Adds the specified directories'
complete -f -c $pew -n '__pew_using_command add' -s h -d 'Show help'
complete -f -c $pew -n '__pew_using_command add' -s d -d 'Removes previously added directiories'
#### toggleglobalsitepackages
complete -f -c $pew -n '__pew_needs_command' -a toggleglobalsitepackages -d 'Active virtualenv can access global site-packages'
#### mkproject
complete -f -c $pew -n '__pew_needs_command' -a mkproject -d 'Create a new environment with a project directory'
complete -f -c $pew -n '__pew_using_command mkproject' -s h -d 'Show help'
complete -f -c $pew -n '__pew_using_command mkproject' -s p -l python -d 'Python executable'
complete -f -c $pew -n '__pew_using_command mkproject' -s i -d 'Install a package after the environment is created'
complete -f -c $pew -n '__pew_using_command mkproject' -s a -d 'Project directory to associate'
complete -f -c $pew -n '__pew_using_command mkproject' -s r -d 'Pip requirements file'
complete -f -c $pew -n '__pew_using_command mkproject' -s t -d 'Apply templates'
complete -f -c $pew -n '__pew_using_command mkproject' -s l -l list -d 'List available templates'
#### setproject
complete -f -c $pew -n '__pew_needs_command' -a setproject -d 'Bind an existing virtualenv to an existing project'
#### version
complete -f -c $pew -n '__pew_needs_command' -a version -d 'Prints current version'
#### wipeenv
complete -f -c $pew -n '__pew_needs_command' -a wipeenv -d 'Remove all installed packages from a virtualenv'
complete -f -c $pew -n '__pew_using_command workon' -a '(__pew_list_envs)' -d 'Virtual env'
#### restore
complete -f -c $pew -n '__pew_needs_command' -a restore -d 'Try to restore a broken virtualenv'
complete -f -c $pew -n '__pew_using_command workon' -a '(__pew_list_envs)' -d 'Virtual env'
#### rename
complete -f -c $pew -n '__pew_needs_command' -a rename -d 'Rename a virtualenv'
#### install
complete -f -c $pew -n '__pew_needs_command' -a install -d 'Use Pythonz to download and build a Python vm'
#### list_pythons
complete -f -c $pew -n '__pew_needs_command' -a list_pythons -d 'List the pythons installed by Pythonz'
#### locate_python
complete -f -c $pew -n '__pew_needs_command' -a locate_python -d 'Locate the path for the python version installed by Pythonz'
@@ -0,0 +1,107 @@
#compdef pew
_pew_list_venvs () {
local expl
local -a venvs
venvs=(${(f)"$(_call_program venvs pew ls | tr " " "\n" 2>/dev/null)"})
_wanted venvs expl 'virtual envs' compadd -a venvs
}
local curcontext="$curcontext" state line
typeset -A opt_args
_arguments -C \
':subcommand:->subcommand' \
'*::option:->option'
case $state in
(subcommand)
local -a subcommands
subcommands=(
'add:Add directories to python path of active virtualenv'
'cp:Duplicate the named virtualenv to make a new one'
'inall:Run a command in each virtualenv:command'
'install:Use Pythonz to download and build the specified Python version'
'list_pythons:List the pythons installed by Pythonz (or all the installable ones)'
'locate_python:Locate the path for the python version installed by Pythonz'
'ls:List all existing virtual environments'
'lssitepackages:List currently active site-packages'
'mkproject:Create environment with an associated project directory'
'mktmpenv:Create a temporary virtualenv'
'new:Create a new environment'
'rename:Rename a virtualenv'
'restore:Try to restore a broken virtualenv by reinstalling the same python version on top of it'
'rm:Remove one or more environments'
'setproject:Bind an existing virtualenv to an existing project directory'
'show:Display currently active virtualenv'
'sitepackages_dir:Location of the currently active site-packages'
'toggleglobalsitepackages:Toggle access to global site-packages for current virtualenv'
'wipeenv:Remove all installed packages from current env'
'workon:Activates an existing virtual environment'
)
_describe -t commands 'pew subcommands' subcommands
;;
(option)
local -a new_env_options
new_env_options=(
'-h[Show help]'
'-p[Python executable]:python:_command_names'
'*-i[Install a package after the environment is created]:package name'
'-a[Project directory to associate]:project directory:_path_files -/'
'-r[Pip requirements file]:requirements file:_files'
)
case "$line[1]" in
(mktmpenv)
_arguments \
$new_env_options
;;
(new)
_arguments \
$new_env_options \
'1:new env name'
;;
(mkproject)
_arguments \
$new_env_options \
'*-t[Apply templates]' \
'-l[List available templates]' \
'1:new env name'
;;
(ls)
_arguments \
'(-l --long)--long[Verbose ls]' \
'(-b --brief)--brief[One line ls]'
;;
(inall)
_arguments \
'*:command'
;;
(show|workon|rm|wipeenv|restore)
_arguments \
'1:virtual env:_pew_list_venvs'
;;
(cp)
_arguments \
'1:virtual env:_pew_list_venvs' \
'2:new env name'
;;
(add)
_arguments \
'-h[Show help]' \
'-d[Removes previously added directories]' \
'*: :_directories -/'
;;
(setproject)
_arguments \
'1:virtual env:_pew_list_venvs' \
'*:project directory:_directories -/'
;;
esac
esac
@@ -0,0 +1,4 @@
#! /usr/bin/env python
from pew.pew import deploy_completions
if __name__ == '__main__':
deploy_completions()
@@ -0,0 +1,3 @@
source "$( dirname "${BASH_SOURCE[0]}" )"/complete.bash
PS1="\[\033[01;34m\]\$(basename '$VIRTUAL_ENV')\[\e[0m\]$PS1"
@@ -0,0 +1,9 @@
. (dirname (status --current-filename))/complete.fish
function pew_prompt
if [ -n "$VIRTUAL_ENV" ]
echo -n (set_color --bold -b blue white) (basename "$VIRTUAL_ENV") (set_color normal)" "
end
end
# Remember to use (pew_prompt) inside fish_prompt if you want your prompt to display the active environment
+12
View File
@@ -0,0 +1,12 @@
fpath=( ${0:a:h} "${fpath[@]}" )
compinit
function virtualenv_prompt_info() {
if [ -n "$VIRTUAL_ENV" ]; then
local name=$(basename $VIRTUAL_ENV)
echo "($name) "
fi
}
PS1="$(virtualenv_prompt_info)$PS1"
# be sure to disable promptinit if the prompt is not updated
+6
View File
@@ -0,0 +1,6 @@
#! /bin/sh
# put this inside $WORKON_HOME
project=$1
project_dir=$2
pip install django
django-admin.py startproject "$project"
+1 -2
View File
@@ -1,5 +1,4 @@
import pew
from pipenv.patched import pew
if __name__ == '__main__':
pew.pew.pew()
+1 -1
View File
@@ -176,7 +176,7 @@ class Project(object):
# The user wants the virtualenv in the project.
if not PIPENV_VENV_IN_PROJECT:
c = delegator.run('pew dir "{0}"'.format(self.virtualenv_name))
c = delegator.run('pewtwo dir "{0}"'.format(self.virtualenv_name))
loc = c.out.strip()
# Default mode.
else:
+6 -2
View File
@@ -23,7 +23,8 @@ if sys.argv[-1] == "publish":
required = [
'virtualenv',
'pew>=0.1.26'
'virtualenv-clone>=0.2.5',
'pathlib;python_version<"3.4"'
]
if sys.version_info < (2, 7):
@@ -113,7 +114,10 @@ setup(
url='https://github.com/pypa/pipenv',
packages=find_packages(exclude=['tests']),
entry_points={
'console_scripts': ['pipenv=pipenv:cli'],
'console_scripts': [
'pipenv=pipenv:cli',
'pewtwo=pipenv.patched.pew.pew:pew'
],
},
install_requires=required,
extras_require={
+3 -3
View File
@@ -692,10 +692,10 @@ requests = {version = "*"}
assert 'requests' in p.pipfile['packages']
assert 'requests' in p.lockfile['default']
# Check that .venv now shows in pew's managed list
pew_list = delegator.run('pew ls')
pew_list = delegator.run('pewtwo ls')
assert '.venv' in pew_list.out
# Check for the venv directory
c = delegator.run('pew dir .venv')
c = delegator.run('pewtwo dir .venv')
# Compare pew's virtualenv path to what we expect
venv_path = get_windows_path(project.project_directory, '.venv')
# os.path.normpath will normalize slashes
@@ -705,7 +705,7 @@ requests = {version = "*"}
# If we can do this we can theoretically make a subshell
# This test doesn't work on *nix
if os.name == 'nt':
args = ['pew', 'in', '.venv', 'pip', 'freeze']
args = ['pewtwo', 'in', '.venv', 'pip', 'freeze']
process = subprocess.Popen(
args,
shell=True,