Skip to content

Commit 8a440f2

Browse files
committed
solve alias+flag case with custom FlagAction
1 parent d101bca commit 8a440f2

File tree

1 file changed

+56
-43
lines changed

1 file changed

+56
-43
lines changed

traitlets/config/loader.py

+56-43
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from ipython_genutils.encoding import DEFAULT_ENCODING
1919
from six import text_type, string_types, PY3
2020
from traitlets.traitlets import (
21-
HasTraits, Container, List, Dict, Any,
21+
HasTraits, Container, List, Dict, Any, Undefined,
2222
)
2323

2424
#-----------------------------------------------------------------------------
@@ -853,14 +853,33 @@ def _kv_opt(traitname, opt_value):
853853
return m.groups()
854854

855855

856+
class _FlagAction(argparse.Action):
857+
"""ArgParse action to handle a flag"""
858+
def __init__(self, *args, **kwargs):
859+
self.flag = kwargs.pop('flag')
860+
self.alias = kwargs.pop('alias', None)
861+
kwargs['const'] = Undefined
862+
if not self.alias:
863+
kwargs['nargs'] = 0
864+
super(_FlagAction, self).__init__(*args, **kwargs)
865+
866+
def __call__(self, parser, namespace, values, option_string=None):
867+
key = option_string.lstrip('-')
868+
print('values %r' % values)
869+
if self.nargs == 0 or values is Undefined:
870+
namespace._flags.append(self.flag)
871+
else:
872+
setattr(namespace, self.alias, values)
873+
874+
856875
class KVArgParseConfigLoader(ArgParseConfigLoader):
857876
"""A config loader that loads aliases and flags with argparse,
858877
but will use KVLoader for the rest. This allows better parsing
859878
of common args, such as `ipython -c 'print 5'`, but still gets
860879
arbitrary config with `ipython --InteractiveShell.autoindent=False`"""
861880

862881
def _add_arguments(self, aliases=None, flags=None, classes=None):
863-
self.alias_flags = {}
882+
alias_flags = {}
864883
# print aliases, flags
865884
if aliases is None:
866885
aliases = self.aliases
@@ -869,6 +888,7 @@ def _add_arguments(self, aliases=None, flags=None, classes=None):
869888
if classes is None:
870889
classes = self.classes
871890
paa = self.parser.add_argument
891+
self.parser.set_defaults(_flags=[])
872892

873893
## An index of all container traits collected::
874894
#
@@ -892,61 +912,54 @@ def _add_arguments(self, aliases=None, flags=None, classes=None):
892912
argparse_traits[argname] = (trait, argparse_kwds)
893913
paa('--'+argname, **argparse_kwds)
894914

895-
for keys, traitname in aliases.items():
915+
for keys, (value, _) in flags.items():
896916
if not isinstance(keys, tuple):
897917
keys = (keys, )
898918
for key in keys:
899-
argparse_kwds = {'type': text_type, 'dest': traitname}
900-
## Is alias for a sequence-trait?
901-
#
902-
if traitname in argparse_traits:
903-
argparse_kwds.update(argparse_traits[traitname][1])
904-
if 'action' in argparse_kwds:
905-
## A flag+alias should have `nargs='?'` multiplicity,
906-
# but base config-property had 'append' multiplicity!
907-
#
908-
if key in flags:
909-
raise ArgumentError(
910-
"The alias `%s` for the 'append' sequence "
911-
"config-trait `%s` cannot be also a flag!'"
912-
% (key, traitname))
913-
else:
914-
if key in flags:
915-
argparse_kwds['nargs'] = '?'
919+
if key in self.aliases:
920+
alias_flags[self.aliases[key]] = value
921+
continue
916922
keys = ('-'+key, '--'+key) if len(key) is 1 else ('--'+key, )
917-
paa(*keys, **argparse_kwds)
923+
paa(*keys, action=_FlagAction, flag=value)
918924

919-
for keys, (value, _) in flags.items():
925+
for keys, traitname in aliases.items():
920926
if not isinstance(keys, tuple):
921927
keys = (keys, )
922928
for key in keys:
923-
if key in self.aliases:
924-
self.alias_flags[self.aliases[key]] = value
925-
continue
929+
argparse_kwds = {'type': text_type, 'dest': traitname}
930+
if traitname in argparse_traits:
931+
argparse_kwds.update(argparse_traits[traitname][1])
932+
if 'action' in argparse_kwds and traitname in alias_flags:
933+
# flag sets 'action', so can't have flag & alias with custom action
934+
# on the same name
935+
raise ArgumentError(
936+
"The alias `%s` for the 'append' sequence "
937+
"config-trait `%s` cannot be also a flag!'"
938+
% (key, traitname))
939+
if traitname in alias_flags:
940+
# alias and flag.
941+
# when called with 0 args: flag
942+
# when called with >= 1: alias
943+
argparse_kwds.setdefault('nargs', '?')
944+
argparse_kwds['action'] = _FlagAction
945+
argparse_kwds['flag'] = alias_flags[traitname]
946+
argparse_kwds['alias'] = traitname
926947
keys = ('-'+key, '--'+key) if len(key) is 1 else ('--'+key, )
927-
paa(*keys, action='append_const', dest='_flags', const=value)
948+
paa(*keys, **argparse_kwds)
928949

929950
def _convert_to_config(self):
930951
"""self.parsed_data->self.config, parse unrecognized extra args via KVLoader."""
931-
# remove subconfigs list from namespace before transforming the Namespace
932-
if '_flags' in self.parsed_data:
933-
subcs = self.parsed_data._flags
934-
del self.parsed_data._flags
935-
else:
936-
subcs = []
937-
938952
for k, v in vars(self.parsed_data).items():
939-
if v is None:
940-
# it was a flag that shares the name of an alias
941-
subcs.append(self.alias_flags[k])
942-
else:
943-
trait = self.argparse_traits.get(k)
944-
if trait:
945-
trait = trait[0]
946-
# eval the KV assignment
947-
self._exec_config_str(k, v, trait=trait)
953+
if k == '_flags':
954+
# _flags will be handled later
955+
continue
956+
trait = self.argparse_traits.get(k)
957+
if trait:
958+
trait = trait[0]
959+
# eval the KV assignment
960+
self._exec_config_str(k, v, trait=trait)
948961

949-
for subc in subcs:
962+
for subc in self.parsed_data._flags:
950963
self._load_flag(subc)
951964

952965
if self.extra_args:

0 commit comments

Comments
 (0)