GithubHelp home page GithubHelp logo

pallets / click Goto Github PK

View Code? Open in Web Editor NEW
15.0K 183.0 1.4K 3.1 MB

Python composable command line interface toolkit

Home Page: https://click.palletsprojects.com

License: BSD 3-Clause "New" or "Revised" License

Python 100.00%
python cli click pallets

click's Introduction

$ click_

Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It's the "Command Line Interface Creation Kit". It's highly configurable but comes with sensible defaults out of the box.

It aims to make the process of writing command line tools quick and fun while also preventing any frustration caused by the inability to implement an intended CLI API.

Click in three points:

  • Arbitrary nesting of commands
  • Automatic help page generation
  • Supports lazy loading of subcommands at runtime

Installing

Install and update using pip:

$ pip install -U click

A Simple Example

$ python hello.py --count=3
Your name: Click
Hello, Click!
Hello, Click!
Hello, Click!

Donate

The Pallets organization develops and supports Click and other popular packages. In order to grow the community of contributors and users, and allow the maintainers to devote more time to the projects, please donate today.

click's People

Contributors

altendky avatar amy-lei avatar blueyed avatar cataman avatar davidism avatar dependabot-preview[bot] avatar dependabot[bot] avatar epequeno avatar ikeviny avatar jab avatar janluke avatar jcrotts avatar jdufresne avatar josiahdub avatar julen avatar kporangehat avatar mitsuhiko avatar mjpieters avatar pre-commit-ci[bot] avatar rsiemens avatar saif807380 avatar segevfiner avatar sirosen avatar sjagoe avatar slafs avatar smurfix avatar stopthatcow avatar untitaker avatar yourun-proger avatar zacbir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

click's Issues

inout example fails with "binary mode doesn't take an errors argument"

When running the inout example using Python 3 and specifying files for the arguments (not -) you get the error:

Traceback (most recent call last):
  File "C:\Work\Scratch\clicktest\Scripts\inout-script.py", line 9, in <module>
    load_entry_point('inout==0.1', 'console_scripts', 'inout')()
  File "c:\work\scratch\clicktest\src\click\click.py", line 612, in __call__
    return self.main(*args, **kwargs)
  File "c:\work\scratch\clicktest\src\click\click.py", line 595, in main
    with self.make_context(prog_name, args, **extra) as ctx:
  File "c:\work\scratch\clicktest\src\click\click.py", line 553, in make_context
    value, args = param.handle_parse_result(ctx, opts, args)
  File "c:\work\scratch\clicktest\src\click\click.py", line 1079, in handle_parse_result
    value = self.full_process_value(ctx, value)
  File "c:\work\scratch\clicktest\src\click\click.py", line 1047, in full_process_value
    value = self.process_value(ctx, value)
  File "c:\work\scratch\clicktest\src\click\click.py", line 1044, in process_value
    return _convert(value, (self.nargs != 1) + bool(self.multiple))
  File "c:\work\scratch\clicktest\src\click\click.py", line 1042, in _convert
    return self.type(value, self, ctx)
  File "c:\work\scratch\clicktest\src\click\click.py", line 763, in __call__
    return self.convert(value, param, ctx)
  File "c:\work\scratch\clicktest\src\click\click.py", line 907, in convert
    self.errors)
  File "c:\work\scratch\clicktest\src\click\click.py", line 55, in open_stream
    return open(filename, mode, encoding=encoding, errors=errors), True
ValueError: binary mode doesn't take an errors argument

The Python 3 version of open_stream for filename != '-' probably needs a similar if encoding is not None conditional as the Python 2 version.

Command aliases

Multiple names for the same command are useful in some settings (e.g., hg commit has the alias hg ci). In click, this would look something like:

@click.command(aliases=['ci'])
def commit():
    ...

I'm not sure whether the default help output should show all aliases or just the primary name. (The argparse implementation does.)

If this seems desirable, I can put together a pull request.

click.edit() expects editor to be executable with no args

$ EDITOR='nano -L'
$ export EDITOR
>>> import click
>>> click.edit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "click/termui.py", line 409, in edit
    return editor.edit(text)
  File "click/_termui_impl.py", line 371, in edit
    self.edit_file(name)
  File "click/_termui_impl.py", line 348, in edit_file
    raise ClickException('%s: Editing failed: %s' % (editor, e))
