Source code for aglyph.integration.cherrypy

# -*- coding: UTF-8 -*-

# Copyright (c) 2006-2015 Matthew Zipay <mattz@ninthtest.net>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""Classes and utilities for integrating Aglyph with
`CherryPy <http://www.cherrypy.org/>`_.

.. versionadded:: 2.1.0

An example using :class:`aglyph.context.XMLContext`::

   from aglyph.assembler import Assembler
   from aglyph.context import XMLContext
   from aglyph.integration.cherrypy import AglyphDIPlugin
   import cherrypy
   
   context = XMLContext("my-aglyph-context.xml")
   assembler = Assembler(context)
   cherrypy.engine.aglyph = AglyphDIPlugin(cherrypy.engine, assembler)
   cherrypy.engine.aglyph.subscribe()

An example using :class:`aglyph.binder.Binder`::

   from aglyph.integration.cherrypy import AglyphDIPlugin
   from bindings import binder
   import cherrypy
   
   cherrypy.engine.aglyph = AglyphDIPlugin(cherrypy.engine, binder)
   cherrypy.engine.aglyph.subscribe()

In either scenario, you can now use Aglyph to assemble components in
your CherryPy application by publishing an "aglyph-assemble" event to
the `Web Site Process Bus
<https://cherrypy.readthedocs.org/en/latest/pkg/cherrypy.process.html#web-site-process-bus>`_.
This event requires an Aglyph component specification (either an ID or
an object whose dotted name is a component ID)::

   ...
   my_obj = cherrypy.engine.publish("aglyph-assemble", "my-id").pop()
   ...

