Esempio n. 1
0
def xontribs_load(
    names: Annotated[
        tp.Sequence[str],
        Arg(nargs="+", completer=xontrib_names_completer),
    ] = (),
    verbose=False,
):
    """Load xontribs from a list of names

    Parameters
    ----------
    names
        names of xontribs
    verbose : -v, --verbose
        verbose output
    """
    ctx = XSH.ctx
    res = ExitCode.OK
    for name in names:
        if verbose:
            print(f"loading xontrib {name!r}")
        try:
            update_context(name, ctx=ctx)
        except Exception:
            res = ExitCode.INIT_FAILED
            print_exception(f"Failed to load xontrib {name}.")
    if hasattr(update_context, "bad_imports"):
        res = ExitCode.NOT_FOUND
        prompt_xontrib_install(update_context.bad_imports)  # type: ignore
        del update_context.bad_imports  # type: ignore
    return res
Esempio n. 2
0
def _list(
    names: Annotated[tuple, Arg(nargs="*")] = (),
    to_json=False,
):
    """List xontribs, whether they are installed, and loaded.

    Parameters
    ----------
    to_json : -j, --json
        reports results as json
    names
        names of xontribs
    """
    data = xontrib_data(names)
    if to_json:
        s = json.dumps(data)
        print(s)
    else:
        nname = max([6] + [len(x) for x in data])
        s = ""
        for name, d in data.items():
            lname = len(name)
            s += "{PURPLE}" + name + "{RESET}  " + " " * (nname - lname)
            if d["installed"]:
                s += "{GREEN}installed{RESET}      "
            else:
                s += "{RED}not-installed{RESET}  "
            if d["loaded"]:
                s += "{GREEN}loaded{RESET}"
            else:
                s += "{RED}not-loaded{RESET}"
            s += "\n"
        print_color(s[:-1])
Esempio n. 3
0
def _colors(
    style: Annotated[str, Arg(nargs="?", completer=xonfig_color_completer)] = None
):
    """Preview color style

    Parameters
    ----------
    style
        name of the style to preview. If not given, current style name is used.
    """
    columns, _ = shutil.get_terminal_size()
    columns -= int(bool(ON_WINDOWS))
    style_stash = XSH.env["XONSH_COLOR_STYLE"]

    if style is not None:
        if style not in color_style_names():
            print(f"Invalid style: {style}")
            return
        XSH.env["XONSH_COLOR_STYLE"] = style

    color_map = color_style()
    if not color_map:
        print("Empty color map - using non-interactive shell?")
        return
    akey = next(iter(color_map))
    if isinstance(akey, str):
        s = _str_colors(color_map, columns)
    else:
        s = _tok_colors(color_map, columns)
    print_color(s)
    XSH.env["XONSH_COLOR_STYLE"] = style_stash
Esempio n. 4
0
    def toggle_color(
        self,
        toggle: Annotated[bool, Arg(type=to_bool)] = False,
    ):
        """output color management for tracer

        Parameters
        ----------
        toggle
            true/false, y/n, etc. to toggle color usage.
        """
        self.color_output(toggle)
Esempio n. 5
0
def disown_fn(
    job_ids: Annotated[tp.Sequence[int],
                       Arg(type=int, nargs="*", completer=job_id_completer)],
    force_auto_continue=False,
):
    """Remove the specified jobs from the job table; the shell will no longer
    report their status, and will not complain if you try to exit an
    interactive shell with them running or stopped.

    If the jobs are currently stopped and the $AUTO_CONTINUE option is not set
    ($AUTO_CONTINUE = False), a warning is printed containing information about
    how to make them continue after they have been disowned.

    Parameters
    ----------
    job_ids
        Jobs to act on or none to disown the current job
    force_auto_continue : -c, --continue
        Automatically continue stopped jobs when they are disowned, equivalent to setting $AUTO_CONTINUE=True
    """

    if len(tasks) == 0:
        return "", "There are no active jobs"

    messages = []
    # if args.job_ids is empty, use the active task
    for tid in job_ids or [tasks[0]]:
        try:
            current_task = get_task(tid)
        except KeyError:
            return "", f"'{tid}' is not a valid job ID"

        auto_cont = XSH.env.get("AUTO_CONTINUE", False)
        if auto_cont or force_auto_continue:
            _continue(current_task)
        elif current_task["status"] == "stopped":
            messages.append(f"warning: job is suspended, use "
                            f"'kill -CONT -{current_task['pids'][-1]}' "
                            f"to resume\n")

        # Stop tracking this task
        tasks.remove(tid)
        del XSH.all_jobs[tid]
        messages.append(f"Removed job {tid} ({current_task['status']})")

    if messages:
        return "".join(messages)
