2008年06月07日

很简单,但是可以领略Uliweb的风采。

阅读地址: http://uliwebproject.appspot.com/documents/hello_uliweb

其实 Uliwebproject 本身就是使用Uliweb构建的,只不过目前还没有使用到数据库的东西,文档都是使用reStructuredText+pygments动态生成的。

2008年06月04日

uliweb 现在我部署在了 GAE 上了。不过目前还没有使用数据库,而且uliweb目前还没有与任何ORM绑定。

访问地址: http://uliwebproject.appspot.com
文档还不全,我会慢慢补齐。另外会慢慢完善它。

目前uliweb的网站使用了docutils,不过你不能直接使用它。可以在:

svn co http://mymisc.googlecode.com/svn/trunk/ulipad_appspot ulipad_appspot

找到我修改的版本,同时还有pygments包。

2008年05月21日

今天zoom.quiet给项目起了一个新名字,叫uliweb,不过他宣传得很大,对于我只想先测试一些web框架方面的想法,当然如果有人愿意用是最好的。同时邀请了ygao加入了项目。目前已经有:我,zoom.quiet, ygao, 刘鑫。项目已经建立了 http://code.google.com/p/uliweb 同时建立了邮件列表 http://groups.google.com/group/uliweb。所以以后有什么问题或建议关于uliweb的,都可以在这个邮件列表中讨论。当然在python-cn邮件列表还可以讨论。如果你感兴趣欢迎随时加入。如果你对trunk版本不满意,可以随意创建自已的分支,对于可以合并到trunk中的代码我想需要小心处理。

目前正在使用uliweb来改造以前的 http://ulipad.appspot.com 项目。因为以前ulipad.appspot没有使用任何框架,模板使用的是web2py的,所以已经很接近现在的uliweb了,改动主要集中在view的替换上。工作正在进行中。

2008年05月20日

现在最新版的kuaiboo(需要从svn中获得)已经可以运行在GAE上了。不过我还没有在真正的环境下测试,只是在dev环境下试的。步骤如下:

1. 从svn中取得最新的版本

2. 使用export导出到一个目录下,可以直接导出到google的开发目录下。我的目录为:C:\Program Files\Google\google_appengine

3. 修改app.yaml和kuaoboo文件夹的名称为你想要的项目名称

4. 启动GAE的测试服务器可以开始运行。应该可以进入Hello这个测试环境中。

目前kuaiboo因为很简单,所以没有数据库的东西,你可以直接使用GAE的数据库接口。不过对于一个框架来说,数据库是一个很微妙的东西。我想在想它未必一定要做为核心组件。

不过在开始在GAE上测试时出现问题,在views导入时提示expose不存在。因为我是将其放在__builtin__中的,本来在一般的Python环境下应该是可以当成内置函数直接使用的,但是好象GAE给处理掉了,不起作用,最后还是使用了

from frameworks.SimpleFrame import expose

这种方式解决了问题。但是在非GAE环境下还是不需要导入的。不过对于在view函数中注入func_globals的方法还是没有问题。有兴趣的试一试吧。

2008年05月17日

更新记录:

1. 增加静态文件的支持。目前你只要在任何一个views文件中增加以下内容:

from frameworks.SimpleFrame import static_serve
@expose(‘/static/<regex(".*$"):filename>’)
def static(filename):
    return static_serve(request, filename)

这样一旦你的链接是/static/filename时,kuaiboo会自动查找所有app下的static文件夹,如果找到则返回这个文件。这里expose可以使用<regex("pattern"):argu_name>的形式进行正则式的匹配。注意正则式需要使用双引号引起来。这个静态文件处理支持条件返回,即如果文件没有改变,则返回304,文件将不会下传。同时支持406 Partial Content下传,可以支持分块下传。当然这些是webob已经提供的,同时参考了webob的这篇文档。因此实现已经很方便。这样在你刷新时,如果静态文件已经下传,再刷新时,会在console的日志中看到304。不错。

一旦部署到生产上,你可能会想使用web server来做静态文件的处理,没问题。只要将所有app下的static文件汇总到一起,统一由web server提供服务就可以了。这个汇总工具还没有做,以后再说,很简单。

2. 扩展werkzeug的URL Converter,扩展了一个正则式的Converter,可以从上例看到。

