诚意
诚意如你,当一诚的态度对待

导航

 

 

介绍

Werkzeug 并不是 一个框架,它是一个 WSGI 工具集的库,你可以通过它来创建你自己的框架或 Web 应用。

官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等

例如我最常用的 Flask 框架就是一 Werkzeug 为基础开发的,这也是我要解析一下 Werkzeug 底层的原因,因为我想知道 Flask 的实现逻辑以及底层控制

 

 

werkzeug的简单使用

from werkzeug.serving import run_simple
from werkzeug.wrappers import Response,Request

@Request.application
def app(request):
    print(request.method)

    return Response("hello world")

run_simple("0.0.0.0",5000,app)  #这里一运行,就执行app函数

 

Flask框架的上下文--源码

 我们可以对比Flask,下面是flask的一个简单使用

1:第一阶段 封装Werkzeug 中 run_simple

from flask import Flask
app = Flask(__name__)

@app.route('/')   #相当于Django中的url.py
def hello_world():   #视图函数相当于Django中的setting.py
    return 'Hello World!'

if __name__ == '__main__':
    app.run()

 

 

下面查看这个app.run()的源码

def run(self, host=None, port=None, debug=None,
            load_dotenv=True, **options):
    ......  ..............
    from werkzeug.serving import run_simple

        try:
            run_simple(host, port, self, **options)  #这里的self实际上是app,因为是app.run()    
            #self = app = Flask(),而一运行,就会执行self()相当于执行app(),那么就会运行Flask中__call__方法

 

 

 

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)  #environ是请求的原始信息

 

2:第二阶段 flask的上文

此阶段实际上是把请求的原始信息转换为request,并放入到一个大的空间结构中

 (1)把原始信息转换为request,获得request、session

def wsgi_app(self, environ, start_response):
    ctx = self.request_context(environ)   #environ是请求的原始信息,ctx最后结果是包含了request和session

 

 

 def request_context(self, environ):
     return RequestContext(self, environ)   #self  = app = Flask()

 

 

class RequestContext(object):

    def __init__(self, app, environ, request=None):   # self =  request_context   app = app =Flask()
        self.app = app
        if request is None:
            request = app.request_class(environ) #这就是把请求的原始信息处理成request   app.request_class() 实际上执行了 Flask()
        self.request = request
        self.url_adapter = app.create_url_adapter(self.request)
        self.flashes = None
        self.session = None

 

app.request_class()
request_class = Request

 

class Request(BaseRequest, AcceptMixin, ETagRequestMixin,
              UserAgentMixin, AuthorizationMixin,
              CommonRequestDescriptorsMixin):
class BaseRequest(object):
        def __init__(self, environ, populate_request=True, shallow=False):
        self.environ = environ
        if populate_request and not shallow:
            self.environ['werkzeug.request'] = self
        self.shallow = shallow

 

(2)把request放入大存储结构空间中

 第一步:先生成数据结构

    def wsgi_app(self, environ, start_response):
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                ctx.push()

  

def push(self):   #self:ctx--request  session
        top = _request_ctx_stack.top    #_request_ctx_stack是{'_local':{'__storage__':{},'__ident_func__':get_ident}}

 

 


 

a: _request_ctx_stack获取的是啥呢?

_request_ctx_stack = LocalStack()  #{'_local':{'__storage__':{},'__ident_func__':get_ident}}
class LocalStack(object):  

    def __init__(self):  #self:LocalStack
        self._local = Local()  #self._local ={'__storage__':{},'__ident_func__':get_ident}

 


try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident


class
Local(object): __slots__ = ('__storage__', '__ident_func__') #插槽:表示这个类里只允许有这些属性 def __init__(self): object.__setattr__(self, '__storage__', {})#给Local自己设置一个__storage__的属性,值为{} object.__setattr__(self, '__ident_func__', get_ident) #get_ident是获取线程id

 

 

b:    _request_ctx_stack.top()获取的是啥呢?

