click-contrib / click-repl Goto Github PK
View Code? Open in Web Editor NEWSubcommand REPL for click apps
License: MIT License
Subcommand REPL for click apps
License: MIT License
I'm writing a repl that looked like this:
import pathlib
from dataclasses import dataclass
import click
from click_repl import repl as click_repl
@dataclass
class MyDB:
db_dir: pathlib.Path
def get_entry(entry_id: int) -> str:
# Stub for code that retrieves entry from the DB and formats it.
return f"ENTRY {entry_id}"
@click.group(invoke_without_command=True)
@click.argument('db', type=click.Path(exists=False, file_okay=False, writable=True, readable=True, path_type=pathlib.Path))
@click.pass_context
def shell(ctx: click.Context, db: pathlib.Path) -> None:
"""Opens a shell to run commands on DB."""
ctx.obj = MyDB(db)
if ctx.invoked_subcommand is None:
ctx.invoke(repl)
@shell.command()
@click.pass_context
def repl(ctx):
click_repl(ctx)
@shell.command()
@click.argument('entry_id', type=int)
@click.pass_obj
def entry(mydb: MyDB, entry_id: int) -> None:
"""Display ENTRY_ID from the DB."""
try:
click.echo(mydb.get_entry(entry_id))
except NotFound as e:
click.secho(e, fg="red")
That code didn't work as expected. I was expecting something like this:
$ mycmd db
> entry 1
ENTRY 1
Instead, I got an exception about no command 1. Digging through the code, I discovered that "entry" is parsed as the argument to the group command, and not as the subcommand.
I also discovered that shell() will also be invoked in each subcommands invocation. My hunch is that the way click_repl dispatches to the parser isn't correct:
with group.make_context(None, args, parent=group_ctx) as ctx:
group.invoke(ctx)
ctx.exit()
But I'm not familiar enough with the click API to know how we should be dispatching the subcommands. From the documentation, it seems that the proper way to delegate to a subcommand is through ctx.invoke()
. But that takes a callable/command which we don't know at this point.
I was able to work around the issue by rewriting my code like this:
@click.group(invoke_without_command=True)
@click.option('--db', type=click.Path(exists=False, file_okay=False, writable=True, readable=True, path_type=pathlib.Path), help="the DB where entries are cached")
@click.pass_context
def shell(ctx: click.Context, db: pathlib.Path) -> None:
# Only initializes ctx.obj on the first invocation. We are being called on every subcommands through click_repl
if ctx.obj is None:
if db is None:
db = click.prompt("Enter the DB")
ctx.obj = MyDB(db)
if ctx.invoked_subcommand is None:
ctx.invoke(repl)
Thanks,
Is there any interest in creating a new release and making it available on pypi? Some features and bug fixes in master (history, etc) would be nice to utilize.
Some open pull requests look handy as well, e.g. (#33) ๐
Hi
thanks for this great library.
I'm wondering that does completion for type=click.Choice
for options available?
this is my code
import click
from click_repl import repl
@click.group(invoke_without_command=True,)
@click.pass_context
def main(ctx):
if not ctx.invoked_subcommand:
repl(click.get_current_context())
@main.group()
def user():
pass
@user.command()
@click.option('-type', type=click.Choice(['1', '2']))
def search(name):
pass
if __name__ == '__main__':
main()
I won't get completion for -type
If I have multiple possible flags for the click command, it will give me the autocomplete options for the first flag entered after the command name, but every next flag will not have any kind of autocompletion,
Click version 8.0 is no in early alpha state and the code for autocompletion has been committed, essentially rewriting the autocomplete functionality.
As part of this work the _bashcomplete function in click.core which is used by click-repl has been completely removed from click which causes a click application that uses click_repl and is testing with the alpha click 8 to fail in startup.
click_repl/init.py", line 6, in
import click._bashcomplete
ModuleNotFoundError: No module named 'click._bashcomplete'
This means to be compatible with click 8.0 click_repl should add code to use the new autocomplete mechanism when use with clic >= 8.0. See the following for more information.
I understand that click-repl is still new and unstable, but I think it's okay to put it on PyPI and mark it as alpha. It'll make it easier for me to reference to specific versions of it as dependencies.
Click 8.0.0 removed an internal name which click-repl depends upon:
E.g. on Python 3.8 on macOS:
$ python -c "import click; import click_repl"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/Users/maiera/virtualenvs/pywbemtools38/lib/python3.8/site-packages/click_repl/__init__.py", line 6, in <module>
import click._bashcomplete
ModuleNotFoundError: No module named 'click._bashcomplete'
File "/usr/local/lib/python2.7/dist-packages/click_repl/__init__.py", line 169, in repl
available_commands = group_ctx.command.commands
AttributeError: 'CommandCollection' object has no attribute 'commands'
This is implemented as list_commands
in CommandCollection
: https://github.com/pallets/click/blob/master/click/core.py#L1225
list_commands
is also implemented in Group
: https://github.com/pallets/click/blob/master/click/core.py#L1197
This provides a consistent interface to both types.
My motivation for using click-repl is to make my script more discovereable for users. So it'll be nice if, when they're starting to type a command, the options for that command would show on the screen so they could choose from them, even without typing -
.
As we all want arguements and options to be auto completed even if they are not the flag itself - new click versions have added a new flag to option called autocompletion which receives a function that returns a list of possible completions.
This can be seen here:
https://github.com/pallets/click/blob/master/docs/bashcomplete.rst
It can be nice to add a support for this feature as it'll allow us to have a non trivial auto completion quite quickly
Latest release was in January and I believe was generated just before a branch was merged that supports autocompletion of argument choices (#33). I verified this with my application using v0.1.3 vs top of tree master.
It would be great to get a new official release out on pypi that includes this feature. Once 0.1.4 (or whatever) is released, I would port my app from 0.1.2 and release to my set of users.
Thanks for all the effort!
E.g. show a datepicker, useful for todoman.
Travis appears to be cutting off the running of tests on all of the free projects now so it would appear that it is time to move the tests on this project to somewhere else like github actions.
Currently the tests are missing from pypi srcdist.
It would be great, if they could be included! Thanks :)
it looks like tab will show a list for commands, but this doesn't seem to work for the subcommands
I went through the README and the code but did not find a way to prevent click_repl from repeatedly asking for the main cli options.
Desired behaviour
$ mycli --user=hello c1
Executed C1!
$ mycli --user=hello
> c1
Executed C1!
With this code:
from click import group, command, option, pass_context
from click_repl import repl
@group(invoke_without_command=True)
@option('--user', prompt=True)
@pass_context
def cli(ctx, user):
if ctx.invoked_subcommand is None:
repl(ctx)
ctx.exit()
@cli.command()
def c1():
print('Executed C1!')
cli()
I get:
$ mycli --user=hello c1
Executed C1!
$ mycli --user=hello
> c1
User: <waiting for prompt>
Thanks in advance for the help
I have cloned from github and done python setup.py install in a virtualenv en macOSX and I have a "click_repl-0.1.0-py3.5.egg" instead click_repl/ in my "site-packages". Is that the intended behaviour?
If I'm aware in click that I'm in a click_repl mode (if I have any indication that I left for myself), I want to sometimes be able to exit the click_repl from my code.
The way I think this solution can be acheived is by being able to let the ExitReplException in the the context try catch also break the loop.
Things I would like to recommend to this repo
0.2.1dev0
or something (or make a pypi branch)ci
branch in this repoprobably due to this line.
click-shell
by contrast prints the error (no traceback) and returns to the prompt.
When in the interactive mode using click_repl and a command is entered that displays possible completions using python 2.7, and prompt-toolkit > version 2.0 an exception is generated from prompt-toolkit. The exception occurs when the first space between comand and subcommands is entered.
This is not an issue with python 3.
Thus in the project pywbemcli entering a command in interactive mdoe like:
class enumerate --no
Fails with the exception on the space after class or after enumerate.
This does not happen on a corresponding command line command when click auto-completion is enabled.
This issue does not occur with prompt-toolkit 1.0 but the failure started when we changed the requirements to use prompt-toolkit >= 2..0
The following comment shows the exception.
I launch the repl and press enter without typing anything, and I have to wait about 0.5 seconds before the next prompt appears. I tried this several times and I get the delay every time. I guess some kind of heavy thinking is performed? Maybe put in a check that if the string is empty, just show another prompt without thinking too much?
How are users supposed to figure out that they should type :help
rather than help
? Go to the click-repl GitHub page and read about it? I'm preparing a script for people who don't care about my script, don't care about click and definitely don't care about click-repl. I want to make the most obvious thing work for them.
Sorry for asking so directly, but the last release was in 10/2018, and since then none of the PRs that had been provided have been answered to or merged.
would be nice to do auto complete of filenames, like ipython. if we don't want to do this in general context, maybe we can do this when specifying the value of the click file type option?
I tried the example in windows10 / python3.7 and the repl exits after the first command unlike the example where you press ctrl+c to exit. Wondering if this is expected due to platform differences, and/or maybe its a quick fix using the advanced method.
C:\Users\.....> my-app.py repl
> hello
Hello!
C:\Users\.....>
Yes, I know it's going to be hard. But it'll probably be less work than manually testing it yourself every time you make a change.
click_repl
works with click==6.7
but upgrading to click==7.0
breaks it. Sample code:
from __future__ import print_function
import click
import click_repl
@click.group()
def cli():
print("in cli")
@cli.command()
def test():
print("in test")
@cli.command()
def repl():
click_repl.repl(click.get_current_context())
if __name__ == '__main__':
cli()
Running with click v6.7, the repl works. With click 7.0, the repl exits after the first command.
Code:
@help.command()
async def ping(ctx):
em = discord.Embed(
title="Ping",
description="This command shows your latency",
color=0x161716
)
await ctx.send(embed=em)
It shows this error:
File "D:\Kunnu\Discord.py\bot.py", line 39, in
@help.command()
AttributeError: 'Command' object has no attribute 'command'
prompt-toolkit doesn't like it when 'message'
and 'get_prompt_tokens'
are both set:
repl(click.get_current_context(), prompt_kwargs=prompt_kwargs)
[...]
File "/tmp/build/foo/lib/python3.6/site-packages/prompt_toolkit/shortcuts.py", line 233, in create_prompt_layout
assert not (message and get_prompt_tokens)
AssertionError
so this line prevents setting a dynamic prompt generator in prompt_kwargs
.
When upgrading to click==7.0
commands help are not displayed anymore. Sample code:
from __future__ import print_function
import click
import click_repl
@click.group()
def cli():
pass
@cli.command()
def test1():
"""test 1 help"""
print("in test 1")
@cli.command()
def test2():
"""test 2 help"""
print("in test 2")
@cli.command()
def repl():
click_repl.repl(click.get_current_context())
if __name__ == '__main__':
cli()
Is there a command to view the current history?
It'll be nice if the shell was using different colors so it'll be easier on the eyes. (e.g. one color for the prompt, one color for commands.)
Just a heads up: prompt_toolkit
just released a 2.0 version. Not sure if it'll break anything else, but it broke history for us. It's possible this actually represents a bug in prompt_toolkit code rather than click-repl, but I haven't dug into it. The docs still imply that history
should be a keyword argument.
We pinned our version to 1.x for now.
Here's the stack:
File "/usr/local/lib/python2.7/site-packages/click_repl/__init__.py", line 192, in repl
command = get_command()
File "/usr/local/lib/python2.7/site-packages/click_repl/__init__.py", line 186, in get_command
return prompt(**prompt_kwargs)
File "/usr/local/lib/python2.7/site-packages/prompt_toolkit/shortcuts/prompt.py", line 793, in prompt
return session.prompt(*a, **kw)
TypeError: prompt() got an unexpected keyword argument 'history'
steps: make testrepl
bar
Bar!
<<< Up arrow does not display the history>>
Hi
when a command executes some operations in multithread/multiprocess fashion and user hit Ctrl+C
he/she gets this error
and cli just freeze and user can't type anything
WARNING: your terminal doesn't support cursor position requests (CPR)
I'm doing a (simple) restricted shell around this (more for convenience than security's sake), and the prompt character doesn't show up unless I do ssh -t
, which makes sense - the library doesn't know it's a valid TTY.
Can we nevertheless get a way to force a prompt character to show up?
Hello,
when running the readme example, it fails with :
bla@cpu:/mnt/data/code/python/quick_tests$ python3 test.py repl
Traceback (most recent call last):
File "test.py", line 15, in
cli()
File "/home/bla/.local/lib/python3.4/site-packages/click/core.py", line 700, in __call__
return self.main(_args, *_kwargs)
File "/home/bla/.local/lib/python3.4/site-packages/click/core.py", line 680, in main
rv = self.invoke(ctx)
File "/home/bla/.local/lib/python3.4/site-packages/click/core.py", line 1027, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/home/bla/.local/lib/python3.4/site-packages/click/core.py", line 873, in invoke
return ctx.invoke(self.callback, *_ctx.params)
File "/home/bla/.local/lib/python3.4/site-packages/click/core.py", line 508, in invoke
return callback(_args, *_kwargs)
File "/home/bla/.local/lib/python3.4/site-packages/click/decorators.py", line 16, in new_func
return f(get_current_context(), *args, *_kwargs)
File "/home/bla/.local/lib/python3.4/site-packages/click_repl/init.py", line 66, in cli
history = History()
TypeError: Can't instantiate abstract class History with abstract methods __getitem__
, __iter__
, __len__
, append
I'm using python 3.4.3. I guess my problem might be similar to this one.
I understand click-repl is experimental. Do you plan to develop it further?
Thx
I'm not sure how to further debug, but if I run the README example the app just exits with an RC of 0.
> python hello.py repl
> pip list | ack click
click 6.7
click-repl 0.1.4
> python --version
Python 3.7.0
>
In my setup, it kind of makes sense to have one repl for one group with multiple commands.
In that usecase, I would like the group context (ie. options passed to the group, for all commands to use) to be kept in memory in the repl between commands... and also accessing it via a 'context' command.
Any advice on the proper way to do this without breaking click context handling ?
I am currently trying various things, as I couldn't find any documentation about that use case ( which seems quite intuitive, to me at least )
commands&options which has hidden should not be shown in the completion list.
Hi!
I package this for Arch Linux's [community] repository. As this is MIT licensed, I need to package the LICENSE file, but it's not in the pypi release tarball.
Can you please add it for the next release?
It seems like tab completion does not work when one of the options has a help field?
import click
from click_repl import register_repl
@click.group()
def cli():
pass
@cli.command()
@click.option('--test', default=1, show_default=True, help='Test help')
def hello(test):
click.echo("Hello world!")
register_repl(cli)
cli()
Option test does not show up in tab completion, but when the help='Test help'
is removed it will show up. Is this a bug?
I'm using the tool and it's great! I would like some option to change the prompt message while the repl is already running.
I would consider implementing this by loading from prompt_kwargs every while True loop, allowing the user of click_repl to edit the prompt_kwargs he passed to partial between commands and with it change running objects, if needed.
In a function called from a setup tools entry point:
@click.command()
def main():
repl(click.get_current_context(), prompt_kwargs={'history': FileHistory(HISTFILE)})
I get this error:
File "/Users/lol/Library/Python/2.7/lib/python/site-packages/click_repl/__init__.py", line 169, in repl
available_commands = group_ctx.command.commands
AttributeError: 'Command' object has no attribute 'commands'
If I do this:
@click.command()
@click.pass_context
def main(ctx):
ctx.command.commands = []
repl(ctx, prompt_kwargs={'history': FileHistory(HISTFILE)})
I get a different error, because pop
is called incorrectly.
Commenting out lines 171-180 fixes the error. available_commands
isn't used anywhere else, so this seems to be dead code?
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
available_commands.pop(repl_command_name, None)
All the commands I use in the shell should be saved somewhere (a dotfile in my user directory?) and available as command history on the next time I use click-repl
. At least using the up arrow, ctrl-R would be better.
Hey, nice job with this utility :) Have you been considering processing args by os.path.expandvars, so that environment variables can be expanded?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.