web.py源码(持续更新)
文件结构:
utils.py中定义的是些 General Utilities,通用工具集和数据结构
1.对序列进行分组的函数(生成器的很好使用,值得借鉴)group
1 def group(seq, size): 2 3 def take(seq, n): 4 for i in xrange(n): 5 yield seq.next() 6 7 if not hasattr(seq, 'next'): 8 seq = iter(seq) 9 10 while True: 11 12 inger=take(seq, size) 13 x = list(inger) 14 if x: 15 yield x 16 else: 17 break
/*重点在take生成器,每次调用后seq会停留在的上次迭代n次seq结束的seq[n]处,因为seq被转换成了迭代器,next会一直有效直到所有元素迭代完为止,
把生成器转换成列表,实际上也是调用next,直到抛出StopIteration异常,被list捕获*/
1 def take(seq, n): 2 for i in xrange(n): 3 yield seq.next() 4 5 #while True: 6 # inger=take(seq,2) 7 # import time 8 # time.sleep(1) 9 # print list(inger) #迭代到最后一个异常被捕获 10 11 res=take(seq,2) 12 #print list(res) 13 print res.next() 14 print res.next() 15 res=take(seq,2) 16 #print list(res) 17 print res.next() 18 print res.next() 19 res=take(seq,2) 20 #print list(res) 21 print res.next() 22 print res.next() #StopIteration,被list捕获
#web.py-0.111中的实现不能,分出最后一个元素
1 def group(seq, size): 2 """Breaks 'seq' into a generator of lists with length 'size'.""" 3 if not hasattr(seq, 'next'): seq = iter(seq) 4 5 while True: yield [seq.next() for i in xrange(size)] 6 7 seq=iter([1,2,3,4,5]) 8 9 #r=group(seq,2) 10 #print r.next() 11 #print r.next() 12 #print r.next() #抛出异常,因此没有生成[5]
2.#缓存函数每个线程调用的返回结果。memoize
1 class memoize:(web.py-0.111定义的版本) 2 def __init__(self, func): 3 self.func = func; 4 self.cache = {} 5 def __call__(self, *a, **k): 6 key = (a, tuple(k.items())) 7 if key not in self.cache: 8 print "no cahe" 9 self.cache[key] = self.func(*a, **k) 10 print "cache",self.cache[key] 11 return self.cache[key] 12 13 14 15 def test(a): 16 return a 17 18 19 me_test=memoize(test) 20 21 me_test("aa") 22 23 t=threading.Thread(target=me_test,args=('aa',)) 24 25 t.start() 26 t.join()
class Memoize:(web.py 0.37) """ 'Memoizes' a function, caching its return values for each input. If `expires` is specified, values are recalculated after `expires` seconds. If `background` is specified, values are recalculated in a separate thread. >>> calls = 0 >>> def howmanytimeshaveibeencalled(): ... global calls ... calls += 1 ... return calls >>> fastcalls = memoize(howmanytimeshaveibeencalled) >>> howmanytimeshaveibeencalled() 1 >>> howmanytimeshaveibeencalled() 2 >>> fastcalls() 3 >>> fastcalls() 3 >>> import time >>> fastcalls = memoize(howmanytimeshaveibeencalled, .1, background=False) >>> fastcalls() 4 >>> fastcalls() 4 >>> time.sleep(.2) >>> fastcalls() 5 >>> def slowfunc(): ... time.sleep(.1) ... return howmanytimeshaveibeencalled() >>> fastcalls = memoize(slowfunc, .2, background=True) >>> fastcalls() 6 >>> timelimit(.05)(fastcalls)() 6 >>> time.sleep(.2) >>> timelimit(.05)(fastcalls)() 6 >>> timelimit(.05)(fastcalls)() 6 >>> time.sleep(.2) >>> timelimit(.05)(fastcalls)() 7 >>> fastcalls = memoize(slowfunc, None, background=True) >>> threading.Thread(target=fastcalls).start() >>> time.sleep(.01) >>> fastcalls() 9 """ def __init__(self, func, expires=None, background=True): self.func = func self.cache = {} self.expires = expires self.background = background self.running = {} def __call__(self, *args, **keywords): key = (args, tuple(keywords.items())) if not self.running.get(key): self.running[key] = threading.Lock() def update(block=False): if self.running[key].acquire(block): try: self.cache[key] = (self.func(*args, **keywords), time.time()) finally: self.running[key].release() if key not in self.cache: update(block=True) elif self.expires and (time.time() - self.cache[key][1]) > self.expires: if self.background: threading.Thread(target=update).start() else: update() return self.cache[key][0] memoize = Memoize re_compile = memoize(re.compile) #@@ threadsafe? re_compile.__doc__ = """ A memoized version of re.compile. """
3.重定向输出到一个对象中(在类中定义一个write方法,用于重定向)
1 2 import sys 3 4 5 6 class A(object): 7 text='' 8 9 class _outputter(object): 10 def write(self, x): 11 A.text+=x 12 13 _oldstdout=sys.stdout 14 sys.stdout=_outputter() 15 print "xxxxxxxx" 16 sys.stdout=_oldstdout 17 print "ctx:",A.text
4.捕获异常,格式化信息输出的函数(可以借用)(web.py-0111)
1 def djangoerror(): 2 def _get_lines_from_file(filename, lineno, context_lines): 3 """ 4 Returns context_lines before and after lineno from file. 5 Returns (pre_context_lineno, pre_context, context_line, post_context). 6 """ 7 try: 8 source = open(filename).readlines() 9 lower_bound = max(0, lineno - context_lines) 10 upper_bound = lineno + context_lines 11 12 #pre_context = [line.strip('\n') for line in source[lower_bound:lineno]]
pre_context = [line.strip('\n') for line in source[lower_bound-1:lineno-1]] 13 #context_line = source[lineno].strip('\n')
context_line = source[lineno-1].strip('\n')#因为readlines是从索引0开始的 14 #post_context = [line.strip('\n') for line in source[lineno+1:upper_bound]]
post_context = [line.strip('\n') for line in source[lineno:upper_bound]] 15 16 return lower_bound, pre_context, context_line, post_context 17 except (OSError, IOError): 18 return None, [], None, [] 19 20 exception_type, exception_value, tb = sys.exc_info() 21 frames = [] 22 while tb is not None: 23 filename = tb.tb_frame.f_code.co_filename 24 function = tb.tb_frame.f_code.co_name 25 #lineno = tb.tb_lineno - 1 #异常信息所在行号显示不对
lineno = tb.tb_lineno 26 pre_context_lineno, pre_context, context_line, post_context = _get_lines_from_file(filename, lineno, 7) 27 frames.append({ 28 'tb': tb, 29 'filename': filename, 30 'function': function, 31 'lineno': lineno, 32 'vars': tb.tb_frame.f_locals.items(), 33 'id': id(tb), 34 'pre_context': pre_context, 35 'context_line': context_line, 36 'post_context': post_context, 37 'pre_context_lineno': pre_context_lineno, 38 }) 39 tb = tb.tb_next
#frames 保存跟踪异常回溯的每层 信息
40 lastframe = frames[-1] 41 urljoin = urlparse.urljoin 42 input_ = input() 43 cookies_ = cookies() 44 context_ = context 45 prettify = pprint.pformat 46 47 #格式化的异常信息,方便在模板展示 48 frames 以及下面的局部变量可以 用于HTML模板系统展示
用于异常展示的HTML模板(django框架里面的):基于Cheetah模板系统(Cheetah是一个用Python写的模板系统与代码生成工具)
1 def djangoerror(): 2 def _get_lines_from_file(filename, lineno, context_lines): 3 """ 4 Returns context_lines before and after lineno from file. 5 Returns (pre_context_lineno, pre_context, context_line, post_context). 6 """ 7 try: 8 source = open(filename).readlines() 9 lower_bound = max(0, lineno - context_lines) 10 upper_bound = lineno + context_lines 11 12 pre_context = [line.strip('\n') for line in source[lower_bound:lineno]] 13 context_line = source[lineno-1].strip('\n')#因为readlines是从索引0开始的 14 post_context = [line.strip('\n') for line in source[lineno:upper_bound]] 15 16 return lower_bound, pre_context, context_line, post_context 17 except (OSError, IOError): 18 return None, [], None, [] 19 20 exception_type, exception_value, tb = sys.exc_info() 21 frames = [] 22 while tb is not None: 23 filename = tb.tb_frame.f_code.co_filename 24 function = tb.tb_frame.f_code.co_name 25 lineno = tb.tb_lineno #- 1#不减一显示的是报异常那句后面的语句,在_get_lines_from_file,处理readlines减1,显示正常 26 pre_context_lineno, pre_context, context_line, post_context = _get_lines_from_file(filename, lineno, 7) 27 frames.append({ 28 'tb': tb, 29 'filename': filename, 30 'function': function, 31 'lineno': lineno, 32 'vars': tb.tb_frame.f_locals.items(), 33 'id': id(tb), 34 'pre_context': pre_context, 35 'context_line': context_line, 36 'post_context': post_context, 37 'pre_context_lineno': pre_context_lineno, 38 }) 39 tb = tb.tb_next 40 lastframe = frames[-1] 41 urljoin = urlparse.urljoin 42 input_ = input() 43 cookies_ = cookies() 44 context_ = context 45 prettify = pprint.pformat 46 47 #格式化的异常信息,方便在模板展示 48 return render(DJANGO_500_PAGE, asTemplate=True, isString=True)
--------------------------------
web.py中定义几种数据结构:
1. Storage类,在utils文件中定义, 类字典对象,从内建对象dict派生,通过实现特殊方法:__getattr__,__setattr__,__delattr__,
以至于像设置、获取、删除对象的属性和值一样设置、获取、删除字典的键和值。
class Storage(dict): def __getattr__(self, key): try: return self[key] except KeyError, k: raise AttributeError, k def __setattr__(self, key, value): self[key] = value def __delattr__(self, key): try: del self[key] except KeyError, k: raise AttributeError, k def __repr__(self): return '<Storage ' + dict.__repr__(self) + '>'
web.config 初始化的是一个storge对象
2.ThreadedDict类,在utils文件中定义,线程字典,线程安全的,该实例化对象存在于主线程中,但是其属性值 在每一个线程中是独立的
1 """ 2 Thread local storage. 3 4 >>> d = ThreadedDict() 5 >>> d.x = 1 6 >>> d.x 7 1 8 >>> import threading 9 >>> def f(): d.x = 2 10 ... 11 >>> t = threading.Thread(target=f) 12 >>> t.start() 13 >>> t.join() 14 >>> d.x 15 1 16 """
当前版本的实现:(依赖于threading模块的local (在python23.py 有个专门对python2.3实现的threadlocal))
1 import threading 2 from threading import local as threadlocal 3 4 class threadlocal(object): 5 """Implementation of threading.local for python2.3. 6 """ 7 def __getattribute__(self, name): 8 if name == "__dict__": 9 return threadlocal._getd(self) 10 else: 11 try: 12 return object.__getattribute__(self, name) 13 except AttributeError: 14 try: 15 return self.__dict__[name] 16 except KeyError: 17 raise AttributeError, name 18 19 def __setattr__(self, name, value): 20 self.__dict__[name] = value 21 22 def __delattr__(self, name): 23 try: 24 del self.__dict__[name] 25 except KeyError: 26 raise AttributeError, name 27 28 def _getd(self): 29 t = threading.currentThread() 30 if not hasattr(t, '_d'): 31 # using __dict__ of thread as thread local storage 32 t._d = {} 33 34 _id = id(self) 35 # there could be multiple instances of threadlocal. 36 # use id(self) as key 37 if _id not in t._d: 38 t._d[_id] = {} 39 return t._d[_id] 40 41 class ThreadedDict(threadlocal): 42 43 _instances = set() 44 45 def __init__(self): 46 ThreadedDict._instances.add(self) 47 48 def __del__(self): 49 ThreadedDict._instances.remove(self) 50 51 def __hash__(self): 52 return id(self) 53 54 def clear_all(): 55 """Clears all ThreadedDict instances. 56 """ 57 for t in list(ThreadedDict._instances): 58 t.clear() 59 clear_all = staticmethod(clear_all) 60 61 # Define all these methods to more or less fully emulate dict -- attribute access 62 # is built into threading.local. 63 64 def __getitem__(self, key): 65 return self.__dict__[key] 66 67 def __setitem__(self, key, value): 68 self.__dict__[key] = value 69 70 def __delitem__(self, key): 71 del self.__dict__[key] 72 73 def __contains__(self, key): 74 return key in self.__dict__ 75 76 has_key = __contains__ 77 78 def clear(self): 79 self.__dict__.clear() 80 81 def copy(self): 82 return self.__dict__.copy() 83 84 def get(self, key, default=None): 85 return self.__dict__.get(key, default) 86 87 def items(self): 88 return self.__dict__.items() 89 90 def iteritems(self): 91 return self.__dict__.iteritems() 92 93 def keys(self): 94 return self.__dict__.keys() 95 96 def iterkeys(self): 97 return self.__dict__.iterkeys() 98 99 iter = iterkeys 100 101 def values(self): 102 return self.__dict__.values() 103 104 def itervalues(self): 105 return self.__dict__.itervalues() 106 107 def pop(self, key, *args): 108 return self.__dict__.pop(key, *args) 109 110 def popitem(self): 111 return self.__dict__.popitem() 112 113 def setdefault(self, key, default=None): 114 return self.__dict__.setdefault(key, default) 115 116 def update(self, *args, **kwargs): 117 self.__dict__.update(*args, **kwargs) 118 119 def __repr__(self): 120 return '<ThreadedDict %r>' % self.__dict__ 121 122 __str__ = __repr__
该类继承自 threading.local 类,然后定制成类字典对象
web.py 0.111中的实现。依赖于threading中的currentThread:(每个线程都必须用线程id作为键更新全局_context字典对象,以便线程根据自己的线程id访问/设置自己的属性)
class threadeddict:
def __init__(self, d):
self.__dict__['_threadeddict__d'] = d #相当于设置一个__d私有属相
def __getattr__(self, a):
return getattr(self.__d[currentThread()], a)
def __getitem__(self, i):
return self.__d[currentThread()][i]
def __setattr__(self, a, v):
return setattr(self.__d[currentThread()], a, v)
def __setitem__(self, i, v):
self.__d[currentThread()][i] = v
###使用示例####
_context = {currentThread():Storage()}
mydata=threadeddict(_context)
mydata.number=345
print mydata.number
def f():
_context[currentThread()]= Storage()#添加键位当前线程id的项
mydata.number=22
print mydata.number
t=threading.Thread(target=f)
t.start()
t.join()
print mydata.number
-----------
>345
>22
>345
-------------------------------------
1. application类
钩子函数:
loadhook(func) 在处理请求之前 func()
unloadhook(func) 在处理请求之后 func()
app.add_processor(processor) processor的参数为handler:请求处理函数
web.config.debug=False 请求处理不会自动重新加载 app所在模块的对象,web.py自带的服务器默认值为True
在 wsgi.py 中web.config.setdefault('debug', _is_dev_mode())设置debug的默认值,_is_dev_mode判断是否是开发环境
wsgifunc 方法,返回一个兼容WSGI的application函数 ,接受一系列中间件middleware参数
该方法中定义的函数
peep(iterator) '''Peeps into an iterator by doing an iteration and returns an equivalent iterator.'''
is_generator(x) 判断对象是否是一个生成器
wsgi
_cleanup 方法 清除所有ThreadedDict的是实例对象(每实例化一个线程字典对象都会被保存)
load 方法,用env字典变量初始化web.ctx各项属性值. 在wsgiffunc方法返回的wsgi函数中调用
附------------
os.stat(mod.__file__).st_mtime os.stat()获取文件的相关属性
itertools.chain 分别迭代多个序列或迭代器对象,然后连接在一起转换成一个迭代器
sys._getframe(1).f_locals
Return a frame object from the call stack. If optional integer depth is
given, return the frame object that many calls below the top of the stack.
If that is deeper than the call stack, ValueError is raised. The default
for depth is zero, returning the frame at the top of the call stack.

浙公网安备 33010602011771号