3. 扩展views函数的func_globals内容,现在有:

    def _prepare_env(self):
        env = {}
        env['url_for'] = url_for
        env['redirect'] = redirect
        env['url_map'] = url_map
        env['render'] = self.render
        env['config'] = config
        from werkzeug import html, xhtml
        env['html'] = html
        env['xhtml'] = xhtml
        from utils import Form
        env['Form'] = Form
        return env
       
    def get_env(self, request, response):
        env = self.env.copy()
        env['request'] = request
        env['response'] = response
        return env

以上在env中的key对应的对象都是你可以直接在views函数中使用的。

4. 将expose放到__builtin__模块中,这样在views文件中,你不需要导入这个函数,可以直接使用,就象是使用内置函数一样。可以减少不必要的导入。目前我只放入了这个。

5. 在request创建后,将appname和function绑定到request对象上,因此你可以直接使用request.appname得到views函数对应的app名字。这样当使用url_for来生成url时,一般是这样的:

url_for(‘Hello.views.index’, **kw)

但这样你的appname是写死的,这里是Hello,但如果你的app改名了怎么办,那么现在可以使用:

url_for(‘%s.views.index’ % request.appname, **kw)

现在你在Hello App中的index.html可以看到这种用法。

6. 增加了一个default_config,将用来放置一些缺省的配置。同时将settings改为config了。这样你可以使用config来读取配置在settings中的信息。它是一个Storage对象。这个Storage是从web2py中弄过来的,它就是一个dict,但是可以使用obj.attr的方式来引用key,同时如果不存在则返回None。那么你使用起来就和Module差不多了。但是对不存在的变量不会抛异常。同时所有配置项需要大写,这一点与django相同。

7. 在views你可以调用redirect(url)来进行转换。原来需要使用return,现在只要调用就行了。它会引发一个异常,并且被Dispatcher处理。

8. 在views中还可以使用error(**kw)来引发一个错误,它会自动调用一个错误模板,同时你也可以自已来指定出错模板。它也会引发一个异常,并且被Dispatcher来处理。

更新说明:

  1. 用户可以设定settings.py。目前kuaiboo支持在apps目录下的settings.py文件和每个app目录下的settings.py。目前apps的settings.py作为主配置文件,kuaiboo会直接导入,其它的app下的settings.py会使用__import__,然后将属性合并到settings中去。因此最终这些settings.py将合成一个,你可以在代码中导入setting。(不过对于子settings.py的处理我还在考虑是否要使用exec来处理,这样,子settings.py将不再是通过导入了,它们将在主settings.py的环境下运行,这样更象是在同一个环境下运行的。不过这块还没有想好。)
  2. expose的处理方式进行了扩充。在张沈鹏的留言中他建议能否自动映射,我理解可能有两种方式:一种是不需要expose,view函数直接映射为url,那么一方面用户无法控制哪些是可以访问,哪些是内部函数,所以我认为不使用expose不太好。当然expose只是一种实现方式,也许以后url的映射的定义会有根它的方式。另一种理解可以是只使用expose,那么当没有参数时使用自动映射。如何映射呢?我现在的方法是将view函数映射为:/appname/viewfuncname/arg1/arg2。这种方式接近web2py的,不过我没有controller一层,所以没有controller的url。那么它是怎么做的?后面会有介绍
  3. view函数的变化。原来view函数需要使用def func(req, arg1, arg2,…):,现在你不再需要req这个参数了。也就是你可以象web2py一样来定义view函数:def func(arg1, arg2):。但是你仍然可以在view函数中直接使用象request, response这样的对象,为什么,后面会有介绍。

expose的自动映射

你可以直接使用@expose来直接映射一个view方法,我是怎么做的?

def expose(rule=None, **kw):
    if callable(rule):
        f = rule
        import inspect
        args = inspect.getargspec(f)[0]
        if args :
            args = ['<%s>' % x for x in args]
        appname = f.__module__.split(‘.’)[1]
        rule = ‘/’ + ‘/’.join([appname, f.__name__] + args)
        kw['endpoint'] = f.__module__ + ‘.’ + f.__name__
        url_map.add(Rule(rule, **kw))
        return f
       
    def decorate(f):
        kw['endpoint'] = f.__module__ + ‘.’ + f.__name__
        url_map.add(Rule(rule, **kw))
        return f
    return decorate

当你使用expose时,rule参数将是function对象,因此,判断出rule是函数对象时,它将根据function本身的信息来得到它所在的模块(f.__module__)和函数名(f.__name__)。只不过f.__module__得到的是类似于apps.App.views的形式,因此要处理一下才能得到App的名字。同时一个view函数可以定义了参数,因此我还使用了inspect模块来得到定义的参数名。详情可以看它的文档,同时做一些试验。因此一个view函数会被自动映射为:/Appname/view_func_name/arg1/arg2的形式。

