I am creating a multi-tiered cli application with the Cement Framework.
My current problem is I want multiple output handlers based on a choise of the argument -o. The end goal being I can output different formats in the cli based on choise.
The way I thought about making it after reading the docs is creating an Output Handler called MultiOutputHandler for obvious reasons. Ideally I would be able to use the output handlers already defined in Cement Framework. For this reasons I’ve started an example with json,yaml,jinja2 choises.
Here is what I managed to get almost working.
This is the MultiOutputHandler file.
from cement.core.output import OutputHandler
from cement.ext.ext_jinja2 import Jinja2OutputHandler
class MultiOutputHandler(OutputHandler):
class Meta:
label = "multi_output_handler"
template_dirs= "./templates"
def render(self, data, template=None, **kw):
"""
Render the given data using the specified output format.
Args:
data: The data to be rendered.
template: The template to be used for rendering (optional).
**kw: Additional keyword arguments.
Returns:
The rendered output.
Raises:
None.
"""
#get the cement json output handler
self.json_handler = self.app.handler.get('output','json')()
#get the cement yaml output handler
self.yaml_handler = self.app.handler.get('output','yaml')()
#get the cement jinja2 output handler
self.jinja2_handler = Jinja2OutputHandler()
# load the cement base argument -o or --output
output = self.app.pargs.output
if output == "jinja2":
# render the data using the jinja2 handler
#self.jinja2_handler._setup(self.app)
foo=self.app.handler.list('output')
self.app.log.debug('foo')
return self.jinja2_handler.render(data, template=template, **kw)
elif output == "json":
# render the data using the json handler
return self.json_handler.render(data)
elif output == "yaml":
# render the data using the yaml handler
return self.yaml_handler.render(data)
My App Meta class has the following setup
class Meta:
label = 'myapp'
# configuration defaults
config_defaults = CONFIG
# call sys.exit() on close
exit_on_close = True
# load additional framework extensions
extensions = [
'yaml',
'colorlog',
'jinja2',
'json',
]
template_dirs = "./templates"
# set the template handler
template_handler = 'jinja2'
handler_override_options = dict(output = ('-o', '--output'))
# configuration handler
config_handler = 'yaml'
# configuration file suffix
config_file_suffix = '.yml'
# set the log handler
log_handler = 'colorlog'
# set the output handler
output_handler = 'multi_output_handler'
# register handlers
handlers = [
Base,
#Bootstrap,
#Control
MultiOutputHandler
]
So here’s where things get interesting.
I output by calling the self.app.render(bla bla) function from inside say the Base Controller but it’s gonna be used a lot.
What works.
- The MultiOutputHandler is registered and then my render code runs when expected.
However it fails when using jinja2 and the others of course. Something is not loaded I was guessing properly because the self.app.jinja2_handler.templater = None and it should have something loaded.
This is where it gets weird
If you noticed in my code I have commented the following snippet.
#self.jinja2_handler._setup(self.app)
I was playing around after reading the source code. I was trying to figure out if maybe if I wasn’t registering the handlers correctly with the app object. So I tried to explicitly call the setup and feed it the app object that is available to me at the render function the self.
And voiala. It worked. Albeit. With one caveat. Debugging no longer works in VSCode. My breakpoints aren’t honored. Which leads me to think this is not the way to go, and somehow I messed up some namespace. I would like very much to have debugging and breakpoints.
The Question
How can I enable and use the other output handlers through my render? Have I missed something blatantly obvious to anyone using the Framework?
Thank you kindly
PS: Of course I know it might be easier to ditch the handlers and implement the jinja2/yaml/json logic in my render but I wanna use the framework’s potential.