Updates
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
from ._completer import ClickCompleter as ClickCompleter # noqa: F401
|
||||
from ._repl import register_repl as register_repl # noqa: F401
|
||||
from ._repl import repl as repl # noqa: F401
|
||||
from .exceptions import CommandLineParserError as CommandLineParserError # noqa: F401
|
||||
from .exceptions import ExitReplException as ExitReplException # noqa: F401
|
||||
from .exceptions import ( # noqa: F401
|
||||
InternalCommandException as InternalCommandException,
|
||||
)
|
||||
from .utils import exit as exit # noqa: F401
|
||||
|
||||
__version__ = "0.3.0"
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,296 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
from glob import iglob
|
||||
|
||||
import click
|
||||
from prompt_toolkit.completion import Completion, Completer
|
||||
|
||||
from .utils import _resolve_context, split_arg_string
|
||||
|
||||
__all__ = ["ClickCompleter"]
|
||||
|
||||
IS_WINDOWS = os.name == "nt"
|
||||
|
||||
|
||||
# Handle backwards compatibility between Click<=7.0 and >=8.0
|
||||
try:
|
||||
import click.shell_completion
|
||||
|
||||
HAS_CLICK_V8 = True
|
||||
AUTO_COMPLETION_PARAM = "shell_complete"
|
||||
except (ImportError, ModuleNotFoundError):
|
||||
import click._bashcomplete # type: ignore[import]
|
||||
|
||||
HAS_CLICK_V8 = False
|
||||
AUTO_COMPLETION_PARAM = "autocompletion"
|
||||
|
||||
|
||||
def text_type(text):
|
||||
return "{}".format(text)
|
||||
|
||||
|
||||
class ClickCompleter(Completer):
|
||||
__slots__ = ("cli", "ctx", "parsed_args", "parsed_ctx", "ctx_command")
|
||||
|
||||
def __init__(self, cli, ctx):
|
||||
self.cli = cli
|
||||
self.ctx = ctx
|
||||
self.parsed_args = []
|
||||
self.parsed_ctx = ctx
|
||||
self.ctx_command = ctx.command
|
||||
|
||||
def _get_completion_from_autocompletion_functions(
|
||||
self,
|
||||
param,
|
||||
autocomplete_ctx,
|
||||
args,
|
||||
incomplete,
|
||||
):
|
||||
param_choices = []
|
||||
|
||||
if HAS_CLICK_V8:
|
||||
autocompletions = param.shell_complete(autocomplete_ctx, incomplete)
|
||||
else:
|
||||
autocompletions = param.autocompletion( # type: ignore[attr-defined]
|
||||
autocomplete_ctx, args, incomplete
|
||||
)
|
||||
|
||||
for autocomplete in autocompletions:
|
||||
if isinstance(autocomplete, tuple):
|
||||
param_choices.append(
|
||||
Completion(
|
||||
text_type(autocomplete[0]),
|
||||
-len(incomplete),
|
||||
display_meta=autocomplete[1],
|
||||
)
|
||||
)
|
||||
|
||||
elif HAS_CLICK_V8 and isinstance(
|
||||
autocomplete, click.shell_completion.CompletionItem
|
||||
):
|
||||
param_choices.append(
|
||||
Completion(text_type(autocomplete.value), -len(incomplete))
|
||||
)
|
||||
|
||||
else:
|
||||
param_choices.append(
|
||||
Completion(text_type(autocomplete), -len(incomplete))
|
||||
)
|
||||
|
||||
return param_choices
|
||||
|
||||
def _get_completion_from_choices_click_le_7(self, param, incomplete):
|
||||
if not getattr(param.type, "case_sensitive", True):
|
||||
incomplete = incomplete.lower()
|
||||
return [
|
||||
Completion(
|
||||
text_type(choice),
|
||||
-len(incomplete),
|
||||
display=text_type(repr(choice) if " " in choice else choice),
|
||||
)
|
||||
for choice in param.type.choices # type: ignore[attr-defined]
|
||||
if choice.lower().startswith(incomplete)
|
||||
]
|
||||
|
||||
else:
|
||||
return [
|
||||
Completion(
|
||||
text_type(choice),
|
||||
-len(incomplete),
|
||||
display=text_type(repr(choice) if " " in choice else choice),
|
||||
)
|
||||
for choice in param.type.choices # type: ignore[attr-defined]
|
||||
if choice.startswith(incomplete)
|
||||
]
|
||||
|
||||
def _get_completion_for_Path_types(self, param, args, incomplete):
|
||||
if "*" in incomplete:
|
||||
return []
|
||||
|
||||
choices = []
|
||||
_incomplete = os.path.expandvars(incomplete)
|
||||
search_pattern = _incomplete.strip("'\"\t\n\r\v ").replace("\\\\", "\\") + "*"
|
||||
quote = ""
|
||||
|
||||
if " " in _incomplete:
|
||||
for i in incomplete:
|
||||
if i in ("'", '"'):
|
||||
quote = i
|
||||
break
|
||||
|
||||
for path in iglob(search_pattern):
|
||||
if " " in path:
|
||||
if quote:
|
||||
path = quote + path
|
||||
else:
|
||||
if IS_WINDOWS:
|
||||
path = repr(path).replace("\\\\", "\\")
|
||||
else:
|
||||
if IS_WINDOWS:
|
||||
path = path.replace("\\", "\\\\")
|
||||
|
||||
choices.append(
|
||||
Completion(
|
||||
text_type(path),
|
||||
-len(incomplete),
|
||||
display=text_type(os.path.basename(path.strip("'\""))),
|
||||
)
|
||||
)
|
||||
|
||||
return choices
|
||||
|
||||
def _get_completion_for_Boolean_type(self, param, incomplete):
|
||||
return [
|
||||
Completion(
|
||||
text_type(k), -len(incomplete), display_meta=text_type("/".join(v))
|
||||
)
|
||||
for k, v in {
|
||||
"true": ("1", "true", "t", "yes", "y", "on"),
|
||||
"false": ("0", "false", "f", "no", "n", "off"),
|
||||
}.items()
|
||||
if any(i.startswith(incomplete) for i in v)
|
||||
]
|
||||
|
||||
def _get_completion_from_params(self, autocomplete_ctx, args, param, incomplete):
|
||||
|
||||
choices = []
|
||||
param_type = param.type
|
||||
|
||||
# shell_complete method for click.Choice is intorduced in click-v8
|
||||
if not HAS_CLICK_V8 and isinstance(param_type, click.Choice):
|
||||
choices.extend(
|
||||
self._get_completion_from_choices_click_le_7(param, incomplete)
|
||||
)
|
||||
|
||||
elif isinstance(param_type, click.types.BoolParamType):
|
||||
choices.extend(self._get_completion_for_Boolean_type(param, incomplete))
|
||||
|
||||
elif isinstance(param_type, (click.Path, click.File)):
|
||||
choices.extend(self._get_completion_for_Path_types(param, args, incomplete))
|
||||
|
||||
elif getattr(param, AUTO_COMPLETION_PARAM, None) is not None:
|
||||
choices.extend(
|
||||
self._get_completion_from_autocompletion_functions(
|
||||
param,
|
||||
autocomplete_ctx,
|
||||
args,
|
||||
incomplete,
|
||||
)
|
||||
)
|
||||
|
||||
return choices
|
||||
|
||||
def _get_completion_for_cmd_args(
|
||||
self,
|
||||
ctx_command,
|
||||
incomplete,
|
||||
autocomplete_ctx,
|
||||
args,
|
||||
):
|
||||
choices = []
|
||||
param_called = False
|
||||
|
||||
for param in ctx_command.params:
|
||||
if isinstance(param.type, click.types.UnprocessedParamType):
|
||||
return []
|
||||
|
||||
elif getattr(param, "hidden", False):
|
||||
continue
|
||||
|
||||
elif isinstance(param, click.Option):
|
||||
for option in param.opts + param.secondary_opts:
|
||||
# We want to make sure if this parameter was called
|
||||
# If we are inside a parameter that was called, we want to show only
|
||||
# relevant choices
|
||||
if option in args[param.nargs * -1 :]: # noqa: E203
|
||||
param_called = True
|
||||
break
|
||||
|
||||
elif option.startswith(incomplete):
|
||||
choices.append(
|
||||
Completion(
|
||||
text_type(option),
|
||||
-len(incomplete),
|
||||
display_meta=text_type(param.help or ""),
|
||||
)
|
||||
)
|
||||
|
||||
if param_called:
|
||||
choices = self._get_completion_from_params(
|
||||
autocomplete_ctx, args, param, incomplete
|
||||
)
|
||||
|
||||
elif isinstance(param, click.Argument):
|
||||
choices.extend(
|
||||
self._get_completion_from_params(
|
||||
autocomplete_ctx, args, param, incomplete
|
||||
)
|
||||
)
|
||||
|
||||
return choices
|
||||
|
||||
def get_completions(self, document, complete_event=None):
|
||||
# Code analogous to click._bashcomplete.do_complete
|
||||
|
||||
args = split_arg_string(document.text_before_cursor, posix=False)
|
||||
|
||||
choices = []
|
||||
cursor_within_command = (
|
||||
document.text_before_cursor.rstrip() == document.text_before_cursor
|
||||
)
|
||||
|
||||
if document.text_before_cursor.startswith(("!", ":")):
|
||||
return
|
||||
|
||||
if args and cursor_within_command:
|
||||
# We've entered some text and no space, give completions for the
|
||||
# current word.
|
||||
incomplete = args.pop()
|
||||
else:
|
||||
# We've not entered anything, either at all or for the current
|
||||
# command, so give all relevant completions for this context.
|
||||
incomplete = ""
|
||||
|
||||
if self.parsed_args != args:
|
||||
self.parsed_args = args
|
||||
self.parsed_ctx = _resolve_context(args, self.ctx)
|
||||
self.ctx_command = self.parsed_ctx.command
|
||||
|
||||
if getattr(self.ctx_command, "hidden", False):
|
||||
return
|
||||
|
||||
try:
|
||||
choices.extend(
|
||||
self._get_completion_for_cmd_args(
|
||||
self.ctx_command, incomplete, self.parsed_ctx, args
|
||||
)
|
||||
)
|
||||
|
||||
if isinstance(self.ctx_command, click.MultiCommand):
|
||||
incomplete_lower = incomplete.lower()
|
||||
|
||||
for name in self.ctx_command.list_commands(self.parsed_ctx):
|
||||
command = self.ctx_command.get_command(self.parsed_ctx, name)
|
||||
if getattr(command, "hidden", False):
|
||||
continue
|
||||
|
||||
elif name.lower().startswith(incomplete_lower):
|
||||
choices.append(
|
||||
Completion(
|
||||
text_type(name),
|
||||
-len(incomplete),
|
||||
display_meta=getattr(command, "short_help", ""),
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
click.echo("{}: {}".format(type(e).__name__, str(e)))
|
||||
|
||||
# If we are inside a parameter that was called, we want to show only
|
||||
# relevant choices
|
||||
# if param_called:
|
||||
# choices = param_choices
|
||||
|
||||
for item in choices:
|
||||
yield item
|
||||
152
ETB-API/venv/lib/python3.12/site-packages/click_repl/_repl.py
Normal file
152
ETB-API/venv/lib/python3.12/site-packages/click_repl/_repl.py
Normal file
@@ -0,0 +1,152 @@
|
||||
from __future__ import with_statement
|
||||
|
||||
import click
|
||||
import sys
|
||||
from prompt_toolkit import PromptSession
|
||||
from prompt_toolkit.history import InMemoryHistory
|
||||
|
||||
from ._completer import ClickCompleter
|
||||
from .exceptions import ClickExit # type: ignore[attr-defined]
|
||||
from .exceptions import CommandLineParserError, ExitReplException, InvalidGroupFormat
|
||||
from .utils import _execute_internal_and_sys_cmds
|
||||
|
||||
|
||||
__all__ = ["bootstrap_prompt", "register_repl", "repl"]
|
||||
|
||||
|
||||
def bootstrap_prompt(
|
||||
group,
|
||||
prompt_kwargs,
|
||||
ctx=None,
|
||||
):
|
||||
"""
|
||||
Bootstrap prompt_toolkit kwargs or use user defined values.
|
||||
|
||||
:param group: click Group
|
||||
:param prompt_kwargs: The user specified prompt kwargs.
|
||||
"""
|
||||
|
||||
defaults = {
|
||||
"history": InMemoryHistory(),
|
||||
"completer": ClickCompleter(group, ctx=ctx),
|
||||
"message": "> ",
|
||||
}
|
||||
|
||||
defaults.update(prompt_kwargs)
|
||||
return defaults
|
||||
|
||||
|
||||
def repl(
|
||||
old_ctx, prompt_kwargs={}, allow_system_commands=True, allow_internal_commands=True
|
||||
):
|
||||
"""
|
||||
Start an interactive shell. All subcommands are available in it.
|
||||
|
||||
:param old_ctx: The current Click context.
|
||||
:param prompt_kwargs: Parameters passed to
|
||||
:py:func:`prompt_toolkit.PromptSession`.
|
||||
|
||||
If stdin is not a TTY, no prompt will be printed, but only commands read
|
||||
from stdin.
|
||||
"""
|
||||
|
||||
group_ctx = old_ctx
|
||||
# Switching to the parent context that has a Group as its command
|
||||
# as a Group acts as a CLI for all of its subcommands
|
||||
if old_ctx.parent is not None and not isinstance(old_ctx.command, click.Group):
|
||||
group_ctx = old_ctx.parent
|
||||
|
||||
group = group_ctx.command
|
||||
|
||||
# An Optional click.Argument in the CLI Group, that has no value
|
||||
# will consume the first word from the REPL input, causing issues in
|
||||
# executing the command
|
||||
# So, if there's an empty Optional Argument
|
||||
for param in group.params:
|
||||
if (
|
||||
isinstance(param, click.Argument)
|
||||
and group_ctx.params[param.name] is None
|
||||
and not param.required
|
||||
):
|
||||
raise InvalidGroupFormat(
|
||||
f"{type(group).__name__} '{group.name}' requires value for "
|
||||
f"an optional argument '{param.name}' in REPL mode"
|
||||
)
|
||||
|
||||
isatty = sys.stdin.isatty()
|
||||
|
||||
# Delete the REPL command from those available, as we don't want to allow
|
||||
# nesting REPLs (note: pass `None` to `pop` as we don't want to error if
|
||||
# REPL command already not present for some reason).
|
||||
repl_command_name = old_ctx.command.name
|
||||
if isinstance(group_ctx.command, click.CommandCollection):
|
||||
available_commands = {
|
||||
cmd_name: cmd_obj
|
||||
for source in group_ctx.command.sources
|
||||
for cmd_name, cmd_obj in source.commands.items()
|
||||
}
|
||||
else:
|
||||
available_commands = group_ctx.command.commands
|
||||
|
||||
original_command = available_commands.pop(repl_command_name, None)
|
||||
|
||||
if isatty:
|
||||
prompt_kwargs = bootstrap_prompt(group, prompt_kwargs, group_ctx)
|
||||
session = PromptSession(**prompt_kwargs)
|
||||
|
||||
def get_command():
|
||||
return session.prompt()
|
||||
|
||||
else:
|
||||
get_command = sys.stdin.readline
|
||||
|
||||
while True:
|
||||
try:
|
||||
command = get_command()
|
||||
except KeyboardInterrupt:
|
||||
continue
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
if not command:
|
||||
if isatty:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
try:
|
||||
args = _execute_internal_and_sys_cmds(
|
||||
command, allow_internal_commands, allow_system_commands
|
||||
)
|
||||
if args is None:
|
||||
continue
|
||||
|
||||
except CommandLineParserError:
|
||||
continue
|
||||
|
||||
except ExitReplException:
|
||||
break
|
||||
|
||||
try:
|
||||
# The group command will dispatch based on args.
|
||||
old_protected_args = group_ctx.protected_args
|
||||
try:
|
||||
group_ctx.protected_args = args
|
||||
group.invoke(group_ctx)
|
||||
finally:
|
||||
group_ctx.protected_args = old_protected_args
|
||||
except click.ClickException as e:
|
||||
e.show()
|
||||
except (ClickExit, SystemExit):
|
||||
pass
|
||||
|
||||
except ExitReplException:
|
||||
break
|
||||
|
||||
if original_command is not None:
|
||||
available_commands[repl_command_name] = original_command
|
||||
|
||||
|
||||
def register_repl(group, name="repl"):
|
||||
"""Register :func:`repl()` as sub-command *name* of *group*."""
|
||||
group.command(name=name)(click.pass_context(repl))
|
||||
@@ -0,0 +1,23 @@
|
||||
class InternalCommandException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ExitReplException(InternalCommandException):
|
||||
pass
|
||||
|
||||
|
||||
class CommandLineParserError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidGroupFormat(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# Handle click.exceptions.Exit introduced in Click 7.0
|
||||
try:
|
||||
from click.exceptions import Exit as ClickExit
|
||||
except (ImportError, ModuleNotFoundError):
|
||||
|
||||
class ClickExit(RuntimeError): # type: ignore[no-redef]
|
||||
pass
|
||||
222
ETB-API/venv/lib/python3.12/site-packages/click_repl/utils.py
Normal file
222
ETB-API/venv/lib/python3.12/site-packages/click_repl/utils.py
Normal file
@@ -0,0 +1,222 @@
|
||||
import click
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
from .exceptions import CommandLineParserError, ExitReplException
|
||||
|
||||
|
||||
__all__ = [
|
||||
"_execute_internal_and_sys_cmds",
|
||||
"_exit_internal",
|
||||
"_get_registered_target",
|
||||
"_help_internal",
|
||||
"_resolve_context",
|
||||
"_register_internal_command",
|
||||
"dispatch_repl_commands",
|
||||
"handle_internal_commands",
|
||||
"split_arg_string",
|
||||
"exit",
|
||||
]
|
||||
|
||||
|
||||
# Abstract datatypes in collections module are moved to collections.abc
|
||||
# module in Python 3.3
|
||||
if sys.version_info >= (3, 3):
|
||||
from collections.abc import Iterable, Mapping # noqa: F811
|
||||
else:
|
||||
from collections import Iterable, Mapping
|
||||
|
||||
|
||||
def _resolve_context(args, ctx=None):
|
||||
"""Produce the context hierarchy starting with the command and
|
||||
traversing the complete arguments. This only follows the commands,
|
||||
it doesn't trigger input prompts or callbacks.
|
||||
|
||||
:param args: List of complete args before the incomplete value.
|
||||
:param cli_ctx: `click.Context` object of the CLI group
|
||||
"""
|
||||
|
||||
while args:
|
||||
command = ctx.command
|
||||
|
||||
if isinstance(command, click.MultiCommand):
|
||||
if not command.chain:
|
||||
name, cmd, args = command.resolve_command(ctx, args)
|
||||
|
||||
if cmd is None:
|
||||
return ctx
|
||||
|
||||
ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True)
|
||||
args = ctx.protected_args + ctx.args
|
||||
else:
|
||||
while args:
|
||||
name, cmd, args = command.resolve_command(ctx, args)
|
||||
|
||||
if cmd is None:
|
||||
return ctx
|
||||
|
||||
sub_ctx = cmd.make_context(
|
||||
name,
|
||||
args,
|
||||
parent=ctx,
|
||||
allow_extra_args=True,
|
||||
allow_interspersed_args=False,
|
||||
resilient_parsing=True,
|
||||
)
|
||||
args = sub_ctx.args
|
||||
|
||||
ctx = sub_ctx
|
||||
args = [*sub_ctx.protected_args, *sub_ctx.args]
|
||||
else:
|
||||
break
|
||||
|
||||
return ctx
|
||||
|
||||
|
||||
_internal_commands = {}
|
||||
|
||||
|
||||
def split_arg_string(string, posix=True):
|
||||
"""Split an argument string as with :func:`shlex.split`, but don't
|
||||
fail if the string is incomplete. Ignores a missing closing quote or
|
||||
incomplete escape sequence and uses the partial token as-is.
|
||||
.. code-block:: python
|
||||
split_arg_string("example 'my file")
|
||||
["example", "my file"]
|
||||
split_arg_string("example my\\")
|
||||
["example", "my"]
|
||||
:param string: String to split.
|
||||
"""
|
||||
|
||||
lex = shlex.shlex(string, posix=posix)
|
||||
lex.whitespace_split = True
|
||||
lex.commenters = ""
|
||||
out = []
|
||||
|
||||
try:
|
||||
for token in lex:
|
||||
out.append(token)
|
||||
except ValueError:
|
||||
# Raised when end-of-string is reached in an invalid state. Use
|
||||
# the partial token as-is. The quote or escape character is in
|
||||
# lex.state, not lex.token.
|
||||
out.append(lex.token)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def _register_internal_command(names, target, description=None):
|
||||
if not hasattr(target, "__call__"):
|
||||
raise ValueError("Internal command must be a callable")
|
||||
|
||||
if isinstance(names, str):
|
||||
names = [names]
|
||||
|
||||
elif isinstance(names, Mapping) or not isinstance(names, Iterable):
|
||||
raise ValueError(
|
||||
'"names" must be a string, or an iterable object, but got "{}"'.format(
|
||||
type(names).__name__
|
||||
)
|
||||
)
|
||||
|
||||
for name in names:
|
||||
_internal_commands[name] = (target, description)
|
||||
|
||||
|
||||
def _get_registered_target(name, default=None):
|
||||
target_info = _internal_commands.get(name)
|
||||
if target_info:
|
||||
return target_info[0]
|
||||
return default
|
||||
|
||||
|
||||
def _exit_internal():
|
||||
raise ExitReplException()
|
||||
|
||||
|
||||
def _help_internal():
|
||||
formatter = click.HelpFormatter()
|
||||
formatter.write_heading("REPL help")
|
||||
formatter.indent()
|
||||
|
||||
with formatter.section("External Commands"):
|
||||
formatter.write_text('prefix external commands with "!"')
|
||||
|
||||
with formatter.section("Internal Commands"):
|
||||
formatter.write_text('prefix internal commands with ":"')
|
||||
info_table = defaultdict(list)
|
||||
|
||||
for mnemonic, target_info in _internal_commands.items():
|
||||
info_table[target_info[1]].append(mnemonic)
|
||||
|
||||
formatter.write_dl( # type: ignore[arg-type]
|
||||
( # type: ignore[arg-type]
|
||||
", ".join(map(":{}".format, sorted(mnemonics))),
|
||||
description,
|
||||
)
|
||||
for description, mnemonics in info_table.items()
|
||||
)
|
||||
|
||||
val = formatter.getvalue() # type: str
|
||||
return val
|
||||
|
||||
|
||||
_register_internal_command(["q", "quit", "exit"], _exit_internal, "exits the repl")
|
||||
_register_internal_command(
|
||||
["?", "h", "help"], _help_internal, "displays general help information"
|
||||
)
|
||||
|
||||
|
||||
def _execute_internal_and_sys_cmds(
|
||||
command,
|
||||
allow_internal_commands=True,
|
||||
allow_system_commands=True,
|
||||
):
|
||||
"""
|
||||
Executes internal, system, and all the other registered click commands from the input
|
||||
"""
|
||||
if allow_system_commands and dispatch_repl_commands(command):
|
||||
return None
|
||||
|
||||
if allow_internal_commands:
|
||||
result = handle_internal_commands(command)
|
||||
if isinstance(result, str):
|
||||
click.echo(result)
|
||||
return None
|
||||
|
||||
try:
|
||||
return split_arg_string(command)
|
||||
except ValueError as e:
|
||||
raise CommandLineParserError("{}".format(e))
|
||||
|
||||
|
||||
def exit():
|
||||
"""Exit the repl"""
|
||||
_exit_internal()
|
||||
|
||||
|
||||
def dispatch_repl_commands(command):
|
||||
"""
|
||||
Execute system commands entered in the repl.
|
||||
|
||||
System commands are all commands starting with "!".
|
||||
"""
|
||||
if command.startswith("!"):
|
||||
os.system(command[1:])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def handle_internal_commands(command):
|
||||
"""
|
||||
Run repl-internal commands.
|
||||
|
||||
Repl-internal commands are all commands starting with ":".
|
||||
"""
|
||||
if command.startswith(":"):
|
||||
target = _get_registered_target(command[1:], default=None)
|
||||
if target:
|
||||
return target()
|
||||
Reference in New Issue
Block a user