View函数的变化

原来view函数要定义为def func(req, arg1,…),现在你不需要第一个req的参数了。但是你仍然可以在函数内部使用request, response等对象。(至于在view函数中还能使用哪些变量我还要考虑中,可能会不断增加,这样非常接近于web2py的效果。)那么是如何实现的呢?原来我发现你可以动态修改一个函数对象的func_globals对象,如:

>>> def f():
…     print response

如果你定义上面的函数,编译不会出错,但是运行会出错。因为我们没有定义response。一种方法是定义全局的response变量,另外就是可以修改func_globals增加这个变量。如:

>>> f.func_globals['response'] = ‘hello’
>>> f()
‘hello’

可以看到执行没有问题。相当于我们定义了全局的response变量。这就相当于变量的注入,你可以直接使用。

2008年05月16日

昨天基本上已经有一个雏型放到svn中去的,如果你下载最新的代码已经是一个可以运行的版本了。目前因为只是在开发阶段,所以许多东西并不定型,很多东西可能会变化,因此你目前只能玩一玩。下面举些简单的例子,这些例子已经在svn中的。

1. 下载源码

svn co http://mymisc.googlecode.com/svn/trunk/kuaiboo

BTW,为什么叫Kuaiboo,因为我实在不擅长起名字,这个名字中英文混合,有些接近“快步”的意思,因此中文名字希望是开发web象快走一样方便(当然只是一个愿望)。当然如果你有好的名字可以贡献给我的话我会非常感谢。

2. 现在已经自带一个开发服务器,它是使用Werkzeug的,并且这个开发服务器自带reload功能,如果你做了任何怎改,它会自动装入,很方便,与django, GAE的差不多。在windows下的话,按Ctrl+C可以关闭。GAE的好象不行。不过按Ctrl+Break应该是都可以的。一旦你的程序出现语法错误,那么这个开发服务器有可能编译出错而退出,所以你要注意检查。这块回头看一看能不能改一下,不让它自动退出。

下载下来后,你的目录应该看上去象这样:

kuaiboo\
    apps\
        __init__.py
        Hello\
            __init__.py
            views.py
            templates\
            static\
    kuaiboo\

apps中将用来放置你的app的代码。要记住kuaiboo是象django一样以app为单位的。固定放在apps下。它是一个python的package,目前命令行工具还没有开发可以自动生成这种目录结构,不过很简单。

kuaiboo是库文件,放置各种各样的模块,现在有Werkzeug, webob,还有其它的一些东西,比如web2py的模板,我写的Form等。

在apps中已经有一个Hello的应用,它是一个测试程序,很简单。每个app都是一个package,因此有__init__.py。目前Hello中还没有数据库的处理,因此没有models模块。不过关于数据库这块我想不要绑得过死。比如GAE数据库与一般的关系数据库就不相同,因此这个框架可能更多的是在非数据库的地方吧,不过还没有想好。

views.py是放view代码的。templates是放模板的。而static是放静态文件的,不过目前还没有用到。为什么要static呢?这是我曾经在django中的想法的一个实现。一个app应该是功能完整的集合,所以静态文件也算其中之一,放在app中是为了方便复用,如拷贝。

OK。结构已经介绍完毕,下面开始写views了。

4. 第一个模板的例子

from frameworks.SimpleFrame import expose

@expose(‘/’)
def index():
    return {}

把上面的代码加到views.py中。让我一行行地解释。

from frameworks.SimpleFrame import expose 这里从frameworkds.SimpleFrame中导出expose将用于url的生成。在SimpleFrame中引入了一些常用的方法或对象可以直接使用。后面还会用到如url_for, redirect等。

@expose(‘/’) 这是一个decorator,它用来将下面的函数与某个URL相对应,这里是’/'。因此当你访问/时,就会调用index()函数。这里url的定义我是使用Werkzeug的,因此更详细的可以看它的文档。这种URL的映射可以支持参数定义,如’/blog/<int:year>/</int:month>/</int:day>,怎么样,再来一个’/user/<username>’,可以看到你可以使用<type:name>来定义或<name>来定义参数,其中type为类型。是不是比django的纯正则要简单?这个例子还没有使用参数例子。目前kuaiboo还不需要urls.py这样的东西,它会自动导入所有views函数,因此只要在你想要输出的函数前加上@expose就可以输出为一个可访问的view函数,没有这个修饰的将不会被访问到。这是一种分散方式url定义,在未来如果view文件非常多不知道会对性能有多大影响,因此以后还会实现一种集中方式的定义。

