Я собираюсь предположить, что вы пытаетесь сделать мое предложение parents
и проиллюстрировать, что может быть. Но даже если вы примете другой подход, это может помочь.
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 like dest after creation
print()
# parser with parents; not the conflict_handler
parserC=argparse.ArgumentParser(conflict_handler='resolve',
parents=[parserA, parserB])
print(parserC._actions) # the actions (arguments) of C
print()
parserA.print_help()
print()
parserC.print_help() # uses the C._actions
который производит
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 this help message and 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:
help show this help message and exit
-f FOO, --foo FOO
usage: stack38071986.py [-f FOO] [-h] [-g BAR]
optional arguments:
-f FOO, --foo FOO
-h, --help show this help message and exit
-g BAR, --goo BAR, --bar BAR
Normal store
поведения является хранение value
на атрибуте dest
в args
пространства имен, setattr(namespace, action.dest, value)
. dest
default - первая длинная строка опций (минус -), но может быть задана как параметр (для опций) или после создания.
Когда одна или несколько опций (флаг) нового действия конфликтуют с существующим действием, вызывается conflict_handler
. Обработчик по умолчанию вызывает ошибку, но здесь я использую обработчик resolve
. Он пытается удалить достаточно существующего аргумента для разрешения конфликта.
Все 3 анализатора создают действие -h
(помощь). resolve
в parserC
удаляет все, кроме одного. Обратите внимание, что [-h]
отсутствует в справке parserA
.
--bar
, определенный в parserB
, конфликтует с той же строкой в parserA
. resolve
удалил его из определения A, но -f
и --foo
остались.
Создание parserC
испортило другие синтаксические анализаторы; поэтому я рекомендую использовать только parserC.parse_args()
в этом прогоне.
Мы могли бы написать другой метод conflict_handler
. resolve
имеет некоторые грубые грани и часто не используется.
Я использую некоторые функции, которые не документированы. Некоторые считают, что это небезопасно. Но если вы хотите необычного поведения, вы должны принять некоторые риски. Кроме того, документация argparse
не является последним словом для ее поведения, и ее легче изменить, чем сам код. При внесении изменений разработчики почти параноидальные по поводу обратных конфликтов.
==================
Вот удар при настройке обработчика resolve
конфликта
import argparse
def pp(adict):
for 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'}
for option_string, action in conflicting_actions:
new_string = rename_dict.get(option_string, None)
if new_string:
# rename rather than 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 the option now has no option string, remove it from the
# container holding it
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())
который производит
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'] help
Action --goo: ['-g', '--goo'] goo
Action --help: ['-h', '--help'] 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'] help
Action --goo: ['-g', '--goo'] goo
Action --help: ['-h', '--help'] help
Action -f: ['-f', '--foo'] foo
usage: stack38071986.py [-f FOO] [--var1 VAR1]
optional arguments:
help show this help message and 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 this help message and exit
-g GOO, --goo GOO
--var VAR
Namespace(foo=None, goo=None, var='3', var1='1')
Функции обработчика конфликта определены в суперклассе, поэтому я не могу просто подкласса ArgumentParser
добавить новый. Легко добавить пользовательские классы Action
и FormatHandler
, но это не так просто. Я предполагаю, что никто не пробовал эту настройку.
Так что мой kludge должен написать модификацию метода resolve
и связать его - на лету. Не чистая, но достаточно для тестирования.
Этот обработчик знает идентификатор существующего действий (action
аргумент), но не новый (т.е. --var
от парсера, но не --var
от ParserB). Поэтому я изменяю «имя» этого существующего. На данный момент этот метод должен знать через свою rename_dict
строку опций, подлежащую замене, и новое имя.
Сделав это, я думаю, что было бы проще написать пользовательскую версию механизма parents
. Тот, который может быть использован как:
parserC = argparse.ArgumentParser()
parserC.add_argument(...) # C's own arguments
copy_arguments(parserC, [parserA, parserB], rename_dict={...})
==========================
Я лучше это нравится - это custom parents
механизм, который позволяет мне указать skip_list
и replace_dict
. (Я могу удалить о решении).
import argparse
def add_actions(parser, other, skip_list=None, rename_dict=None):
# adapted from _add_container_actions (used for parents)
# copy (by reference) selected actions from other to parser
# can skip actions (to avoid use of conflict_handler)
# can rename other actions (again to avoid conflict)
if skip_list is None:
skip_list = ['-h','--help']
if rename_dict is None:
rename_dict = {}
# group handling as before
# collect groups by titles
title_group_map = {}
for group in parser._action_groups:
if group.title in title_group_map:
msg = _('cannot merge actions - two groups are named %r')
raise ValueError(msg % (group.title))
title_group_map[group.title] = group
# map each action to its group
group_map = {}
for group in other._action_groups:
# if a group with the title exists, use that, otherwise
# create a new group matching the 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 the actions to their new group
for 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= and
# description= then this code will need to be expanded as above
for group in other._mutually_exclusive_groups:
mutex_group = parser.add_mutually_exclusive_group(
required=group.required)
# map the actions to their new mutex group
for action in group._group_actions:
group_map[action] = mutex_group
# add all actions to this other or their group
# addition with skip and rename
for action in other._actions:
option_strings = action.option_strings
if any([s for s in option_strings if s in skip_list]):
print('skipping ', action.dest)
continue
else:
sl = [s for 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())
и образец выполнения
2245:~/mypy$ python3 stack38071986_1.py --varA 1 --varB 3
skipping help
skipping help
usage: stack38071986_1.py [-h] [-f FOO] [--varA VARA] [-g GOO] [--varB VARB]
optional arguments:
-h, --help show this help message and exit
-f FOO, --foo FOO
--varA VARA
-g GOO, --goo GOO
--varB VARB
Namespace(foo=None, goo=None, varA='1', varB='3')
=============================
Если добавить
print('parserC actions')
for action in parserC._actions:
print(action.option_strings, action.dest)
Я получаю эту распечатку
parserC actions
['-h', '--help'] help
['-f', '--foo'] foo
['--varA'] varA
['-g', '--goo'] goo
['--varB'] varB
_actions
- это список действий (аргументов) анализатора. Это «скрыто», поэтому используйте с осторожностью, но я не ожидаю никаких изменений в фундаментальном свойстве, подобном этому.Вы можете изменить многие из атрибутов этих действий, таких как dest
Например, если переименовать некоторые dest
:
for action in parserC._actions:
if action.dest.startswith('var'):
action.dest = action.dest+'_C'
print(parserC.parse_args())
пространство имен будет выглядеть
Namespace(foo=None, goo=None, varA_C=None, varB_C='one')
«собственно вещь "- это делать то, что имеет наибольшее значение для ваших пользователей. Дайте нам небольшой конкретный пример для начала. – hpaulj
Я предложил различные альтернативы в связанном вопросе. Что вы используете? – hpaulj
hpaulj- Я планировал использовать ваше «родительское» решение, но потом я увидел, что у меня все еще будет проблема с конфликтующими аргументами, поэтому я задал этот вопрос – MichalD