click.exceptions.ClickException: nano -L: Editing failed: [Errno 2] No such file or directory

Attribute error: Simple Example

I am trying to run the examples that show usage of the click.group decorator. After creating a group:

@click.group
@click.version_option()
def cli():
    """
    Dsc-Cli is
    """

In attempt to use the group cli in a command:

@cli.command
def provision():
    """
    Manages provisioning of users and data
    """

I get an error: AttributeError: 'function' object has no attribute 'group'

I am running on windows with python 2.7.3 ?

Improve decorator usability

Currently each of the bundled decorators has one of three signature styles:

  1. no arguments
  2. all arguments optional
  3. one or more mandatory arguments

IMO it adds unnecessary mental load having to remember which is which.
I suggest allowing all decorators of type 1 and 2 to be called with and without arguments.

Optionally decorators of type 3 could raise an appropriate exception if called without arguments.

Python 3 UnicodeEncodeError when passing unicode options

In Python3.3, when I pass a unicode argument, click gives me an uncorrectly encoded str. I couldn't find where is the argument encoded, I also don't know which encoding is this one it is using, but it is not UTF-8, because when I try to print the argument it gives me an Error:

python main.py novo --produto="Sabão em pó Omo 1kg"
<class 'str'> 'Sab\udcc3\udca3o em p\udcc3\udcb3 Omo 1kg'
command:  '--produto="Sab\udcc3\udca3o em p\udcc3\udcb3 Omo 1kg"
Traceback (most recent call last):
  File "main.py", line 188, in <module>
    main()
  File "/home/fiatjaf/comp/inflacao/click/click.py", line 612, in __call__
    return self.main(*args, **kwargs)
  File "/home/fiatjaf/comp/inflacao/click/click.py", line 596, in main
    self.invoke(ctx)
  File "/home/fiatjaf/comp/inflacao/click/click.py", line 689, in invoke
    return cmd.invoke(cmd_ctx)
  File "/home/fiatjaf/comp/inflacao/click/click.py", line 568, in invoke
    ctx.invoke(self.callback, **ctx.params)
  File "/home/fiatjaf/comp/inflacao/click/click.py", line 377, in invoke
    return callback(*args, **kwargs)
  File "main.py", line 136, in novo
    ), abort=True)
  File "/home/fiatjaf/comp/inflacao/click/click.py", line 1541, in confirm
    value = raw_input(prompt).lower().strip()
UnicodeEncodeError: 'utf-8' codec can't encode character '\udcc3' in position 24: surrogates not allowed

My system locale is set to UTF-8.
The lines printed at the start of the example printed before the error are from inside the program for debugging purposes (the first was made with type() and repr()).

Command Completion

A generic Command completion module is required to make Click more user friendly.

For example

$ ClickDemo 
(ClickDemo) G                  # on pressing tab
(ClickDemo) Greeting

Case-sensitve options

I check this commit: f353cf1. I am no sure why we should use lowercase for all options.
Can we keep case-sensitive as a feature?

Help doesn't show up if help is a longer string?

Hey Armin, great work on this library 😄

It seems that longer help strings don't show up at all:

e.g.

#!/usr/bin/env python
import click


@click.group()
def cli():
    pass


@click.command(help='generate a set of LESS and Coffeescript assets')
@click.argument('asset')
def asset(name):
    click.echo('Generating a asset')

cli.add_command(asset)


if __name__ == '__main__':
    cli()

Produces the output:

Usage: clicker2.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  asset

However, if I shorten the help string:

@click.command(help='generate a set of assets')

Then it works:

Usage: clicker2.py [OPTIONS] COMMAND [ARGS]...

Options:
  --help  Show this message and exit.

Commands:
  asset  generate a set of assets.

Cheers
Fotis

Building docs fails with Python 3.x

REASON:
docs/clickdoctools.py uses unicode which is undefined in Python 3 (OOPS).

Travis step "make docs" (or tox) should probably catch that in the future.

Add ability to change the application's name

At present (from what I can tell) the name of the click application is figured out automatically, for instance...

$ python app.py
Usage: app.py [OPTIONS] COMMAND [ARGS]...

If the application is a module, and the click application is invoked via __main__.py the help text is a little unhelpful...

$ python -m app
Usage: __main__.py [OPTIONS] COMMAND [ARGS]...

Is there a way of changing this, and if not could someone point me in the direction for a pull request?

default value for an option with multiple=True

