Skip to content

Instantly share code, notes, and snippets.

@rmorshea
Created December 15, 2018 18:31
Show Gist options
  • Save rmorshea/1d2e98d2a5ad360d0586b1d494284ed8 to your computer and use it in GitHub Desktop.
Save rmorshea/1d2e98d2a5ad360d0586b1d494284ed8 to your computer and use it in GitHub Desktop.
component_application
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