最近工作有点多,趁周末有空,继续分享我在学习和使用python过程中的一些小tips。

有没有遇到过这样的事情:对数据库执行插入或更新操作,因为数据量大或其他原因,导致此次操作非常耗时,有时甚至等上好几个小时,也无法完成。很郁闷,怎么操作不超时啊?因为数据库配置时超时时间很长,并且有些操作又是需要很长时间的,所以不能修改默认的超时时间。

因为客观条件不允许,我们不能靠数据库超时来终止此次操作,所以必须要在自己的方法逻辑模块里实现超时检测的功能。

在python里有没有可以不用修改原来的方法内部逻辑,就能实现超时检测呢?肯定有啦,就是利用装饰器。装饰器是什么?在博客园找到了一篇介绍文章:函数和方法装饰漫谈(Function decorator)

废话听完,我现在介绍主角出场:超时装饰器,timeout decorator。

超时检测逻辑:启动新子线程执行指定的方法,主线程等待子线程的运行结果,若在指定时间内子线程还未执行完毕,则判断为超时,抛出超时异常,并杀掉子线程;否则未超时,返回子线程所执行的方法的返回值。

在实现过程中,发现python默认模块里是没有方法可以杀掉线程的,怎么办呢?当然先问问google或百度,果然,keill thread这个关键词很热门,很快就搜索到我想要的东西了:"Kill a thread in Python",就是以下这个KThread类,它继承了threading.Thread,并添加了kill方法,让我们能杀掉它:

 

import sys


class KThread(threading.Thread):
    
"""A subclass of threading.Thread, with a kill()
    method.
    
    Come from:
    Kill a thread in Python: 
    http://mail.python.org/pipermail/python-list/2004-May/260937.html
    
"""
    
def __init__(self, *args, **kwargs):
        threading.Thread.
__init__(self, *args, **kwargs)
        self.killed 
= False

    
def start(self):
        
"""Start the thread."""
        self.
__run_backup = self.run
        self.run 
= self.__run      # Force the Thread to install our trace.
        threading.Thread.start(self)

    
def __run(self):
        
"""Hacked run function, which installs the
        trace.
"""
        sys.settrace(self.globaltrace)
        self.
__run_backup()
        self.run 
= self.__run_backup

    
def globaltrace(self, frame, why, arg):
        
if why == 'call':
          
return self.localtrace
        
else:
          
return None

    
def localtrace(self, frame, why, arg):
        
if self.killed:
          
if why == 'line':
            
raise SystemExit()
        
return self.localtrace

    
def kill(self):
        self.killed 
= True


好了,万事戒备,让我们来完成剩下的代码吧,也就是timeout decorator:

class Timeout(Exception):
    
"""function run timeout"""
    
def timeout(seconds):
    
"""超时装饰器,指定超时时间
    若被装饰的方法在指定的时间内未返回,则抛出Timeout异常
"""
    
def timeout_decorator(func):
        
"""真正的装饰器"""
        
        
def _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):
            result.append(oldfunc(
*oldfunc_args, **oldfunc_kwargs))
        
        
def _(*args, **kwargs):
            result 
= []
            new_kwargs 
= { # create new args for _new_func, because we want to get the func return val to result list
                'oldfunc': func,
                
'result': result,
                
'oldfunc_args': args,
                
'oldfunc_kwargs': kwargs
            }
            thd 
= KThread(target=_new_func, args=(), kwargs=new_kwargs)
            thd.start()
            thd.join(seconds)
            alive 
= thd.isAlive()
            thd.kill() 
# kill the child thread
            if alive:
                
raise Timeout(u'function run too long, timeout %d seconds.' % seconds)
            
else:
                
return result[0]
        _.
__name__ = func.__name__
        _.
__doc__ = func.__doc__
        
return _
    
return timeout_decorator

 

真的能运行吗?写个测试程序运行运行:

@timeout(5)
def method_timeout(seconds, text):
    
print 'start', seconds, text
    time.sleep(seconds)
    
print 'finish', seconds, text
    
return seconds

if __name__ == '__main__':
    
for sec in range(110):
        
try:
            
print '*' * 20
            
print method_timeout(sec, 'test waiting %d seconds' % sec)
        
except Timeout, e:
            
print e

 

看,真的行:

 

 

本次的tips可能有点复杂,运用到多线程,装饰器等,希望它对你有所帮助。

PS:山顶风光果然一流,独缺美女相伴!!!

posted @ 2008-08-30 22:29 MK2 阅读(818) | 评论 (3)编辑
对于我这个从.NET过来的人,对python的str和unicode会感到非常不适应。经常在一些常用的地方遇到编码异常问题。如保存字符串到文本中,是要先编码还是直接保存呢?字符串是str还是unicode呢?保存字符串到数据库是直接保存str又或是先将unicode编码得到的str呢?
好多个问号,这都是我个python初学者碰到的问题。在尝试多次痛苦后,总算有了一些思路。原来unicode早已在python实现的很好,只是我使用不当罢了。

