为什么argparse不会引发异常?

时间:2014-12-13 12:51:14

标签: logging argparse python-3.4

我使用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)

1 个答案:

答案 0 :(得分:1)

您要分配给Address.__init__ { self._zip_code,其中绕过了setter 。因此,最小的修复是使用:

self.zip_code = zip_code
   # ^ note no underscore 

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


除此之外,你的属性之间存在不一致; statezip_code一旦设置就可以更改,但其他是只读的。如果这不是所需的行为,我会在没有setter的情况下删除属性的getter,并在__init__中直接访问所有内容(即没有下划线)。

或者,如果您确实希望它们都是只读的,请删除setter并将其测试放在__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)