Esempio n. 6
0
def remove_completer(
    name: Annotated[str, Arg(completer=complete_completer_names)], ):
    """Removes a completer from xonsh

    Parameters
    ----------
    name:
        NAME is a unique name of a completer (run "completer list" to see the current
        completers in order)
    """
    err = None
    if name not in XSH.completers:
        err = f"The name {name} is not a registered completer function."
    if err is None:
        del XSH.completers[name]
        return
    else:
        return None, err + "\n", 1
Esempio n. 7
0
    def off_files(
        self,
        _args,
        files: Annotated[tp.Iterable[str], Arg(nargs="*")] = ("__file__",),
    ):
        """removes selected files fom tracing.

        Parameters
        ----------
        files
            file paths to stop watching, use ``__file__`` (default) to select the current file.

        """
        for f in files:
            if f == "__file__":
                f = _find_caller(_args)
            if f is None:
                continue
            self.stop(f)
Esempio n. 8
0
    def on_files(
        self,
        _args,
        files: Annotated[tp.Iterable[str], Arg(nargs="*")] = ("__file__",),
    ):
        """begins tracing selected files.

        Parameters
        ----------
        _args
            argv from alias parser
        files
            file paths to watch, use "__file__" (default) to select the current file.
        """

        for f in files:
            if f == "__file__":
                f = _find_caller(_args)
            if f is None:
                continue
            self.start(f)
Esempio n. 9
0
def dirs_fn(
    nth: Annotated[tp.Optional[str],
                   Arg(metavar="N", nargs="?")] = None,
    clear=False,
    print_long=False,
    verbose=False,
    long=False,
):
    """Manage the list of currently remembered directories.

    Parameters
    ----------
    nth
        Displays the Nth directory (counting from the left/right according to +/x prefix respectively),
        starting with zero
    clear : -c
        Clears the directory stack by deleting all of the entries.
    print_long : -p
        Print the directory stack with one entry per line.
    verbose : -v
        Print the directory stack with one entry per line,
        prefixing each entry with its index in the stack.
    long : -l
        Produces a longer listing; the default listing format
        uses a tilde to denote the home directory.
    """
    global DIRSTACK

    env = XSH.env
    dirstack = [os.path.expanduser(env["PWD"])] + DIRSTACK

    if env.get("PUSHD_MINUS"):
        BACKWARD = "-"
        FORWARD = "+"
    else:
        BACKWARD = "-"
        FORWARD = "+"

    if clear:
        DIRSTACK = []
        return None, None, 0

    if long:
        o = dirstack
    else:
        d = os.path.expanduser("~")
        o = [i.replace(d, "~") for i in dirstack]

    if verbose:
        out = ""
        pad = len(str(len(o) - 1))
        for (ix, e) in enumerate(o):
            blanks = " " * (pad - len(str(ix)))
            out += f"\n{blanks}{ix} {e}"
        out = out[1:]
    elif print_long:
        out = "\n".join(o)
    else:
        out = " ".join(o)

    if nth is not None:
        try:
            num = int(nth[1:])
        except ValueError:
            e = "Invalid argument to dirs: {0}\n"
            return None, e.format(nth), 1

        if num < 0:
            e = "Invalid argument to dirs: {0}\n"
            return None, e.format(len(o)), 1

        if num >= len(o):
            e = "Too few elements in dirstack ({0} elements)\n"
            return None, e.format(len(o)), 1

        if nth.startswith(BACKWARD):
            idx = num
        elif nth.startswith(FORWARD):
            idx = len(o) - 1 - num
        else:
            e = "Invalid argument to dirs: {0}\n"
            return None, e.format(nth), 1

        out = o[idx]

    return out + "\n", None, 0
