123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- """Cement core hooks module."""
- import operator
- import types
- from ..core import exc, backend
- from ..utils.misc import minimal_logger
- LOG = minimal_logger(__name__)
- class HookManager(object):
- """
- Manages the hook system to define, get, run, etc hooks within the
- the Cement Framework and applications Built on Cement (tm).
- :param use_backend_globals: Whether to use backend globals (backward
- compatibility and deprecated).
- """
- def __init__(self, use_backend_globals=False):
- if use_backend_globals is True:
- self.__hooks__ = backend.__hooks__
- else:
- self.__hooks__ = {}
- def define(self, name):
- """
- Define a hook namespace that the application and plugins can register
- hooks in.
- :param name: The name of the hook, stored as hooks['name']
- :raises: cement.core.exc.FrameworkError
- Usage:
- .. code-block:: python
- from cement.core.foundation import CementApp
- with CementApp('myapp') as app:
- app.hook.define('my_hook_name')
- """
- LOG.debug("defining hook '%s'" % name)
- if name in self.__hooks__:
- raise exc.FrameworkError("Hook name '%s' already defined!" % name)
- self.__hooks__[name] = []
- def defined(self, hook_name):
- """
- Test whether a hook name is defined.
- :param hook_name: The name of the hook.
- I.e. ``my_hook_does_awesome_things``.
- :returns: True if the hook is defined, False otherwise.
- :rtype: ``boolean``
- Usage:
- .. code-block:: python
- from cement.core.foundation import CementApp
- with CementApp('myapp') as app:
- app.hook.defined('some_hook_name'):
- # do something about it
- pass
- """
- if hook_name in self.__hooks__:
- return True
- else:
- return False
- def register(self, name, func, weight=0):
- """
- Register a function to a hook. The function will be called, in order
- of weight, when the hook is run.
- :param name: The name of the hook to register too.
- I.e. ``pre_setup``, ``post_run``, etc.
- :param func: The function to register to the hook. This is an
- *un-instantiated*, non-instance method, simple function.
- :param weight: The weight in which to order the hook function.
- :type weight: ``int``
- Usage:
- .. code-block:: python
- from cement.core.foundation import CementApp
- def my_hook_func(app):
- # do something with app?
- return True
- with CementApp('myapp') as app:
- app.hook.define('my_hook_name')
- app.hook.register('my_hook_name', my_hook_func)
- """
- if name not in self.__hooks__:
- LOG.debug("hook name '%s' is not defined! ignoring..." % name)
- return False
- LOG.debug("registering hook '%s' from %s into hooks['%s']" %
- (func.__name__, func.__module__, name))
- # Hooks are as follows: (weight, name, func)
- self.__hooks__[name].append((int(weight), func.__name__, func))
- def run(self, name, *args, **kwargs):
- """
- Run all defined hooks in the namespace. Yields the result of each
- hook function run.
- :param name: The name of the hook function.
- :param args: Additional arguments to be passed to the hook functions.
- :param kwargs: Additional keyword arguments to be passed to the hook
- functions.
- :raises: FrameworkError
- Usage:
- .. code-block:: python
- from cement.core.foundation import CementApp
- def my_hook_func(app):
- # do something with app?
- return True
- with CementApp('myapp') as app:
- app.hook.define('my_hook_name')
- app.hook.register('my_hook_name', my_hook_func)
- for res in app.hook.run('my_hook_name', self):
- # do something with the result?
- pass
- """
- if name not in self.__hooks__:
- raise exc.FrameworkError("Hook name '%s' is not defined!" % name)
- # Will order based on weight (the first item in the tuple)
- self.__hooks__[name].sort(key=operator.itemgetter(0))
- for hook in self.__hooks__[name]:
- LOG.debug("running hook '%s' (%s) from %s" %
- (name, hook[2], hook[2].__module__))
- res = hook[2](*args, **kwargs)
- # Check if result is a nested generator - needed to support e.g.
- # asyncio
- if isinstance(res, types.GeneratorType):
- for _res in res:
- yield _res
- else:
- yield res
- # the following is only used for backward compat with < 2.7.x!
- def define(name):
- """
- DEPRECATION WARNING: This function is deprecated as of Cement 2.7.x and
- will be removed in future versions of Cement.
- Use ``CementApp.hook.define()`` instead.
- ---
- Define a hook namespace that plugins can register hooks in.
- :param name: The name of the hook, stored as hooks['name']
- :raises: cement.core.exc.FrameworkError
- Usage:
- .. code-block:: python
- from cement.core import hook
- hook.define('myhookname_hook')
- """
- # only log debug for now as this won't be removed until Cement 3.x and
- # we don't have access to CementApp.Meta.ignore_deprecation_warnings here
- LOG.debug(
- 'Cement Deprecation Warning: `hook.define()` has been deprecated, '
- 'and will be removed in future versions of Cement. You should now '
- 'use `CementApp.hook.define()` instead.'
- )
- LOG.debug("defining hook '%s'" % name)
- if name in backend.__hooks__:
- raise exc.FrameworkError("Hook name '%s' already defined!" % name)
- backend.__hooks__[name] = []
- def defined(hook_name):
- """
- DEPRECATION WARNING: This function is deprecated as of Cement 2.7.x and
- will be removed in future versions of Cement.
- Use ``CementApp.hook.defined()`` instead.
- ---
- Test whether a hook name is defined.
- :param hook_name: The name of the hook.
- I.e. ``my_hook_does_awesome_things``.
- :returns: True if the hook is defined, False otherwise.
- :rtype: ``boolean``
- """
- # only log debug for now as this won't be removed until Cement 3.x and
- # we don't have access to CementApp.Meta.ignore_deprecation_warnings here
- LOG.debug(
- 'Cement Deprecation Warning: `hook.defined()` has been deprecated, '
- 'and will be removed in future versions of Cement. You should now '
- 'use `CementApp.hook.defined()` instead.'
- )
- if hook_name in backend.__hooks__:
- return True
- else:
- return False
- def register(name, func, weight=0):
- """
- DEPRECATION WARNING: This function is deprecated as of Cement 2.7.x and
- will be removed in future versions of Cement.
- Use ``CementApp.hook.register()`` instead.
- ---
- Register a function to a hook. The function will be called, in order of
- weight, when the hook is run.
- :param name: The name of the hook to register too. I.e. ``pre_setup``,
- ``post_run``, etc.
- :param func: The function to register to the hook. This is an
- *un-instantiated*, non-instance method, simple function.
- :param weight: The weight in which to order the hook function.
- :type weight: ``int``
- Usage:
- .. code-block:: python
- from cement.core import hook
- def my_hook(*args, **kwargs):
- # do something here
- res = 'Something to return'
- return res
- hook.register('post_setup', my_hook)
- """
- # only log debug for now as this won't be removed until Cement 3.x and
- # we don't have access to CementApp.Meta.ignore_deprecation_warnings here
- LOG.debug(
- 'Cement Deprecation Warning: `hook.register()` has been deprecated, '
- 'and will be removed in future versions of Cement. You should now '
- 'use `CementApp.hook.register()` instead.'
- )
- if name not in backend.__hooks__:
- LOG.debug("hook name '%s' is not defined! ignoring..." % name)
- return False
- LOG.debug("registering hook '%s' from %s into hooks['%s']" %
- (func.__name__, func.__module__, name))
- # Hooks are as follows: (weight, name, func)
- backend.__hooks__[name].append((int(weight), func.__name__, func))
- def run(name, *args, **kwargs):
- """
- DEPRECATION WARNING: This function is deprecated as of Cement 2.7.x and
- will be removed in future versions of Cement.
- Use ``CementApp.hook.run()`` instead.
- ---
- Run all defined hooks in the namespace. Yields the result of each hook
- function run.
- :param name: The name of the hook function.
- :param args: Additional arguments to be passed to the hook functions.
- :param kwargs: Additional keyword arguments to be passed to the hook
- functions.
- :raises: FrameworkError
- Usage:
- .. code-block:: python
- from cement.core import hook
- for result in hook.run('hook_name'):
- # do something with result from each hook function
- ...
- """
- # only log debug for now as this won't be removed until Cement 3.x and
- # we don't have access to CementApp.Meta.ignore_deprecation_warnings here
- LOG.debug(
- 'Cement Deprecation Warning: `hook.run()` has been deprecated, '
- 'and will be removed in future versions of Cement. You should now '
- 'use `CementApp.hook.run()` instead.'
- )
- if name not in backend.__hooks__:
- raise exc.FrameworkError("Hook name '%s' is not defined!" % name)
- # Will order based on weight (the first item in the tuple)
- backend.__hooks__[name].sort(key=operator.itemgetter(0))
- for hook in backend.__hooks__[name]:
- LOG.debug("running hook '%s' (%s) from %s" %
- (name, hook[2], hook[2].__module__))
- res = hook[2](*args, **kwargs)
- # Check if result is a nested generator - needed to support e.g.
- # asyncio
- if isinstance(res, types.GeneratorType):
- for _res in res:
- yield _res
- else:
- yield res
|