mirror of https://github.com/chriskiehl/Gooey.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
5.6 KiB
209 lines
5.6 KiB
"""
|
|
Converts argparse parser actions into json "Build Specs"
|
|
"""
|
|
|
|
from argparse import (
|
|
_CountAction,
|
|
_HelpAction,
|
|
_StoreConstAction,
|
|
_StoreFalseAction,
|
|
_StoreTrueAction,
|
|
ArgumentParser)
|
|
import argparse
|
|
import itertools
|
|
|
|
|
|
VALID_WIDGETS = (
|
|
'FileChooser',
|
|
'MultiFileChooser',
|
|
'FileSaver',
|
|
'DirChooser',
|
|
'DateChooser',
|
|
'TextField',
|
|
'Dropdown',
|
|
'Counter',
|
|
'RadioGroup',
|
|
'CheckBox'
|
|
)
|
|
|
|
class UnknownWidgetType(Exception):
|
|
pass
|
|
|
|
|
|
def convert(parser):
|
|
|
|
widget_dict = getattr(parser, 'widgets', {})
|
|
|
|
mutually_exclusive_group = [
|
|
mutex_action
|
|
for group_actions in parser._mutually_exclusive_groups
|
|
for mutex_action in group_actions._group_actions]
|
|
|
|
|
|
base_actions = [(action, get_widget(action, widget_dict))
|
|
for action in parser._actions
|
|
if action not in mutually_exclusive_group]
|
|
|
|
|
|
|
|
positional_args = get_required_and_positional_args(base_actions)
|
|
|
|
choice_args = get_optionals_with_choices(base_actions)
|
|
standard_args = get_optionals_without_choices(base_actions)
|
|
counter_args = get_counter_style_optionals(base_actions)
|
|
radio_args = get_mutually_exclusive_optionals(mutually_exclusive_group)
|
|
checkable_args = get_flag_style_optionals(base_actions)
|
|
|
|
return {
|
|
'required': positional_args,
|
|
'optional': list(itertools.chain(
|
|
get_optionals_with_choices(base_actions),
|
|
get_optionals_without_choices(base_actions),
|
|
get_counter_style_optionals(base_actions),
|
|
get_mutually_exclusive_optionals(mutually_exclusive_group),
|
|
get_flag_style_optionals(base_actions)
|
|
)),
|
|
}
|
|
|
|
|
|
def get_widget(action, widgets):
|
|
supplied_widget = widgets.get(action.dest, None)
|
|
type_arg_widget = 'FileChooser' if type(action.type) == type(argparse.FileType) else None
|
|
return supplied_widget or type_arg_widget or None
|
|
|
|
|
|
def get_required_and_positional_args(actions):
|
|
"""
|
|
Extracts positional or required args from the actions list
|
|
In argparse, positionals are defined by either an empty option_strings
|
|
or by the option_strings parameters being sans a leading hyphen
|
|
"""
|
|
filtered_actions = [(action, widget)
|
|
for action, widget in actions
|
|
if not action.option_strings
|
|
or action.required == True]
|
|
|
|
return [as_json(action, widget=widget or 'TextField')
|
|
for action, widget in filtered_actions]
|
|
|
|
|
|
def get_optionals_with_choices(actions):
|
|
"""
|
|
All optional arguments which are constrained
|
|
to specific choices.
|
|
"""
|
|
filtered_actions = [(action, widget)
|
|
for action, widget in actions
|
|
if action.choices]
|
|
|
|
return [as_json(action, widget=widget or 'Dropdown')
|
|
for action, widget in filtered_actions]
|
|
|
|
|
|
def get_optionals_without_choices(actions):
|
|
"""
|
|
All actions which are:
|
|
(a) Optional, but without required choices
|
|
(b) Not of a "boolean" type (storeTrue, etc..)
|
|
(c) Of type _AppendAction
|
|
|
|
e.g. anything which has an argument style like:
|
|
>>> -f myfilename.txt
|
|
"""
|
|
boolean_actions = (
|
|
_StoreConstAction, _StoreFalseAction,
|
|
_StoreTrueAction
|
|
)
|
|
filtered_actions = [
|
|
(action, widget)
|
|
for action, widget in actions
|
|
if action.option_strings
|
|
and not action.choices
|
|
and not isinstance(action, _CountAction)
|
|
and not isinstance(action, _HelpAction)
|
|
and type(action) not in boolean_actions
|
|
]
|
|
|
|
return [as_json(action, widget=widget or 'TextField')
|
|
for action, widget in filtered_actions]
|
|
|
|
|
|
def get_flag_style_optionals(actions):
|
|
"""
|
|
Gets all instances of "flag" type options.
|
|
i.e. options which either store a const, or
|
|
store boolean style options (e.g. StoreTrue).
|
|
Types:
|
|
_StoreTrueAction
|
|
_StoreFalseAction
|
|
_StoreConst
|
|
"""
|
|
filtered_actions = [
|
|
(action, widget)
|
|
for action, widget in actions
|
|
if isinstance(action, _StoreTrueAction)
|
|
or isinstance(action, _StoreFalseAction)
|
|
or isinstance(action, _StoreConstAction)
|
|
]
|
|
|
|
return [as_json(action, widget=widget or 'CheckBox')
|
|
for action, widget in filtered_actions]
|
|
|
|
|
|
def get_counter_style_optionals(actions):
|
|
"""
|
|
Returns all instances of type _CountAction
|
|
"""
|
|
filtered_actions = [(action, widget)
|
|
for action, widget in actions
|
|
if isinstance(action, _CountAction)]
|
|
|
|
_json_options = [as_json(action, widget=widget or 'Counter')
|
|
for action, widget in filtered_actions]
|
|
|
|
# Counter should show as Dropdowns, so pre-populare with numeric choices
|
|
for opt in _json_options:
|
|
opt['choices'] = range(10)
|
|
return _json_options
|
|
|
|
|
|
def get_mutually_exclusive_optionals(mutex_group):
|
|
if not mutex_group:
|
|
return []
|
|
|
|
options = [
|
|
{
|
|
'display_name': mutex_arg.dest,
|
|
'help': mutex_arg.help,
|
|
'nargs': mutex_arg.nargs or '',
|
|
'commands': mutex_arg.option_strings,
|
|
'choices': mutex_arg.choices,
|
|
} for mutex_arg in mutex_group
|
|
]
|
|
|
|
return [{
|
|
'type': 'RadioGroup',
|
|
'group_name': 'Choose Option',
|
|
'data': options
|
|
}]
|
|
|
|
|
|
def as_json(action, widget):
|
|
if widget not in VALID_WIDGETS:
|
|
raise UnknownWidgetType('Widget Type {0} is unrecognized'.format(widget))
|
|
|
|
option_strings = action.option_strings
|
|
return {
|
|
'type': widget,
|
|
'data': {
|
|
'display_name': action.dest,
|
|
'help': action.help,
|
|
'nargs': action.nargs or '',
|
|
'commands': action.option_strings,
|
|
'choices': action.choices or [],
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|