Resolves Issues #225, #229, and #274

This commit is contained in:
BJ Dierkes 2014-08-29 18:33:28 -05:00
parent d04ddf0723
commit a3b5317fb2
15 changed files with 516 additions and 154 deletions

View File

@ -35,6 +35,8 @@ Features:
* :issue:`119` - Added cement.utils.shell.Prompt to quickly gather user
input in several different ways.
* :issue:`229` - Ability to override handlers via command line options.
Also related: :issue:`225`.
* :issue:`248` - Added documentation for BASH Auto-Completion example.
* :issue:`249` - Allow utils.shell.exec_cmd* to accept additional.
parameters, passing all `args` and `kwargs` down to subprocess.Popen.
@ -49,12 +51,15 @@ Features:
* :issue:`269` - Allow app.close() to accept an exit code, and exit with
that code.
* :issue:`270` - Add support for multiple template_dirs
* :issue:`274` - Add cement.core.interface.list function
Incompatible:
* :issue:`227` - Standardize handler config section naming conventions.
All handler config sections are now labeled after ``interface.handler``
(i.e. ``output.json``, or ``mail.sendgrid``, etc).
* :issue:`229` - Handler override options deprecate the use of ``--json``,
and ``--yaml`` output handler options.
* :issue:`260` - Extensions/Plugins/Bootstrap modules must accept `app`
argument in `load()` function. See Upgrading doc for more information.

View File

@ -82,6 +82,8 @@ class IArgument(interface.Interface):
:keyword help: The help text for --help output (for that argument).
:keyword action: Must support: ['store', 'store_true', 'store_false',
'store_const']
:keyword choices: A list of valid values that can be passed to an
option whose action is ``store``.
:keyword const: The value stored if action == 'store_const'.
:keyword default: The default value.
:returns: None

View File

