Scrapy中的BaseSetting的行为是一个包含优先级的字典,当多次set一个key时,value中只保存优先级最大的一个。
首先看几个辅助的元素,优先级字典:
SETTINGS_PRIORITIES = {
'default': 0,
'command': 10,
'project': 20,
'spider': 30,
'cmdline': 40,
}
对于优先级字典的包装函数,如果是字符串型参数,则返回dict中对应的值,否则返回参数本身(当做是int型了):
def get_settings_priority(priority):
"""
Small helper function that looks up a given string priority in the
:attr:`~scrapytest.settings.SETTINGS_PRIORITIES` dictionary and returns its
numerical value, or directly returns a given numerical priority.
"""
if isinstance(priority, six.string_types):
return SETTINGS_PRIORITIES[priority]
else:
return priority
BaseSetting中的属性类,考虑到了value本身是BaseSettings类实例的情况,需要取较高的优先级:
class SettingsAttribute(object):
"""Class for storing data related to settings attributes.
This class is intended for internal usage, you should try Settings class
for settings configuration, not this one.
"""
def __init__(self, value, priority):
self.value = value
if isinstance(self.value, BaseSettings):
self.priority = max(self.value.maxpriority(), priority)
else:
self.priority = priority
def set(self, value, priority):
"""Sets value if priority is higher or equal than current priority."""
if priority >= self.priority:
if isinstance(self.value, BaseSettings):
value = BaseSettings(value, priority=priority)
self.value = value
self.priority = priority
def __str__(self):
return "<SettingsAttribute value={self.value!r} " \
"priority={self.priority}>".format(self=self)
__repr__ = __str__
接下来是重头戏,BaseSetting类,这个类的特点类似于内置dict型,但是可以为属性设置优先级,并且冻结全部属性,使其无法被修改。
class BaseSettings(MutableMapping):
"""
Instances of this class behave like dictionaries, but store priorities
along with their ``(key, value)`` pairs, and can be frozen (i.e. marked
immutable).
Key-value entries can be passed on initialization with the ``values``
argument, and they would take the ``priority`` level (unless ``values`` is
already an instance of :class:`~scrapytest.settings.BaseSettings`, in which
case the existing priority levels will be kept). If the ``priority``
argument is a string, the priority name will be looked up in
:attr:`~scrapytest.settings.SETTINGS_PRIORITIES`. Otherwise, a specific integer
should be provided.
Once the object is created, new settings can be loaded or updated with the
:meth:`~scrapytest.settings.BaseSettings.set` method, and can be accessed with
the square bracket notation of dictionaries, or with the
:meth:`~scrapytest.settings.BaseSettings.get` method of the instance and its
value conversion variants. When requesting a stored key, the value with the
highest priority will be retrieved.
"""
def __init__(self, values=None, priority='project'):
self.frozen = False
self.attributes = {}
self.update(values, priority)
def __getitem__(self, opt_name):
if opt_name not in self:
return None
return self.attributes[opt_name].value
def __contains__(self, name):
return name in self.attributes
def get(self, name, default=None):
"""
Get a setting value without affecting its original type.
:param name: the setting name
:type name: string
:param default: the value to return if no setting is found
:type default: any
"""
return self[name] if self[name] is not None else default
def getbool(self, name, default=False):
"""
Get a setting value as a boolean.
``1``, ``'1'``, and ``True`` return ``True``, while ``0``, ``'0'``,
``False`` and ``None`` return ``False``.
For example, settings populated through environment variables set to
``'0'`` will return ``False`` when using this method.
:param name: the setting name
:type name: string
:param default: the value to return if no setting is found
:type default: any
"""
return bool(int(self.get(name, default)))
def getint(self, name, default=0):
"""
Get a setting value as an int.
:param name: the setting name
:type name: string
:param default: the value to return if no setting is found
:type default: any
"""
return int(self.get(name, default))
def getfloat(self, name, default=0.0):
"""
Get a setting value as a float.
:param name: the setting name
:type name: string
:param default: the value to return if no setting is found
:type default: any
"""
return float(self.get(name, default))
def getlist(self, name, default=None):
"""
Get a setting value as a list. If the setting original type is a list, a
copy of it will be returned. If it's a string it will be split by ",".
For example, settings populated through environment variables set to
``'one,two'`` will return a list ['one', 'two'] when using this method.
:param name: the setting name
:type name: string
:param default: the value to return if no setting is found
:type default: any
"""
value = self.get(name, default or [])
if isinstance(value, six.string_types):
value = value.split(',')
return list(value)
def getdict(self, name, default=None):
"""
Get a setting value as a dictionary. If the setting original type is a
dictionary, a copy of it will be returned. If it is a string it will be
evaluated as a JSON dictionary. In the case that it is a
:class:`~scrapytest.settings.BaseSettings` instance itself, it will be
converted to a dictionary, containing all its current settings values
as they would be returned by :meth:`~scrapytest.settings.BaseSettings.get`,
and losing all information about priority and mutability.
:param name: the setting nam