Browse Source

partial re-write to allow better user customization

pull/240/head
chriskiehl 7 years ago
parent
commit
4c61c29b65
6 changed files with 451 additions and 219 deletions
  1. 471
      gooey/python_bindings/argparse_to_json.py
  2. 118
      gooey/python_bindings/config_generator.py
  3. 5
      gooey/python_bindings/constants.py
  4. 29
      gooey/python_bindings/gooey_decorator.py
  5. 47
      gooey/python_bindings/gooey_parser.py
  6. 0
      gooey/python_bindings/parser/gooey_parser.py

471
gooey/python_bindings/argparse_to_json.py

@ -1,237 +1,362 @@
""" """
Converts argparse parser actions into json "Build Specs" Converts argparse parser actions into json "Build Specs"
""" """
import functools
import pprint
import argparse import argparse
import os import os
import sys
from argparse import ( from argparse import (
_CountAction, _CountAction,
_HelpAction, _HelpAction,
_StoreConstAction, _StoreConstAction,
_StoreFalseAction, _StoreFalseAction,
_StoreTrueAction, _StoreTrueAction,
ArgumentParser, _SubParsersAction)
_SubParsersAction)
from collections import OrderedDict from collections import OrderedDict
from functools import partial from functools import partial
from itertools import chain from uuid import uuid4
import sys from gooey.util.functional import merge, getin
VALID_WIDGETS = ( VALID_WIDGETS = (
'FileChooser', 'FileChooser',
'MultiFileChooser', 'MultiFileChooser',
'FileSaver', 'FileSaver',
'DirChooser', 'DirChooser',
'DateChooser', 'DateChooser',
'TextField', 'TextField',
'Dropdown', 'Dropdown',
'Counter', 'Counter',
'RadioGroup', 'RadioGroup',
'CheckBox', 'CheckBox',
'MultiDirChooser', 'MultiDirChooser',
'Textarea', 'Textarea',
'PasswordField', 'PasswordField',
'Listbox' 'Listbox'
) )
class UnknownWidgetType(Exception): class UnknownWidgetType(Exception):
pass pass
class UnsupportedConfiguration(Exception): class UnsupportedConfiguration(Exception):
pass pass
{ group_defaults = {
'siege': { 'columns': 2,
'command': 'siege', 'padding': 10,
'display_name': 'Siege', 'show_border': False
'contents': []
}
} }
item_default = {
'error_color': '#ea7878',
'validator': {
'type': 'local',
'test': 'lambda x: True',
'message': ''
},
'external_validator': {
'cmd': '',
}
}
def convert(parser):
widget_dict = getattr(parser, 'widgets', {})
actions = parser._actions
if has_subparsers(actions):
if has_required(actions):
raise UnsupportedConfiguration("Gooey doesn't currently support required arguments when subparsers are present.")
layout_type = 'column'
layout_data = OrderedDict(
(choose_name(name, sub_parser), {
'command': name,
'contents': process(sub_parser, getattr(sub_parser, 'widgets', {}))
}) for name, sub_parser in get_subparser(actions).choices.items())
else:
layout_type = 'standard'
layout_data = OrderedDict([
('primary', {
'command': None,
'contents': process(parser, widget_dict)
})
])
return { def convert(parser, **kwargs):
'layout_type': layout_type, assert_subparser_constraints(parser)
'widgets': layout_data x = {
} 'layout': 'standard',
'widgets': OrderedDict(
(choose_name(name, sub_parser), {
'command': name,
'contents': process(sub_parser,
getattr(sub_parser, 'widgets', {}),
getattr(sub_parser, 'options', {}))
}) for name, sub_parser in iter_parsers(parser))
}
if kwargs.get('use_legacy_titles'):
return apply_default_rewrites(x)
return x
def process(parser, widget_dict, options):
mutex_groups = parser._mutually_exclusive_groups
raw_action_groups = [extract_groups(group) for group in parser._action_groups
if group._group_actions]
corrected_action_groups = reapply_mutex_groups(mutex_groups, raw_action_groups)
return categorize2(strip_empty(corrected_action_groups), widget_dict, options)
def strip_empty(groups):
return [group for group in groups if group['items']]
def assert_subparser_constraints(parser):
if has_subparsers(parser._actions):
if has_required(parser._actions):
raise UnsupportedConfiguration(
"Gooey doesn't currently support top level required arguments "
"when subparsers are present.")
def iter_parsers(parser):
''' Iterate over name, parser pairs '''
try:
return get_subparser(parser._actions).choices.items()
except:
return iter([('::gooey/default', parser)])
def extract_groups(action_group):
'''
Recursively extract argument groups and associated actions
from ParserGroup objects
'''
return {
'name': action_group.title,
'description': action_group.description,
'items': [action for action in action_group._group_actions
if not is_help_message(action)],
'groups': [extract_groups(group)
for group in action_group._action_groups],
'options': merge(group_defaults,
getattr(action_group, 'gooey_options', {}))
}
def process(parser, widget_dict):
mutually_exclusive_groups = [
[mutex_action for mutex_action in group_actions._group_actions]
for group_actions in parser._mutually_exclusive_groups]
group_options = list(chain(*mutually_exclusive_groups))
base_actions = [action for action in parser._actions
if action not in group_options
and action.dest != 'help']
required_actions = filter(is_required, base_actions)
optional_actions = filter(is_optional, base_actions)
return list(categorize(required_actions, widget_dict, required=True)) + \
list(categorize(optional_actions, widget_dict)) + \
list(map(build_radio_group, mutually_exclusive_groups))
def categorize(actions, widget_dict, required=False):
_get_widget = partial(get_widget, widgets=widget_dict)
for action in actions:
if is_standard(action):
yield as_json(action, _get_widget(action) or 'TextField', required)
elif is_choice(action):
yield as_json(action, _get_widget(action) or 'Dropdown', required)
elif is_flag(action):
yield as_json(action, _get_widget(action) or 'CheckBox', required)
elif is_counter(action):
_json = as_json(action, _get_widget(action) or 'Counter', required)
# pre-fill the 'counter' dropdown
_json['data']['choices'] = list(map(str, range(1, 11)))
yield _json
else:
raise UnknownWidgetType(action)
def get_widget(action, widgets): def apply_default_rewrites(spec):
supplied_widget = widgets.get(action.dest, None) top_level_subgroups = list(spec['widgets'].keys())
type_arg_widget = 'FileChooser' if action.type == argparse.FileType else None for subgroup in top_level_subgroups:
return supplied_widget or type_arg_widget or None path = ['widgets', subgroup, 'contents']
contents = getin(spec, path)
for group in contents:
if group['name'] == 'positional arguments':
group['name'] = 'Required Arguments'
if group['name'] == 'optional arguments':
group['name'] = 'Optional Arguments'
return spec
def contains_actions(a, b):
''' check if any actions(a) are present in actions(b) '''
return set(a).intersection(set(b))
def reapply_mutex_groups(mutex_groups, action_groups):
# argparse stores mutually exclusive groups independently
# of all other groups. So, they must be manually re-combined
# with the groups/subgroups to which they were originally declared
# in order to have them appear in the correct location in the UI.
#
# Order is attempted to be preserved by inserting the MutexGroup
# into the _actions list at the first occurrence of any item
# where the two groups intersect
def swap_actions(actions):
for mutexgroup in mutex_groups:
mutex_actions = mutexgroup._group_actions
if contains_actions(mutex_actions, actions):
# make a best guess as to where we should store the group
targetindex = actions.index(mutexgroup._group_actions[0])
# insert the _ArgumentGroup container
actions[targetindex] = mutexgroup
# remove the duplicated individual actions
return [action for action in actions
if action not in mutex_actions]
return actions
return [group.update({'items': swap_actions(group['items'])}) or group
for group in action_groups]
def categorize2(groups, widget_dict, options):
return [{
'name': group['name'],
'items': list(categorize(group['items'], widget_dict, options)),
'groups': categorize2(group['groups'], widget_dict, options),
'description': group['description'],
'options': group['options']
} for group in groups]
def categorize(actions, widget_dict, options):
_get_widget = partial(get_widget, widget_dict)
for action in actions:
if is_mutex(action):
yield build_radio_group(action, widget_dict, options)
elif is_standard(action):
yield action_to_json(action, _get_widget(action, 'TextField'), options)
elif is_choice(action):
yield action_to_json(action, _get_widget(action, 'Dropdown'), options)
elif is_flag(action):
yield action_to_json(action, _get_widget(action, 'CheckBox'), options)
elif is_counter(action):
_json = action_to_json(action, _get_widget(action, 'Counter'), options)
# pre-fill the 'counter' dropdown
_json['data']['choices'] = list(map(str, range(1, 11)))
yield _json
else:
raise UnknownWidgetType(action)
def get_widget(widgets, action, default):
supplied_widget = widgets.get(action.dest, None)
type_arg_widget = 'FileChooser' if action.type == argparse.FileType else None
return supplied_widget or type_arg_widget or default
def is_required(action): def is_required(action):
''' '''
_actions possessing the `required` flag and not implicitly optional _actions possessing the `required` flag and not implicitly optional
through `nargs` being '*' or '?' through `nargs` being '*' or '?'
''' '''
return not isinstance(action, _SubParsersAction) and (action.required == True and action.nargs not in ['*', '?']) return not isinstance(action, _SubParsersAction) and (
action.required == True and action.nargs not in ['*', '?'])
def is_mutex(action):
return isinstance(action, argparse._MutuallyExclusiveGroup)
def has_required(actions): def has_required(actions):
return list(filter(None, list(filter(is_required, actions)))) return list(filter(None, list(filter(is_required, actions))))
def is_subparser(action): def is_subparser(action):
return isinstance(action,_SubParsersAction) return isinstance(action, _SubParsersAction)
def has_subparsers(actions): def has_subparsers(actions):
return list(filter(is_subparser, actions)) return list(filter(is_subparser, actions))
def get_subparser(actions): def get_subparser(actions):
return list(filter(is_subparser, actions))[0] return list(filter(is_subparser, actions))[0]
def is_optional(action): def is_optional(action):
''' '''
_actions either not possessing the `required` flag or implicitly optional through `nargs` being '*' or '?' _actions either not possessing the `required` flag or implicitly optional
''' through `nargs` being '*' or '?'
return (not action.required) or action.nargs in ['*', '?'] '''
return (not action.required) or action.nargs in ['*', '?']
def is_choice(action): def is_choice(action):
''' action with choices supplied ''' ''' action with choices supplied '''
return action.choices return action.choices
def is_standard(action): def is_standard(action):
""" actions which are general "store" instructions. """ actions which are general "store" instructions.
e.g. anything which has an argument style like: e.g. anything which has an argument style like:
$ script.py -f myfilename.txt $ script.py -f myfilename.txt
""" """
boolean_actions = ( boolean_actions = (
_StoreConstAction, _StoreFalseAction, _StoreConstAction, _StoreFalseAction,
_StoreTrueAction _StoreTrueAction
) )
return (not action.choices return (not action.choices
and not isinstance(action, _CountAction) and not isinstance(action, _CountAction)
and not isinstance(action, _HelpAction) and not isinstance(action, _HelpAction)
and type(action) not in boolean_actions) and type(action) not in boolean_actions)
def is_flag(action): def is_flag(action):
""" _actions which are either storeconst, store_bool, etc.. """ """ _actions which are either storeconst, store_bool, etc.. """
action_types = [_StoreTrueAction, _StoreFalseAction, _StoreConstAction] action_types = [_StoreTrueAction, _StoreFalseAction, _StoreConstAction]
return any(list(map(lambda Action: isinstance(action, Action), action_types))) return any(list(map(lambda Action: isinstance(action, Action), action_types)))
def is_counter(action): def is_counter(action):
""" _actions which are of type _CountAction """ """ _actions which are of type _CountAction """
return isinstance(action, _CountAction) return isinstance(action, _CountAction)
def is_default_progname(name, subparser): def is_default_progname(name, subparser):
return subparser.prog == '{} {}'.format(os.path.split(sys.argv[0])[-1], name) return subparser.prog == '{} {}'.format(os.path.split(sys.argv[0])[-1], name)
def is_help_message(action):
return isinstance(action, _HelpAction)
def choose_name(name, subparser): def choose_name(name, subparser):
return name if is_default_progname(name, subparser) else subparser.prog return name if is_default_progname(name, subparser) else subparser.prog
def build_radio_group(mutex_group):
if not mutex_group:
return []
options = [
{
'display_name': mutex_arg.metavar or 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
]
def build_radio_group(mutex_group, widget_group, options):
return { return {
'id': str(uuid4()),
'type': 'RadioGroup', 'type': 'RadioGroup',
'cli_type': 'optional',
'group_name': 'Choose Option', 'group_name': 'Choose Option',
'required': False, 'required': mutex_group.required,
'data': options 'options': getattr(mutex_group, 'gooey_options', {}),
'data': {
'commands': [action.option_strings for action in mutex_group._group_actions],
'widgets': list(categorize(mutex_group._group_actions, widget_group, options))
}
} }
def as_json(action, widget, required): def action_to_json(action, widget, options):
if widget not in VALID_WIDGETS: if action.required:
raise UnknownWidgetType('Widget Type {0} is unrecognized'.format(widget)) # check that it's present and not just spaces
validator = 'user_input and not user_input.isspace()'
return { error_msg = 'This field is required'
'type': widget, else:
'required': required, # not required; do nothing;
'data': { validator = 'True'
'display_name': action.metavar or action.dest, error_msg = ''
'help': action.help, base = merge(item_default, {
'nargs': action.nargs or '', 'validator': {
'commands': action.option_strings, 'test': validator,
'choices': action.choices or [], 'message': error_msg
'default': clean_default(widget, action.default) },
})
return {
'id': action.option_strings[0] if action.option_strings else action.dest,
'type': widget,
'cli_type': choose_cli_type(action),
'required': action.required,
'data': {
'display_name': action.metavar or action.dest,
'help': action.help,
'required': action.required,
'nargs': action.nargs or '',
'commands': action.option_strings,
'choices': action.choices or [],
'default': clean_default(action.default),
'dest': action.dest,
},
'options': merge(base, options.get(action.dest) or {})
} }
}
def clean_default(widget_type, default):
'''
Attemps to safely coalesce the default value down to
a valid JSON type.
See: Issue #147. def choose_cli_type(action):
function references supplied as arguments to the return 'positional' \
`default` parameter in Argparse cause errors in Gooey. if action.required and not action.option_strings \
''' else 'optional'
if widget_type != 'CheckBox':
return default.__name__ if callable(default) else default
# checkboxes must be handled differently, as they
# must be forced down to a boolean value
return default if isinstance(default, bool) else False
def clean_default(default):
'''
Attemps to safely coalesce the default value down to
a valid JSON type.
See: Issue #147.
function references supplied as arguments to the
`default` parameter in Argparse cause errors in Gooey.
'''
return default.__name__ if callable(default) else default

118
gooey/python_bindings/config_generator.py

@ -1,12 +1,28 @@
import os import os
import sys import sys
from gooey.gui.windows import layouts import warnings
from gooey.python_bindings import argparse_to_json from gooey.python_bindings import argparse_to_json
from gooey.gui.util.quoting import quote from gooey.gui.util.quoting import quote
from gooey.python_bindings import constants
default_layout = {
'widgets': [{
'type': 'CommandField',
'required': True,
'data': {
'display_name': 'Enter Commands',
'help': 'Enter command line arguments',
'nargs': '',
'commands': '',
'choices': [],
'default': None,
}
}],
}
def create_from_parser(parser, source_path, **kwargs): def create_from_parser(parser, source_path, **kwargs):
auto_start = kwargs.get('auto_start', False)
run_cmd = kwargs.get('target') run_cmd = kwargs.get('target')
if run_cmd is None: if run_cmd is None:
@ -15,32 +31,76 @@ def create_from_parser(parser, source_path, **kwargs):
else: else:
run_cmd = '{} -u {}'.format(quote(sys.executable), quote(source_path)) run_cmd = '{} -u {}'.format(quote(sys.executable), quote(source_path))
build_spec = { build_spec = {
'language': kwargs.get('language', 'english'), 'language': kwargs.get('language', 'english'),
'target': run_cmd, 'target': run_cmd,
'program_name': kwargs.get('program_name') or os.path.basename(sys.argv[0]).replace('.py', ''), 'program_name': kwargs.get('program_name') or os.path.basename(sys.argv[0]).replace('.py', ''),
'program_description': kwargs.get('program_description', ''), 'program_description': kwargs.get('program_description') or '',
'auto_start': kwargs.get('auto_start', False), 'sidebar_title': kwargs.get('sidebar_title', 'Actions'),
'show_advanced': kwargs.get('advanced', True), 'default_size': kwargs.get('default_size', (610, 530)),
'default_size': kwargs.get('default_size', (610, 530)), 'auto_start': kwargs.get('auto_start', False),
'num_required_cols': kwargs.get('required_cols', 1), 'show_advanced': kwargs.get('advanced', True),
'num_optional_cols': kwargs.get('optional_cols', 3), 'run_validators': kwargs.get('run_validators', True),
'manual_start': False, 'encoding': kwargs.get('encoding', 'utf-8'),
'layout_type': 'flat', 'show_stop_warning': kwargs.get('show_stop_warning', True),
'monospace_display': kwargs.get('monospace_display', False), 'show_success_modal': kwargs.get('show_success_modal', True),
'image_dir': kwargs.get('image_dir'), 'force_stop_is_error': kwargs.get('force_stop_is_error', True),
'language_dir': kwargs.get('language_dir'), 'poll_external_updates':kwargs.get('poll_external_updates', False),
'progress_regex': kwargs.get('progress_regex'), # Legacy/Backward compatibility interop
'progress_expr': kwargs.get('progress_expr'), 'use_legacy_titles': kwargs.get('use_legacy_titles', True),
'disable_progress_bar_animation': kwargs.get('disable_progress_bar_animation'), 'num_required_cols': kwargs.get('required_cols', 1),
'disable_stop_button': kwargs.get('disable_stop_button'), 'num_optional_cols': kwargs.get('optional_cols', 3),
'group_by_type': kwargs.get('group_by_type', True) 'manual_start': False,
} 'monospace_display': kwargs.get('monospace_display', False),
'image_dir': kwargs.get('image_dir'),
if not auto_start: 'language_dir': kwargs.get('language_dir'),
build_spec['program_description'] = parser.description or build_spec['program_description'] 'progress_regex': kwargs.get('progress_regex'),
'progress_expr': kwargs.get('progress_expr'),
layout_data = argparse_to_json.convert(parser) if build_spec['show_advanced'] else layouts.basic_config.items() 'disable_progress_bar_animation': kwargs.get('disable_progress_bar_animation'),
build_spec.update(layout_data) 'disable_stop_button': kwargs.get('disable_stop_button'),
# Layouts
'navigation': kwargs.get('navigation', constants.SIDEBAR),
'show_sidebar': kwargs.get('show_sidebar', False),
'tabbed_groups': kwargs.get('tabbed_groups', False),
'group_by_type': kwargs.get('group_by_type', True),
# styles
'body_bg_color': kwargs.get('body_bg_color', '#f0f0f0'),
'header_bg_color': kwargs.get('header_bg_color', '#ffffff'),
'header_height': kwargs.get('header_height', 90),
'header_show_title': kwargs.get('header_show_title', True),
'header_show_subtitle': kwargs.get('header_show_subtitle', True),
'header_image_center': kwargs.get('header_image_center', False),
'footer_bg_color': kwargs.get('footer_bg_color', '#f0f0f0'),
'sidebar_bg_color': kwargs.get('sidebar_bg_color', '#f2f2f2'),
# font family, weight, and size are determined at runtime
'terminal_panel_color': kwargs.get('terminal_panel_color', '#F0F0F0'),
'terminal_font_color': kwargs.get('terminal_font_color', '#000000'),
'terminal_font_family': kwargs.get('terminal_font_family', None),
'terminal_font_weight': kwargs.get('terminal_font_weight', None),
'terminal_font_size': kwargs.get('terminal_font_size', None),
'error_color': kwargs.get('error_color', '#ea7878')
}
if build_spec['monospace_display']:
warnings.warn('Gooey Option `monospace_display` is a legacy option.\n'
'See the terminal_font_x options for more flexible control '
'over Gooey\'s text formatting')
build_spec['program_description'] = parser.description or build_spec['program_description']
layout_data = (argparse_to_json.convert(parser, **build_spec)
if build_spec['show_advanced']
else default_layout.items())
build_spec.update(layout_data)
if len(build_spec['widgets']) > 1:
# there are subparsers involved
build_spec['show_sidebar'] = True
return build_spec return build_spec

5
gooey/python_bindings/constants.py

@ -0,0 +1,5 @@
SIDEBAR = 'SIDEBAR'
TABBED = 'TABBED'
INLINE = 'INLINE'
HIDDEN = 'HIDDEN'

29
gooey/python_bindings/gooey_decorator.py

@ -12,11 +12,13 @@ import sys
from argparse import ArgumentParser from argparse import ArgumentParser
from gooey.gui import application from gooey.gui import application
from gooey.gui.util.freeze import get_resource_path from gooey.gui.util.freeze import getResourcePath
from gooey.util.functional import merge
from . import config_generator from . import config_generator
IGNORE_COMMAND = '--ignore-gooey' IGNORE_COMMAND = '--ignore-gooey'
# TODO: kwargs all the things
def Gooey(f=None, def Gooey(f=None,
advanced=True, advanced=True,
language='english', language='english',
@ -25,24 +27,29 @@ def Gooey(f=None,
program_name=None, program_name=None,
program_description=None, program_description=None,
default_size=(610, 530), default_size=(610, 530),
use_legacy_titles=True,
required_cols=2, required_cols=2,
optional_cols=2, optional_cols=2,
dump_build_config=False, dump_build_config=False,
load_build_config=None, load_build_config=None,
monospace_display=False, # TODO: add this to the docs monospace_display=False, # TODO: add this to the docs
image_dir='default', image_dir='::gooey/default',
language_dir=get_resource_path('languages'), language_dir=getResourcePath('languages'),
progress_regex=None, # TODO: add this to the docs progress_regex=None, # TODO: add this to the docs
progress_expr=None, # TODO: add this to the docs progress_expr=None, # TODO: add this to the docs
disable_progress_bar_animation=False, disable_progress_bar_animation=False,
disable_stop_button=False, disable_stop_button=False,
group_by_type=True): # TODO: add this to the docs group_by_type=True,
header_height=80,
navigation='SIDEBAR', # TODO: add this to the docs
tabbed_groups=False,
**kwargs):
''' '''
Decorator for client code's main function. Decorator for client code's main function.
Serializes argparse data to JSON for use with the Gooey front end Serializes argparse data to JSON for use with the Gooey front end
''' '''
params = locals() params = merge(locals(), locals()['kwargs'])
def build(payload): def build(payload):
def run_gooey(self, args=None, namespace=None): def run_gooey(self, args=None, namespace=None):
@ -57,7 +64,11 @@ def Gooey(f=None,
sys.exit(1) sys.exit(1)
if not build_spec: if not build_spec:
build_spec = config_generator.create_from_parser(self, source_path, payload_name=payload.__name__, **params) build_spec = config_generator.create_from_parser(
self,
source_path,
payload_name=payload.__name__,
**params)
if dump_build_config: if dump_build_config:
config_path = os.path.join(os.getcwd(), 'gooey_config.json') config_path = os.path.join(os.getcwd(), 'gooey_config.json')

47
gooey/python_bindings/gooey_parser.py

@ -8,39 +8,64 @@ class GooeySubParser(_SubParsersAction):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(GooeySubParser, self).__init__(*args, **kwargs) super(GooeySubParser, self).__init__(*args, **kwargs)
# TODO: dedupe code # TODO: figure out how to correctly dispatch all of these
# so that the individual wrappers aren't needed
class GooeyArgumentGroup(_ArgumentGroup): class GooeyArgumentGroup(_ArgumentGroup):
def __init__(self, parser, widgets, *args, **kwargs): def __init__(self, parser, widgets, options, *args, **kwargs):
self.parser = parser self.parser = parser
self.widgets = widgets self.widgets = widgets
self.options = options
super(GooeyArgumentGroup, self).__init__(self.parser, *args, **kwargs) super(GooeyArgumentGroup, self).__init__(self.parser, *args, **kwargs)
def add_argument(self, *args, **kwargs): def add_argument(self, *args, **kwargs):
widget = kwargs.pop('widget', None) widget = kwargs.pop('widget', None)
metavar = kwargs.pop('metavar', None) metavar = kwargs.pop('metavar', None)
options = kwargs.pop('gooey_options', None)
super(GooeyArgumentGroup, self).add_argument(*args, **kwargs) super(GooeyArgumentGroup, self).add_argument(*args, **kwargs)
self.parser._actions[-1].metavar = metavar self.parser._actions[-1].metavar = metavar
self.widgets[self.parser._actions[-1].dest] = widget self.widgets[self.parser._actions[-1].dest] = widget
self.options[self.parser._actions[-1].dest] = options
def add_argument_group(self, *args, **kwargs):
options = kwargs.pop('gooey_options', {})
group = GooeyArgumentGroup(self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self._action_groups.append(group)
return group
def add_mutually_exclusive_group(self, *args, **kwargs):
options = kwargs.pop('gooey_options', {})
container = self
group = GooeyMutuallyExclusiveGroup(container, self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self.parser._mutually_exclusive_groups.append(group)
return group
class GooeyMutuallyExclusiveGroup(_MutuallyExclusiveGroup): class GooeyMutuallyExclusiveGroup(_MutuallyExclusiveGroup):
def __init__(self, parser, widgets, *args, **kwargs): def __init__(self, container, parser, widgets, options, *args, **kwargs):
self.parser = parser self.parser = parser
self.widgets = widgets self.widgets = widgets
super(GooeyMutuallyExclusiveGroup, self).__init__(self.parser, *args, **kwargs) self.options = options
super(GooeyMutuallyExclusiveGroup, self).__init__(container, *args, **kwargs)
def add_argument(self, *args, **kwargs): def add_argument(self, *args, **kwargs):
widget = kwargs.pop('widget', None) widget = kwargs.pop('widget', None)
metavar = kwargs.pop('metavar', None) metavar = kwargs.pop('metavar', None)
options = kwargs.pop('gooey_options', None)
super(GooeyMutuallyExclusiveGroup, self).add_argument(*args, **kwargs) super(GooeyMutuallyExclusiveGroup, self).add_argument(*args, **kwargs)
self.parser._actions[-1].metavar = metavar self.parser._actions[-1].metavar = metavar
self.widgets[self.parser._actions[-1].dest] = widget self.widgets[self.parser._actions[-1].dest] = widget
self.options[self.parser._actions[-1].dest] = options
class GooeyParser(object): class GooeyParser(object):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.__dict__['parser'] = ArgumentParser(**kwargs) self.__dict__['parser'] = ArgumentParser(**kwargs)
self.widgets = {} self.widgets = {}
self.options = {}
@property @property
def _mutually_exclusive_groups(self): def _mutually_exclusive_groups(self):
@ -57,6 +82,7 @@ class GooeyParser(object):
def add_argument(self, *args, **kwargs): def add_argument(self, *args, **kwargs):
widget = kwargs.pop('widget', None) widget = kwargs.pop('widget', None)
metavar = kwargs.pop('metavar', None) metavar = kwargs.pop('metavar', None)
options = kwargs.pop('gooey_options', None)
if widget and widget == 'Listbox': if widget and widget == 'Listbox':
if not 'nargs' in kwargs or kwargs['nargs'] not in ['*', '+']: if not 'nargs' in kwargs or kwargs['nargs'] not in ['*', '+']:
@ -67,15 +93,20 @@ class GooeyParser(object):
self.parser.add_argument(*args, **kwargs) self.parser.add_argument(*args, **kwargs)
self.parser._actions[-1].metavar = metavar self.parser._actions[-1].metavar = metavar
self.widgets[self.parser._actions[-1].dest] = widget self.widgets[self.parser._actions[-1].dest] = widget
self.options[self.parser._actions[-1].dest] = options
def add_mutually_exclusive_group(self, **kwargs): def add_mutually_exclusive_group(self, *args, **kwargs):
group = GooeyMutuallyExclusiveGroup(self.parser, self.widgets, **kwargs) options = kwargs.pop('gooey_options', {})
group = GooeyMutuallyExclusiveGroup(self, self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self.parser._mutually_exclusive_groups.append(group) self.parser._mutually_exclusive_groups.append(group)
return group return group
def add_argument_group(self, *args, **kwargs): def add_argument_group(self, *args, **kwargs):
group = GooeyArgumentGroup(self.parser, self.widgets, **kwargs) options = kwargs.pop('gooey_options', {})
self.parser.add_argument_group(*args, **kwargs) group = GooeyArgumentGroup(self.parser, self.widgets, self.options, *args, **kwargs)
group.gooey_options = options
self.parser._action_groups.append(group)
return group return group
def parse_args(self, args=None, namespace=None): def parse_args(self, args=None, namespace=None):

0
gooey/python_bindings/parser/gooey_parser.py

|||||||
100:0
Loading…
Cancel
Save