Giter VIP home page Giter VIP logo

clize's Introduction

Clize

Documentation Status

Join the chat at https://gitter.im/epsy/clize

image

image

Clize is an argument parser for Python. You can use Clize as an alternative to argparse if you want an even easier way to create command-line interfaces.

With Clize, you can:

  • Create command-line interfaces by creating functions and passing them to clize.run.
  • Enjoy a CLI automatically created from your functions' parameters.
  • Bring your users familiar --help messages generated from your docstrings.
  • Reuse functionality across multiple commands using decorators.
  • Extend Clize with new parameter behavior.

Here's an example:

from clize import run

def hello_world(name=None, *, no_capitalize=False):
    """Greets the world or the given name.

    :param name: If specified, only greet this person.
    :param no_capitalize: Don't capitalize the given name.
    """
    if name:
        if not no_capitalize:
            name = name.title()
        return 'Hello {0}!'.format(name)
    return 'Hello world!'

if __name__ == '__main__':
    run(hello_world)

The python code above can now be used on the command-line as follows:

$ pip install clize
$ python3 hello.py --help
    Usage: hello.py [OPTIONS] name

    Greets the world or the given name.

    Positional arguments:
      name   If specified, only greet this person.

    Options:
      --no-capitalize   Don't capitalize the given name.

    Other actions:
      -h, --help   Show the help
$ python3 hello.py
Hello world!
$ python3 hello.py john
Hello John!
$ python3 hello.py dave --no-capitalize
Hello dave!

You can find the documentation and tutorials at http://clize.readthedocs.io/

clize's People

Contributors

bersace avatar epsy avatar gitter-badger avatar karanparikh avatar kianmeng avatar kouk avatar mgielda avatar nodakai avatar rosuav avatar scholer avatar shir0kamii 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

clize's Issues

Assist in creating subcommand groups that add parameters and/or behavior

Usefulness and API to be determined.

Clize currently supports sharing behavior and parameters through decorators. They offer flexibility in that they can be used arbitrarily, at the cost of limiting usage of those parameters to only after all arguments that designate subcommands. example:

$ prog command --quiet    # ok because command is decorated with @with_output_control
$ prog --quiet command    # not ok because only command's decorators know of --quiet

This feature request proposes a more elaborate API be devised for creating groups of commands, one that allows for behavior and parameters to be processed at the group level.

The file argument converter should convert default values

As it is if you write the following and no value is supplied, the function code will just receive the string 'default.json`. Instead, it should receive the same kind of value as it would if a value was supplied.

def fun(f: converters.file() = 'default.json'):
    ...

This may involve changes to parser's ParameterWithValue

Allow the creation of command aliases

In many cases, several names are needed to invoke the same command.

It would be nice to have a way to specify an alias for a command, and have the alias mentioned in the help, without the need to define repeated entries in a command dictionary, and without the redefined function appearing several times in the help message.

For instance, doing this:

# 1.py
import clize

def foo():
    "Function foo"
    pass

def bar():
    "Function bar"
    pass


if __name__ == '__main__':
    clize.run(dict(foo=foo, bar=bar, foo2=foo))

produces

Usage: 1.py command [args...]

Commands:
  foo    Function foo
  bar    Function bar
  foo2   Function foo

The desired outcome (by whatever method is implemented) would be more like

Usage: 1.py command [args...]

Commands:
  foo    Function foo (alias: foo2)
  bar    Function bar

Ensure sphinx interoperability

Clize is geared towards letting code still be used as code, but the legacy of clize's docstring format impairs this.

Most notably, they cannot be properly parsed by sphinx.ext.autodoc as it is. A sphinx extension could be devised for this. However it is more preferable for Clize to support sphinx/docutils fields since it falls in line with the goal of not needing to learn anything new to use Clize.

