WukooPy -- Web usage Keep object of Python

悟空系统 -- 保持对象化的 Web应用!

::-- ZoomQuiet [2005-07-13 02:41:38]

2. 缘起

现有 Web Application 点评 这么多Web Application,到底用什么好啊

俺以前一直用zope,后来感觉是越来越差,原因与Quixote和Cherrypy的作者差不多,网上有他们的文章,俺这里就不说了。后来找啊找啊,前前后后重点试过Quixote、Cherrypy、Karrigell,感觉都还不错,各有各的特点:

  1. Cherrypy:

    • 将对象机制引入web开发,完全的面向对象发布机制,底层数据、api函数使用cpg集中管理。缺点是底层api调用代码老在变,天啊,俺真的受不了,以前做了一个cherrypy小应用,前前后后重构了10来遍,每次只要用svn更新一下cherrypy代码,就得改一次俺的应用代码。俺就搞不明白,比如一个调用session,就用cpg.sessions就成了吧,为什么一会儿这个样式,一会儿另外个样式。

  2. Quixote:

    • 不错,发布方式多,代码结构好,质量最佳,基本上没什么BUG。只是语法调用有点怪异,适应了半天,最后还是不适应。

  3. Karrigell:

    • 最易用的系统,入门真简便,良好的debug框架,动态载入。只是代码结构不好,做个应用就得将Karrigell移过去。再有url不支持定制,太有局限性,比如我动态生成一个js文件,总不能扩展名是个py,hip...吧,而且作者象俺有点不务正业,不老老实实做好框架,却去设计什么dbstrange,还做了一堆没用的半成品,唉,有这功夫把自留地种好啊。感觉做做原型、快速开发还不错。

2.1. 自我需求

见识了这么多框架,俺理想的架构长得模样也自然产生了(主要性能):

  1. 具有多多的发布接口,象Quixote,就是俺以前提过的万能接口,接口越多,余地越大啊。

  2. 底层API与数据的集中管理与调用。比如dgn.get_request(),dgn.get_reponse(),这里的特性是Quixote与Cherrypy的结合和升华。

  3. 简便的发布方式。考虑web前端部份的python代码是最易变的,尽可能简单,易读。利用所有都是对象的性能,选择函数发布。

  4. 支持url定制,易url的default性能。这个需求好象现在很重要,总比使用apache的mod_write好多了。

  5. 语法规则分离出来,用户可使用内置规则,也可定制自已的。内置:quixote_publish1,quixote_publish2,karrigell和函数发布

  6. 方便的角色、权限定义和管理

2.2. 设定前题

系统使用Karrigell和Quixote代码,对于这两个系统的补丁修改,一律使用动态修正方式偷梁换柱,不改变系统的任何代码,以满足原系统的升级和版权等要求。

使用Karrigell的发布方式注意,需将Karrigell发布为KarrigellLib包。即增加init.py,放在site_packages下

2.2.1. 特性预览

还是用一个简便例子说明:

   1 
   2 #--系统函数--
   3 def _charsetGB2312(fn):
   4     '''函数包装函数,将页面设为gb2312'''
   5     def _init(dgn,*args):
   6         dgn.set_charset('gb2312')
   7         return fn(dgn,*args)
   8     return _init
   9 def _baseAuth(dgn,func,realm='Protected'):
  10     '''权限函数,只有我能上'''
  11     t=dgn.get_baseauth()
  12     if t is not  None and t==('lihui','password'):
  13         result=None
  14     else:
  15         dgn.set_status(401)
  16         dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
  17         result=True
  18     return result
  19 #--web前端--
  20 def helloworld1(dgn):
  21     '''发布一个函数,dgn为数据api集中调用入口,为第一个参数,dgn使用的singlon模式,除系统内置定义外,可自行加入定义,比如dbpool,可定义为dgn.db.dbpool
  22 '''
  23     return 'helloworld1'
  24 
  25 def helloworld2(dgn):
  26     '''发布一个函数,加个权限'''
  27     return 'helloworld1'
  28 helloworld2._q_access=_baseAuth
  29 
  30 def helloworld3(dgn):
  31     '''发布一个函数,gb2312发布'''
  32     return 'helloworld1'
  33 helloworld3=_charsetGB2312(helloworld3)
  34 
  35 def helloworld4(dgn,*args):
  36     '''有args的支持default,比如这个函数url为http:/***/p,这个http:/***/p/1/1/1.html,则转为args=['1','1','1.html']'''
  37     return 'helloworld1'
  38 
  39 def helloworld5(dgn):
  40     '''发布一个图形文件'''
  41     return "图文件"
  42 
  43 
  44 def images(dgn,*args):
  45     '''发布images子目录,静态目录'''
  46     return dgn.StaticDirectory(os.path.join(dgn.get_config().path_top,'images'),*args)
  47 
  48 #--发布函数
  49 def root():
  50     result=helloworld1
  51     result.images=images
  52     result.hl2=helloworld2
  53     result.hl3=helloworld3
  54     result.hl4=helloworld4
  55     result.hl5_jpg=helloworld5  #扩展名为jpg
  56     return result