def index(): 这里定义了index函数,它目前接受一个req的参数(原来是需要req参数,但是现在已经不需要的,你可以在view函数中直接使用request, response对象,为什么?在另一篇Blog中我会仔细描述。)所以有view函数的第一个参数都是req。不过view是使用函数还是class好在我昨天的blog中也说了,不好选择,先用函数吧,至少decorator用着比较方便。这里req其实是webob的Request对象,因此详细信息要查看webob的Request文档。写到这真是有些象turbogears了,都是大杂烩,文档也都在别人哪里。看来用是一回事,写是另外一回事啊。

return {} 在kuaiboo中一个view可以返回三种东西:

  1. 字典,这样kuaiboo会自动查找对应的模板进行处理。目前kuaiboo的策略就是在所有app的templates目录下查找与函数名相匹配的模板文件,如index对应的模板文件名为index.html。因此要注意,如果你的app有重名的函数,在查找模板时可能会使用其它的app中的模板。不过kuaiboo在查找时会优先查找当前app的templates目录,因此一般你不需要担心。不过建议view函数名最好不一样。想一想如果不采用app的开发方式,将所有的view方法放在一起的话,它们会重名吗?不过我也在考虑是否可以自定义其它的模板,不过目前还不支持,有待于以后有需求或有机会去实现它。
  2. 字符串,可以是unicode。它将自动使用Response来封装。
  3. Response对象。使用它可以处理附件下件,cookie之类的设置。

因此上面的例子不是最简单的,因为我还没有定义一个模板。不过如果你把return {}改为return ‘Hello,kuaiboo’,你就已经可以测试了。先让我们把这个例子做完。

5. 进入Hello/templates目录,然后创建一个index.html文件,内容如下:

<html>
<head>
<title>Index Test</title>
</head>
<body>
<h1>Kuaiboo Framework</h1>
<p>This is a test page index.</p>
</body>
</html>

6. 好,下面就可以测试了。进入kuaiboo目录,进入命令行,执行:

python manage.py.py runserver或 manage.py runserver

7. 在浏览器输入: http://localhost:8000

看到结果了吗?

2008年05月15日

这是我新挖的一个坑,不过已经填了些土了。它是什么呢?它是我写的一个web framework。怎么连我也造轮呢?记得我以前是更注重向现有框架做贡献的呀。

首先这个框架是一个试验品,或者说是主要是个人使用,因此我将有完全的控制权,这一点很重要。我可以用它学到许多框架的知识。以前只是使用,学习,象学过:cherrypy, Karrigell, snakelets等,不过没有做过什么开发;zope则是我学得最早了,不过也早就放弃了;django投入的精力最多,也开发了不少东西;再后来就是web2py了,不过重用搞得我很不爽,而且有些想法不被认同。但这些更多的还是集中在开发方面,对于框架本身了解有限,这次造轮是一个好机会。

其次用过的框架都有不让人满意的,而且许多观点也不被认同,因此我想信只有自已的才是最好的。就象我当初离开drPython自已创建了UliPad一样,想怎么做怎么么,很爽,这下子变成别人的观点将不被我认同了(是不是有些小人得志的感觉)?不过现实如此,我也很清楚地早就认识到这一点。因此要么你的努力被社区认同,要么另起炉灶,这种例子太多了,不然也不会有这么多的选择。那么django不让我满意的象模板,虽然对设计者友好,但是对程序员不友好,不想细说了。开发还不够简洁,在我写完Django Step by Step教程之后我已经意识到,用它作为入门的还不如使用Karrigell。太多需要知道的。不过这个也不好解决,功能随着复杂,自然会越来越难。而且这种不简洁是与web2py相比的结果。在学了web2py才知道web也可以这样开发,集成在浏览器几乎可以做一切事,自动的ticket,适合程序员的模板系统,自动维护的数据库模块,最少配置开发,约定url规则,这一些与django相比显得很轻巧和方便。如果你看过代码,的确要比django简单,但是说句实话,因为只是个人项目难免代码的风格和技术要逊于django。不过由于设计上不方便重用,url的固定方式,虽然支持routing的修改,但是还是不够方便,因此仍然让我感到遗憾。而web2py的设计上的修改很有可能是对web2py的某种否定,所以在我看来它无法按我满意的方向发展。不过web2py的作者massimo是一个非常让人敬佩的人,非常有创造力和激情。

