Я использую пакет Python argparse, с простой конфигурацией
Когда я пытаюсь добавить в свой парсер formatter_class=argparse.MetavarTypeHelpFormatter, получаю ошибку:
AttributeError: 'NoneType' object has no attribute '__name__'
кто-нибудь еще сталкивался с этим? и может быть знаете почему?
это мой код:
import argparse
argParser = argparse.ArgumentParser(
formatter_class=argparse.MetavarTypeHelpFormatter
)
argParser.add_argument("models", default='', nargs='+',
help = "Which model(s) to execute")
args = argParser.parse_args()
print(args)
Когда я пробую без добавления formatter_class, все работает.
Он также работает с другими классами форматирования, например formatter_class=argparse.RawDescriptionHelpFormatter
Я также пробовал это с атрибутом prog и без него.






Краткий ответ: аргумент type для add_argument по умолчанию равен None, а не str.
Менее короткий ответ: тип None ведет себя как тип str, но не работает с MetaVarTypeHelpFormatter.
MetaVarTypeHelpFormatter требует явного типа для каждого аргумента.
argParser.add_argument("models", default='', nargs='+', type=str,
help = "Which model(s) to execute")
Его документированная цель — использовать имя атрибута type аргумента в качестве метапеременной, и по умолчанию этот атрибут имеет значение None.
Хотя тип аргумента по умолчанию — str, это просто означает, что значение str, взятое из списка аргументов, сохраняется, если атрибут type аргумента равен None, или преобразуется в заданный тип, если это не None.
>>> p = argparse.ArgumentParser()
>>> print(p.add_argument("foo").type)
None
>>> print(p.add_argument("foo", type=int).type)
<class 'int'>
Каждый синтаксический анализатор поддерживает реестр типов, что означает, что вы можете добавлять свои собственные произвольные сопоставления значений к вызываемым объектам для создания типов. Например,
>>> import argparse
>>> p = argparse.ArgumentParser()
>>> p.register('type', 'lowercase', lambda x: x.lower())
>>> p.add_argument("foo", type='lowercase')
_StoreAction(option_strings=[], dest='foo', nargs=None, const=None, default=None, type='lowercase', choices=None, required=True, help=None, metavar=None)
>>> p.parse_args(["HELLO WORLD"])
Namespace(foo='hello world')
Первоначально запись в реестре типов сопоставляется None с
def identity(string):
return string
поэтому аргумент с «типом» None в противном случае ведет себя так, как если бы вы указали str явно.
Реестр типов не документирован и, за исключением этой единственной записи, больше не используется, поэтому неясно, почему он существует. Возможно, у автора были планы использовать его чаще, но они так и не осуществились. (Обратите внимание, что каждый парсер также имеет реестр действий, который используется чаще, поэтому вы можете указать действия, такие как 'append' и 'store_const', вместо прямого указания закрытых классов, таких как _AppendAction и _StoreConstAction.)
Вам нужно передать type= в add_argument:
MetavarTypeHelpFormatter использует имя аргумента
typeдля каждого аргумент как отображаемое имя для его значений
Вы не уточнили, поэтому получается None.
Поскольку в вашем примере это тоже по умолчанию '', вы можете сделать:
argParser.add_argument(
"models",
default = "",
nargs = "+",
help = "Which model(s) to execute",
type=str,
)
Это похоже на ошибку. Предполагается, что type по умолчанию имеет значение str для action по умолчанию, поэтому он должен вести себя так, даже если type опущен. Чрезвычайно глупо, если type=str можно и нужно опускать при обычных обстоятельствах (точно так же, как мы опускаем action='store', required=True и т. д.), но для MetavarTypeHelpFormatter это необходимо.
argparse действительно гибко; Аргумент type может быть любым значением, а не только типом, а вызываемый объект, используемый для преобразования аргумента, ищется в реестре. По какой-то причине реестр сопоставляет None с функцией идентификации, а не просто сопоставляет str с str.
(Непонятно, почему он такой гибкий; похоже, ничто другое не использует реестр типов, и он не задокументирован как что-то, что пользователи могли бы дополнять. Он похож на реестр действий, который сопоставляет, например, строку типа 'append' с классом _AppendAction, но гораздо реже используется.)
Я предполагаю, что type был добавлен в реестр с самого начала, чтобы можно было добавить больше строк типов. Но, за исключением нескольких общих функций, таких как int и float, пользователи не используют множество функций специального типа. Позже кто-то добавил этот форматтер Metavar, но не протестировал его тщательно. Поскольку указать метавар так легко, я не уверен, что этот подкласс необходим (или часто используется). В любом случае, если мы вносим изменения, это должно быть связано с подклассом форматтера, а не с использованием реестра. parser._getvalue отлично справляется с реестром.
Из документации Python:
MetavarTypeHelpFormatter использует имя аргумента типа для каждого аргумента в качестве отображаемого имени для его значений (вместо использования dest, как это делает обычный форматтер)
Итак, вам нужно добавить аргумент, например, как
p.add_argument('models', default='', nargs='+', help='no help', type=str)
Но на самом деле это ошибка в argparse, которая позже выдает исключение: оно должно было вызвать это исключение сразу же, когда вы добавили аргумент без указания типа.
type по умолчанию None он же str отлично работает для синтаксического анализа. Просто этот подкласс Formatter использует слишком упрощенный подход к получению метавара по умолчанию. Я не думаю, что этот подкласс Formatter широко используется.
С использованием nargs='+' проблем нет. Это с вашим тестовым примером. parse_args должен получить список строк, например. p.parse_args(['abc xyz']) или p.parse_args('abc xyz'.split())
Вы правы - я забыл об этом.
Я удалил комментарий о nargs='+' из своего предыдущего поста.
p._get_value использует
type_func = self._registry_get('type', action.type, action.type)
который вернет str по умолчанию type.
Вместо этого этот подкласс Formatter использует
return action.type.__name__
чтобы получить тип, игнорируя _registry.
Я согласен, что использование _registry вместо type, вероятно, является недостаточно используемой функцией. реестр в основном используется для action. Если пользователям нужна настройка type, они могут определить необходимую функцию и использовать ее напрямую. Нет реальной необходимости добавлять его в реестр.
За все годы, что я следил за проблемами argparse, я не припомню, чтобы видел эту проблему раньше. Это говорит о том, что мало кто использовал MetavarTypeHelpFormatter, а если и использовал, то имел привычку использовать type=str. Установка metavar напрямую также является хорошей альтернативой.
Обратите внимание, что в формате по умолчанию default_metavar является производным от action.dest либо как есть, либо в верхнем регистре.
MetavarTypeHelpFormatterкласс не получил широкого распространения. Это простая модификация базового форматтера. Как показывают ответы, проблема заключается в том, как реализовано значение по умолчаниюtype. Это прекрасно работает для других целей,