ArgparserAlias - xonfig2 (#4436)

* feat: use ArgparserAlias for xonfig

* test: update test for xonfig completions

* refactor: update docs

* refactor: use function based completer

* fix: remove old import

* docs:

* fix: merge issues

* test: move xsh_with_aliases to root conftest.py

* docs:

* feat: set subparser's prog from func-name
This commit is contained in:
Noorhteen Raja NJ 2021-08-28 16:39:59 +05:30 committed by GitHub
parent 31146604fa
commit c4b54e06a1
Failed to generate hash of commit
10 changed files with 180 additions and 161 deletions

23
news/ap-xonfig.rst Normal file
View file

@ -0,0 +1,23 @@
**Added:**
* <news item>
**Changed:**
* ``xonfig`` now has colored help message when ran interactively.
**Deprecated:**
* <news item>
**Removed:**
* <news item>
**Fixed:**
* <news item>
**Security:**
* <news item>

View file

@ -1,15 +1,6 @@
import pytest
@pytest.fixture
def xsh_with_aliases(xession, monkeypatch):
from xonsh.aliases import Aliases, make_default_aliases
xsh = xession
monkeypatch.setattr(xsh, "aliases", Aliases(make_default_aliases()))
return xsh
@pytest.fixture
def mock_completer(monkeypatch, xsh_with_aliases):
xsh = xsh_with_aliases

View file

@ -3,31 +3,30 @@ from xonsh.parsers.completion_context import (
CommandContext,
CompletionContext,
)
from xonsh.completers.xompletions import complete_xonfig, complete_xontrib, xt
from xonsh.completers.xompletions import complete_xontrib
import pytest
def test_xonfig():
assert complete_xonfig(
CompletionContext(
CommandContext(args=(CommandArg("xonfig"),), arg_index=1, prefix="-")
)
) == {"-h"}
@pytest.mark.parametrize(
"args, prefix, exp",
[
(
"xonfig",
"-",
{"-h", "--help"},
),
(
"xonfig colors",
"b",
{"blue", "brown"},
),
],
)
def test_xonfig(args, prefix, exp, xsh_with_aliases, monkeypatch, check_completer):
from xonsh import xonfig
def test_xonfig_colors(monkeypatch):
monkeypatch.setattr(xt, "color_style_names", lambda: ["blue", "brown", "other"])
assert (
complete_xonfig(
CompletionContext(
CommandContext(
args=(CommandArg("xonfig"), CommandArg("colors")),
arg_index=2,
prefix="b",
)
)
)
== {"blue", "brown"}
)
monkeypatch.setattr(xonfig, "color_style_names", lambda: ["blue", "brown", "other"])
assert check_completer(args, prefix=prefix) == exp
def test_xontrib():

View file

@ -141,6 +141,15 @@ def xession(xonsh_builtins) -> XonshSession:
return XSH
@pytest.fixture
def xsh_with_aliases(xession, monkeypatch):
from xonsh.aliases import Aliases, make_default_aliases
xsh = xession
monkeypatch.setattr(xsh, "aliases", Aliases(make_default_aliases()))
return xsh
@pytest.fixture(scope="session")
def completion_context_parse():
return CompletionContextParser().parse

View file

@ -13,7 +13,7 @@ import json
import pytest # noqa F401
from xonsh.tools import ON_WINDOWS
from xonsh.xonfig import XONFIG_MAIN_ACTIONS, xonfig_main
from xonsh.xonfig import xonfig_main
def test_xonfg_help(capsys, xonsh_builtins):
@ -25,8 +25,15 @@ def test_xonfg_help(capsys, xonsh_builtins):
m = pat.match(capout)
assert m[1]
verbs = set(v.strip().lower() for v in m[1].split(","))
exp = set(v.lower() for v in XONFIG_MAIN_ACTIONS)
assert verbs == exp
assert verbs == {
"jupyter-kernel",
"info",
"styles",
"wizard",
"web",
"colors",
"tutorial",
}
@pytest.mark.parametrize(

View file

@ -703,11 +703,12 @@ def xexec(args, stdin=None):
)
def xonfig(args, stdin=None):
@lazyobject
def xonfig():
"""Runs the xonsh configuration utility."""
from xonsh.xonfig import xonfig_main # lazy import
return xonfig_main(args)
return xonfig_main
@unthreadable

View file

@ -297,7 +297,10 @@ class ArgParser(ap.ArgumentParser):
doc = get_doc(func)
kwargs.setdefault("description", doc)
kwargs.setdefault("help", doc)
parser = self.commands.add_parser(kwargs.pop("prog", func.__name__), **kwargs)
name = kwargs.pop("prog", None)
if not name:
name = func.__name__.lstrip("_").replace("_", "-")
parser = self.commands.add_parser(name, **kwargs)
add_args(parser, func, allowed_params=args)
return parser

View file

@ -16,7 +16,7 @@ from xonsh.completers.commands import (
complete_end_proc_tokens,
complete_end_proc_keywords,
)
from xonsh.completers.xompletions import complete_xonfig, complete_xontrib
from xonsh.completers.xompletions import complete_xontrib
from xonsh.completers._aliases import complete_argparser_aliases
from xonsh.completers.environment import complete_environment_vars
@ -36,7 +36,6 @@ def default_completers():
("pip", complete_pip),
("cd", complete_cd),
("rmdir", complete_rmdir),
("xonfig", complete_xonfig),
("xontrib", complete_xontrib),
("import", complete_import),
("bash", complete_from_bash),

View file

@ -1,24 +1,9 @@
"""Provides completions for xonsh internal utilities"""
from xonsh.parsers.completion_context import CommandContext
import xonsh.xontribs as xx
import xonsh.xontribs_meta as xmt
import xonsh.tools as xt
from xonsh.xonfig import XONFIG_MAIN_ACTIONS
from xonsh.completers.tools import contextual_command_completer_for
@contextual_command_completer_for("xonfig")
def complete_xonfig(command: CommandContext):
"""Completion for ``xonfig``."""
curix = command.arg_index
if curix == 1:
possible = set(XONFIG_MAIN_ACTIONS.keys()) | {"-h"}
elif curix == 2 and command.args[1].value == "colors":
possible = set(xt.color_style_names())
else:
raise StopIteration
return {i for i in possible if i.startswith(command.prefix)}
from xonsh.parsers.completion_context import CommandContext
def _list_installed_xontribs():

View file

@ -9,8 +9,6 @@ import random
import pprint
import tempfile
import textwrap
import argparse
import functools
import itertools
import contextlib
import collections
@ -21,6 +19,7 @@ from xonsh.ply import ply
import xonsh.wizard as wiz
from xonsh import __version__ as XONSH_VERSION
from xonsh.built_ins import XSH
from xonsh.cli_utils import ArgParserAlias, Annotated, Arg, add_args
from xonsh.prompt.base import is_template_string
from xonsh.platform import (
is_readline_available,
@ -445,15 +444,25 @@ def make_xonfig_wizard(default_file=None, confirm=False, no_wizard_file=None):
return w
def _wizard(ns):
def _wizard(
rcfile: Annotated[str, Arg("--file")] = None,
confirm: Annotated[bool, Arg("--confirm", action="store_true")] = False,
):
"""Launch configurator in terminal
Parameters
-------
rcfile
config file location, default=$XONSHRC
confirm
confirm that the wizard should be run.
"""
env = XSH.env
shell = XSH.shell.shell
xonshrcs = env.get("XONSHRC", [])
fname = xonshrcs[-1] if xonshrcs and ns.file is None else ns.file
fname = xonshrcs[-1] if xonshrcs and rcfile is None else rcfile
no_wiz = os.path.join(env.get("XONSH_CONFIG_DIR"), "no-wizard")
w = make_xonfig_wizard(
default_file=fname, confirm=ns.confirm, no_wizard_file=no_wiz
)
w = make_xonfig_wizard(default_file=fname, confirm=confirm, no_wizard_file=no_wiz)
tempenv = {"PROMPT": "", "XONSH_STORE_STDOUT": False}
pv = wiz.PromptVisitor(w, store_in_history=False, multiline=False)
@ -504,9 +513,18 @@ def _xonfig_format_json(data):
return s
def _info(ns):
def _info(
to_json: Annotated[bool, Arg("--json", action="store_true")] = False,
) -> str:
"""Displays configuration information
Parameters
----------
to_json
reports results as json
"""
env = XSH.env
data = [("xonsh", XONSH_VERSION)]
data: tp.List[tp.Any] = [("xonsh", XONSH_VERSION)]
hash_, date_ = githash()
if hash_:
data.append(("Git SHA", hash_))
@ -526,7 +544,7 @@ def _info(ns):
)
if ON_LINUX:
data.append(("distro", linux_distro()))
data.append(("on wsl", bool(ON_WSL))),
data.append(("on wsl", bool(ON_WSL)))
if ON_WSL:
data.append(("wsl version", 1 if ON_WSL1 else 2))
data.extend(
@ -554,16 +572,25 @@ def _info(ns):
data.extend([("xontrib", xontribs_loaded())])
data.extend([("RC file", XSH.rc_files)])
formatter = _xonfig_format_json if ns.json else _xonfig_format_human
formatter = _xonfig_format_json if to_json else _xonfig_format_human
s = formatter(data)
return s
def _styles(ns):
def _styles(
to_json: Annotated[bool, Arg("--json", action="store_true")] = False, _stdout=None
):
"""Prints available xonsh color styles
Parameters
----------
to_json
reports results as json
"""
env = XSH.env
curr = env.get("XONSH_COLOR_STYLE")
styles = sorted(color_style_names())
if ns.json:
if to_json:
s = json.dumps(styles, sort_keys=True, indent=1)
print(s)
return
@ -574,7 +601,7 @@ def _styles(ns):
else:
lines.append(" " + style)
s = "\n".join(lines)
print_color(s)
print_color(s, file=_stdout)
def _str_colors(cmap, cols):
@ -621,16 +648,29 @@ def _tok_colors(cmap, cols):
return toks
def _colors(args):
def xonfig_color_completer(*_, **__):
yield from color_style_names()
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(ON_WINDOWS)
columns -= int(bool(ON_WINDOWS))
style_stash = XSH.env["XONSH_COLOR_STYLE"]
if args.style is not None:
if args.style not in color_style_names():
print("Invalid style: {}".format(args.style))
if style is not None:
if style not in color_style_names():
print("Invalid style: {}".format(style))
return
XSH.env["XONSH_COLOR_STYLE"] = args.style
XSH.env["XONSH_COLOR_STYLE"] = style
color_map = color_style()
akey = next(iter(color_map))
@ -642,20 +682,46 @@ def _colors(args):
XSH.env["XONSH_COLOR_STYLE"] = style_stash
def _tutorial(args):
def _tutorial():
"""Launch tutorial in browser."""
import webbrowser
webbrowser.open("http://xon.sh/tutorial.html")
def _web(args):
def _web(
_args,
browser: Annotated[bool, Arg("--no-browser", action="store_false")] = True,
):
"""Launch configurator in browser.
Parameters
----------
browser
don't open browser
"""
import subprocess
subprocess.run([sys.executable, "-m", "xonsh.webconfig"] + args.orig_args[1:])
subprocess.run([sys.executable, "-m", "xonsh.webconfig"] + _args[1:])
def _jupyter_kernel(args):
"""Make xonsh available as a Jupyter kernel."""
def _jupyter_kernel(
user: Annotated[bool, Arg("--user", action="store_true")] = False,
prefix: Annotated[str, Arg("--prefix")] = None,
root: Annotated[str, Arg("--root")] = None,
):
"""Generate xonsh kernel for jupyter.
Parameters
----------
user
Install kernel spec in user config directory.
prefix
Installation prefix for bin, lib, etc.
root
Install relative to this alternate root directory.
"""
try:
from jupyter_client.kernelspec import KernelSpecManager, NoSuchKernel
except ImportError as e:
@ -663,9 +729,7 @@ def _jupyter_kernel(args):
ksm = KernelSpecManager()
root = args.root
prefix = args.prefix if args.prefix else sys.prefix
user = args.user
prefix = prefix or sys.prefix
spec = {
"argv": [
sys.executable,
@ -719,87 +783,25 @@ def _jupyter_kernel(args):
return 0
@functools.lru_cache(1)
def _xonfig_create_parser():
p = argparse.ArgumentParser(
prog="xonfig", description="Manages xonsh configuration."
)
subp = p.add_subparsers(title="action", dest="action")
info = subp.add_parser(
"info", help=("displays configuration information, " "default action")
)
info.add_argument(
"--json", action="store_true", default=False, help="reports results as json"
)
web = subp.add_parser("web", help="Launch configurator in browser.")
web.add_argument(
"--no-browser",
action="store_false",
dest="browser",
default=True,
help="don't open browser",
)
wiz = subp.add_parser("wizard", help="Launch configurator in terminal")
wiz.add_argument(
"--file", default=None, help="config file location, default=$XONSHRC"
)
wiz.add_argument(
"--confirm",
action="store_true",
default=False,
help="confirm that the wizard should be run.",
)
sty = subp.add_parser("styles", help="prints available xonsh color styles")
sty.add_argument(
"--json", action="store_true", default=False, help="reports results as json"
)
colors = subp.add_parser("colors", help="preview color style")
colors.add_argument(
"style", nargs="?", default=None, help="style to preview, default: <current>"
)
subp.add_parser("tutorial", help="Launch tutorial in browser.")
kern = subp.add_parser("jupyter-kernel", help="Generate xonsh kernel for jupyter.")
kern.add_argument(
"--user",
action="store_true",
default=False,
help="Install kernel spec in user config directory.",
)
kern.add_argument(
"--root",
default=None,
help="Install relative to this alternate root directory.",
)
kern.add_argument(
"--prefix", default=None, help="Installation prefix for bin, lib, etc."
)
class XonfigAlias(ArgParserAlias):
"""Manage xonsh configuration."""
return p
def build(self):
parser = self.create_parser(prog="xonfig")
# register as default action
add_args(parser, _info, allowed_params=())
parser.add_command(_info)
parser.add_command(_web)
parser.add_command(_wizard)
parser.add_command(_styles)
parser.add_command(_colors)
parser.add_command(_tutorial)
parser.add_command(_jupyter_kernel)
return parser
XONFIG_MAIN_ACTIONS = {
"info": _info,
"web": _web,
"wizard": _wizard,
"styles": _styles,
"colors": _colors,
"tutorial": _tutorial,
"jupyter-kernel": _jupyter_kernel,
}
def xonfig_main(args=None):
"""Main xonfig entry point."""
if not args or (
args[0] not in XONFIG_MAIN_ACTIONS and args[0] not in {"-h", "--help"}
):
args.insert(0, "info")
parser = _xonfig_create_parser()
ns = parser.parse_args(args)
ns.orig_args = args
if ns.action is None: # apply default action
ns = parser.parse_args(["info"] + args)
return XONFIG_MAIN_ACTIONS[ns.action](ns)
xonfig_main = XonfigAlias()
@lazyobject