Flask这个人人称赞的微框架就是构建在werkzeug之上,werkzeug给自己的定位就是工具集合。它实现了wsgi server、Requests/Response封装、DEBUG、热重启、路由控制以及其他的一些辅助功能。接下来的几篇文章会从一些大的方面去分析它。本篇如标题:D
WSGI Server
这个初略说一下。和它相关的代码不多。和wsgiref一样它构建在python自带模块SocketServer和BaseHTTPRequestHandler之上。甚至比自带的wsgiref代码要少。它主要增加了SSL和socket.fromfd支持。并且将debug、静态文件分发、热重启组合再了一起
Request、Response封装
这个可能算是重点了。有个库webob就是专门做这个的。占得代码量也很大。可惜我并不想详细写每一个的过程,太麻烦了,了解了大概就好啦。以后会主要分析下datastructures.py这个文件。下面是超简洁的原理代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Request(): def __init__(self, env): pass
@classmethod def application(cls, f): def decorator(env, start_response): request = cls(env) return f(request)(env, start_response)
return decorator
class Response(): def __init__(self, str): self.body = str
def __call__(self, env, start_response): start_response('200 OK', []) return [self.body]
@Request.application def app(req): return Response(b'Hello')
from wsgiref.simple_server import make_server
make_server('', 8000, app).serve_forever()
|
可以大概了解是这么回事:
- Request它主要封装的是headers,然后嘞,各种属性都用property就好了,剩下的body和其他的也差不多啦。
- Request.application这个装饰器的作用就是将原来的env,start_response参数变成req给我们使用
Response它就是一个wsgiapp对象
看下它们2个的继承图
1 2 3 4 5
| class PlainRequest(StreamOnlyMixin, Request):
class Response(BaseResponse, ETagResponseMixin, ResponseStreamMixin, CommonResponseDescriptorsMixin, WWWAuthenticateMixin):
|
翻源码可以看到它们的继承大部分都是对headers的操作
热重启实现原理
上简图:
上原理代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| import os import sys import time import subprocess import threading
def main(): print('Hello')
def file_change(): mtimes = {} while 1: filename = __file__ mtime = os.stat(filename).st_mtime old_time = mtimes.get(filename) if old_time is None: mtimes[filename] = mtime continue elif mtime > old_time: print(' * Detected change in {}, reloading'.format(filename)) os._exit(3) time.sleep(1)
if os.environ.get('secord_process'): threading.Thread(target=main, args=()).start() file_change() else: while 1: env = os.environ.copy() env['secord_process'] = 'true' exit_code = subprocess.call([sys.executable] + sys.argv, env=env) if exit_code != 3: break
|
做法就是主线程啥事没做,跑一个死循环,生成子进程(就相当运行自身,区别就是os.environ)。这个进程内使用单独的线程跑需要运行的函数,另外就是检查相关文件是否被改变。改变就执行sys.exit。然后就又被主线程的死循环生成了新的子进程