How to get arguments for 2 different objects from the command line when there is a naming conflict in Python argparse -


i have 2 classes, , b, each have own argument parser defined (using argparse) want add functionality a, calls class b. doing using composition (i.e has instance of object b)

i asked here how combine 2 arg parse objects, argparsea include arguments in argparseb in question can 2 python argparse objects combined? problem follows: both , b have arguments same names. but- need 2 different values entered user (ie. argpasea.val1 needs values argparsea.val1 , argparseb.val1)

(the obvious solution renaming val1 in either argparsea or argpaseb, there on 50 scripts inherit class a, , 50 scripts inherit class b, want changes , b minimal possible.)

i thought of adding new , differently named argument argpasea called val2, can passed argparseb val1.

my question is- proper way of doing such conversion or arguments argparsea argparseb?

or there better way design this?

i'm going guess trying parents suggestion, , illustrate might going on. if you've adopted approach may help.

import argparse  parsera=argparse.argumentparser() a1=parsera.add_argument('-f','--foo','--bar') print(a1) print()  parserb=argparse.argumentparser() b1=parserb.add_argument('-g','--goo','--bar') print(b1) b1.dest='bar'    # can change attributes dest after creation print()  # parser parents; not conflict_handler parserc=argparse.argumentparser(conflict_handler='resolve',     parents=[parsera, parserb]) print(parserc._actions)   # actions (arguments) of c print() parsera.print_help() print() parserc.print_help()   # uses c._actions 

which produces

1445:~/mypy$ python3 stack38071986.py  _storeaction(option_strings=['-f', '--foo', '--bar'], dest='foo',           nargs=none, const=none, default=none, type=none, choices=none, help=none, metavar=none)  _storeaction(option_strings=['-g', '--goo', '--bar'], dest='goo',      nargs=none, const=none, default=none, type=none, choices=none, help=none, metavar=none)  [_storeaction(option_strings=['-f', '--foo'], dest='foo', nargs=none,      const=none, default=none, type=none, choices=none, help=none, metavar=none),   _helpaction(option_strings=['-h', '--help'], dest='help', nargs=0,      const=none, default='==suppress==', type=none, choices=none, help='show message , exit', metavar=none),   _storeaction(option_strings=['-g', '--goo', '--bar'], dest='bar',      nargs=none, const=none, default=none, type=none, choices=none, help=none, metavar=none)]  usage: stack38071986.py [-f foo]  optional arguments:                 show message , exit   -f foo, --foo foo  usage: stack38071986.py [-f foo] [-h] [-g bar]  optional arguments:   -f foo, --foo foo   -h, --help            show message , exit   -g bar, --goo bar, --bar bar 

normal store behavior store value @ dest attribute in args namespace, setattr(namespace, action.dest, value). dest default first long option string (minus --), may set parameter (for optionals), or after creation.

when 1 or more option string (flag) of new action conflicts existing action, conflict_handler evoked. default handler raises error, here use resolve handler. tries remove enough of existing argument resolve conflict.

all 3 parsers create -h (help) action. resolve in parserc removes one. note [-h] missing parsera help.

the --bar defined in parserb conflicts same string in parsera. resolve has removed a's definition, -f , --foo remain.

the creation of parserc has messed other parsers; i'd recommend using parserc.parse_args() in run.

we write different conflict_handler method. resolve 1 has rough edges, , doesn't used often.

i using features aren't documented. consider unsafe. if want unusual behavior have accept risks. plus argparse documentation not last word on behavior, , easier change code itself. developers paranoid backward conflicts when making changes.

==================

here's stab @ customizing resolve conflict handler

import argparse  def pp(adict):     k in adict:         v=adict[k]         print('action %10s:'%k,v.option_strings, v.dest)  def new_resolve(self, action, conflicting_actions):     rename_dict={'--var':'--var1'}     option_string, action in conflicting_actions:         new_string = rename_dict.get(option_string, none)         if new_string:             # rename rather replace             print(action.option_strings)             action.option_strings = [new_string]             action.dest = new_string[2:]              pp(self._option_string_actions)             a1=self._option_string_actions.pop(option_string, none)             print(a1)             self._option_string_actions[new_string] = a1             pp(self._option_string_actions)          else:             # regular remove action             action.option_strings.remove(option_string)             self._option_string_actions.pop(option_string, none)              # if option has no option string, remove             # container holding             if not action.option_strings:                 action.container._remove_action(action)  argparse._actionscontainer._handle_conflict_resolve=new_resolve  parsera=argparse.argumentparser() a1=parsera.add_argument('-f','--foo') a1=parsera.add_argument('--var')  parserb=argparse.argumentparser() b1=parserb.add_argument('-g','--goo') b1=parserb.add_argument('--var')  parserc=argparse.argumentparser(conflict_handler='resolve',    parents=[parsera, parserb],    add_help=false)  parsera.print_help() print() parserc.print_help() print(parserc.parse_args()) 

which produces

