foundation.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. """Cement core foundation module."""
  2. import re
  3. import os
  4. import sys
  5. import signal
  6. from ..core import backend, exc, handler, hook, log, config, plugin
  7. from ..core import output, extension, arg, controller, meta, cache
  8. from ..ext import ext_configparser, ext_argparse, ext_logging
  9. from ..ext import ext_nulloutput, ext_plugin
  10. from ..utils.misc import is_true, minimal_logger
  11. from ..utils import fs
  12. if sys.version_info[0] >= 3:
  13. from imp import reload # pragma: nocover
  14. LOG = minimal_logger(__name__)
  15. class NullOut(object):
  16. def write(self, s):
  17. pass
  18. def flush(self):
  19. pass
  20. def cement_signal_handler(signum, frame):
  21. """
  22. Catch a signal, run the 'signal' hook, and then raise an exception
  23. allowing the app to handle logic elsewhere.
  24. :param signum: The signal number
  25. :param frame: The signal frame.
  26. :raises: cement.core.exc.CaughtSignal
  27. """
  28. LOG.debug('Caught signal %s' % signum)
  29. for res in hook.run('signal', signum, frame):
  30. pass
  31. raise exc.CaughtSignal(signum, frame)
  32. class CementApp(meta.MetaMixin):
  33. """
  34. The primary class to build applications from.
  35. Usage:
  36. The following is the simplest CementApp:
  37. .. code-block:: python
  38. from cement.core import foundation
  39. app = foundation.CementApp('helloworld')
  40. try:
  41. app.setup()
  42. app.run()
  43. finally:
  44. app.close()
  45. A more advanced example looks like:
  46. .. code-block:: python
  47. from cement.core import foundation, controller
  48. class MyController(controller.CementBaseController):
  49. class Meta:
  50. label = 'base'
  51. arguments = [
  52. ( ['-f', '--foo'], dict(help='Notorious foo option') ),
  53. ]
  54. config_defaults = dict(
  55. debug=False,
  56. some_config_param='some_value',
  57. )
  58. @controller.expose(help='This is the default command', hide=True)
  59. def default(self):
  60. print('Hello World')
  61. class MyApp(foundation.CementApp):
  62. class Meta:
  63. label = 'helloworld'
  64. extensions = ['daemon','json',]
  65. base_controller = MyController
  66. app = MyApp()
  67. try:
  68. app.setup()
  69. app.run()
  70. finally:
  71. app.close()
  72. """
  73. class Meta:
  74. """
  75. Application meta-data (can also be passed as keyword arguments to the
  76. parent class).
  77. """
  78. label = None
  79. """
  80. The name of the application. This should be the common name as you
  81. would see and use at the command line. For example 'helloworld', or
  82. 'my-awesome-app'.
  83. """
  84. debug = False
  85. """
  86. Used internally, and should not be used by developers. This is set
  87. to `True` if `--debug` is passed at command line."""
  88. config_files = None
  89. """
  90. List of config files to parse.
  91. Note: Though Meta.config_section defaults to None, Cement will
  92. set this to a default list based on Meta.label (or in other words,
  93. the name of the application). This will equate to:
  94. .. code-block:: python
  95. ['/etc/<app_label>/<app_label>.conf',
  96. '~/.<app_label>.conf',
  97. '~/.<app_label>/config']
  98. """
  99. plugins = []
  100. """
  101. A list of plugins to load. This is generally considered bad
  102. practice since plugins should be dynamically enabled/disabled
  103. via a plugin config file.
  104. """
  105. plugin_config_dir = None
  106. """
  107. A directory path where plugin config files can be found. Files
  108. must end in '.conf'. By default, this setting is also overridden
  109. by the '[base] -> plugin_config_dir' config setting parsed in any
  110. of the application configuration files.
  111. Note: Though the meta default is None, Cement will set this to
  112. ``/etc/<app_label>/plugins.d/`` if not set during app.setup().
  113. """
  114. plugin_bootstrap = None
  115. """
  116. A python package (dotted import path) where plugin code can be
  117. loaded from. This is generally something like 'myapp.plugins'
  118. where a plugin file would live at ``myapp/plugins/myplugin.py``.
  119. This provides a facility for applications that use 'namespace'
  120. packages allowing plugins to share the applications python
  121. namespace.
  122. Note: Though the meta default is None, Cement will set this to
  123. ``<app_label>.plugins`` if not set during app.setup().
  124. """
  125. plugin_dir = None
  126. """
  127. A directory path where plugin code (modules) can be loaded from.
  128. By default, this setting is also overridden by the
  129. '[base] -> plugin_dir' config setting parsed in any of the
  130. application configuration files (where [base] is the
  131. base configuration section of the application which is determined
  132. by Meta.config_section but defaults to Meta.label).
  133. Note: Though the meta default is None, Cement will set this to
  134. ``/usr/lib/<app_label>/plugins/`` if not set during app.setup()
  135. """
  136. argv = None
  137. """
  138. A list of arguments to use for parsing command line arguments
  139. and options.
  140. Note: Though Meta.argv defaults to None, Cement will set this to
  141. ``list(sys.argv[1:])`` if no argv is set in Meta during setup().
  142. """
  143. arguments_override_config = False
  144. """
  145. A boolean to toggle whether command line arguments should
  146. override configuration values if the argument name matches the
  147. config key. I.e. --foo=bar would override config['myapp']['foo'].
  148. This is different from ``override_arguments`` in that if
  149. ``arguments_override_config`` is ``True``, then all arguments will
  150. override (you don't have to list them all).
  151. """
  152. override_arguments = ['debug']
  153. """
  154. List of arguments that override their configuration counter-part.
  155. For example, if ``--debug`` is passed (and it's config value is
  156. ``debug``) then the ``debug`` key of all configuration sections will
  157. be overridden by the value of the command line option (``True`` in
  158. this example).
  159. This is different from ``arguments_override_config`` in that this is
  160. a selective list of specific arguments to override the config with
  161. (and not all arguments that match the config). This list will take
  162. affect whether ``arguments_override_config`` is ``True`` or ``False``.
  163. """
  164. config_section = None
  165. """
  166. The base configuration section for the application.
  167. Note: Though Meta.config_section defaults to None, Cement will
  168. set this to the value of Meta.label (or in other words, the name
  169. of the application).
  170. """
  171. config_defaults = None
  172. """Default configuration dictionary. Must be of type 'dict'."""
  173. catch_signals = [signal.SIGTERM, signal.SIGINT]
  174. """
  175. List of signals to catch, and raise exc.CaughtSignal for.
  176. Can be set to None to disable signal handling.
  177. """
  178. signal_handler = cement_signal_handler
  179. """A function that is called to handle any caught signals."""
  180. config_handler = ext_configparser.ConfigParserConfigHandler
  181. """
  182. A handler class that implements the IConfig interface. This can
  183. be a string (label of a registered handler), an uninstantiated
  184. class, or an instantiated class object.
  185. """
  186. extension_handler = extension.CementExtensionHandler
  187. """
  188. A handler class that implements the IExtension interface. This can
  189. be a string (label of a registered handler), an uninstantiated
  190. class, or an instantiated class object.
  191. """
  192. log_handler = ext_logging.LoggingLogHandler
  193. """
  194. A handler class that implements the ILog interface. This can
  195. be a string (label of a registered handler), an uninstantiated
  196. class, or an instantiated class object.
  197. """
  198. plugin_handler = ext_plugin.CementPluginHandler
  199. """
  200. A handler class that implements the IPlugin interface. This can
  201. be a string (label of a registered handler), an uninstantiated
  202. class, or an instantiated class object.
  203. """
  204. argument_handler = ext_argparse.ArgParseArgumentHandler
  205. """
  206. A handler class that implements the IArgument interface. This can
  207. be a string (label of a registered handler), an uninstantiated
  208. class, or an instantiated class object.
  209. """
  210. output_handler = ext_nulloutput.NullOutputHandler
  211. """
  212. A handler class that implements the IOutput interface. This can
  213. be a string (label of a registered handler), an uninstantiated
  214. class, or an instantiated class object.
  215. """
  216. cache_handler = None
  217. """
  218. A handler class that implements the ICache interface. This can
  219. be a string (label of a registered handler), an uninstantiated
  220. class, or an instantiated class object.
  221. """
  222. base_controller = None
  223. """
  224. This is the base application controller. If a controller is set,
  225. runtime operations are passed to the controller for command
  226. dispatch and argument parsing when CementApp.run() is called.
  227. Note that cement will automatically set the `base_controller` to a
  228. registered controller whose label is 'base' (only if `base_controller`
  229. is not currently set).
  230. """
  231. extensions = []
  232. """List of additional framework extensions to load."""
  233. bootstrap = None
  234. """
  235. A bootstrapping module to load after app creation, and before
  236. app.setup() is called. This is useful for larger applications
  237. that need to offload their bootstrapping code such as registering
  238. hooks/handlers/etc to another file.
  239. This must be a dotted python module path.
  240. I.e. 'myapp.bootstrap' (myapp/bootstrap.py). Cement will then
  241. import the module, and if the module has a 'load()' function, that
  242. will also be called. Essentially, this is the same as an
  243. extension or plugin, but as a facility for the application itself
  244. to bootstrap 'hardcoded' application code. It is also called
  245. before plugins are loaded.
  246. """
  247. core_extensions = [
  248. 'cement.ext.ext_nulloutput',
  249. 'cement.ext.ext_plugin',
  250. 'cement.ext.ext_configparser',
  251. 'cement.ext.ext_logging',
  252. 'cement.ext.ext_argparse',
  253. ]
  254. """
  255. List of Cement core extensions. These are generally required by
  256. Cement and should only be modified if you know what you're
  257. doing. Use 'extensions' to add to this list, rather than
  258. overriding core extensions. That said if you want to prune down
  259. your application, you can remove core extensions if they are
  260. not necessary (for example if using your own log handler
  261. extension you likely don't want/need LoggingLogHandler to be
  262. registered).
  263. """
  264. core_meta_override = [
  265. 'debug',
  266. 'plugin_config_dir',
  267. 'plugin_dir',
  268. 'ignore_deprecation_warnings',
  269. 'template_dir',
  270. ]
  271. """
  272. List of meta options that can/will be overridden by config options
  273. of the '[base]' config section (where [base] is the base
  274. configuration section of the application which is determined by
  275. Meta.config_section but defaults to Meta.label). These overrides
  276. are required by the framework to function properly and should not
  277. be used by end user (developers) unless you really know what
  278. you're doing. To add your own extended meta overrides please use
  279. 'meta_override'.
  280. """
  281. meta_override = []
  282. """
  283. List of meta options that can/will be overridden by config options
  284. of the '[base]' config section (where [base] is the
  285. base configuration section of the application which is determined
  286. by Meta.config_section but defaults to Meta.label).
  287. """
  288. ignore_deprecation_warnings = False
  289. """Disable deprecation warnings from being logged by Cement."""
  290. template_module = None
  291. """
  292. A python package (dotted import path) where template files can be
  293. loaded from. This is generally something like 'myapp.templates'
  294. where a plugin file would live at ``myapp/templates/mytemplate.txt``.
  295. Templates are first loaded from ``CementApp.Meta.template_dir``, and
  296. and secondly from ``CementApp.Meta.template_module``.
  297. """
  298. template_dir = None
  299. """
  300. A directory path where template files can be loaded from. By default,
  301. this setting is also overridden by the '[base] -> template_dir' config
  302. setting parsed in any of the application configuration files (where
  303. [base] is the base configuration section of the application which is
  304. determinedby Meta.config_section but defaults to Meta.label).
  305. Note: Though the meta default is None, Cement will set this to
  306. ``/usr/lib/<app_label>/templates/`` if not set during app.setup()
  307. """
  308. def __init__(self, label=None, **kw):
  309. super(CementApp, self).__init__(**kw)
  310. # for convenience we translate this to _meta
  311. if label:
  312. self._meta.label = label
  313. self._validate_label()
  314. self._loaded_bootstrap = None
  315. self._parsed_args = None
  316. self._last_rendered = None
  317. self.ext = None
  318. self.config = None
  319. self.log = None
  320. self.plugin = None
  321. self.args = None
  322. self.output = None
  323. self.controller = None
  324. self.cache = None
  325. # setup argv... this has to happen before lay_cement()
  326. if self._meta.argv is None:
  327. self._meta.argv = list(sys.argv[1:])
  328. # hack for command line --debug
  329. if '--debug' in self.argv:
  330. self._meta.debug = True
  331. # setup the cement framework
  332. self._lay_cement()
  333. @property
  334. def debug(self):
  335. """
  336. Returns boolean based on whether `--debug` was passed at command line
  337. or set via the application's configuration file.
  338. :returns: boolean
  339. """
  340. return self._meta.debug
  341. @property
  342. def argv(self):
  343. """The arguments list that will be used when self.run() is called."""
  344. return self._meta.argv
  345. def extend(self, member_name, member_object):
  346. """
  347. Extend the CementApp() object with additional functions/classes such
  348. as 'app.my_custom_function()', etc. It provides an interface for
  349. extensions to provide functionality that travel along with the
  350. application object.
  351. :param member_name: The name to attach the object to.
  352. :type member_name: str
  353. :param member_object: The function or class object to attach to
  354. CementApp().
  355. :raises: cement.core.exc.FrameworkError
  356. """
  357. if hasattr(self, member_name):
  358. raise exc.FrameworkError("App member '%s' already exists!" %
  359. member_name)
  360. LOG.debug("extending appication with '.%s' (%s)" %
  361. (member_name, member_object))
  362. setattr(self, member_name, member_object)
  363. def _validate_label(self):
  364. if not self._meta.label:
  365. raise exc.FrameworkError("Application name missing.")
  366. # validate the name is ok
  367. ok = ['_', '-']
  368. for char in self._meta.label:
  369. if char in ok:
  370. continue
  371. if not char.isalnum():
  372. raise exc.FrameworkError(
  373. "App label can only contain alpha-numeric, dashes, " +
  374. "or underscores."
  375. )
  376. def setup(self):
  377. """
  378. This function wraps all '_setup' actons in one call. It is called
  379. before self.run(), allowing the application to be _setup but not
  380. executed (possibly letting the developer perform other actions
  381. before full execution.).
  382. All handlers should be instantiated and callable after setup is
  383. complete.
  384. """
  385. LOG.debug("now setting up the '%s' application" % self._meta.label)
  386. if self._meta.bootstrap is not None:
  387. LOG.debug("importing bootstrap code from %s" %
  388. self._meta.bootstrap)
  389. if self._meta.bootstrap not in sys.modules \
  390. or self._loaded_bootstrap is None:
  391. __import__(self._meta.bootstrap, globals(), locals(), [], 0)
  392. if hasattr(sys.modules[self._meta.bootstrap], 'load'):
  393. sys.modules[self._meta.bootstrap].load()
  394. self._loaded_bootstrap = sys.modules[self._meta.bootstrap]
  395. else:
  396. reload(self._loaded_bootstrap)
  397. for res in hook.run('pre_setup', self):
  398. pass
  399. self._setup_signals()
  400. self._setup_extension_handler()
  401. self._setup_config_handler()
  402. self._setup_cache_handler()
  403. self._setup_log_handler()
  404. self._setup_plugin_handler()
  405. self._setup_arg_handler()
  406. self._setup_output_handler()
  407. self._setup_controllers()
  408. for res in hook.run('post_setup', self):
  409. pass
  410. def run(self):
  411. """
  412. This function wraps everything together (after self._setup() is
  413. called) to run the application.
  414. """
  415. for res in hook.run('pre_run', self):
  416. pass
  417. # If controller exists, then pass controll to it
  418. if self.controller:
  419. self.controller._dispatch()
  420. else:
  421. self._parse_args()
  422. for res in hook.run('post_run', self):
  423. pass
  424. def close(self):
  425. """
  426. Close the application. This runs the pre_close and post_close hooks
  427. allowing plugins/extensions/etc to 'cleanup' at the end of program
  428. execution.
  429. """
  430. for res in hook.run('pre_close', self):
  431. pass
  432. LOG.debug("closing the application")
  433. for res in hook.run('post_close', self):
  434. pass
  435. def render(self, data, template=None):
  436. """
  437. This is a simple wrapper around self.output.render() which simply
  438. returns an empty string if no self.output handler is defined.
  439. :param data: The data dictionary to render.
  440. :param template: The template to render to. Default: None (some
  441. output handlers do not use templates).
  442. """
  443. for res in hook.run('pre_render', self, data):
  444. if not type(res) is dict:
  445. LOG.debug("pre_render hook did not return a dict().")
  446. else:
  447. data = res
  448. if self.output is None:
  449. LOG.debug('render() called, but no output handler defined.')
  450. out_text = ''
  451. else:
  452. out_text = self.output.render(data, template)
  453. for res in hook.run('post_render', self, out_text):
  454. if not type(res) is str:
  455. LOG.debug('post_render hook did not return a str()')
  456. else:
  457. out_text = str(res)
  458. self._last_rendered = (data, out_text)
  459. return out_text
  460. def get_last_rendered(self):
  461. """
  462. DEPRECATION WARNING: This function is deprecated as of Cement 2.1.3
  463. in favor of the `self.last_rendered` property, and will be removed in
  464. future versions of Cement.
  465. Return the (data, output_text) tuple of the last time self.render()
  466. was called.
  467. :returns: tuple (data, output_text)
  468. """
  469. if not is_true(self._meta.ignore_deprecation_warnings):
  470. self.log.warn("Cement Deprecation Warning: " +
  471. "CementApp.get_last_rendered() has been " +
  472. "deprecated, and will be removed in future " +
  473. "versions of Cement. You should use the " +
  474. "CementApp.last_rendered property instead.")
  475. return self._last_rendered
  476. @property
  477. def last_rendered(self):
  478. """
  479. Return the (data, output_text) tuple of the last time self.render() was
  480. called.
  481. :returns: tuple (data, output_text)
  482. """
  483. return self._last_rendered
  484. @property
  485. def pargs(self):
  486. """
  487. Returns the `parsed_args` object as returned by self.args.parse().
  488. """
  489. return self._parsed_args
  490. def add_arg(self, *args, **kw):
  491. """A shortcut for self.args.add_argument."""
  492. self.args.add_argument(*args, **kw)
  493. def _lay_cement(self):
  494. """Initialize the framework."""
  495. LOG.debug("laying cement for the '%s' application" %
  496. self._meta.label)
  497. # overrides via command line
  498. suppress_output = False
  499. if '--debug' in self._meta.argv:
  500. self._meta.debug = True
  501. else:
  502. # the following are hacks to suppress console output
  503. for flag in ['--quiet', '--json', '--yaml']:
  504. if flag in self._meta.argv:
  505. suppress_output = True
  506. break
  507. if suppress_output:
  508. LOG.debug('suppressing all console output per runtime config')
  509. backend.__saved_stdout__ = sys.stdout
  510. backend.__saved_stderr__ = sys.stderr
  511. sys.stdout = NullOut()
  512. sys.stderr = NullOut()
  513. # start clean
  514. backend.__hooks__ = {}
  515. backend.__handlers__ = {}
  516. # define framework hooks
  517. hook.define('pre_setup')
  518. hook.define('post_setup')
  519. hook.define('pre_run')
  520. hook.define('post_run')
  521. hook.define('pre_argument_parsing')
  522. hook.define('post_argument_parsing')
  523. hook.define('pre_close')
  524. hook.define('post_close')
  525. hook.define('signal')
  526. hook.define('pre_render')
  527. hook.define('post_render')
  528. # define and register handlers
  529. handler.define(extension.IExtension)
  530. handler.define(log.ILog)
  531. handler.define(config.IConfig)
  532. handler.define(plugin.IPlugin)
  533. handler.define(output.IOutput)
  534. handler.define(arg.IArgument)
  535. handler.define(controller.IController)
  536. handler.define(cache.ICache)
  537. # extension handler is the only thing that can't be loaded... as,
  538. # well, an extension. ;)
  539. handler.register(extension.CementExtensionHandler)
  540. def _parse_args(self):
  541. for res in hook.run('pre_argument_parsing', self):
  542. pass
  543. self._parsed_args = self.args.parse(self.argv)
  544. if self._meta.arguments_override_config is True:
  545. for member in dir(self._parsed_args):
  546. if member and member.startswith('_'):
  547. continue
  548. # don't override config values for options that weren't passed
  549. # or in otherwords are None
  550. elif getattr(self._parsed_args, member) is None:
  551. continue
  552. for section in self.config.get_sections():
  553. if member in self.config.keys(section):
  554. self.config.set(section, member,
  555. getattr(self._parsed_args, member))
  556. for member in self._meta.override_arguments:
  557. for section in self.config.get_sections():
  558. if member in self.config.keys(section):
  559. self.config.set(section, member,
  560. getattr(self._parsed_args, member))
  561. for res in hook.run('post_argument_parsing', self):
  562. pass
  563. def _setup_signals(self):
  564. if self._meta.catch_signals is None:
  565. LOG.debug("catch_signals=None... not handling any signals")
  566. return
  567. for signum in self._meta.catch_signals:
  568. LOG.debug("adding signal handler for signal %s" % signum)
  569. signal.signal(signum, self._meta.signal_handler)
  570. def _resolve_handler(self, handler_type, handler_def, raise_error=True):
  571. han = handler.resolve(handler_type, handler_def, raise_error)
  572. if han is not None:
  573. han._setup(self)
  574. return han
  575. def _setup_extension_handler(self):
  576. LOG.debug("setting up %s.extension handler" % self._meta.label)
  577. self.ext = self._resolve_handler('extension',
  578. self._meta.extension_handler)
  579. self.ext.load_extensions(self._meta.core_extensions)
  580. self.ext.load_extensions(self._meta.extensions)
  581. def _setup_config_handler(self):
  582. LOG.debug("setting up %s.config handler" % self._meta.label)
  583. self.config = self._resolve_handler('config',
  584. self._meta.config_handler)
  585. if self._meta.config_section is None:
  586. self._meta.config_section = self._meta.label
  587. self.config.add_section(self._meta.config_section)
  588. if not self._meta.config_defaults is None:
  589. self.config.merge(self._meta.config_defaults)
  590. if self._meta.config_files is None:
  591. label = self._meta.label
  592. if 'HOME' in os.environ:
  593. user_home = fs.abspath(os.environ['HOME'])
  594. else:
  595. # Kinda dirty, but should resolve issues on Windows per #183
  596. user_home = fs.abspath('~') # pragma: nocover
  597. self._meta.config_files = [
  598. os.path.join('/', 'etc', label, '%s.conf' % label),
  599. os.path.join(user_home, '.%s.conf' % label),
  600. os.path.join(user_home, '.%s' % label, 'config'),
  601. ]
  602. for _file in self._meta.config_files:
  603. self.config.parse_file(_file)
  604. self.validate_config()
  605. # hack for --debug
  606. if '--debug' in self.argv:
  607. self.config.set(self._meta.config_section, 'debug', True)
  608. # override select Meta via config
  609. base_dict = self.config.get_section_dict(self._meta.config_section)
  610. for key in base_dict:
  611. if key in self._meta.core_meta_override or \
  612. key in self._meta.meta_override:
  613. # kind of a hack for core_meta_override
  614. if key in ['debug']:
  615. setattr(self._meta, key, is_true(base_dict[key]))
  616. else:
  617. setattr(self._meta, key, base_dict[key])
  618. def _setup_log_handler(self):
  619. LOG.debug("setting up %s.log handler" % self._meta.label)
  620. self.log = self._resolve_handler('log', self._meta.log_handler)
  621. def _setup_plugin_handler(self):
  622. LOG.debug("setting up %s.plugin handler" % self._meta.label)
  623. # modify app defaults if not set
  624. if self._meta.plugin_config_dir is None:
  625. self._meta.plugin_config_dir = '/etc/%s/plugins.d/' % \
  626. self._meta.label
  627. if self._meta.plugin_dir is None:
  628. self._meta.plugin_dir = '/usr/lib/%s/plugins' % self._meta.label
  629. if self._meta.plugin_bootstrap is None:
  630. self._meta.plugin_bootstrap = '%s.plugins' % self._meta.label
  631. self.plugin = self._resolve_handler('plugin',
  632. self._meta.plugin_handler)
  633. self.plugin.load_plugins(self._meta.plugins)
  634. self.plugin.load_plugins(self.plugin.get_enabled_plugins())
  635. def _setup_output_handler(self):
  636. if self._meta.output_handler is None:
  637. LOG.debug("no output handler defined, skipping.")
  638. return
  639. LOG.debug("setting up %s.output handler" % self._meta.label)
  640. self.output = self._resolve_handler('output',
  641. self._meta.output_handler,
  642. raise_error=False)
  643. if self._meta.template_module is None:
  644. self._meta.template_module = '%s.templates' % self._meta.label
  645. if self._meta.template_dir is None:
  646. self._meta.template_dir = '/usr/lib/%s/templates' % \
  647. self._meta.label
  648. def _setup_cache_handler(self):
  649. if self._meta.cache_handler is None:
  650. LOG.debug("no cache handler defined, skipping.")
  651. return
  652. LOG.debug("setting up %s.cache handler" % self._meta.label)
  653. self.cache = self._resolve_handler('cache',
  654. self._meta.cache_handler,
  655. raise_error=False)
  656. def _setup_arg_handler(self):
  657. LOG.debug("setting up %s.arg handler" % self._meta.label)
  658. self.args = self._resolve_handler('argument',
  659. self._meta.argument_handler)
  660. self.args.add_argument('--debug', dest='debug',
  661. action='store_true',
  662. help='toggle debug output')
  663. self.args.add_argument('--quiet', dest='suppress_output',
  664. action='store_true',
  665. help='suppress all output')
  666. def _setup_controllers(self):
  667. LOG.debug("setting up application controllers")
  668. if self._meta.base_controller is not None:
  669. cntr = self._resolve_handler('controller',
  670. self._meta.base_controller)
  671. self.controller = cntr
  672. self._meta.base_controller = self.controller
  673. elif self._meta.base_controller is None:
  674. if handler.registered('controller', 'base'):
  675. self.controller = self._resolve_handler('controller', 'base')
  676. self._meta.base_controller = self.controller
  677. # This is necessary for some backend usage
  678. if self._meta.base_controller is not None:
  679. if self._meta.base_controller._meta.label != 'base':
  680. raise exc.FrameworkError("Base controllers must have " +
  681. "a label of 'base'.")
  682. def validate_config(self):
  683. """
  684. Validate application config settings.
  685. Usage:
  686. .. code-block:: python
  687. import os
  688. from cement.core import foundation
  689. class MyApp(foundation.CementApp):
  690. class Meta:
  691. label = 'myapp'
  692. def validate_config(self):
  693. # test that the log file directory exist, if not create it
  694. logdir = os.path.dirname(self.config.get('log', 'file'))
  695. if not os.path.exists(logdir):
  696. os.makedirs(logdir)
  697. """
  698. pass