Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added FuncAlias #5366

Merged
merged 54 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
5fb6485
FuncAlias
Apr 25, 2024
2dcc6f9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 25, 2024
12322eb
FuncAlias
Apr 25, 2024
6c9ceca
Merge remote-tracking branch 'origin/main' into func_alias
Apr 27, 2024
d7a4af7
wip
Apr 27, 2024
0ddbca0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 27, 2024
8f58a2f
wip
Apr 27, 2024
6893614
update
Apr 27, 2024
daf7f18
wip
Apr 27, 2024
49732d3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 27, 2024
87b59f9
wip
Apr 27, 2024
a95de7f
update
Apr 27, 2024
226ef6f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 27, 2024
ee3dad5
Merge remote-tracking branch 'origin/main' into func_alias
Apr 29, 2024
bf12f62
inherit __doc__
Apr 29, 2024
330fa12
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 29, 2024
ea5014d
Merge remote-tracking branch 'origin/main' into func_alias
May 2, 2024
d13b5a8
Merge remote-tracking branch 'origin/main' into func_alias
May 2, 2024
898d24a
Merge remote-tracking branch 'origin/main' into func_alias
May 3, 2024
61172fd
Merge remote-tracking branch 'origin/main' into func_alias
May 4, 2024
096abdb
Merge remote-tracking branch 'origin/main' into func_alias
May 6, 2024
aa4cd8c
wip
May 6, 2024
67f3bfa
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 6, 2024
6154480
wip
May 6, 2024
7a526f6
Merge remote-tracking branch 'origin/func_alias' into func_alias
May 6, 2024
312ed42
update
May 6, 2024
8cf9762
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 6, 2024
a9e0595
[pre-commit.ci] pre-commit autoupdate (#5397)
pre-commit-ci[bot] May 6, 2024
4eef469
update
May 6, 2024
51c5c37
update
May 7, 2024
75bc389
removed partial_proxy
May 7, 2024
c77ff66
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 7, 2024
9f8b7cd
test
May 7, 2024
c35cd32
Merge remote-tracking branch 'origin/func_alias' into func_alias
May 7, 2024
1fbca90
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 7, 2024
79fc021
test
May 7, 2024
dd21ec8
Merge remote-tracking branch 'origin/func_alias' into func_alias
May 7, 2024
b77dbf2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 7, 2024
4b490b0
Merge remote-tracking branch 'origin/main' into func_alias
May 7, 2024
1609d01
test
May 7, 2024
a8d7ff8
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 7, 2024
8b5b76a
news
May 7, 2024
1b5c30b
Merge remote-tracking branch 'origin/func_alias' into func_alias
May 7, 2024
09e7d36
Merge remote-tracking branch 'origin/main' into func_alias
May 10, 2024
235917c
wip
May 11, 2024
c3d84e9
wip
May 11, 2024
f00c121
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 11, 2024
5790d54
wip
May 11, 2024
a1726c7
Fix tests
May 11, 2024
00a2ccc
Merge remote-tracking branch 'origin/func_alias' into func_alias
May 11, 2024
c7dc5c0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 11, 2024
73afcbe
Fix tests
May 11, 2024
847317f
Merge remote-tracking branch 'origin/func_alias' into func_alias
May 11, 2024
7a26c12
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ default_language_version:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: 'v0.4.2'
rev: 'v0.4.3'
hooks:
- id: ruff
args: [., --fix, --exit-non-zero-on-fix]
Expand Down
23 changes: 23 additions & 0 deletions news/aliases_imp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**Added:**

* Added FuncAlias to process callable aliases.

**Changed:**

* <news item>

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* <news item>

**Security:**

* <news item>
5 changes: 3 additions & 2 deletions tests/test_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from xonsh.aliases import Aliases, ExecAlias


def cd(args, stdin=None, **kwargs):
def cd(args, stdin=None):
return args


Expand All @@ -30,10 +30,11 @@ def test_imports(xession):
"o": ["omg", "lala"],
"ls": ["ls", "- -"],
"color_ls": ["ls", "--color=true"],
"cd": cd,
"cd": "FuncAlias",
"indirect_cd": ["cd", ".."],
}
raw = ales._raw
raw["cd"] = type(ales["cd"]).__name__
assert raw == expected