It appears to not be possible to set a proper default for options with multiple=True set. I'd like to do something like this:

@click.option('--foo', multiple=True, default=(1, 2, 3))

It appears the default is simply being ignored.

There is a error about merging multi commands ?

There is a error when i use CommandCollection to merge multi commands ?
Following is the error:

Traceback (most recent call last):
  File "group.py", line 20, in <module>
    cli = click.CommandCollection(sources=[cli1, cli2])
AttributeError: 'module' object has no attribute 'CommandCollection'

Store exc_info in `testing.Result`?

It might be nice to store the exc_info in the testing.Result object in case you want to print out the traceback (if there is one) when testing.

Support for interactive mode

An interactive mode for click in which the user is presented with a separate command shell will make it more user friendly and the command plugins available from the command line are automatically configured as commands within the shell.

For example.

$ ClickDemo
(ClickDemo) help

Example as interactive and option based.

Do you have any examples setting up Click to run as an interactive or prompt driven CLI? Basically, I'm looking to build an interactive CLI which can all be called with one liners including long form options to support other utilities but using the interactive mode when driven by a human. I'm thinking this would work by holding the Click executable open instead of exiting each time waiting for input of the next command, etc. I'm guessing you've already done this and it's just not clear to me in the examples.

Global options not visible on sub-commands?

Hey there Armin, hope you're doing well 😄

In many cases (as per your Git example), there is a need for certain options to be available across the entire CLI application.

e.g.

  • Enabling or disabling colour
  • Verbose switch
  • Help

Using the exact Git example code provided, the options registered against the global group do not appear in the help of sub-commands.

e.g. when running python git.py --help, we get:

Usage: git.py [OPTIONS] COMMAND [ARGS]...

Options:
  --repo-home REPO_HOME
  --debug / --no-debug
  --help                Show this message and exit.

Commands:
  clone

and now when running python git.py clone --help, the repo-home and debug options are no longer shown:

Usage: git.py clone [OPTIONS] SRC [DEST]

Options:
  --help  Show this message and exit.

Any ideas?

Thanks heaps
Fotis

Allow to override options

My usecase: In #19, @douglarek
suggested the following way to alias --help to -h:

def print_help(ctx, value):
    if not value:
        return
    print ctx.format_help()
    ctx.exit()

@click.command()
@click.option('-h', is_flag=True, callback=print_help, expose_value=False,
        is_eager=True, help='Show this message and exit.')
def hello():
    pass

However, click then doesn't recognize -h as an alias for --help and shows
this ugly help output for those options:

-h      Show this message and exit.
--help  Show this message and exit.

So i tried overriding --help too, so that i get something like this:

-h, --help      Show this message and exit.

I used this code:

@click.option('-h', '--help', is_flag=True, ...)

But as it stands, optparse doesn't support that and intentionally raises an exception.

Option order

Is there a way for me to understand the order in which the options were passed by the user? Previously I've had to do comparisons between sys.argv and argparse.

Asyncio integration

It would be nice if I could do:

@click.command()
@ascyncio.coroutine
def command():
    yield from whatever

I think the only place that should be changed to make that work would be Context.invoke (check if function is coroutine, do loop.run_until_complete()). But it's hard to subclass context in current click.

Would you mind making this functionality built-in?

Support for shared arguments?

What are your thoughts on shared argument support for click? Sometimes it is useful to have simple inheritance hierarchies in options for complex applications. This might be frowned upon since click's raison d'etre is dynamic subcommands, however, I wanted to explore possible solutions.

A very simple and trivial example is the verbose example. Assume you have more than one subcommand in a CLI app. An ideal user experience on the CLI would be:

python script.py subcmd -vvv

However, this wouldn't be the case with click, since subcmd doesn't define a verbose option. You'd have to invoke it as follows:

python script.py -vvv subcmd

This example is very simple, but when there are many subcommands, sometimes a root support option would go a long way to make something simple and easy to use. Let me know if you'd like further clarification.

Help needed with click prompt

Hi,
I am new to Python and just discovered click library.
It's awesome!

I stumbled on behavior of click prompting I can't explain.

First I've tried the following:
https://gist.github.com/lidi/b946cde1e89bd89a04e9#file-gistfile1-py

And it worked as expected, i.e. if prompt_test is set to True user is prompted for a name. If it's set to False user gets and Missing option error and usage help.