所以作为尝试,我打算自已做一个框架,也许用在自已的项目中,也许是放在GAE上用。现在已经有一个雏开型了。目前项目地址在 http://mymisc.googlecode.com/svn/trunk/kuaiboo 你可以使用svn将代码取下来。可以说它是一个大杂烩,我目前集中了:

  • Werkzeug 用它来完成框架的搭建,因为它本来就是用来做框架的
  • webob 用来生成Request和Response对象,因为GAE和pylons就使用它。本来我是使用Werkzeug自带的,不过ygao建议我使用webob,好就先用它,不行再换。
  • 模板使用web2py的,不过进行了改造。我还是挺喜欢它可以支持python代码的方式,虽然块结束时要输入pass,不过没关系,可以用来解决使用缩近造成的排版问题。
  • 数据库还没有加,目前打算使用web2py的模块
  • Form处理我将使用我自已写的,不过更接近django的方式
  • URL mapping还没有想太好,目前我是采用Werkzeug的教程的方式,使用了一个@expose在每个输出的view方法前进行修饰。不过这样的做法需要将所有的view模块先导入才可以,不知道会不会影响效率。
  • 处理模式为MVT
  • View目前采用函数方式,正在考虑是否要使用class方式,不过这样一来decorator的方式就用不了了。
  • 组织方式采用django的app方式,你可以在多个app中组织代码,最终形成完整应用。
  • 与webserver交互一方面是支持wsgi标准,另一方面借用了web2py中的cherrypy模块,可以独立部署为服务器,不过只是测了一下,还没有加进去。

还有些没有想到或没有实现的东西,比如:i18n的问题。可能你已经看出,一个框架要关心的事情真是很多。不过我会一点点完善它。有兴趣你可以关注。那么我会在我的Blog上记录对它的修改,因为是个人项目,所以可能修改得随心所欲。当然欢迎你参与和讨论。

2008年04月22日

1. 发送邮件的问题

sender不能为空,必须是创建应用的管理员的邮箱。

2. 如何在有代理的环境下使用appcfg.py来更新项目

先在命令行上设置代理,如:

set http_proxy=username:password@proxy_host:proxy_port

上面是带用户和口令的,如果没有,则形式为:

set http_proxy=proxy_host:proxy_port

3. 使用数据库时,不支持!=操作,没有like, match之类的

4. 打印调试信息,可以使用:

import logging
logging.error(xxx)

sys.stderr.write(str(xxx))
2008年04月18日

今天开发了一个wraptext模块,它可以对文本进行折行处理。详情:

 说明
    本程序用来进行文本的折行处理,支持中文和英文,可以处理Unicode和普通字符串

 参数说明
    text       待处理的文本,可以是unicode或str
    width      处理宽度
    encoding   字符串编码,只当text不是unicode时有效
    cr         换行符,如果为None则自动从文本中判断,自动设定为找到的第一个换
               行符,如果找不到缺省为’\n’,如果不为None,则使用设定的换行符
    indent     非首行缩近量
    firstindent首行缩近量
    skipchar   行首忽略字符,如果存在则在处理前会清除每前开始前有skipchar的文本

 功能描述
  1.支持unicode和非unicode文本,如果为非unicode文本,则会使用encoding指定的编
    码对文本转换为unicode,在返回时,会根据原文本是unicode还是非unicode进行转
    换并输出。
  2.支持段落概念。两个以上连续的回车为段落分隔,其中如果一行只包括空白(空格或
    制表符的行)将视为空行。最终的结果段落间只保存一个空行。如果只存在单个换行
    则相邻的行视为同一段落。支持’\n’, ‘\r\n’, ‘\r’三种形式的换行符。可以自动
    使用文本中的回车符或指定转换后的回车符。
  3.自动处理亚洲文字和半角字符,自动处理空白,多个空白(包括制表符)将自动合并
    成一个。亚洲文字和英文之间以空格分隔。对于亚洲文字中间的空白自动删除。
  4.支持缩近设置,首行缩近和非首行缩近。缩近量可以是数值,则为空格*数值,可以
    是字符串。如果firstindent没有设置将缺省为indent的值。
  5.可以设置每行行首要忽略的字符,如注释行的’#',在处理时将先删除匹配的行首字
    符。

 示例
  msg = ”’中文 中文hello, world”’
  wraptext(msg, 10)

现在代码放在 http://code.google.com/p/pyzh 中。原来它已经有一个模块,不过不满足我的要求,于是自已重新写了一个。