Special care needs to be given towards the following:

  • Features of Clize docstrings such as customizable ordering and naming sections for named options must be preserved.
  • Decorators must be handled correctly (epsy/sigtools#6)

It could be acceptable to diminish the flexibility of sections to just one per decorator.

Optionally, a directive could be added to format a function to be documented as a command-line interface.

Also, sphinx's contributed napoleon extension could be used to support Google- and Numpy-style docstrings.

Errors when using dictionnary to specify alternate commands

Hi,

Thanks for this project....

I am reading examples from documentation and get problem/error trying to use dictionnary as alternate commands list.

Following is the code (mainly copied from documentation) and the errors i get.

What i am doing wrong ?

from clize import ArgumentError, Parameter, run

VERSION = "0.2"

#------------------------------------------------------------------------------
def echo(*text:Parameter.REQUIRED,
            prefix:'p'='', suffix:'s'='', reverse:'r'=False, repeat:'n'=1):
    """Echoes text back

    :param text: The text to echo back
    :param reverse: Reverse text before processing
    :param repeat: Amount of times to repeat text
    :param prefix: Prepend this to each line in word
    :param suffix: Append this to each line in word
    """
    text = ' '.join(text)

    if 'spam' in text:
        raise ArgumentError("I don't want any spam!")

    if reverse:
        text = text[::-1]

    text = text * repeat

    if prefix or suffix:
        return '\n'.join(prefix + line + suffix for line in text.split('\n'))

    return text

#------------------------------------------------------------------------------
def version():
    """Show the version"""
    return (f"version={VERSION}")

#------------------------------------------------------------------------------
def build_date(*,show_time=False):
    """Show the build date for this version"""
    print("Build date: 17 August 1979", end='')
    if show_time:
        print(" afternoon, about tea time")
    print()


#------------------------------------------------------------------------------
if __name__ == '__main__':
    # next is OK
    # run(echo, alt=[version, build_date])
    # this not working 
    run(echo,  alt= 
        {   "totally_not_the_version": version,
            "birth": build_date
        })

Tested with python 3.6.9 and 3.9.0

cd /home/user/MyPyProjs/Py390/CliTests
venv_activate
(venv) > python clize_t01.py

Traceback (most recent call last):
  File "/home/user/MyPyProjs/Py390/CliTests/clize_t01.py", line 51, in <module>
    run(echo, alt={
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/sigtools/modifiers.py", line 161, in __call__
    return self.func(*args, **kwargs)
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/runner.py", line 395, in run
    ret = cli(*args)
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/runner.py", line 261, in __call__
    func, name, posargs, kwargs = self.read_commandline(args)
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/runner.py", line 271, in read_commandline
    ba = self.signature.read_arguments(args[1:], args[0])
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/util.py", line 121, in __get__
    ret = obj.__dict__[self.key] = self.func(obj)
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/runner.py", line 220, in signature
    return parser.CliSignature.from_signature(
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/parser.py", line 1143, in from_signature
    return cls(
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/parser.py", line 1107, in __init__
    for param in _develop_extras(parameters):
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/parser.py", line 1052, in _develop_extras
    for param in params:
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/runner.py", line 252, in _process_alt
    for name, func in util.dict_from_names(alt).items():
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/util.py", line 104, in dict_from_names
    receiver.update((x.__name__, x) for x in maybe_iter(obj))
  File "/home/user/MyPyProjs/Py390/CliTests/venv/lib/python3.9/site-packages/clize/util.py", line 104, in <genexpr>
    receiver.update((x.__name__, x) for x in maybe_iter(obj))
AttributeError: 'str' object has no attribute '__name__'

[Question] Simple version printing

1. Problem

In most programs, what I use, I can get version, use --version and -v command line arguments without additional parameters.

D:\SashaPythonista>python SashaAscent.py --version
Do Nothing version 0.2

or

D:\SashaPythonista>python SashaAscent.py -v
0.2

I don't understand, how I can to create the same behavior, if I write a program, use Clize.

2. Attempts

1. First

from clize import run

VERSION = "0.2"


def program_version(version):
    print(VERSION)

run(program_version)

That get a version, I need input version, not --version:

D:\SashaPythonista>python SashaAscent.py version
0.2

D:\SashaPythonista>python SashaAscent.py --version
SashaAscent.py: Unknown option '--version'
Usage: SashaAscent.py version

2. Second

from clize import run

VERSION = "0.2"


def program_version(b="VERSION", *, version:'v'=True):
    """Show the version"""
    if version is True:
        b = "0.2"
        print(b)

run(program_version)

I need input -v b, not -v:

D:\SashaPythonista>python SashaAscent.py -v b
0.2

3. Third

from clize import run

VERSION = "0.2"


def do_nothing():
    """Does nothing"""
    return "I did nothing, I swear!"


def version():
    """Show the version"""
    print(VERSION)

run(do_nothing, alt=version)

It works for --version,

D:\SashaPythonista>python SashaAscent.py --version
0.2

but I don't find, how I can to create alias -v.

4. Fourth

It works nice.

from clize import run

VERSION = "0.2"


def do_nothing():
    """Does nothing"""
    return "I did nothing, I swear!"


def version():
    """Show the version"""
    print(VERSION)


def v():
    """Show the version"""
    print(VERSION)

run(do_nothing, alt=[version, v])

But in --help menu -v is not --version alias.

Actual:

D:\SashaPythonista>python SashaAscent.py --help
Usage: SashaAscent.py

Does nothing

Other actions:
  -h, --help   Show the help
  --version    Show the version
  -v           Show the version

Expected:

D:\SashaPythonista>python SashaAscent.py --help
Usage: SashaAscent.py

Does nothing

Other actions:
  -h, --help     Show the help
  -v, --version  Show the version

Thanks.

Switch docs to Python 3 syntax by default

The docs currently use sigtools.modifiers decorators to augment parameters with annotations or to make them keyword-only. I'm suspecting this distracts users from the examples' main points.

Given:

  • The above,
  • That new python users are increasingly being directed to Python 3,
  • That most major packages work on it,
  • That Debian and Ubuntu both intend to remove Python 2 from their default installation,

I suggest adjusting the examples so they make use of keyword-only parameters and annotations, and using "note" sections destined to users stuck with Python 2 or trying to keep compatibility with it.

make pathlib.Path a first citizen by adding default converter

if object of type pathlib.Path is used as function argument, clize will complain:

ValueError: Cannot find value converter for default value PosixPath('/foo/bar'). Please specify one as an annotation.
If the default value's type should be used to convert the value, make sure it is decorated with clize.parser.value_converter()

how about add the converter in clize by default? Many of the standard lib in Python3 already changed to support pathlib

[Question] Disable multiple help across files

1. Briefly

I don't understand, how I can disable multiple help, if my program contains multiple files.

2. Configuration

For example, I have a folder with 4 files:

  • config.py (see #33):
import logbook
import sys

log = logbook.Logger("Sasha Logbook")


def clize_log_level(*, logbook_level: 'll'="NOTICE"):
    """Change log levels via command line.

    User select, which logging messages to see. See about 6 log levels here:
    https://logbook.readthedocs.io/en/stable/quickstart.html

    :param logbook_level: user select logging level
    """
    if logbook_level == "DEBUG":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.DEBUG).push_application()
    elif logbook_level == "ERROR":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.ERROR).push_application()
    else:
        logbook.StreamHandler(sys.stdout,
                              level=logbook.NOTICE).push_application()
  • first_test.py:
from clize import run
from config import clize_log_level
from config import log

run(clize_log_level, exit=False)

log.debug("First test + debug message")
log.notice("First test + notice message")
log.error("First test + error message")


first_test_variable = True
  • second_test.py:
from clize import run
from config import clize_log_level
from config import log

run(clize_log_level, exit=False)

log.debug("Second test + debug message")
log.notice("Second test + notice message")
log.error("Second test + error message")


second_test_variable = True
  • run_tests.py:
from clize import run
from config import clize_log_level
from config import log
from first_test import first_test_variable
from second_test import second_test_variable

if first_test_variable and second_test_variable is True:
    log.debug("Run tests + debug message")
    log.notice("Run tests + notice message")
    log.error("Run tests + error message")

3. Behavior of the program

If I run run_tests.py, run first_test.py and second_test.py. Logging messages from first_test.py and second_test.py return.

The program works as expected:

D:\SashaClize>python run_tests.py --ll=DEBUG
[2018-01-06 15:21:38.251565] DEBUG: Sasha Logbook: First test + debug message
[2018-01-06 15:21:38.251565] NOTICE: Sasha Logbook: First test + notice message
[2018-01-06 15:21:38.251565] ERROR: Sasha Logbook: First test + error message
[2018-01-06 15:21:38.252565] DEBUG: Sasha Logbook: Second test + debug message
[2018-01-06 15:21:38.253197] NOTICE: Sasha Logbook: Second test + notice message
[2018-01-06 15:21:38.253332] ERROR: Sasha Logbook: Second test + error message
[2018-01-06 15:21:38.253457] DEBUG: Sasha Logbook: Run tests + debug message
[2018-01-06 15:21:38.253556] NOTICE: Sasha Logbook: Run tests + notice message
[2018-01-06 15:21:38.253556] ERROR: Sasha Logbook: Run tests + error message
D:\SashaClize>python run_tests.py --ll=ERROR
[2018-01-06 15:22:01.131626] ERROR: Sasha Logbook: First test + error message
[2018-01-06 15:22:01.132525] ERROR: Sasha Logbook: Second test + error message
[2018-01-06 15:22:01.133558] ERROR: Sasha Logbook: Run tests + error message

4. Problem

One problem: help menu show for me 2 times:

D:\SashaClize>python run_tests.py --help
Usage: run_tests.py [OPTIONS]

Change log levels via command line.

User select, which logging messages to see. See about 6 log levels here: https://logbook.readthedocs.io/en/stable/quickstart.html

Options:
  --logbook-level, --ll=STR   user select logging level (default: NOTICE)

Other actions:
  -h, --help                  Show the help
Usage: run_tests.py [OPTIONS]

Change log levels via command line.

User select, which logging messages to see. See about 6 log levels here: https://logbook.readthedocs.io/en/stable/quickstart.html

Options:
  --logbook-level, --ll=STR   user select logging level (default: NOTICE)

Other actions:
  -h, --help                  Show the help

If I have more <number>_test files, I have more help repeats.

How I need edit in my code, that don't see help multiple times?

Thanks.

Any issue with `attrs>=20.0.0`?

I'm using clize with attrs>=20.3.0. requirements are currently conflict between clize and attrs.

Any plan for update attrs>=19.1.0,<20 to attrs>=19.1.0,<21?

[Question] Changing variable from command line

1. Briefly

I don't understand, how I can change one variable from command line.

2. Settings

1. Environment

  • Windows 10 64-bit Enterprise EN,
  • Python 3.6.3,
  • Clize 4.0.2.

2. File

For example, I have a file, where I use Logbook logging.

import logbook
import sys

logbook.StreamHandler(sys.stdout,
                        level=logbook.WARNING).push_application()
log = logbook.Logger("Sasha Logbook")

log.debug("Debug message")
log.warning("Warning message")
log.error("Error message")

If I run this file in console, I get output:

D:\SashaPythonista>python SashaLogbook.py
[2018-01-01 17:55:47.063625] WARNING: Sasha Logbook: Warning message
[2018-01-01 17:55:47.064624] ERROR: Sasha Logbook: Error message

Output, if logbook.StreamHandler(sys.stdout, level=logbook.DEBUG).push_application():

[2018-01-01 17:55:46.994054] DEBUG: Sasha Logbook: Debug message
[2018-01-01 17:57:26.982679] WARNING: Sasha Logbook: Warning message
[2018-01-01 17:57:26.983677] ERROR: Sasha Logbook: Error message

And so on.

2. Expected behavior

If:

D:\SashaPythonista>python SashaLogbook.py level=DEBUG
[2018-01-01 17:55:46.994054] DEBUG: Sasha Logbook: Debug message
[2018-01-01 17:55:47.063625] WARNING: Sasha Logbook: Warning message
[2018-01-01 17:55:47.064624] ERROR: Sasha Logbook: Error message

If:

D:\SashaPythonista>python SashaLogbook.py level=WARNING
[2018-01-01 17:57:26.982679] WARNING: Sasha Logbook: Warning message
[2018-01-01 17:57:26.983677] ERROR: Sasha Logbook: Error message

And so on.

3. Actual behavior

I try to add Clize:

import logbook
import sys

from clize import run

log = logbook.Logger("Sasha Logbook")


def test(level="WARNING"):

    if level == "DEBUG":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.DEBUG).push_application()
    if level == "WARNING":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.WARNING).push_application()
    if level == "ERROR":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.ERROR).push_application()


run(test)

log.debug("Debug message")
log.warning("Warning message")
log.error("Error message")

If I replace run(test) to test(), Logbook messages print to console. But if I use run(test), I haven't output in console.

D:\SashaPythonista>python SashaLogbook.py --help
Usage: SashaLogbook.py [level]

Arguments:
  level         (default: WARNING)

Other actions:
  -h, --help   Show the help

D:\SashaPythonista>python SashaLogbook.py level=DEBUG

D:\SashaPythonista>python SashaLogbook.py level=ERROR

4. Did not help

  1. I see example for logging, but it hard for me. I want only control logging levels from command line.

5. Do not offer

  1. I want to use Logbook, not default Python logging module.

Thanks.

[Bug] ValueError: path is on mount 'C:', start on mount 'D:'

1. Summary

My CLI program successful work for disk C, but not for disk D.

Possibly, it Clize bug, not bug in my program.

2. Settings

2.1. Environment

  • Windows 10 Enterprise LTSB 64-bit EN,
  • Python 3.6.4,
  • clize 4.0.3,
  • logbook 1.1.0.

2.2. Project

https://github.com/Kristinita/SashaPythonTest

my simple program check, contains <body> in .txt files in folder or no.

It that I want in #35.

2.3. Folders

I have 2 folders with similar content:

  • SashaPythonTestOnDiskC — on disk C,
  • SashaPythonTestOnDiskD — on disk D.

Content of SashaPythonTestOnDiskC and SashaPythonTestOnDiskD folders:

C:\SashaPythonTestOnDiskC>tree /f
C:.
    BodyExists.txt
    NoBody.txt

BodyExists.txt file:

<body>

NoBody.txt file:

No

3. Steps to reproduce

I install my project:

setup.py install

I run command sashatest in console.

4. Expected behavior

For C:\SashaPythonTestOnDiskC:

C:\SashaPythonTestOnDiskC>sashatest --help
Usage: ..\Python36\Scripts\sashatest [OPTIONS]

Change log levels via command line.

User select, which logging messages to see. See about 6 log levels here: https://logbook.readthedocs.io/en/stable/quickstart.html

Options:
  --logbook-level, --ll=STR   user select logging level (default: NOTICE)

Other actions:
  -h, --help                  Show the help
  --version                   Show version.
  -v                          Alternative show version.

C:\SashaPythonTestOnDiskC>sashatest
[2018-01-20 17:44:13.035785] ERROR: eric_body logbook: File NoBody.txt not contain <body>.
[2018-01-20 17:44:13.036806] ERROR: eric_body logbook: Not all files contains <body>. Please, correct your files.
[2018-01-20 17:44:13.036806] ERROR: run_tests logbook: Failure!

C:\SashaPythonTestOnDiskC>sashatest --ll=DEBUG
[2018-01-20 17:52:41.227187] DEBUG: eric_body logbook: BodyExists.txt contains <body>
[2018-01-20 17:52:41.228178] ERROR: eric_body logbook: File NoBody.txt not contain <body>.
[2018-01-20 17:52:41.228178] ERROR: eric_body logbook: Not all files contains <body>. Please, correct your files.
[2018-01-20 17:52:41.228178] ERROR: run_tests logbook: Failure!

5. Actual behavior

For D:\SashaPythonTestOnDiskD:

D:\SashaPythonTestOnDiskD>sashatest
Traceback (most recent call last):
  File "C:\Python36\Scripts\sashatest-script.py", line 11, in <module>
    load_entry_point('sashatest==0.0.1', 'console_scripts', 'sashatest')()
  File "C:\Python36\lib\site-packages\sashatest-0.0.1-py3.6.egg\sashatest\__main__.py", line 18, in main
    run(clize_log_level, alt=[version, v], exit=False)
  File "C:\Python36\lib\site-packages\sigtools\modifiers.py", line 158, in __call__
    return self.func(*args, **kwargs)
  File "C:\Python36\lib\site-packages\clize\runner.py", line 353, in run
    args = fix_argv(sys.argv, sys.path, module)
  File "C:\Python36\lib\site-packages\clize\runner.py", line 284, in fix_argv
    name = get_executable(argv[0], argv[0])
  File "C:\Python36\lib\site-packages\clize\runner.py", line 303, in get_executable
    rel = os.path.relpath(path)
  File "C:\Python36\lib\ntpath.py", line 585, in relpath
    path_drive, start_drive))
