Source code for google.appengine.ext.ndb.utils

## Copyright 2008 The ndb Authors. All Rights Reserved.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License."""Low-level utilities used internally by NDB.These are not meant for use by code outside NDB."""importfunctoolsimportloggingimportosimportsysimportthreading__all__=[]DEBUG=True# Set to False for some speedups# pylint: disable=invalid-namedeflogging_debug(*args):# NOTE: If you want to see debug messages, set the logging level# manually to logging.DEBUG - 1; or for tests use -v -v -v (see below).ifDEBUGandlogging.getLogger().level<logging.DEBUG:logging.debug(*args)defwrapping(wrapped):# A decorator to decorate a decorator's wrapper. Following the lead# of Twisted and Monocle, this is supposed to make debugging heavily# decorated code easier. We'll see...# TODO(user): This copies the functionality of functools.wraps# following the patch in http://bugs.python.org/issue3445. We can replace# this once upgrading to python 3.3.defwrapping_wrapper(wrapper):try:wrapper.__wrapped__=wrappedwrapper.__name__=wrapped.__name__wrapper.__doc__=wrapped.__doc__wrapper.__dict__.update(wrapped.__dict__)# Local functions won't have __module__ attribute.ifhasattr(wrapped,'__module__'):wrapper.__module__=wrapped.__module__exceptException:passreturnwrapperreturnwrapping_wrapper# Define a base class for classes that need to be thread-local.# This is pretty subtle; we want to use threading.local if threading# is supported, but object if it is not.ifthreading.local.__module__=='thread':logging_debug('Using threading.local')threading_local=threading.localelse:logging_debug('Not using threading.local')threading_local=objectdefget_stack(limit=10):# Return a list of strings showing where the current frame was called.ifnotDEBUG:return()frame=sys._getframe(1)# Always skip get_stack() itself.lines=[]whilelen(lines)<limitandframeisnotNone:f_locals=frame.f_localsndb_debug=f_locals.get('__ndb_debug__')ifndb_debug!='SKIP':line=frame_info(frame)ifndb_debugisnotNone:line+=' # '+str(ndb_debug)lines.append(line)frame=frame.f_backreturnlinesdeffunc_info(func,lineno=None):ifnotDEBUG:returnNonefunc=getattr(func,'__wrapped__',func)code=getattr(func,'func_code',None)returncode_info(code,lineno)defgen_info(gen):ifnotDEBUG:returnNoneframe=gen.gi_frameifgen.gi_running:prefix='running generator 'elifframe:ifframe.f_lasti<0:prefix='initial generator 'else:prefix='suspended generator 'else:prefix='terminated generator 'ifframe:returnprefix+frame_info(frame)code=getattr(gen,'gi_code',None)ifcode:returnprefix+code_info(code)returnprefix+hex(id(gen))defframe_info(frame):ifnotDEBUG:returnNonereturncode_info(frame.f_code,frame.f_lineno)defcode_info(code,lineno=None):ifnotDEBUGornotcode:return''funcname=code.co_name# TODO: Be cleverer about stripping filename,# e.g. strip based on sys.path.filename=os.path.basename(code.co_filename)iflinenoisNone:lineno=code.co_firstlinenoreturn'%s(%s:%s)'%(funcname,filename,lineno)defpositional(max_pos_args):"""A decorator to declare that only the first N arguments may be positional. Note that for methods, n includes 'self'. """__ndb_debug__='SKIP'defpositional_decorator(wrapped):ifnotDEBUG:returnwrapped__ndb_debug__='SKIP'@wrapping(wrapped)defpositional_wrapper(*args,**kwds):__ndb_debug__='SKIP'iflen(args)>max_pos_args:plural_s=''ifmax_pos_args!=1:plural_s='s'raiseTypeError('%s() takes at most %d positional argument%s (%d given)'%(wrapped.__name__,max_pos_args,plural_s,len(args)))returnwrapped(*args,**kwds)returnpositional_wrapperreturnpositional_decoratordefdecorator(wrapped_decorator):"""Converts a function into a decorator that optionally accepts keyword arguments in its declaration. Example usage: @utils.decorator def decorator(func, args, kwds, op1=None): ... apply op1 ... return func(*args, **kwds) # Form (1), vanilla @decorator foo(...) ... # Form (2), with options @decorator(op1=5) foo(...) ... Args: wrapped_decorator: A function that accepts positional args (func, args, kwds) and any additional supported keyword arguments. Returns: A decorator with an additional 'wrapped_decorator' property that is set to the original function. """defhelper(_func=None,**options):defouter_wrapper(func):@wrapping(func)definner_wrapper(*args,**kwds):returnwrapped_decorator(func,args,kwds,**options)returninner_wrapperif_funcisNone:# Form (2), with options.returnouter_wrapper# Form (1), vanilla.ifoptions:# Don't allow @decorator(foo, op1=5).raiseTypeError('positional arguments not supported')returnouter_wrapper(_func)helper.wrapped_decorator=wrapped_decoratorreturnhelperdeftweak_logging():# Hack for running tests with verbose logging. If there are two or# more -v flags, turn on INFO logging; if there are 3 or more, DEBUG.# (A single -v just tells unittest.main() to print the name of each# test; we don't want to interfere with that.)# Also, if there is a -q flag, set DEBUG to False, suppressing more# debug info even from warnings.q=0v=0forarginsys.argv[1:]:ifarg.startswith('-v'):v+=arg.count('v')ifarg.startswith('-q'):q+=arg.count('q')ifv>=2:level=logging.INFOifv>=3:level=logging.DEBUG-1logging.basicConfig(level=level)ifq>0:globalDEBUGDEBUG=Falseif'test'inos.path.basename(sys.argv[0]):tweak_logging()