def runin( self, venv: xcli.Annotated[ str, xcli.Arg(completer=venv_names_completer), ], args: xcli.Annotated[tp.List[str], xcli.Arg(nargs="...")], ): """Run the command in the given environment Parameters ---------- venv The environment to run the command for args The actual command to run Examples -------- vox runin venv1 black --check-only """ env_dir = self._get_env_dir(venv) if not args: self.parser.error("No command is passed") self._in_venv(env_dir, *args)
def remove( self, names: xcli.Annotated[ tp.List[str], xcli.Arg(metavar="ENV", nargs="+", completer=venv_names_completer ), ], force=False, ): """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 force : -f, --force Delete virtualenv without prompt """ self.vox.force_removals = force for name in names: try: del self.vox[name] except voxapi.EnvironmentInUse: raise self.Error( f'The "{name}" environment is currently active. ' 'In order to remove it, deactivate it first with "vox deactivate".\n', ) except KeyError: raise self.Error(f'"{name}" environment doesn\'t exist.\n') else: self.out(f'Environment "{name}" removed.') self.out()
def complete( self, line: xcli.Annotated["list[str]", xcli.Arg(nargs="...")], prefix: "str | None" = None, ): """Output the completions to stdout Parameters ---------- line pass the CLI arguments as if they were typed prefix : -p, --prefix word at cursor Examples -------- To get completions such as `git checkout` $ completer complete --prefix=check git """ from xonsh.completer import Completer completer = Completer() completions, prefix_length = completer.complete_line(" ".join(line), prefix=prefix) self.out(f"Prefix Length: {prefix_length}") for comp in completions: self.out(repr(comp))
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: try: del self.vox[name] except voxapi.EnvironmentInUse: self.parser.error( f'The "{name}" environment is currently active. ' 'In order to remove it, deactivate it first with "vox deactivate".\n', ) except KeyError: self.parser.error(f'"{name}" environment doesn\'t exist.\n') else: print(f'Environment "{name}" removed.') print()
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() try: self.vox.activate(name) except KeyError: self.parser.error( f'This environment doesn\'t exist. Create it with "vox new {name}".\n', ) return None else: print(f'Activated "{name}".\n')
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=False, symlinks: bool = False, with_pip: bool = True, activate=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 : --system-site-packages, --ssp 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 : -a, --activate Activate the newly created virtual environment. """ print("Creating environment...") self.vox.create( name, system_site_packages=system_site_packages, symlinks=symlinks, with_pip=with_pip, interpreter=interpreter, ) if activate: self.vox.activate(name) print(f"Environment {name!r} created and activated.\n") else: print( f'Environment {name!r} created. Activate it with "vox activate {name}".\n' )
def gc( size: xcli.Annotated[tp.Tuple[int, str], xcli.Arg(nargs=2)] = None, force=False, _blocking=True, ): """Launches a new history garbage collector Parameters ---------- size : -s, --size Next two arguments represent the history size and units; e.g. "--size 8128 commands" force : -f, --force perform garbage collection even if history much bigger than configured limit """ hist = XSH.history hist.run_gc(size=size, blocking=_blocking, force=force)
def gc( size: xcli.Annotated[tp.Tuple[int, str], xcli.Arg(nargs=2)] = None, force=False, blocking=True, ): """Launches a new history garbage collector Parameters ---------- size : -s, --size Next two arguments represent the history size and units; e.g. "--size 8128 commands" force : -f, --force perform garbage collection even if history much bigger than configured limit blocking : -n, --non-blocking makes the gc non-blocking, and thus return sooner. By default it runs on main thread blocking input. """ hist = XSH.history hist.run_gc(size=size, blocking=blocking, force=force)
def runin_all( self, args: xcli.Annotated[tp.List[str], xcli.Arg(nargs="...")], ): """Run the command in all environments found under $VIRTUALENV_HOME Parameters ---------- args The actual command to run with arguments """ errors = False for env in self.vox: print("\n%s:" % env) try: self.runin(env, *args) except subprocess.CalledProcessError as e: errors = True print(e, file=sys.stderr) sys.exit(errors)
def new( self, name: xcli.Annotated[str, xcli.Arg(metavar="ENV")], interpreter: "str|None" = None, system_site_packages=False, symlinks=True, without_pip=False, activate=False, temporary=False, packages: xcli.Annotated[tp.Sequence[str], xcli.Arg(nargs="*")] = (), requirements: xcli.Annotated[tp.Sequence[str], xcli.Arg(action="append")] = (), link_project_dir=False, prompt: "str|None" = None, ): """Create a virtual environment in $VIRTUALENV_HOME with python3's ``venv``. Parameters ---------- name : str Virtual environment name interpreter: -p, --interpreter Python interpreter used to create the virtual environment. Can be configured via the $VOX_DEFAULT_INTERPRETER environment variable. system_site_packages : --system-site-packages, --ssp If True, the system (global) site-packages dir is available to created environments. symlinks : --copies Try to use copies rather than symlinks. without_pip : --without-pip, --wp Skips installing or upgrading pip in the virtual environment activate : -a, --activate Activate the newly created virtual environment. temporary: -t, --temp Create the virtualenv under a temporary directory. packages: -i, --install Install one or more packages (by repeating the option) after the environment is created using pip requirements: -r, --requirements The argument value is passed to ``pip -r`` to be installed. link_project_dir: -l, --link, --link-project Associate the current directory with the new environment. prompt: --prompt Provides an alternative prompt prefix for this environment. """ self.out("Creating environment...") if temporary: path = tempfile.mkdtemp(prefix=f"vox-env-{name}") name = os.path.join(path, name) self.vox.create( name, system_site_packages=system_site_packages, symlinks=symlinks, with_pip=(not without_pip), interpreter=interpreter, prompt=prompt, ) if link_project_dir: self.project_set(name) if packages: self.runin(name, ["pip", "install", *packages]) if requirements: def _generate_args(): for req in requirements: yield "-r" yield req self.runin(name, ["pip", "install"] + list(_generate_args())) if activate: self.activate(name) self.out(f"Environment {name!r} created and activated.\n") else: self.out( f'Environment {name!r} created. Activate it with "vox activate {name}".\n' )
yield from envs paths, _ = complete_dir(command) yield from paths 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 _venv_option = xcli.Annotated[ tp.Optional[str], xcli.Arg(metavar="ENV", nargs="?", completer=venv_names_completer), ] 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") parser.add_command(self.new, aliases=["create"]) 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"]) parser.add_command(self.info)
def show( self, session: xcli.Annotated[ str, xcli.Arg(nargs="?", choices=tuple(_XH_HISTORY_SESSIONS) )] = "session", slices: xcli.Annotated[tp.List[int], xcli.Arg(nargs="*")] = None, datetime_format: tp.Optional[str] = None, start_time: tp.Optional[str] = None, end_time: tp.Optional[str] = None, location: tp.Optional[str] = None, reverse=False, numerate=False, timestamp=False, null_byte=False, _stdout=None, _stderr=None, _unparsed=None, ): """Display history of a session, default command Parameters ---------- session: The history session to get. (all is an alias for xonsh) slices: integer or slice notation to get only portions of history. datetime_format : -f the datetime format to be used for filtering and printing start_time: --start-time, +T show only commands after timestamp end_time: -T, --end-time show only commands before timestamp location: -l, --location The history file location (bash or zsh) reverse: -r, --reverse Reverses the direction numerate: -n, --numerate Numerate each command timestamp: -t, --ts, --time-stamp show command timestamps null_byte: -0, --nb, --null-byte separate commands by the null character for piping history to external filters _unparsed remaining args from ``parser.parse_known_args`` """ slices = list(slices or ()) if _unparsed: slices.extend(_unparsed) try: commands = _xh_get_history( session, slices=slices, start_time=start_time, end_time=end_time, datetime_format=datetime_format, location=location, ) except Exception as err: self.parser.error(err) return if reverse: commands = reversed(list(commands)) end = "\0" if null_byte else "\n" if numerate and timestamp: for c in commands: dt = datetime.datetime.fromtimestamp(c["ts"]) print( "{}:({}) {}".format(c["ind"], xt.format_datetime(dt), c["inp"]), file=_stdout, end=end, ) elif numerate: for c in commands: print("{}: {}".format(c["ind"], c["inp"]), file=_stdout, end=end) elif timestamp: for c in commands: dt = datetime.datetime.fromtimestamp(c["ts"]) print( "({}) {}".format(xt.format_datetime(dt), c["inp"]), file=_stdout, end=end, ) else: for c in commands: print(c["inp"], file=_stdout, end=end)
def _register_completer( name: str, func: xcli.Annotated[str, xcli.Arg(completer=complete_func_name_choices)], pos: xcli.Annotated[str, xcli.Arg(completer=complete_completer_pos_choices, nargs="?")] = "start", _stack=None, ): """Add a new completer to xonsh Parameters ---------- name unique name to use in the listing (run "completer list" to see the current completers in order) func the name of a completer function to use. This should be a function that takes a Completion Context object and marked with the ``xonsh.completers.tools.contextual_completer`` decorator. It should return a set of valid completions for the given prefix. If this completer should not be used in a given context, it should return an empty set or None. For more information see https://xon.sh/tutorial_completers.html#writing-a-new-completer. pos position into the list of completers at which the new completer should be added. It can be one of the following values: * "start" indicates that the completer should be added to the start of the list of completers (it should be run before all other exclusive completers) * "end" indicates that the completer should be added to the end of the list of completers (it should be run after all others) * ">KEY", where KEY is a pre-existing name, indicates that this should be added after the completer named KEY * "<KEY", where KEY is a pre-existing name, indicates that this should be added before the completer named KEY """ err = None func_name = func xsh = XSH if name in xsh.completers: err = f"The name {name} is already a registered completer function." else: if func_name in xsh.ctx: func = xsh.ctx[func_name] if not callable(func): err = f"{func_name} is not callable" else: for frame_info in _stack: frame = frame_info[0] if func_name in frame.f_locals: func = frame.f_locals[func_name] break elif func_name in frame.f_globals: func = frame.f_globals[func_name] break else: err = "No such function: %s" % func_name if err is None: _add_one_completer(name, func, pos) else: return None, err + "\n", 1