Esempio n. 10
0
def popd_fn(
    nth: Annotated[tp.Optional[str],
                   Arg(metavar="+N|-N", nargs="?")] = None,
    cd=True,
    quiet=False,
):
    """When no arguments are given, popd removes the top directory from the stack
    and performs a cd to the new top directory.
    The elements are numbered from 0 starting at the first directory listed with ``dirs``;
    that is, popd is equivalent to popd +0.

    Parameters
    ----------
    cd : -n, --cd
        Suppresses the normal change of directory when removing directories from the stack,
        so that only the stack is manipulated.
    nth
        Removes the Nth directory (counting from the left/right of the list printed by dirs w.r.t. -/+ prefix),
        starting with zero.
    quiet : -q, --quiet
        Do not call dirs, regardless of $PUSHD_SILENT
    """
    global DIRSTACK

    env = XSH.env

    if env.get("PUSHD_MINUS"):
        BACKWARD = "-"
        FORWARD = "+"
    else:
        BACKWARD = "-"
        FORWARD = "+"

    new_pwd: tp.Optional[str] = None
    if nth is None:
        try:
            new_pwd = DIRSTACK.pop(0)
        except IndexError:
            e = "popd: Directory stack is empty\n"
            return None, e, 1
    else:
        try:
            num = int(nth[1:])
        except ValueError:
            e = "Invalid argument to popd: {0}\n"
            return None, e.format(nth), 1

        if num < 0:
            e = "Invalid argument to popd: {0}\n"
            return None, e.format(nth), 1

        if num > len(DIRSTACK):
            e = "Too few elements in dirstack ({0} elements)\n"
            return None, e.format(len(DIRSTACK)), 1
        elif nth.startswith(FORWARD):
            if num == len(DIRSTACK):
                new_pwd = DIRSTACK.pop(0)
            else:
                DIRSTACK.pop(len(DIRSTACK) - 1 - num)
        elif nth.startswith(BACKWARD):
            if num == 0:
                new_pwd = DIRSTACK.pop(0)
            else:
                DIRSTACK.pop(num - 1)
        else:
            e = "Invalid argument to popd: {0}\n"
            return None, e.format(nth), 1

    if new_pwd is not None:
        if cd:
            env = XSH.env
            pwd = env["PWD"]

            _change_working_directory(new_pwd)

            if ON_WINDOWS:
                drive, rem_path = os.path.splitdrive(pwd)
                _unc_unmap_temp_drive(drive.casefold(), new_pwd)

    if not quiet and not env.get("PUSHD_SILENT"):
        return dirs([], None)

    return None, None, 0