返回None

    @property
    def top(self):#self是{'_local':{'__storage__':{},'__ident_func__':get_ident}}

     try: return self._local.stack[-1] #执行__getattr__() except (AttributeError, IndexError): return None

 

 获取结果__storage__ = {9527:{‘stack’:value}}

class Local(object):
    def __getattr__(self, name):  #name=stack
        try:
            return self.__storage__[self.__ident_func__()][name]  #__ident_func__()是线程或携程id
     except KeyError: 
      raise AttributeError(name)

  


 

第二步:把request、session放入数据结构中

    def push(self):
        app_ctx = _app_ctx_stack.top

        _request_ctx_stack.push(self)

#_request_ctx_stack是{'_local':{'__storage__'{},'__ident_func__':get_ident}}

#self = ctx  - request \session  = request_context

 

 

    def push(self, obj):
        rv = getattr(self._local, 'stack', None)   #rv=None
        if rv is None:
            self._local.stack = rv = []  #执行Local()的__setattr__
                  #之后
self是{'_local':{'__storage__'{9527:{‘stack’:[]}},'__ident_func__':get_ident}}
        rv.append(obj) #这个结果得到{'_local':{'__storage__'{9527:{‘stack’:[request  session]}},'__ident_func__':get_ident}}
    return rv 

#self是{'_local':{'__storage__'{},'__ident_func__':get_ident}}

#obj = self = ctx - request \session = request_context

 

 


 

    def __setattr__(self, name, value):  #name=stack   value=rv = []
        ident = self.__ident_func__()  #线程id
        storage = self.__storage__   #{}
        try:
            storage[ident][name] = value
        except KeyError:
            storage[ident] = {name: value}    {9527:{‘stack’:[]}}

 

 

 

3:第三阶段 flask的下文

这一阶段实际上就是当我们使用request的时候的一个获取request的阶段

第一步:获取request对象的偏函数,但没有执行

from flask import request

request.method

 

request = LocalProxy(partial(_lookup_req_object, 'request'))   #偏函数

 

 

def _lookup_req_object(name):   #name = request
    top = _request_ctx_stack.top
    if top is None:   #top=request、session
        raise RuntimeError(_request_ctx_err_msg)
    return getattr(top, name)   #拿出request对象

#{'_local':{'__storage__'{9527:{‘stack’:[request session]}},'__ident_func__':get_ident}}

 

 

    @property
    def top(self):
        try:
            return self._local.stack[-1]   #执行__getattr__,获取到request、session
        except (AttributeError, IndexError):
            return None

 

    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]   #取出request、session
        except KeyError:
            raise AttributeError(name)

 

 

第二步:执行偏函数,真正获取request

 

初始化

@implements_bool
class LocalProxy(object):

    __slots__ = ('__local', '__dict__', '__name__', '__wrapped__')

    def __init__(self, local, name=None):#local=_lookup_req_object=request偏函数
       object.__setattr__(self, '_LocalProxy__local', local)   #设置类属性
        object.__setattr__(self, '__name__', name)
        if callable(local) and not hasattr(local, '__release_local__'):   #callable(local)表示是否可执行,local是偏函数
         object.__setattr__(self, '__wrapped__', local)

 

 

真正使用,request.method,执行LocalProxy类__getattr__方法

    def __getattr__(self, name):   #name=method
        if name == '__members__':
            return dir(self._get_current_object())
        return getattr(self._get_current_object(), name)  #从request对象里拿method

 

    def _get_current_object(self):

        if not hasattr(self.__local, '__release_local__'):
            return self.__local()   #执行偏函数
        try:
            return getattr(self.__local, self.__name__)
        except AttributeError:
            raise RuntimeError('no object bound to %s' % self.__name__)

 

 

 

 

 

 


 

参考资料

https://werkzeug-docs-cn.readthedocs.io/zh_CN/latest/tutorial.html

posted on 2019-01-10 16:08  诚意  阅读(704)  评论(0编辑  收藏  举报