2.2.2. 网站架构组合

原来一直用的是zope的zpt和dtml,cherrytemplate是两者的混合,使用很方便,模块就选它拉

sqlobject真不错,实行了关系数据库+逻辑+对象处理,zodb和存储过程俺再也不用拉。

俺的最佳组合就是dragon+cherrytemplate+sqlobject

3. 下载

0.11 版发布 WukooPy-0.11.zip

0.10 版发布 WukooPy-0.10.zip

支持json,请使用新新的svn版本,感觉是最简单的json发布,不是吹牛。不过可以发送中文,返回的中文编码不知怎么做,熟悉js帮一下先。 wukoopy_testjson.rar

SVN下载:

[WWW] http://www.woodpecker.org.cn/svn/woodpecker/WukooPy/trunk/

user:woodpecker,pass:[MAILTO] [email protected]

TRAC访问:

[WWW] http://www.woodpecker.org.cn/trac.cgi/browser/WukooPy/trunk/

4. 快速体验

如何利用 WukooPy 进行Web 发布 ::-- ZoomQuiet [2005-07-16 07:05:19]

4.1. 建立一个wukoo_helloworld包

所有应用项目均以python包方式发布,在这里建立python包的方法很多,不再详述。用于测试,比较偷赖的方法是:

1)在系统site-packages子目录下建立wukoo_helloworld子目录

2)在wukoo_helloworld下建立一个init.py

这样我们就建立了一个wukoo_helloworld包,下面如不特指,一切操作均在wukoo_helloworld子目录下进行

4.2. 建立项目

4.2.1. 建立发布内容

增加一个helloword.py文件,内容:

   1 
   2 def helloworld(wkp):
   3     return 'Hello World'

这样我们就做了一个函数发布的页面,页面内容为Hello World,wkp参数为api调用入口

4.2.2. 制作页面发布接口

建立发布内容后,就要建立url调用逻辑,这种系统指定使用一个root函数,修改文件为

   1 
   2 def helloworld(wkp):
   3     return 'Hello World'
   4 
   5 def root():
   6     result=helloworld
   7     return result

4.2.3. 制作项目发布接口

每个项目均应制作一个内容发布接口函数create_publisher,该函数调用不用的语法规则(使用不用的publisher),上页内容增加为:

   1 
   2 def helloworld(wkp):
   3     return 'Hello World'
   4 
   5 def root():
   6     result=helloworld
   7     return result
   8 
   9 from wukoopy.publish_wukoopy import Publisher ##使用publish_wukoopy发布
  10 def create_publisher():
  11     return Publisher(
  12         root
  13         )

wukoopy下内置quixote1,quixote2,wukoopy,karrigell四种发布方式,Publisher 参数为:

第一个参数:项目入口,每种publisher的入口形式均不一样,wukoopy为root函数

session_manager:为使用sessions,不加则不使用

config:配置文件

logger:log记录

**kwargs :其它配置

到此我们的项目基本做完了

