2014-12-13 136 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) 
相關問題