2010-04-28 1 views
7

Мой Python скрипт (для списков списка задач) запускается из командной строки следующим образом:Как вы обрабатываете параметры, которые нельзя использовать вместе (с помощью OptionParser)?

todo [options] <command> [command-options] 

Некоторые параметры не могут быть использованы вместе, например

todo add --pos=3 --end "Ask Stackoverflow" 

бы определить как третью позицию и конец списка. Аналогично

todo list --brief --informative 

путает мою программу о том, чтобы быть кратким или информативным. Поскольку я хочу иметь достаточно мощный контроль над параметрами, такие случаи будут кучкой, и новые, несомненно, возникнут в будущем. Если пользователи передают плохую комбинацию параметров, я хочу дать информативное сообщение, желательно вместе с помощью помощи, предоставляемой optparse. В настоящее время я обрабатываю это с помощью выражения if-else, которое я считаю очень уродливым и бедным. Моя мечта - иметь что-то вроде этого в моем коде:

и OptionParser использовали бы это при разборе параметров.

Поскольку это не существует, насколько я знаю, я спрашиваю сообщество SO: Как вы справляетесь с этим?

ответ

6

Возможно, расширив optparse.OptionParser:

class Conflict(object): 
    __slots__ = ("combination", "message", "parser") 

    def __init__(self, combination, message, parser): 
     self.combination = combination 
     self.message = str(message) 
     self.parser = parser 

    def accepts(self, options): 
     count = sum(1 for option in self.combination if hasattr(options, option)) 
     return count <= 1 

class ConflictError(Exception): 
    def __init__(self, conflict): 
     self.conflict = conflict 

    def __str__(self): 
     return self.conflict.message 

class MyOptionParser(optparse.OptionParser): 
    def __init__(self, *args, **kwds): 
     optparse.OptionParser.__init__(self, *args, **kwds) 
     self.conflicts = [] 

    def set_not_allowed(self, combination, message): 
     self.conflicts.append(Conflict(combination, message, self)) 

    def parse_args(self, *args, **kwds): 
     # Force-ignore the default values and parse the arguments first 
     kwds2 = dict(kwds) 
     kwds2["values"] = optparse.Values() 
     options, _ = optparse.OptionParser.parse_args(self, *args, **kwds2) 

     # Check for conflicts 
     for conflict in self.conflicts: 
      if not conflict.accepts(options): 
       raise ConflictError(conflict) 

     # Parse the arguments once again, now with defaults 
     return optparse.OptionParser.parse_args(self, *args, **kwds) 

Вы можете обрабатывать ConflictError где вы звоните parse_args: ответ

try: 
    options, args = parser.parse_args() 
except ConflictError as err: 
    parser.error(err.message) 
+0

Потрясающе решение! – Joel

+0

'super()' не будет работать для Python 2.X, так как 'OptionParser' создается как класс старого стиля. Решение предоставляется по [Вопрос переполнения стека 2023940] (http://stackoverflow.com/questions/2023940/using-super-when-subclassing-python-class-that-is-not-derived-from-objectold). – gotgenes

+0

Спасибо, я исправил его, явно написав суперкласс. –

3

Тамас является хорошим началом, но я не мог заставить его работать, поскольку у него было (или было) количество ошибок, в том числе , сломанный вызов super , "parser" отсутствует в Conflict.__slots__, всегда поднимая ошибку, когда конфликт определены из-за использования parser.has_option() в Conflicts.accepts() и т.д.

Поскольку я действительно нужна эта функция, я свернул свое собственное решение, и сделали его доступным из Python Package Index в ConflictsOptionParser. Он работает практически так же, как и замена на optparse.OptionParser. (Я знаю, что argparse - это новая синтаксическая развязка командной строки, но она недоступна в Python 2.6 и ниже и в настоящее время меньше, чем optparse. Пришлите мне письмо, если вы хотите взломать или взломать дополнительный argparse основанное раствор) ключ два новых метода, register_conflict(), и, в меньшей степени, unregister_conflict():.

#/usr/bin/env python 

import conflictsparse 
parser = conflictsparse.ConflictsOptionParser("python %prog [OPTIONS] ARG") 
# You can retain the Option instances for flexibility, in case you change 
# option strings later 
verbose_opt = parser.add_option('-v', '--verbose', action='store_true') 
quiet_opt = parser.add_option('-q', '--quiet', action='store_true') 
# Alternatively, you don't need to keep references to the instances; 
# we can re-use the option strings later 
parser.add_option('--no-output', action='store_true') 
# Register the conflict. Specifying an error message is optional; the 
# generic one that is generated will usually do. 
parser.register_conflict((verbose_opt, quiet_opt, '--no-output')) 
# Now we parse the arguments as we would with 
# optparse.OptionParser.parse_args() 
opts, args = parser.parse_args() 

Она имеет несколько преимуществ по сравнению с решением, начатого Тамас:

  • Он работает из коробки и устанавливается через pip (или easy_install, если вы должны).
  • Параметры в конфликте могут быть указаны либо их опционными строками, либо их экземплярами optparse.Option, что помогает с принципом DRY; если вы используете экземпляры, вы можете изменить фактические строки, не беспокоясь о нарушении кода конфликта.
  • Выполняется нормальное поведение optparse.OptionParser.parse_args() и автоматически вызывает optparse.OptionParser.error(), когда он обнаруживает конфликтующие параметры в аргументах командной строки, а не бросает ошибку напрямую. (Это как функция и ошибка;. Вид ошибки в общий дизайн optparse «s, но особенность этого пакета в том, что она по крайней мере, в соответствии с optparse поведением)
Смежные вопросы