4.2.4. 选择不同方式发布

在wukoo.server下有不同的server发布方式

1)选择simple方式

(1)在命令行下执行

python \*\*\simple_server.py --factory wukoo_helloworld.helloworld.create_publisher

(2)使用python程序

建立一个run_simple.py文件,内容

   1 from wukoo_helloworld.helloworld import create_publisher
   2 from wukoopy.server.simple_server import run
   3 run(create_publisher)

运行后,访问http://localhost:8080/

2)选择cgi访问

建立一个run_cgi.cgi文件,并放在apache可以访问的地方

   1 from wukoo_helloworld.helloworld import create_publisher
   2 from wukoopy.server.cgi_server import run
   3 run(create_publisher)

3)选择fast cgi访问

建立一个run_cgi.fcgi文件,并放在apache可以访问的地方(apache配置不详述)

   1 from wukoo_helloworld.helloworld import create_publisher
   2 from wukoopy.server.fastcgi_server import run
   3 run(create_publisher)

4)选择xitami访问

建立一个run_xitami.py文件

   1 from wukoo_helloworld.helloworld import create_publisher
   2 from wukoopy.server.xitami_server import run
   3 run(create_publisher,'helloworld')

5)选择mod_python访问

配置apache.conf

<LocationMatch "^/helloworld(/|$)">
    SetHandler python-program
    PythonHandler wukoopy.server.mod_python_handler
    PythonOption quixote-publisher-factory wukoo_helloworld.helloworld.create_publisher
    PythonInterpreter wukoo_helloworld.helloworld
    PythonDebug On
</LocationMatch>

6)选择scgi方式

(略)

7)server下还用几种发布方式,参照上述进行。(有一些还是半成品,文件名中有todo)

4.3. 更多特性

4.3.1. 加入子页面

增加一个index,初步展示调用api的方法:

   1 
   2 def index(wkp):
   3     wkp.set_content_type("text/html")
   4     result="<html><body>"
   5     result+='<a href="/hello">Hello World</a><br><br>'
   6     result+='</body></html>'
   7     return result
   8 
   9 def helloworld(wkp):
  10     return 'Hello World'
  11 
  12 def root():
  13     result=index
  14     result.hello=helloworld
  15     return result

4.3.2. 发布一个静态目录

建立一个images子目录,并发布这个目录为/images

   1 
   2 def index(wkp):
   3     wkp.set_content_type("text/html")
   4     result="<html><body>"
   5     result+='<a href="/hello">Hello World</a><br><br>'
   6     result+='</body></html>'
   7     return result
   8 
   9 def helloworld(wkp):
  10     return 'Hello World'
  11 
  12 import os.path
  13 def images(dgn,*args):
  14     return dgn.StaticDirectory(os.path.join(os.path.dirname(__file__),'images'),*args)
  15 
  16 def root():
  17     result=index
  18     result.hello=helloworld
  19     result.images=images
  20     return result

4.3.3. 发布一个静态文件

在目录下放一个favicon.ico文件,并发布这个favicon,发布为/favicon.ico

   1 
   2 def index(wkp):
   3     wkp.set_content_type("text/html")
   4     result="<html><body>"
   5     result+='<a href="/hello">Hello World</a><br><br>'
   6     result+='</body></html>'
   7     return result
   8 
   9 def helloworld(wkp):
  10     return 'Hello World'
  11 
  12 import os.path
  13 def images(wkp,*args):
  14     return wkp.StaticDirectory(os.path.join(os.path.dirname(__file__),'images'),*args)
  15 
  16 def favicon(wkp):
  17     return wkp.StaticFile(os.path.join(os.path.dirname(__file__),'favicon.ico'),mime_type='image/x-icon')
  18 
  19 def root():
  20     result=index
  21     result.hello=helloworld
  22     result.images=images
  23     result.favicon_ico=favicon   #使用扩展名为.ico
  24     return result

4.3.4. 动态改态页面属性

