PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/google_appengine/google/appengine/ext/ndb/utils.py

https://bitbucket.org/Liosan/gizapi
Python | 220 lines | 174 code | 23 blank | 23 comment | 31 complexity | 832ecfa698563d04fa1e907a203444e6 MD5 | raw file
Possible License(s): LGPL-2.1, MIT, LGPL-2.0, BSD-3-Clause, GPL-2.0, Apache-2.0
  1. """Low-level utilities used internally by NDB.
  2. These are not meant for use by code outside NDB.
  3. """
  4. import functools
  5. import logging
  6. import os
  7. import sys
  8. import threading
  9. __all__ = []
  10. DEBUG = True # Set to False for some speedups
  11. def logging_debug(*args):
  12. # NOTE: If you want to see debug messages, set the logging level
  13. # manually to logging.DEBUG - 1; or for tests use -v -v -v (see below).
  14. if DEBUG and logging.getLogger().level < logging.DEBUG:
  15. logging.debug(*args)
  16. def wrapping(wrapped):
  17. # A decorator to decorate a decorator's wrapper. Following the lead
  18. # of Twisted and Monocle, this is supposed to make debugging heavily
  19. # decorated code easier. We'll see...
  20. # TODO(pcostello): This copies the functionality of functools.wraps
  21. # following the patch in http://bugs.python.org/issue3445. We can replace
  22. # this once upgrading to python 3.3.
  23. def wrapping_wrapper(wrapper):
  24. try:
  25. wrapper.__wrapped__ = wrapped
  26. wrapper.__name__ = wrapped.__name__
  27. wrapper.__doc__ = wrapped.__doc__
  28. wrapper.__dict__.update(wrapped.__dict__)
  29. # Local functions won't have __module__ attribute.
  30. if hasattr(wrapped, '__module__'):
  31. wrapper.__module__ = wrapped.__module__
  32. except Exception:
  33. pass
  34. return wrapper
  35. return wrapping_wrapper
  36. # Define a base class for classes that need to be thread-local.
  37. # This is pretty subtle; we want to use threading.local if threading
  38. # is supported, but object if it is not.
  39. if threading.local.__module__ == 'thread':
  40. logging_debug('Using threading.local')
  41. threading_local = threading.local
  42. else:
  43. logging_debug('Not using threading.local')
  44. threading_local = object
  45. def get_stack(limit=10):
  46. # Return a list of strings showing where the current frame was called.
  47. if not DEBUG:
  48. return ()
  49. frame = sys._getframe(1) # Always skip get_stack() itself.
  50. lines = []
  51. while len(lines) < limit and frame is not None:
  52. f_locals = frame.f_locals
  53. ndb_debug = f_locals.get('__ndb_debug__')
  54. if ndb_debug != 'SKIP':
  55. line = frame_info(frame)
  56. if ndb_debug is not None:
  57. line += ' # ' + str(ndb_debug)
  58. lines.append(line)
  59. frame = frame.f_back
  60. return lines
  61. def func_info(func, lineno=None):
  62. if not DEBUG:
  63. return None
  64. func = getattr(func, '__wrapped__', func)
  65. code = getattr(func, 'func_code', None)
  66. return code_info(code, lineno)
  67. def gen_info(gen):
  68. if not DEBUG:
  69. return None
  70. frame = gen.gi_frame
  71. if gen.gi_running:
  72. prefix = 'running generator '
  73. elif frame:
  74. if frame.f_lasti < 0:
  75. prefix = 'initial generator '
  76. else:
  77. prefix = 'suspended generator '
  78. else:
  79. prefix = 'terminated generator '
  80. if frame:
  81. return prefix + frame_info(frame)
  82. code = getattr(gen, 'gi_code', None)
  83. if code:
  84. return prefix + code_info(code)
  85. return prefix + hex(id(gen))
  86. def frame_info(frame):
  87. if not DEBUG:
  88. return None
  89. return code_info(frame.f_code, frame.f_lineno)
  90. def code_info(code, lineno=None):
  91. if not DEBUG or not code:
  92. return ''
  93. funcname = code.co_name
  94. # TODO: Be cleverer about stripping filename,
  95. # e.g. strip based on sys.path.
  96. filename = os.path.basename(code.co_filename)
  97. if lineno is None:
  98. lineno = code.co_firstlineno
  99. return '%s(%s:%s)' % (funcname, filename, lineno)
  100. def positional(max_pos_args):
  101. """A decorator to declare that only the first N arguments may be positional.
  102. Note that for methods, n includes 'self'.
  103. """
  104. __ndb_debug__ = 'SKIP'
  105. def positional_decorator(wrapped):
  106. if not DEBUG:
  107. return wrapped
  108. __ndb_debug__ = 'SKIP'
  109. @wrapping(wrapped)
  110. def positional_wrapper(*args, **kwds):
  111. __ndb_debug__ = 'SKIP'
  112. if len(args) > max_pos_args:
  113. plural_s = ''
  114. if max_pos_args != 1:
  115. plural_s = 's'
  116. raise TypeError(
  117. '%s() takes at most %d positional argument%s (%d given)' %
  118. (wrapped.__name__, max_pos_args, plural_s, len(args)))
  119. return wrapped(*args, **kwds)
  120. return positional_wrapper
  121. return positional_decorator
  122. def decorator(wrapped_decorator):
  123. """Converts a function into a decorator that optionally accepts keyword
  124. arguments in its declaration.
  125. Example usage:
  126. @utils.decorator
  127. def decorator(func, args, kwds, op1=None):
  128. ... apply op1 ...
  129. return func(*args, **kwds)
  130. # Form (1), vanilla
  131. @decorator
  132. foo(...)
  133. ...
  134. # Form (2), with options
  135. @decorator(op1=5)
  136. foo(...)
  137. ...
  138. Args:
  139. wrapped_decorator: A function that accepts positional args (func, args,
  140. kwds) and any additional supported keyword arguments.
  141. Returns:
  142. A decorator with an additional 'wrapped_decorator' property that is set to
  143. the original function.
  144. """
  145. def helper(_func=None, **options):
  146. def outer_wrapper(func):
  147. @wrapping(func)
  148. def inner_wrapper(*args, **kwds):
  149. return wrapped_decorator(func, args, kwds, **options)
  150. return inner_wrapper
  151. if _func is None:
  152. # Form (2), with options.
  153. return outer_wrapper
  154. # Form (1), vanilla.
  155. if options:
  156. # Don't allow @decorator(foo, op1=5).
  157. raise TypeError('positional arguments not supported')
  158. return outer_wrapper(_func)
  159. helper.wrapped_decorator = wrapped_decorator
  160. return helper
  161. def tweak_logging():
  162. # Hack for running tests with verbose logging. If there are two or
  163. # more -v flags, turn on INFO logging; if there are 3 or more, DEBUG.
  164. # (A single -v just tells unittest.main() to print the name of each
  165. # test; we don't want to interfere with that.)
  166. # Also, if there is a -q flag, set DEBUG to False, suppressing more
  167. # debug info even from warnings.
  168. q = 0
  169. v = 0
  170. for arg in sys.argv[1:]:
  171. if arg.startswith('-v'):
  172. v += arg.count('v')
  173. if arg.startswith('-q'):
  174. q += arg.count('q')
  175. if v >= 2:
  176. level = logging.INFO
  177. if v >= 3:
  178. level = logging.DEBUG - 1
  179. logging.basicConfig(level=level)
  180. if q > 0:
  181. global DEBUG
  182. DEBUG = False
  183. if 'test' in os.path.basename(sys.argv[0]):
  184. tweak_logging()