Simple command line argument parser for Python
Project description
QuickParse
Simple command line argument parser for Python
Example
list_things.py
:
from quickparse import QuickParse
def list_things(a_list, quickparse):
if quickparse.numeric:
if isinstance(quickparse.numeric, tuple):
print(', '.join(map(str, a_list[:quickparse.numeric[-1]])))
else:
print(', '.join(map(str, a_list[:quickparse.numeric])))
else:
print("How many items? Give a numeric value like '-3'")
commands_config = {
'ls': list_things,
'': lambda: print("Command is missing, use 'ls'"),
}
things = ['apple', 'banana', 'blueberry', 'orange', 'pear', 'pineapple']
QuickParse(commands_config).execute(things)
Run it:
$ python list_things.py ls -5
apple, banana, blueberry, orange, pear
The way it works:
commands_config
tells QuickParse to look forls
as a command and calllist_things
on it - when no commands show help- QuickParse parses arguments as normal while
ls
is privileged as a command - QuickParse finds
-5
so it adds asquickparse.numeric = 5
(quickparse
being theQuickParse
instance that otherwise would come asquickparse = QuickParse(commands_config)
) - QuickParse sees
list_things
being associated tols
, soquickparse.execute(things)
calls it, passing on the arguments ofexecute(..)
- one positional argument in this case - since
list_things
expects a named argumentquickparse
, QuickParse makes sure it passes on the reference to its own instance ofquickparse
- if there are multiple numeric flags are given all are passed down with
quickparse.numeric
in a tuple
GNU Argument Syntax implementation with extensions
GNU Argument Syntax: https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
Extensions
Numeric '-' values
$ my_cmd -12
Numeric '+' values
$ my_cmd +12
Long '-' options - only with explicit config
$ my_cmd -list
By default it becomes -l -i -s -t
, but adding QuickParse(options_config = [ ('-list', ) ])
will stop unpacking.
Long '+' options by default
$ my_cmd +list
Equivalent options - using options_config
$ my_cmd -l
is equivalent to
$ my_cmd --list
if adding QuickParse(options_config = [ ('-l', '--list') ])
Command-subcommand hierarchy and function bindings - using commands_config
Defining a random sample from git
looks like this:
commands_config = {
'': do_show_help,
'commit': do_commit,
'log': do_log,
'stash': {
'': do_stash,
'list': do_stash_list,
}
}
options_config = [
('-a', '--all'),
]
QuickParse(commands_config, options_config).execute()
Commands are called according to commands_config.
That is $ git log -3
calls do_log
do_log
may look like this:
def do_log(quickparse):
print(get_log_entries()[:quickparse.numeric])
If there is a named argument in do_log
's signature called quickparse
, the instance coming from QuickParse(commands_config, options_config)
is passed down holding all the results of parsing.
Parsing happens by using the defaults and applying what options_config
adds to it.
Argument Formats
Argument Format | Example | Remarks |
---|---|---|
-<number> |
$ my_cmd -12 |
(default) |
+<number> |
$ my_cmd +12 |
(default) |
-<single_letter> |
$ my_cmd -x |
(default) |
+<single_letter> |
$ my_cmd +x |
(default) |
-<single_letter><value> |
$ my_cmd -nFoo |
unpacking is the default: -n -F -ooptions_config needs a type entry saying it expects a value (other than bool) |
+<single_letter><value> |
$ my_cmd +nFoo |
unpacking is the default: +n +F +ooptions_config needs a type entry saying it expects a value (other than bool) |
-<single_letter>=<value> |
$ my_cmd -n=Foo |
(default) |
+<single_letter>=<value> |
$ my_cmd +n=Foo |
(default) |
-<single_letter> <value> |
$ my_cmd -n Foo |
options_config needs a type entry saying it expects a value (other than bool) |
+<single_letter> <value> |
$ my_cmd +n Foo |
options_config needs a type entry saying it expects a value (other than bool) |
-<letters> |
$ my_cmd -abc |
unpacking is the default: -a -b -c if in options_config it's taken as -abc |
+<letters> |
$ my_cmd +abc |
unpacking is the default: +a +b +c if in options_config it's taken as +abc |
-<letters>=<value> |
$ my_cmd -name=Foo |
(default) |
+<letters>=<value> |
$ my_cmd +name=Foo |
(default) |
--<letters> |
$ my_cmd --list |
(default) |
--<letters>=<value> |
$ my_cmd --message=Bar |
(default) |
--<letters> <value> |
$ my_cmd --message Bar |
options_config needs a type entry saying it expects a value (other than bool) |
-- |
$ my_cmd -- --param-anyway |
parameters delimiter (default) |
<letters>
means [a-zA-Z] and '-'s not in the first place
An argument like '-a*' gets unpacked if...
- '-a' is not defined to expect a value
- the '*' part has only letters, not '-' or '='
How to change the interpretation of -swing
It can mean (default):
-s -w -i -n -g
or
-s wing
/ -s=wing
To acheve the latter make the parser aware that '-s' expects a str
value:
options_config = [
('-s', str),
]
Make the parser aware that an option expects a value after a space
Add type explicitly in options_config
.
For just getting as it is add str
.
How to define option types
Use build-in types like int
or float
, or create a callable that raises exceptions.
Using bool
is a special case: parser will not expect a value but explicitly adds an error if one provided.
How to add empty value to an option
--option=
Some commands support '-' as empty value like curl -C - -O http://domanin.com/
To avoid ambiguities this syntax is not supported.
Use --option=
instead.
How to define options
options_test.py
:
from quickparse import QuickParse
options_config = [
('-u', '--utc', '--universal'),
('-l', '--long'),
('-n', '--name', str),
]
quickparse = QuickParse(options_config=options_config)
print(f'quickparse.options: {quickparse.options}')
print(f'quickparse.errors: {quickparse.errors}')
Run it:
$ python options_test.py
quickparse.options: {}
quickparse.errors: {}
$ python options_test.py -u
quickparse.options: {'-u': True, '--utc': True, '--universal': True}
quickparse.errors: {}
$ python options_test.py -ul
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True}
quickparse.errors: {}
$ python options_test.py -uln
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True, '-n': True, '--name': True}
quickparse.errors: {'-n': {'type': 1, 'message': "No value got for '-n/--name' - validator: str"}, '--name': {'type': 1, 'message': "No value got for '-n/--name' - validator: str"}}
$ python options_test.py -ul -nthe_name
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True, '-n': 'the_name', '--name': 'the_name'}
quickparse.errors: {}
$ python options_test.py -ul -n the_name
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True, '-n': 'the_name', '--name': 'the_name'}
quickparse.errors: {}
$ python options_test.py -ul -n=the_name
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True, '-n': 'the_name', '--name': 'the_name'}
quickparse.errors: {}
$ python options_test.py -ul --name the_name
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True, '--name': 'the_name', '-n': 'the_name'}
quickparse.errors: {}
$ python options_test.py -ul --name=the_name
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True, '--name': 'the_name', '-n': 'the_name'}
quickparse.errors: {}
Test your command line arguments
quickparse_test_args.py
(committed in the repo):
from pprint import pformat
from quickparse import QuickParse
def do_show_help():
print("Executing 'do_show_help'...")
def do_commit():
print("Executing 'do_commit'...")
def do_log(quickparse):
print("Executing 'do_log'...")
def do_stash():
print("Executing 'do_stash'...")
def do_stash_list():
print("Executing 'do_stash_list'...")
commands_config = {
'': do_show_help,
'commit': do_commit,
'log': do_log,
'stash': {
'': do_stash,
'list': do_stash_list,
}
}
options_config = [
('-m', '--message', str),
('-p', '--patch'),
]
quickparse = QuickParse(commands_config, options_config)
print(f'Commands:\n{pformat(quickparse.commands)}')
print(f'Parameters:\n{pformat(quickparse.parameters)}')
print(f'Options:\n{pformat(quickparse.options)}')
print(f'\'-\' numeric argument:\n{pformat(quickparse.numeric)}')
print(f'\'+\' numeric argument:\n{pformat(quickparse.plusnumeric)}')
print(f'Functions to call:\n{pformat(quickparse.to_execute)}')
quickparse.execute()
Error handling
If the parser parameters 'commands_config' or 'options_config' are not valid, ValueError is rased from the underlying AssertionError.
If the arguments are not compliant with the config (e.g. no value provided for an option that requires one) then no exceptions are raised but an errors
list is populated on the QuickParse
object.
See the error object again from options_test.py
$ python options_test.py -uln
quickparse.options: {'-u': True, '--utc': True, '--universal': True, '-l': True, '--long': True, '-n': True, '--name': True}
quickparse.errors: {'-n': {'type': 1, 'message': "No value got for '-n/--name' - validator: str"}, '--name': {'type': 1, 'message': "No value got for '-n/--name' - validator: str"}}
quickparse.errors
dict is about validation of options. These are the types:
ERROR_TYPE_VALIDATION = 0
ERROR_VALUE_NOT_FOUND = 1
ERROR_INCOMPLETE_COMMAND = 2
quickparse.has_errors is also available to check if any errors occurred.
Validation
Well, I still need to elaborate the docs on this but here is a quick example snippet.
quickparse.validate({
'parameters': { 'mincount': 1, },
'options': {
'mandatory': '--branch',
'optional': '--stage',
},
'numeric': { 'maxcount': 0 },
'plusnumeric': { 'maxcount': 0 },
})
assert 'parameters.mincount' not in quickparse.errors, f'Add a target'
assert not quickparse.has_errors, '\n'.join(quickparse.error_messages)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for quickparse-0.9.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 4578732e6ecbf3dfb54f5900ac825c9548efc02606339c7fcc3d9071be3b8cf5 |
|
MD5 | 6b091819f5827bb033519dfad86e9edf |
|
BLAKE2b-256 | 266f60e2cb2845a6bc0cce6b24ce3a4a150e653c5ea89c8dc40fe11f76032a6e |