增加一个函数包装函数_charsetGB2312,将index改为GB2312发布

   1 
   2 def _charsetGB2312(fn):
   3     '''函数包装函数,将页面设为gb2312'''
   4     def _init(dgn,*args):
   5         dgn.set_charset('gb2312')
   6         return fn(dgn,*args)
   7     return _init
   8 
   9 #---------------------
  10 def index(wkp):
  11     wkp.set_content_type("text/html")
  12     result="<html><body>"
  13     result+='<a href="/hello">Hello World</a><br><br>'
  14     result+='</body></html>'
  15     return result
  16 index=_charsetGB2312(index)
  17 
  18 def helloworld(wkp):
  19     return 'Hello World'
  20 
  21 import os.path
  22 def images(wkp,*args):
  23     return wkp.StaticDirectory(os.path.join(os.path.dirname(__file__),'images'),*args)
  24 
  25 def favicon(wkp):
  26     return wkp.StaticFile(os.path.join(os.path.dirname(__file__),'favicon.ico'),mime_type='image/x-icon')
  27 
  28 def root():
  29     result=index
  30     result.hello=helloworld
  31     result.images=images
  32     result.favicon_ico=favicon   #使用扩展名为.ico
  33     return result

4.3.5. 使用基本认证,设置页面访问权限

增加一个权限函数_baseAuth,将protect页面设置访问权限

   1 
   2 def _charsetGB2312(fn):
   3     '''函数包装函数,将页面设为gb2312'''
   4     def _init(dgn,*args):
   5         dgn.set_charset('gb2312')
   6         return fn(dgn,*args)
   7     return _init
   8 
   9 def _baseAuth(dgn,func,realm='Protected'):
  10     t=dgn.get_baseauth()
  11     if t is not  None and t==('1','1'):
  12         result=None
  13     else:
  14         dgn.set_status(401)
  15         dgn.set_charset('gb2312')
  16         dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
  17         result='密码虽然弱智,你还是访问不了'
  18     return result
  19 #---------------------
  20 def index(wkp):
  21     wkp.set_content_type("text/html")
  22     result="<html><body>"
  23     result+='<a href="/hello">Hello World</a><br><br>'
  24     result+='<a href="/protect">你能进来算你牛</a><br><br>'
  25     result+='<a href="http://1:[email protected]:8080/protect">都告诉密码了还能进不来</a><br><br>'
  26     result+='</body></html>'
  27     return result
  28 index=_charsetGB2312(index)
  29 
  30 def helloworld(wkp):
  31     return 'Hello World'
  32 
  33 def protect(wkp):
  34     return '哈哈,你进来了'
  35 protect._q_access=_baseAuth
  36 
  37 import os.path
  38 def images(wkp,*args):
  39     return wkp.StaticDirectory(os.path.join(os.path.dirname(__file__),'images'),*args)
  40 
  41 def favicon(wkp):
  42     return wkp.StaticFile(os.path.join(os.path.dirname(__file__),'favicon.ico'),mime_type='image/x-icon')
  43 
  44 def root():
  45     result=index
  46     result.hello=helloworld
  47     result.images=images
  48     result.protect=protect
  49     result.favicon_ico=favicon   #使用扩展名为.ico
  50     return result

4.3.6. 动态构建和发布一个图形

