为什么argparse不会引发异常?
问题描述:
我正在使用模块通过命令行使用创建地址对象。但是当我给它提供无效的参数(即那些应该引发异常的参数)时,不会引发异常。更糟的是,没有记录(除非我创建一个有效的对象)。为什么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)
答
在Address.__init__
您正在分配给例如, self._zip_code
,其中绕过设定器。因此,最小的解决办法是使用:
self.zip_code = zip_code
#^note no underscore
这意味着,调用setter时,你的支票。
除此之外,您的属性之间不一致; state
和zip_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)
为什么不测试邮政编码作为参数的一部分;见例如http://*.com/q/25470844/3001761 – jonrsharpe 2014-12-13 12:56:12
这是有道理的。但为什么我的例外不是首先被提出? – 2014-12-13 13:11:10
因为在'Address .__ init__'你分配给'self._zip_code',绕过setter,而不是'self.zip_code'。 – jonrsharpe 2014-12-13 13:12:25