Created
December 15, 2018 18:31
-
-
Save rmorshea/1d2e98d2a5ad360d0586b1d494284ed8 to your computer and use it in GitHub Desktop.
component_application
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import inspect | |
from weakref import finalize | |
class Component: | |
def _prepare(self): | |
pass | |
def _require(self): | |
pass | |
def _finalize(self): | |
pass | |
def is_component(x): | |
if isinstance(x, type): | |
for method in ["_prepare", "_require", "_finalize"]: | |
if not callable(getattr(x, "_prepare", None)): | |
break | |
else: | |
return True | |
return False | |
class MetaApplication(type): | |
def __init__(cls, name, base, attrs): | |
components = [] | |
for k in dir(cls): | |
v = getattr(cls, k, None) | |
if is_component(v): | |
if not k.startswith("_"): | |
raise TypeError("Builtin components must be private.") | |
components.append(k) | |
cls._builtin_components = tuple(components) | |
class Application(metaclass=MetaApplication): | |
def __init__(self, settings=None, **custom_components): | |
components = {} | |
settings = settings or {} | |
self._custom_components = tuple("_" + n for n in custom_components) | |
# initialize builtin components with settings | |
for attr in self._builtin_components: | |
key = attr[1:] | |
cls = getattr(self, attr) | |
options = settings.get(key, {}) | |
instance = cls(self) | |
instance._prepare(**options) | |
components[key] = instance | |
# initialize custom components with settings | |
for attr in self._custom_components: | |
key = attr[1:] | |
cls = custom_components[key] | |
if not is_component(cls): | |
raise TypeError("Expected a component, not %r." % cls) | |
options = settings.get(key, {}) | |
instance = cls() | |
instance._prepare(**options) | |
components[key] = instance | |
# pass required components to dependent ones | |
for key, cmpt in components.items(): | |
requirements = {} | |
for param in inspect.signature(cmpt._require).parameters.values(): | |
expected, actual = param.annotation, components.get(param.name) | |
if not (param.name in components and isinstance(actual, expected)): | |
msg = "The component %r expected %r to be an instance of %r, but found %r." | |
raise ValueError(msg % (key, param.name, expected, actual)) | |
else: | |
requirements[param.name] = components[param.name] | |
cmpt._require(**requirements) | |
# assign initialized components to the application | |
for key, cmpt in components.items(): | |
setattr(self, "_" + key, cmpt) | |
# register callback to destory components on deletion | |
finalize(self, self._finalize) | |
def _finalize(self): | |
for attr in self._builtin_components: | |
getattr(self, attr)._finalize() | |
for attr in self._custom_components: | |
getattr(self, attr)._finalize() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment