mirror of
https://github.com/datafolklabs/cement.git
synced 2026-02-06 13:56:49 +00:00
Resolves Issue #395
This commit is contained in:
parent
1f98a434e6
commit
d17ff65c67
@ -51,6 +51,8 @@ Features:
|
||||
* :issue:`389` - ConfigObj support for Python 3
|
||||
* :issue:`394` - Watchdog extension for cross-platform filesystem event
|
||||
monitoring
|
||||
* :issue:`395` - Ability to pass metadata keyword arguments to handlers
|
||||
via ``CementApp.Meta.meta_defaults``.
|
||||
|
||||
Refactoring;
|
||||
|
||||
|
||||
@ -416,6 +416,32 @@ class CementApp(meta.MetaMixin):
|
||||
config_defaults = None
|
||||
"""Default configuration dictionary. Must be of type 'dict'."""
|
||||
|
||||
meta_defaults = {}
|
||||
"""
|
||||
Default metadata dictionary used to pass high level options from the
|
||||
application down to handlers at the point they are registered by the
|
||||
framework **if the handler has not already been instantiated**.
|
||||
|
||||
For example, if requiring the ``json`` extension, you might want to
|
||||
override ``JsonOutputHandler.Meta.json_module`` with ``ujson`` by
|
||||
doing the following
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.foundation import CementApp
|
||||
from cement.utils.misc import init_defaults
|
||||
|
||||
META = init_defaults('output.json')
|
||||
META['output.json']['json_module'] = 'ujson'
|
||||
|
||||
class MyApp(CementApp):
|
||||
class Meta:
|
||||
label = 'myapp'
|
||||
extensions = ['json']
|
||||
meta_defaults = META
|
||||
|
||||
"""
|
||||
|
||||
catch_signals = SIGNALS
|
||||
"""
|
||||
List of signals to catch, and raise exc.CaughtSignal for.
|
||||
@ -1170,7 +1196,17 @@ class CementApp(meta.MetaMixin):
|
||||
self.catch_signal(signum)
|
||||
|
||||
def _resolve_handler(self, handler_type, handler_def, raise_error=True):
|
||||
han = self.handler.resolve(handler_type, handler_def, raise_error)
|
||||
meta_defaults = {}
|
||||
if type(handler_def) == str:
|
||||
_meta_label = "%s.%s" % (handler_type, handler_def)
|
||||
meta_defaults = self._meta.meta_defaults.get(_meta_label, {})
|
||||
elif hasattr(handler_def, 'Meta'):
|
||||
_meta_label = "%s.%s" % (handler_type, handler_def.Meta.label)
|
||||
meta_defaults = self._meta.meta_defaults.get(_meta_label, {})
|
||||
|
||||
han = self.handler.resolve(handler_type, handler_def,
|
||||
raise_error=raise_error,
|
||||
meta_defaults=meta_defaults)
|
||||
if han is not None:
|
||||
han._setup(self)
|
||||
return han
|
||||
|
||||
@ -256,7 +256,7 @@ class HandlerManager(object):
|
||||
|
||||
return False
|
||||
|
||||
def resolve(self, handler_type, handler_def, raise_error=True):
|
||||
def resolve(self, handler_type, handler_def, **kwargs):
|
||||
"""
|
||||
Resolves the actual handler, as it can be either a string identifying
|
||||
the handler to load from self.__handlers__, or it can be an
|
||||
@ -265,9 +265,12 @@ class HandlerManager(object):
|
||||
:param handler_type: The type of handler (aka the interface label)
|
||||
:param handler_def: The handler as defined in CementApp.Meta.
|
||||
:type handler_def: str, uninstantiated object, or instantiated object
|
||||
:param raise_error: Whether or not to raise an exception if unable
|
||||
:keyword raise_error: Whether or not to raise an exception if unable
|
||||
to resolve the handler.
|
||||
:type raise_error: boolean
|
||||
:keywork meta_defaults: Optional meta-data dictionary used as
|
||||
defaults to pass when instantiating uninstantiated handlers. See
|
||||
``CementApp.Meta.meta_defaults``.
|
||||
:returns: The instantiated handler object.
|
||||
|
||||
Usage:
|
||||
@ -284,15 +287,18 @@ class HandlerManager(object):
|
||||
log = app.handler.resolve('log', ColorLogHandler())
|
||||
|
||||
"""
|
||||
raise_error = kwargs.get('raise_error', True)
|
||||
meta_defaults = kwargs.get('meta_defaults', {})
|
||||
han = None
|
||||
|
||||
if type(handler_def) == str:
|
||||
han = self.get(handler_type, handler_def)()
|
||||
han = self.get(handler_type, handler_def)(**meta_defaults)
|
||||
elif hasattr(handler_def, '_meta'):
|
||||
if not self.registered(handler_type, handler_def._meta.label):
|
||||
self.register(handler_def.__class__)
|
||||
han = handler_def
|
||||
elif hasattr(handler_def, 'Meta'):
|
||||
han = handler_def()
|
||||
han = handler_def(**meta_defaults)
|
||||
if not self.registered(handler_type, han._meta.label):
|
||||
self.register(handler_def)
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ class CementTestCase(unittest.TestCase):
|
||||
super(CementTestCase, self).__init__(*args, **kw)
|
||||
self.tmp_file = None
|
||||
self.tmp_dir = None
|
||||
self.rando = None
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@ -59,6 +60,10 @@ class CementTestCase(unittest.TestCase):
|
||||
_, self.tmp_file = mkstemp(prefix=_prefix)
|
||||
self.tmp_dir = mkdtemp(prefix=_prefix)
|
||||
|
||||
# create a random string for each test (useful to verify things
|
||||
# uniquely so every test isn't using the same "My Test String")
|
||||
self.rando = rando()[:12]
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Tears down the test environment (if necessary), removes any temporary
|
||||
|
||||
@ -63,15 +63,99 @@ Related:
|
||||
* :issue:`394`
|
||||
|
||||
|
||||
Extensions
|
||||
^^^^^^^^^^
|
||||
Ability To Pass Meta Defaults From CementApp.Meta Down To Handlers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* :ref:`Jinja2 <cement.ext.ext_jinja2>` - Provides template support using
|
||||
the Jinja2 language.
|
||||
Cement handlers are often referenced by their label, and not passed as
|
||||
pre-instantiated objects which requires the framework to instantiate them
|
||||
dynamically with no keyword arguments.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.foundation import CementApp
|
||||
|
||||
class MyApp(CementApp):
|
||||
class Meta:
|
||||
label = 'myapp'
|
||||
extensions = ['json']
|
||||
|
||||
In the above, Cement will load the ``json`` extension, which
|
||||
registers ``JsonOutputHandler``. When it comes time to recall that handler,
|
||||
it is looked up as ``output.json`` where ``output`` is the handler type
|
||||
(interface) and ``json`` is the handler label. The class is then instantiated
|
||||
without any arguments or keyword arguments before use. If a developer needed
|
||||
to override any meta options in ``JsonOutputHandler.Meta`` they would
|
||||
**previously** have had to sub-class it. Consider the following example,
|
||||
where we sub-class ``JsonOutputHandler`` in order to override
|
||||
``JsonOutputHandler.Meta.json_module``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.foundation import CementApp
|
||||
from cement.ext.ext_json import JsonOutputHandler
|
||||
|
||||
class MyJsonOutputHandler(JsonOutputHandler):
|
||||
class Meta:
|
||||
json_module = 'ujson'
|
||||
|
||||
def override_json_output_handler(app):
|
||||
app.handler.register(MyJsonOutputHandler, force=True)
|
||||
|
||||
class MyApp(CementApp):
|
||||
class Meta:
|
||||
label = 'myapp'
|
||||
extensions = ['json']
|
||||
hooks = [
|
||||
('post_setup', override_json_output_handler)
|
||||
]
|
||||
|
||||
|
||||
If there were anything else in the ``JsonOutputHandler`` that the developer
|
||||
needed to subclass, this would be fine. However the purpose of the above is
|
||||
soley to override ``JsonOutputHandler.Meta.json_module``, which is tedious.
|
||||
|
||||
As of Cement 2.9, the above can be accomplished more-easily by the following
|
||||
by way of ``CementApp.Meta.meta_defaults`` (similar to how ``config_defaults``
|
||||
are handled:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cement.core.foundation import CementApp
|
||||
from cement.utils.misc import init_defaults
|
||||
|
||||
META = init_defaults('output.json')
|
||||
META['output.json']['json_module'] = 'ujson'
|
||||
|
||||
class MyApp(CementApp):
|
||||
class Meta:
|
||||
label = 'myapp'
|
||||
extensions = ['json']
|
||||
output_handler = 'json'
|
||||
meta_defaults = META
|
||||
|
||||
|
||||
When ``JsonOutputHandler`` is instantiated, the defaults from
|
||||
``META['output.json']`` will be passed as ``**kwargs`` (overriding builtin
|
||||
meta options).
|
||||
|
||||
Related:
|
||||
|
||||
* :issue:`395`
|
||||
|
||||
|
||||
Additional Extensions
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* :ref:`Jinja2 <cement.ext.ext_jinja2>` - Provides template based output
|
||||
handling using the Jinja2 templating language
|
||||
* :ref:`Redis <cement.ext.ext_redis>` - Provides caching support using
|
||||
Redis backend.
|
||||
Redis backend
|
||||
* :ref:`Watchdog <cement.ext.ext_watchdog>` - Provides cross-platform
|
||||
filesystem event monitoring using Watchdog library.
|
||||
filesystem event monitoring using the Watchdog library.
|
||||
* :ref:`Handlebars <cement.ext.ext_handlebars>` - Provides template based
|
||||
output handling using the Handlebars templating language
|
||||
|
||||
|
||||
|
||||
|
||||
@ -535,3 +535,13 @@ class FoundationTestCase(test.CementCoreTestCase):
|
||||
|
||||
app.__import__('time')
|
||||
app.__import__('sleep', from_module='time')
|
||||
|
||||
def test_meta_defaults(self):
|
||||
DEBUG_FORMAT = "TEST DEBUG FORMAT - %s" % self.rando
|
||||
META = {}
|
||||
META['log.logging'] = {}
|
||||
META['log.logging']['debug_format'] = DEBUG_FORMAT
|
||||
app = self.make_app(meta_defaults=META)
|
||||
app.setup()
|
||||
self.eq(app.log._meta.debug_format, DEBUG_FORMAT)
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user