Esempio n. 11
0
def pushd_fn(
    dir_or_n: Annotated[tp.Optional[str],
                        Arg(metavar="+N|-N|dir", nargs="?")] = None,
    cd=True,
    quiet=False,
):
    r"""Adds a directory to the top of the directory stack, or rotates the stack,
    making the new top of the stack the current working directory.

    On Windows, if the path is a UNC path (begins with `\\<server>\<share>`) and if the `DisableUNCCheck` registry
    value is not enabled, creates a temporary mapped drive letter and sets the working directory there, emulating
    behavior of `PUSHD` in `CMD.EXE`

    Parameters
    ----------
    dir_or_n
        * dir :
            Makes dir be the top of the stack,
            making it the new current directory as if it had been supplied as an argument to the cd builtin.
        * +N :
            Brings the Nth directory (counting from the left of the list printed by dirs, starting with zero)
            to the top of the list by rotating the stack.
        * -N :
            Brings the Nth directory (counting from the right of the list printed by dirs, starting with zero)
            to the top of the list by rotating the stack.
    cd : -n, --cd
        Suppresses the normal change of directory when adding directories to the stack,
        so that only the stack is manipulated.
    quiet : -q, --quiet
        Do not call dirs, regardless of $PUSHD_SILENT
    """
    global DIRSTACK

    env = XSH.env

    pwd = env["PWD"]

    if env.get("PUSHD_MINUS", False):
        BACKWARD = "-"
        FORWARD = "+"
    else:
        BACKWARD = "+"
        FORWARD = "-"

    if dir_or_n is None:
        try:
            new_pwd: tp.Optional[str] = DIRSTACK.pop(0)
        except IndexError:
            e = "pushd: Directory stack is empty\n"
            return None, e, 1
    elif os.path.isdir(dir_or_n):
        new_pwd = dir_or_n
    else:
        try:
            num = int(dir_or_n[1:])
        except ValueError:
            e = "Invalid argument to pushd: {0}\n"
            return None, e.format(dir_or_n), 1

        if num < 0:
            e = "Invalid argument to pushd: {0}\n"
            return None, e.format(dir_or_n), 1

        if num > len(DIRSTACK):
            e = "Too few elements in dirstack ({0} elements)\n"
            return None, e.format(len(DIRSTACK)), 1
        elif dir_or_n.startswith(FORWARD):
            if num == len(DIRSTACK):
                new_pwd = None
            else:
                new_pwd = DIRSTACK.pop(len(DIRSTACK) - 1 - num)
        elif dir_or_n.startswith(BACKWARD):
            if num == 0:
                new_pwd = None
            else:
                new_pwd = DIRSTACK.pop(num - 1)
        else:
            e = "Invalid argument to pushd: {0}\n"
            return None, e.format(dir_or_n), 1
    if new_pwd is not None:
        if ON_WINDOWS and _is_unc_path(new_pwd):
            new_pwd = _unc_map_temp_drive(new_pwd)
        if cd:
            DIRSTACK.insert(0, os.path.expanduser(pwd))
            _change_working_directory(new_pwd)
        else:
            DIRSTACK.insert(0, os.path.expanduser(new_pwd))

    maxsize = env.get("DIRSTACK_SIZE")
    if len(DIRSTACK) > maxsize:
        DIRSTACK = DIRSTACK[:maxsize]

    if not quiet and not env.get("PUSHD_SILENT"):
        return dirs([], None)

    return None, None, 0
Esempio n. 12
0
def xexec_fn(
    command: Annotated[tp.List[str],
                       Arg(nargs=argparse.REMAINDER)],
    login=False,
    clean=False,
    name="",
    _stdin=None,
):
    """exec (also aliased as xexec) uses the os.execvpe() function to
    replace the xonsh process with the specified program.

    This provides the functionality of the bash 'exec' builtin::

        >>> exec bash -l -i
        bash $

    Parameters
    ----------
    command
        program to launch along its arguments
    login : -l, --login
        the shell places a dash at the
        beginning of the zeroth argument passed to command to simulate login
        shell.
    clean : -c, --clean
        causes command to be executed with an empty environment.
    name : -a, --name
        the shell passes name as the zeroth argument
        to the executed command.

    Notes
    -----
    This command **is not** the same as the Python builtin function
    exec(). That function is for running Python code. This command,
    which shares the same name as the sh-lang statement, is for launching
    a command directly in the same process. In the event of a name conflict,
    please use the xexec command directly or dive into subprocess mode
    explicitly with ![exec command]. For more details, please see
    http://xon.sh/faq.html#exec.
    """
    if len(command) == 0:
        return (None, "xonsh: exec: no command specified\n", 1)

    cmd = command[0]
    if name:
        command[0] = name
    if login:
        command[0] = f"-{command[0]}"

    denv = {}
    if not clean:
        denv = XSH.env.detype()

    try:
        os.execvpe(cmd, command, denv)
    except FileNotFoundError as e:
        return (
            None,
            "xonsh: exec: file not found: {}: {}"
            "\n".format(e.args[1], command[0]),
            1,
        )
