from werkzur.serving import run_simple def run(environ,start_response): reuturn [b'hello world'] if __name__ == "__main__": run_simple('localhost',4000,run) # run_simple --> 啓動監聽接收 socket(一個死循環); run 會加上 () 去執行
def __call__(self, environ, start_response): # 當請求來的時候,纔會執行 __call__ 方法 """The WSGI server calls the Flask application object as the WSGI application. This calls :meth:`wsgi_app` which can be wrapped to applying middleware.""" return self.wsgi_app(environ, start_response) # wsgi_app() 真正去執行源碼 # app.run() 這行代碼執行的時候,並無執行 __call__ 方法
import time import threading # 獲取 線程或者協程的惟一id try: import greenlet get_ident = greenlet.getcurrent except Exception: get_ident = threading.get_ident class Local(object): DIC = {} # 數據都放到這個大字典中 def __getattr__(self,item): get_ident = get_ident() if get_ident in self.DIC: return self.DIC[get_ident].get(item) return None def __setattr__(self,key,value): get_ident = get_ident() if get_ident in self.DIC: self.DIC[get_ident][key] = value else: self.DIC[get_ident] = {key:value} # __getattr__ 和 __setattr__ 的用處 ; 同一個對象的同一個屬性,根據線程和協程號這個惟一標識,來獲取不一樣的值
請求到來時: # ctx = RequestContext(self,environ) # self 是 app對象,environ表示請求相關的原始數據 # ctx.request = Request(environ) # ctx.session = None # 將包含了 request和session 的ctx對象打包放到某個地方(至關於一個大字典;根據線程或協程加個惟一標識,放進去的,因此數據相互隔離) { 1232:{ctx:ctx對象}, ... } 視圖函數: from flask import request,session # 上述代碼背後的過程:根據當前線程或者協程的惟一標識,取到 ctx對象,而後取到 request和session 請求結束: 根據當前線程的惟一標識,將 大字典 中該線程對應的數據移除
import functools def func(a,b): return a+b new_func = functools.partial(func,6) # 第一個參數是函數名;第二個參數 6 會看成 func 的第一個參數自動傳入 func 中 ret = new_func(2) # new_func(2) 執行時, 就是 func 函數在執行,6做爲第一個參數自動傳入 func(),2做爲第二個參數傳入func print(ret) # 偏函數 ---> 幫助開發人員自動傳遞參數
class Stack(object): """ 基於列表維護一個棧 """ def __init__(self): self.stack = [] def push(self,item): self.stack.append(item) def pop(self): return self.stack.pop() def top(self): """ 讀取最後一個值 """ return self.stack[-1]
# 本身寫的 Local: try: from greenlet import getcurrent as get_ident except Exception as e: from threading import get_ident class Local(object): """docstring for Local""" def __init__(self, arg): object.__setattr__(self,"storage",{}) # Local 實例化的時候會在對象中存一個 "storage" 的空字典;注意這種設置對象屬性值點語法的方法 # 這是不能使用 self.storage = {} ,由於 self.storage 會調用下面的 __setattr__ ,__setattr__ 中也有 self.storage def __setattr__(self,key,value): get_ident = get_ident() # 線程號 if get_ident not in self.storage: self.storage[get_ident] = {key,value} else: self.storage[get_ident][key] = value def __getattr__(self,item): get_ident = get_ident() if get_ident in self.storage: return self.storage[get_ident].get(item) return None # 實現的效果: 一個對象, .同一個屬性的時候 可獲取到不一樣的值(根據當前線程號等惟一標識)---> 爲每一個線程開闢一塊獨立的空間 # flask 中的 Local 和 LocalStack 類: # flask 源碼中的 Local 部分源碼: # 查詢「路徑」: flask 中的 globals ---> LocalStack ---> Local 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__() 的做用:只容許該類的實例添加 __slots__ () 中的屬性 __slots__ = ('__storage__', '__ident_func__') def __init__(self): object.__setattr__(self, '__storage__', {}) object.__setattr__(self, '__ident_func__', get_ident) def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) def __setattr__(self, name, value): ident = self.__ident_func__() storage = self.__storage__ try: storage[ident][name] = value except KeyError: storage[ident] = {name: value} def __delattr__(self, name): try: del self.__storage__[self.__ident_func__()][name] except KeyError: raise AttributeError(name) obj = Local() obj.stack = [] """ 此時 obj. 的時候, 並非給 obj 對象新添加了一個屬性,而是把 "stack" 做爲 key,[] 做爲 value 放到了 __storage__ 這個大字典中,以下: { "線程號":{"stack":[]} } """ obj.stack.append("abc") obj.stack.append("123") obj.stack.pop() # 經過重寫 __setattr__ 方法,Local對象全部經過 . 方法設置屬性值時,都把 屬性名和其對應的值放到了 __storage__ 這個大字典當前線程號對應的字典中; # 經過重寫 __getattr__ 方法,Local對象經過 . 方法獲取屬性值時,也是從 __storage__ 這個大字典中 當前線程號對應的字典中 獲取該屬性key 對應的值 """ LocalStack 存的數據格式: __storage__ = { 1231:{"stack":[]}, ... } """ # 每次 obj.stack 這個棧 append 或者 pop 時都要加上 .stack ,下面作簡化: 用一個代理 LocalStack class LocalStack(object): def __init__(self): self._local = Local() # 實例化 Local ---> 獲得一個 __storage__ 屬性對應的空字典 : Local()對象.__storage__ = {} def push(self,item): """Pushes a new item to the stack""" rv = getattr(self._local, 'stack', None) # self._local.stack ---> 觸發 Local 的 __getattr__ 方法 if rv is None: self._local.stack = rv = [] # self._local.stack 和 rv 同時指向同一個引用; 此處執行 __setattr__ 方法 rv.append(item) return rv def pop(self): """Removes the topmost item from the stack, will return the old value or `None` if the stack was already empty. """ stack = getattr(self._local, 'stack', None) if stack is None: return None elif len(stack) == 1: return stack[-1] # 讀取棧中的最後一個值 else: return stack.pop() ls = LocalStack() ls.push("abc") ls.push("123") ls.pop() """ __storage__ = { "線程號":{"stack":[]} } """ # LocalStack 對象至關於一個代理,幫助咱們在Local中維護一個列表,並把這個列表維護成一個棧 """ 小結: Local 做用: 爲每一個線程開闢一個獨立的空間 (內部維護了一個 __storage__ 的大字典,該字典中的key是當前線程號,字典的value是該線程獨立的數據) LocalStack 做用: 幫助咱們把 Local 中的一個列表維護成一個棧 """
# LocalStack的對象 ls push() 時再也不傳字符串,而是傳一個字典,如: ls.push({"request":"xxx","session":"yyy"}) # 全局變量只有在程序啓動時會加載執行
上下文管理:flask
1. request 上下文管理 (請求上下文管理)session
app.__call__ ---> app.wsgi_app wsgi_app 中 : 1. ctx = self.request_context(environ) ---> 封裝請求數據; environ --> 原始的請求數據 ctx = RequestContext(self, environ) ctx.request = Request(environ) ctx.session = None 2. ctx.push() ---> _request_ctx_stack.push(self) 1. # self 便是 ctx 對象, ctx 又封裝了 request 和 session # self = ctx = (request,session) 2. _request_ctx_stack = LocalStack() ---> 這行代碼執行完後(主要是 LocalStack()), 會在內存中建立一個名字爲 __storage__ 的空字典 ---> _request_ctx_stack 幫助咱們維護一個 __storage__ = {} ---> ctx.push() 執行完後 ---> __storage__ = { "當前線程號":{"stack":[封裝了 request 和 session 的ctx對象]} } wsgi(幫助咱們初步處理請求) ---> 而後執行 __call__ 方法 ---> __call__ 方法 執行 wsgi_app 方法 ---> wsgi_app 方法中: 1. 建立 ctx 對象 : ctx = RequestContext(self, environ) # environ 中有 request 和 session 2. 執行 push 方法 ctx.push() ---> LocalStack , 把 ctx對象添加到 Local 對象中, ---> Local 中的數據格式: __storage__ = { "當前線程號":{"stack":[封裝了request和session的ctx對象]} } # 走到視圖函數 取數據 : # 視圖函數取數據也是經過 LocalStack 去 Local 中取數據(ctx對象),而不能直接去 Local 中取 原理: ctx = _request_ctx_stack.top ctx.request # 取 request ctx.session # 取 session @property def top(self): """The topmost item on the stack. If the stack is empty, `None` is returned. """ try: return self._local.stack[-1] except (AttributeError, IndexError): return None """ 第一階段:請求到來 將 request 和 session 相關數據封裝到 ctx=RequestContext() 對象中。 再經過 LocalStack 將 ctx 對象添加到 Local 中 __storage__ = { 1231:{"stack":[ctx(request,session)]} } 第二階段: 視圖函數中獲取 request 或 session 方式一: 直接找 LocalStack 獲取 _request_ctx_stack.top.request 方式二: 經過代理 LocalProxy 獲取 from flask import request,session 上面兩種方式都是經過 LocalStack 去 Local 中取值 """ # 經過代理 LocalProxy 獲取 request 或 session request = LocalProxy(partial(_lookup_req_object, 'request')) ---> 獲取 ctx 中的 request session = LocalProxy(partial(_lookup_req_object, 'session')) ---> 獲取 ctx 中的 session # request 是一個 LocalProxy 的對象,以 request.method 爲例, .mothod 會執行 LocalProxy 的 __getattr__ 方法: @implements_bool class LocalProxy(object): def __init__(self, local, name=None): object.__setattr__(self, '_LocalProxy__local', local) # __local 是 LocalProxy 在實例化的時候傳入的參數 object.__setattr__(self, '__name__', name) if callable(local) and not hasattr(local, '__release_local__'): # "local" is a callable that is not an instance of Local or # LocalManager: mark it as a wrapped function. object.__setattr__(self, '__wrapped__', local) def __getattr__(self, name): if name == '__members__': return dir(self._get_current_object()) # 以 request.method 爲例, 此時 name == "method" return getattr(self._get_current_object(), name) # 上面的 return 等同於下面的兩句代碼: """ obj = self._get_current_object() --> 從ctx中獲取 request return getattr(obj,name) --> 從 request 中獲取 method """ def _get_current_object(self): """Return the current object. This is useful if you want the real object behind the proxy at a time for performance reasons or because you want to pass the object into a different context. """ if not hasattr(self.__local, '__release_local__'): return self.__local() # 執行偏函數 # __local 是 LocalProxy 在實例化的時候傳入的參數,即偏函數 partial(_lookup_req_object, 'request')--> 獲取到ctx中的request try: return getattr(self.__local, self.__name__) except AttributeError: raise RuntimeError('no object bound to %s' % self.__name__) def _lookup_req_object(name): # name 爲 "request" 或者 " session" top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name)