Expand Down
39 changes: 38 additions & 1 deletion xonsh/aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
argvquote,
escape_windows_cmd_string,
print_color,
print_exception,
strip_simple_quotes,
swap_values,
to_repr_pretty_,
Expand All @@ -54,6 +55,40 @@ def EXEC_ALIAS_RE():
return re.compile(r"@\(|\$\(|!\(|\$\[|!\[|\&\&|\|\||\s+and\s+|\s+or\s+|[>|<]")


class FuncAlias:
"""Provides a callable alias for xonsh commands."""

attributes_inherit = ["__xonsh_threadable__", "__xonsh_capturable__", "__doc__"]
attributes_show = ["__xonsh_threadable__", "__xonsh_capturable__"]

def __init__(self, name, func):
self.__name__ = self.name = name
self.func = func
for attr in self.attributes_inherit:
if (val := getattr(func, attr, None)) is not None:
self.__setattr__(attr, val)

def __repr__(self):
r = {"name": self.name, "func": self.func}
r |= {
attr: val
for attr in self.attributes_show
if (val := getattr(self, attr, None)) is not None
}
return f"FuncAlias({repr(r)})"

def __call__(
self, args=None, stdin=None, stdout=None, stderr=None, spec=None, stack=None
):
try:
func_args = [args, stdin, stdout, stderr, spec, stack][
: len(inspect.signature(self.func).parameters)
]
anki-code marked this conversation as resolved.
Show resolved Hide resolved
return self.func(*func_args)
except Exception:
print_exception(f"Exception in {repr(self)}")
anki-code marked this conversation as resolved.
Show resolved Hide resolved


class Aliases(cabc.MutableMapping):
"""Represents a location to hold and look up aliases."""

Expand Down Expand Up @@ -182,6 +217,8 @@ def __setitem__(self, key, val):
else:
# need to exec alias
self._raw[key] = ExecAlias(val, filename=f)
elif isinstance(val, types.FunctionType):
self._raw[key] = FuncAlias(key, val)
else:
self._raw[key] = val

Expand Down Expand Up @@ -225,7 +262,7 @@ def __repr__(self):


class ExecAlias:
"""Provides a callable alias for xonsh source code."""
"""Provides an exec alias for xonsh source code."""