Esempio n. 13
0
def source_cmd_fn(
    files: Annotated[tp.List[str], Arg(nargs="+")],
    login=False,
    aliascmd=None,
    extra_args="",
    safe=True,
    postcmd="",
    funcscmd="",
    seterrprevcmd=None,
    overwrite_aliases=False,
    suppress_skip_message=False,
    show=False,
    dryrun=False,
    _stderr=None,
):
    """
        Source cmd.exe files

    Parameters
    ----------
    files
        paths to source files.
    login : -l, --login
        whether the sourced shell should be login
    envcmd : --envcmd
        command to print environment
    aliascmd : --aliascmd
        command to print aliases
    extra_args : --extra-args
        extra arguments needed to run the shell
    safe : -s, --safe
        whether the source shell should be run safely, and not raise any errors, even if they occur.
    postcmd : --postcmd
        command(s) to run after all other commands
    funcscmd : --funcscmd
        code to find locations of all native functions in the shell language.
    seterrprevcmd : --seterrprevcmd
        command(s) to set exit-on-error before any other commands.
    overwrite_aliases : --overwrite-aliases
        flag for whether or not sourced aliases should replace the current xonsh aliases.
    suppress_skip_message : --suppress-skip-message
        flag for whether or not skip messages should be suppressed.
    show : --show
        show the script output.
    dryrun : -d, --dry-run
        Will not actually source the file.
    """
    args = list(files)
    fpath = locate_binary(args[0])
    args[0] = fpath if fpath else args[0]
    if not os.path.isfile(args[0]):
        return (None, f"xonsh: error: File not found: {args[0]}\n", 1)
    prevcmd = "call "
    prevcmd += " ".join([argvquote(arg, force=True) for arg in args])
    prevcmd = escape_windows_cmd_string(prevcmd)
    with XSH.env.swap(PROMPT="$P$G"):
        return source_foreign_fn(
            shell="cmd",
            files_or_code=args,
            interactive=True,
            sourcer="call",
            envcmd="set",
            seterrpostcmd="if errorlevel 1 exit 1",
            use_tmpfile=True,
            prevcmd=prevcmd,
            #     from this function
            login=login,
            aliascmd=aliascmd,
            extra_args=extra_args,
            safe=safe,
            postcmd=postcmd,
            funcscmd=funcscmd,
            seterrprevcmd=seterrprevcmd,
            overwrite_aliases=overwrite_aliases,
            suppress_skip_message=suppress_skip_message,
            show=show,
            dryrun=dryrun,
        )
