Deprecate App.Meta.base_controller and Scrub App.Meta Options

- Resolves Issue #519
- Resolves Issue #522
This commit is contained in:
BJ Dierkes 2018-08-10 18:32:42 -05:00
parent 18438aba81
commit 5cd8f952ec
9 changed files with 82 additions and 89 deletions

View File

@ -13,6 +13,7 @@ from ..core.handler import HandlerManager
from ..core.hook import HookManager
from ..utils.misc import is_true, minimal_logger
from ..utils import fs, misc
from ..ext.ext_argparse import ArgparseController as Controller
# The `imp` module is deprecated in favor of `importlib` in 3.4, but it
# wasn't introduced until 3.1. Finally, reload is a builtin on Python < 3
@ -442,17 +443,6 @@ class App(meta.MetaMixin):
A handler class that implements the Cache interface.
"""
base_controller = None
"""
This is the base application controller. If a controller is set,
runtime operations are passed to the controller for command
dispatch and argument parsing when ``App.run()`` is called.
Note that Cement will automatically set the ``base_controller`` to a
registered controller whose label is ``base`` (only if
``base_controller`` is not currently set).
"""
extensions = []
"""List of additional framework extensions to load."""
@ -920,7 +910,7 @@ class App(meta.MetaMixin):
if self.controller:
return_val = self.controller._dispatch()
else:
self._parse_args()
self._parse_args() # pragma: nocover
LOG.debug('running post_run hook')
for res in self.hook.run('post_run', self):
@ -1200,20 +1190,19 @@ class App(meta.MetaMixin):
self.catch_signal(signum)
def _resolve_handler(self, handler_type, handler_def, raise_error=True):
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, {})
# 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,
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
setup=True)
return han
def _setup_extension_handler(self):
LOG.debug("setting up %s.extension handler" % self._meta.label)
@ -1527,21 +1516,22 @@ class App(meta.MetaMixin):
def _setup_controllers(self):
LOG.debug("setting up application controllers")
if self._meta.base_controller is not None:
cntr = self._resolve_handler('controller',
self._meta.base_controller)
self.controller = cntr
self._meta.base_controller = self.controller
elif self._meta.base_controller is None:
if self.handler.registered('controller', 'base'):
self.controller = self._resolve_handler('controller', 'base')
self._meta.base_controller = self.controller
if self.handler.registered('controller', 'base'):
self.controller = self._resolve_handler('controller', 'base')
# This is necessary for some backend usage
if self._meta.base_controller is not None:
if self._meta.base_controller._meta.label != 'base':
raise exc.FrameworkError("Base controllers must have " +
"a label of 'base'.")
else:
class DefaultBaseController(Controller):
class Meta:
label = 'base'
def _default(self):
# don't enforce anything cause developer might not be
# using controllers... if they are, they should define
# a base controller.
pass
self.handler.register(DefaultBaseController)
self.controller = self._resolve_handler('controller', 'base')
def validate_config(self):
"""
@ -1702,6 +1692,5 @@ class TestApp(App):
label = "app-%s" % misc.rando()[:12]
config_files = []
argv = []
base_controller = None
arguments = []
exit_on_close = False

View File

