sander76 / clipstick Goto Github PK
View Code? Open in Web Editor NEWcreate cli applications using pydantic models
Home Page: https://sander76.github.io/clipstick/
License: MIT License
create cli applications using pydantic models
Home Page: https://sander76.github.io/clipstick/
License: MIT License
Put here as a placeholder to remind me of refactoring the parsing part.
There are several places where it can be improved.
In this case:
# clipstick_cmds.py
from clipstick import parse
from pydantic import BaseModel
class AlfaCmd(BaseModel):
"""Hacer cosas de alfa"""
uno: str
"""Parámetro para alfa"""
def run(self):
print(f"alfa {self.uno}")
class BetaCmd(BaseModel):
"""Hacer cosas de beta"""
dos: str
"""Parámetro para beta"""
def run(self):
print(f"beta {self.dos}")
class CmdApp(BaseModel):
"""Herramienta de test"""
subcmd: AlfaCmd | BetaCmd
def run(self):
self.subcmd.run()
if __name__ == "__main__":
app = parse(CmdApp)
app.run()
the resulting app returns this to the help request:
$ python clipstick_cmds.py -h
Usage: clipstick_cmds.py [Subcommands]
Herramienta de test
Subcommands:
alfa-cmd Hacer cosas de alfa
beta-cmd Hacer cosas de beta
The command name is taken from the class name. It would be useful to have the ability to add one or more shorter or different alias(es) to the commands.
expecting to see the types to be displayed in help, but they are not.
Something like the below works:
description: Optional[str] = None
Something like the below doesn't work (supported by 3.11 which is equivalent)
description: str | None = None
which throws the error ERROR: A union composing a subcommand must all be of type BaseModel
Hi @sander76, I'am trying clipstick for the first time. Love the feeling, elegant & minimal. I take my 🎩 off!
The issue I am facing, I don't recall seeing in the docs, how do I handle the optional argument if the default value is None
.
With good old argparse I'd write
parser = argparse.ArgumentParser()
parser.add_argument('--effort', '-e', type=int, required=False)
args = parser.parse_args()
effective type of args.effort
would be int | None
With clipstick
I do
# test.py
from clipstick import parse
from pydantic import BaseModel
class Arguments(BaseModel):
effort: int | None = None
def main() -> None:
print(parse(Arguments))
if __name__ == '__main__':
main()
python -m test -h
Parsing fails with following output
Usage: test.py [Options]
Options:
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/home/kolia/git/sandbox/src/test.py", line 14, in <module>
main()
File "/home/kolia/git/sandbox/src/test.py", line 10, in main
print(parse(Arguments))
^^^^^^^^^^^^^^^^
File "/home/kolia/.cache/pypoetry/virtualenvs/sandbox-y84fort7-py3.11/lib/python3.11/site-packages/clipstick/_clipstick.py", line 45, in parse
success, idx = root_node.match(0, args)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/kolia/.cache/pypoetry/virtualenvs/sandbox-y84fort7-py3.11/lib/python3.11/site-packages/clipstick/_tokens.py", line 501, in match
_help.help(self)
File "/home/kolia/.cache/pypoetry/virtualenvs/sandbox-y84fort7-py3.11/lib/python3.11/site-packages/clipstick/_help.py", line 99, in help
tbl.add_row("", *_help_from_token(kwarg.help()))
^^^^^^^^^^^^
File "/home/kolia/.cache/pypoetry/virtualenvs/sandbox-y84fort7-py3.11/lib/python3.11/site-packages/clipstick/_tokens.py", line 182, in help
"type": self.field_info.annotation.__name__
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'types.UnionType' object has no attribute '__name__'. Did you mean: '__ne__'?
Give this it's own section in the docs
Sometimes (many times ?) it is unncecessary to display typing info in your help as the help text (the docstring) itself is descriptive enough to not need the typing information.
current:
Usage: dummy-entrypoint [Arguments]
None
Arguments:
-no-a/-a/--add-flag/--no-add-flag [bool]
must become
Usage: dummy-entrypoint [Arguments]
None
Arguments:
-no-a/-a --add-flag/--no-add-flag [bool] #<-- notice removal of forward slash
Look at the examples/name.py
file and run it with python examples/name.py --age too-old
.
This should raise some kind of conversion error ("too-old" cannot be converted to an int). But is not.
Currently a subcommand is defined as a list, indicating there might be more than one.
But clipstick allows only one subcommand per model.
divide a multiline docstring into two parts. Short part (as a summary) and a long part.
This can be used in help output.
The clone
subcommand in the example below should only have the summary help. The long part should be omitted here.
❯ python examples/subcommand.py -h
Usage: subcommand.py [Subcommands]
My git tool.
Subcommands:
clone Clone a repo.
This is a subcommand.
merge Merge a branch.
when you are calling your cli as a module which has a __main__.py
entrypoint your help output shows like this:
Usage: __main__.py [Arguments] [Options]
which is not very descriptive. An alternative is preferred.
I don't know how click/typer/argparse manage this, but it could serve as inspiration.
Nested models require a dictionary which cannot be parsed via command line
Consider the first example in the readme. The second line of the docstring in the cli output is indented and should not be.
Have a look here: https://sander76.github.io/clipstick/usage.html#positional-arguments
and observe the error message when no argument is provided. the "user entered" seems to be out of place.
Consider this code:
from cli.Config import Load as LoadConfig
class Main(BaseModel):
sub_command: LoadConfig | Delete # the LoadConfig is important here.
In help output the original "load" command is used instead of "load-config"
Given
# adder1.py
from clipstick import parse
from pydantic import BaseModel
class Adder(BaseModel):
ints: int
def run(self):
print(sum([self.ints]))
cmd = parse(Adder)
cmd.run()
this runs OK:
$ adder1.py 10
10
but this
# adder2.py
from clipstick import parse
from pydantic import BaseModel
class Adder(BaseModel):
ints: list[int]
def run(self):
print(sum(self.ints))
cmd = parse(Adder)
cmd.run()
fails for multiple arguments:
$ adder2.py 10 20
ERROR:
Missing a value for positional argument '--ints'
and requires
$ adder2.py --ints 10 --ints 20
30
It would be desirable that positional arguments for a collection could be passed just as values.
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.