Then I've tried a bit more complex use case using grouping.
I want to be able dynamically set prompting for all fields in a inner command depending on switch set in outer command.

Example:
https://gist.github.com/lidi/d8d9dee41f9029bb5140

I guess this is something to do with the scope in which test_prompt is defined.

Is there any other way I could achieve my goal with click.

Please let me know if you have any questions

Thank you for your help

Support for '--' to seperate options and arguments

I would like to replace docopt with click for one of my projects. One feature that I miss from docopt is the support for the double-dash feature.
With docopt, I can have a command with the format

program [options] [--] arguments...

This will allow commands like,

program --option1 -o -- some-argument --this-is-an-argument -some-other-thing

where --this-is-an-argument and -some-other-thing will be parsed as argument strings instead of as a option to the command.

Click will attempt to parse these as options and fail the command with an unknown option error.

UsageError show assumes self.ctx is set

Was testing flask dev version with click, got this error:

Traceback (most recent call last):
File "/home/harro/virtualenvs//failing_tests/bin/flask", line 9, in
load_entry_point('Flask==0.11-dev-20140507', 'console_scripts', 'flask')()
File "/home/harro/virtualenvs/failing_tests/local/lib/python2.7/site-packages/flask/cli.py", line 352, in main
auto_envvar_prefix='FLASK')
File "/home/harro/virtualenvs/failing_tests/local/lib/python2.7/site-packages/click/core.py", line 464, in main
e.show()
File "/home/harro/virtualenvs/failing_tests/local/lib/python2.7/site-packages/click/exceptions.py", line 33, in show
echo(self.ctx.get_usage() + '\n', file=file)
AttributeError: 'NoneType' object has no attribute 'get_usage'

Some debugging revealed the exception is:
NoAppException('Could not locate application. You did not provide FLASK_APP or the --app parameter.',)

So the exception comes from Flask but in click it assumes we have a self.ctx.

Are you here for the PyGrunn sprints this weekend?

_tempfilepager typo

It appears as if

with open_stream(filename, 'w')[1] as f:

should be

with open_stream(filename, 'w')[0] as f:

Also, I'm not sure whether you want to deal with the other return value which I guess you know will be False.

File arguments documentation is confusing

I couldn't understand File arguments documentation:

Command line tools are more fun if they work with files the unix way which is to accept - as a special file that refers to stdin/stdout.
Click supports this through the click.File type which intelligently handles files for you.

Ok, sounds good. Now let's see how to read/write - files:

$ python inout.py - hello.txt
Usage: inout.py [OPTIONS] INPUT OUTPUT

Error: no such option: -.  Did you mean --help?

I'm lost, why does documentation shows script that says "Error: no such option" instead of it trying to read from stdin?

Invoke vs click

First of all thanks to everyone and @mitsuhiko for click. I could easily create a CLI tool for my organization and distribute across team. click is awesome. 👍

I use Invoke for my task management for some projects. Invoke has pretty cool APIs to run a command locally. I like click's ease to create commands(mainly help text, arguments & options) which is not the same for invoke. Can we use click for something like that?

Please let me know if I am unclear in my explanation.

support -h ?

Is there a reason why --help is supported but not -h?
I don't specially want it, but a user could be lost here as there is no mention of --help:

⏚ [laurent:~/src/click] master 2 ± ./test
Usage: test [OPTIONS] NAME

Error: Missing argument "name".

Ability to display regular line breaks in help

Hello again,

The formatter.fill_paragraphs function seems to be quite aggressive in formatting multi-line help text. At the moment, only paragraphs are allowed (separated by a new line), but it seems there is no way to have a regular line break in help text.

e.g.

#!/usr/bin/env python
import click

@click.command()
def hello():
    """Here's some help information and here is a little list of things
    you can do:

    - a
    - b
    - c

    Have a nice day
    """
    click.echo('Hi there')

if __name__ == '__main__':
    hello()

And the output:

Usage: help_newline.py [OPTIONS]

  Here's some help information and here is a little list of things you can
  do:

  - a - b - c

  Have a nice day

Options:
  --help  Show this message and exit.

Is there a way to do this?

Thanks heaps!
Fotis

Running - 'naval' example

How do we run the 'naval' example without using setup.py. I added a main in that file:

if __name__ == '__main__':
    cli()

And after running python naval.py, I noticed ship & mine were commands and I wasn't able to run the sub-commands for those groups ?