@ -335,7 +335,7 @@ class HandlerManager(object):
to resolve the handler.
meta_defaults (dict): Optional meta-data dictionary used as
defaults to pass when instantiating uninstantiated handlers.
See ``App.Meta.meta_defaults``.
Use ``App.Meta.meta_defaults`` by default.
setup (bool): Whether or not to call ``.setup()`` before return.
Default: ``False``
@ -357,7 +357,18 @@ class HandlerManager(object):
"""
raise_error = kwargs.get('raise_error', True)
meta_defaults = kwargs.get('meta_defaults', {})
meta_defaults = kwargs.get('meta_defaults', None)
if meta_defaults is None:
meta_defaults = {}
if type(handler_def) == str:
_meta_label = "%s.%s" % (interface, handler_def)
meta_defaults = self.app._meta.meta_defaults.get(_meta_label,
{})
elif hasattr(handler_def, 'Meta'):
_meta_label = "%s.%s" % (interface, handler_def.Meta.label)
meta_defaults = self.app._meta.meta_defaults.get(_meta_label,
{})
setup = kwargs.get('setup', False)
han = None

View File

@ -339,8 +339,7 @@ class ArgparseController(ControllerHandler):
if contr == self.__class__:
continue
contr = contr()
contr._setup(self.app)
contr = self.app.handler.resolve('controller', contr, setup=True)
unresolved_controllers.append(contr)
# treat self/base separately

View File

@ -7,7 +7,7 @@ import os
import inspect
import yaml
import shutil
from .. import Controller, minimal_logger, shell, FrameworkError
from .. import Controller, minimal_logger, shell
from ..utils.version import VERSION, get_version
LOG = minimal_logger(__name__)
@ -133,14 +133,6 @@ def setup_template_items(app):
template_dirs = []
template_items = []
# nothing will work without a base controller
try:
assert app._meta.base_controller is not None, \
"The ext.generate extension requires an application base " + \
"controller, but none is defined!"
except AssertionError as e:
raise FrameworkError(e.args[0])
# look in app template dirs
for path in app._meta.template_dirs:
subpath = os.path.join(path, 'generate')

View File

@ -3,6 +3,7 @@ Cement scrub extension module.
"""
import re
from .. import Controller
from ..utils.misc import minimal_logger
LOG = minimal_logger(__name__)
@ -27,22 +28,39 @@ def extend_scrub(app):
app.extend('scrub', scrub)
if hasattr(app._meta, 'scrub_argument'):
arg = app._meta.scrub_argument
else:
arg = ['--scrub']
if hasattr(app._meta, 'scrub_argument_help'):
arg_help = app._meta.scrub_argument_help
else:
arg_help = 'obfuscate sensitive data from output'
class ScrubController(Controller):
"""
Add embedded options to the base controller to support scrubbing output.
"""
app.args.add_argument(*arg,
help=arg_help,
action='store_true',
dest='scrub')
class Meta:
#: Controller label
label = 'scrub'
#: Parent controller to stack ontop of
stacked_on = 'base'
#: Stacking method
stacked_type = 'embedded'
#: Command line argument options
argument_options = ['--scrub']
#: Command line argument options help
argument_help = 'obfuscate sensitive data from rendered output'
def _pre_argument_parsing(self):
if self._meta.argument_options is not None:
assert isinstance(self._meta.argument_options, list), \
"ScrubController.Meta.argument_options must be a list"
self.app.args.add_argument(*self._meta.argument_options,
help=self._meta.argument_help,
action='store_true',
dest='scrub')
def load(app):
app.handler.register(ScrubController)
app.hook.register('post_render', scrub_output)
app.hook.register('pre_argument_parsing', extend_scrub)

View File

@ -325,17 +325,6 @@ def test_config_files_is_none():
assert f in app._meta.config_files
def test_base_controller_label():
class BogusBaseController(Controller):
class Meta:
label = 'bad_base_controller_label'
msg = "must have a label of 'base'"
with pytest.raises(FrameworkError, match=msg):
with TestApp(base_controller=BogusBaseController):
pass
def test_pargs():
with TestApp(argv=['--debug']) as app:
app.run()
@ -483,7 +472,7 @@ def test_run_forever():
def handler(signum, frame):
raise AssertionError('It ran forever!')
app = TestApp(base_controller=MyController, argv=['run-it'])
app = TestApp(handlers=[MyController], argv=['run-it'])
# set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)

View File

@ -575,7 +575,7 @@ def test_get_exposed_commands():
def cmd2_two(self):
pass
with TestApp(base_controller=MyController) as app:
with TestApp(handlers=[MyController]) as app:
app.run()
assert 'cmd1' in app.controller._get_exposed_commands()
assert 'cmd2-two' in app.controller._get_exposed_commands()
@ -591,5 +591,5 @@ def test_coverage():
def hidden(self):
pass
with TestApp(base_controller=MyController) as app:
with TestApp(handlers=[MyController]) as app:
app.run()

View File

@ -1,7 +1,7 @@
import os
import re
from unittest.mock import patch
from cement import TestApp, Controller, FrameworkError
from cement import TestApp, Controller
from cement.utils import shell
from cement.utils.test import raises
@ -54,14 +54,6 @@ def test_generate(tmp):
app.run()
def test_missing_base_controller(tmp):
argv = ['generate', 'test1', tmp.dir, '--defaults']
with TestApp(argv=argv, extensions=['jinja2', 'generate']) as app:
msg = 'ext.generate extension requires an application base controller'
with raises(FrameworkError, match=msg):
app.run()
def test_prompt(tmp):
argv = ['generate', 'test1', tmp.dir]

View File

@ -1,4 +1,5 @@
from cement import init_defaults
from cement.utils.test import TestApp
@ -22,11 +23,13 @@ def test_scrub():
def test_argument():
META = init_defaults('controller.scrub')
META['controller.scrub']['argument_options'] = ['--not-scrub']
META['controller.scrub']['argument_help'] = 'not scrub'
class MyScrubApp(ScrubApp):
class Meta:
scrub_argument = ['--not-scrub']
scrub_argument_help = 'not scrub'
meta_defaults = META
with MyScrubApp(argv=['--not-scrub']) as app:
app.run()