ValueError: path is on mount 'C:', start on mount 'D:'

Thanks.

Pypi is not updated

Please, could you update Pypi with version which supports sub commands? Thank you!

Problem with Python 2.6

OrderedDict is a new class added to Python 2.7, so Clize is not compatible with Python 2.6.

To solve this issue, you can install a backport:

pip install ordereddict

and patch Clize by replacing:

from collections import OrderedDict

by

try:
from collections import OrderedDict
except ImportError:
# python 2.6 or earlier, use backport
from ordereddict import OrderedDict

I will propose an pull request.

Internationalization

Technically this is a regression from Clize 2.4, although it never had translations back then.

This request proposes strings in Clize be actually marked, and a French translation to be made (by me, presumably).

Additionally, Clize should provide facilities for users to translate function and decorator docstrings so that client code can translate their --help messages.

[Question] Disable print(), if --help

1. Summary

I don't understand, how I can disable print() in output, if I run command with --help or --version.

2. Example

For example, I have SashaClizePrint.py file, based on my previous questions:

"""Demo code for #38.

Variables:
    VARIABLE {bool} -- True or False
    VERSION {str} -- version number
"""

import logbook
import sys

from clize import run

VARIABLE = True

VERSION = "0.1"

LOG = logbook.Logger("summary logbook")


