2016-07-20 16:56:39 -04:00
|
|
|
"""Python virtual environment manager for xonsh."""
|
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
import xonsh.cli_utils as xcli
|
2016-08-04 19:19:28 -04:00
|
|
|
import xontrib.voxapi as voxapi
|
2021-10-13 19:32:06 +05:30
|
|
|
from xonsh.built_ins import XSH
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2016-08-04 19:19:28 -04:00
|
|
|
__all__ = ()
|
2016-07-21 14:36:46 -04:00
|
|
|
|
2016-08-04 22:31:45 -04:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
def venv_names_completer(command, alias: "VoxHandler", **_):
|
|
|
|
envs = alias.vox.keys()
|
|
|
|
from xonsh.completers.path import complete_dir
|
2016-07-21 00:26:13 -04:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
yield from envs
|
2019-04-26 11:11:11 -04:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
paths, _ = complete_dir(command)
|
|
|
|
yield from paths
|
2016-07-21 13:54:58 -04:00
|
|
|
|
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
def py_interpreter_path_completer(xsh, **_):
|
|
|
|
for _, (path, is_alias) in xsh.commands_cache.all_commands.items():
|
|
|
|
if not is_alias and ("/python" in path or "/pypy" in path):
|
|
|
|
yield path
|
2016-07-21 13:54:58 -04:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
|
|
|
|
class VoxHandler(xcli.ArgParserAlias):
|
|
|
|
"""Vox is a virtual environment manager for xonsh."""
|
|
|
|
|
|
|
|
def build(self):
|
|
|
|
"""lazily called during dispatch"""
|
|
|
|
self.vox = voxapi.Vox()
|
|
|
|
parser = self.create_parser(prog="vox")
|
|
|
|
|
|
|
|
# todo: completer for interpreter
|
|
|
|
create = parser.add_command(
|
|
|
|
self.new,
|
|
|
|
aliases=["create"],
|
|
|
|
args=("name", "interpreter", "system_site_packages", "activate"),
|
2018-12-20 20:36:41 -08:00
|
|
|
)
|
2018-12-20 20:27:47 -08:00
|
|
|
|
2016-07-21 13:54:58 -04:00
|
|
|
from xonsh.platform import ON_WINDOWS
|
2019-04-26 11:11:11 -04:00
|
|
|
|
2016-07-21 13:54:58 -04:00
|
|
|
group = create.add_mutually_exclusive_group()
|
2019-04-26 11:11:11 -04:00
|
|
|
group.add_argument(
|
|
|
|
"--symlinks",
|
|
|
|
default=not ON_WINDOWS,
|
|
|
|
action="store_true",
|
|
|
|
dest="symlinks",
|
|
|
|
help="Try to use symlinks rather than copies, "
|
|
|
|
"when symlinks are not the default for "
|
|
|
|
"the platform.",
|
|
|
|
)
|
|
|
|
group.add_argument(
|
|
|
|
"--copies",
|
|
|
|
default=ON_WINDOWS,
|
|
|
|
action="store_false",
|
|
|
|
dest="symlinks",
|
|
|
|
help="Try to use copies rather than symlinks, "
|
|
|
|
"even when symlinks are the default for "
|
|
|
|
"the platform.",
|
|
|
|
)
|
|
|
|
create.add_argument(
|
|
|
|
"--without-pip",
|
|
|
|
dest="with_pip",
|
|
|
|
default=True,
|
|
|
|
action="store_false",
|
|
|
|
help="Skips installing or upgrading pip in the "
|
|
|
|
"virtual environment (pip is bootstrapped "
|
|
|
|
"by default)",
|
|
|
|
)
|
2016-07-21 00:26:13 -04:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
parser.add_command(self.activate, aliases=["workon", "enter"])
|
|
|
|
parser.add_command(self.deactivate, aliases=["exit"])
|
|
|
|
parser.add_command(self.list, aliases=["ls"])
|
|
|
|
parser.add_command(self.remove, aliases=["rm", "delete", "del"])
|
2016-07-21 15:18:44 -04:00
|
|
|
return parser
|
2016-07-21 00:26:13 -04:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
def new(
|
|
|
|
self,
|
|
|
|
name: xcli.Annotated[str, xcli.Arg(metavar="ENV")],
|
|
|
|
interpreter: xcli.Annotated[
|
|
|
|
str,
|
|
|
|
xcli.Arg("-p", "--interpreter", completer=py_interpreter_path_completer),
|
|
|
|
] = None,
|
|
|
|
system_site_packages: xcli.Annotated[
|
|
|
|
bool,
|
|
|
|
xcli.Arg("--system-site-packages", "--ssp", action="store_true"),
|
|
|
|
] = False,
|
|
|
|
symlinks: bool = False,
|
|
|
|
with_pip: bool = True,
|
|
|
|
activate: xcli.Annotated[
|
|
|
|
bool,
|
|
|
|
xcli.Arg("-a", "--activate", action="store_true"),
|
|
|
|
] = False,
|
|
|
|
):
|
|
|
|
"""Create a virtual environment in $VIRTUALENV_HOME with python3's ``venv``.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
name : str
|
|
|
|
Virtual environment name
|
|
|
|
interpreter: str
|
|
|
|
Python interpreter used to create the virtual environment.
|
|
|
|
Can be configured via the $VOX_DEFAULT_INTERPRETER environment variable.
|
|
|
|
system_site_packages : bool
|
|
|
|
If True, the system (global) site-packages dir is available to
|
|
|
|
created environments.
|
|
|
|
symlinks : bool
|
|
|
|
If True, attempt to symlink rather than copy files into virtual
|
|
|
|
environment.
|
|
|
|
with_pip : bool
|
|
|
|
If True, ensure pip is installed in the virtual environment. (Default is True)
|
|
|
|
activate
|
|
|
|
Activate the newly created virtual environment.
|
|
|
|
"""
|
2018-12-20 20:27:47 -08:00
|
|
|
print("Creating environment...")
|
|
|
|
self.vox.create(
|
2021-10-13 19:32:06 +05:30
|
|
|
name,
|
|
|
|
system_site_packages=system_site_packages,
|
|
|
|
symlinks=symlinks,
|
|
|
|
with_pip=with_pip,
|
|
|
|
interpreter=interpreter,
|
2018-12-20 20:27:47 -08:00
|
|
|
)
|
2021-10-13 19:32:06 +05:30
|
|
|
if activate:
|
|
|
|
self.vox.activate(name)
|
|
|
|
print(f"Environment {name!r} created and activated.\n")
|
2020-08-28 13:56:24 +02:00
|
|
|
else:
|
2020-08-28 15:55:10 +02:00
|
|
|
print(
|
2021-10-13 19:32:06 +05:30
|
|
|
f'Environment {name!r} created. Activate it with "vox activate {name}".\n'
|
2020-08-28 15:55:10 +02:00
|
|
|
)
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
def activate(
|
|
|
|
self,
|
|
|
|
name: xcli.Annotated[
|
|
|
|
str,
|
|
|
|
xcli.Arg(metavar="ENV", nargs="?", completer=venv_names_completer),
|
|
|
|
] = None,
|
|
|
|
):
|
|
|
|
"""Activate a virtual environment.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
name
|
|
|
|
The environment to activate.
|
|
|
|
ENV can be either a name from the venvs shown by ``vox list``
|
|
|
|
or the path to an arbitrary venv
|
|
|
|
"""
|
|
|
|
|
|
|
|
if name is None:
|
|
|
|
return self.list()
|
2016-07-20 15:02:56 -04:00
|
|
|
|
|
|
|
try:
|
2021-10-13 19:32:06 +05:30
|
|
|
self.vox.activate(name)
|
2016-07-20 15:02:56 -04:00
|
|
|
except KeyError:
|
2021-10-13 19:32:06 +05:30
|
|
|
self.parser.error(
|
|
|
|
f'This environment doesn\'t exist. Create it with "vox new {name}".\n',
|
2019-04-26 11:11:11 -04:00
|
|
|
)
|
2016-02-11 11:47:16 +03:00
|
|
|
return None
|
|
|
|
else:
|
2021-10-13 19:32:06 +05:30
|
|
|
print(f'Activated "{name}".\n')
|
|
|
|
|
|
|
|
def deactivate(
|
|
|
|
self,
|
|
|
|
remove: xcli.Annotated[
|
|
|
|
bool,
|
|
|
|
xcli.Arg("--remove", action="store_true"),
|
|
|
|
] = False,
|
|
|
|
):
|
|
|
|
"""Deactivate the active virtual environment.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
remove
|
|
|
|
Remove the virtual environment after leaving it.
|
|
|
|
"""
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2016-07-20 15:02:56 -04:00
|
|
|
if self.vox.active() is None:
|
2021-10-13 19:32:06 +05:30
|
|
|
self.parser.error(
|
2019-04-26 11:11:11 -04:00
|
|
|
'No environment currently active. Activate one with "vox activate".\n',
|
|
|
|
)
|
2016-07-20 15:02:56 -04:00
|
|
|
env_name = self.vox.deactivate()
|
2021-10-13 19:32:06 +05:30
|
|
|
if remove:
|
2020-08-28 13:56:24 +02:00
|
|
|
del self.vox[env_name]
|
|
|
|
print(f'Environment "{env_name}" deactivated and removed.\n')
|
|
|
|
else:
|
|
|
|
print(f'Environment "{env_name}" deactivated.\n')
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
def list(self):
|
2016-02-11 11:47:16 +03:00
|
|
|
"""List available virtual environments."""
|
|
|
|
|
2016-05-15 21:59:28 +02:00
|
|
|
try:
|
2016-07-21 00:26:13 -04:00
|
|
|
envs = sorted(self.vox.keys())
|
2016-05-15 21:59:28 +02:00
|
|
|
except PermissionError:
|
2021-10-13 19:32:06 +05:30
|
|
|
self.parser.error("No permissions on VIRTUALENV_HOME")
|
2016-05-15 21:59:28 +02:00
|
|
|
return None
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2016-07-20 15:02:56 -04:00
|
|
|
if not envs:
|
2021-10-13 19:32:06 +05:30
|
|
|
self.parser.error(
|
2019-04-26 11:11:11 -04:00
|
|
|
'No environments available. Create one with "vox new".\n',
|
|
|
|
)
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2019-04-26 11:11:11 -04:00
|
|
|
print("Available environments:")
|
|
|
|
print("\n".join(envs))
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
def remove(
|
|
|
|
self,
|
|
|
|
names: xcli.Annotated[
|
|
|
|
list,
|
|
|
|
xcli.Arg(metavar="ENV", nargs="+", completer=venv_names_completer),
|
|
|
|
],
|
|
|
|
):
|
|
|
|
"""Remove virtual environments.
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
names
|
|
|
|
The environments to remove. ENV can be either a name from the venvs shown by vox
|
|
|
|
list or the path to an arbitrary venv
|
|
|
|
"""
|
|
|
|
for name in names:
|
2016-07-20 15:02:56 -04:00
|
|
|
try:
|
2016-07-21 00:36:35 -04:00
|
|
|
del self.vox[name]
|
2016-08-04 19:19:28 -04:00
|
|
|
except voxapi.EnvironmentInUse:
|
2021-10-13 19:32:06 +05:30
|
|
|
self.parser.error(
|
|
|
|
f'The "{name}" environment is currently active. '
|
|
|
|
'In order to remove it, deactivate it first with "vox deactivate".\n',
|
2019-04-26 11:11:11 -04:00
|
|
|
)
|
2020-08-23 20:38:03 +02:00
|
|
|
except KeyError:
|
2021-10-13 19:32:06 +05:30
|
|
|
self.parser.error(f'"{name}" environment doesn\'t exist.\n')
|
2016-07-20 15:02:56 -04:00
|
|
|
else:
|
2021-10-13 19:32:06 +05:30
|
|
|
print(f'Environment "{name}" removed.')
|
2016-07-15 14:42:54 -07:00
|
|
|
print()
|
2016-02-11 11:47:16 +03:00
|
|
|
|
2016-07-21 00:05:17 +02:00
|
|
|
|
2021-10-13 19:32:06 +05:30
|
|
|
XSH.aliases["vox"] = VoxHandler()
|