mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
Alias that returns modified command (#5473)
* Command Alias * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * news * docs * tests * docs * wip * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * tests * tests * clean * news * news * bumptests * bumptests * new api * tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * doooocs * comments * comments * comments * aliases.CUT_ARGS * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * aliases.CUT_ARGS * aliases.CUT_ARGS * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * aliases.CUT_ARGS * aliases.CUT_ARGS * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * aliases.CUT_ARGS * comments * bump test * remove CUT_ARGS * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * test * test * wip * revert * wip * wip * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * wip * docs * news * tests * tests * test * test * test * Update docs/tutorial.rst Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * Update xonsh/aliases.py Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix, thanks jaraco! * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * more comments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * cleaning --------- Co-authored-by: a <1@1.1> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jason R. Coombs <jaraco@jaraco.com>
This commit is contained in:
parent
24fcbb4638
commit
87032f6d30
7 changed files with 382 additions and 51 deletions
|
@ -1275,7 +1275,7 @@ functions. If you don't know what these do, you probably don't need them.
|
|||
|
||||
|
||||
Aliases
|
||||
==============================
|
||||
=======
|
||||
Another important xonsh built-in is the ``aliases`` mapping. This is
|
||||
like a dictionary that affects how subprocess commands are run. If you are
|
||||
familiar with the Bash ``alias`` built-in, this is similar. Alias command
|
||||
|
@ -1305,6 +1305,44 @@ If you were to run ``gco feature-fabulous`` with the above aliases in effect,
|
|||
the command would reduce to ``['git', 'checkout', 'feature-fabulous']`` before
|
||||
being executed.
|
||||
|
||||
Alias to modify command
|
||||
-----------------------
|
||||
|
||||
The best way to modify command on the fly is to use alias that returns modified command.
|
||||
One of the most interesting application is expanding an alias:
|
||||
|
||||
.. code-block:: xonshcon
|
||||
|
||||
>>> @aliases.register
|
||||
... @aliases.return_command
|
||||
... def _xsudo(args):
|
||||
... """Sudo with expanding aliases."""
|
||||
... return ['sudo', '--', *aliases.eval_alias(args)]
|
||||
...
|
||||
>>> aliases['install'] = "apt install cowsay"
|
||||
>>> xsudo install
|
||||
# Password:
|
||||
# Install cowsay
|
||||
|
||||
Or implement logic to run the right command:
|
||||
|
||||
.. code-block:: xonshcon
|
||||
|
||||
>>> @aliases.register
|
||||
... @aliases.return_command
|
||||
... def _vi(args):
|
||||
... """Universal vi editor."""
|
||||
... if $(which vim 2>/dev/null):
|
||||
... return ['vim'] + args
|
||||
... else:
|
||||
... return ['vi'] + args
|
||||
...
|
||||
>>> vi file
|
||||
|
||||
|
||||
ExecAlias
|
||||
---------
|
||||
|
||||
If the string is representing a block of xonsh code, the alias will be registered
|
||||
as an ``ExecAlias``, which is a callable alias. This block of code will then be
|
||||
executed whenever the alias is run. The arguments are available in the list ``$args``
|
||||
|
|
23
news/alias_return_cmd.rst
Normal file
23
news/alias_return_cmd.rst
Normal file
|
@ -0,0 +1,23 @@
|
|||
**Added:**
|
||||
|
||||
* Added ``@aliases.return_command`` decorator to eliminate the need to wrap the logic for modifying command into callable alias wrapper (#5473).
|
||||
|
||||
**Changed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -183,6 +183,7 @@ def test_interrupted_process_returncode(xonsh_session, captured, interactive):
|
|||
|
||||
|
||||
@skip_if_on_windows
|
||||
@pytest.mark.flaky(reruns=3, reruns_delay=1)
|
||||
def test_proc_raise_subproc_error(xonsh_session):
|
||||
xonsh_session.env["RAISE_SUBPROC_ERROR"] = False
|
||||
|
||||
|
@ -469,3 +470,110 @@ def test_partial_args_from_classmethod(xession):
|
|||
xession.aliases["alias_with_partial_args"] = Class.alias
|
||||
out = run_subproc([["alias_with_partial_args"]], captured="stdout")
|
||||
assert out == "ok"
|
||||
|
||||
|
||||
def test_alias_return_command_alone(xession):
|
||||
@xession.aliases.register("wakka")
|
||||
@xession.aliases.return_command
|
||||
def _wakka(args):
|
||||
return ["echo"] + args
|
||||
|
||||
cmds = [
|
||||
["wakka"],
|
||||
]
|
||||
spec = cmds_to_specs(cmds, captured="object")[-1]
|
||||
assert spec.cmd == ["echo"]
|
||||
assert spec.alias_name == "wakka"
|
||||
|
||||
|
||||
def test_alias_return_command_alone_args(xession):
|
||||
@xession.aliases.register("wakka")
|
||||
@xession.aliases.return_command
|
||||
def _wakka(args):
|
||||
return ["echo", "e0", "e1"] + args
|
||||
|
||||
cmds = [
|
||||
["wakka", "0", "1"],
|
||||
]
|
||||
spec = cmds_to_specs(cmds, captured="object")[-1]
|
||||
assert spec.cmd == ["echo", "e0", "e1", "0", "1"]
|
||||
assert spec.alias_name == "wakka"
|
||||
|
||||
|
||||
def test_alias_return_command_chain(xession):
|
||||
xession.aliases["foreground"] = "midground f0 f1"
|
||||
|
||||
@xession.aliases.register("midground")
|
||||
@xession.aliases.return_command
|
||||
def _midground(args):
|
||||
return ["ground", "m0", "m1"] + args
|
||||
|
||||
xession.aliases["ground"] = "background g0 g1"
|
||||
xession.aliases["background"] = "echo b0 b1"
|
||||
|
||||
cmds = [
|
||||
["foreground", "0", "1"],
|
||||
]
|
||||
spec = cmds_to_specs(cmds, captured="object")[-1]
|
||||
assert spec.cmd == [
|
||||
"echo",
|
||||
"b0",
|
||||
"b1",
|
||||
"g0",
|
||||
"g1",
|
||||
"m0",
|
||||
"m1",
|
||||
"f0",
|
||||
"f1",
|
||||
"0",
|
||||
"1",
|
||||
]
|
||||
assert spec.alias_name == "foreground"
|
||||
|
||||
|
||||
def test_alias_return_command_chain_spec_modifiers(xession):
|
||||
xession.aliases["foreground"] = "midground f0 f1"
|
||||
|
||||
xession.aliases["xunthread"] = SpecAttrModifierAlias(
|
||||
{"threadable": False, "force_threadable": False}
|
||||
)
|
||||
|
||||
@xession.aliases.register("midground")
|
||||
@xession.aliases.return_command
|
||||
def _midground(args):
|
||||
return ["ground", "m0", "m1"]
|
||||
|
||||
xession.aliases["ground"] = "background g0 g1"
|
||||
xession.aliases["background"] = "xunthread echo b0 b1"
|
||||
|
||||
cmds = [
|
||||
["foreground", "0", "1"],
|
||||
]
|
||||
spec = cmds_to_specs(cmds, captured="object")[-1]
|
||||
assert spec.cmd == ["echo", "b0", "b1", "g0", "g1", "m0", "m1"]
|
||||
assert spec.alias_name == "foreground"
|
||||
assert spec.threadable is False
|
||||
|
||||
|
||||
def test_alias_return_command_eval_inside(xession):
|
||||
xession.aliases["xthread"] = SpecAttrModifierAlias(
|
||||
{"threadable": True, "force_threadable": True}
|
||||
)
|
||||
|
||||
@xession.aliases.register("xsudo")
|
||||
@xession.aliases.return_command
|
||||
def _midground(args, spec_modifiers=None):
|
||||
return [
|
||||
"sudo",
|
||||
*xession.aliases.eval_alias(args, spec_modifiers=spec_modifiers),
|
||||
]
|
||||
|
||||
xession.aliases["cmd"] = "xthread echo 1"
|
||||
|
||||
cmds = [
|
||||
["xsudo", "cmd"],
|
||||
]
|
||||
spec = cmds_to_specs(cmds, captured="object")[-1]
|
||||
assert spec.cmd == ["sudo", "echo", "1"]
|
||||
assert spec.alias_name == "xsudo"
|
||||
assert spec.threadable is True
|
||||
|
|
|
@ -6,7 +6,7 @@ import sys
|
|||
|
||||
import pytest
|
||||
|
||||
from xonsh.aliases import Aliases, ExecAlias
|
||||
from xonsh.aliases import Aliases, ExecAlias, run_alias_by_params
|
||||
|
||||
|
||||
def cd(args, stdin=None):
|
||||
|
@ -53,10 +53,17 @@ def test_eval_recursive(xession):
|
|||
assert ales.get("color_ls") == ["ls", "- -", "--color=true"]
|
||||
|
||||
|
||||
def test_eval_callable(xession):
|
||||
ales = make_aliases()
|
||||
resolved = ales.get(["cd", "tmp"])
|
||||
assert callable(resolved[0])
|
||||
assert isinstance(resolved[1], str)
|
||||
|
||||
|
||||
def test_eval_recursive_callable_partial(xonsh_execer, xession):
|
||||
ales = make_aliases()
|
||||
xession.env["HOME"] = os.path.expanduser("~")
|
||||
assert ales.get("indirect_cd")(["arg2", "arg3"]) == ["..", "arg2", "arg3"]
|
||||
assert ales.get(["indirect_cd", "arg2", "arg3"])[1:] == ["..", "arg2", "arg3"]
|
||||
|
||||
|
||||
def _return_to_sender_all(args, stdin, stdout, stderr, spec, stack):
|
||||
|
@ -74,9 +81,11 @@ def _return_to_sender_all(args, stdin, stdout, stderr, spec, stack):
|
|||
|
||||
def test_recursive_callable_partial_all(xession):
|
||||
ales = Aliases({"rtn": _return_to_sender_all, "rtn-recurse": ["rtn", "arg1"]})
|
||||
alias = ales.get("rtn-recurse")
|
||||
alias = ales.get("rtn-recurse")[0]
|
||||
assert callable(alias)
|
||||
args, obs = alias(["arg2"], stdin="a", stdout="b", stderr="c", spec="d", stack="e")
|
||||
args, obs = alias(
|
||||
["arg1", "arg2"], stdin="a", stdout="b", stderr="c", spec="d", stack="e"
|
||||
)
|
||||
assert args == ["arg1", "arg2"]
|
||||
assert len(obs) == 5
|
||||
exp = {"stdin": "a", "stdout": "b", "stderr": "c", "spec": "d", "stack": "e"}
|
||||
|
@ -89,9 +98,9 @@ def _return_to_sender_handles(args, stdin, stdout, stderr):
|
|||
|
||||
def test_recursive_callable_partial_handles(xession):
|
||||
ales = Aliases({"rtn": _return_to_sender_handles, "rtn-recurse": ["rtn", "arg1"]})
|
||||
alias = ales.get("rtn-recurse")
|
||||
alias = ales.get("rtn-recurse")[0]
|
||||
assert callable(alias)
|
||||
args, obs = alias(["arg2"], stdin="a", stdout="b", stderr="c")
|
||||
args, obs = alias(["arg1", "arg2"], stdin="a", stdout="b", stderr="c")
|
||||
assert args == ["arg1", "arg2"]
|
||||
assert len(obs) == 3
|
||||
exp = {"stdin": "a", "stdout": "b", "stderr": "c"}
|
||||
|
@ -104,7 +113,7 @@ def _return_to_sender_none():
|
|||
|
||||
def test_recursive_callable_partial_none(xession):
|
||||
ales = Aliases({"rtn": _return_to_sender_none, "rtn-recurse": ["rtn"]})
|
||||
alias = ales.get("rtn-recurse")
|
||||
alias = ales.get("rtn-recurse")[0]
|
||||
assert callable(alias)
|
||||
args, obs = alias()
|
||||
assert args == "wakka"
|
||||
|
@ -214,3 +223,26 @@ def test_register_decorator(xession):
|
|||
def _private(): ...
|
||||
|
||||
assert set(aliases) == {"debug", "name", "private"}
|
||||
|
||||
|
||||
def test_run_alias_by_params():
|
||||
def alias_named_params(args, stdout):
|
||||
return (args, stdout)
|
||||
|
||||
def alias_named_params_rev(stdout, args):
|
||||
return (args, stdout)
|
||||
|
||||
def alias_list_params(a, i, o, e):
|
||||
return (a, i, o, e)
|
||||
|
||||
assert run_alias_by_params(alias_named_params, {"args": 1, "stdout": 2}) == (1, 2)
|
||||
assert run_alias_by_params(alias_named_params_rev, {"args": 1, "stdout": 2}) == (
|
||||
1,
|
||||
2,
|
||||
)
|
||||
assert run_alias_by_params(alias_list_params, {"args": 1, "stderr": 4}) == (
|
||||
1,
|
||||
None,
|
||||
None,
|
||||
4,
|
||||
)
|
||||
|
|
172
xonsh/aliases.py
172
xonsh/aliases.py
|
@ -1,15 +1,17 @@
|
|||
"""Aliases for the xonsh shell."""
|
||||
|
||||
import argparse
|
||||
import collections.abc as cabc
|
||||
import functools
|
||||
import inspect
|
||||
import operator
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import types
|
||||
import typing as tp
|
||||
from collections import abc as cabc
|
||||
from typing import Literal
|
||||
|
||||
import xonsh.completers._aliases as xca
|
||||
import xonsh.history.main as xhm
|
||||
|
@ -42,6 +44,7 @@ from xonsh.tools import (
|
|||
argvquote,
|
||||
escape_windows_cmd_string,
|
||||
print_color,
|
||||
print_exception,
|
||||
strip_simple_quotes,
|
||||
swap_values,
|
||||
to_repr_pretty_,
|
||||
|
@ -59,8 +62,9 @@ def EXEC_ALIAS_RE():
|
|||
class FuncAlias:
|
||||
"""Provides a callable alias for xonsh commands."""
|
||||
|
||||
attributes_show = ["__xonsh_threadable__", "__xonsh_capturable__"]
|
||||
attributes_show = ["__xonsh_threadable__", "__xonsh_capturable__", "return_what"]
|
||||
attributes_inherit = attributes_show + ["__doc__"]
|
||||
return_what: Literal["command", "result"] = "result"
|
||||
|
||||
def __init__(self, name, func=None):
|
||||
self.__name__ = self.name = name
|
||||
|
@ -79,12 +83,27 @@ class FuncAlias:
|
|||
return f"FuncAlias({repr(r)})"
|
||||
|
||||
def __call__(
|
||||
self, args=None, stdin=None, stdout=None, stderr=None, spec=None, stack=None
|
||||
self,
|
||||
args=None,
|
||||
stdin=None,
|
||||
stdout=None,
|
||||
stderr=None,
|
||||
spec=None,
|
||||
stack=None,
|
||||
spec_modifiers=None,
|
||||
):
|
||||
func_args = [args, stdin, stdout, stderr, spec, stack][
|
||||
: len(inspect.signature(self.func).parameters)
|
||||
]
|
||||
return self.func(*func_args)
|
||||
return run_alias_by_params(
|
||||
self.func,
|
||||
{
|
||||
"args": args,
|
||||
"stdin": stdin,
|
||||
"stdout": stdout,
|
||||
"stderr": stderr,
|
||||
"spec": spec,
|
||||
"stack": stack,
|
||||
"spec_modifiers": spec_modifiers,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class Aliases(cabc.MutableMapping):
|
||||
|
@ -132,28 +151,17 @@ class Aliases(cabc.MutableMapping):
|
|||
|
||||
return wrapper
|
||||
|
||||
def get(self, key, default=None, spec_modifiers=None):
|
||||
"""Returns the (possibly modified) value. If the key is not present,
|
||||
then `default` is returned.
|
||||
If the value is callable, it is returned without modification. If it
|
||||
is an iterable of strings it will be evaluated recursively to expand
|
||||
other aliases, resulting in a new list or a "partially applied"
|
||||
callable.
|
||||
"""
|
||||
spec_modifiers = spec_modifiers if spec_modifiers is not None else []
|
||||
val = self._raw.get(key)
|
||||
if val is None:
|
||||
return default
|
||||
elif isinstance(val, cabc.Iterable) or callable(val):
|
||||
return self.eval_alias(
|
||||
val, seen_tokens={key}, spec_modifiers=spec_modifiers
|
||||
)
|
||||
else:
|
||||
msg = "alias of {!r} has an inappropriate type: {!r}"
|
||||
raise TypeError(msg.format(key, val))
|
||||
def return_command(self, f):
|
||||
"""Decorator that switches alias from returning result to return in new command for execution."""
|
||||
f.return_what = "command"
|
||||
return f
|
||||
|
||||
def eval_alias(
|
||||
self, value, seen_tokens=frozenset(), acc_args=(), spec_modifiers=None
|
||||
self,
|
||||
value,
|
||||
seen_tokens=frozenset(),
|
||||
acc_args=(),
|
||||
spec_modifiers=None,
|
||||
):
|
||||
"""
|
||||
"Evaluates" the alias ``value``, by recursively looking up the leftmost
|
||||
|
@ -182,8 +190,18 @@ class Aliases(cabc.MutableMapping):
|
|||
break
|
||||
value = value[i:]
|
||||
|
||||
if callable(value) and getattr(value, "return_what", "result") == "command":
|
||||
try:
|
||||
value = value(acc_args, spec_modifiers=spec_modifiers)
|
||||
acc_args = []
|
||||
except Exception as e:
|
||||
print_exception(f"Exception inside alias {value}: {e}")
|
||||
return None
|
||||
if not len(value):
|
||||
raise ValueError("return_command alias: zero arguments.")
|
||||
|
||||
if callable(value):
|
||||
return partial_eval_alias(value, acc_args=acc_args)
|
||||
return [value] + list(acc_args)
|
||||
else:
|
||||
expand_path = XSH.expand_path
|
||||
token, *rest = map(expand_path, value)
|
||||
|
@ -205,6 +223,54 @@ class Aliases(cabc.MutableMapping):
|
|||
spec_modifiers=spec_modifiers,
|
||||
)
|
||||
|
||||
def get(
|
||||
self,
|
||||
key,
|
||||
default=None,
|
||||
spec_modifiers=None,
|
||||
):
|
||||
"""
|
||||
Returns list that represent command with resolved aliases.
|
||||
The ``key`` can be string with alias name or list for a command.
|
||||
In the first position will be the resolved command name or callable alias.
|
||||
If the key is not present, then `default` is returned.
|
||||
|
||||
``spec_modifiers`` is the list of SpecModifier objects that found during
|
||||
resolving aliases (#5443).
|
||||
|
||||
Note! The return value is always list because during resolving
|
||||
we can find return_command alias that can completely replace
|
||||
command and add new arguments.
|
||||
"""
|
||||
spec_modifiers = spec_modifiers if spec_modifiers is not None else []
|
||||
args = []
|
||||
if isinstance(key, list):
|
||||
args = key[1:]
|
||||
key = key[0]
|
||||
val = self._raw.get(key)
|
||||
if callable(val) and getattr(val, "return_what", "result") == "command":
|
||||
try:
|
||||
val = val(args, spec_modifiers=spec_modifiers)
|
||||
args = []
|
||||
except Exception as e:
|
||||
print_exception(f"Exception inside alias {key!r}: {e}")
|
||||
return None
|
||||
if not len(val):
|
||||
raise ValueError("return_command alias: zero arguments.")
|
||||
|
||||
if val is None:
|
||||
return default
|
||||
elif isinstance(val, cabc.Iterable) or callable(val):
|
||||
return self.eval_alias(
|
||||
val,
|
||||
seen_tokens={key},
|
||||
spec_modifiers=spec_modifiers,
|
||||
acc_args=args,
|
||||
)
|
||||
else:
|
||||
msg = "alias of {!r} has an inappropriate type: {!r}"
|
||||
raise TypeError(msg.format(key, val))
|
||||
|
||||
def expand_alias(self, line: str, cursor_index: int) -> str:
|
||||
"""Expands any aliases present in line if alias does not point to a
|
||||
builtin function and if alias is only a single command.
|
||||
|
@ -408,6 +474,21 @@ class PartialEvalAlias6(PartialEvalAliasBase):
|
|||
return self.f(args, stdin, stdout, stderr, spec, stack)
|
||||
|
||||
|
||||
class PartialEvalAlias7(PartialEvalAliasBase):
|
||||
def __call__(
|
||||
self,
|
||||
args,
|
||||
stdin=None,
|
||||
stdout=None,
|
||||
stderr=None,
|
||||
spec=None,
|
||||
stack=None,
|
||||
spec_modifiers=None,
|
||||
):
|
||||
args = list(self.acc_args) + args
|
||||
return self.f(args, stdin, stdout, stderr, spec, stack, spec_modifiers)
|
||||
|
||||
|
||||
PARTIAL_EVAL_ALIASES = (
|
||||
PartialEvalAlias0,
|
||||
PartialEvalAlias1,
|
||||
|
@ -416,6 +497,7 @@ PARTIAL_EVAL_ALIASES = (
|
|||
PartialEvalAlias4,
|
||||
PartialEvalAlias5,
|
||||
PartialEvalAlias6,
|
||||
PartialEvalAlias7,
|
||||
)
|
||||
|
||||
|
||||
|
@ -436,13 +518,43 @@ def partial_eval_alias(f, acc_args=()):
|
|||
numargs += 1
|
||||
elif name in ALIAS_KWARG_NAMES and param.kind == param.KEYWORD_ONLY:
|
||||
numargs += 1
|
||||
if numargs < 7:
|
||||
if numargs < 8:
|
||||
return PARTIAL_EVAL_ALIASES[numargs](f, acc_args=acc_args)
|
||||
else:
|
||||
e = "Expected proxy with 6 or fewer arguments for {}, not {}"
|
||||
e = "Expected proxy with 7 or fewer arguments for {}, not {}"
|
||||
raise XonshError(e.format(", ".join(ALIAS_KWARG_NAMES), numargs))
|
||||
|
||||
|
||||
def run_alias_by_params(func: tp.Callable, params: dict[str, tp.Any]):
|
||||
"""
|
||||
Run alias function based on signature and params.
|
||||
If function param names are in alias signature fill them.
|
||||
If function params have unknown names fill using alias signature order.
|
||||
"""
|
||||
alias_params = {
|
||||
"args": None,
|
||||
"stdin": None,
|
||||
"stdout": None,
|
||||
"stderr": None,
|
||||
"spec": None,
|
||||
"stack": None,
|
||||
"spec_modifiers": None,
|
||||
}
|
||||
alias_params |= params
|
||||
sign = inspect.signature(func)
|
||||
func_params = sign.parameters.items()
|
||||
kwargs = {
|
||||
name: alias_params[name] for name, p in func_params if name in alias_params
|
||||
}
|
||||
|
||||
if len(kwargs) != len(func_params):
|
||||
# There is unknown param. Switch to positional mode.
|
||||
kwargs = dict(
|
||||
zip(map(operator.itemgetter(0), func_params), alias_params.values())
|
||||
)
|
||||
return func(**kwargs)
|
||||
|
||||
|
||||
#
|
||||
# Actual aliases below
|
||||
#
|
||||
|
|
|
@ -158,7 +158,7 @@ def complete_aliases(command: CommandContext):
|
|||
if cmd not in XSH.aliases:
|
||||
# only complete aliases
|
||||
return
|
||||
alias = XSH.aliases.get(cmd) # type: ignore
|
||||
alias = XSH.aliases.get(cmd)[0] # type: ignore
|
||||
|
||||
completer = getattr(alias, "xonsh_complete", None)
|
||||
if not completer:
|
||||
|
|
|
@ -705,29 +705,42 @@ class SubprocSpec:
|
|||
self.cmd = new_cmd
|
||||
|
||||
def resolve_alias(self):
|
||||
"""Sets alias in command, if applicable."""
|
||||
"""Resolving alias and setting up command."""
|
||||
cmd0 = self.cmd[0]
|
||||
spec_modifiers = []
|
||||
if cmd0 in self.alias_stack:
|
||||
# Disabling the alias resolving to prevent infinite loop in call stack
|
||||
# and futher using binary_loc to resolve the alias name.
|
||||
# and further using binary_loc to resolve the alias name.
|
||||
self.alias = None
|
||||
return
|
||||
|
||||
if callable(cmd0):
|
||||
alias = cmd0
|
||||
self.alias = cmd0
|
||||
else:
|
||||
found_spec_modifiers = []
|
||||
if isinstance(XSH.aliases, dict):
|
||||
# Windows tests
|
||||
alias = XSH.aliases.get(cmd0, None)
|
||||
if alias is not None:
|
||||
alias = alias + self.cmd[1:]
|
||||
else:
|
||||
alias = XSH.aliases.get(cmd0, None, spec_modifiers=spec_modifiers)
|
||||
alias = XSH.aliases.get(
|
||||
self.cmd,
|
||||
None,
|
||||
spec_modifiers=found_spec_modifiers,
|
||||
)
|
||||
if alias is not None:
|
||||
self.alias_name = cmd0
|
||||
self.alias = alias
|
||||
if spec_modifiers:
|
||||
for mod in spec_modifiers:
|
||||
self.add_spec_modifier(mod)
|
||||
if callable(alias[0]):
|
||||
# E.g. `alias == [FuncAlias({'name': 'cd'}), '/tmp']`
|
||||
self.alias = alias[0]
|
||||
self.cmd = [cmd0] + alias[1:]
|
||||
else:
|
||||
# E.g. `alias == ['ls', '-la']`
|
||||
self.alias = alias
|
||||
|
||||
if found_spec_modifiers:
|
||||
for mod in found_spec_modifiers:
|
||||
self.add_spec_modifier(mod)
|
||||
|
||||
def resolve_binary_loc(self):
|
||||
"""Sets the binary location"""
|
||||
|
@ -765,8 +778,7 @@ class SubprocSpec:
|
|||
self.cmd.pop(0)
|
||||
return
|
||||
else:
|
||||
self.cmd = alias + self.cmd[1:]
|
||||
# resolve any redirects the aliases may have applied
|
||||
self.cmd = alias
|
||||
self.resolve_redirects()
|
||||
if self.binary_loc is None:
|
||||
return
|
||||
|
@ -971,7 +983,13 @@ def _trace_specs(trace_mode, specs, cmds, captured):
|
|||
}
|
||||
p |= {
|
||||
a: getattr(s, a, None)
|
||||
for a in ["alias_name", "binary_loc", "threadable", "background"]
|
||||
for a in [
|
||||
"alias_name",
|
||||
"alias",
|
||||
"binary_loc",
|
||||
"threadable",
|
||||
"background",
|
||||
]
|
||||
}
|
||||
if trace_mode == 3:
|
||||
p |= {
|
||||
|
|
Loading…
Add table
Reference in a new issue