我刚开始学习webpy的时候根据官网文档能完成大部分功能,但用着用着还是感觉有些不太爽感觉自己使用不灵活,有些功能想实现不知道该怎样入手,而现在网上关于深入使用webpy的开发资料也很少。所以决定还是自己来看看源码吧,很庆幸webpy源码不多(应该是很少)看两个小时就能理清楚了。
1 首先看看webpy的源码目录结构
这么少的代码写出一个web服务器很显然它肯定是依靠了其他的某些工具
2 找到源码分析的切入点, 以下是webpy官网提供的helloworld程序代码
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()函数其代码为:
def run(self, *middleware): return wsgi.runwsgi(self.wsgifunc(*middleware))
好吧它又调转到了wsgi.py的runwsgi()函数了其代码为(该代码可以基本飘过只看最后一行)
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()源码:
def runsimple(func, server_address=("0.0.0.0", 8080)): global server 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文件中
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中。如下代码
''' 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)
有图有真相
本人最近做了一个android插件化开发框架可以不安装运行apk文件的插件,欢迎开发者们去试试网址www.apkplug.com