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

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-02-19
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-07-04
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-07-16
  • 2021-07-12
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案