【问题标题】:Why is argparse not raising exceptions?为什么 argparse 不引发异常?
【发布时间】:2014-12-13 21:48:56
【问题描述】:

我正在使用argparse 模块通过命令行创建地址对象。但是当我给它提供无效参数(即那些应该引发异常的参数)时,不会引发任何异常。更糟糕的是,没有记录任何内容(除非我创建了一个有效的对象)。

所以,这行得通:

-n Patrick -a "151 Piedmont Ave" -c "Winston Salem" -s "NC" -z 27101

这不是:

-l ERROR -n Patrick -a "151 Piedmont Ave" -c "Winston Salem" -s "NC" -z 271

我哪里错了?

注意:这是家庭作业,因此非常感谢您对绝对答案的指导。

"""
property_address.py
"""
import re
import logging
import argparse

LOG_FILENAME = 'property_address.log'
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(funcName)s - %(message)s"
DEFAULT_LOG_LEVEL = "debug"
LEVELS = {'debug': logging.DEBUG,
          'info': logging.INFO,
          'warning': logging.WARNING,
          'error': logging.ERROR,
          'critical': logging.CRITICAL
         }

def start_logging(filename=LOG_FILENAME, level=None):
    logging.basicConfig(filename=filename, level=level, filemode='w', format=LOG_FORMAT)
    logging.info('Starting up the property address program')

class ZipCodeError(Exception):
    "Custom exception for invalid zip codes."
    pass


class StateError(Exception):
    "Custom exception for invalid state abbreviation."
    pass


class Address(object):
    """
    An address object.
    """

    def __init__(self, name, street_address, city, state, zip_code):
        self._name = name
        self._street_address = street_address
        self._city = city
        self._state = state
        self._zip_code = zip_code
        logging.info('Instantiated an address')

    @property
    def name(self):
        return self._name

    @property
    def street_address(self):
        return self._street_address

    @property
    def city(self):
        return self._city

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        "Validate that states are abbreviated as US Postal style."
        state_valid = re.compile(r'[A-Z]{2}$')
        if re.match(state_valid, value):
            self._state = value
        else:
            message = 'STATE Exception: State not in correct format'
            logging.error(message)
            raise StateError()

    @property
    def zip_code(self):
        return self._zip_code

    @zip_code.setter
    def zip_code(self, value):
        "Validate zip codes are five digits."
        zip_valid = re.compile(r'\d{5}$')
        if re.match(zip_valid, value):
            self._zip_code = value
        else:
            message = 'ZIP CODE Exception: Zip code not in correct format'
            logging.error(message)
            raise ZipCodeError()

    def __str__(self):
        return self._name

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Set attributes for property address.')
    parser.add_argument(
                        '-l',
                        '--level',
                        dest='level',
                        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
                        default='INFO',
                        help='Sets the log level to DEBUG, INFO, WARNING, ERROR, and CRITICAL')
    parser.add_argument(
                        '-n',
                        '--name',
                        dest='name',
                        action='store',
                        required=True,
                        help='Sets the name value of the Address object')
    parser.add_argument(
                        '-a',
                        '--address',
                        dest='address',
                        action='store',
                        required=True,
                        help='Sets the street_address value of the Address object')
    parser.add_argument(
                        '-c',
                        '--city',
                        dest='city',
                        action='store',
                        required=True,
                        help='Sets the city value of the Address object')
    parser.add_argument(
                        '-s',
                        '--state',
                        dest='state',
                        action='store',
                        required=True,
                        help='Sets the state value of the Address object')
    parser.add_argument(
                        '-z',
                        '--zip_code',
                        dest='zip_code',
                        action='store',
                        required=True,
                        help='Sets the zip_code value of the Address object')
    args = parser.parse_args()

    # Start our logger
    start_logging(level=(args.level))

    # Create our Address object
    a = Address(args.name, args.address, args.city, args.state, args.zip_code)

【问题讨论】:

  • 为什么不将邮政编码作为参数的一部分进行测试;参见例如stackoverflow.com/q/25470844/3001761
  • 这是有道理的。但是为什么我的异常一开始没有被提出呢?
  • 因为在Address.__init__ 中,您分配给self._zip_code,绕过了setter,而不是self.zip_code
  • 另外,你应该更加一致; zip_codestate 可以更改,但其他属性是只读的。要么将检查放入__init__ 并将它们全部设为只读,要么将它们保留为普通属性并放弃它们的吸气剂。 Python 可以很容易地在以后将它们放回原处。
  • 好点。看起来这里也有一个很好的属性解释:chimera.labs.oreilly.com/books/1230000000393/…

标签: logging argparse python-3.4


【解决方案1】:

Address.__init__ 中,您分配给例如self._zip_code绕过了设置器。因此,最小的解决方法是使用:

self.zip_code = zip_code
   # ^ note no underscore 

这意味着调用了 setter 并进行了检查。


除此之外,您的属性之间存在不一致; statezip_code 设置后可以更改,但其他为只读。万一这不是所需的行为,我将删除不带 setter 的属性的 getter,并直接访问 __init__ 中的所有内容(即不带下划线)。

或者,如果您确实希望它们都是只读的,请删除设置器并将它们的测试放在__init__ 中。


最后一个选择是在解析时测试参数,而不是在类中;参见例如Specify format for input arguments argparse python

如果您仍然希望该类测试其参数(因为当它们来自argparse),您可以考虑为您的类中的测试公开@staticmethod,即然后也被setter 调用:

class Address:

    ...

    @staticmethod
    def valid_zip_code(zip_code, zip_valid=re.compile(r'\d{5}$')):
        if not re.match(zip_valid, zip_code):
            raise ZipCodeError
        return zip_code

    @zip_code.setter
    def zip_code(self, new_zip):
          new_zip = self.valid_zip_code(new_zip)
          self._zip_code = new_zip

...

parser.add_argument('-z', ..., type=Address.valid_zip_code)

【讨论】:

    猜你喜欢
    • 2016-12-29
    • 1970-01-01
    • 2021-01-28
    • 2019-07-23
    • 1970-01-01
    • 1970-01-01
    • 2014-11-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多