Size: 485
Comment:
|
Size: 16146
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
## page was renamed from DragonPy | |
Line 3: | Line 4: |
DragonPy -- lihui 混合 Karrigell 和 Quixote 创造的 一个应用轮子 | DragonPy -- 又一个新的webapp架构,它是面向python程序员的,包含了 Karrigell 和 Quixote,大家一起来造这个轮子(LiHui)。 |
Line 6: | Line 7: |
* 哈哈哈!别直接龙了,叫 蟒龙是也乎? |
* 龙的名字俺也不满意,太俗太没创造性,前两天陪女儿看挪吒,这个名字也不错,孙猴子也成,就是不知道英文怎么写? * '''WukooPy''' -- Web usage Keep object of Python '''悟空系统''' -- 哈哈哈!保持对象化的 Web应用! 如何? |
Line 10: | Line 11: |
= 文章大标 = ''简述'' == 章标题1 == === 小节标题1 === |
= 现有 Web Application 点评 = ''这么多Web Application,到底用什么好啊'' 俺以前一直用zope,后来感觉是越来越差,原因与Quixote和Cherrypy的作者差不多,网上有他们的文章,俺这里就不说了。后来找啊找啊,前前后后重点试过Quixote、Cherrypy、Karrigell,感觉都还不错,各有各的特点: 1. Cherrypy: * 将对象机制引入web开发,完全的面向对象发布机制,底层数据、api函数使用cpg集中管理。缺点是底层api调用代码老在变,天啊,俺真的受不了,以前做了一个cherrypy小应用,前前后后重构了10来遍,每次只要用svn更新一下cherrypy代码,就得改一次俺的应用代码。俺就搞不明白,比如一个调用session,就用cpg.sessions就成了吧,为什么一会儿这个样式,一会儿另外个样式。 1. Quixote: * 不错,发布方式多,代码结构好,质量最佳,基本上没什么BUG。只是语法调用有点怪异,适应了半天,最后还是不适应。 1. Karrigell: * 最易用的系统,入门真简便,良好的debug框架,动态载入。只是代码结构不好,做个应用就得将Karrigell移过去。再有url不支持定制,太有局限性,比如我动态生成一个js文件,总不能扩展名是个py,hip...吧,而且作者象俺有点不务正业,不老老实实做好框架,却去设计什么dbstrange,还做了一堆没用的半成品,唉,有这功夫把自留地种好啊。感觉做做原型、快速开发还不错。 * PS:这两天谈的比较多的是ruby的web架构,不太感冒,这东东也就是让java程序员见识一下:哦,这么这么简单啊,其实不用编码的框架是不存在的,功能实现与编码量本身就是一对对头。 = 需求 = 见识了这么多框架,俺理想的架构长得模样也自然产生了(主要性能): 1. 具有多多的发布接口,象Quixote,就是俺以前提过的万能接口,接口越多,余地越大啊。 1. 底层API与数据的集中管理与调用。比如dgn.get_request(),dgn.get_reponse(),这里的特性是Quixote与Cherrypy的结合和升华。 1. 简便的发布方式。考虑web前端部份的python代码是最易变的,尽可能简单,易读。利用所有都是对象的性能,选择函数发布。 1. 支持url定制,易url的default性能。这个需求好象现在很重要,总比使用apache的mod_write好多了。 1. 语法规则分离出来,用户可使用内置规则,也可定制自已的。内置:quixote_publish1,quixote_publish2,karrigell和函数发布 1. 方便的角色、权限定义和管理 == 前题 == 系统使用Karrigell和Quixote代码,对于这两个系统的补丁修改,一律使用动态修正方式偷梁换柱,不改变系统的任何代码,以满足原系统的升级和版权等要求。 === 一个Hello world模块 === 还是用一个简便例子说明: |
Line 17: | Line 50: |
Python code | #--系统函数-- def _charsetGB2312(fn): '''函数包装函数,将页面设为gb2312''' def _init(dgn,*args): dgn.set_charset('gb2312') return fn(dgn,*args) return _init def _baseAuth(dgn,func,realm='Protected'): '''权限函数,只有我能上''' t=dgn.get_baseauth() if t is not None and t==('lihui','password'): result=None else: dgn.set_status(401) dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm) result=True return result #--web前端-- def helloworld1(dgn): '''发布一个函数,dgn为数据api集中调用入口,为第一个参数,dgn使用的singlon模式,除系统内置定义外,可自行加入定义,比如dbpool,可定义为dgn.db.dbpool ''' return 'helloworld1' def helloworld2(dgn): '''发布一个函数,加个权限''' return 'helloworld1' helloworld2._q_access=_baseAuth def helloworld3(dgn): '''发布一个函数,gb2312发布''' return 'helloworld1' helloworld3=_charsetGB2312(helloworld3) def helloworld4(dgn,*args): '''有args的支持default,比如这个函数url为http:/***/p,这个http:/***/p/1/1/1.html,则转为args=['1','1','1.html']''' return 'helloworld1' def helloworld5(dgn): '''发布一个图形文件''' return 图文件 def images(dgn,*args): '''发布images子目录,静态目录''' return dgn.StaticDirectory(os.path.join(dgn.get_config().path_top,'images'),*args) #--发布函数 def root(): result=helloworld1 result.images=images result.hl2=helloworld2 result.hl3=helloworld3 result.hl4=helloworld4 result.hl5_jpg=helloworld5 #扩展名为jpg return result |
Line 20: | Line 108: |
==== 次节标题1 ==== xxx == 章标题2 == === 小节标题2 === |
=== 网站架构组合 === 原来一直用的是zope的zpt和dtml,cherrytemplate是两者的混合,使用很方便,模块就选它拉 sqlobject真不错,实行了关系数据库+逻辑+对象处理,zodb和存储过程俺再也不用拉。 俺的最佳组合就是dragon+cherrytemplate+sqlobject === sqlobject一段代码 === 谈sqlobject的好象不是很多,发一段俺的代码,见识一下sqlobject的必杀绝技 |
Line 27: | Line 121: |
其它 代码引用 |
from sqlobject import * __connection__='mysql://root:123@localhost:3306/bm' class UserMark(SQLObject): class sqlmeta(sqlmeta): table = "user_mark" idName = "id" #style = Style() defaultOrder = 'id' _cacheValues = True username=StringCol(alternateID=True,length=30,notNone=True,default='') userpasswd=StringCol(length=20,notNone=True,default='') userdisplay=StringCol(length=60,notNone=True,default='') #---------------------------------- ip=StringCol(length=40,notNone=True,default='') #---------------------------------- sex=StringCol(length=2,notNone=True,default='') #本地库表 email=StringCol(length=60,notNone=True,default='') #本地库表 qq=StringCol(length=20,notNone=True,default='') #本地库表 web=StringCol(length=60,notNone=True,default='') #记录ID msn=StringCol(length=60,notNone=True,default='') #记录ID timeCreate=DateTimeCol(notNone=True,default=now) countAccess=IntCol(notNone=True,default=0) countReply=IntCol(notNone=True,default=0) countSubmit=IntCol(notNone=True,default=0) userType=IntCol(notNone=True,default=0) userMoney=IntCol(notNone=True,default=0) #---------------------------------- def addUser(cls,user,passwd1,passwd2,email): errorchar=['"',"'",'=','?','.','*','&','%','<','>',' ','!'] try: if passwd1<>passwd2: raise 'Password is not same' for v in errorchar: if v in user or v in passwd1: raise 'Error username or password' result=cls(username=user,userpasswd=passwd1,email=email) except: result=None return result addUser=classmethod(addUser) #---------------------------------- def checkUser(cls,user,passwd): try: t=cls.byUsername(user) if t.userpasswd==passwd: result=t else: result=None except: result=None return result checkUser=classmethod(checkUser) #----------------init db-------------- DBDict={ 'UserMark':'UserMark', } def proxyDB(tname): return globals()[tname] def clearDB(): for k,v in DBDict.items(): proxyDB(v).dropTable(True) proxyDB(v).createTable() if __name__=='__main__': clearDB() |
Line 31: | Line 192: |
==== 次节标题2 ==== yyy |
=== Karrigell 的 quixte 发布 === 先发一部分: {{{ #!python import urlparse import sys import os.path import os import time from cStringIO import StringIO import posixpath import urllib import string from quixote.publish import Publisher as _Publisher import quixote.publish from quixote.http_response import HTTPResponse as _HTTPResponse from quixote.util import FileStream from quixote.config import Config from quixote.http_response import HTTPResponse from quixote.logger import DefaultLogger #------------------------------------------------------ my_k_config=dict( initFile='Karrigell.ini', PORT=8080, protectedDirs=['D:\\pythonMy\\KarrigellLib\\admin'], alias={'doc':'D:\\pythonMy\\KarrigellLib/doc','base':'D:\\pythonMy\\KarrigellLib'}, globalScriptsList=['D:\\pythonMy\\KarrigellLib/myScript1.py','D:\\pythonMy\\KarrigellLib/myScript.py'], globalScripts=['myScript1','myScript'], extensions_map={'.gif':'image/gif','.jpg':'image/jpeg','.py':'text/html','.nwc':'application/nwc','.html':'text/html','.htm':'text/html'}, debug=1, base='', persistentSession=False, silent=False, language='', ignore=['/favicon.ico'], serverDir=r'D:\pythonMy\KarrigellLib', #rootDir=r'D:\pythonMy\KarrigellLib', extensions=['gif','html','nwc','htm','py','jpg'], gzip=False, ) import KarrigellLib m=os.path.dirname(os.path.abspath(KarrigellLib.__file__)) if m not in sys.path: sys.path.append(m) msave=sys.argv sys.argv=[] import k_config sys.argv=msave for k,v in my_k_config.items(): setattr(k_config,k,v) k_config.serverDir=os.path.dirname(os.path.abspath(k_config.__file__)) if not k_config.alias.has_key('debugger'): k_config.alias['debugger']=os.path.join(k_config.serverDir,'debugger') if not k_config.alias.has_key('demo'): k_config.alias['demo']=os.path.join(k_config.serverDir,'demo') if not k_config.alias.has_key('fckeditor'): k_config.alias['fckeditor']=os.path.join(k_config.serverDir,'fckeditor') if not k_config.alias.has_key('doc'): k_config.alias['doc']=os.path.join(k_config.serverDir,'doc') if not k_config.alias.has_key('instant_site'): k_config.alias['instant_site']=os.path.join(k_config.serverDir,'instant_site') if not k_config.alias.has_key('components'): k_config.alias['components']=os.path.join(k_config.serverDir,'components') if not k_config.alias.has_key('listDirectory.pih'): k_config.alias['listDirectory.pih']=os.path.join(k_config.serverDir,'listDirectory.pih') if not k_config.alias.has_key('karrigell.css'): k_config.alias['karrigell.css']=os.path.join(k_config.serverDir,'karrigell.css') if not k_config.alias.has_key('admin'): k_config.alias['admin']=os.path.join(k_config.serverDir,'admin') paths=[] paths.append(os.path.join(k_config.serverDir,'databases')) for v in paths: if v not in sys.path: sys.path.append(v) import SimpleHTTPServer, KarrigellRequestHandler, URLResolution,Template def translate_path(path): """Overrides SimpleHTTPServer's translate_path to handle aliases Returns the path to a file name""" path = posixpath.normpath(urllib.unquote(path)) path = path[len(k_config.base):] # remove base beginning the url words = path.split('/') words = filter(None, words) # for ks scripts, path ends at the word ending with "ks" w1,w2=[],[] for i,word in enumerate(words): w1.append(word) if word.lower().endswith(".ks"): w2=words[i+1:] break words=w1 m=words[-1] if m.endswith('.html') and '_' in m: words[-1]=m.split('_',1)[0] if words and words[0] in k_config.alias.keys(): path=posixpath.normpath(k_config.alias[words[0]]) path=os.path.join(k_config.alias[words[0]],string.join(words[1:],"/")) else: path=os.path.join(k_config.rootDir,string.join(words,"/")) return path def list_directory(path,dirName): script = Template.getScript(os.path.join(k_config.serverDir,'listDirectory.pih')) return script.render({'path':path,'dirName':dirName}).value setattr(Template,'list_directory',list_directory) setattr(URLResolution,'translate_path',translate_path) class QuixoteHandler(KarrigellRequestHandler.KarrigellRequestHandler,SimpleHTTPServer.SimpleHTTPRequestHandler): def __init__(self, request): self.response_status=(200,'OK') self.response_headers=[] self.request = request self.wfile = StringIO() fs=request.get_fields() self.headers=dict(request.environ.items()) for item in self.headers.keys(): if item.startswith("HTTP_"): hd=item[5:] self.headers[hd.lower()]=self.headers[item] self.command = self.headers['REQUEST_METHOD'] self.body = {} if self.command == 'POST': self.body=fs self.client_address=(self.headers["REMOTE_ADDR"],0) self.path=self.path_without_qs=self.headers["PATH_INFO"] if self.headers.has_key("QUERY_STRING"): self.path+="?"+self.headers["QUERY_STRING"] self.request_version=self.headers["SERVER_PROTOCOL"] self.requestline="%s %s %s" %(self.headers["SERVER_PROTOCOL"], self.headers["REQUEST_METHOD"],self.path) try: self.handle_data() finally: sys.exc_traceback = None # Help garbage collection def send_response(self,status, reason=None): self.response_status=(status, reason) def send_header(self,key,value): self.response_headers.append((key,value)) def end_headers(self): pass class Publisher(_Publisher): def __init__(self, root_directory, logger=None, session_manager=None,config=None, **kwargs): if config is None: self.config = Config(**kwargs) else: if kwargs: raise ValueError("cannot provide both 'config' object and" " config arguments") self.config = config for k in my_k_config.keys(): if hasattr(self.config,k.lower()): setattr(k_config,k,getattr(self.config,k.lower())) if logger is None: self.logger = DefaultLogger(error_log=self.config.error_log, access_log=self.config.access_log, error_email=self.config.error_email) else: self.logger = logger if session_manager is not None: self.session_manager = session_manager else: from quixote.session import NullSessionManager self.session_manager = NullSessionManager() if quixote.publish._publisher is not None: raise RuntimeError, "only one instance of Publisher allowed" quixote.publish._publisher = self self.root_directory = root_directory if hasattr(root_directory,'__file__'): k_config.rootDir=os.path.dirname(os.path.abspath(root_directory.__file__)) self._request = None def try_publish(self, request): self.start_request() path = request.get_environ('PATH_INFO', '') assert path[:1] == '/' # split path into components path = path[1:].split('/') t=QuixoteHandler(request) request.response.set_status(t.response_status[0]) for v in t.response_headers: request.response.set_header(v[0],v[1]) output = t.wfile.getvalue() # The callable ran OK, commit any changes to the session self.finish_successful_request() return output }}} |
DragonPy -- 又一个新的webapp架构,它是面向python程序员的,包含了 Karrigell 和 Quixote,大家一起来造这个轮子(LiHui)。
- 龙的名字俺也不满意,太俗太没创造性,前两天陪女儿看挪吒,这个名字也不错,孙猴子也成,就是不知道英文怎么写?
WukooPy -- Web usage Keep object of Python 悟空系统 -- 哈哈哈!保持对象化的 Web应用! 如何?
::-- ZoomQuiet [DateTime(2005-07-13T02:41:38Z)] TableOfContents
现有 Web Application 点评
这么多Web Application,到底用什么好啊
俺以前一直用zope,后来感觉是越来越差,原因与Quixote和Cherrypy的作者差不多,网上有他们的文章,俺这里就不说了。后来找啊找啊,前前后后重点试过Quixote、Cherrypy、Karrigell,感觉都还不错,各有各的特点:
- Cherrypy:
- 将对象机制引入web开发,完全的面向对象发布机制,底层数据、api函数使用cpg集中管理。缺点是底层api调用代码老在变,天啊,俺真的受不了,以前做了一个cherrypy小应用,前前后后重构了10来遍,每次只要用svn更新一下cherrypy代码,就得改一次俺的应用代码。俺就搞不明白,比如一个调用session,就用cpg.sessions就成了吧,为什么一会儿这个样式,一会儿另外个样式。
- Quixote:
- 不错,发布方式多,代码结构好,质量最佳,基本上没什么BUG。只是语法调用有点怪异,适应了半天,最后还是不适应。
- Karrigell:
- 最易用的系统,入门真简便,良好的debug框架,动态载入。只是代码结构不好,做个应用就得将Karrigell移过去。再有url不支持定制,太有局限性,比如我动态生成一个js文件,总不能扩展名是个py,hip...吧,而且作者象俺有点不务正业,不老老实实做好框架,却去设计什么dbstrange,还做了一堆没用的半成品,唉,有这功夫把自留地种好啊。感觉做做原型、快速开发还不错。
- PS:这两天谈的比较多的是ruby的web架构,不太感冒,这东东也就是让java程序员见识一下:哦,这么这么简单啊,其实不用编码的框架是不存在的,功能实现与编码量本身就是一对对头。
需求
见识了这么多框架,俺理想的架构长得模样也自然产生了(主要性能):
- 具有多多的发布接口,象Quixote,就是俺以前提过的万能接口,接口越多,余地越大啊。
- 底层API与数据的集中管理与调用。比如dgn.get_request(),dgn.get_reponse(),这里的特性是Quixote与Cherrypy的结合和升华。
- 简便的发布方式。考虑web前端部份的python代码是最易变的,尽可能简单,易读。利用所有都是对象的性能,选择函数发布。
- 支持url定制,易url的default性能。这个需求好象现在很重要,总比使用apache的mod_write好多了。
- 语法规则分离出来,用户可使用内置规则,也可定制自已的。内置:quixote_publish1,quixote_publish2,karrigell和函数发布
- 方便的角色、权限定义和管理
前题
系统使用Karrigell和Quixote代码,对于这两个系统的补丁修改,一律使用动态修正方式偷梁换柱,不改变系统的任何代码,以满足原系统的升级和版权等要求。
一个Hello world模块
还是用一个简便例子说明:
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 def _baseAuth(dgn,func,realm='Protected'):
9 '''权限函数,只有我能上'''
10 t=dgn.get_baseauth()
11 if t is not None and t==('lihui','password'):
12 result=None
13 else:
14 dgn.set_status(401)
15 dgn.set_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
16 result=True
17 return result
18 #--web前端--
19 def helloworld1(dgn):
20 '''发布一个函数,dgn为数据api集中调用入口,为第一个参数,dgn使用的singlon模式,除系统内置定义外,可自行加入定义,比如dbpool,可定义为dgn.db.dbpool
21 '''
22 return 'helloworld1'
23
24 def helloworld2(dgn):
25 '''发布一个函数,加个权限'''
26 return 'helloworld1'
27 helloworld2._q_access=_baseAuth
28
29 def helloworld3(dgn):
30 '''发布一个函数,gb2312发布'''
31 return 'helloworld1'
32 helloworld3=_charsetGB2312(helloworld3)
33
34 def helloworld4(dgn,*args):
35 '''有args的支持default,比如这个函数url为http:/***/p,这个http:/***/p/1/1/1.html,则转为args=['1','1','1.html']'''
36 return 'helloworld1'
37
38 def helloworld5(dgn):
39 '''发布一个图形文件'''
40 return 图文件
41
42
43 def images(dgn,*args):
44 '''发布images子目录,静态目录'''
45 return dgn.StaticDirectory(os.path.join(dgn.get_config().path_top,'images'),*args)
46
47 #--发布函数
48 def root():
49 result=helloworld1
50 result.images=images
51 result.hl2=helloworld2
52 result.hl3=helloworld3
53 result.hl4=helloworld4
54 result.hl5_jpg=helloworld5 #扩展名为jpg
55 return result
网站架构组合
原来一直用的是zope的zpt和dtml,cherrytemplate是两者的混合,使用很方便,模块就选它拉
sqlobject真不错,实行了关系数据库+逻辑+对象处理,zodb和存储过程俺再也不用拉。
俺的最佳组合就是dragon+cherrytemplate+sqlobject
sqlobject一段代码
谈sqlobject的好象不是很多,发一段俺的代码,见识一下sqlobject的必杀绝技
from sqlobject import * __connection__='mysql://root:123@localhost:3306/bm' class UserMark(SQLObject): class sqlmeta(sqlmeta): table = "user_mark" idName = "id" #style = Style() defaultOrder = 'id' _cacheValues = True username=StringCol(alternateID=True,length=30,notNone=True,default='') userpasswd=StringCol(length=20,notNone=True,default='') userdisplay=StringCol(length=60,notNone=True,default='') #---------------------------------- ip=StringCol(length=40,notNone=True,default='') #---------------------------------- sex=StringCol(length=2,notNone=True,default='') #本地库表 email=StringCol(length=60,notNone=True,default='') #本地库表 qq=StringCol(length=20,notNone=True,default='') #本地库表 web=StringCol(length=60,notNone=True,default='') #记录ID msn=StringCol(length=60,notNone=True,default='') #记录ID timeCreate=DateTimeCol(notNone=True,default=now) countAccess=IntCol(notNone=True,default=0) countReply=IntCol(notNone=True,default=0) countSubmit=IntCol(notNone=True,default=0) userType=IntCol(notNone=True,default=0) userMoney=IntCol(notNone=True,default=0) #---------------------------------- def addUser(cls,user,passwd1,passwd2,email): errorchar=['"',"'",'=','?','.','*','&','%','<','>',' ','!'] try: if passwd1<>passwd2: raise 'Password is not same' for v in errorchar: if v in user or v in passwd1: raise 'Error username or password' result=cls(username=user,userpasswd=passwd1,email=email) except: result=None return result addUser=classmethod(addUser) #---------------------------------- def checkUser(cls,user,passwd): try: t=cls.byUsername(user) if t.userpasswd==passwd: result=t else: result=None except: result=None return result checkUser=classmethod(checkUser) #----------------init db-------------- DBDict={ 'UserMark':'UserMark', } def proxyDB(tname): return globals()[tname] def clearDB(): for k,v in DBDict.items(): proxyDB(v).dropTable(True) proxyDB(v).createTable() if __name__=='__main__': clearDB()
Karrigell 的 quixte 发布
先发一部分:
1 import urlparse
2 import sys
3 import os.path
4 import os
5 import time
6 from cStringIO import StringIO
7 import posixpath
8 import urllib
9 import string
10
11 from quixote.publish import Publisher as _Publisher
12 import quixote.publish
13 from quixote.http_response import HTTPResponse as _HTTPResponse
14 from quixote.util import FileStream
15 from quixote.config import Config
16 from quixote.http_response import HTTPResponse
17 from quixote.logger import DefaultLogger
18
19
20
21 #------------------------------------------------------
22
23 my_k_config=dict(
24 initFile='Karrigell.ini',
25 PORT=8080,
26 protectedDirs=['D:\\pythonMy\\KarrigellLib\\admin'],
27 alias={'doc':'D:\\pythonMy\\KarrigellLib/doc','base':'D:\\pythonMy\\KarrigellLib'},
28 globalScriptsList=['D:\\pythonMy\\KarrigellLib/myScript1.py','D:\\pythonMy\\KarrigellLib/myScript.py'],
29 globalScripts=['myScript1','myScript'],
30 extensions_map={'.gif':'image/gif','.jpg':'image/jpeg','.py':'text/html','.nwc':'application/nwc','.html':'text/html','.htm':'text/html'},
31 debug=1,
32 base='',
33 persistentSession=False,
34 silent=False,
35 language='',
36 ignore=['/favicon.ico'],
37 serverDir=r'D:\pythonMy\KarrigellLib',
38 #rootDir=r'D:\pythonMy\KarrigellLib',
39 extensions=['gif','html','nwc','htm','py','jpg'],
40 gzip=False,
41 )
42
43
44 import KarrigellLib
45 m=os.path.dirname(os.path.abspath(KarrigellLib.__file__))
46 if m not in sys.path:
47 sys.path.append(m)
48
49 msave=sys.argv
50 sys.argv=[]
51 import k_config
52 sys.argv=msave
53
54 for k,v in my_k_config.items():
55 setattr(k_config,k,v)
56 k_config.serverDir=os.path.dirname(os.path.abspath(k_config.__file__))
57 if not k_config.alias.has_key('debugger'):
58 k_config.alias['debugger']=os.path.join(k_config.serverDir,'debugger')
59 if not k_config.alias.has_key('demo'):
60 k_config.alias['demo']=os.path.join(k_config.serverDir,'demo')
61 if not k_config.alias.has_key('fckeditor'):
62 k_config.alias['fckeditor']=os.path.join(k_config.serverDir,'fckeditor')
63 if not k_config.alias.has_key('doc'):
64 k_config.alias['doc']=os.path.join(k_config.serverDir,'doc')
65 if not k_config.alias.has_key('instant_site'):
66 k_config.alias['instant_site']=os.path.join(k_config.serverDir,'instant_site')
67 if not k_config.alias.has_key('components'):
68 k_config.alias['components']=os.path.join(k_config.serverDir,'components')
69 if not k_config.alias.has_key('listDirectory.pih'):
70 k_config.alias['listDirectory.pih']=os.path.join(k_config.serverDir,'listDirectory.pih')
71 if not k_config.alias.has_key('karrigell.css'):
72 k_config.alias['karrigell.css']=os.path.join(k_config.serverDir,'karrigell.css')
73 if not k_config.alias.has_key('admin'):
74 k_config.alias['admin']=os.path.join(k_config.serverDir,'admin')
75
76
77
78 paths=[]
79 paths.append(os.path.join(k_config.serverDir,'databases'))
80 for v in paths:
81 if v not in sys.path:
82 sys.path.append(v)
83
84
85 import SimpleHTTPServer, KarrigellRequestHandler, URLResolution,Template
86
87
88
89 def translate_path(path):
90 """Overrides SimpleHTTPServer's translate_path to handle aliases
91 Returns the path to a file name"""
92 path = posixpath.normpath(urllib.unquote(path))
93 path = path[len(k_config.base):] # remove base beginning the url
94 words = path.split('/')
95 words = filter(None, words)
96 # for ks scripts, path ends at the word ending with "ks"
97 w1,w2=[],[]
98 for i,word in enumerate(words):
99 w1.append(word)
100 if word.lower().endswith(".ks"):
101 w2=words[i+1:]
102 break
103 words=w1
104 m=words[-1]
105 if m.endswith('.html') and '_' in m:
106 words[-1]=m.split('_',1)[0]
107 if words and words[0] in k_config.alias.keys():
108 path=posixpath.normpath(k_config.alias[words[0]])
109 path=os.path.join(k_config.alias[words[0]],string.join(words[1:],"/"))
110 else:
111 path=os.path.join(k_config.rootDir,string.join(words,"/"))
112 return path
113
114 def list_directory(path,dirName):
115 script = Template.getScript(os.path.join(k_config.serverDir,'listDirectory.pih'))
116 return script.render({'path':path,'dirName':dirName}).value
117
118 setattr(Template,'list_directory',list_directory)
119 setattr(URLResolution,'translate_path',translate_path)
120
121
122
123
124 class QuixoteHandler(KarrigellRequestHandler.KarrigellRequestHandler,SimpleHTTPServer.SimpleHTTPRequestHandler):
125 def __init__(self, request):
126 self.response_status=(200,'OK')
127 self.response_headers=[]
128 self.request = request
129 self.wfile = StringIO()
130 fs=request.get_fields()
131 self.headers=dict(request.environ.items())
132 for item in self.headers.keys():
133 if item.startswith("HTTP_"):
134 hd=item[5:]
135 self.headers[hd.lower()]=self.headers[item]
136 self.command = self.headers['REQUEST_METHOD']
137 self.body = {}
138 if self.command == 'POST':
139 self.body=fs
140 self.client_address=(self.headers["REMOTE_ADDR"],0)
141 self.path=self.path_without_qs=self.headers["PATH_INFO"]
142 if self.headers.has_key("QUERY_STRING"):
143 self.path+="?"+self.headers["QUERY_STRING"]
144 self.request_version=self.headers["SERVER_PROTOCOL"]
145 self.requestline="%s %s %s" %(self.headers["SERVER_PROTOCOL"],
146 self.headers["REQUEST_METHOD"],self.path)
147 try:
148 self.handle_data()
149 finally:
150 sys.exc_traceback = None # Help garbage collection
151
152 def send_response(self,status, reason=None):
153 self.response_status=(status, reason)
154 def send_header(self,key,value):
155 self.response_headers.append((key,value))
156 def end_headers(self):
157 pass
158
159
160
161 class Publisher(_Publisher):
162
163 def __init__(self, root_directory, logger=None, session_manager=None,config=None, **kwargs):
164 if config is None:
165 self.config = Config(**kwargs)
166 else:
167 if kwargs:
168 raise ValueError("cannot provide both 'config' object and"
169 " config arguments")
170 self.config = config
171 for k in my_k_config.keys():
172 if hasattr(self.config,k.lower()):
173 setattr(k_config,k,getattr(self.config,k.lower()))
174 if logger is None:
175 self.logger = DefaultLogger(error_log=self.config.error_log,
176 access_log=self.config.access_log,
177 error_email=self.config.error_email)
178 else:
179 self.logger = logger
180 if session_manager is not None:
181 self.session_manager = session_manager
182 else:
183 from quixote.session import NullSessionManager
184 self.session_manager = NullSessionManager()
185
186 if quixote.publish._publisher is not None:
187 raise RuntimeError, "only one instance of Publisher allowed"
188 quixote.publish._publisher = self
189
190 self.root_directory = root_directory
191 if hasattr(root_directory,'__file__'):
192 k_config.rootDir=os.path.dirname(os.path.abspath(root_directory.__file__))
193 self._request = None
194
195
196 def try_publish(self, request):
197 self.start_request()
198 path = request.get_environ('PATH_INFO', '')
199 assert path[:1] == '/'
200 # split path into components
201 path = path[1:].split('/')
202 t=QuixoteHandler(request)
203 request.response.set_status(t.response_status[0])
204 for v in t.response_headers:
205 request.response.set_header(v[0],v[1])
206 output = t.wfile.getvalue()
207 # The callable ran OK, commit any changes to the session
208 self.finish_successful_request()
209 return output