2009-04-26

请,给点提示吧,bitten

一直在用bitten做集成测试,效果挺好,现在天天离不了它。不过最近被它搞的很郁闷,因为bitten-slave吐出一行神秘的错误信息:
[DEBUG   ] Sending POST request to 'https://mytrac.com/build/5632/steps/'
[WARNING ] Server returned error 403: Forbidden
[ERROR ] HTTP Error 403: Forbidden

起因是因为最近升级了bitten,顺便想把以前的单个进程拆成多个,加快一下bitten的执行速度。人家说,一个和尚有水吃,两个和尚抬水吃,三个和尚没水吃,没想到软件也是这样。两个slave一块跑起来,就有了问题:有时这个出错,有时那个出错,提示都是神秘的403。观察了一下,出现错误的时候,都是某slave A先执行,B稍后也开始执行,然后A再次向服务器POST数据就会出错。猜测可能用户之间有冲突,试过了几种方式,使用各自独立的用户名,在不同目录执行,在不同机器执行,但问题依旧。今天费了些力气,把bitten的代码跟踪了半天,发现程序调用了这个函数:
    def reset_orphaned_builds(self):
"""Reset all in-progress builds to ``PENDING`` state if they've been
running so long that the configured timeout has been reached.

This is used to cleanup after slaves that have unexpectedly cancelled
a build without notifying the master, or are for some other reason not
reporting back status updates.
"""

幸亏代码里有注释,这才恍然大悟,原来是timeout参数在作怪。进入trac管理界面,把timeout从10秒调高到500秒,总算解决了问题。

实际上,在bitten描述客户端协议的文档中,已经指出了这一问题:
To handle the case of build slaves going away at some point between having created a build and completing the build, the build master should have a configurable timeout. All in-progress builds would be checked against this timeout; if there has been no activity on the build for an amount of time exceeding the timeout, the master should cancel the build, resetting it the PENDING state. If a slave later does decide to come back to life and post results, it would get 404 (Not Found) or 409 (Conflict) errors, and should cancel the build on its side, too.

但由于未明的原因,bitten的实现用403代替了404、409,并且没有给出进一步的提示信息。要是bitten-slave能打印一条附加信息,告诉我403的原因可能跟timeout设置有关系,那该多好呀。

这个故事告诉我们,别嫌提示信息废话太多,能多写一点是一点,不定什么时候它就会节省自己和别人的若干时间。折腾了三天以后,我现在太喜欢话唠的软件啦。

2007-11-30

用于django中的缓存decorator

myproject/decorators.py代码:
from django.core.cache import cache

def cached(cache_key='', timeout_seconds=1800):
def _cached(func):
def do_cache(*args, **kws):
if isinstance(cache_key, str):
key = cache_key % locals()
elif callable(cache_key):
key = cache_key(*args, **kws)
data = cache.get(key)
if data: return data
data = func(*args, **kws)
cache.set(key, data, timeout_seconds)
return data
return do_cache
return _cached
只能用于静态数据的缓存,如果需要对connection.cursor等对象进行缓存,那需要函数本身做更多的处理,不是这个decorator要解决的问题。

使用范例1,简单的cache key:
from myproject.decorators import cached

class MenuItem(models.Model):
@classmethod
@cached('menu_root')
def get_root(self):
return MenuItem.objects.get(pk=1)

使用范例2,cache key需要根据调用参数来决定:
@cached(lambda u: 'user_privileges_%s' % u.username, 3600)
def get_user_privileges(user):
#...

使用范例3,需要只对直接调用者做缓存,递归的调用不需要,那么把要递归函数独立出来:
class MenuItem(models.Model):
@cached(lambda s,u: 'user_menu_%s_%s' % (u.username, s.id), 3600)
def permit_menu_items(self, user):
return self._permit_menu_items(user)

def _permit_menu_items(self, user):
items = []
for mi in self.children():
items += [n for n in mi._permit_menu_items(user)]
return items

使用范例4,返回结果中部分需要做缓存的,首先把要缓存的部分extract出来,然后对其应用缓存机制:
class Report:
def get_summary(self, day, path='', sort='path', type='daily'):
data = self._get_summary(day, path, type)
# sort ...
return data

@cached(lambda s,d,p,t:'summary_%s_%s_%s'%(d,p,t), 3600*24)
def _get_summary(self, day, path='', type='daily'):
#...

2007-11-16

人们研究android的热情高涨

短短一天时间,又有很多新情况发生。

首先,有人测试了汉字的显示,发现可以支持。我也在模拟器里试了一下,确实很容易。系统内带了几种字体,其中有一款支持CJK字符。

其次,有人成功编译了c版本的hello world,并执行成功。使用的是arm的编译器。而且还编译了全功能的busybox,可以安装到模拟器的系统中。

另外,已经有人成功编译linux放入android模拟器中运行。

邮件列表里很多人都在打听、讨论能不能用C/C++/Python/Ruby之类的语言代替java来开发。今天去jython的项目主页看了看,惊奇的发现项目复苏了,jython 2.2已经正式发布。期待高手把jython打包集成进android吧。