增加msn函数,从远程获得图形文件并以/msn.jpg发布出去

   1 
   2 def _charsetGB2312(fn):
   3     '''函数包装函数,将页面设为gb2312'''
   4     def _init(dgn,*args):
   5         dgn.set_charset('gb2312')
   6         return fn(dgn,*args)
   7     return _init
   8 
   9 def _baseAuth(dgn,func,realm='Protected'):
  10     t=dgn.get_baseauth()
  11     if t is not  None and t==('1','1'):
  12         result=None
  13     else:
  14         dgn.set_status(401)
  15         dgn.set_charset('gb2312')
  16         dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
  17         result='密码虽然弱智,你还是访问不了'
  18     return result
  19 #---------------------
  20 def index(wkp):
  21     wkp.set_content_type("text/html")
  22     result="<html><body>"
  23     result+='<a href="/hello">Hello World</a><br><br>'
  24     result+='<a href="/protect">你能进来算你牛</a><br><br>'
  25     result+='<a href="http://1:[email protected]:8080/protect">都告诉密码了还能进不来</a><br><br>'
  26     result+='<a href="/msn.jpg">这是俺的msn,zq老大做的,频繁访问他的网站会受不了</a><br><br>'
  27     result+='</body></html>'
  28     return result
  29 index=_charsetGB2312(index)
  30 
  31 def helloworld(wkp):
  32     return 'Hello World'
  33 
  34 def protect(wkp):
  35     return '哈哈,你进来了'
  36 protect._q_access=_baseAuth
  37 
  38 import os.path
  39 def images(wkp,*args):
  40     return wkp.StaticDirectory(os.path.join(os.path.dirname(__file__),'images'),*args)
  41 
  42 def favicon(wkp):
  43     return wkp.StaticFile(os.path.join(os.path.dirname(__file__),'favicon.ico'),mime_type='image/x-icon')
  44 
  45 def msn(wkp):
  46     wkp.set_content_type('image/jpeg')
  47     result=wkp.web.urlget(
  48         'http://wiki.woodpecker.org.cn/moin/WukooPy/TouchAuthor?action=AttachFile&do=get&target=hui5774hot.png'
  49         )
  50     return result
  51 
  52 def root():
  53     result=index
  54     result.hello=helloworld
  55     result.images=images
  56     result.protect=protect
  57     result.favicon_ico=favicon   #使用扩展名为.ico
  58     result.msn_jpg=msn
  59     return result

4.3.7. URL MAP处理

增加urlmap函数,用来作为缺省urlmap的处理函数,很简单,有*args参数即可

   1 
   2 def _charsetGB2312(fn):
   3     '''函数包装函数,将页面设为gb2312'''
   4     def _init(dgn,*args):
   5         dgn.set_charset('gb2312')
   6         return fn(dgn,*args)
   7     return _init
   8 
   9 def _baseAuth(dgn,func,realm='Protected'):
  10     t=dgn.get_baseauth()
  11     if t is not  None and t==('1','1'):
  12         result=None
  13     else:
  14         dgn.set_status(401)
  15         dgn.set_charset('gb2312')
  16         dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
  17         result='密码虽然弱智,你还是访问不了'
  18     return result
  19 
  20 
  21 #---------------------
  22 def index(wkp):
  23     wkp.set_content_type("text/html")
  24     result="<html><body>"
  25     result+='<a href="/hello">Hello World</a><br><br>'
  26     result+='<a href="/protect">你能进来算你牛</a><br><br>'
  27     result+='<a href="http://1:[email protected]:8080/protect">都告诉密码了还能进不来</a><br><br>'
  28     result+='<a href="/msn.jpg">这是俺的msn,zq老大做的,频繁访问他的网站会受不了</a><br><br>'
  29     result+='<a href="/urlmap/1/1/m/1.html">什么样的垃级,俺都照吃不误</a><br><br>'
  30     result+='</body></html>'
  31     return result
  32 index=_charsetGB2312(index)
  33 
  34 def helloworld(wkp):
  35     return 'Hello World'
  36 
  37 def protect(wkp):
  38     return '哈哈,你进来了'
  39 protect._q_access=_baseAuth
  40 
  41 def urlmap(wkp,*args):
  42     return '哈'+str(args)+',原来俺吃的是这个'
  43 
  44 
  45 import os.path
  46 def images(wkp,*args):
  47     return wkp.StaticDirectory(os.path.join(os.path.dirname(__file__),'images'),*args)
  48 
  49 def favicon(wkp):
  50     return wkp.StaticFile(os.path.join(os.path.dirname(__file__),'favicon.ico'),mime_type='image/x-icon')
  51 
  52 def msn(wkp):
  53     wkp.set_content_type('image/jpeg')
  54     result=wkp.web.urlget(
  55         'http://wiki.woodpecker.org.cn/moin/WukooPy/TouchAuthor?action=AttachFile&do=get&target=hui5774hot.png'
  56         )
  57     return result
  58 
  59 def root():
  60     result=index
  61     result.hello=helloworld
  62     result.images=images
  63     result.protect=protect
  64     result.urlmap=urlmap
  65     result.favicon_ico=favicon   #使用扩展名为.ico
  66     result.msn_jpg=msn
  67     return result