@ -5,7 +5,8 @@ import os
import sys
import signal
from ..core import backend, exc, handler, hook, log, config, plugin
from ..core import backend, exc, handler, hook, log, config, plugin, interface
from ..core import output, extension, arg, controller, meta, cache
from ..ext import ext_configparser, ext_argparse, ext_logging
from ..ext import ext_nulloutput, ext_plugin
@ -14,71 +15,90 @@ from ..utils import fs
if sys.version_info[0] >= 3:
from imp import reload # pragma: nocover
from io import StringIO # pragma: nocover
else:
from StringIO import StringIO # pragma: nocover
LOG = minimal_logger(__name__)
class NullOut(object):
def write(self, s):
pass
def flush(self):
pass
def add_output_handler_override_option(app):
def add_handler_override_options(app):
"""
This is a ``post_setup`` hook that adds the ``--json`` argument to the
argument object.
This is a ``post_setup`` hook that adds the handler override options to
the argument parser
:param app: The application object.
"""
if len(handler.list('output')) > 1:
output_handlers = []
for h in handler.list('output'):
output_handlers.append(h())
if app._meta.handler_override_options is None:
return
output_options = [x._meta.label
for x in output_handlers
if x._meta.display_override_option is True]
for i in app._meta.handler_override_options:
if i not in interface.list():
LOG.debug("interface '%s'" % i +
" is not defined, can not override handlers")
continue
# don't display the option if not output handlers have
# display_override_option enabled
if len(output_options) > 0:
if len(handler.list(i)) > 1:
handlers = []
for h in handler.list(i):
handlers.append(h())
help_txt = "%s [%s]" % (
app._meta.output_handler_override_help,
', '.join(output_options)
choices = [x._meta.label
for x in handlers
if x._meta.overridable is True]
# don't display the option if no handlers are overridable
if not len(choices) > 0:
LOG.debug("no handlers are overridable within the " +
"%s interface" % i)
continue
# override things that we need to control
argument_kw = app._meta.handler_override_options[i][1]
argument_kw['dest'] = '%s_handler_override' % i
argument_kw['action'] = 'store'
argument_kw['choices'] = choices
app.args.add_argument(
*app._meta.handler_override_options[i][0],
**app._meta.handler_override_options[i][1]
)
if app._meta.output_handler_override is not None:
app.args.add_argument(
*app._meta.output_handler_override,
help=help_txt,
dest='output_handler_handler',
action='store',
metavar='STR'
)
def output_handler_override(app):
def handler_override(app):
"""
This is a ``post_argument_parsing`` hook that overrides the configured
output handler if ``CementApp.Meta.output_handler_override`` is enabled
and is passed at the command line.
This is a ``post_argument_parsing`` hook that overrides a configured
handler if defined in ``CementApp.Meta.handler_override_options`` and
the option is passed at command line with a valid handler label.
:param app: The application object.
"""
if app._meta.output_handler_override is None:
if app._meta.handler_override_options is None:
return
elif not hasattr(app.pargs, 'output_handler_override'):
return
for i in app._meta.handler_override_options.keys():
if not hasattr(app.pargs, '%s_handler_override' % i):
continue
elif getattr(app.pargs, '%s_handler_override' % i) is None:
continue
else:
# get the argument value from command line
argument = getattr(app.pargs, '%s_handler_override' % i)
setattr(app._meta, '%s_handler' % i, argument)
# and then re-setup the handler
getattr(app, '_setup_%s_handler' % i)()
if app.pargs.output_handler_override is not None:
app._meta.output_handler = app.pargs.output_handler_override
app._setup_output_handler()
def cement_signal_handler(signum, frame):
"""
@ -317,6 +337,43 @@ class CementApp(meta.MetaMixin):
affect whether ``arguments_override_config`` is ``True`` or ``False``.
"""
core_handler_override_options = dict(
output=(['-o'], dict(help='output handler')),
)
"""
Similar to ``CementApp.Meta.handler_override_options`` but these are
the core defaults required by Cement. This dictionary can be
overridden by ``CementApp.Meta.handler_override_options`` (when they
are merged together).
"""
handler_override_options = {}
"""
Dictionary of handler override options that will be added to the
argument parser, and allow the end-user to override handlers. Useful
for interfaces that have multiple uses within the same application
(for example: Output Handler (json, yaml, etc) or maybe a Cloud
Provider Handler (rackspace, digitalocean, amazon, etc).
This dictionary will merge with
``CementApp.Meta.core_handler_override_options`` but this one has
precedence.
Dictionary Format:
.. code-block:: text
<interface_name> = (option_arguments, help_text)
See ``CementApp.Meta.core_handler_override_options`` for an example
of what this should look like.
Note, if set to ``None`` then no options will be defined, and the
``CementApp.Meta.core_meta_override_options`` will be ignore (not
recommended as some extensions rely on this feature).
"""
config_section = None
"""
The base configuration section for the application.
@ -380,19 +437,6 @@ class CementApp(meta.MetaMixin):
class, or an instantiated class object.
"""
output_handler_override = ['-o', '--output']
"""
Options added to ``argument_handler`` command line arguments allowing
the user to override the ``output_handler`` (i.e. ``-o json``,
``-o yaml``, etc.
"""
output_handler_override_help = "output handler"
"""
Help text that is displayed for the
``output_handler_override_option``.
"""
cache_handler = None
"""
A handler class that implements the ICache interface. This can
@ -517,12 +561,6 @@ class CementApp(meta.MetaMixin):
``template_dirs``.
"""
suppress_output = False
"""
Used internally to suppress all console output (for example, when
``--quiet`` is passed at command line).
"""
def __init__(self, label=None, **kw):
super(CementApp, self).__init__(**kw)
@ -691,7 +729,7 @@ class CementApp(meta.MetaMixin):
"Invalid exit status code (must be integer)"
sys.exit(code)
def render(self, data, template=None):
def render(self, data, template=None, out=sys.stdout):
"""
This is a simple wrapper around self.output.render() which simply
returns an empty string if no self.output handler is defined.
@ -699,6 +737,9 @@ class CementApp(meta.MetaMixin):
:param data: The data dictionary to render.
:param template: The template to render to. Default: None (some
output handlers do not use templates).
:param out: A file like object (sys.stdout, or actual file). Set to
``None`` is no output is desired (just render and return).
Default: sys.stdout
"""
for res in hook.run('pre_render', self, data):
@ -719,6 +760,13 @@ class CementApp(meta.MetaMixin):
else:
out_text = str(res)
if out is not None and not hasattr(out, 'write'):
raise TypeError("Argument 'out' must be a 'file' like object")
elif out is not None and out_text is None:
LOG.debug('render() called but output text is None')
elif out:
out.write(out_text)
self._last_rendered = (data, out_text)
return out_text
@ -764,6 +812,22 @@ class CementApp(meta.MetaMixin):
"""A shortcut for self.args.add_argument."""
self.args.add_argument(*args, **kw)
def _suppress_output(self):
if self._meta.debug is True:
LOG.debug('not suppressing console output because of debug mode')
return
LOG.debug('suppressing all console output')
backend.__saved_stdout__ = sys.stdout
backend.__saved_stderr__ = sys.stderr
sys.stdout = NullOut()
sys.stderr = NullOut()
def _unsuppress_output(self):
LOG.debug('unsuppressing all console output')
sys.stdout = backend.__saved_stdout__
sys.stderr = backend.__saved_stderr__
def _lay_cement(self):
"""Initialize the framework."""
LOG.debug("laying cement for the '%s' application" %
@ -772,16 +836,7 @@ class CementApp(meta.MetaMixin):
if '--debug' in self._meta.argv:
self._meta.debug = True
elif '--quiet' in self._meta.argv:
# the following are hacks to suppress console output
# for flag in ['--quiet', '--json', '--yaml']:
self._meta.suppress_output = True
if self._meta.suppress_output:
LOG.debug('suppressing all console output per runtime config')
backend.__saved_stdout__ = sys.stdout
backend.__saved_stderr__ = sys.stderr
sys.stdout = NullOut()
sys.stderr = NullOut()
self._suppress_output()
# start clean
backend.__hooks__ = {}
@ -801,8 +856,8 @@ class CementApp(meta.MetaMixin):
hook.define('post_render')
# register some built-in framework hooks
hook.register('post_setup', add_output_handler_override_option)
hook.register('post_argument_parsing', output_handler_override)
hook.register('post_setup', add_handler_override_options, weight=-99)
hook.register('post_argument_parsing', handler_override, weight=-99)
# define and register handlers
handler.define(extension.IExtension)
@ -998,6 +1053,16 @@ class CementApp(meta.MetaMixin):
action='store_true',
help='suppress all output')
# merge handler override meta data
if self._meta.handler_override_options is not None:
# fucking long names... fuck. anyway, merge the core handler
# override options with developer defined options
core = self._meta.core_handler_override_options.copy()
dev = self._meta.handler_override_options.copy()
core.update(dev)
self._meta.handler_override_options = core
def _setup_controllers(self):
LOG.debug("setting up application controllers")

View File

@ -44,12 +44,12 @@ class CementBaseHandler(meta.MetaMixin):
override any existing defaults under that section.
"""
display_override_option = False
overridable = False
"""
Whether or not to display this handlers label along with other
override options (if handler override options are enabled in
``CementApp``). Generally used for things like
``CementApp.Meta.output_handler_override``
Whether or not handler can be overridden by
``CementApp.Meta.handler_override_options``. Will be listed as an
available choice to override the specific handler (i.e.
``CementApp.Meta.output_handler``, etc).
"""
def __init__(self, **kw):

View File

@ -3,11 +3,22 @@ Cement core interface module.
"""
from ..core import exc
from ..core import exc, backend
DEFAULT_META = ['interface', 'label', 'config_defaults', 'config_section']
def list():
"""
Return a list of defined interfaces (handler types).
:returns: List of defined interfaces
:rtype: list
"""
return backend.__handlers__.keys()
class Interface(object):
"""

View File

@ -9,31 +9,48 @@ from ..ext.ext_configparser import ConfigParserConfigHandler
LOG = minimal_logger(__name__)
# def add_json_option(app):
# """
# This is a ``post_setup`` hook that adds the ``--json`` argument to the
# argument object.
def suppress_output_before_run(app):
"""
This is a ``post_argument_parsing`` hook that suppresses console output if
the ``JsonOutputHandler`` is triggered via command line.
# :param app: The application object.
:param app: The application object.
# """
# app.args.add_argument('--json', dest='output_handler',
# action='store_const',
# help='toggle json output handler',
# const='json')
"""
if not hasattr(app.pargs, 'output_handler_override'):
return
elif app.pargs.output_handler_override == 'json':
app._suppress_output()
# def set_output_handler(app):
# """
# This is a ``pre_run`` hook that overrides the configured output handler
# if ``--json`` is passed at the command line.
def unsuppress_output_before_render(app, data):
"""
This is a ``pre_render`` that unsuppresses console output if
the ``JsonOutputHandler`` is triggered via command line so that the JSON
is the only thing in the output.
# :param app: The application object.
:param app: The application object.
# """
# if '--json' in app._meta.argv:
# app._meta.output_handler = 'json'
# app._setup_output_handler()
"""
if not hasattr(app.pargs, 'output_handler_override'):
return
elif app.pargs.output_handler_override == 'json':
app._unsuppress_output()
def suppress_output_after_render(app, out_text):
"""
This is a ``post_render`` hook that suppresses console output again after
rendering, only if the ``JsonOutputHandler`` is triggered via command
line.
:param app: The application object.
"""
if not hasattr(app.pargs, 'output_handler_override'):
return
elif app.pargs.output_handler_override == 'json':
app._suppress_output()
class JsonOutputHandler(output.CementOutputHandler):
@ -44,10 +61,15 @@ class JsonOutputHandler(output.CementOutputHandler):
library. Please see the developer documentation on
:ref:`Output Handling <dev_output_handling>`.
Note: The cement framework detects the '--json' option and suppresses
output (same as if passing --quiet). Therefore, if debugging or
troubleshooting issues you must pass the --debug option to see whats
going on.
Note: By default, Cement adds the ``-o`` command line option to allow the
end user to override the output handler. For example: passing ``-o json``
will override the default output handler and set it to
``JsonOutputHandler``. See ``CementApp.Meta.handler_override_options``.
This extension forces Cement to suppress console output until
``app.render`` is called (keeping the output pure JSON). If
troubleshooting issues, you will need to pass the ``--debug`` option in
order to unsuppress output and see what's happening.
"""
class Meta:
@ -60,7 +82,7 @@ class JsonOutputHandler(output.CementOutputHandler):
label = 'json'
"""The string identifier of this handler."""
display_override_option = True
overridable = True
def __init__(self, *args, **kw):
super(JsonOutputHandler, self).__init__(*args, **kw)
@ -78,8 +100,6 @@ class JsonOutputHandler(output.CementOutputHandler):
"""
LOG.debug("rendering output as Json via %s" % self.__module__)
sys.stdout = backend.__saved_stdout__
sys.stderr = backend.__saved_stderr__
return json.dumps(data_dict)
@ -114,7 +134,8 @@ class JsonConfigHandler(ConfigParserConfigHandler):
def load(app):
#hook.register('post_setup', add_json_option)
#hook.register('pre_run', set_output_handler)
hook.register('post_argument_parsing', suppress_output_before_run)
hook.register('pre_render', unsuppress_output_before_render)
hook.register('post_render', suppress_output_after_render)
handler.register(JsonOutputHandler)
handler.register(JsonConfigHandler)

View File

@ -10,6 +10,50 @@ from ..ext.ext_configparser import ConfigParserConfigHandler
LOG = minimal_logger(__name__)
def suppress_output_before_run(app):
"""
This is a ``post_argument_parsing`` hook that suppresses console output if
the ``YamlOutputHandler`` is triggered via command line.
:param app: The application object.
"""
if not hasattr(app.pargs, 'output_handler_override'):
return
elif app.pargs.output_handler_override == 'yaml':
app._suppress_output()
def unsuppress_output_before_render(app, data):
"""
This is a ``pre_render`` that unsuppresses console output if
the ``YamlOutputHandler`` is triggered via command line so that the YAML
is the only thing in the output.
:param app: The application object.
"""
if not hasattr(app.pargs, 'output_handler_override'):
return
elif app.pargs.output_handler_override == 'yaml':
app._unsuppress_output()
def suppress_output_after_render(app, out_text):
"""
This is a ``post_render`` hook that suppresses console output again after
rendering, only if the ``YamlOutputHandler`` is triggered via command
line.
:param app: The application object.
"""
if not hasattr(app.pargs, 'output_handler_override'):
return
elif app.pargs.output_handler_override == 'yaml':
app._suppress_output()
class YamlOutputHandler(output.CementOutputHandler):
"""
This class implements the :ref:`IOutput <cement.core.output>`
@ -18,20 +62,21 @@ class YamlOutputHandler(output.CementOutputHandler):
STDOUT. Please see the developer documentation on
:ref:`Output Handling <dev_output_handling>`.
**Note** The cement framework detects the '--yaml' option and suppresses
output (same as if passing --quiet). Therefore, if debugging or
troubleshooting issues you must pass the --debug option to see whats
going on.
Note: By default, Cement adds the ``-o`` command line option to allow the
end user to override the output handler. For example: passing ``-o yaml``
will override the default output handler and set it to
``YamlOutputHandler``. See ``CementApp.Meta.handler_override_options``.
**Note** This extension has an external dependency on `pyYAML`. You must
include `pyYAML` in your application's dependencies as Cement explicitly
does *not* include external dependencies for optional extensions.
This extension forces Cement to suppress console output until
``app.render`` is called (keeping the output pure YAML). If
troubleshooting issues, you will need to pass the ``--debug`` option in
order to unsuppress output and see what's happening.
"""
class Meta:
interface = output.IOutput
label = 'yaml'
display_override_option = True
overridable = True
def __init__(self, *args, **kw):
super(YamlOutputHandler, self).__init__(*args, **kw)
@ -58,34 +103,6 @@ class YamlOutputHandler(output.CementOutputHandler):
return yaml.dump(data_dict)
# def add_yaml_option(app):
# """
# This is a ``post_setup`` hook that adds the ``--yaml`` argument to the
# command line.
# :param app: The application object.
# """
# app.args.add_argument('--yaml',
# dest='output_handler',
# action='store_const',
# help='toggle yaml output handler',
# const='yaml')
# def set_output_handler(app):
# """
# This is a ``pre_run`` hook that overrides the configured output handler
# if ``--yaml`` is passed at the command line.
# :param app: The application object.
# """
# if '--yaml' in app._meta.argv:
# app._meta.output_handler = 'yaml'
# app._setup_output_handler()
class YamlConfigHandler(ConfigParserConfigHandler):
"""
This class implements the :ref:`IConfig <cement.core.config>`
@ -123,7 +140,8 @@ class YamlConfigHandler(ConfigParserConfigHandler):
def load(app):
hook.register('post_argument_parsing', suppress_output_before_run)
hook.register('pre_render', unsuppress_output_before_render)
hook.register('post_render', suppress_output_after_render)
handler.register(YamlOutputHandler)
handler.register(YamlConfigHandler)
#hook.register('post_setup', add_yaml_option)
#hook.register('pre_run', set_output_handler)

View File

@ -1,6 +1,7 @@
"""Cement testing utilities."""
import unittest
from tempfile import mkstemp, mkdtemp
from ..core import backend, foundation
# shortcuts
@ -45,6 +46,8 @@ class CementTestCase(unittest.TestCase):
"""
self.app = self.make_app()
_, self.tmp_file = mkstemp()
self.tmp_dir = mkdtemp()
def make_app(self, *args, **kw):
"""

View File

@ -254,9 +254,9 @@ Multiple Registered Handlers
All handlers and interfaces are unique. In most cases, where the framework
is concerned, only one handler is used. For example, whatever is configured
for the ``log_handler`` will be used and setup as ``app.log``. However, take
for example an Output handler. You might have a default ``output_handler`` of
for example an Output Handler. You might have a default ``output_handler`` of
``mustache``' (a text templating language) but may also want to override that
handler with the ``json`` output handler when '--json' is passed at command
handler with the ``json`` output handler when ``-o json`` is passed at command
line. In order to allow this functionality, both the ``mustache`` and
``json`` output handlers must be registered.
@ -337,3 +337,105 @@ In the real world this may look like ``[cache.memcached]``, or
``[database.mysql]`` depending on what the interface label, and handler
label's are. Additionally, individual handlers can override their config
section by setting ``Meta.config_section``.
Overriding Handlers Via Command Line
------------------------------------
In some use cases, you will want the end user to have access to override the
default handler of a particular interface. For example, Cement ships with
multiple Output Handlers including ``json``, ``yaml``, and ``mustache``. A
typical application might default to using ``mustache`` to render console
output from text templates. That said, without changing any code in the
application, the end user can simply pass the ``-o json`` command line
option and output the same data that is rendered to template, out in pure
JSON.
The only built-in handler override that Cement includes is for the above
mentioned example, but you can add any that your application requires.
The following example shows this in action... note that the following is
already setup by Cement, but we're putting it here for clarity:
.. code-block:: python
from cement.core.foundation import CementApp
class MyApp(CementApp):
class Meta:
label = 'myapp'
# define what extensions we want to load
extensions = ['mustache', 'json', 'yaml']
# define our default output handler
output_handler = 'mustache'
# define our handler override options
handler_override_options = dict(
output = (['-o'], dict(help='output format')),
)
# create the app
app = MyApp()
try:
# setup the app
app.setup()
# define some data for the output handler
data = dict(foo='bar')
# run the app
app.run()
# render something using our output handlers, using mustache by
# default which use the default.m template
app.render(data, 'default.m')
finally:
# close the app
app.close()
Note what we see at command line:
.. code-block:: text
$ python myapp.py --help
usage: myapp.py [-h] [--debug] [--quiet] [-o {yaml,json}]
optional arguments:
-h, --help show this help message and exit
--debug toggle debug output
--quiet suppress all output
-o {yaml,json} output format
Notice the ``-o`` command line option, that includes the choices: ``yaml``
and ``json``. This feature will include all Output Handlers that have the
``overridable`` meta-data option set to ``True``. The MustacheOutputHandler
does not set this option, therefore it does not show up as a valid choice.
Now what happens when we run it?
.. code-block:: text
$ python myapp.py
This text is being rendered via Mustache.
The value of the 'foo' variable is => 'bar'
The above is the default output, using ``mustache`` as our ``output_handler``,
and rendering the output text from a template called ``default.m``. We can
now override the output handler using the ``-o`` option and modify the output
format:
.. code-block:: text
$ python myapp.py -o json
{"foo": "bar"}
Again, any handler can be overridden in this fashion.

View File

@ -160,11 +160,11 @@ And this looks like:
The value of the 'foo' variable is => 'bar'
Optionally, we can use the ``JsonOutputHandler`` via ``--json`` to trigger
Optionally, we can use the ``JsonOutputHandler`` via ``-o json`` to trigger
just Json output (supressing all other output) using our return dictionary:
.. code-block:: text
$ python myapp.py --json
$ python myapp.py -o json
{"foo": "bar"}

View File

@ -73,12 +73,13 @@ Which looks like:
MyApp Does Amazing Things
optional arguments:
-h, --help show this help message and exit
--debug toggle debug output
--quiet suppress all output
--json toggle json output handler
--yaml toggle yaml output handler
-h, --help show this help message and exit
--debug toggle debug output
--quiet suppress all output
-o {json,yaml} output format
Note the ``--json`` and ``--yaml`` configuration options that are provided
by the loaded extensions.
Note the ``-o`` command line option that are provided by Cement allowing the
end user to override the output handler with the available/loaded extensions
(that support this feature).

View File

@ -12,6 +12,40 @@ Upgrading from 2.2.x to 2.4.x
Cement 2.4 introduced a few incompatible changes from the previous 2.2 stable
release, as noted in the :ref:`Changelog <changelog>`.
error: unrecognized arguments: --json/--yaml
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
After upgrading to Cement > 2.3.2 you might encounter the error:
.. code-block:: text
error: unrecognized arguments: --json
Or similar errors like:
.. code-block:: text
error: unrecognized arguments: --yaml
This is due to a design change, and a new feature allowing the end user to
optionally override handlers via command line. Rather than having a unique
option for every type of output handler, you now have one option that allows
overriding the defined output handler by passing it the handler label.
Note that only handlers that have ``overridable = True`` in their meta-data
will be valid options.
To resolve this issue, you simply need to pass ``-o json`` or ``-o yaml`` at
command line to override the default output handler.
Related:
* :issue:`229`
NoSectionError: No section: 'log'
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -33,6 +67,11 @@ The necessary change to resolve this issue is to change all references of
``log`` in relation to the log configuration section, to ``log.logging``.
Related:
* :issue:`227`
TypeError: load() takes no arguments (1 given)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -2,6 +2,7 @@
import os
import sys
import json
from cement.core import foundation, exc, backend, config, extension, plugin
from cement.core import log, output, handler, hook, arg, controller
from cement.utils import test
@ -46,6 +47,7 @@ def my_hook_three(app):
class FoundationTestCase(test.CementCoreTestCase):
def setUp(self):
super(FoundationTestCase, self).setUp()
self.app = self.make_app('my_app')
def test_argv_is_none(self):
@ -139,6 +141,34 @@ class FoundationTestCase(test.CementCoreTestCase):
app.output = None
app.render(dict(foo='bar'))
def test_render_out_to_file(self):
self.app = self.make_app(APP, extensions=['json'],
output_handler='json')
self.app.setup()
self.app.run()
f = open(self.tmp_file, 'w')
self.app.render(dict(foo='bar'), out=f)
f.close()
f = open(self.tmp_file, 'r')
data = json.load(f)
f.close()
self.eq(data, dict(foo='bar'))
@test.raises(TypeError)
def test_render_bad_out(self):
self.app.setup()
self.app.run()
try:
self.app.render(dict(foo='bar'), out='bogus type')
except TypeError as e:
self.eq(e.args[0], "Argument 'out' must be a 'file' like object")
raise
@test.raises(exc.FrameworkError)
def test_bad_label(self):
try:
@ -171,7 +201,6 @@ class FoundationTestCase(test.CementCoreTestCase):
def test_lay_cement(self):
app = self.make_app('test', argv=['--quiet'])
app = self.make_app('test', argv=['--json', '--yaml'])
def test_none_member(self):
class Test(object):
@ -270,3 +299,42 @@ class FoundationTestCase(test.CementCoreTestCase):
except AssertionError as e:
self.eq(e.args[0], "Invalid exit status code (must be integer)")
raise
def test_handler_override_options(self):
app = self.make_app(APP,
argv=['-o', 'json'],
extensions=['yaml', 'json'],
)
app.setup()
app.run()
self.eq(app._meta.output_handler, 'json')
def test_handler_override_options_is_none(self):
app = self.make_app(APP,
core_handler_override_options=None,
handler_override_options=None
)
app.setup()
app.run()
def test_handler_override_invalid_interface(self):
app = self.make_app(APP,
handler_override_options=dict(
bogus_interface = (['-f'], ['--foo'], {}),
)
)
app.setup()
app.run()
def test_handler_override_options_not_passed(self):
app = self.make_app(APP,
extensions=['yaml', 'json'],
)
app.setup()
app.run()
def test_suppress_output_while_debug(self):
app = self.make_app(APP, debug=True)
app.setup()
app._suppress_output()

View File

@ -5,6 +5,10 @@ import sys
from tempfile import mkstemp
from cement.core import handler, backend, hook
from cement.utils import test
from cement.utils.misc import rando
APP = rando()[:12]
class JsonExtTestCase(test.CementExtTestCase):
CONFIG = '''{
@ -38,7 +42,7 @@ class JsonExtTestCase(test.CementExtTestCase):
output_handler='json',
config_handler='json',
config_files = [self.tmppath],
argv=['--json']
argv=['-o', 'json']
)
def test_json(self):
@ -67,3 +71,13 @@ class JsonExtTestCase(test.CementExtTestCase):
self.eq(self.app.config.get_section_dict('section'),
self.CONFIG_PARSED['section'])
def test_handler_override_options_is_none(self):
app = self.make_app(APP,
extensions=['json'],
core_handler_override_options={},
handler_override_options={}
)
app.setup()
app.run()
app.render(dict(foo='bar'))

View File

@ -6,6 +6,9 @@ import yaml
from tempfile import mkstemp
from cement.core import handler, hook
from cement.utils import test
from cement.utils.misc import rando
APP = rando()[:12]
class YamlExtTestCase(test.CementTestCase):
@ -41,7 +44,7 @@ class YamlExtTestCase(test.CementTestCase):
config_handler='yaml',
output_handler='yaml',
config_files = [self.tmppath],
argv=['--yaml']
argv=['-o', 'yaml']
)
def tearDown(self):
@ -74,3 +77,13 @@ class YamlExtTestCase(test.CementTestCase):
self.eq(self.app.config.get_section_dict('section'),
self.CONFIG_PARSED['section'])
def test_handler_override_options_is_none(self):
app = self.make_app(APP,
extensions=['yaml'],
core_handler_override_options=None,
handler_override_options=None
)
app.setup()
app.run()
app.render(dict(foo='bar'))