2007-11-15

Django+Cheetah

最近在用Django做东西,考虑到现在的流行程度,用Django在稳定性、bug修正速度、参考资料等方面很有优势。但它的模板系统很被一些人诟病,很多用python开发者第一次使用Django都会对它发点牢骚。Python语言的魅力之一就是它的开发,甚至对象实例在运行中都可以随时被改变。但Django因为一些考虑,人为的限制了模板系统的功能,不允许它过于强大。作为一种设计思想,增加限制可以简化问题的复杂程度、提高效率和代码安全性等等,好处不少。但我们是Python程序员,不受拘束、流畅而连贯的书写代码是我们的一贯风格(或说是追求目标),反正我可不愿被当孩子一样限制不许做这、不许做那。用最快的速度,写出糟糕但是能运行的代码,也是程序员应该争取的一个权利--有了可以跑的代码,才能有生存的机会,才能有后续的优化。所以我要寻找一种Django模板的替换方案。

从编写者的舒适角度来看,zpt等类似php的语法写起来感觉都恩罗唆,逻辑之外要码的累赘字符太多了,不够爽快。类似webpy、Cheetah的模板用起来更简单,而且有着类似python的语法风格,个人比较喜欢。据测试,Cheetah的速度也非常快,历史又很悠久,社区活跃,使用起来基本没有后顾之忧。所以我选择Cheetah。参考了Eric Florenzano的文章,在Django中使用Cheetah非常简单。首先要在settings.py中配置模板目录:
TEMPLATE_DIRS = (
'/path/to/myproject/templates',
)

然后编写一个使用Cheetah模板的render_to_response函数,用来代替Django自带的:
import os.path
from Cheetah.Template import Template
from django.conf import settings
from django.http import HttpResponse

def render_to_response(template_name, context=None, **kwargs):
for template_dir in settings.TEMPLATE_DIRS:
path = os.path.join(template_dir, template_name)
if os.path.exists(path):
template = Template(file = path, searchList = (context,))
return HttpResponse(unicode(str(template), 'utf-8'), **kwargs)
raise ValueError, 'Could not find template for %s' % template_name

我是把上面这段代码放在myproject/shortcuts.py文件中。使用起来是这样子:
from myproject.shortcuts import render_to_response
def index(request):
return render_to_response('index.tmpl', {'title': 'index')

Dell 640m的双显示器配置

参考了一下Ubuntu论坛里一个帖子,配置好了双显示器,也算是把多出来的一个显示器利用上了。这里是xorg.conf的后半部分:

Section "Device"
BoardName "945 GM"
BusID "0:2:0"
Driver "i810"
Identifier "Device[1]"
Option "MonitorLayout" "CRT,LFP"
Screen 1
VendorName "Intel"
EndSection

Section "Device"
BoardName "945 GM"
BusID "0:2:0"
Driver "i810"
Identifier "Device[0]"
Screen 0
VendorName "Intel"
EndSection

Section "Monitor"
DisplaySize 340 270
Identifier "Monitor[0]"
ModelName "DELL 1708FP"
VendorName "DELL"
Option "DPMS"
HorizSync 31-80
VertRefresh 56-75
EndSection

Section "Monitor"
DisplaySize 305 230
Identifier "Monitor[1]"
ModelName "DELL 1280X800 LAPTOP"
VendorName "DELL"
Option "DPMS"
HorizSync 30-67
VertRefresh 30-60
EndSection

Section "Screen"
Device "Device[0]"
Identifier "Screen[0]"
Monitor "Monitor[0]"
DefaultDepth 24
SubSection "Display"
Modes "1280x1024" "1152x864" "1024x768" "800x600" "720x400" "640x480"
EndSubSection
EndSection

Section "Screen"
Device "Device[1]"
Identifier "Screen[1]"
Monitor "Monitor[1]"
DefaultDepth 24
SubSection "Display"
Modes "1280x800" "1024x768" "800x600" "640x480"
EndSubSection
EndSection

Section "ServerLayout"
Identifier "Default Layout"
InputDevice "Generic Keyboard"
InputDevice "Configured Mouse"
Option "Clone" "off"
Option "Xinerama" "on"
Screen "Screen[1]" leftof "Screen[0]"
Screen "Screen[0]"
InputDevice "Synaptics Touchpad"
InputDevice "stylus" "SendCoreEvents"
InputDevice "cursor" "SendCoreEvents"
InputDevice "eraser" "SendCoreEvents"
EndSection

Section "DRI"
Mode 0666
EndSection

Xinerama模式非常好用,笔记本屏幕的和外接的DELL LCD显示器都可以打到最大分辨率;如果按ctrl_alt_"num +"或者ctrl_alt_"num 1",还可以即时切换鼠标指针所在屏幕的分辨率,在外接投影仪时很方便。让我很纳闷的是,配置里“Screen "Screen[0]"”这一句必须写在screen[1]的后面,不知道是什么原因。