1 """Low-level utilities used internally by NDB.
3 These are not meant for use by code outside NDB.
14 DEBUG
= True # Set to False for some speedups
17 def logging_debug(*args
):
18 # NOTE: If you want to see debug messages, set the logging level
19 # manually to logging.DEBUG - 1; or for tests use -v -v -v (see below).
20 if DEBUG
and logging
.getLogger().level
< logging
.DEBUG
:
24 def wrapping(wrapped
):
25 # A decorator to decorate a decorator's wrapper. Following the lead
26 # of Twisted and Monocle, this is supposed to make debugging heavily
27 # decorated code easier. We'll see...
28 # TODO(pcostello): This copies the functionality of functools.wraps
29 # following the patch in http://bugs.python.org/issue3445. We can replace
30 # this once upgrading to python 3.3.
31 def wrapping_wrapper(wrapper
):
33 wrapper
.__wrapped
__ = wrapped
34 wrapper
.__name
__ = wrapped
.__name
__
35 wrapper
.__doc
__ = wrapped
.__doc
__
36 wrapper
.__dict
__.update(wrapped
.__dict
__)
37 # Local functions won't have __module__ attribute.
38 if hasattr(wrapped
, '__module__'):
39 wrapper
.__module
__ = wrapped
.__module
__
43 return wrapping_wrapper
46 # Define a base class for classes that need to be thread-local.
47 # This is pretty subtle; we want to use threading.local if threading
48 # is supported, but object if it is not.
49 if threading
.local
.__module
__ == 'thread':
50 logging_debug('Using threading.local')
51 threading_local
= threading
.local
53 logging_debug('Not using threading.local')
54 threading_local
= object
57 def get_stack(limit
=10):
58 # Return a list of strings showing where the current frame was called.
61 frame
= sys
._getframe
(1) # Always skip get_stack() itself.
63 while len(lines
) < limit
and frame
is not None:
64 f_locals
= frame
.f_locals
65 ndb_debug
= f_locals
.get('__ndb_debug__')
66 if ndb_debug
!= 'SKIP':
67 line
= frame_info(frame
)
68 if ndb_debug
is not None:
69 line
+= ' # ' + str(ndb_debug
)
75 def func_info(func
, lineno
=None):
78 func
= getattr(func
, '__wrapped__', func
)
79 code
= getattr(func
, 'func_code', None)
80 return code_info(code
, lineno
)
88 prefix
= 'running generator '
91 prefix
= 'initial generator '
93 prefix
= 'suspended generator '
95 prefix
= 'terminated generator '
97 return prefix
+ frame_info(frame
)
98 code
= getattr(gen
, 'gi_code', None)
100 return prefix
+ code_info(code
)
101 return prefix
+ hex(id(gen
))
104 def frame_info(frame
):
107 return code_info(frame
.f_code
, frame
.f_lineno
)
110 def code_info(code
, lineno
=None):
111 if not DEBUG
or not code
:
113 funcname
= code
.co_name
114 # TODO: Be cleverer about stripping filename,
115 # e.g. strip based on sys.path.
116 filename
= os
.path
.basename(code
.co_filename
)
118 lineno
= code
.co_firstlineno
119 return '%s(%s:%s)' % (funcname
, filename
, lineno
)
122 def positional(max_pos_args
):
123 """A decorator to declare that only the first N arguments may be positional.
125 Note that for methods, n includes 'self'.
127 __ndb_debug__
= 'SKIP'
128 def positional_decorator(wrapped
):
131 __ndb_debug__
= 'SKIP'
133 def positional_wrapper(*args
, **kwds
):
134 __ndb_debug__
= 'SKIP'
135 if len(args
) > max_pos_args
:
137 if max_pos_args
!= 1:
140 '%s() takes at most %d positional argument%s (%d given)' %
141 (wrapped
.__name
__, max_pos_args
, plural_s
, len(args
)))
142 return wrapped(*args
, **kwds
)
143 return positional_wrapper
144 return positional_decorator
147 def decorator(wrapped_decorator
):
148 """Converts a function into a decorator that optionally accepts keyword
149 arguments in its declaration.
153 def decorator(func, args, kwds, op1=None):
155 return func(*args, **kwds)
162 # Form (2), with options
168 wrapped_decorator: A function that accepts positional args (func, args,
169 kwds) and any additional supported keyword arguments.
172 A decorator with an additional 'wrapped_decorator' property that is set to
173 the original function.
175 def helper(_func
=None, **options
):
176 def outer_wrapper(func
):
178 def inner_wrapper(*args
, **kwds
):
179 return wrapped_decorator(func
, args
, kwds
, **options
)
183 # Form (2), with options.
188 # Don't allow @decorator(foo, op1=5).
189 raise TypeError('positional arguments not supported')
190 return outer_wrapper(_func
)
191 helper
.wrapped_decorator
= wrapped_decorator
196 # Hack for running tests with verbose logging. If there are two or
197 # more -v flags, turn on INFO logging; if there are 3 or more, DEBUG.
198 # (A single -v just tells unittest.main() to print the name of each
199 # test; we don't want to interfere with that.)
200 # Also, if there is a -q flag, set DEBUG to False, suppressing more
201 # debug info even from warnings.
204 for arg
in sys
.argv
[1:]:
205 if arg
.startswith('-v'):
207 if arg
.startswith('-q'):
212 level
= logging
.DEBUG
- 1
213 logging
.basicConfig(level
=level
)
219 if 'test' in os
.path
.basename(sys
.argv
[0]):