Подкласс Argparse Argument Parser - PullRequest
2 голосов
/ 06 января 2012

Я пытаюсь написать класс синтаксического анализатора, полученный из класса ArgumentParser Python argparse. Контуры следующего кода прекрасно работают в командной строке, но генерируют ошибку, которую я пытаюсь понять в контексте моего модуля.

Код (немного урезанный для удаления неважных вещей) выглядит следующим образом:

class SansParser(argparse.ArgumentParser):
"""Argument parser for preparing a SansModel fit or calculation

"""

def __init__(self):
    """Initialisation method for the parser class"""

    argparse.ArgumentParser.__init__(self)


    # Subparsers for the two commands 'calc' and 'fit'
    self.subparsers = self.add_subparsers()
    self.fit_parser = self.subparsers.add_parser('fit', help="Fit a dataset")
    self.fit_parser.add_argument('-d', '-data', '-dataset', type = str,
                                 dest = 'dataset',
                                 help = "The dataset to fit in SasXML format")
    self.fit_parser.set_defaults(func=fit)
    self.calc_parser = self.subparsers.add_parser('calc', prog='test')
    self.calc_parser.set_defaults(func=calculate)

Я могу запустить эквивалент этого в виде сценария, и это нормально. Если я запускаю его из оболочки или импортирую в командную строку python и пытаюсь создать экземпляр класса, который я получаю:

$ python sansmodel.py
    Traceback (most recent call last):
    File "sansmodel.py", line 57, in <module>
      parser = SansParser()
    File "sansmodel.py", line 41, in __init__
      self.fit_parser = self.subparsers.add_parser('fit', help="Fit a dataset")
    File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/argparse.py",
    line 1064, in add_parser
      parser = self._parser_class(**kwargs)
 TypeError: __init__() got an unexpected keyword argument 'prog'

Насколько я могу судить, код в самом argparse в строке 1064 явно создает ключевое слово "prog", и это ожидаемое поведение, поэтому я не совсем понимаю, где оно неожиданно. Я предполагаю, что у меня что-то не так с прицелом?

Ответы [ 3 ]

4 голосов
/ 06 января 2012

Если вы не перезаписываете какое-либо поведение argparse.ArgumentParser, я рекомендую создать объект парсера и добавить аргументы и подпарамеры к этому объекту.

Тем не менее, проблема в том, что при добавлении нового синтаксического анализатора метод __init__, который был перезаписан реализацией SansParser, не принимает те же аргументы, что и исходный ArgumentParser.

Обходной путь к проблеме должен быть таким:

self.subparsers._parser_class = argparse.ArgumentParser

Таким образом, когда вызывается add_parser, вместо создания нового SansParser (который потерпит неудачу из-за бесконечной рекурсии), будет создан новый ArgumentParser.

1 голос
/ 06 января 2012

Я согласен с предложением @ jcollado просто добавлять аргументы к объекту ArgumentParser, а не к подклассу.

Однако, если вы делаете подкласс, я бы рекомендовал вам изменить сигнатуру вашего __init__ метода.вместо того, чтобы изменять значение self.subparsers._parser_class.

class SansParser(argparse.ArgumentParser):
"""Argument parser for preparing a SansModel fit or calculation

"""

    def __init__(self, *args, **kwargs):
        """Initialisation method for the parser class"""
        if 'my_keyword' in kwargs:
            # Do what needs to be done with kwargs['my_keyword']
            del kwargs['my_keyword'] # Since ArgumentParser won't recognize it

        argparse.ArgumentParser.__init__(self, *args, **kwargs)

Таким образом, ваш подкласс может работать так же, как и ArgumentParser, за исключением случаев, когда вы переопределяете его поведение.

0 голосов
/ 08 декабря 2017

Сегодня днем ​​я получил ту же ошибку и нашел ваш вопрос, когда искал какое-то решение.

Если вы прочитали __init__() аргумента ArgumentParser, вы можете увидеть, что он принимает ряд аргументов, включая правильные'prog':

class ArgumentParser(_AttributeHolder, _ActionsContainer):
"""SOME DOC STRING..."""

    def __init__(self,
             prog=None,
             usage=None,
             description=None,
             epilog=None,
             parents=[],
             formatter_class=HelpFormatter,
             prefix_chars='-',
             fromfile_prefix_chars=None,
             argument_default=None,
             conflict_handler='error',
             add_help=True,
             allow_abbrev=True):
        ...... # SOME IMPLEMENTATION

Я думаю, что факт: пользовательский класс синтаксического анализатора переопределяет метод __init__() и не принимает аргументов.Но другие методы не изменены.Это привело к конфликту поведения методов.При создании подпарсера add_parser () вызывает синтаксический анализатор __init__() с аргументами, включая «prog».Для ArgumentParser это нормально, но для пользовательского синтаксического анализатора с переопределенной __init__() очевидно, что он потерпит неудачу.

Конечно, предложение @ jcollado работает нормально, но, похоже, оно также отменяет настройку поведения субпарсера.

Мое решение этой проблемы немного уродливо, но также хорошо работает.При переопределении __init__() ArgumentParser просто сохраните каждый аргумент и его значение по умолчанию.Как это:

class MyParser(argparse.ArgumentParser):
    def __init__(
        self,
        prog=None,
        usage=None,
        description=None,
        epilog=None,
        parents=[],
        formatter_class=argparse.HelpFormatter,
        prefix_chars='-',
        fromfile_prefix_chars=None,
        argument_default=None,
        conflict_handler='error',
        add_help=True,
        allow_abbrev=True
        # and your custom arguments here
        ):

        super(MyParser, self).__init__(prog=prog, usage=usage, description=description, epilog=epilog,
                                   parents=parents, formatter_class=formatter_class,
                                   prefix_chars=prefix_chars, fromfile_prefix_chars=fromfile_prefix_chars,
                                   argument_default=argument_default, conflict_handler=conflict_handler,
                                   add_help=add_help, allow_abbrev=allow_abbrev
                                   )
        # and your custom actions here
...