SettingsBase

This commit is contained in:
enkore 2013-02-23 17:40:35 +01:00
parent 040341021c
commit 1cdc722f46

View File

@ -22,20 +22,41 @@ class ConfigurationError(Exception):
super().__init__(message) super().__init__(message)
class Module: class SettingsBase:
output = None """
position = 0 Support class for providing a nice and flexible settings interface
Classes inherit from this class and define what settings they provide and
which are required.
The constructor is either passed a dictionary containing these settings, or
keyword arguments specifying the same.
Settings are stored as attributes of self
"""
settings = tuple() # Can also be a tuple of two-tuples (setting, docstring) settings = tuple() # Can also be a tuple of two-tuples (setting, docstring)
"""settings should be tuple containing two types of elements:
* bare strings, which must be valid identifiers.
* two-tuples, the first element being a identifier (as above) and the second
a docstring for the particular setting"""
required = tuple() required = tuple()
"""required can list settings which are required"""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if len(self.settings) and isinstance(self.settings[0], tuple): def flatten_settings(settings):
self.settings, docstrings = zip(*self.settings) return tuple((flatten_setting(setting) for setting in settings))
def flatten_setting(setting):
return setting[0] if isinstance(setting, tuple) else setting
self.settings = flatten_settings(self.settings)
required = set() required = set()
self.required = set(self.required) self.required = set(self.required)
if len(args) == 1 and not len(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
# Note2: just for backwards compatibility # Note2: just for backwards compatibility
@ -48,6 +69,7 @@ class Module:
else: else:
raise ConfigurationError(type(self).__name__, key=key) raise ConfigurationError(type(self).__name__, key=key)
# Some nice set magic :-)
required &= set(self.required) required &= set(self.required)
if len(required) != len(self.required): if len(required) != len(self.required):
raise ConfigurationError(type(self).__name__, missing=self.required-required) raise ConfigurationError(type(self).__name__, missing=self.required-required)
@ -59,6 +81,10 @@ class Module:
In case you don't want to type that super()…blabla :-)""" In case you don't want to type that super()…blabla :-)"""
class Module(SettingsBase):
output = None
position = 0
def registered(self, status_handler): def registered(self, status_handler):
"""Called when this module is registered with a status handler""" """Called when this module is registered with a status handler"""
@ -121,7 +147,7 @@ class StandaloneIO(IOHandler):
""" """
I/O handler for standalone usage of i3pystatus (w/o i3status) I/O handler for standalone usage of i3pystatus (w/o i3status)
writing as usual, reading will always return a empty JSON array, Writing works as usual, but reading will always return a empty JSON array,
and the i3bar protocol header and the i3bar protocol header
""" """
@ -132,6 +158,7 @@ class StandaloneIO(IOHandler):
"[]", "[]",
",[]", ",[]",
) )
def __init__(self, interval=1): def __init__(self, interval=1):
super().__init__() super().__init__()
self.interval = interval self.interval = interval
@ -164,9 +191,11 @@ class JSONIO:
@contextmanager @contextmanager
def parse_line(self, line): def parse_line(self, line):
"""Parse a single line of JSON and write modified JSON back. """
Parse a single line of JSON and write modified JSON back.
Usage is quite simple using the usual with-Syntax.""" Usage is quite simple using the usual with-Syntax.
"""
prefix = "" prefix = ""
@ -179,9 +208,7 @@ class JSONIO:
self.io.write_line(prefix + json.dumps(j)) self.io.write_line(prefix + json.dumps(j))
class ClassFinder: class ClassFinder:
""" """Support class to find classes of specific bases in a module"""
Support class to find classes of specific bases in a module
"""
def __init__(self, baseclass, exclude=[]): def __init__(self, baseclass, exclude=[]):
self.baseclass = baseclass self.baseclass = baseclass
@ -192,7 +219,7 @@ class ClassFinder:
def search_module(self, module): def search_module(self, module):
# Neat trick: [(x,y),(u,v)] becomes [(x,u),(y,v)] # Neat trick: [(x,y),(u,v)] becomes [(x,u),(y,v)]
return zip(*inspect.getmembers(module, self.predicate))[1] return list(zip(*inspect.getmembers(module, self.predicate)))[1]
def get_class(self, module): def get_class(self, module):
classes = self.search_module(module) classes = self.search_module(module)
@ -206,6 +233,9 @@ class ClassFinder:
return classes[0] return classes[0]
def instanciate_class(self, module, *args, **kwargs):
return self.get_class(module)(*args, **kwargs)
class i3pystatus: class i3pystatus:
modules = [] modules = []
@ -217,14 +247,8 @@ class i3pystatus:
self.finder = ClassFinder(baseclass=Module, exclude=[Module, IntervalModule, AsyncModule]) self.finder = ClassFinder(baseclass=Module, exclude=[Module, IntervalModule, AsyncModule])
@classmethod def get_instance_for_module(self, module, position, args, kwargs):
def _make_instance(cls, module, position, args, kwargs):
if isinstance(module, types.ModuleType): if isinstance(module, types.ModuleType):
# Okay, we got a module, let's find the class
# and create an instance
cls = self.finder.get_class(module)
if not isinstance(position, int) and not args: if not isinstance(position, int) and not args:
# If the user does this: register(modsde, mdesettings) with mdesettings # If the user does this: register(modsde, mdesettings) with mdesettings
# being a dict Python will put mdesettings into the position argument # being a dict Python will put mdesettings into the position argument
@ -233,7 +257,7 @@ class i3pystatus:
args = (position,) args = (position,)
position = 0 position = 0
module = cls(*args, **kwargs) module = self.finder.instanciate_class(module, *args, **kwargs)
elif args or kwargs: elif args or kwargs:
raise ValueError("Additional arguments are invalid if 'module' is already an object") raise ValueError("Additional arguments are invalid if 'module' is already an object")
@ -242,7 +266,7 @@ class i3pystatus:
def register(self, module, position=0, *args, **kwargs): def register(self, module, position=0, *args, **kwargs):
"""Register a new module.""" """Register a new module."""
module, position = self._make_instance(module, position, args, kwargs) module, position = self.get_instance_for_module(module, position, args, kwargs)
self.modules.append(module) self.modules.append(module)
module.position = position module.position = position