2014-12-13 99 views
0

我正在使用​​模块通过命令行使用创建地址对象。但是当我给它提供无效的参数(即那些应该引发异常的参数)时,不会引发异常。更糟的是,没有记录(除非我创建一个有效的对象)。为什么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

为什么不测试邮政编码作为参数的一部分;见例如http://stackoverflow.com/q/25470844/3001761 – jonrsharpe 2014-12-13 12:56:12

+0

这是有道理的。但为什么我的例外不是首先被提出? – 2014-12-13 13:11:10

+1

因为在'Address .__ init__'你分配给'self._zip_code',绕过setter,而不是'self.zip_code'。 – jonrsharpe 2014-12-13 13:12:25

回答

1

Address.__init__您正在分配给例如, self._zip_code,其中绕过设定器。因此,最小的解决办法是使用:

self.zip_code = zip_code 
    #^note no underscore 

这意味着,调用setter时,你的支票。


除此之外,您的属性之间不一致; statezip_code可以更改一次,但其他设置为只读。在可能的情况下,这不是我们想要的行为,我会删除没有setter的属性的获取者,并在__init__中直接访问所有的属性(即没有下划线)。

或者,如果您确实希望它们全部为只读,请移除setter并将其测试放在__init__中。


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

如果您仍然希望类来测试它的参数(当他们通过​​来的),你可以考虑暴露在你的类中的测试一个@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) 
相关问题