ext_yaml.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. """
  2. The Yaml Extension adds the :class:`YamlOutputHandler` to render
  3. output in pure Yaml, as well as the :class:`YamlConfigHandler` that allows
  4. applications to use Yaml configuration files as a drop-in replacement of
  5. the default :class:`cement.ext.ext_configparser.ConfigParserConfigHandler`.
  6. Requirements
  7. ------------
  8. * pyYaml (``pip install pyYaml``)
  9. Configuration
  10. -------------
  11. This extension does not honor any application configuration settings.
  12. Usage
  13. _____
  14. **myapp.conf**
  15. .. code-block:: Yaml
  16. ---
  17. myapp:
  18. foo: bar
  19. **myapp.py**
  20. .. code-block:: python
  21. from cement.core.foundation import CementApp
  22. class MyApp(CementApp):
  23. class Meta:
  24. label = 'myapp'
  25. extensions = ['Yaml']
  26. config_handler = 'Yaml'
  27. # you probably don't want to do this.. but you can
  28. # output_handler = 'Yaml'
  29. with MyApp() as app:
  30. app.run()
  31. # create some data
  32. data = dict(foo=app.config.get('myapp', 'foo'))
  33. app.render(data)
  34. In general, you likely would not set ``output_handler`` to ``Yaml``, but
  35. rather another type of output handler that displays readable output to the
  36. end-user (i.e. Mustache, Genshi, or Tabulate). By default Cement
  37. adds the ``-o`` command line option to allow the end user to override the
  38. output handler. For example: passing ``-o Yaml`` will override the default
  39. output handler and set it to ``YamlOutputHandler``.
  40. See ``CementApp.Meta.handler_override_options``.
  41. .. code-block:: console
  42. $ python myapp.py -o Yaml
  43. {foo: bar}
  44. """
  45. import os
  46. import sys
  47. import yaml
  48. from ..core import backend, output, config
  49. from ..utils.misc import minimal_logger
  50. from ..ext.ext_configparser import ConfigParserConfigHandler
  51. LOG = minimal_logger(__name__)
  52. def suppress_output_before_run(app):
  53. """
  54. This is a ``post_argument_parsing`` hook that suppresses console output if
  55. the ``YamlOutputHandler`` is triggered via command line.
  56. :param app: The application object.
  57. """
  58. if not hasattr(app.pargs, 'output_handler_override'):
  59. return
  60. elif app.pargs.output_handler_override == 'yaml':
  61. app._suppress_output()
  62. def unsuppress_output_before_render(app, data):
  63. """
  64. This is a ``pre_render`` that unsuppresses console output if
  65. the ``YamlOutputHandler`` is triggered via command line so that the Yaml
  66. is the only thing in the output.
  67. :param app: The application object.
  68. """
  69. if not hasattr(app.pargs, 'output_handler_override'):
  70. return
  71. elif app.pargs.output_handler_override == 'yaml':
  72. app._unsuppress_output()
  73. def suppress_output_after_render(app, out_text):
  74. """
  75. This is a ``post_render`` hook that suppresses console output again after
  76. rendering, only if the ``YamlOutputHandler`` is triggered via command
  77. line.
  78. :param app: The application object.
  79. """
  80. if not hasattr(app.pargs, 'output_handler_override'):
  81. return
  82. elif app.pargs.output_handler_override == 'yaml':
  83. app._suppress_output()
  84. class YamlOutputHandler(output.CementOutputHandler):
  85. """
  86. This class implements the :ref:`IOutput <cement.core.output>`
  87. interface. It provides Yaml output from a data dictionary and uses
  88. `pyYaml <http://pyYaml.org/wiki/PyYamlDocumentation>`_ to dump it to
  89. STDOUT. Please see the developer documentation on
  90. :ref:`Output Handling <dev_output_handling>`.
  91. This handler forces Cement to suppress console output until
  92. ``app.render`` is called (keeping the output pure Yaml). If
  93. troubleshooting issues, you will need to pass the ``--debug`` option in
  94. order to unsuppress output and see what's happening.
  95. """
  96. class Meta:
  97. """Handler meta-data."""
  98. interface = output.IOutput
  99. label = 'yaml'
  100. #: Whether or not to include ``Yaml`` as an available to choice
  101. #: to override the ``output_handler`` via command line options.
  102. overridable = True
  103. def __init__(self, *args, **kw):
  104. super(YamlOutputHandler, self).__init__(*args, **kw)
  105. self.config = None
  106. def _setup(self, app_obj):
  107. self.app = app_obj
  108. def render(self, data_dict, **kw):
  109. """
  110. Take a data dictionary and render it as Yaml output. Note that the
  111. template option is received here per the interface, however this
  112. handler just ignores it.
  113. :param data_dict: The data dictionary to render.
  114. :returns: A Yaml encoded string.
  115. :rtype: ``str``
  116. """
  117. LOG.debug("rendering output as yaml via %s" % self.__module__)
  118. return yaml.dump(data_dict)
  119. class YamlConfigHandler(ConfigParserConfigHandler):
  120. """
  121. This class implements the :ref:`IConfig <cement.core.config>`
  122. interface, and provides the same functionality of
  123. :ref:`ConfigParserConfigHandler <cement.ext.ext_configparser>`
  124. but with Yaml configuration files. See
  125. `pyYaml <http://pyYaml.org/wiki/PyYamlDocumentation>`_ for more
  126. information on pyYaml.
  127. **Note** This extension has an external dependency on `pyYaml`. You must
  128. include `pyYaml` in your application's dependencies as Cement explicitly
  129. does *not* include external dependencies for optional extensions.
  130. """
  131. class Meta:
  132. label = 'yaml'
  133. def __init__(self, *args, **kw):
  134. super(YamlConfigHandler, self).__init__(*args, **kw)
  135. def _parse_file(self, file_path):
  136. """
  137. Parse Yaml configuration file settings from file_path, overwriting
  138. existing config settings. If the file does not exist, returns False.
  139. :param file_path: The file system path to the Yaml configuration file.
  140. :returns: boolean
  141. """
  142. self.merge(yaml.load(open(file_path)))
  143. # FIX ME: Should check that file was read properly, however if not it
  144. # will likely raise an exception anyhow.
  145. return True
  146. def load(app):
  147. app.hook.register('post_argument_parsing', suppress_output_before_run)
  148. app.hook.register('pre_render', unsuppress_output_before_render)
  149. app.hook.register('post_render', suppress_output_after_render)
  150. app.handler.register(YamlOutputHandler)
  151. app.handler.register(YamlConfigHandler)