def clize_log_level(*, logbook_level: 'll'="NOTICE"):
    """Change log levels via command line.

    User select, which logging messages to see. See about 6 log levels here:
    https://logbook.readthedocs.io/en/stable/quickstart.html

    :param logbook_level: user select logging level
    """
    if logbook_level == "DEBUG":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.DEBUG).push_application()
    elif logbook_level == "NOTICE":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.NOTICE).push_application()
    elif logbook_level == "ERROR":
        logbook.StreamHandler(sys.stdout,
                              level=logbook.INFO).push_application()


def version():
    """Show version.

    For details see:
    https://clize.readthedocs.io/en/stable/dispatching.html#alternate-actions
    """
    print(VERSION)


def main():
    run(clize_log_level, alt=[version], exit=False)
    if VARIABLE:
        LOG.debug("Success!")
        print("Success!")

    else:
        LOG.error("Failure!")
        print("Failure!")


if __name__ == '__main__':
    main()

3. Command line

3.1. Expected

I want, that Success! print, if I run this file.

D:иролайна>python SashaClizePrint.py
[2018-01-22 10:59:34.455838] NOTICE: summary logbook: Success!
Success!
D:иролайна>python SashaClizePrint.py --ll=ERROR
Success!

3.2. Non-expected

But I don't want Success! in console, if I want get help or version.

