mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-04 08:24:40 +01:00
spec modifier: rename to decorator alias (#5579)
* rename * rename * rename * rename * rename * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * rename * rename * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * rename * rename * rename * rename * rename * rename * rename * rename * rename * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix * fix --------- Co-authored-by: a <1@1.1> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
96c2c0af14
commit
233e407b52
7 changed files with 98 additions and 77 deletions
|
@ -1632,26 +1632,26 @@ best used in conjunction with the ``unthreadable`` decorator. For example:
|
|||
Note that ``@()`` is required to pass the python list ``args`` to a subprocess
|
||||
command.
|
||||
|
||||
Specification Modifier Aliases
|
||||
------------------------------
|
||||
Decorator Aliases
|
||||
-----------------
|
||||
|
||||
Using ``SpecAttrModifierAlias`` and callable ``output_format`` you can
|
||||
Using ``DecoratorAlias`` and ``SpecAttrDecoratorAlias`` and callable ``output_format`` you can
|
||||
convert subprocess command output into Python object:
|
||||
|
||||
.. code-block:: xonshcon
|
||||
|
||||
import json, pathlib, yaml
|
||||
from xonsh.procs.specs import SpecAttrModifierAlias
|
||||
from xonsh.procs.specs import SpecAttrDecoratorAlias
|
||||
|
||||
aliases['@lines'] = SpecAttrModifierAlias({"output_format": 'list_lines'},
|
||||
aliases['@lines'] = SpecAttrDecoratorAlias({"output_format": 'list_lines'},
|
||||
"Set `list_lines` output format.")
|
||||
aliases['@json'] = SpecAttrModifierAlias({"output_format": lambda lines: json.loads('\n'.join(lines))},
|
||||
aliases['@json'] = SpecAttrDecoratorAlias({"output_format": lambda lines: json.loads('\n'.join(lines))},
|
||||
"Set `json` output format.")
|
||||
aliases['@path'] = SpecAttrModifierAlias({"output_format": lambda lines: pathlib.Path(':'.join(lines))},
|
||||
aliases['@path'] = SpecAttrDecoratorAlias({"output_format": lambda lines: pathlib.Path(':'.join(lines))},
|
||||
"Set `path` output format.")
|
||||
aliases['@yaml'] = SpecAttrModifierAlias({"output_format": lambda lines: yaml.safe_load('\n'.join(lines))},
|
||||
aliases['@yaml'] = SpecAttrDecoratorAlias({"output_format": lambda lines: yaml.safe_load('\n'.join(lines))},
|
||||
"Set `yaml` output format.")
|
||||
aliases['@noerr'] = SpecAttrModifierAlias({"raise_subproc_error": False},
|
||||
aliases['@noerr'] = SpecAttrDecoratorAlias({"raise_subproc_error": False},
|
||||
"Set `raise_subproc_error` to False.")
|
||||
|
||||
|
||||
|
|
24
news/rename_xthread.rst
Normal file
24
news/rename_xthread.rst
Normal file
|
@ -0,0 +1,24 @@
|
|||
**Added:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Changed:**
|
||||
|
||||
* Rename: ``SpecModifier``, ``xthread``, ``xunthread`` renamed to ``DecoratorAlias``, ``@thread``, ``@unthread`` to support idea that
|
||||
spec modifier is like a Python decorator.
|
||||
|
||||
**Deprecated:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Removed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* <news item>
|
||||
|
||||
**Security:**
|
||||
|
||||
* <news item>
|
|
@ -10,8 +10,8 @@ import pytest
|
|||
from xonsh.procs.posix import PopenThread
|
||||
from xonsh.procs.proxies import STDOUT_DISPATCHER, ProcProxy, ProcProxyThread
|
||||
from xonsh.procs.specs import (
|
||||
SpecAttrModifierAlias,
|
||||
SpecModifierAlias,
|
||||
DecoratorAlias,
|
||||
SpecAttrDecoratorAlias,
|
||||
SubprocSpec,
|
||||
_run_command_pipeline,
|
||||
cmds_to_specs,
|
||||
|
@ -288,8 +288,8 @@ def test_run_subproc_background(captured, exp_is_none):
|
|||
assert (return_val is None) == exp_is_none
|
||||
|
||||
|
||||
def test_spec_modifier_alias_alone(xession):
|
||||
xession.aliases["xunthread"] = SpecAttrModifierAlias(
|
||||
def test_spec_decorator_alias_alone(xession):
|
||||
xession.aliases["xunthread"] = SpecAttrDecoratorAlias(
|
||||
{"threadable": False, "force_threadable": False}
|
||||
)
|
||||
|
||||
|
@ -300,8 +300,8 @@ def test_spec_modifier_alias_alone(xession):
|
|||
assert spec.alias_name == "xunthread"
|
||||
|
||||
|
||||
def test_spec_modifier_alias(xession):
|
||||
xession.aliases["xunthread"] = SpecAttrModifierAlias(
|
||||
def test_spec_decorator_alias(xession):
|
||||
xession.aliases["xunthread"] = SpecAttrDecoratorAlias(
|
||||
{"threadable": False, "force_threadable": False}
|
||||
)
|
||||
|
||||
|
@ -313,11 +313,11 @@ def test_spec_modifier_alias(xession):
|
|||
assert spec.force_threadable is False
|
||||
|
||||
|
||||
def test_spec_modifier_alias_tree(xession):
|
||||
xession.aliases["xthread"] = SpecAttrModifierAlias(
|
||||
def test_spec_decorator_alias_tree(xession):
|
||||
xession.aliases["xthread"] = SpecAttrDecoratorAlias(
|
||||
{"threadable": True, "force_threadable": True}
|
||||
)
|
||||
xession.aliases["xunthread"] = SpecAttrModifierAlias(
|
||||
xession.aliases["xunthread"] = SpecAttrDecoratorAlias(
|
||||
{"threadable": False, "force_threadable": False}
|
||||
)
|
||||
|
||||
|
@ -337,11 +337,11 @@ def test_spec_modifier_alias_tree(xession):
|
|||
assert spec.force_threadable is False
|
||||
|
||||
|
||||
def test_spec_modifier_alias_multiple(xession):
|
||||
xession.aliases["@unthread"] = SpecAttrModifierAlias(
|
||||
def test_spec_decorator_alias_multiple(xession):
|
||||
xession.aliases["@unthread"] = SpecAttrDecoratorAlias(
|
||||
{"threadable": False, "force_threadable": False}
|
||||
)
|
||||
xession.aliases["@dict"] = SpecAttrModifierAlias({"output_format": "list_lines"})
|
||||
xession.aliases["@dict"] = SpecAttrDecoratorAlias({"output_format": "list_lines"})
|
||||
|
||||
cmds = [
|
||||
["@unthread", "@dict", "echo", "1"],
|
||||
|
@ -356,12 +356,12 @@ def test_spec_modifier_alias_multiple(xession):
|
|||
|
||||
|
||||
@skip_if_on_windows
|
||||
def test_spec_modifier_alias_output_format(xession):
|
||||
class SpecModifierOutputLinesAlias(SpecModifierAlias):
|
||||
def on_modifer_added(self, spec):
|
||||
def test_spec_decorator_alias_output_format(xession):
|
||||
class OutputLinesDecoratorAlias(DecoratorAlias):
|
||||
def decorate_spec(self, spec):
|
||||
spec.output_format = "list_lines"
|
||||
|
||||
xession.aliases["xlines"] = SpecModifierOutputLinesAlias()
|
||||
xession.aliases["xlines"] = OutputLinesDecoratorAlias()
|
||||
|
||||
cmds = [["xlines", "echo", "1\n2\n3"]]
|
||||
specs = cmds_to_specs(cmds, captured="stdout")
|
||||
|
@ -531,10 +531,10 @@ def test_alias_return_command_chain(xession):
|
|||
assert spec.alias_name == "foreground"
|
||||
|
||||
|
||||
def test_alias_return_command_chain_spec_modifiers(xession):
|
||||
def test_alias_return_command_chain_decorators(xession):
|
||||
xession.aliases["foreground"] = "midground f0 f1"
|
||||
|
||||
xession.aliases["xunthread"] = SpecAttrModifierAlias(
|
||||
xession.aliases["xunthread"] = SpecAttrDecoratorAlias(
|
||||
{"threadable": False, "force_threadable": False}
|
||||
)
|
||||
|
||||
|
@ -556,16 +556,16 @@ def test_alias_return_command_chain_spec_modifiers(xession):
|
|||
|
||||
|
||||
def test_alias_return_command_eval_inside(xession):
|
||||
xession.aliases["xthread"] = SpecAttrModifierAlias(
|
||||
xession.aliases["xthread"] = SpecAttrDecoratorAlias(
|
||||
{"threadable": True, "force_threadable": True}
|
||||
)
|
||||
|
||||
@xession.aliases.register("xsudo")
|
||||
@xession.aliases.return_command
|
||||
def _midground(args, spec_modifiers=None):
|
||||
def _midground(args, decorators=None):
|
||||
return [
|
||||
"sudo",
|
||||
*xession.aliases.eval_alias(args, spec_modifiers=spec_modifiers),
|
||||
*xession.aliases.eval_alias(args, decorators=decorators),
|
||||
]
|
||||
|
||||
xession.aliases["cmd"] = "xthread echo 1"
|
||||
|
|
|
@ -1365,10 +1365,10 @@ def test_alias_stability():
|
|||
|
||||
@skip_if_on_windows
|
||||
@pytest.mark.flaky(reruns=3, reruns_delay=1)
|
||||
def test_spec_modifier_alias():
|
||||
def test_spec_decorator_alias():
|
||||
"""Testing spec modifier alias with `@` in the alias name."""
|
||||
stdin_cmd = (
|
||||
"from xonsh.procs.specs import SpecAttrModifierAlias as mod\n"
|
||||
"from xonsh.procs.specs import SpecAttrDecoratorAlias as mod\n"
|
||||
'aliases["@dict"] = mod({"output_format": lambda lines: eval("\\n".join(lines))})\n'
|
||||
"d = $(@dict echo '{\"a\":42}')\n"
|
||||
"print('Answer =', d['a'])\n"
|
||||
|
|
|
@ -35,7 +35,7 @@ from xonsh.platform import (
|
|||
)
|
||||
from xonsh.procs.executables import locate_file
|
||||
from xonsh.procs.jobs import bg, clean_jobs, disown, fg, jobs
|
||||
from xonsh.procs.specs import SpecAttrModifierAlias, SpecModifierAlias
|
||||
from xonsh.procs.specs import DecoratorAlias, SpecAttrDecoratorAlias
|
||||
from xonsh.timings import timeit_alias
|
||||
from xonsh.tools import (
|
||||
ALIAS_KWARG_NAMES,
|
||||
|
@ -90,7 +90,7 @@ class FuncAlias:
|
|||
stderr=None,
|
||||
spec=None,
|
||||
stack=None,
|
||||
spec_modifiers=None,
|
||||
decorators=None,
|
||||
):
|
||||
return run_alias_by_params(
|
||||
self.func,
|
||||
|
@ -101,7 +101,7 @@ class FuncAlias:
|
|||
"stderr": stderr,
|
||||
"spec": spec,
|
||||
"stack": stack,
|
||||
"spec_modifiers": spec_modifiers,
|
||||
"decorators": decorators,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -161,7 +161,7 @@ class Aliases(cabc.MutableMapping):
|
|||
value,
|
||||
seen_tokens=frozenset(),
|
||||
acc_args=(),
|
||||
spec_modifiers=None,
|
||||
decorators=None,
|
||||
):
|
||||
"""
|
||||
"Evaluates" the alias ``value``, by recursively looking up the leftmost
|
||||
|
@ -173,7 +173,7 @@ class Aliases(cabc.MutableMapping):
|
|||
callable. The resulting callable will be "partially applied" with
|
||||
``["-al", "arg"]``.
|
||||
"""
|
||||
spec_modifiers = spec_modifiers if spec_modifiers is not None else []
|
||||
decorators = decorators if decorators is not None else []
|
||||
# Beware of mutability: default values for keyword args are evaluated
|
||||
# only once.
|
||||
if (
|
||||
|
@ -183,8 +183,8 @@ class Aliases(cabc.MutableMapping):
|
|||
):
|
||||
i = 0
|
||||
for v in value:
|
||||
if isinstance(mod := self._raw.get(str(v)), SpecModifierAlias):
|
||||
spec_modifiers.append(mod)
|
||||
if isinstance(mod := self._raw.get(str(v)), DecoratorAlias):
|
||||
decorators.append(mod)
|
||||
i += 1
|
||||
else:
|
||||
break
|
||||
|
@ -192,7 +192,7 @@ class Aliases(cabc.MutableMapping):
|
|||
|
||||
if callable(value) and getattr(value, "return_what", "result") == "command":
|
||||
try:
|
||||
value = value(acc_args, spec_modifiers=spec_modifiers)
|
||||
value = value(acc_args, decorators=decorators)
|
||||
acc_args = []
|
||||
except Exception as e:
|
||||
print_exception(f"Exception inside alias {value}: {e}")
|
||||
|
@ -220,14 +220,14 @@ class Aliases(cabc.MutableMapping):
|
|||
self._raw[token],
|
||||
seen_tokens,
|
||||
acc_args,
|
||||
spec_modifiers=spec_modifiers,
|
||||
decorators=decorators,
|
||||
)
|
||||
|
||||
def get(
|
||||
self,
|
||||
key,
|
||||
default=None,
|
||||
spec_modifiers=None,
|
||||
decorators=None,
|
||||
):
|
||||
"""
|
||||
Returns list that represent command with resolved aliases.
|
||||
|
@ -235,14 +235,14 @@ class Aliases(cabc.MutableMapping):
|
|||
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
|
||||
``decorators`` is the list of `DecoratorAlias` 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 []
|
||||
decorators = decorators if decorators is not None else []
|
||||
args = []
|
||||
if isinstance(key, list):
|
||||
args = key[1:]
|
||||
|
@ -250,7 +250,7 @@ class Aliases(cabc.MutableMapping):
|
|||
val = self._raw.get(key)
|
||||
if callable(val) and getattr(val, "return_what", "result") == "command":
|
||||
try:
|
||||
val = val(args, spec_modifiers=spec_modifiers)
|
||||
val = val(args, decorators=decorators)
|
||||
args = []
|
||||
except Exception as e:
|
||||
print_exception(f"Exception inside alias {key!r}: {e}")
|
||||
|
@ -264,7 +264,7 @@ class Aliases(cabc.MutableMapping):
|
|||
return self.eval_alias(
|
||||
val,
|
||||
seen_tokens={key},
|
||||
spec_modifiers=spec_modifiers,
|
||||
decorators=decorators,
|
||||
acc_args=args,
|
||||
)
|
||||
else:
|
||||
|
@ -483,10 +483,10 @@ class PartialEvalAlias7(PartialEvalAliasBase):
|
|||
stderr=None,
|
||||
spec=None,
|
||||
stack=None,
|
||||
spec_modifiers=None,
|
||||
decorators=None,
|
||||
):
|
||||
args = list(self.acc_args) + args
|
||||
return self.f(args, stdin, stdout, stderr, spec, stack, spec_modifiers)
|
||||
return self.f(args, stdin, stdout, stderr, spec, stack, decorators)
|
||||
|
||||
|
||||
PARTIAL_EVAL_ALIASES = (
|
||||
|
@ -538,7 +538,7 @@ def run_alias_by_params(func: tp.Callable, params: dict[str, tp.Any]):
|
|||
"stderr": None,
|
||||
"spec": None,
|
||||
"stack": None,
|
||||
"spec_modifiers": None,
|
||||
"decorators": None,
|
||||
}
|
||||
alias_params |= params
|
||||
sign = inspect.signature(func)
|
||||
|
@ -1064,11 +1064,11 @@ def make_default_aliases():
|
|||
"completer": xca.completer_alias,
|
||||
"xpip": detect_xpip_alias(),
|
||||
"xonsh-reset": xonsh_reset,
|
||||
"xthread": SpecAttrModifierAlias(
|
||||
"@thread": SpecAttrDecoratorAlias(
|
||||
{"threadable": True, "force_threadable": True},
|
||||
"Mark current command as threadable.",
|
||||
),
|
||||
"xunthread": SpecAttrModifierAlias(
|
||||
"@unthread": SpecAttrDecoratorAlias(
|
||||
{"threadable": False, "force_threadable": False},
|
||||
"Mark current command as unthreadable.",
|
||||
),
|
||||
|
|
|
@ -167,8 +167,8 @@ class CommandPipeline:
|
|||
# to stop.
|
||||
pipeline_group = os.getpgid(0)
|
||||
for i, spec in enumerate(specs):
|
||||
for mod in spec.spec_modifiers:
|
||||
mod.on_pre_run(self, spec, i)
|
||||
for mod in spec.decorators:
|
||||
mod.decorate_spec_pre_run(self, spec, i)
|
||||
if self.starttime is None:
|
||||
self.starttime = time.time()
|
||||
try:
|
||||
|
|
|
@ -277,10 +277,10 @@ def no_pg_xonsh_preexec_fn():
|
|||
signal.signal(signal.SIGTSTP, default_signal_pauser)
|
||||
|
||||
|
||||
class SpecModifierAlias:
|
||||
"""Spec modifier base class."""
|
||||
class DecoratorAlias:
|
||||
"""Decorator alias base class."""
|
||||
|
||||
descr = "Spec modifier base class."
|
||||
descr = "DecoratorAlias base."
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
|
@ -294,24 +294,24 @@ class SpecModifierAlias:
|
|||
):
|
||||
print(self.descr, file=stdout)
|
||||
|
||||
def on_modifer_added(self, spec):
|
||||
def decorate_spec(self, spec):
|
||||
"""Modify spec immediately after modifier added."""
|
||||
pass
|
||||
|
||||
def on_pre_run(self, pipeline, spec, spec_num):
|
||||
def decorate_spec_pre_run(self, pipeline, spec, spec_num):
|
||||
"""Modify spec before run."""
|
||||
pass
|
||||
|
||||
|
||||
class SpecAttrModifierAlias(SpecModifierAlias):
|
||||
"""Modifier for spec attributes."""
|
||||
class SpecAttrDecoratorAlias(DecoratorAlias):
|
||||
"""Decorator Alias for spec attributes."""
|
||||
|
||||
def __init__(self, set_attributes: dict, descr=""):
|
||||
self.set_attributes = set_attributes
|
||||
self.descr = descr
|
||||
super().__init__()
|
||||
|
||||
def on_modifer_added(self, spec):
|
||||
def decorate_spec(self, spec):
|
||||
for a, v in self.set_attributes.items():
|
||||
setattr(spec, a, v)
|
||||
|
||||
|
@ -419,7 +419,7 @@ class SubprocSpec:
|
|||
self.captured_stdout = None
|
||||
self.captured_stderr = None
|
||||
self.stack = None
|
||||
self.spec_modifiers = [] # List of SpecModifierAlias objects that applied to spec.
|
||||
self.decorators = [] # List of DecoratorAlias objects that applied to spec.
|
||||
self.output_format = XSH.env.get("XONSH_SUBPROC_OUTPUT_FORMAT", "stream_lines")
|
||||
self.raise_subproc_error = None # Spec-based $RAISE_SUBPROC_ERROR.
|
||||
|
||||
|
@ -642,7 +642,7 @@ class SubprocSpec:
|
|||
# modifications that do not alter cmds may come before creating instance
|
||||
spec = kls(cmd, cls=cls, **kwargs)
|
||||
# modifications that alter cmds must come after creating instance
|
||||
spec.resolve_spec_modifiers() # keep this first
|
||||
spec.resolve_decorators() # keep this first
|
||||
spec.resolve_args_list()
|
||||
spec.resolve_redirects()
|
||||
spec.resolve_alias()
|
||||
|
@ -653,21 +653,19 @@ class SubprocSpec:
|
|||
spec.resolve_stack()
|
||||
return spec
|
||||
|
||||
def add_spec_modifier(self, mod: SpecModifierAlias):
|
||||
def add_decorator(self, mod: DecoratorAlias):
|
||||
"""Add spec modifier to the specification."""
|
||||
mod.on_modifer_added(self)
|
||||
self.spec_modifiers.append(mod)
|
||||
mod.decorate_spec(self)
|
||||
self.decorators.append(mod)
|
||||
|
||||
def resolve_spec_modifiers(self):
|
||||
"""Apply spec modifier."""
|
||||
def resolve_decorators(self):
|
||||
"""Apply decorators."""
|
||||
if (ln := len(self.cmd)) == 1:
|
||||
return
|
||||
for i in range(ln):
|
||||
c = self.cmd[i]
|
||||
if c in XSH.aliases and isinstance(
|
||||
mod := XSH.aliases[c], SpecModifierAlias
|
||||
):
|
||||
self.add_spec_modifier(mod)
|
||||
if c in XSH.aliases and isinstance(mod := XSH.aliases[c], DecoratorAlias):
|
||||
self.add_decorator(mod)
|
||||
else:
|
||||
break
|
||||
self.cmd = self.cmd[i:]
|
||||
|
@ -716,7 +714,7 @@ class SubprocSpec:
|
|||
if callable(cmd0):
|
||||
self.alias = cmd0
|
||||
else:
|
||||
found_spec_modifiers = []
|
||||
decorators = []
|
||||
if isinstance(XSH.aliases, dict):
|
||||
# Windows tests
|
||||
alias = XSH.aliases.get(cmd0, None)
|
||||
|
@ -726,7 +724,7 @@ class SubprocSpec:
|
|||
alias = XSH.aliases.get(
|
||||
self.cmd,
|
||||
None,
|
||||
spec_modifiers=found_spec_modifiers,
|
||||
decorators=decorators,
|
||||
)
|
||||
if alias is not None:
|
||||
self.alias_name = cmd0
|
||||
|
@ -738,9 +736,8 @@ class SubprocSpec:
|
|||
# E.g. `alias == ['ls', '-la']`
|
||||
self.alias = alias
|
||||
|
||||
if found_spec_modifiers:
|
||||
for mod in found_spec_modifiers:
|
||||
self.add_spec_modifier(mod)
|
||||
for mod in decorators:
|
||||
self.add_decorator(mod)
|
||||
|
||||
def resolve_binary_loc(self):
|
||||
"""Sets the binary location"""
|
||||
|
|
Loading…
Add table
Reference in a new issue