Rewrote SettingsBase

This commit is contained in:
enkore 2013-02-23 23:48:48 +01:00
parent 4e06a9c2ee
commit 3677ffc852

View File

@ -8,6 +8,7 @@ from contextlib import contextmanager
import types import types
import inspect import inspect
import functools import functools
import collections
__all__ = [ __all__ = [
"SettingsBase", "ClassFinder", "ModuleFinder", "SettingsBase", "ClassFinder", "ModuleFinder",
@ -44,6 +45,34 @@ class ConfigInvalidModuleError(ConfigError):
def get_path(): def get_path():
return __path__ return __path__
class KeyConstraintDict(collections.UserDict):
class MissingKeys(Exception):
def __init__(self, keys):
self.keys = keys
def __init__(self, valid_keys, required_keys):
super().__init__()
self.valid_keys = valid_keys
self.required_keys = set(required_keys)
self.seen_keys = set()
def __setitem__(self, key, value):
if key in self.valid_keys:
self.seen_keys.add(key)
self.data[key] = value
else:
raise KeyError(key)
def missing(self):
return self.required_keys - (self.seen_keys & self.required_keys)
def __iter__(self):
if self.missing():
raise self.MissingKeys(self.missing())
return self.data.__iter__()
class SettingsBase: class SettingsBase:
""" """
Support class for providing a nice and flexible settings interface Support class for providing a nice and flexible settings interface
@ -57,7 +86,7 @@ class SettingsBase:
Settings are stored as attributes of self Settings are stored as attributes of self
""" """
settings = tuple() # Can also be a tuple of two-tuples (setting, docstring) settings = tuple()
"""settings should be tuple containing two types of elements: """settings should be tuple containing two types of elements:
* bare strings, which must be valid identifiers. * bare strings, which must be valid identifiers.
* two-tuples, the first element being a identifier (as above) and the second * two-tuples, the first element being a identifier (as above) and the second
@ -67,33 +96,32 @@ class SettingsBase:
"""required can list settings which are required""" """required can list settings which are required"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
def flatten_settings(settings):
return tuple((flatten_setting(setting) for setting in settings))
def flatten_setting(setting): def flatten_setting(setting):
return setting[0] if isinstance(setting, tuple) else setting return setting[0] if isinstance(setting, tuple) else setting
def flatten_settings(settings):
return tuple(flatten_setting(setting) for setting in settings)
self.settings = flatten_settings(self.settings) def get_argument_dict(args, kwargs):
required = set()
self.required = set(self.required)
if len(args) == 1 and not kwargs: if len(args) == 1 and not kwargs:
# User can also pass in a dict for their settings # User can also pass in a dict for their settings
# Note: you could do that anyway, with the ** syntax # Note: you could do that anyway, with the ** syntax
kwargs = args[0] return args[0]
return kwargs
for key, value in kwargs.items(): self.settings = flatten_settings(self.settings)
if key in self.settings:
setattr(self, key, value)
required.add(key)
else:
raise ConfigKeyError(type(self).__name__, key=key)
# Some nice set magic :-) [that's more efficient if we have classes with a few thousand settings] sm = KeyConstraintDict(self.settings, self.required)
required &= set(self.required) settings_source = get_argument_dict(args, kwargs)
if len(required) != len(self.required):
raise ConfigMissingError(type(self).__name__, self.required-required) try:
sm.update(settings_source)
except KeyError as exc:
raise ConfigKeyError(type(self).__name__, key=exc.args[0]) from exc
try:
self.__dict__.update(sm)
except KeyConstraintDict.MissingKeys as exc:
raise ConfigMissingError(type(self).__name__, missing=exc.keys) from exc
self.__name__ = "{}.{}".format(self.__module__, self.__class__.__name__) self.__name__ = "{}.{}".format(self.__module__, self.__class__.__name__)