webpy源码阅读

转载自:http://my.oschina.net/u/138488/blog/169092

2 找到源码分析的切入点, 以下是webpy官网提供的helloworld程序代码

?
1
2
3
4
5
6
7
8
9
10
11
import web
 
urls = ( "/.*" , "hello" )
app = web.application(urls, globals ())
 
class hello:
     def GET( self ):
         return 'Hello, world!'
 
if __name__ = = "__main__" :
     app.run()

很明显 application.run()就是程序的启动入口了。打开application.py看代码果然很多,不过飘过其他的代码先不看找到run()函数其代码为:

?
1
2
def run( self , * middleware):
           return wsgi.runwsgi( self .wsgifunc( * middleware))

好吧它又调转到了wsgi.py的runwsgi()函数了其代码为(该代码可以基本飘过只看最后一行)

?
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
def runwsgi(func):
     """
     Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server,
     as appropriate based on context and `sys.argv`.
     """
     
     if os.environ.has_key( 'SERVER_SOFTWARE' ): # cgi
         os.environ[ 'FCGI_FORCE_CGI' ] = 'Y'
 
     if (os.environ.has_key( 'PHP_FCGI_CHILDREN' ) #lighttpd fastcgi
       or os.environ.has_key( 'SERVER_SOFTWARE' )):
         return runfcgi(func, None )
     
     if 'fcgi' in sys.argv or 'fastcgi' in sys.argv:
         args = sys.argv[ 1 :]
         if 'fastcgi' in args: args.remove( 'fastcgi' )
         elif 'fcgi' in args: args.remove( 'fcgi' )
         if args:
             return runfcgi(func, validaddr(args[ 0 ]))
         else :
             return runfcgi(func, None )
     
     if 'scgi' in sys.argv:
         args = sys.argv[ 1 :]
         args.remove( 'scgi' )
         if args:
             return runscgi(func, validaddr(args[ 0 ]))
         else :
             return runscgi(func)
     
     
     server_addr = validip(listget(sys.argv, 1 , ''))
     if os.environ.has_key( 'PORT' ): # e.g. Heroku
         server_addr = ( '0.0.0.0' , intget(os.environ[ 'PORT' ]))
     
     return httpserver.runsimple(func, server_addr)

上面这段代码主要的意思就是通过传人的命令行参数进行不同的初始化,最后传人到httpservice.runsimple()函数了 并且将启动的application(func)也传了进去一下为httpservice.runsimple()源码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def runsimple(func, server_address = ( "0.0.0.0" , 8080 )):
     global serv<span>< / span>er
     func = StaticMiddleware(func)
     func = LogMiddleware(func)
     
     server = WSGIServer(server_address, func)
 
     if server.ssl_adapter:
         print "https://%s:%d/" % server_address
     else :
         print "http://%s:%d/" % server_address
 
     try :
         server.start()
     except (KeyboardInterrupt, SystemExit):
         server.stop()
         server = None

 看到这段代码我就豁然开朗了原理8080端口默认是在这里啊!

再看下面的代码

    func = StaticMiddleware(func)
    func = LogMiddleware(func)

它们分别是加入了两个路由第一个是我们静态资源文件夹路由"/static/",另外一个是日志路由(所谓路由可以理解为一个拦截器,路由拦截自定url路径的数据)不明白的话还是看代码比较清楚 以StaticMiddleware为例他们都在httpservice.py文件中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class StaticMiddleware:
     """WSGI middleware for serving static files."""
     def __init__( self , app, prefix = '/static/' ):
         #保存下一个路由
         self .app = app
         #保存自己将要拦截的路径
         self .prefix = prefix
         
     def __call__( self , environ, start_response):
         #当用户请求是wsgi将依次调用路由器的这个函数
         path = environ.get( 'PATH_INFO' , '')
         path = self .normpath(path)
         #判断一下如果是我要拦截的路径"/static/"
         if path.startswith( self .prefix):
             return StaticApp(environ, start_response)
         else :
             #如果不是就交由下一个路由处理
             return self .app(environ, start_response)

   这个地方其实挺关键因为webpy的application本身也可以理解为一个路由(这样你也就可以路径application中其实还可以添加下一层application了)webpy使用子应用http://webpy.org/cookbook/subapp.zh-cn

   读到这里你可能会有点不明白了  def __call__(self, environ, start_response):这传入的两个参数  environ, start_response是怎么来的?有什么意义?

   其实这是python的wsgi协议里面定义的东东了你可以通过这篇文章了解wsgi的部分知识http://linluxiang.iteye.com/blog/799163

  如果你看明白了wsgi的知识也就自然明白了runsimple()函数中server.start()是个什么了,它其实就是启动wsgiserver服务

 通过以上的学习你就可以写自己的路由了(仿照StaticMiddleware哦),其实webpy在application.run()就定义了路由接口,开发者可以写自己的路由然后传人run()函数 启动的时候会一步一步的传递到wgsi中。如下代码

?
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
'''
Created on 2013-10-15
 
@author: liangqianwu
'''
#_*_ coding:utf-8_*_
import web
urls = ( "/.*" , "hello" )
app = web.application(urls, globals ())
 
class hello:
     def GET( self ):
         return 'Hello, world!'
def my(environ, start_response):
     status = '200 OK'
     output = 'this is my Middleware!'
     response_headers = [( 'Content-type' , 'text/plain' )]
     write = start_response(status, response_headers)
     write( 'Hello ' )
     return [output]
class myMiddleware:
     """WSGI middleware for serving static files."""
     def __init__( self , app, prefix = '/my' ):
         self .app = app
         self .prefix = prefix
         
     def __call__( self , environ, start_response):
         path = environ.get( 'PATH_INFO' , '')
 
         if path.startswith( self .prefix):
             return my(environ, start_response)
         else :
             return self .app(environ, start_response)
if __name__ = = "__main__" :
     app.run(myMiddleware)


我们在运用webpy时有两个特性可能注意到了,一个是子应用(在上一篇文章已经提及到),另外是"应用处理器"和“钩子”Wbepy-coobook资料里面提及到钩子的使用方法,我们可以先明白它们的使用方法后面一步一步分析其实现原理。

   首先我们进入appliction.py的 def __init__(self, mapping=(), fvars={}, autoreload=None):方法webpy初始化就基本从这里开始了

?
1
2
3
4
5
6
7
8
9
10
11
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处理。

  下一步我们来了解当请求到来是webpy的执行过程,通过上一篇文章了解我们可以确定webpy其实是一个wsgi应用,webpy通过application.py 的run方法启动wsgi服务,并且传人自己的处理函数以供wsgi在有请求是回调处理。

?
1
2
def run( self , * middleware):
         return wsgi.runwsgi( self .wsgifunc( * middleware))
  通过以上代码可以确定回调函数就是从 self.wsgifunc(*middleware)  函数中取得的

  

?
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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"的代码。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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()函数代码如下

?
1
2
3
4
5
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函数去执行

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 ' ):


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值