def __init__(self, src, filename="<exec-alias>"):
"""
Expand Down
72 changes: 3 additions & 69 deletions xonsh/procs/proxies.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import collections.abc as cabc
import functools
import inspect
import io
import os
import signal
Expand Down Expand Up @@ -274,7 +273,7 @@ def parse_proxy_return(r, stdout, stderr):
stdout.write(str(r[0]))
stdout.flush()
if rlen > 1 and r[1] is not None:
stderr.write(str(r[1]))
stderr.write(xt.endswith_newline(str(r[1])))
stderr.flush()
if rlen > 2 and isinstance(r[2], int):
cmd_result = r[2]
Expand All @@ -285,69 +284,6 @@ def parse_proxy_return(r, stdout, stderr):
return cmd_result


def proxy_zero(f, args, stdin, stdout, stderr, spec, stack):
"""Calls a proxy function which takes no parameters."""
return f()


def proxy_one(f, args, stdin, stdout, stderr, spec, stack):
"""Calls a proxy function which takes one parameter: args"""
return f(args)


def proxy_two(f, args, stdin, stdout, stderr, spec, stack):
"""Calls a proxy function which takes two parameter: args and stdin."""
return f(args, stdin)


def proxy_three(f, args, stdin, stdout, stderr, spec, stack):
"""Calls a proxy function which takes three parameter: args, stdin, stdout."""
return f(args, stdin, stdout)


def proxy_four(f, args, stdin, stdout, stderr, spec, stack):
"""Calls a proxy function which takes four parameter: args, stdin, stdout,
and stderr.
"""
return f(args, stdin, stdout, stderr)


def proxy_five(f, args, stdin, stdout, stderr, spec, stack):
"""Calls a proxy function which takes four parameter: args, stdin, stdout,
stderr, and spec.
"""
return f(args, stdin, stdout, stderr, spec)


PROXIES = (proxy_zero, proxy_one, proxy_two, proxy_three, proxy_four, proxy_five)


def partial_proxy(f):
"""Dispatches the appropriate proxy function based on the number of args."""
numargs = 0

for name, param in inspect.signature(f).parameters.items():
# handle *args/**kwargs signature
if param.kind in {param.VAR_KEYWORD, param.VAR_POSITIONAL}:
numargs = 6
break
if (
param.kind == param.POSITIONAL_ONLY
or param.kind == param.POSITIONAL_OR_KEYWORD
):
numargs += 1
elif name in xt.ALIAS_KWARG_NAMES and param.kind == param.KEYWORD_ONLY:
numargs += 1
if numargs < 6:
return functools.partial(PROXIES[numargs], f)
elif numargs == 6:
# don't need to partial.
return f
else:
e = "Expected proxy with 6 or fewer arguments for {}, not {}"
raise xt.XonshError(e.format(", ".join(xt.ALIAS_KWARG_NAMES), numargs))


def get_proc_proxy_name(cls):
func_name = cls.f
if type(cls.f) is functools.partial:
Expand Down Expand Up @@ -409,8 +345,7 @@ def __init__(
env : Mapping, optional
Environment mapping.
"""
self.orig_f = f
self.f = partial_proxy(f)
self.f = f
self.args = args
self.pid = None
self.returncode = None
Expand Down Expand Up @@ -799,8 +734,7 @@ def __init__(
close_fds=False,
env=None,
):
self.orig_f = f
self.f = partial_proxy(f)
self.f = f
self.args = args
self.pid = os.getpid()
self.returncode = None
Expand Down
16 changes: 12 additions & 4 deletions xonsh/procs/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,10 +488,18 @@ def _run_binary(self, kwargs):
except FileNotFoundError as ex:
cmd0 = self.cmd[0]
if len(self.cmd) == 1 and cmd0.endswith("?"):
with contextlib.suppress(OSError):
return self.cls(
["man", cmd0.rstrip("?")], bufsize=bufsize, **kwargs
cmdq = cmd0.rstrip("?")
if cmdq in XSH.aliases:
alias = XSH.aliases[cmdq]
descr = (
repr(alias) + (":\n" + doc)
if (doc := getattr(alias, "__doc__", ""))
else ""
)
return self.cls(["echo", descr], bufsize=bufsize, **kwargs)
else:
with contextlib.suppress(OSError):
return self.cls(["man", cmdq], bufsize=bufsize, **kwargs)
e = f"xonsh: subprocess mode: command not found: {repr(cmd0)}"
env = XSH.env
sug = xt.suggest_commands(cmd0, env)
Expand Down Expand Up @@ -701,7 +709,7 @@ def resolve_stack(self):
if not callable(self.alias):
return
# check that we actual need the stack
sig = inspect.signature(self.alias)
sig = inspect.signature(getattr(self.alias, "func", self.alias))
if len(sig.parameters) <= 5 and "stack" not in sig.parameters:
return
# compute the stack, and filter out these build methods
Expand Down
5 changes: 5 additions & 0 deletions xonsh/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -2858,3 +2858,8 @@ def unquote(s: str, chars="'\""):
if len(s) >= 2 and s[0] == s[-1] and s[0] in chars:
return s[1:-1]
return s


def endswith_newline(s: str):
"""Add new line character to string if needed."""
return s if s.endswith("\n") else (s + "\n")