一个很关键的并且要常记住的,就是代码中所有字符串都统一使用unicode,而不是str。这样,自己就能很清楚要处理的字符串类型了。请记住,是所有,任何地方。
例如:
>>> s1 = u'%s欢迎您!' % u'北京'
>>> s1
u'\u5317\u4eac\u6b22\u8fce\u60a8\uff01'
>>> print s1
北京欢迎你!

若像这样,就会抛异常:
>>> s2 = '%s欢迎您!' % u'北京'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 2: ordinal not in range(128)
由UnicodeDecodeError可猜想得到,解析器尝试使用ascii对'%s欢迎您!'进行解码,由于'%s欢迎您!'实际是使用utf-8编码的(这是我系统终端默认的),所以使用ascii解码肯定会错,只要如下,就可以重现这个异常了:
>>> s2 = '%s欢迎您!'.decode('ascii')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 2: ordinal not in range(128)

分清encode和decode。str --> decode(c) --> unicode, unicode --> encode(c) --> str,其中编码类型c必须相同。

将unicode字符串写入文件前先使用特定编码对其进行编码(如unicodestr.encode('utf-8'))得到str,保证写入文件的是str;从文件读取到str,然后对其进行解码(如encodestr.decode('utf-8'))得到unicode。
这是互逆的两个操作,编码类型一定要一致,否则会出现异常。

自己支持了unicode,但是你团队的其他人是否都使用unicode呢?你使用的其他模块是否也使用unicode呢?这个一定要清楚的,不然同样会出现许多因为编码问题的异常。

好了,晚了,随便写了点,大家晚安,2008.8.1,本世纪的第一次日全蚀,你要看吗?

Technorati 标签: , ,
posted @ 2008-08-01 01:15 MK2 阅读(66) | 评论 (0)编辑

最近都在研究如何利用最少的字节判断两个PE文件是否相同。

PE文件几个关键:

1. 在一个PE文件的开始处,我们会看到一个MS-DOS可执行体(英语叫“stub”,意为“根,存根”);它使任何PE文件都是一个有效的MS-DOS可执行文件。如图蓝色框部分。0x5A4D:MZ,每个PE文件都是以此开始。

image

 

2. 在DOS-根之后是一个32位的签名以及魔数0x00004550 (IMAGE_NT_SIGNATURE)(意为“NT签名”,也就是PE签名;十六进制数45和50分别代表ASCII码字母E和P)。

image

3.

image

 

4. DOS-根和签名(DOS-stub and Signature)

image

你可以通过确认DOS-头部分是否为一个IMAGE_DOS_HEADER(DOS头)结构来认出DOS-根,它的前两个字节必须为连续的两个字母“MZ”(有一个#define IMAGE_DOS_SIGNATURE的定义是针对这个WORD单元的)。

DOS-根的概念很早从16位windows的可执行文件(当时是“NE”格式),现在是PE。

可以通过跟在后面的签名来将一个PE二进制文件和其它含有根的二进制文件区分开来,跟在后面的签名可由头成员'e_lfanew'(它是从字节偏移地址 60(0x3C)处开始的,有32字节长)所设定的偏移地址找到。对于OS/2系统和Windows系统的二进制文件来说,签名是一个16位的word单元;对于PE 文件来说,它是一个按照8位字节边界对齐的32位的longword单元,并且IMAGE_NT_SIGNATURE(NT签名)的值已由#defined定义为0x00004550(即字母“PE/0/0”)。

即:判断一个文件是否PE:1)判断开始两字节是否 0x5A4D;2)根据0x3C~3F的得到偏移地址,然后根据偏移地址获得签名,判断该签名是否属于PE即可。

 

5. 文件头

image 

开始于0x00E4,有0x14字节长,到0xF8:
    Machine                              4c 01       ; i386
    NumberOfSections              03 00       ; 代码段和数据段
    TimeDateStamp                  20 84 7D 3B ; 
    PointerToSymbolTable        00 00 00 00 ; 未用
    NumberOfSymbols               00 00 00 00 ; 未用
    SizeOfOptionalHeader         e0 00       ; 常量
    Characteristics                    0F 01(0000 0001 0000 1111B)       ; 32位机器上的可执行文件

 

