webpy源碼閱讀--application請求原理

   上一篇文章對webpy啓動進行了講解,今天主要分析如下appliction.py這個文件的代碼(webpy核心的主要功能就在這裏面),以瞭解webpy對請求的處理過程。 python

   咱們在運用webpy時有兩個特性可能注意到了,一個是子應用(在上一篇文章已經說起到),另外是"應用處理器"和「鉤子」Wbepy-coobook資料裏面說起到鉤子的使用方法,咱們能夠先明白它們的使用方法後面一步一步分析其實現原理。 web

   首先咱們進入appliction.py的 def __init__(self, mapping=(), fvars={}, autoreload=None):方法webpy初始化就基本從這裏開始了 session

def __init__(self, mapping=(), fvars={}, autoreload=None):
        if autoreload is None:
            autoreload = web.config.get('debug', False)
        self.init_mapping(mapping)
        self.fvars = fvars
        self.processors = []
        #將appliction類自身的_load,_unload函數加入應用處理器
        self.add_processor(loadhook(self._load))
        self.add_processor(unloadhook(self._unload))
        
        if autoreload:
經過以上源碼註釋下的兩行代碼與鉤子的使用方法比較能夠看出它們是同樣的,因此咱們能夠把"鉤子"歸一到"應用處理器"。

  這裏咱們能夠猜測到webpy裏面很大一部分功能可能都是經過"processor"實現的,實際上在分析如下session.py代碼之後就能夠發現web.py的session處理就是經過加入一個processor實現的。processor能夠理解爲一個處理鏈條當請求到來時一步一步經過這個鏈條裏面的processor處理。 app

  下一步咱們來了解當請求到來是webpy的執行過程,經過上一篇文章瞭解咱們能夠肯定webpy實際上是一個wsgi應用,webpy經過application.py 的run方法啓動wsgi服務,而且傳人本身的處理函數以供wsgi在有請求是回調處理。 函數

def run(self, *middleware):
        return wsgi.runwsgi(self.wsgifunc(*middleware))
  經過以上代碼能夠肯定回調函數就是從 self.wsgifunc(*middleware) 函數中取得的

  

def wsgifunc(self, *middleware):
        """Returns a WSGI-compatible function for this application."""
        def peep(iterator):
            """Peeps into an iterator by doing an iteration
            and returns an equivalent iterator.
            """
            # wsgi requires the headers first
            # so we need to do an iteration
            # and save the result for later
            try:
                firstchunk = iterator.next()
            except StopIteration:
                firstchunk = ''

            return itertools.chain([firstchunk], iterator)    
                                
        def is_generator(x): return x and hasattr(x, 'next')
        #本appliction處理回調函數,當請求到來時最早調用這個回調函數
        def wsgi(env, start_resp):
            # clear threadlocal to avoid inteference of previous requests
            self._cleanup()
            #判斷請求,提取請求的參數,狀態等信息
            self.load(env)
            try:
                # allow uppercase methods only
                if web.ctx.method.upper() != web.ctx.method:
                    raise web.nomethod()
                #遞歸的調用已加入的"應用處理器"
                result = self.handle_with_processors()
                if is_generator(result):
                    result = peep(result)
                else:
                    result = [result]
            except web.HTTPError, e:
                result = [e.data]

            result = web.safestr(iter(result))
            #處理完畢返回給用戶
            status, headers = web.ctx.status, web.ctx.headers
            start_resp(status, headers)
            
            def cleanup():
                self._cleanup()
                yield '' # force this function to be a generator
                            
            return itertools.chain(result, cleanup())
        #將用戶傳人的應用處理器(上一篇文章最後實現的wsgi應用)加入到列表中
        for m in middleware: 
            wsgi = m(wsgi)

        return wsgi

   分析以上代碼 請求到來時將首先執行  def wsgifunc(self, *middleware):函數中def wsgi(env, start_resp):函數的代碼,def wsgi(env, start_resp):又調用appliction.py的def handle_with_processors(self):函數遞歸調用執行"processors"的代碼。 ui

def handle_with_processors(self):
        def process(processors):
            try:
                if processors:
                    p, processors = processors[0], processors[1:]
                    return p(lambda: process(processors))
                else:
                    return self.handle()
            except web.HTTPError:
                raise
            except (KeyboardInterrupt, SystemExit):
                raise
            except:
                print >> web.debug, traceback.format_exc()
                raise self.internalerror()
        
        # processors must be applied in the resvere order. (??)
        return process(self.processors)
如此完整的請求執行鏈條便基本完成了。

經過以上代碼分析若是processors被遞歸執行完畢之後便執行self.handle()函數代碼以下 this

def handle(self):
        #匹配與url對應的執行函數
        fn, args = self._match(self.mapping, web.ctx.path)
        #執行查找到的函數
        return self._delegate(fn, self.fvars, args)
_match函數對匹配用戶的url與咱們配置的urls進行匹配查找正確的url處理函數。而後調用_delegate函數去執行

def _delegate(self, f, fvars, args=[]):
        def handle_class(cls):
            meth = web.ctx.method
            if meth == 'HEAD' and not hasattr(cls, meth):
                meth = 'GET'
            if not hasattr(cls, meth):
                raise web.nomethod(cls)
            tocall = getattr(cls(), meth)
            return tocall(*args)
            
        def is_class(o): return isinstance(o, (types.ClassType, type))
            
        if f is None:
            #沒有匹配
            raise web.notfound()
        elif isinstance(f, application):
            #若是找到的是一個appliction(子應用)
            return f.handle_with_processors()
        elif is_class(f):
            return handle_class(f)
        elif isinstance(f, basestring):
            if f.startswith('redirect '):
相關文章
相關標籤/搜索