D:иролайна>python SashaClizePrint.py --help
Usage: SashaClizePrint.py [OPTIONS]

Change log levels via command line.

User select, which logging messages to see. See about 6 log levels here: https://logbook.readthedocs.io/en/stable/quickstart.html

Options:
  --logbook-level, --ll=STR   user select logging level (default: NOTICE)

Other actions:
  -h, --help                  Show the help
  --version                   Show version.
Success!
D:иролайна>python SashaClizePrint.py --version
0.1
Success!

4. Argumentation

In real, I want beautiful colored output instead of print("Success!"), for example, as here.

I don't find, how I can log this output.

Thanks.

What is going wrong here?

I'm having a hard time interpreting what's wrong based on the error message. Can anyone throw me a bone? Or maybe it's a bug?

Here's all the code you need.

from pathlib import Path
from typing import List, Optional, Union

from clize import run


def clize_demo(
    one_path_or_many_paths: Union[Path, List[Path]],
    optional_path: Optional[Path] = None,
    optional_int: Optional[int] = None,
    bool_with_default: bool = False,
):
    pass


if __name__ == "__main__":
    run(clize_demo)

Here's what you get when you try it out.

(venv-clize) me@neato:~/repos/clive-demo$ python wow.py
Traceback (most recent call last):
  File "wow.py", line 17, in <module>
    run(clize_demo)
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/sigtools/modifiers.py", line 158, in __call__
    return self.func(*args, **kwargs)
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/runner.py", line 363, in run
    ret = cli(*args)
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/runner.py", line 219, in __call__
    func, name, posargs, kwargs = self.read_commandline(args)
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/runner.py", line 229, in read_commandline
    ba = self.signature.read_arguments(args[1:], args[0])
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/util.py", line 128, in __get__
    ret = obj.__dict__[self.key] = self.func(obj)
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/runner.py", line 195, in signature
    return parser.CliSignature.from_signature(
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/parser.py", line 998, in from_signature
    return cls(
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/parser.py", line 962, in __init__
    for param in _develop_extras(parameters):
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/parser.py", line 907, in _develop_extras
    for param in params:
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/parser.py", line 1001, in <genexpr>
    (cls.convert_parameter(param)
  File "/home/me/virtualenvs/venv-clize/lib/python3.8/site-packages/clize/parser.py", line 1013, in convert_parameter
    if Parameter.IGNORE in annotations:
  File "/usr/lib/python3.8/typing.py", line 261, in inner
    return func(*args, **kwds)
  File "/usr/lib/python3.8/typing.py", line 685, in __getitem__
    params = tuple(_type_check(p, msg) for p in params)
  File "/usr/lib/python3.8/typing.py", line 685, in <genexpr>
    params = tuple(_type_check(p, msg) for p in params)
  File "/usr/lib/python3.8/typing.py", line 149, in _type_check
    raise TypeError(f"{msg} Got {arg!r:.100}.")
TypeError: Parameters to generic types must be types. Got 0.

Is this project still alive?

Is this project alive / still maintained? I really like what I see but I can't use it if this project is not maintained anymore...

Remove test deps from „install_requires”

One thing that annoys me a lil bit is that install_requires has various deps that are only needed for testing.

Perhaps clize could move them to tests_require or install them specifically just for CI? What are your thoughts on this?

Proposing a PR to fix a few small typos

Issue Type

[x] Bug (Typo)

Steps to Replicate and Expected Behaviour

  • Examine clize/tests/test_help.py, docs/docstring-reference.rst and observe overriden, however expect to see overridden.
  • Examine docs/compositing.rst and observe suplied, however expect to see supplied.
  • Examine docs/faq.rst and observe succesfully, however expect to see successfully.
  • Examine docs/faq.rst and observe sigatures, however expect to see signatures.
  • Examine clize/runner.py and observe regularily, however expect to see regularly.
  • Examine docs/alternatives.rst and observe previoous, however expect to see previous.
  • Examine docs/compositing.rst and observe preferrable, however expect to see preferable.
  • Examine docs/reference.rst and observe preceeded, however expect to see preceded.
  • Examine docs/basics.rst and observe inteteger, however expect to see integer.
  • Examine docs/contributing.rst and observe insigna, however expect to see insignia.
  • Examine docs/docstring-reference.rst and observe documenation, however expect to see documentation.
  • Examine docs/docstring-reference.rst and observe contiue, however expect to see continue.
  • Examine docs/parser.rst and observe contraints, however expect to see constraints.

Notes

Semi-automated issue generated by
https://github.com/timgates42/meticulous/blob/master/docs/NOTE.md

To avoid wasting CI processing resources a branch with the fix has been
prepared but a pull request has not yet been created. A pull request fixing
the issue can be prepared from the link below, feel free to create it or
request @timgates42 create the PR. Alternatively if the fix is undesired please
close the issue with a small comment about the reasoning.

https://github.com/timgates42/clize/pull/new/bugfix_typos

Thanks.

@argument_decorator decorated functions are not reusable

Salut @epsy. 👋

It seems that if you have two @argument_decorator decorated functions and want to use one inside the other, it's currently impossible.

Intuitively, I'd have expected it to work by:

  • either calling one function from the other, which doesn't work because the function signature is modified;
@argument_decorator
def foo(bar):
    return bar

@argument_decorator
def baz(qux):
    return foo(qux)
  • or by reusing the other as an argument, à la pytest fixtures fashion, which doesn't seem implemented.
@argument_decorator
def foo(bar):
    return bar

@argument_decorator
def baz(qux: foo):
    return qux

What's your recommandation on this? Would you see yourself implement one of those two examples?

Use `Annotated` to add aliases to options

The documentation gives an example of how to create aliases:

def echo(*text:Parameter.REQUIRED,
         prefix:'p'='', suffix:'s'='', reverse:'r'=False, repeat:'n'=1):
    ...

This, however, causes most linters to warn:

Unresolved reference 'p'

for all the aliases thus defined.

Since Python 3.9, typing.Annotated is available to add metadata to annotations, so this could be defined as

def echo(*text:Parameter.REQUIRED,
         prefix: Annotated[str, 'p'] ='', 
         suffix: Annotated[str, 's'] = '', 
         reverse: Annotated[str, 'r'] = False, 
         repeat: Annotated[int, 'n'] = 1,
):
    ...

and linters wouldn't complain.

KeyError when decorator rewrites varargs

Hi,

$ pip show clize
Name: clize
Version: 4.0.3
Summary: Turn functions into command-line interfaces
Home-page: https://github.com/epsy/clize
Author: Yann Kaiser
Author-email: [email protected]
License: MIT
Location: .../lib/python3.6/site-packages
Requires: docutils, six, sigtools, od, attrs

Here is a sample program to reproduce the traceback.

from sigtools.wrappers import decorator
from clize import run


@decorator
def f(a, *args, name=False, **kwargs):
    args = args
    return a(*args, **kwargs)


@f
def g():
    pass


run(g)

Here is the crash :

$ python -m pdb my-sigtool.py  --help
Traceback (most recent call last):
  File "my-sigtool.py", line 16, in <module>
    run(g)
  File ".../lib/python3.6/site-packages/sigtools/modifiers.py", line 158, in __call__
    return self.func(*args, **kwargs)
  File ".../lib/python3.6/site-packages/clize/runner.py", line 360, in run
    ret = cli(*args)
  File ".../lib/python3.6/site-packages/clize/runner.py", line 220, in __call__
    return func(*posargs, **kwargs)
  File ".../lib/python3.6/site-packages/clize/runner.py", line 220, in __call__
    return func(*posargs, **kwargs)
  File ".../lib/python3.6/site-packages/sigtools/modifiers.py", line 158, in __call__
    return self.func(*args, **kwargs)
  File ".../lib/python3.6/site-packages/clize/help.py", line 887, in cli
    help = self.get_help()
  File ".../lib/python3.6/site-packages/clize/help.py", line 896, in get_help
    return self.builder(self.subject, self.owner)
  File ".../lib/python3.6/site-packages/clize/help.py", line 449, in from_subject
    ret.add_from_parameter_sources(subject)
  File ".../lib/python3.6/site-packages/clize/help.py", line 491, in add_from_parameter_sources
    for func in func_signature.sources[pname]:
KeyError: 'args'
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> .../lib/python3.6/site-packages/clize/help.py(491)add_from_parameter_sources()
-> for func in func_signature.sources[pname]:
(Pdb) pp func_signature.sources
{'+depths': {<function g at 0x7f700f11f950>: 3,
             <function _SimpleWrapped.__call__ at 0x7f70113f89d8>: 0,
             <function f at 0x7f7011d7d950>: 2,
             functools.partial(<function f at 0x7f7011d7d950>, <function g at 0x7f700f11f950>): 1},
 'name': [<function f at 0x7f7011d7d950>]}
(Pdb)

I expect a more insightful error. I'm wondering wheither the bug is in clize or sigtools.

I found a workaround : i edit kwargs instead of varargs.

clize.converters.file always uses default value

The following code doesn't work as expected when a value is provided:

from clize import run
from clize.converters import file

def write_config(*args, output: file(mode='w')='-'):
    """Write out a sample config file.

    :param output: the path to the file to write.
    """
    with output as fp:
        fp.write("hi!\n")

if __name__ == '__main__':
    run(write_config)

What I expected was the default would be not be used, but in this case it is always used even if I pass a parameter. For example:

$ python test.py --output=/tmp/test
hi!

AFAICS changes made to clize.parser.CliBoundArguments in c2b5b85 have created the problem.

Unexpected behaviour with capitals in function names

Hi team, I have tried getting my head around using clize and I came across unexpected behaviour with function names that use capitals. For a start, what is the rational for altering the function names, ie. converting all to lower case and replying underscores? To me it defeats the point of having a CLI that corresponds to my code as closely as possible. I'm guessing it's a Windows related problem. But here is the buggy thing about it. Mini example from the docs, with a camel-cased function name:

import clize
def addThis():
    return "OK I will remember that."

def list_():
    """Lists the existing entries."""
    return "Sorry I forgot it all :("

if __name__=='__main__':
    clize.run(addThis, list_)

On the command line:

$> python testclize.py --help
Usage: testclize.py command [args...]

Commands:
  addThis
  list      Lists the existing entries.

Great, addThis is a command. But then:

$> python testclize.py addThis
testclize.py: Unknwon command "addthis"
$> python testclize.py addthis
testclize.py: Unknwon command "addthis"

What am I missing here?
Tested on both py2.7 and 3.5 under Linux SLES 11.3 (64_x86).

Stringification of empty bound parameters fails with exception

Example:

>>> from clize import parser
>>> a=parser.CliSignature([])      
>>> b=a.read_arguments([], "thing")
>>> str(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/moshez/src/caparg/build/caparg/lib/python3.6/site-packages/attr/_make.py", line 814, in repr_
    for name in attr_names
  File "/home/moshez/src/caparg/build/caparg/lib/python3.6/site-packages/attr/_make.py", line 814, in <genexpr>
    for name in attr_names
AttributeError: 'CliBoundArguments' object has no attribute 'namedparam'

Expected instead useful string return value.

`python -m clize path.to.a.function` (Clize launcher)

Given that Clize now supports most decorators and docstring formats, the time seems right to introduce this idea again:

$ python -m clize some_module.some_function arguments for --that function

would be (roughly) equivalent to the Python code:

from some_module import some_function
some_function('arguments', 'for', that='function')

Additionally, an entry point can be added to setup so that you can use clize ... instead of python -m clize ....

Tips:

  • some_function may not be directly importable, e.g. some_module.SomeClass.some_classmethod
  • The Usage: line of the --help output needs to match what was entered.
  • You can most likely use a Clize CLI for this :)

Parameter post-converters

Currently Clize converts each Python parameter to a Parameter instance by calling a function and using its result:

  • Either the first annotation on that parameter if it is a parameter converter,
  • or the default converter

This makes it impossible to use parameters.multi with parameters.one_of, for instance. This is counter-intuitive and also quite unfortunate.

To fix this, the parameter converter should collect callables from the annotations designated as "post-converters". Once the parameter converter has completed creating a Parameter instance, it should post-process this parameter with each post-converter in order. Each post converters does some changes and returns a Parameter instance, much like decorators.

Example post-converters:

  • parameter.one_of, parameter.mapped, (both already existing as parameter converters)
  • argument_decorator, (already existing as parameter converter)
  • Something to change the name of the value in the help (argparse calls it metavar)
  • UNDOCUMENTED and LAST_OPTION could be refactored as post-converters.

The documentation will need additional clarification on the roles of parameter converters, post-converters and value converters, possibly with a drawing :)

Document name translation

The user reference should document the name translation that occurs when converting functions to CLIs. The tutorials should refer to this section when talking about option names and subcommand names. It should be highlighted that translation does not apply in parameter names in docstrings.

Initially reported as #17.

Support more docstring formats through napoleon

Currently Clize supports docstrings in docutils/Sphinx format and in its own legacy docstring format.

This could be done in one of two ways:

  • Sphinx includes a 'napoleon' extension that converts docstrings in Numpy and Google style to something that docutils can process. We may be able to use it for this purpose. If so we need to ask about API stability with them
  • If the above solution is too overkill, maybe we can implement our own converter.

Case-sensitive 1-character aliases are not allowed

This code in 1.py:

import clize

def main(*, update:'U'=False, uranus:'u'=False):
    pass

if __name__ == '__main__':
    clize.run(main)

produces this result:

> 1.py -h

ValueError: parameters '--update' and '--uranus' use a duplicate alias '-u'

In previous versions this worked.

Tested in Windows 7, Python 3.6.2, clize 4.0.3

Add asyncio bootstrapper decorator

The decorator would allow the use of async functions by running them using loop.run_until_complete. This should be implemented in a way that doesn't prevent execution if asyncio isn't present. (import within the decorator will do)

How to support custom types and typing?

I was trying to figure out from the docs how to support custom types (even pathlib's Path) as well as python's type hints. Any pointers would be very appreciated.

For type hints, it looks like the values after the colon (:) are being used by both clize and the typing library. I also want to use the typing library to indicate something is optional to the API, while also have it exposed with clize on the command line. For example:

from clize import run, Parameter
from typing import Optional

def double(*, number: ('n', Optional[int]) = 2):
    '''
    Doubles the number
    
    :param number: the number to double
    '''
    print(2 * number)

if __name__ == '__main__':
    run(double)

`typing.List[str]` not supported by Clize

I have a function definition with a parameter of type typing.List[str]. I'd like for this to be handled in the CLI as an argument that supports multiple entries either --foo arg1 arg2 arg3 or preferably --foo arg1 --foo arg2 --foo arg3. Is this possible with Clize? If so, what's the best way to accomplish this?

FR: pre-dispatch options

Say I have mycommand foo and mycommand bar. The way clize is written now, I have to write, when invoking mycommand, either ./mycommand foo --debug or ./mycommand bar --debug and can't write ./mycommand --debug foo. It'd be nice to be able to somehow specify common arguments that applied to all commands in a dispatch set.

Any way to give negative number as argument ?

Hi,
is there any way to set negative number as argument ?

# -*- coding: utf-8 -*-

from clize import ArgumentError, Parameter, run

def cli_main(arg:float, *, debug:'d'=False):
    """cli_main
    
    :param arg: arg parameter
    :param debug: debug flag
    """
    return (arg,debug)

if __name__ == '__main__' :
    run (cli_main)
    
>python cli.py --help
Usage: cli.py [OPTIONS] arg

cli_main

Arguments:
  arg           arg paramter (type: FLOAT)

Options:
  -d, --debug   debug flag

Other actions:
  -h, --help    Show the help

>python cli.py -10.0
cli.py: Unknown option '-1'
Usage: cli.py [OPTIONS] arg

Add intro for `repeated_test` in contribution guidelines

While repeated_test helps leverage large amounts of test cases succinctly, it is rather opaque to the uninitiated. The contribution guidelines for Clize should have an intro or link to such a document in repeated_test's documentation.

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.