成员“Characteristics(特性)”是一个16位的,由许多标志位形成的集合组成,但大多数标志位只对目标文件和库文件有效。具体如下:
    位0 IMAGE_FILE_RELOCS_STRIPPED(重定位被剥离文件) 表示如果文件中没有重定位信息,该位置1,这就表明各节的重定位信息都在它们各自的节中;可执行文件不使用该位,它们的重定位信息放在下面将要描述的“base relocation”(基址重定位)目录中。
    位1 IMAGE_FILE_EXECUTABLE_IMAGE(可执行映象文件) 表示如果文件是一个可执行文件,也即不是目标文件或者库文件时,置1。如果链接器尝试创建一个可执行文件,却因为一些原因失败了,并保存映像以便下次例如增量链接时使用,此时此标志位也可能置1。
    位2 IMAGE_FILE_LINE_NUMS_STRIPPED(行数被剥离文件) 表示如果行数信息被剥除,此位置1;此位也不用于可执行文件。
    位3 IMAGE_FILE_LOCAL_SYMS_STRIPPED(本地符号被剥离文件) 表示如果文件中没有关于本地符号的信息时,此位置1(此位也不用于可执行文件)。
    位4 IMAGE_FILE_AGGRESIVE_WS_TRIM(强行工作集修剪文件) 表示如果操作系统被假定为:通过将正在运行的进程(它所使用的内存数量)强行的页清除来修剪它的工作集时,此位置1。如果一进程是大部分时间处于等待,且一天中仅被唤醒一次的演示性的应用程序之类时,此位也应该被置1。
    位7 IMAGE_FILE_BYTES_REVERSED_LO(低字节变换文件)和 位 15IMAGE_FILE_BYTES_REVERSED_HI(高字节变换文件) 表示如果一文件的字节序不是机器所预期的形式,因此它在读入前必须调换字节时,此位置1。这样做对可执行文件是不可靠的(操作系统期望可执行文件都已经被正确地按字节排整齐了)。
    位8 IMAGE_FILE_32BIT_MACHINE(32位机器文件) 表示如果使用的机器被期望为32位的机器时,此位置1。现在的应用程序总将此位置1;NT5系统可能工作不同。
    位9 IMAGE_FILE_DEBUG_STRIPPED(调试信息被剥离文件) 表示如果文件中没有调试信息,此位置1。此位可执行文件不用。按照其它信息([6])(这里指的是参考书目中的第[6]种----译者注),此位被称作“恒定”,并且当一个映象文件只有在被装入优先的装入地址才能运行(亦即:此文件不可重定位)时,此位置1。
    位10 IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP(移动介质文件从交换文件运行) 表示如果一个应用程序不可以从可移动的介质,如软盘或CD-ROM上运行时,此位置1。在这种情况下,建议操作系统将文件复制到交换文件并从那里执行。
    位11 IMAGE_FILE_NET_RUN_FROM_SWAP(网络文件从交换文件运行) 表示如果一个应用程序不可以从网络上运行时,此位置1。在这种情况下,建议操作系统将文件复制到交换文件并从那里执行。
    位12 IMAGE_FILE_SYSTEM(系统文件) 表示如果文件是一个象驱动程序那样的系统文件,此位置1。此位可执行文件不用;我所见过的所有NT系统的驱动程序也不用。
    位13 IMAGE_FILE_DLL(DLL文件) 表示如果文件是一个DLL文件时,此位置1。
    位14 IMAGE_FILE_UP_SYSTEM_ONLY(仅但处理器系统的文件) 表示如果文件不设计运行在多处理器系统上(也就是说,因为此文件严格地依赖单一处理器的一些方式工作,所以它会发生冲突)时,此位置1。

在看来一个DLL的文件头

image

Characteristics                    0E 21(0010 0001 0000 1110B 高-低)       ; 32位机器上的DLL文件

 

参考:

http://bbs.pediy.com/showthread.php?threadid=21932

posted @ 2008-06-23 19:32 MK2 阅读(60) | 评论 (0)编辑
A呵呵,一直都是用WLW写blog的,很久之前就装了Scribefire,一直都没用,今天来个测试,呵呵,如果满意,以后就用它了。



直接帖截图测试:



edit





上传图片测试:OK





测试其他吧哈哈乱来。

©®°±¶º¿¿¾


呵呵,看来真的可以取代WLW



Technorati Tags: , ,
posted @ 2008-06-10 19:23 MK2 阅读(66) | 评论 (0)编辑

因经常在多台机器上使用Firefox,难免会想装一些已经习惯了的插件(晕,都依赖他们),没安装上总是用起来不顺手。于是就有了这篇文章。

1. 第一个要备忘的肯定是它:Google Browser Sync

image

可以同步些什么?看看这个截图吧:

image

很Cool吧,仿佛在那台机打开FF,都感觉是一样的,完全获取回上次的所有状态,并且支持加密传输,这样个人私隐就不会泄露了。

2. GMarks