"""

from __future__ import absolute_import

__author__ = "Matthew Zipay <mattz@ninthtest.net>"
__version__ = "2.1.0"

import logging

from cherrypy.process.plugins import SimplePlugin

__all__ = [
    "AglyphDIPlugin",
]

_logger = logging.getLogger(__name__)


[docs]class AglyphDIPlugin(SimplePlugin): """A `CherryPy <http://www.cherrypy.org/>`_ `plugin <https://cherrypy.readthedocs.org/en/latest/extend.html#plugins>`_ that provides Aglyph dependency injection support to CherryPy applications. The Aglyph DI plugin subscribes to the following channels: aglyph-assemble Publish a component ID to this channel to assemble the component. aglyph-init-singletons Publish to this channel to pre-assemble and cache all *singleton* components. aglyph-clear-singletons Publish to this channel to clear all cached *singleton* components. aglyph-init-borgs Publish to this channel to pre-assemble and cache the shared-states of all *borg* components. aglyph-clear-borgs Publish to this channel to clear all cached *borg* components. aglyph-clear-weakrefs Publish to this channel to clear all cached *weakref* components. """ def __init__(self, bus, assembler, eager_init=True): """ :arg cherrypy.process.wspbus.Bus bus:\ the CherryPy Web Site Process Bus :arg aglyph.assembler.Assembler assembler:\ the configured Aglyph assembler (or binder) :keyword bool eager_init: if ``True``, all *singleton* and\ *borg* components in the assembler's\ context will be pre-assembed and\ cached when the Aglyph DI plugin is\ started """ SimplePlugin.__init__(self, bus) self._assembler = assembler self._eager_init = eager_init @property def eager_init(self): """Return the current value of the eager initialization flag.""" return self._eager_init @eager_init.setter
[docs] def eager_init(self, flag): """Set the eager initialization flag. :arg bool flag: whether (``True``) or not (``False``) the\ Aglyph DI plugin should pre-assemble and cache\ all *singleton* and *borg* components when the\ plugin is (re)started """ self._eager_init = flag
[docs] def start(self): """Subscribe to all Aglyph DI channels. aglyph-assemble Publish a component ID to this channel to assemble the component. aglyph-init-singletons Publish to this channel to pre-assemble and cache all *singleton* components. aglyph-clear-singletons Publish to this channel to clear all cached *singleton* components. aglyph-init-borgs Publish to this channel to pre-assemble and cache the shared-states of all *borg* components. aglyph-clear-borgs Publish to this channel to clear all cached *borg* components. aglyph-clear-weakrefs Publish to this channel to clear all cached *weakref* components. .. note:: If :attr:`eager_init` is ``True``, all *singleton* and *borg* components are pre-assembled and cached before the channels are subscribed. """ if (self._eager_init): self.bus.log( "initializing Aglyph singleton and borg component objects") self.init_singletons() self.init_borgs() self.bus.log("starting Aglyph dependency injection support") self.bus.subscribe("aglyph-assemble", self.assemble) self.bus.subscribe("aglyph-init-singletons", self.init_singletons) self.bus.subscribe("aglyph-clear-singletons", self.clear_singletons) self.bus.subscribe("aglyph-init-borgs", self.init_borgs) self.bus.subscribe("aglyph-clear-borgs", self.clear_borgs) self.bus.subscribe("aglyph-clear-weakrefs", self.clear_weakrefs)
[docs] def stop(self): """Unsubscribe from all Aglyph DI channels. .. note:: After all Aglyph DI channels have been unsubscribed, the *singleton*, *borg*, and *weakref* caches are automatically cleared. """ self.bus.log("stopping Aglyph dependency injection support") self.bus.unsubscribe("aglyph-assemble", self.assemble) self.bus.unsubscribe("aglyph-init-singletons", self.init_singletons) self.bus.unsubscribe("aglyph-clear-singletons", self.clear_singletons) self.bus.unsubscribe("aglyph-init-borgs", self.init_borgs) self.bus.unsubscribe("aglyph-clear-borgs", self.clear_borgs) self.bus.unsubscribe("aglyph-clear-weakrefs", self.clear_weakrefs) self.bus.log( "clearing Aglyph singleton, borg, and weakref component objects") self.clear_singletons() self.clear_borgs() self.clear_weakrefs()
[docs] def assemble(self, component_spec): """Return the object assembled according to *component_spec*. :arg component_spec: a string representing a component dotted\ name or unique ID; or an importable class,\ function, or module :return: a complete object with all of its resolved dependencies This method handles messages published to the **aglyph-assemble** channel. """ self.bus.log("assembling %r" % component_spec) return self._assembler.assemble(component_spec)
[docs] def init_singletons(self): """Assemble and cache all singleton component objects. :return: the initialized singleton component IDs :rtype: :obj:`list` This method handles messages published to the **aglyph-init-singletons** channel. """ singleton_ids = self._assembler.init_singletons() if (singleton_ids): self.bus.log("initialized singletons %s" % repr(singleton_ids)) return singleton_ids
[docs] def clear_singletons(self): """Evict all cached singleton component objects. :return: the evicted singleton component IDs :rtype: :obj:`list` This method handles messages published to the **aglyph-clear-singletons** channel. """ singleton_ids = self._assembler.clear_singletons() if (singleton_ids): self.bus.log("cleared singletons %s" % repr(singleton_ids)) return singleton_ids
[docs] def init_borgs(self): """Assemble and cache the shared-states for all borg component objects. :return: the initialized borg component IDs :rtype: :obj:`list` This method handles messages published to the **aglyph-init-borgs** channel. """ borg_ids = self._assembler.init_borgs() if (borg_ids): self.bus.log("initialized borgs %s" % repr(borg_ids)) return borg_ids
[docs] def clear_borgs(self): """Evict all cached borg component shared-states. :return: the evicted borg component IDs :rtype: :obj:`list` This method handles messages published to the **aglyph-clear-borgs** channel. """ borg_ids = self._assembler.clear_borgs() if (borg_ids): self.bus.log("cleared borgs %s" % repr(borg_ids)) return borg_ids
[docs] def clear_weakrefs(self): """Evict all cached weakref component objects. :return: the evicted weakref component IDs :rtype: :obj:`list` This method handles messages published to the **aglyph-clear-weakrefs** channel. """ weakref_ids = self._assembler.clear_weakrefs() if (weakref_ids): self.bus.log("cleared weakrefs %s" % repr(weakref_ids)) return weakref_ids