4.3.8. 使用页面变量,设置页面访问权限

增加一个权限函数_formAuth,将formprotect页面设置访问权限

   1 
   2 def _charsetGB2312(fn):
   3     '''函数包装函数,将页面设为gb2312'''
   4     def _init(dgn,*args):
   5         dgn.set_charset('gb2312')
   6         return fn(dgn,*args)
   7     return _init
   8 
   9 def _baseAuth(dgn,func,realm='Protected'):
  10     t=dgn.get_baseauth()
  11     if t is not  None and t==('1','1'):
  12         result=None
  13     else:
  14         dgn.set_status(401)
  15         dgn.set_charset('gb2312')
  16         dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
  17         result='密码虽然弱智,你还是访问不了'
  18     return result
  19 
  20 def _formAuth(dgn,func):
  21     if dgn.get_form('user')=='王二' and dgn.get_form('password')=='麻子'
  22         result=None
  23     else:
  24         dgn.set_charset('gb2312')
  25         result='只有王二有权进,去求他吧'
  26     return result
  27 
  28 #---------------------
  29 def index(wkp):
  30     wkp.set_content_type("text/html")
  31     result="<html><body>"
  32     result+='<a href="/hello">Hello World</a><br><br>'
  33     result+='<a href="/protect">你能进来算你牛</a><br><br>'
  34     result+='<a href="http://1:[email protected]:8080/protect">都告诉密码了还能进不来</a><br><br>'
  35     result+='<a href="/msn.jpg">这是俺的msn,zq老大做的,频繁访问他的网站会受不了</a><br><br>'
  36     result+='<a href="/urlmap/1/1/m/1.html">什么样的垃级,俺都照吃不误</a><br><br>'
  37     result+='<a href="/form_protect">试试能不能进</a><br><br>'
  38     result+='<a href="/form_protect?user=王二&password=麻子">只有王二才能进</a><br><br>'
  39     result+='</body></html>'
  40     return result
  41 index=_charsetGB2312(index)
  42 
  43 def helloworld(wkp):
  44     return 'Hello World'
  45 
  46 def protect(wkp):
  47     return '哈哈,你进来了'
  48 protect._q_access=_baseAuth
  49 protect=_charsetGB2312(protect)
  50 
  51 def form_protect(wkp):
  52     return '哈哈,我王二麻子进来了'
  53 form_protect._q_access=_formAuth
  54 form_protect=_charsetGB2312(form_protect)
  55 
  56 
  57 def urlmap(wkp,*args):
  58     return '哈'+str(args)+',原来俺吃的是这个'
  59 
  60 
  61 import os.path
  62 def images(wkp,*args):
  63     return wkp.StaticDirectory(os.path.join(os.path.dirname(__file__),'images'),*args)
  64 
  65 def favicon(wkp):
  66     return wkp.StaticFile(os.path.join(os.path.dirname(__file__),'favicon.ico'),mime_type='image/x-icon')
  67 
  68 def msn(wkp):
  69     wkp.set_content_type('image/jpeg')
  70     result=wkp.web.urlget(
  71         'http://wiki.woodpecker.org.cn/moin/WukooPy/TouchAuthor?action=AttachFile&do=get&target=hui5774hot.png'
  72         )
  73     return result
  74 
  75 def root():
  76     result=index
  77     result.hello=helloworld
  78     result.images=images
  79     result.protect=protect
  80     result.form_protect=form_protect
  81     result.urlmap=urlmap
  82     result.favicon_ico=favicon   #使用扩展名为.ico
  83     result.msn_jpg=msn
  84     return result

暂时到这:)

5. 反馈

有什么体验?

last edited 2005-07-17 15:11:04 by LiHui