Calling commands from commands is broken

I would expect that something like the following works:

import click

cli = click.Group()

@cli.command()
@click.option('--count', default=1)
def test(count):
    print 'test', count

@cli.command()
@click.option('--count', default=1)
def dist(count):
    test()

if __name__ == '__main__':
    cli()

But this fails with

$ py cli.py dist
Usage: cli.py [OPTIONS]

Error: Got unexpected extra argument (dist)

test_filename_formatting() fails on Python 3 (Unicode)

Running the tests on Python 3.3 / Python 3.4:

    def test_filename_formatting():
        assert click.format_filename(b'foo.txt') == 'foo.txt'
        assert click.format_filename(b'/x/foo.txt') == '/x/foo.txt'
        assert click.format_filename(u'/x/foo.txt') == '/x/foo.txt'
        assert click.format_filename(u'/x/foo.txt', shorten=True) == 'foo.txt'
>       assert click.format_filename('/x/foo\xff.txt', shorten=True) \
            == u'foo\ufffd.txt'
E       assert 'fooÿ.txt' == 'foo�.txt'
E         - fooÿ.txt
E         ?    ^
E         + foo�.txt
E         ?    ^

Multiple options passed via environment variables are split by character

An option with multiple=True yields unexpected (i.e., not useful) behavior when it is specified via an environment variable.

Here's a simple example script:

import click

@click.command()
@click.option('--arg', multiple=True)
def cmd(arg):
    print(arg)

if __name__ == '__main__':
    cmd(auto_envvar_prefix='TEST')

It works as expected when arg is specified with an ordinary command-line option:

$ python3 test.py --arg foo
('foo',)

But when we use an environment variable instead, Click splits the string into characters:

$ TEST_ARG=foo python3 test.py
('f', 'o', 'o')

This is the classic Python iterating-over-a-string-when-you-thought-you-had-a-list failure mode. (These examples were run with click 0.7 from PyPI.)

I can see two reasonable resolutions here: split environment variables on whitespace or don't split them at all. The latter is probably more predictable but the former is more flexible.

Argument.consume_value returns None when ngargs is -1

I was testing click and created a subcommand that expects a list of hostnames as arguments. See:

import click

@click.command()
@click.argument('hostnames', nargs=-1)
def subcommand(hostnames):
    print('Executing subcommand')
    print(hostnames)

@click.group()
def main():
    pass

main.add_command(subcommand)

if __name__ == '__main__':
    main()

Calling python3 clicktst.py subcommand host1 host2 returns:
(None,)
instead of the list of hostnames.

To correct this you need to change Argument.consume_value
from

    def consume_value(self, ctx, opts, args):
        found = True
        if self.nargs == 1:
            try:
                value = args.pop(0)
            except IndexError:
                found = False
        elif self.nargs < 0:
            value = tuple(args)
            found = not value
            args = []
        else:
            values = args[:self.nargs]
            values += [None] * (self.nargs - len(values))
            args = args[self.nargs:]
            value = tuple(values)
            found = not value
        if not found:
            value = self.value_from_envvar(ctx)
            if self.nargs != 1:
                value = (value,)
        return value, args

to

    def consume_value(self, ctx, opts, args):
        found = True
        if self.nargs == 1:
            try:
                value = args.pop(0)
            except IndexError:
                found = False
        elif self.nargs < 0:
            value = tuple(args)
            found = bool(value)
            args = []
        else:
            values = args[:self.nargs]
            values += [None] * (self.nargs - len(values))
            args = args[self.nargs:]
            value = tuple(values)
            found = not value
        if not found:
            value = self.value_from_envvar(ctx)
            if self.nargs != 1:
                value = (value,)
        return value, args

The difference is on the line 1217: found = not value becomes found = bool(value)

Allow cli to double as python API

It would be very useful if the python module that defines the click command-line interface could also double as a python api.

Consider your repo.py example; I'd like to do something like this:

import repo
repo.setuser(repo.Repo('/var/tmp'), 'chad', '[email protected]', 'so_amaze')

The problem is that the decorated functions become Command instances, whose __call__ method expects different args, processes sys.argv and calls sys.exit, etc, so the above attempt fails.