虽然说Google Browser Sync可以同步Bookmarks,但是我还是喜欢将Bookmarks保存到互联网上,GMarks就是一个Google Bookmarks的插件,使用也非常简单,Bookmarks太多了吧,不記得放在那个位置了吧,呵呵,不怕,在自己的Bookmarks上搜索一下不就行了?

image

3. All-in-One Gestures Extension

有使用鼠标手势控制的习惯吧?怕使用了Firefox而无法拥有这个功能吗?呵呵,哪All-in-One Gestures Extension肯定是你的最爱,完全支持你想要的手势。

image

image

4. DictCN (呵呵,忘记推荐沪江小D,绝对比这个精彩多了!!!)

呵呵,像我这些英语很差但是又要经常看英文的人,这个插件肯定比钱包更加重要,放在你的FF上吧,肯定会用上的,特别是它还支持发音功能。

image image

5. AutoHide

觉得FF上方的工具栏太占位置了吗?导致看到的页面太小了吧?呵呵,这个插件绝对帮你将工具栏完全隐藏了,按Shift+F11让FF全屏吧。

image

6. Firebug 不顶不行

呵呵,这个不用我介绍了吧,太出名了!!!

image

估计每个做Web开发的人的FF上都会有它。o(∩_∩)o...

7. Showcase

经常打开几十个tab而又不想关掉它们吗?是不是要找一个tab花了很多时间?使用Showcase可以让你马上找到你想找的tab,不信?看看截图吧:

image

想用了吧?

8. IE Tab

不用IE不行啊,有时候真的没有了IE,很多事情就没法做了,于是有打开IE。很麻烦吧,如果你的系统是Windows,那么IE Tab可以很方便地设置当前页面使用IE内核访问,还可以设置更多的规则,唉,我们的世界没了IE真不容易:

image

9. Fireshot

一个页面太长了吧,是否有想将整个页面截图处理的需求呢?Fireshot可以实现,并且还有上传,直接将截图的链接发给你的朋友吧,呵呵,这样介绍网站,也是一个不错的选择。

image

image

10. Tab Scope

想预览一下tab里面是什么内容吗?看看它是不是已经加载完了?这个插件可以提高你的兴趣。

image

11. QuickDrag

可以直接拖拉页面上的链接,并在新tab中打开,不用点鼠标中键了,Cool啊!!

12. ColorZilla

可以获取页面上任意部位的颜色。

image

??. FEBE

好了,装了这么多插件,有备份这些插件的插件吗?呵呵,你都想备份了吧?用FEBE吧,你想怎样备份都可以。

image

image

这里可以查看更多截图:http://customsoftwareconsult.com/extensions/febe/febe50/FEBE5.0.html

 

呵呵,今天到此为止吧,以后用到更多好的插件我会继续补充的,:-)

 

希望对你有用!!

 

Technorati 标签: ,,,
posted @ 2008-06-09 10:49 MK2 阅读(251) | 评论 (2)编辑

在没有使用AjaxForm前,我做的一个小小的评论提交的Web form,评论内容使用了TinyMCE做文本编辑。为了增加一点点的用户体验,就顺手拿AjaxForm来实现Ajax提交。可是发现出现了一个意外的事情。就是每次提交,第一次提交时,AjaxForm会无法获得当前编辑的评论内容,即TextArea里面的内容,要再点击一次提交,才能将TextArea的内容提交上去。

关键是TinyMCE上的内容没有在提交前更新到TextArea中。于是想看看AjaxForm是否有在提交前的事件绑定,发现在beforeSubmit事件中,formData的内容已经被填充,虽然可以在此处自行将当前的TinyMCE的内容填充上去(详细可查看这里),可是总觉得是不太漂亮的解决方案。

为了找是否有其它途径解决此问题,我查看了一下AjaxForm的源代码,发现原来AjaxForm作者已经为这问题提出了统一的解决方案,具体源代码如下:

    // hook for manipulating the form data before it is extracted;
    // convenient for use with rich editors like tinyMCE or FCKEditor
    var veto = {};
    this.trigger('form-pre-serialize', [this, options, veto]);
    if (veto.veto) {
        log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
        return this;
   }

果然,真的有对应的事件让我们可以在formData被序列号前将TinyMCE的数据更新到TextArea中。只要我们绑定form的form-pre-serialize事件即可。

于是一个我的代码就这样出来了:(对应FCKEditor类似)

    // bind form using 'ajaxForm' 
    $('#commentForm').ajaxForm(options);
    // 绑定form-pre-serialize事件,在触发form-serilaize事件前保存tinyMCE的数据到textarea中
    $('#commentForm').bind('form-pre-serialize', function(event, form, options, veto) {
	tinyMCE.triggerSave();
    });
 
^_^ 希望对你有用...

Technorati: , , ,

 
posted @ 2008-06-08 20:38 MK2 阅读(261) | 评论 (2)编辑