Clize 3.0b1: An argument parser that draws a CLI from your function sigature

Chris Angelico rosuav at gmail.com
Tue Apr 28 05:27:28 CEST 2015


On Tue, Apr 28, 2015 at 12:45 PM, Yann Kaiser <kaiser.yann at gmail.com> wrote:
> On Mon, 27 Apr 2015 at 17:04 Chris Angelico <rosuav at gmail.com> wrote:
>> Interesting. I've also been working on a simpler arg handling module;
>> maybe we can work together. The goals for mine are:
>>
>> * Each function should be as independent as possible.
>
>
> In Clize, commands are also just regular functions which have the desired
> amount of keyword-only parameters and annotations. They can still be run
> individually, be tested, and so forth.

To implement something like the examples I gave, though, you'd need to
enumerate the subcommand functions at the end. I'd like to be able to
not do that.

My inspiration came partly from Flask. I can build up a web site with
several separate files, where one file might define routes for all the
human-accessible content (the stuff that's designed for a web browser)
and a separate file could define the API endpoints (emitting JSON or
something). There's no need to have a single gathering point that
lists every function that needs to be called - they get squirreled
away by the decorator.

>> * Minimize repetition (of names, descriptions, etc)
>
>
> I try to minimize repetition, but the reality of things is that a parameter
> name could be repeated up to 4 times in Clize:
>
> * In the function's parameter list
> * In the function's docstring
> * If named explicitly, in a @-kwoargs decorator (not needed with Py3)
> * In an annotate decorator (again, not needed with Py2 compatibility isn't
> desired)
>
> Two of these wouldn't be relevant in a Python 3-only world (if it's just a
> shell script replacement, you can probably pretend to be in one for a
> moment), the remainder being normal things in a function. So I'm not doing
> too badly there. Descriptions are of course only written in the description
> :-)

A lot of my projects are Py3-only. I have no problem with that.

>> * Keep everything in the function signature
>
> I started taking this at heart with 3.0, after seeing the rather disgusting
> constructs that had to be used in Clize 2.x for specifying aliases and value
> converters. Now everything is annotations. Nothing in the docstring
> specifies behavior, and it isn't even read at all until --help is triggered.
> I intend to keep docstrings behavior-free because, well, they're strictly
> for documentation IMO.
>
> What I mean to say, is that I am definitely committed to keeping everything
> in the function signature. Probably even more than you :-)

:)

There's a spectrum of convenience:

1) The actual function signature - information that already exists.
Function and parameter names, basically.
2) Annotations, directly attached to the parameters.
3) Docstrings and decorators, adjacent to the 'def' statement.
4) Code elsewhere in the file.
5) Code or directives in a separate file.

Some of Clize stretches as far as level 4, and that's what I'd like to
pull up a bit. With docstringargs, the only things at level 4 are
generic setup - importing the module (unavoidable) and "if name is
main, do stuff" (also unavoidable unless you want to put in some major
MAJOR magic).

(I took this spectrum from the discussions surrounding PEP 484,
incidentally. I'm sure you won't be even *considering* having crucial
command-line parsing out in a separate file, but that's precisely what
type-hint stub files are.)

>> There's a demo file in the source repo, plus here are a couple of
>> actual usage examples:
>>
>> https://github.com/Rosuav/LetMeKnow/blob/master/letmeknow.py
>> https://github.com/MikeiLL/appension/blob/master/fore/database.py
>>
>> The latter is an existing module in an existing project, and it grew a
>> command-line interface with minimal changes, eg:
>>
>> https://github.com/MikeiLL/appension/commit/566f195
>>
>> Can we merge our plans and make a single module that's more likely to
>> be maintained long-term? No point over-duplicating!
>
>
> Agreed. I'm open to have more maintainers and to take input, but I have to
> admit that at this stage of development, I'm quite attached to Clize's
> existing codebase and features (although I'm always open to refactoring, and
> the test suite helps with that).

If Clize can implement something comparably simple to what I'm doing
in the above examples, I'd be happy to drop docstringargs in favour of
it. Basically, I didn't like how argparse code looked:

https://github.com/Rosuav/snippets/blob/master/snippets.py

(That file actually has a deliberate bug in it, which I intended to
use as a demo of 'git bisect', but so far, haven't had a chance to do
that with my students yet.)

Taking the 'put' subcommand as an example, we have these lines of code
to handle the CLI:

2: generic boilerplate (import)
10: function signature
11-15: docstring
33-35: generic boilerplate (setup)
39: repetition of function name and purpose
40-41: repetition of function arguments, now with their purposes
48-51: generic boilerplate (execution)
53: repetition of function name
54: repetition of function name
60-61: generic boilerplate (execution)

That's a lot of separate pieces. Here's the docstringargs equivalent:

https://github.com/Rosuav/snippets/blob/dsa/snippets.py

1-2: generic boilerplate (import/setup)
10: simple decorator
11: function signature
12-16: docstring
30-31: generic boilerplate (execution)

All the put-specific code is in one place - the signature, plus one
marker line above it, plus the docstring. It's not scattered all up
and down the entire program. Adding another command is simply a matter
of creating a new function. THAT is an important part of what I'm
looking for; I don't want to have to go dig through five different
places when I want to add or remove a subcommand.

Can you put together a Clize equivalent? I'm pretty sure it's going to
be remarkably close to what I want.

ChrisA



More information about the Python-list mailing list