1027:~/mypy$ python3 stack38071986.py --var1 1 --var 3 ['--var'] action      --var: ['--var1'] var1 action         -g: ['-g', '--goo'] goo action      --foo: ['-f', '--foo'] foo action         -h: ['-h', '--help'] action      --goo: ['-g', '--goo'] goo action     --help: ['-h', '--help'] action         -f: ['-f', '--foo'] foo _storeaction(option_strings=['--var1'], dest='var1', nargs=none, const=none, default=none, type=none, choices=none, help=none, metavar=none) action         -g: ['-g', '--goo'] goo action     --var1: ['--var1'] var1 action      --foo: ['-f', '--foo'] foo action         -h: ['-h', '--help'] action      --goo: ['-g', '--goo'] goo action     --help: ['-h', '--help'] action         -f: ['-f', '--foo'] foo  usage: stack38071986.py [-f foo] [--var1 var1]  optional arguments:                 show message , exit   -f foo, --foo foo   --var1 var1  usage: stack38071986.py [-f foo] [--var1 var1] [-h] [-g goo] [--var var]  optional arguments:   -f foo, --foo foo   --var1 var1   -h, --help         show message , exit   -g goo, --goo goo   --var var  namespace(foo=none, goo=none, var='3', var1='1') 

the conflict handler functions defined in super class, can't subclass argumentparser add new one. it's easy add custom action , formathandler classes, isn't easy. guess noone has tried customization.

so kludge write modification of resolve method, , link in - on fly. not clean, enough testing.

this handler knows identity of existing action (action argument), not new 1 (i.e. --var parsera, not --var parserb). modifying 'name' of existing one. method has know, via rename_dict option string replaced , new name.

having done this, i'm thinking might easier write custom version of parents mechanism. 1 used as:

parserc = argparse.argumentparser() parserc.add_argument(...)   # c's own arguments copy_arguments(parserc, [parsera, parserb], rename_dict={...}) 

==========================

i better - custom parents mechanism, 1 lets me specify skip_list , replace_dict. (i may delete solution).

import argparse  def add_actions(parser, other, skip_list=none, rename_dict=none):     # adapted _add_container_actions (used parents)     # copy (by reference) selected actions other parser     # can skip actions (to avoid use of conflict_handler)     # can rename other actions (again avoid conflict)     if skip_list none:         skip_list = ['-h','--help']     if rename_dict none:         rename_dict = {}      # group handling before     # collect groups titles     title_group_map = {}     group in parser._action_groups:         if group.title in title_group_map:             msg = _('cannot merge actions - 2 groups named %r')             raise valueerror(msg % (group.title))         title_group_map[group.title] = group      # map each action group     group_map = {}     group in other._action_groups:          # if group title exists, use that, otherwise         # create new group matching other's group         if group.title not in title_group_map:             title_group_map[group.title] = parser.add_argument_group(                 title=group.title,                 description=group.description,                 conflict_handler=group.conflict_handler)          # map actions new group         action in group._group_actions:             group_map[action] = title_group_map[group.title]      # add other's mutually exclusive groups     # note: if add_mutually_exclusive_group ever gains title= ,     # description= code need expanded above     group in other._mutually_exclusive_groups:         mutex_group = parser.add_mutually_exclusive_group(             required=group.required)          # map actions new mutex group         action in group._group_actions:             group_map[action] = mutex_group      # add actions other or group      # addition skip , rename     action in other._actions:         option_strings = action.option_strings         if any([s s in option_strings if s in skip_list]):              print('skipping ', action.dest)              continue         else:             sl = [s s in option_strings if s in rename_dict]             if len(sl):                  mod = rename_dict[sl[0]]                  action.dest = action.dest+mod                  action.option_strings = [option_strings[0]+mod]         group_map.get(action, parser)._add_action(action)  parsera=argparse.argumentparser() a1=parsera.add_argument('-f','--foo') a1=parsera.add_argument('--var')  parserb=argparse.argumentparser() b1=parserb.add_argument('-g','--goo') b1=parserb.add_argument('--var')  parserc=argparse.argumentparser() # parserc.add_argument('baz') add_actions(parserc, parsera, rename_dict={'--var':'a'}) add_actions(parserc, parserb, rename_dict={'--var':'b'})  parserc.print_help() print(parserc.parse_args()) 

and sample run

2245:~/mypy$ python3 stack38071986_1.py --vara 1 --varb 3 skipping  skipping   usage: stack38071986_1.py [-h] [-f foo] [--vara vara] [-g goo] [--varb varb]  optional arguments:   -h, --help         show message , exit   -f foo, --foo foo   --vara vara   -g goo, --goo goo   --varb varb namespace(foo=none, goo=none, vara='1', varb='3') 

=============================

if add

print('parserc actions') action in parserc._actions:     print(action.option_strings, action.dest) 

i printout

parserc actions ['-h', '--help'] ['-f', '--foo'] foo ['--vara'] vara ['-g', '--goo'] goo ['--varb'] varb 

_actions list of actions (argument) of parser. 'hidden' use caution, don't anticipate changes in fundamental property this. can modify many of attributes of these actions, such dest

for example if rename of dest:

for action in parserc._actions:     if action.dest.startswith('var'):         action.dest = action.dest+'_c' print(parserc.parse_args()) 

the namespace like

namespace(foo=none, goo=none, vara_c=none, varb_c='one') 

Comments

Popular posts from this blog

sql - invalid in the select list because it is not contained in either an aggregate function -

Angularjs unit testing - ng-disabled not working when adding text to textarea -

How to start daemon on android by adb -