Esempio n. 14
0
def source_foreign_fn(
    shell: str,
    files_or_code: Annotated[tp.List[str], Arg(nargs="+")],
    interactive=True,
    login=False,
    envcmd=None,
    aliascmd=None,
    extra_args="",
    safe=True,
    prevcmd="",
    postcmd="",
    funcscmd="",
    sourcer=None,
    use_tmpfile=False,
    seterrprevcmd=None,
    seterrpostcmd=None,
    overwrite_aliases=False,
    suppress_skip_message=False,
    show=False,
    dryrun=False,
    _stderr=None,
):
    """Sources a file written in a foreign shell language.

    Parameters
    ----------
    shell
        Name or path to the foreign shell
    files_or_code
        file paths to source or code in the target language.
    interactive : -n, --non-interactive
        whether the sourced shell should be interactive
    login : -l, --login
        whether the sourced shell should be login
    envcmd : --envcmd
        command to print environment
    aliascmd : --aliascmd
        command to print aliases
    extra_args : --extra-args
        extra arguments needed to run the shell
    safe : -u, --unsafe
        whether the source shell should be run safely, and not raise any errors, even if they occur.
    prevcmd : -p, --prevcmd
        command(s) to run before any other commands, replaces traditional source.
    postcmd : --postcmd
        command(s) to run after all other commands
    funcscmd : --funcscmd
        code to find locations of all native functions in the shell language.
    sourcer : --sourcer
        the source command in the target shell language.
        If this is not set, a default value will attempt to be
        looked up based on the shell name.
    use_tmpfile : --use-tmpfile
        whether the commands for source shell should be written to a temporary file.
    seterrprevcmd : --seterrprevcmd
        command(s) to set exit-on-error before any other commands.
    seterrpostcmd : --seterrpostcmd
        command(s) to set exit-on-error after all other commands.
    overwrite_aliases : --overwrite-aliases
        flag for whether or not sourced aliases should replace the current xonsh aliases.
    suppress_skip_message : --suppress-skip-message
        flag for whether or not skip messages should be suppressed.
    show : --show
        show the script output.
    dryrun : -d, --dry-run
        Will not actually source the file.
    """
    extra_args = tuple(extra_args.split())
    env = XSH.env
    suppress_skip_message = (env.get("FOREIGN_ALIASES_SUPPRESS_SKIP_MESSAGE")
                             if not suppress_skip_message else
                             suppress_skip_message)
    files: tp.Tuple[str, ...] = ()
    if prevcmd:
        pass  # don't change prevcmd if given explicitly
    elif os.path.isfile(files_or_code[0]):
        if not sourcer:
            return (None,
                    "xonsh: error: `sourcer` command is not mentioned.\n", 1)
        # we have filenames to source
        prevcmd = "".join([f"{sourcer} {f}\n" for f in files_or_code])
        files = tuple(files_or_code)
    elif not prevcmd:
        prevcmd = " ".join(files_or_code)  # code to run, no files
    foreign_shell_data.cache_clear()  # make sure that we don't get prev src
    fsenv, fsaliases = foreign_shell_data(
        shell=shell,
        login=login,
        interactive=interactive,
        envcmd=envcmd,
        aliascmd=aliascmd,
        extra_args=extra_args,
        safe=safe,
        prevcmd=prevcmd,
        postcmd=postcmd,
        funcscmd=funcscmd
        or None,  # the default is None in the called function
        sourcer=sourcer,
        use_tmpfile=use_tmpfile,
        seterrprevcmd=seterrprevcmd,
        seterrpostcmd=seterrpostcmd,
        show=show,
        dryrun=dryrun,
        files=files,
    )
    if fsenv is None:
        if dryrun:
            return
        else:
            msg = f"xonsh: error: Source failed: {prevcmd!r}\n"
            msg += "xonsh: error: Possible reasons: File not found or syntax error\n"
            return (None, msg, 1)
    # apply results
    denv = env.detype()
    for k, v in fsenv.items():
        if k in denv and v == denv[k]:
            continue  # no change from original
        env[k] = v
    # Remove any env-vars that were unset by the script.
    for k in denv:
        if k not in fsenv:
            env.pop(k, None)
    # Update aliases
    baliases = XSH.aliases
    for k, v in fsaliases.items():
        if k in baliases and v == baliases[k]:
            continue  # no change from original
        elif overwrite_aliases or k not in baliases:
            baliases[k] = v
        elif suppress_skip_message:
            pass
        else:
            msg = (
                "Skipping application of {0!r} alias from {1!r} "
                "since it shares a name with an existing xonsh alias. "
                'Use "--overwrite-alias" option to apply it anyway.'
                'You may prevent this message with "--suppress-skip-message" or '
                '"$FOREIGN_ALIASES_SUPPRESS_SKIP_MESSAGE = True".')
            print(msg.format(k, shell), file=_stderr)