求助,webpy python3的东西在ML下跑不起了

6653人阅读
Web.py github 地址:& & & &&
Web.py Cookbook 简体中文版:
web.py 0.3 新手指南:
webpy 官网文档:
web.py 十分钟创建简易博客:
一个简单的web.py论坛:
web.py 是一个Python 的web 框架,它简单而且功能强大。web.py 是公开的,无论用于什么用途都是没有限制的。而且相当的小巧,应当归属于轻量级的web 框架。但这并不影响web.py 的强大,而且使用起来很简单、很直接。在实际应用上,web.py 更多的是学术上的价值,因为你可以看到更多web 应用的底层,这在当今“抽象得很好”的web 框架上是学不到的 :) 如果想了解更多web.py,可以访问web.py 的官方文档。
先感受一下web.py 的简单而强大:
import web
'/(.*)', 'hello'
app = web.application(urls, globals())
class hello:
def GET(self, name):
i = web.input(times=1)
if not name:
name = 'world'
for c in xrange(int(i.times)):
print 'Hello,', name+'!'
return 'Hello, ' + name + '!'
if __name__ == &__main__&:
上面就是一个基于web.py 的完整的Web 应用。将上面的代码保存为文件code.py,在命令行下执行python
code.py。然后打开你的浏览器,打开地址:http://localhost:8080
或者 http://localhost:8080/test&没有意外的话(当然要先安装web.py,下面会有介绍),浏览器会显示“Hello,
world”或者&“Hello, test”。
Linux 下运行
这是一个最简单的Hello world Web 应用。是不是很简单?!下面将较为详细地介绍下web.py 。
下载 web.py 的安装文件,将下载得到的文件 web.py 解压,进入解压后的文件夹,在命令行下执行:python setup.py install,在Linux 等系统下,需要root 的权限,可以执行:sudo python setup.py install。
2. URL 处理
对于一个站点来说,URL 的组织是最重要的一个部分,因为这是用户看得到的部分,而且直接影响到站点是如何工作的,例如: ,其URLs 甚至是网页界面的一部分。而web.py 以简单的方式就能够构造出一个强大的URLs。
在每个web.py 应用,必须先import web 模块:
import web
现在,我们须要告诉web.py URL 如何组织,让我们以一个简单例子开始:
'/', 'index'
在上面的例子中,第一部分是匹配URL的正则表达式,像/、/help/faq、/item/(\d+)等(\d+将匹配数字)。圆括号表示捕捉对应的数据以便后面使用。第二部分是接受请求的类名称,像index、view、welcomes.hello(welcomes模块的hello类),或者get_\1。\1&会被正则表达式捕捉到的内容替换,剩下来捕捉的的内容将被传递到你的函数中去。(‘index’)是一个类名,匹配的请求将会被发送过去。这行表示我们要URL/(首页)被一个叫index的类处理。
现在我们需要创建一个列举这些 url 的 application。
app = web.application(urls, globals())
GET 和 POST : 区别
现在,我们需要编写index 类。当大部人浏览网页的时候,他们都没有注意到,浏览器是通过HTTP 跟World Wide Web 通信的。通信的细节不太重要,但要明白一点,用户是通过URLs(例如 / 或者 /foo?f=1)来请求web 服务器完成一定请求的(例如 GET 或者POST)。
GET 是最普遍的方法,用来请求一个页面。当我们在浏览器里输入“harvard.edu” 的时候,实际上它是向Web 服务器请求GET ”/“。另一个常见的方法是POST,常用于提交特定类型的表单,比如请求买什么东西。每当提交一个去做什么事情(像使用信用卡处理一笔交易)的请求时,你可以使用POST。这是关键,因为GET的URL可以明文传输提交的参数。如果提交的是一些重要的敏感信息,例如用户名,密码,则可能被别人抓包获取到。而 POST 则不会在 URL 上传输 提交的信息,POST 是通过表单提交信息。
在我们的web.py 代码中。我们清晰区分这两种方法:
class index:
def GET(self):
print &Hello, world!&
当接收到一个GET 请求时,上面的GET 方法将会被web.py 调用。好的。现在,我们只需添加最后一行代码,让web.py 启动网页应用:
if __name__ == &__main__&:
上面告诉web.py 如何配置URLs,以及找寻的类在文件中的全局命名空间。然后为我们启动上面的应用。
整个 code.py 文件的内容如下:
import web
'/(.*)', 'hello'
app = web.application(urls, globals())
class hello:
def GET(self, name):
i = web.input(times=1)
if not name:
name = 'world'
for c in xrange(int(i.times)):
print 'Hello,', name+'!'
return 'Hello, ' + name + '!'
if __name__ == &__main__&:
实际上web 应用的代码就只得上面的几行,而且这是一个完整的web.py 应用。
3.启动服务
在你的命令行下输入:
$ python code.py
# 使用默认端口 8080
$ python code.py 10000
# 改变端口为 10000
你的web.py 应用已经启动了服务器。通过浏览器访问:http://localhost:8080/&,会见到”Hello,
world!“。
修改默认端口
在启动服务器的时候,如果你不想使用默认端口,你可以使用这样的命令来指定端口号:&python code.py 8888。
直接添加一行&web.internalerror = web.debugerror 即可。如下
if __name__==&__main__&:
web.internalerror = web.debugerror
更多关于 web.py templates 可以访问& &&
在Python 里面编写HTML 代码是相当累赘的,而在HTML 里嵌入Python 代码则有趣得多。幸运地,web.py 使这过程变得相当容易。
注意:旧版本的web.py 是用&模板的,你可以继续使用,但官方已不再提供支持。
新建一个 code.py 的 python文件。内容如下:
import web
# '/(.*)', 'hello',
'/hello_1[/]?.*', 'hello_1',
'/hello_2/(.*)', 'hello_2',
app = web.application(urls, globals())
render=web.template.render('templates')
class hello_1:
def GET(self):
return render.index_1()
class hello_2:
def GET(self, name):
return render.index_2(&Lisa&)
if __name__ == &__main__&:
这里,我们先在项目 code.py 同一级目录中新建一个目录(例如&templates&)集中存放并用来组织模板文件,便于管理。然后在templates下新建HTML
文件(例如:”index.html“)。这里新建 两个 HTML 文件。 index_1.html 和&index_2.html
index_1.html 文件内容如下:
&em&Hello&/em&, world!
这是一个最简单的html页面代码。
index_2.html 文件内容如下:
$def with (name)
&!DOCTYPE html PUBLIC &-//W3C//DTD XHTML 1.0 Strict//EN& &http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&&
&html xmlns=&http://www.w3.org/1999/xhtml&&
&meta http-equiv=&Content-Type& content=&text/ charset=utf-8& /&
&title&Template&/title&
注意上面代码的缩进!
正如你所见的,上面的模板看上去跟这Python 文件很相似,以def
with&语句开始,但在关键字前需要添加”$“。
注意:在模板内的变量,如果包含有HTML 标记,以$&方式引用变量的话,HTML
标记只会以纯文本的显示出来。要想HTML 标记产生效果,可以用$:&引用变量。
现在,回到 code.py 文件,在”import web”
的下一行添加:
render = web.template.render('templates/')
这告诉web.py 在哪里可以搜索得到模板目录。提示:可在render 调用里添加cache = False 使得每次访问页面时都重载模板。
然后再修改使用这个模板的类,在这里 &修改 类 hello_1 和 类&hello_2
class hello_1:
def GET(self):
return render.index_1()
class hello_2:
def GET(self, name):
# name = &Lisa&
return render.index_2(&Lisa&)
上面的 ”index_1“ 和 “index_2” 是模板的名字,”Lisa“ 是传递过去的参数。
同时修改urls为:
# '/(.*)', 'hello',
'/hello_1[/]?.*', 'hello_1',
'/hello_2/(.*)', 'hello_2',
上面的“/(.*)” 是一个正则表达式。括号里面是要传递的参数。再将GET 方法修改如下:
def GET(self,name):
print render.index_2(name)
hello_1&页面调用 hello_1 类,使用 index_1.html 模板。打开 http://localhost:8080/hello_1&,页面就会打印出&Hello,
world&的字样。
hello_2/ 页面调用&hello_2 类,使用 index_2.html 模板,打开 http://localhost:8080/hello_2/,页面就会打印出&Hello,
Lisa&的字样。
除此之外还有两种使用模板的方法
使用frender直接指定模板文件。GET函数最后两行改为
render=web.template.frender(&templates/index.html&)
return render(&Lisa&)直接在代码里写出模板文件。GET最后两行改为
template = &$def with (name)\nHello $name&
render = web.template.Template(template)
return render(&Lisa&)
现在解释一下这个 index.html&模板的含义:
$def with (name)
&!DOCTYPE html PUBLIC &-//W3C//DTD XHTML 1.0 Strict//EN& &http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&&
&html xmlns=&http://www.w3.org/1999/xhtml&&
&meta http-equiv=&Content-Type& content=&text/ charset=utf-8& /&
&title&Template&/title&
在index.html第一行 $def with (name)表示本模板接受一个名为name的参数,也就是对应index类中return render.index(“Lisa”)中的Lisa。
而render=web.template.render(“templates”)表示创建一个模板对象,模板是存放于templates目录下,然后就可以用所创建的 render 对象来访问相应的模板
比如templates目录下的index.html就是用render.index来表示(实际上是匹配寻找index.*文件,第一个匹配的就认为是所对应的模板文件),如果templates下还有个a目录,a目录下有个pagea.html,那么访问这个pagea模板就要用render.a.pagea的形式了。
页面接收的参数可以多于一个,也可以没有,如果不需要参数,则就不需要$def with (name)这样的代码,删除掉这一句,同时修改模板中对name变量的引用,修改index类最后一句为return render.index()就可以了。
如果有参数,那么模板的第一行代码就必须是这个 $def with (…),可以多于一个参数,比如是这样$def with (gname, fname)。
模板下面的那行字改为Hi, $gname $fname。
同时Index类GET返回的时候赋予对应两个参数return render.index(“Lisa”,”Hayes”)。
这样,页面最后显示的是打印出Hi, Lisa Hayes的字样。
另外,模板接受的这个参数也可以是一个元组,比如像下面这样:return render.index((“Lisa”,”Hayes”))
在模板中可以如下以元组方式访问参数数据:Hi, $name[0] $name[1]
模板语法与python语法基本一致,主要差别可以从上面的代码中看到,要使用到$符号表明这不是文本而是模板代码。也就是每当用到程序代码、对象的时候就必须用$来与html代码和页面显示文本相区别。
对象赋值
向对象赋值时需要在$与对象名之间留空格,如为一个名为vara的字符串对象赋值apple的代码为$ vara = “apple”。
另外,对象赋值语句必须独占一行,前面或后面有其他代码则会程序出错。
引用对象的时候直接使用 $+对象名的形式,如$vara。
另外引用对象时还可以用{}或()将对象进行明确的分组,如$(vara)s就会表示apples,如果没有括号,程序则会把 $varas作为一个整体,也就变成对varas对象的引用而发生错误。另如果像如下定义两个数字型对象:
然后希望计算两个值的和,如果用$varb+$varc的形式,页面上只会得到1+2而不是3,这时也就需要把两个对象放在括号里,如$(varb+varc)的形式才能得到正确答案3。
模板中支持单行注释,以$#符号开始到行末都是注释内容。
$#This is comment
注释前面可以有其他内容,但是不可以有赋值代码。
如下代码是正确的:Hi $#This is comment
但下面的则会出错:$ vara = “apple” $#This is comment
由于$符号在模板中有特殊用途,所以在页面上输出$时需要进行转义操作,用连续两个$表示在页面上输出一个$符号。
Can you lend me $50?
控制代码(循环、条件判断)
模板中支持for、while、if、elif、else,用法与在python一致,只是控制代码行要以$开始(包括break和continue命令),$开始的代码行中对象不需要在前面再加$符号,同时要注意缩进规则,如:
for 循环:
$def with (toUser,fromUser,createTime,articleCnt,articles)
&ToUserName&&![CDATA[$toUser]]&&/ToUserName&
&FromUserName&&![CDATA[$fromUser]]&&/FromUserName&
&CreateTime&$createTime&/CreateTime&
&MsgType&&![CDATA[news]]&&/MsgType&
&ArticleCount&$articleCnt&/ArticleCount&
&Articles&
$for a in articles:
&Title&&![CDATA[$a['title']]]&&/Title&
&Description&&![CDATA[$a['desc']]]&&/Description&
&PicUrl&&![CDATA[$a['picUrl']]]&&/PicUrl&
&Url&&![CDATA[$a['url']]]&&/Url&
&/Articles&
if else判断:
$if times & max:
Stop! In the name of love.
Keep on, you can do it.
在for循环中,有一组内置的变量可以使用,非常方便,分别如下所示:
loop.index: 循环次数计数 (1-开始)loop.index0: 循环次数计数(0-开始)loop.first: 如果是第一次循环则为Trueloop.last: 如果是最后一次循环则为Trueloop.odd: 如果是第奇数次循环则为Trueloop.even: 如果是第偶数次循环则为Trueloop.parity: 如果循环次数为奇数值为“odd” ,反之为 “even”loop.parent: 本循环的外层循环对象
$for a in [&a&, &b&, &c&, &d&]:
$loop.index,$loop.index0,$loop.first,$loop.last,$loop.odd,$loop.even,$loop.parity&br/&
将在页面上打印出:
1,0,True,False,True,False,odd
2,1,False,False,False,True,even
3,2,False,False,True,False,odd
4,3,False,True,False,True,even
函数定义也是与在python中类似,用def,只是也要在前面加$,代码也要注意$的使用和缩进:
$def hello(name=&&):
Hello $name!函数调用也是用$加函数名的形式:$hello(&Lisa&)当然,定义函数时也可以与html代码混编:$def hello(name=&&):
&strong/&Hello $name!&/strong&但是调用的时候需要在函数名前用$:前缀,否则html代码将以plain text形式打印到页面上。$:hello(&Lisa&)
输出程序代码-$code块
如果想在模板里输入写一段python代码而又不想被$所烦恼,那么可以用到$code块。
页面上输出一段代码而不希望被系统理解为模板程序代码,就需要用到$code命令,比如在模板文件中写入下面一段:
def print_num(num):
return &num is %d& % num
然后再加上下面代码:
$print_num(x)
这里就用在$code块中定义的print_num函数以x变量为参数在页面上输出一行:num is 10
然后下一行直接引用x变量,直接在页面上输出数字10。
$var命令可以在模板中定义变量,在其他地方引用此模板对象的时候可以访问此定义的变量。
比如我们可以在index.html中添加如下一行:$var vara: this is vara
表示定义了一个名为vara的变量,变量值是字符串this is vara。
把index的GET函数改为:
def GET(self):
render=web.template.render(&templates&)
return render.index(&Lisa&, &Hayes&).vara
那么结果显示在页面上的就是this is vara这句话。要注意一点的是,这种变量是字符串,即便如下定义变量:$var vara: 0
Vara也并不是数字0,如果把GET函数最后改成:return render.index(“Lisa”, “Hayes”).vara+1
会导致程序出错。如果希望得到期望中的结果1,则需要如下形式代码:return int(render.index(“Lisa”, “Hayes”).vara)+1
builtins and globals
在模板中,用户可以直接使用python的内建函数和变量,写函数变量包括range, min, max 以及 True 和 False等。 除此之外,如果希望在模板中使用其他的非内建功能,就需要一点特殊操作。要在创建render的时候显式指定所需要的功能函数。
import web
import markdown
globals = {'markdown': markdown.markdown}
render =web.template.render('templates', globals=globals)
这样,在模板中就可以用$markdown来引用markdown.markdown了。同样,也可以用这种办法来禁用builtins
# disable all builtins
render = web.template.render('templates', builtins={})
当多个页面有着相同的结构框架的时候,为每一个页面单独维护一个模板就显得比较麻烦,web.py提供了一种简易的解决方法。
这时候就要用到创建render时使用base参数:
render=web.template.render(&templates&,base=&layout&)
return render.index(&Lisa&, &Hayes&)
这个layout表示要以templates下的layout.html模板为通用模板框架。因此我们还要在templates目录下新建一个layout.html文件,写下如下代码:
$def with (content)
&!DOCTYPE html PUBLIC &-//W3C//DTD XHTML 1.0 Strict//EN& &http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&&
&html xmlns=&http://www.w3.org/1999/xhtml&&
&meta http-equiv=&Content-Type& content=&text/ charset=utf-8& /&
&title&Layout&/title&
&/html&可以看到,这个模板文件必须是有一个参数content。然后修改index.html,只保留如下代码,其他删掉:
$def with(gname, fname)
Hi, $(gname) $(fname)
运行程序,页面上打印Hi, Lisa Hayes,查看代码会发现最终代码就是index.html和layout.html合并在一起的结果,index.html中的内容被嵌入到layout.html中的$:content处。
在layout.html模板中还可以引用index.html中定义的var变量,这为程序带来了更多的灵活性,比如我们希望在不同的页面在使用同一个layout模板的时候能有不同的title,可以在使用layout的模板中定义如下一个var变量:$var title:This is index.html
然后在layout.html中的title处修改为:&title&$content.title&/title&
这样,访问index.html时显示在浏览器上的title就是This is index.html,而不是原来的Layout了。
在模板中使用python代码模块
在默认状态下,在模板中是不能直接调用其他python代码模块文件中的程序的,必须做一些额外的操作。
首先,我们新建一个模块,叫module1.py,在里面写一个函数:
def hello_from_m1(name=&&):
return &hello %s, this is module1& % name
在main.py里导入module1:import module1
并且修改GET函数中创建render的代码为:
def GET(self):
render=web.template.render(&templates&,base=&layout&,globals={&m1&:module1})
return render.index(&Lisa&)
globals参数中传递的是一个字典,key以字符串表示模块在模板中使用时的名称,value部分就是这个要在模块中使用的模块或对象的真实名称了。
最后在要使用此模块的模板中就可以用$m1来引用此模块了。比如在index.html中添加下面一行代码:$m1.hello_from_m1(gname)
就会调用module1中的hello_from_m1函数,在页面上打印出:hello Lisa, this is module1
在web.py模板中使用jQuery
在jQuery中$也是一个关键字,这样的话如果在模板中使用jQuery就会冲突,这时候只需要用$做一下转义就可以了,比如:
&script type=&text/javascript&&
$(document).ready(function()
alert(&It works.&);
Web.py 更多关于数据库的操作:
注意:在你开始连接数据库之前,请先安装正确的数据库驱动。比如 MySQLdb、psycopg2。如果需要尝试连接
池(database pool)功能,还得装下DBUtils。这几个模块都可以通过easy_install 或者 pip 来安装。
连接数据库:
import web
db = web.database(dbn='postgres', db='mydata', user='dbuser', pw='')
操作 数据库 示例
select 查询
entries = db.select('mytable')
# where 条件
myvar = dict(name=&Bob&)
results = db.select('mytable', myvar, where=&name = $name&)
results = db.select('mytable', where=&id&100&)
# 查询具体列
results = db.select('mytable', what=&id,name&)
# order by
results = db.select('mytable', order=&post_date DESC&)
results = db.select('mytable', group=&color&)
results = db.select('mytable', limit=10)
results = db.select('mytable', offset=10)
db.update('mytable', where=&id = 10&, value1 = &foo&)
db.delete('mytable', where=&id=10&)
results = db.query(&SELECT COUNT(*) AS total_users FROM users&)
print results[0].total_users
results = db.query(&SELECT * FROM entries JOIN users WHERE entries.author_id = users.id&)
# 防止SQL注入可以这么干
results = db.query(&SELECT * FROM users WHERE id=$id&, vars={'id':10})
多数据库操作 (web.py大于0.3)
db1 = web.database(dbn='mysql', db='dbname1', user='foo')
db2 = web.database(dbn='mysql', db='dbname2', user='foo')
print db1.select('foo', where='id=1')
print db2.select('bar', where='id=5')
t = db.transaction()
db.insert('person', name='foo')
db.insert('person', name='bar')
t.rollback()
t.commit()
# Python 2.5+ 可以用with
from __future__ import with_statement
with db.transaction():
db.insert('person', name='foo')
db.insert('person', name='bar')
现在,在数据库里创建一个简单的表:
CREATE TABLE todo (
id serial primary key,
title text,
created timestamp default now(),
done boolean default 'f'
/* 初始化一行 */
INSERT INTO todo (title) VALUES ('Learn web.py');
回到 code.py,修改GET 方法如下:
def GET(self):
todos = db.select('todo')
print render.index(todos)
修改urls 变量:
'/', 'index')
重新编辑index.html 文件如下:
$def with (todos)
$for todo in todos:
&li id=&t$todo.id&&$todo.title&/li&
现在,可以访问”/“,如果显示”Learn
web.py“,则祝贺你成功了!
现在,再让我们看看如何向数据库写入。在index.html 文件的尾部添加如下内容:
&form method=&post& action=&add&&
&input type=&text& name=&title& /&
&input type=&submit& value=&Add& /&
修改code.py 文件的urls 变量如下:
'/', 'index',
'/add', 'add'
在code.py 里添加一个类:
class add:
def POST(self):
i = web.input()
n = db.insert('todo', title=i.title)
web.seeother('/')
web.input&使你能够方便地访问用户通过表单提交上来的变量。db.insert&用于向数据库的
“todo” 表插入数据,并且返回新插入行的ID。web.seeother&用于重转向到”/“。
提示:对于数据库的操作,还有db.transact(),&db.commit(),&db.rollback(),db.update()。
在web.py 里,还有web.input,web.query&和其它的函数,返回”Storage
objects”,可以像字典型类(dictionaries) 的使用。
使用 Web.py 搭建一个测试网站案例
Web.py Form库文档 和 示例代码 :
参考&& 这篇文章改写成 Web.py 搭建测试网站
先看 官网一个使用 Form 表单的示例(code.py):
import web
from web import form
render = web.template.render('templates/')
urls = ('/', 'index')
app = web.application(urls, globals())
myform = form.Form(
form.Textbox(&boe&),
form.Textbox(&bax&,
form.notnull,
form.regexp('\d+', 'Must be a digit'),
form.Validator('Must be more than 5', lambda x:int(x)&5)),
form.Textarea('moe'),
form.Checkbox('curly'),
form.Dropdown('french', ['mustard', 'fries', 'wine']))
class index:
def GET(self):
form = myform()
# make sure you create a copy of the form by calling it (line above)
# Otherwise changes will appear globally
print(form.render())
return render.formtest(form)
def POST(self):
form = myform()
if not form.validates():
print(form.render())
return render.formtest(form)
# form.d.boe and form['boe'].value are equivalent ways of
# extracting the validated arguments from the form.
return &Grrreat success! boe: %s, bax: %s& % (form.d.boe, form['bax'].value)
if __name__==&__main__&:
web.internalerror = web.debugerror
formtest.html 代码如下:
$def with (form)
&div align=&center&&
&form name=&main& method=&post&&
$if not form.valid: &p class=&error&&Try again, AmeriCAN:&/p&
$:form.render()
&input type=&submit& /&
Linux 下运行结果
然后根据上面内容开始改写 自己的网站
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:147779次
积分:1797
积分:1797
排名:千里之外
原创:36篇
转载:172篇
(8)(18)(3)(20)(2)(14)(1)(5)(9)(2)(8)(14)(11)(8)(17)(35)(28)(9)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'详细解读Python的web.py框架下的application.py模块
作者:diabloneo
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了Python的web.py框架下的application.py模块,作者深入分析了web.py的源码,需要的朋友可以参考下
本文主要分析的是web.py库的application.py这个模块中的代码。总的来说,这个模块主要实现了WSGI兼容的接口,以便应用程序能够被WSGI应用服务器调用。WSGI是Web Server Gateway Interface的缩写,具体细节可以查看WSGI的WIKI页面
接口的使用
使用web.py自带的HTTP Server
下面这个例子来自官方文档的Hello World,这个代码一般是应用入口的代码:
import web
urls = ("/.*", "hello")
app = web.application(urls, globals())
class hello:
def GET(self):
return 'Hello, world!'
if __name__ == "__main__":
上面的例子描述了一个web.py应用最基本的组成元素:
&&& URL路由表
&&& 一个web.application实例app
&&& 调用app.run()
其中,app.run()的调用是初始化各种WCGI接口,并启动一个内置的HTTP服务器和这些接口对接,代码如下:
def run(self, *middleware):
return wsgi.runwsgi(self.wsgifunc(*middleware))
与WSGI应用服务器对接
如果你的应用要与WSGI应用服务器对接,比如uWSGI,gunicorn等,那么应用入口的代码就要换一种写法了:
import web
class hello:
def GET(self):
return 'Hello, world!'
urls = ("/.*", "hello")
app = web.application(urls, globals())
application = app.wsgifunc()
在这种场景下,应用的代码不需要启动HTTP服务器,而是实现一个WSGI兼容的接口供WSGI服务器调用。web.py框架为我们实现了这样的接口,你只需要调用application = app.wsgifunc()就可以了,这里所得到的application变量就是WSGI接口(后面分析完代码你就会知道了)。
WSGI接口的实现分析
分析主要围绕着下面两行代码进行:
app = web.application(urls, globals())
application = app.wsgifunc()
web.application实例化
初始化这个实例需要传递两个参数:URL路由元组和globals()的结果。
另外,还可以传递第三个变量:autoreload,用来指定是否需要自动重新导入Python模块,这在调试的时候很有用,不过我们分析主要过程的时候可以忽略。
application类的初始化代码如下:
class application:
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 = []
self.add_processor(loadhook(self._load))
self.add_processor(unloadhook(self._unload))
if autoreload:
其中,autoreload相关功能的代码略去了。其他的代码主要作了如下几个事情:
&&& self.init_mapping(mapping):初始化URL路由映射关系。
&&& self.add_processor():添加了两个处理器。
初始化URL路由映射关系
def init_mapping(self, mapping):
self.mapping = list(utils.group(mapping, 2))
这个函数还调用了一个工具函数,效果是这样的:
urls = ("/", "Index",
"/hello/(.*)", "Hello",
"/world", "World")
如果用户初始化时传递的元组是这样的,那么调用init_mapping之后:
self.mapping = [["/", "Index"],
["/hello/(.*)", "Hello"],
["/world", "World"]]
后面框架在进行URL路由时,就会遍历这个列表。
添加处理器
self.add_processor(loadhook(self._load))
self.add_processor(unloadhook(self._unload))
这两行代码添加了两个处理器:self._load和self._unload,而且还对这两个函数进行了装饰。处理器的是用在HTTP请求处理前后的,它不是真正用来处理一个HTTP请求,但是可以用来作一些额外的工作,比如官方教程里面有提到的给子应用添加session的做法,就是使用了处理器:
def session_hook():
web.ctx.session = session
app.add_processor(web.loadhook(session_hook))
处理器的定义和使用都是比较复杂的,后面专门讲。
wsgifunc函数
wsgifunc的执行结果是返回一个WSGI兼容的函数,并且该函数内部实现了URL路由等功能。
def wsgifunc(self, *middleware):
"""Returns a WSGI-compatible function for this application."""
for m in middleware:
wsgi = m(wsgi)
return wsgi
除开内部函数的定义,wsgifunc的定义就是这么简单,如果没有实现任何中间件,那么就是直接返回其内部定义的wsgi函数。
该函数实现了WSGI兼容接口,同时也实现了URL路由等功能。
def wsgi(env, start_resp):
# clear threadlocal to avoid inteference of previous requests
self._cleanup()
self.load(env)
# 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)
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())
for m in middleware:
wsgi = m(wsgi)
return wsgi
下面来仔细分析一下这个函数:
self._cleanup()
self.load(env)
self._cleanup()内部调用utils.ThreadedDict.clear_all(),清除所有的thread local数据,避免内存泄露(因为web.py框架的很多数据都会保存在thread local变量中)。
self.load(env)使用env中的参数初始化web.ctx变量,这些变量涵盖了当前请求的信息,我们在应用中有可能会使用到,比如web.ctx.fullpath。
# 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)
result = [result]
except web.HTTPError, e:
result = [e.data]
这一段主要是调用self.handle_with_processors(),这个函数会对请求的URL进行路由,找到合适的类或子应用来处理该请求,也会调用添加的处理器来做一些其他工作(关于处理器的部分,后面专门讲)。对于处理的返回结果,可能有三种方式:
&&& 返回一个可迭代对象,则进行安全迭代处理。
&&& 返回其他值,则创建一个列表对象来存放。
&&& 如果抛出了一个HTTPError异常(比如我们使用raise web.OK("hello, world")这种方式来返回结果时),则将异常中的数据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())
接下来的这段代码,会对前面返回的列表result进行字符串化处理,得到HTTP Response的body部分。然后根据WSGI的规范作如下两个事情:
&&& 调用start_resp函数。
&&& 将result结果转换成一个迭代器。
现在你可以看到,之前我们提到的application = app.wsgifunc()就是将wsgi函数赋值给application变量,这样应用服务器就可以采用WSGI标准和我们的应用对接了。
处理HTTP请求
前面分析的代码已经说明了web.py框架如何实现WSGI兼容接口的,即我们已经知道了HTTP请求到达框架以及从框架返回给应用服务器的流程。那么框架内部是如何调用我们的应用代码来实现一个请求的处理的呢?这个就需要详细分析刚才忽略掉的处理器的添加和调用过程。
loadhook和unloadhook装饰器
这两个函数是真实处理器的函数的装饰器函数(虽然他的使用不是采用装饰器的@操作符),装饰后得到的处理器分别对应请求处理之前(loadhook)和请求处理之后(unloadhook)。
def loadhook(h):
def processor(handler):
return handler()
return processor
这个函数返回一个函数processor,它会确保先调用你提供的处理器函数h,然后再调用后续的操作函数handler。
unloadhook
def unloadhook(h):
def processor(handler):
result = handler()
is_generator = result and hasattr(result, 'next')
# run the hook even when handler raises some exception
if is_generator:
return wrap(result)
return result
def wrap(result):
def next():
return result.next()
# call the hook at the and of iterator
result = iter(result)
while True:
yield next()
return processor
这个函数也返回一个processor,它会先调用参数传递进来的handler,然后再调用你提供的处理器函数。
handle_with_processors函数
def handle_with_processors(self):
def process(processors):
if processors:
p, processors = processors[0], processors[1:]
return p(lambda: process(processors))
return self.handle()
except web.HTTPError:
except (KeyboardInterrupt, SystemExit):
print && web.debug, traceback.format_exc()
raise self.internalerror()
# processors must be applied in the resvere order. (??)
return process(self.processors)
这个函数挺复杂的,最核心的部分采用了递归实现(我感觉不递归应该也能实现同样的功能)。为了说明清晰,采用实例说明。
前面有提到,初始化application实例的时候,会添加两个处理器到self.processors:
self.add_processor(loadhook(self._load))
self.add_processor(unloadhook(self._unload))
所以,现在的self.processors是下面这个样子的:
self.processors = [loadhook(self._load), unloadhook(self._unload)]
# 为了方便后续说明,我们缩写一下:
self.processors = [load_processor, unload_processor]
当框架开始执行handle_with_processors的时候,是逐个执行这些处理器的。我们还是来看代码分解,首先简化一下handle_with_processors函数:
def handle_with_processors(self):
def process(processors):
if processors: # 位置2
p, processors = processors[0], processors[1:]
return p(lambda: process(processors)) # 位置3
return self.handle() # 位置4
except web.HTTPError:
# processors must be applied in the resvere order. (??)
return process(self.processors) # 位置1
&&& 函数执行的起点是位置1,调用其内部定义函数process(processors)。
&&& 如果位置2判断处理器列表不为空,则进入if内部。
&&& 在位置3调用本次需要执行的处理器函数,参数为一个lambda函数,然后返回。
&&& 如果位置2判断处理器列表为空,则执行self.handle(),该函数真正的调用我们的应用代码(下面会讲到)。
以上面的例子来说,目前有两个处理器:
self.processors = [load_processor, unload_processor]
从位置1进入代码后,在位置2会判断还有处理器要执行,会走到位置3,此时要执行代码是这样的:
return load_processor(lambda: process([unload_processor]))
load_processor函数是一个经过loadhook装饰的函数,因此其定义在执行时是这样的:
def load_processor(lambda: process([unload_processor])):
self._load()
return process([unload_processor]) # 就是参数的lambda函数
会先执行self._load(),然后再继续执行process函数,依旧会走到位置3,此时要执行的代码是这样的:
return unload_processor(lambda: process([]))
unload_processor函数是一个经过unloadhook装饰的函数,因此其定义在执行时是这样的:
def unload_processor(lambda: process([])):
result = process([]) # 参数传递进来的lambda函数
is_generator = result and hasattr(result, 'next')
# run the hook even when handler raises some exception
self._unload()
if is_generator:
return wrap(result)
self._unload()
return result
现在会先执行process([])函数,并且走到位置4(调用self.handle()的地方),从而得到应用的处理结果,然后再调用本处理器的处理函数self._unload()。
总结一下执行的顺序:
self._load()
self.handle()
self._unload()
如果还有更多的处理器,也是按照这种方法执行下去,对于loadhook装饰的处理器,先添加的先执行,对于unloadhook装饰的处理器,后添加的先执行。
handle函数
讲了这么多,才讲到真正要调用我们写的代码的地方。在所有的load处理器执行完之后,就会执行self.handle()函数,其内部会调用我们写的应用代码。比如返回个hello, world之类的。self.handle的定义如下:
def handle(self):
fn, args = self._match(self.mapping, web.ctx.path)
return self._delegate(fn, self.fvars, args)
这个函数就很好理解了,第一行调用的self._match是进行路由功能,找到对应的类或者子应用,第二行的self._delegate就是调用这个类或者传递请求到子应用。
_match函数
_match函数的定义如下:
def _match(self, mapping, value):
for pat, what in mapping:
if isinstance(what, application): # 位置1
if value.startswith(pat):
f = lambda: self._delegate_sub_application(pat, what)
return f, None
elif isinstance(what, basestring): # 位置2
what, result = utils.re_subm('^' + pat + '$', what, value)
else: # 位置3
result = utils.re_compile('^' + pat + '$').match(value)
if result: # it's a match
return what, [x for x in result.groups()]
return None, None
该函数的参数中mapping就是self.mapping,是URL路由映射表;value则是web.ctx.path,是本次请求路径。该函数遍历self.mapping,根据映射关系中处理对象的类型来处理:
&&& 位置1,处理对象是一个application实例,也就是一个子应用,则返回一个匿名函数,该匿名函数会调用self._delegate_sub_application进行处理。
&&& 位置2,如果处理对象是一个字符串,则调用utils.re_subm进行处理,这里会把value(也就是web.ctx.path)中的和pat匹配的部分替换成what(也就是我们指定的一个URL模式的处理对象字符串),然后返回替换后的结果以及匹配的项(是一个re.MatchObject实例)。
&&& 位置3,如果是其他情况,比如直接指定一个类对象作为处理对象。
如果result非空,则返回处理对象和一个参数列表(这个参数列表就是传递给我们实现的GET等函数的参数)。
_delegate函数
从_match函数返回的结果会作为参数传递给_delegate函数:
fn, args = self._match(self.mapping, web.ctx.path)
return self._delegate(fn, self.fvars, args)
&&& fn:是要处理当前请求的对象,一般是一个类名。
&&& args:是要传递给请求处理对象的参数。
&&& self.fvars:是实例化application时的全局名称空间,会用于查找处理对象。
_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):
return f.handle_with_processors()
elif is_class(f):
return handle_class(f)
elif isinstance(f, basestring):
if f.startswith('redirect '):
url = f.split(' ', 1)[1]
if web.ctx.method == "GET":
x = web.ctx.env.get('QUERY_STRING', '')
url += '?' + x
raise web.redirect(url)
elif '.' in f:
mod, cls = f.rsplit('.', 1)
mod = __import__(mod, None, None, [''])
cls = getattr(mod, cls)
cls = fvars[f]
return handle_class(cls)
elif hasattr(f, '__call__'):
return f()
return web.notfound()
这个函数主要是根据参数f的类型来做出不同的处理:
&&& f为空,则返回302 Not Found.
&&& f是一个application实例,则调用子应用的handle_with_processors()进行处理。
&&& f是一个类对象,则调用内部函数handle_class。
&&& f是一个字符串,则进行重定向处理,或者获取要处理请求的类名后,调用handle_class进行处理(我们写的代码一般是在这个分支下被调用的)。
&&& f是一个可调用对象,直接调用。
&&& 其他情况返回302 Not Found.
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具}

我要回帖

更多关于 web2py webpy 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信