I dug through the code a bit and it seems like I could solve this problem by subclassing core.Group to implement a new command() decorator that returns the original, undecorated function:

    def command(self, *args, **kwargs):
        """A shortcut decorator for declaring and attaching a command to
        the group.  This takes the same arguments as :func:`command` but
        immediately registers the created command with this instance by
        calling into :meth:`add_command`.
        """
        def decorator(f):
            cmd = command(*args, **kwargs)(f)
            self.add_command(cmd)
            return f # <--- return original function
        return decorator

It seems to me that it could be quite useful if the default behavior for Group.command was to return the original function: since the Group object is the entry-point of the CLI it seems unnecessary that the individual functions can also serve as entry points.

In case it gives some perspective, celery has a similar decorator-based API, and the default behavior for the __call__ method of its tasks is to execute the original function:

@app.task
def add(x, y):
    return x + y

add(1, 3)  # runs immediately in this process
add.delay(1, 3) # runs on a slave

Is this a change that you think should be an official part of click? Do you see any problems with this approach, or a way to tweak it to make it more acceptable?

pip install click not found error

$ pip install click
Downloading/unpacking click
Could not find any downloads that satisfy the requirement click
Cleaning up...
No distributions at all found for click

cat /home/igniteflow/.pip/pip.log


/home/igniteflow/.virtualenvs/locus/bin/pip run on Fri Apr 25 10:27:14 2014
Downloading/unpacking click
Getting page https://pypi.python.org/simple/click/
URLs to search for versions for click:
https://pypi.python.org/simple/click/
Analyzing links from page https://pypi.python.org/simple/click/
Could not find any downloads that satisfy the requirement click
Cleaning up...
Removing temporary dir /home/igniteflow/.virtualenvs/locus/build...
No distributions at all found for click
...

Using pip 1.5.4 python 2.7 virtualenv 1.10.1 in Xubuntu

optparse optimizations break gettext

The recent optimizations for gettext in _optparse cause problems if click is imported before another library which uses gettext, like ipdb.

The problem is we're setting sys.modules['gettext'] to None, which isn't the same as it being unset. We can fix this by using del.

Wildcards do fail if empty

I am no expert but it seems that gnu coreutils commands do fail when are passed a wildcard which matched nothing.

In the arguments docs, you wrote :

This is supported by setting required=True. However, this should not be used if you can avoid it as we believe scripts should gracefully degrade into becoming noops if a variadic argument is empty. The reason for this is that very often, scripts are invoked with wildcard inputs from the commandline and they should not error out if the wildcard is empty.

But in bash, we have :

[piroux@localhost taratata]$ ls -l
total 0
[piroux@localhost taratata]$ find *
find: ‘*’: No such file or directory
[piroux@localhost taratata]$ ls *
ls: cannot access *: No such file or directory
[piroux@localhost taratata]$ tree *
* [error opening dir]
0 directories, 0 files
[piroux@localhost taratata]$ grep hello *
grep: *: No such file or directory
[piroux@localhost taratata]$ cat *
cat: *: No such file or directory

Group options forced to be before the group's commands.

When using groups, if you add options on to a group, those options must be specified before the group's commands when invoking it on the command line. This is not only annoying, but it is not how a lot of other command line tools work.

It may be the way I think of group options: I think of them as 'Common options for all nested commands."

I may be missing something, as this seems like something a lot of people would want.

import click


@click.group()
@click.option('-q', '--quiet', is_flag=True)
@click.pass_context
def app(ctx, quiet):
    ctx.obj['quiet'] = quiet


@app.command()
@click.argument('branch', default='master', type=str)
@click.pass_context
def push(ctx, branch):
    pass


if __name__ == '__main__':
    app(obj={})

This must be invoked with the -q option before the push command:

foo -q push master

This makes much more sense:

foo push master -q

Note: I know this could be done by adding the option to each nested command, but that would get VERY tedious and annoying if we need to change the option.

Dynamic Options

Assume I add several dynamic options for some subcommands:

class MyCLI(click.MultiCommand):
    def list_commands(self, ctx):
        rv = ['subcmd1']
        rv.sort()
        return rv

    def get_command(self, ctx, name):
        ns = {}
        params = [click.Option(("--dyn-opt1",), is_flag=True, default=False), click.Option(("--dyn-opt2",))]
        ret = click.Command(name, params=params)
        return ret

But I can not get any option values at runtime

@cli.command(cls=MyCLI)
@click.pass_context
def run(ctx, *args, **kvargs):
    print(args)       # empty
    print(kvargs)    # empty

Any clue for this problem?

Is click too verbose in its api?

Hey there, I started a discussion on twitter, asking why

@click.option('--shout', is_flag=True) 

is not just

@click.flag('--shout')

This might seem like a minor issue, but to me, it feels like the option concept is being overused. Please stop me if I'm wrong, I have a windows background but I use the command line a lot, so it might just be a unix/windows/German/English language barrier preventing me to get the idea.

I am unsure I care about the fact that something is an option to begin with. Another part is that the option now uses a lot of kwargs that make it work in strange and oddly specific ways. I can't imagine a good use case for counting with

@click.option('-v', '--verbose', count=True)

At least, when using separate decorators such as

@click.count('-v', '--verbose')

The concept could be isolated. I would like to point somewhere in the direction of the single responsibility principle, or something like that, but I would not like to make this a discussion on principles. I feel that this, if it is a specific concept, deserves its own decorator that reflects its name,

I believe that like this signature would become less noisy, since the function itself could preconfigure a lot of values (as with the flag thingy the flag decorator could easily just set the is_flag to true, withough me having to remember to put in yet another kwarg).

You answered to my inquiry, that a solution like @click.flag('--shout') would be unnecessary, because a flag would still be an option. This is true, but the distinction would allow for a separation of the concepts non the less. Of course, a flag is still an option, but it is a specific case of one and there is no need to name things by the highest level of objects in their inheritance hierarchy. After all, a string in python is an object, yet you don't call it a string_object and, I guess it would be fair to argue that a string is a fairly specific type of an object, as is an int or a character.

Rather than having one big click.option decorator I would love for click to separate the concepts some more.

Rather than using

@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True)

The code could already provide this (indeed more useful than the counting) case as a specific decorator:

@click.password('--password', confirm=True)

The functionality is already there and writing a decorator like this would be possible by merely creating a couple of aliases that in the end point back to or derive from option, and could easily abstract away a lot of the complexity by providing sensible defaults.

Going through this page: http://click.pocoo.org/options/#basic-value-options
I can't help but invent more specific decorators for the individual sections in my mind, such as counting, flags, switches and so on, all of which are different concepts, all of which are provided by one massive function, but all of which appear to be different concepts, at least enough so to deserve their own very nice documentation.

This was a lot of criticism, I apologize if it is very direct, please read it with a smile and see it as a different point of view and never a need to be offended.
I really love the hard work you have put into this, I really love where this is going, I really love flask and your work and have a very deep respect for you.

Optional arguments

Are optional arguments currently possible with click?

For example...

$ greet
hello all
$ greet john
hello john
$ greet john sally
hello john and sally

eager required options suppress help

When there is an option that has is_eager=True as well as required=True, it will suppress the help message. Perhaps the --help option needs to be somehow more eager than other options?

Here's a failing example:

import click

def get_from_source(ctx,value):
    return value or ctx.params['source']

@click.command()
@click.option('--source',
    is_eager=True,
    required=True,
    help='source file')
@click.option('--dest',
    callback=get_from_source,
    help='destination file. If not supplied, operates in place.')
def process_file(source,dest):
    print 'source:',source
    print 'dest:',dest

if __name__=='__main__':
    process_file()

Running this example produces:

bash$ python process_file.py --help
Usage: process_file.py [OPTIONS]

Error: Missing option "--source"

I would expect (and prefer) to see the following, which is what you get if you turn is_eager to False.:

bash$ python process_file.py --help
Usage: process_file.py [OPTIONS]

Options:
  --source=SOURCE  source file  [required]
  --dest=DEST      destination file. If not supplied, operates in place.
  --help           Show this message and exit.

It can be helpful to know the motivation. I'm trying to use a callbacks to validate my input and convert the train parameter from a comma separated list training_set1,training_set2,training_set3 to a python list. The train parameter is required. If the test parameter is not specified, it is taken as the first entry in the train list. Because test depends on the processed, validated version of train, I have train set to be 'eager'.

Tab autocompletion

I looked through the docs and didn't see any mention but do you plan to add tab bash completion? I'm looking to build a CLI for an application backend and I'd really like to emulate a JunOS like CLI. I'd also like to support the notion of a configuration/edit mode with rollback, commit messages, etc. Maybe Click isn't the best fit for this but things are looking promising so far. Here was a library I found that supports bash completion for optparse. http://furius.ca/optcomplete/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.