webpy 源码学习(一),该怎么处理

webpy 源码学习(一)
本帖最后由 diaocow 于 2013-08-09 22:26:30 编辑
自己是个python新手,之前买了本<<python核心编程>>,但看了一半实在看不下去了(内容过于啰嗦,而且在关键点的地方又浅尝辄止),所以希望通过阅读一些简单的开源项目来快速提高python水平,最终让我发现了webpy这个好东西!

那么webpy是什么呢? 阅读它的源码我们又能学到什么呢?

简单说webpy就是一个开源的web应用框架(官方首页:http://webpy.org/)

它的源代码非常整洁精干,学习它一方面可以让我们快速了解python语法(遇到看不懂的语法就去google),另一方面可以学习到python的高级特性的使用(譬如装饰器),而且在webpy中还内置了一个简单HTTP服务器(文档建议该服务器仅用于开发环境,生产环境应使用apache之类的),对于想简单了解下HTTP服务器实现的朋友来说,这个是再好不过的例子了(并且在这个服务器代码中,还可以学习到线程池,消息队列等技术),除此之外webpy还包括模板渲染引擎,DB框架等等,这里面的每一个部分都可以单独拿出来学习.

在JavaWeb开发中有Servlet规范,那么Python Web开发中有规范吗?
答案就是:WSGI,它定义了服务器如何与你的webapp交互

关于WSGI规范,可以参看下面这个链接:
http://ivory.idyll.org/articles/wsgi-intro/what-is-wsgi.html

现在我们利用webpy内置的WSGIServer,按照WSGI规范,写一个简单的webapp,eg:
#/usr/bin/python
import web.wsgiserver

def my_wsgi_app(env, start_response):
    status = '200 OK'                                                                                                                         
    response_headers = [('Content-type','text/plain')]
    start_response(status, response_headers)
    return ['Hello world!']

server = web.wsgiserver.CherryPyWSGIServer(("127.0.0.1", 8080), my_wsgi_app);
server.start()

执行代码:

webpy 源码学习(一),该怎么处理
在具体看WSGIServer代码之前,我们先看一幅图,这幅图概述了WSGIServer内部执行流程:

webpy 源码学习(一),该怎么处理

接下来我们看下代码,ps: 为了较清晰的梳理主干流程,我只列出核心代码段
# Webpy内置的WSGIServer
class CherryPyWSGIServer(HTTPServer):

    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
                 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
        # 线程池(用来处理外部请求,稍后详述)
        self.requests = ThreadPool(self, min=numthreads or 1, max=max)
        # 响应外部请求的webapp
        self.wsgi_app = wsgi_app
        # wsgi网关(http_request ->wsgi_gateway ->webpy/webapp)
        self.gateway = WSGIGateway_10
        # wsgi_server监听地址
        self.bind_addr = bind_addr
    # ...

class HTTPServer(object):
    # 启动一个网络服务器
    # 如果你阅读过<<Unix网络编程>>,那么对于后面这些代码将会再熟悉不过,唯一的区别一个是c,
    #一个是python
    def start(self):

        # 如果bind_addr是一个字符串(文件名),那么采用unix domain协议
        if isinstance(self.bind_addr, basestring):
            try: os.unlink(self.bind_addr)
            except: pass
            info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
        else:
            # 否则采用TCP/IP协议
            host, port = self.bind_addr
            try:
                info = socket.getaddrinfo(host, port, socket.AF_UNSPEC, 
                                            socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
            except socket.gaierror:
                # ...
        
        # 循环测试 getaddrinfo函数返回值,直到有一个bind成功或是遍历完所有结果集
        for res in info:
            af, socktype, proto, canonname, sa = res
            try: