misc.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. """Misc utilities."""
  2. import os
  3. import sys
  4. import logging
  5. import hashlib
  6. from textwrap import TextWrapper
  7. from random import random
  8. def rando(salt=None):
  9. """
  10. Generate a random MD5 hash for whatever purpose. Useful for testing
  11. or any other time that something random is required.
  12. :param salt: Optional 'salt', if None then random() is used.
  13. :returns: Random MD5 hash (str).
  14. """
  15. if salt is None:
  16. salt = random()
  17. return hashlib.md5(str(salt).encode()).hexdigest()
  18. # class NullLogger(object):
  19. # def __init__(self, namespace, debug, *args, **kw):
  20. # pass
  21. # def info(self, *args, **kw):
  22. # pass
  23. # def warn(self, *args, **kw):
  24. # pass
  25. # def error(self, *args, **kw):
  26. # pass
  27. # def fatal(self, *args, **kw):
  28. # pass
  29. # def debug(self, *args, **kw):
  30. # pass
  31. class MinimalLogger(object):
  32. def __init__(self, namespace, debug, *args, **kw):
  33. self.namespace = namespace
  34. self.backend = logging.getLogger(namespace)
  35. formatter = logging.Formatter(
  36. "%(asctime)s (%(levelname)s) %(namespace)s : %(message)s"
  37. )
  38. console = logging.StreamHandler()
  39. console.setFormatter(formatter)
  40. console.setLevel(logging.INFO)
  41. self.backend.setLevel(logging.INFO)
  42. # FIX ME: really don't want to hard check sys.argv like this but
  43. # can't figure any better way get logging started (only for debug)
  44. # before the app logging is setup.
  45. if '--debug' in sys.argv or debug:
  46. console.setLevel(logging.DEBUG)
  47. self.backend.setLevel(logging.DEBUG)
  48. self.backend.addHandler(console)
  49. def _get_logging_kwargs(self, namespace, **kw):
  50. if not namespace:
  51. namespace = self.namespace
  52. if 'extra' in kw.keys() and 'namespace' in kw['extra'].keys():
  53. pass
  54. elif 'extra' in kw.keys() and 'namespace' not in kw['extra'].keys():
  55. kw['extra']['namespace'] = namespace
  56. else:
  57. kw['extra'] = dict(namespace=namespace)
  58. return kw
  59. @property
  60. def logging_is_enabled(self):
  61. if 'CEMENT_FRAMEWORK_LOGGING' in os.environ.keys():
  62. if is_true(os.environ['CEMENT_FRAMEWORK_LOGGING']):
  63. res = True
  64. else:
  65. res = False
  66. else:
  67. res = True
  68. return res
  69. def info(self, msg, namespace=None, **kw):
  70. if self.logging_is_enabled:
  71. kwargs = self._get_logging_kwargs(namespace, **kw)
  72. self.backend.info(msg, **kwargs)
  73. def warn(self, msg, namespace=None, **kw):
  74. if self.logging_is_enabled:
  75. kwargs = self._get_logging_kwargs(namespace, **kw)
  76. self.backend.warn(msg, **kwargs)
  77. def error(self, msg, namespace=None, **kw):
  78. if self.logging_is_enabled:
  79. kwargs = self._get_logging_kwargs(namespace, **kw)
  80. self.backend.error(msg, **kwargs)
  81. def fatal(self, msg, namespace=None, **kw):
  82. if self.logging_is_enabled:
  83. kwargs = self._get_logging_kwargs(namespace, **kw)
  84. self.backend.fatal(msg, **kwargs)
  85. def debug(self, msg, namespace=None, **kw):
  86. if self.logging_is_enabled:
  87. kwargs = self._get_logging_kwargs(namespace, **kw)
  88. self.backend.debug(msg, **kwargs)
  89. def init_defaults(*sections):
  90. """
  91. Returns a standard dictionary object to use for application defaults.
  92. If sections are given, it will create a nested dict for each section name.
  93. :arg sections: Section keys to create nested dictionaries for.
  94. :returns: Dictionary of nested dictionaries (sections)
  95. :rtype: dict
  96. .. code-block:: python
  97. from cement.core import foundation
  98. from cement.utils.misc import init_defaults
  99. defaults = init_defaults('myapp', 'section2', 'section3')
  100. defaults['myapp']['debug'] = False
  101. defaults['section2']['foo'] = 'bar
  102. defaults['section3']['foo2'] = 'bar2'
  103. app = foundation.CementApp('myapp', config_defaults=defaults)
  104. """
  105. defaults = dict()
  106. for section in sections:
  107. defaults[section] = dict()
  108. return defaults
  109. def minimal_logger(namespace, debug=False):
  110. """
  111. Setup just enough for cement to be able to do debug logging. This is the
  112. logger used by the Cement framework, which is setup and accessed before
  113. the application is functional (and more importantly before the
  114. applications log handler is usable).
  115. :param namespace: The logging namespace. This is generally '__name__' or
  116. anything you want.
  117. :param debug: Toggle debug output. Default: False
  118. :type debug: boolean
  119. :returns: Logger object
  120. .. code-block:: python
  121. from cement.utils.misc import minimal_logger
  122. LOG = minimal_logger('cement')
  123. LOG.debug('This is a debug message')
  124. """
  125. return MinimalLogger(namespace, debug)
  126. def is_true(item):
  127. """
  128. Given a value, determine if it is one of [True, 'True', 'true', 1, '1'].
  129. :param item: The item to convert to a boolean.
  130. :returns: True if `item` is in ``[True, 'True', 'true', 1, '1']``, False
  131. otherwise.
  132. :rtype: boolean
  133. """
  134. if item in [True, 'True', 'true', 1, '1']:
  135. return True
  136. else:
  137. return False
  138. def wrap(text, width=77, indent='', long_words=False, hyphens=False):
  139. """
  140. Wrap text for cleaner output (this is a simple wrapper around
  141. `textwrap.TextWrapper` in the standard library).
  142. :param text: The text to wrap
  143. :param width: The max width of a line before breaking
  144. :param indent: String to prefix subsequent lines after breaking
  145. :param long_words: Break on long words
  146. :param hyphens: Break on hyphens
  147. :returns: str(text)
  148. """
  149. if sys.version_info[0] < 3: # pragma: no cover
  150. types = [str, unicode] # pragma: no cover
  151. else: # pragma: no cover
  152. types = [str] # pragma: no cover
  153. if type(text) not in types:
  154. raise TypeError("Argument `text` must be one of [str, unicode].")
  155. wrapper = TextWrapper(subsequent_indent=indent, width=width,
  156. break_long_words=long_words,
  157. break_on_hyphens=hyphens)
  158. return wrapper.fill(text)