2005年11月30日

Casing 还提供了一个叫start_sync_thread()的方法,它是用来进行同步控制。记得吗?上一篇为了实现处理线程与状态更新线程之间的信息交换,我们通过创建一个可变对象( 实例,列表,字典都可以)作为共同的参数,这样就可以了。但还是需要你自已去创建,同时不能保证线程安全(new_obj()只是一个空对象,没有线程安全的保护)。这样,为了解决线程同步和修改状态信息时的线程安全,Casing提供了一个叫 SyncVar 的对象。你可以单独使用它,也可以在调用start_sync_thread() 时由 Casing 来替你生成。

SyncVar 对象可以进行如下操作:

var = SyncVar()
print bool(var)
var.set()
print var.isset()
var.set(‘hello’)
print var.get()
var.clear()
print var.isset()
print bool(var)
if not var:print ‘False’
var.set(‘hello’)
print var == ‘hello’
print var != ‘hello’

创建一个新的 SyncVar 对象,它的缺省状态是 False 。执行set()时,如果没有参数,则状态改为True。它可以传入任何值的参数。通过isset()来得到状态值。执行clear()可以将状态清除为False。可以直接对 SyncVar 对象本身求bool()值,可以进行等于,不等于的比较。执行get()与isset()是一样的,只是方便理解,都是返回状态值。

那么一旦你调用start_sync_thread()方法来执行的话,需要在加载的函数中增加"syncvar"参数。同时对于process函数也要增加这一参数。并且 start_sync_thread() 会将自动创建 SyncVar 对象同时将对象初始化为 True 的状态。同时在加载的函数和process函数中都应该对这个变量进行判断,一旦变为 False,则表示程序需要结束,那么这些函数都应该退出。同时 Casing 在函数切换时一旦发现 syncvar 的值为 False,则会停止后继的处理直接退出。同时Casing 还提供了 stop_thread()  的方法,用来将 syncvar 的值改为False,从而起到中断运行的效果。但这一些都基于这些约定。可能相对来说有些复杂。但如果你自已需要这样做,方法与此应该是相差不多。

from Casing import *
import time

def test_A(n, syncvar):
    for i in range(1, n+1):
        if syncvar:
            syncvar.set(i)
            print "=",i
            time.sleep(0.5)
        else:
            break
       
def process(syncvar):
    if syncvar:
        print ‘process…’, syncvar.get()
   
d = Casing(test_A, 10) + Casing(test_A, 20)
d.onprocess(process, timestep=2)
d.start_sync_thread()
time.sleep(20)
print ’stop’

这是一个使用了 SyncVar 约定的例子,需要注意:test_A和process都需要接收一个名为syncvar的变量,变量名不能错。但在定义Casing对象时并不需要传递这个参数,因为这是 Casing 自动为你生成的。

前面讲了这么多只是将函数串了起来,并没有什么,但却是最基础的功能。下面就要进入线程控制了。Casing 目前有三种启动方式:start(), start_thread(), start_sync_thread()。

start()是不带线程的方式。

start_thread()将启动线程,但在运行中不会对线程进行干预,用户可以自已想办法。

start_sync_thread() 将动线程,同时对调用的函数的参数有要求,它会传递一个同步对象给调用的方法,可以让方法使用这个同步对象,从而对线程的运行进行更精细的操作。

请注意,Casing 目前实现的线程控制是将多个函数串行,而不是并行(以后可以考虑实现多线程并行调度)。

例1:

from Casing import *
import time

def test_A(n):
    for i in range(1, n+1):
        print i
        time.sleep(.5)

d = Casing(test_A, n=10) + Casing(test_A, n=20)
d.start_thread()
time.sleep(20)
print ’stop’

这就是一个使用线程方式执行的例子。test_A会输入一系列的数字,每输出一个间隔0.5秒。d是一个Casing对象,它将两个test_A调用串起来。最后的time.sleep(10)是必须的,如果没有这一句,主线程一下子就执行完毕了,因此子线程将自动结束。而通过sleep()等待一会就可以让子线程运行完毕。

这时我们将其扩展一下,增加进度控制处理:

例2:

from Casing import *
import time

def test_A(n):
    for i in range(1, n+1):
        print i
        time.sleep(.5)
   
def process():
    print ‘process…’

d = Casing(test_A, n=10) + Casing(test_A, n=20)
d.onprocess(process, timestep=2)
d.start_thread()
time.sleep(20)
print ’stop’

可以看到上述的代码在一边打印数字的同时,一边显示’process…’字符串。但’process…’字符串的显示与打印是无关的,只是由 Casing 来进行调度的。特别是在挂接 onprocess 时,传入一个 timestep 的参数,它表示间隔多长就调用一次 process 的处理。但这个参数在真正调用 process() 时会被 Casing 去掉。这是需要注意的。

有时是需要这种处理的。比如你在下载,一边做下载,一边显示下载的进度。如果你不需要显示为百分比,那么只要不停地显示一种动画效果就好了。这样不用关心下载了多少之类的信息。因此完全可以象上面一样。如果转到 wxPython 下,那么可以在 process() 中进行界面的显示处理。此时可以使用 wx.CallAfter() 方面来刷新界面。要知道 process() 的处理是放在单独的一个线程中进行的。

有时情况更复杂。我就是需要实现一个进度百分比的显示,那么我必须要知道处理线程执行到什么程度了,然后再通知状态更新线程去显示这个程度,那我该怎么办呢?

1.你可以通过一个可变参数,在启动线程前创建好,然后分别传给处理线程和状态更新线程。这样只要处理线程改变了这个可变参数的值,那么状态更新线程自然就知道值是什么了。

2. 如果你不想自已创建这个可变参数,Casing提供了new_obj()可以创建一个空对象。

例3:

from Casing import *
import time

def test_A(n, var):
    for i in range(1, n+1):
        print i
        var.number = i
        time.sleep(.5)

def process(var):
    print ‘process…’, var.number
   
var = new_obj()

d = Casing(test_A, n=10, var=var) + Casing(test_A, n=20, var=var)
d.onprocess(process, timestep=2, var=var)
d.start_thread()
time.sleep(20)
print ’stop’

这时在执行结果中可以看到process…的后面有一个数字了。这个数字就是test_A()函数修改的。注意上面的参数都使用了字典关键字,象n=10, var=var。python不允许前面使用字典参数,后面却不用。

上一篇只是讲了 Casing 将无参数的方法串起来调用,下面就是将有参数的方法串起来调用。举例为:

import Casing

def test_A(a):
    print a
   
def test_B(a, b):
    print a, b
   
def test_C(a, b, c):
    print a, b, c
   
d = Casing.Casing(test_A, 1) + Casing.Casing(test_B, ‘a’, b=2) + Casing.Casing(test_C, 1, b=2, c=’c')
d.start()

不用太多的解释。可以使用tuple参数,也可以使用字典参数,只要对应上就好。执行结果为:

1
a 2
1 2 c

象前面的问题,如果调用的函数之间有关联怎么办呢?那么可以使用可变参数。为了方便 Casing 提供了一个 new_obj() 的方法,它其实就是创建一个空对象。你可以在上面添加属性,然后用它作为参数来传递。这个就不再演示了。大家自已去试吧。

那么在处理过程中可能会发生一些事件,比如说:抛出异常,执行完毕,这些我如何知道呢?

Casing 允许你注册一些特殊事件的回调函数来进行处理。如:

  • onsuccess()
    当所有函数执行完毕后将调用
  • onexception()
    当出现异常时将调用,如果没有定义则向标准输出打印异常信息
  • onabort()
    当某一函数抛出特殊的AbortException时调时,调用onabort回调函数并退出
  • onprocess()
    用在线程执行时处理进度状态

举例为:

def test_E():
    raise Casing.AbortException

def on_abort():
    print ‘abort…’

d = Casing.Casing(test_A, 1) + Casing.Casing(test_B, ‘a’, b=2) + Casing.Casing(test_E)
d += Casing.Casing(test_C, 1, b=2, c=’c')
d.onabort(on_abort)
d.start()

输出结果为:

1
a 2
abort…

首先演示一下 Casing 将函数串起来执行的能力,同时演示了如何创建 Casing 对象和向这个对象增加新的方法。首先看一段代码:

import Casing

def test_A():
    print ‘A’
   
def test_B():
    print ‘B’
   
def test_C():
    print ‘C’
   
d = Casing.Casing(test_A) + Casing.Casing(test_B) + Casing.Casing(test_C)
d.start()

上面很简单。有三个函数,分别打印出’A', ‘B’, ‘C’。然后通过Casing将它们分别包装起来,创建了三个 Casing 对象。然后将这个三个对象加起来生成最终的一个 Casing 对象。然后调用 Casing 对象的 start() 方法去执行。可以看到结果为:

A
B
C

那么 Casing 的生成还可以在创建了第一个对象后使用 += 的方法加追加函数,如:

d = Casing.Casing(test_A)
d += Casing.Casing(test_B)
d += Casing.Casing(test_C)
d.start()

执行效果同上面。

还可以通过 Casing 的 append 方法来追加函数,如:

d = Casing.Casing(test_A)
d.append(test_B)
d.append(test_C)
d.start()

更新的 Casing 版本还可以在允许产生一个空的 Casing  对象,如:

d = Casing.Casing()
d.append(test_A)
d.append(test_B)
d.append(test_C)
d.start()

刚开始时 d 中没有添加任何函数。

通过上面的执行可以看到 Casing 中的函数是按照添加的顺序来执行的。要注意 Casing 在调用其中的函数时不会将上一个函数的返回值传给下一个函数。因此如果想在函数间传递参数的话,可以使用可变参数来传递。这一点我们在后面会讲到。

Casing 是我写的一个模块,它将用于 NewEdit 中,主要的用途是为了实现多线程处理。那么在开发这一模块时我面临的需求是:

  • 一个完整的处理是由几个分离的步骤完成,我需要按顺序依次对它们进行调用
  • 我需要将这个完整处理放在一个线程中运行
  • 在运行同时我需要有一个进度处理的线程表示它正在工作
  • 处理线程结束时需要通知进度处理线程退出,即需要一定的同步功能

wxPython 做线程处理我已经摸索了一段时间了。说复杂也复杂,说简单也简单。从一般的编程思维方式转到线程编程可能有些不适应。象我们习惯在一段程序中加上一些信息的提示,表示过程的执行状态。如果只是向stdout输出,没有什么问题。但如果在 GUI 中更新GUI,在线程中可能就会有问题,可能造成系统挂起。GUI执行的线程一般称为主线程,其它线程为子线程。为了在子线程中更新GUI界面,需要转到主线程中去执行。这样,在 wxPython 中,或者使用它提供的方便的 wx.CallAfter() 方法,或者自已定义一个事件处理,然后通过发送自定义消息的方法来转到主线程去执行界面更新的操作。这些在我以前的 Blog 中也讲过了。

那么线程知道该如何办了,是不是就容易处理了呢?还不是。如果协调子线程与主线程,这是很麻烦的事。可以在需要更新界面时使用 wx.CallAfter() 来方便处理,但就是最基本的。创建子线程这也是一块工作。一般可以从 threading.Thread 派生自已的线程类。如果再有处理进度的功能,那么如果去通知它的状态也是一个问题。状态显示,一种是由子线程主动发出,一种可以是创建另一个并行的子线程专门用来显示状态,它只是显示运行中的状态,然后与处理子线程之间进行同步,当处理子线程执行完毕,需要通知状态更新线程结束。再有就是由处理子线程改变状态,由状态更新线程来反映出来。总之方式很多。那么这些也都需要编码。再有,一个处理分为几个函数调用。当然可以写成一个通一的调用函数来处理。但有没有简单的方法可以不写这个统一的调用方法而是实现函数调用的组合呢?这些想法就构成了我上面的需求。

于是我创建了这个名为 Casing 的模块,它由几个线程类和调度类组成。原来不叫这个名字,叫Deffer,但查字典没这个单词。后来才知道是 Defer ,原来是我拼写错误。Defer 在 Twisted中有,表示延迟执行。在我新学的 MochiKit 中也有。但后来想我写的这个模块并不是为了延迟执行,而是为了将多个函数进行组合与封装,创建成线程方式,因此这个名字并不合适。于是查了查字典就叫了 Casing 了。

关于它的功能和使用,我会不断地写出来。如果有兴趣可以下载使用。我也不知道有没有类似的其它 Python 模块具有它的功能。

代码已经放在 NewEdit 中去了。modules/Casing.py 就是。你可以从 svn 中更新。如果不想下载或更新 NewEdit ,那么可以从这里下载

这个模块刚开发出来,不能保证不做修改。

2005年11月29日

下面是与 Python, cpug, CZUG, 啄木鸟社区 社区相关的邮件列集合,看一看。相关的wiki页面

 CPUG系列组织讨论群

  •  python.cn — 主列表的group 镜像讨论群

    • 强烈建议与 python-chinese@lists.python.cn 同时订阅,以免服务中断

  •  CPUG — 中国 Python 用户组讨论群

  •  BPUG — 中国北京 Python 用户组讨论群

  •  SPUG — 长三角地区 Python 用户组讨论群

  •  CPUG-eastchina — 东南中国 Python 用户组讨论群

Zope技术系列讨论群

CPUG系列项目相关讨论群

2005年11月28日

PycURL 是一个C语言写的 libcurlPython 绑定库。libcurl 是一个自由的,并且容易使用的用在客户端的 URL 传输库。它的功能很强大,在 PycURL  的主页上介绍的支持的功能有:

supporting FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE and LDAP. libcurl supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploading, kerberos, HTTP form based upload, proxies, cookies, user+password authentication, file transfer resume, http proxy tunneling and more!

那一大堆的协议已经让人惊喜了,特别是还有代理服务器和用户认证之类的功能。这个库相对于 urllib2 来说,它不是纯 Python 的,它是一个 C 库,但因此速度更快,但它不是很 pythonic ,学起来有些复杂。它在多种平台下都有移植,象 Linux , Mac, Windows, 和多种Unix。

我安装了一个,并且测试了一小段代码,是有些复杂,代码如下:

        import pycurl
        c =
pycurl.Curl()
        c.setopt(pycurl.URL, ‘http://feeds.feedburner.com/solidot’)
        import StringIO
        b = StringIO.StringIO()
        c.setopt(pycurl.WRITEFUNCTION, b.write)
        c.setopt(pycurl.FOLLOWLOCATION, 1)
        c.setopt(pycurl.MAXREDIRS, 5)
#        c.setopt(pycurl.PROXY, ‘http://11.11.11.11:8080′)
#        c.setopt(pycurl.PROXYUSERPWD, ‘aaa:aaa’)
        c.perform()
        print b.getvalue()

上述代码将会把奇客(Solidot)的RSS抓下来。如果有代理服务器,那么修改一下注释的两行即可。在 PycURL 的主页上还有一个多线程抓取的例子,有兴趣的可以看一看。

2005年11月26日

django 邮件列表中发现 new admin 终于正式合并到主干上去了。不过我的确对于 new admin 没有太多的了解。于是来到这个网址了解了一下,并且从 svn 中更新了最新的代码安装了上去。从了解上我得到主要是对 admin 的代码进行了重构,目的是为了让用户更方便的对 admin 实现重用。同时将 admin 的模板进行重新处理,变成了一系列的模板和 tag 的使用。这样使得用户定制起来更方便。更具体的改变有:

  • View
    主要是 View 中的几个方法的建壮性和调用方式都进行了修改。对于 Manipulators 和 inline editing 功能有很大的增强。比如Manipulators中增加了follow关联字典,用于控制字段间的相关性。
  • Template
    增加了 include 标签。改进了错误输出。同时使用自定义新的标签更容易。
  • Admin
    将模板转换为分离的模板,更容易被定制。因为 admin 在查找模板时会先查找应用下的对应模板文件,然后是 django 系统下的模板文件。因此你就可以在你的应用中对 admin 模板进行客户化了。

我安装完新版的 django 之后,启动server,然后运行以前的 polls 界面。对了,同时我更新了 django 中 i18n 的汉化部分,有些单词的确不好翻译,先凑和吧。打开 admin 界面后看到增加了 core ,它可以增加新的站点。同时更多的字符串被改为了国际化的处理。polls程序没有什么影响,看来兼容性没有什么问题。至少我是这样的。

除了这些变化,还看到一则blog说关于在伦敦的 django 展示的内容已经做好了。是一个 pdf ,大家可以下载看一看。是关于 django 项目的一个整体介绍。

2005年11月25日

在前面的 Blog 中我谈到如何使用 zipfile 模块,并且完成了一个 ZFile 的模块,它可以方便地进行压缩和解压。今天又继续将其完善了。大家可以到>>>这里<<<下载。

那么主要的变化是:

  • 增加命令行处理功能
  • 可以在命令行处理多个文件或目录
  • 可以指定生成文件列表清单
  • 可以通过一个文件列表清单来生成压缩文件
  • 可以设定写入文件清单时的起始目录

在命令行下运行:

Python zfile.py

Usage zfile.py [options] zipfilename directories|filenames
      zfile.py -i listfile zipfilename

-V      Show version
-h      Show usage
-l      Create a listinfo file
-i      Input file list
-b      Specify base directory

功能基本上很清楚了。比如我有一个目录结构:

———test\
              |—–mixin\
                          |——-trunk\
                                        |———a.c
                                        |———b.c
                          |——-branches
             |——a.txt
             |——zfile.py

当前目录为test。如果我想压缩mixin,则执行:

d:\test> python zfile.py -l files.txt t.zip mixin

则会生成一个t.zip 文件,同时因为指定了 -l 参数,因此会生成一个文件列表。打开files.txt你会看到:

mixin/trunk/a.c
mixin/trunk/b.c

然后你可以使用这个清单文件再生成zip文件:

d:\test> python zfile.py -i files.txt t1.zip

还可以指定起始目录,使用 -b 参数(此时不能使用 -i ):

d:\test> python zfile.py -l files.txt -b mixin t.zip mixin

此时的效果相当于进入了 mixin 目录后再对工作目录进行压缩。文件清单就变成了:

trunk/a.c
trunk/b.c

目前将用在 NewEdit 的包生成中。因为一个 NewEdit  可能是wizard, script, plugin, acp等文件的集成,主要是方便大家使用,不用分别下载。但 NewEdit 中的上述内容是在不同的目录下存放的,生成这样一个包如果每次手工做还是很麻烦的。因此才想到写这么一个小工具。

我的想法是,第一次先找一个干净的目录,然后按目录结构将相关的内容拷贝到这个目录下,然后使用 ZFile 来生成压缩文件,同时生成一个文件清单。因为这个文件清单带有相对路径。因此下次有改动时,我只要先修改下清单文件(如果文件有增加或减少),然后在 NewEdit 的起始目录下运行压缩就可以了。虽然 NewEdit 的目录下有许多的文件,但根据文件清单我会只挑出需要的文件进行压缩,不用再象第一次一样手工来做的。因此只有第一次可能麻烦一些,以后会省很大的事。甚至还可以考虑做这么一个 GUI 的小工具来做生成清单,压缩这样的事。

2005年11月24日

最近在做一个安全方面的东西,需要对一个ZMK(区域主密钥)进行处理。原来的平台都是 Java 的,要求是录入三段明文,然后生成一个ZMK,再使用主密钥(LMK)进行DES算法加密保存。在网上找到一个叫 jpos 的包,东西很多,算法也可以参考。最后找到了相关的算法。简单来说就是录入三段明文,如果密钥长为64位,则采用单DES算法,每段明文为16个字符。得到三段明文后,将其视为一个16进制表示的串,然后转为8个字节。这样将三段8字节,位位异或,又得到一个8字节,这就是最终的ZMK明文。再将这8字节转为16进制表示保存在文件中即可。如果密钥长是128位,则每段明文为32个字符,其它类似扩大。加密算法使用3DES。Java下的程序已经做好,我原本想在命令行录入得了,做界面太麻烦。头说做个图形的给别人看,密钥文件可以手工传,即然这样,那我就使用 Python 来开发,但要保证 Java 程序可以读出加密后的密钥才行。这样我必须保证 Python 程序的主密钥与Java一致,并且采用相同的处理方法和加密算法。

于是我先使用 NewEdit 中的 EasyApp 生成一个框架,这个框架已经生成好了菜单,工具条等内容。然后我将工具条去掉,修改了资源文件(resource.py),再改一下MainFrame.py,去掉工具条的生成调用。这样基本架子就搭好了。然后在资源文件中对相应的菜单增加事件处理。在我这个需求中就一个功能菜单,就是提供一个可以录入三段明文的对话框。要求不高。于是在事件处理函数中使用了EasyGui来生成界面,很简单。这样界面就生成好了。剩下的就是将三段明文进行处理,然后保存到文件中。

主要的处理代码(MainFrame.py)如下:

#coding=gbk
import wx
import resource
from EasyGui import EasyActions
from EasyGui import EasyMenu

class MainFrame(wx.Frame):

    def __init__(self, title=”):
        wx.Frame.__init__(self, None, -1, title, size=(400, 400))

        alist = EasyActions.makeactions(resource.actions)
        EasyMenu.makemenubar(self, alist, resource.menubar)
#        EasyMenu.maketoolbar(self, alist, resource.toolbar)

        self.init()

        self.Bind(wx.EVT_CLOSE, self.OnClose)

    def init(self):
        """add your initialization code"""
        pass

    def OnClose(self, event):
        event.Skip()

    def OnExit(self, event):
        self.Close()

    #add other event handler function
   
    def OnSecZMK(self, event):
        from EasyGui import EasyDialog
       
        dialog = [
            ('string', 'k1', u'', u'第一段密钥', None),
            ('string', 'k2', u'', u'第二段密钥', None),
            ('string', 'k3', u'', u'第三段密钥', None),
            ]
        easy = EasyDialog.EasyDialog(None, u’区域主密钥录入’, dialog)
        values = None
        if easy.ShowModal() == wx.ID_OK:
            values = easy.GetValue()
        easy.Destroy()
        if values:
            k1, k2, k3 = hex2byte(values['k1']), hex2byte(values['k1']), hex2byte(values['k1'])
            import binascii
            k = xor(binascii.a2b_hex(k1), binascii.a2b_hex(k2), binascii.a2b_hex(k3))
            from Crypto.Cipher import DES
            lmk = DES.new(binascii.a2b_hex(‘0000000000000000′), DES.MODE_ECB)
            zmk = lmk.encrypt(k)
            file(‘zmkfile.txt’, ‘wb’).write(binascii.b2a_hex(zmk))
            wx.MessageDialog(None, u’处理成功!’, u’消息’, wx.OK | wx.ICON_INFORMATION).ShowModal()

def hex2byte(hexstring):
    s = []
    for ch in hexstring:
        if ch.lower() in ‘01234567890abcdef’:
            s.append(ch)
        else:
            s.append(‘f’)
    return ”.join(s)
           
def xor(*args):
    arg1 = args[0]
    for arg in args[1:]:
        s = []
        for i, ch in enumerate(arg):
            s.append(chr(ord(arg1[i]) ^ ord(ch)))
        arg1 = ”.join(s)
    return arg1
           
def test_hex2byte():
    assert hex2byte("1234567890abcdef") == "1234567890abcdef"
    assert hex2byte("1234567890ghijkl") == "1234567890ffffff"
   
def test_xor():
    assert xor(‘\x00\x00\x00\x00\x00\x00′, ‘\x00\x00\x00\x00\x00\x00′) == ‘\x00\x00\x00\x00\x00\x00′
    assert xor(‘\x01\x01\x01\x01\x01\x01′, ‘\x00\x00\x00\x00\x00\x00′) == ‘\x01\x01\x01\x01\x01\x01′

上面代码中,16个’0′那个是与 java 程序中一致的16进制字符串表示的主密钥。16进制字符串就是将每个字节转换为两个字节,第一个字节为高4位的ascii表示,第二个字节为低4位的ascii表示。4bit的范围是从0-15,分别可以转换为’0′-’9”A’-'F’这16个字符。’A'-’F'也可以使用’a'-’f'来表示。

hex2byte函数是将一个16位的16进制字符串压缩为一个8字节的字符串。因此就是两个字节转换成一个字节。binascii有方便的方法,但在这里可能不适用。binascii.a2b_hex()这个方法要求字符串中的字符必须是对应的16进制字符,不能有其它的字符,而实际情况下允许录入非16进制字符。我看了jpos中的算法,它使用Character.digit()方法,这个方法对于非16进制字符会返回-1,因此不会报错,而-1正好对应为’F'。因此我自已写了一个hex2byte()的函数,用来达到相同的效果。

k1,k2,k3是将16个字符压缩为8个字节后的三个明文。然后将它们异或。于是又自已写了一个xor()函数。对字符串进行循环,然后将每个字节转为整数再异或,将结果再转为字符串,最后将8个字符串拼成一个串返回。

接下来就是使用DES算法对ZMK加密。为了使用DES算法,我从网上下载了 pycrypto 模块。但在它的主页上由于美国对安全出口的限制,它上面不提供在Windows下的二进制包下载,只提供源码下载(这还是由于限制放宽的缘故)。于是它还提供了另外一个链接,有人做好了二进制的包。下载后安装即可,是一堆的pyd文件。

使用DES算法很简单。导入 Crypto的Cipher。首先使用DES.new()方法来生成一个加密对象。它需要一个key的字符串和加密算法。这里key我设为16个’0′,然后使用binascii.a2b_hex()压缩为8字节。加密算法一般都是DES.MODE_ECB。这样就得到一个加密对象,然后调用它的encrypt()和decrypt()进行加密和解密即可。

对ZMK加密后,再使用binascii.b2a_hex()转换为16进制字符串,然后再写到文件中去。这样就处理完毕了。

经过与Java程序的测试应该是没有问题了。

上面 test 开头的函数是我写的测试函数,我用 nose 进行了测试:

nosetests -o MainFrame.py

这个 nose 还真是很好用。以前不喜欢用主要是嫌麻烦,现在倒是简单,可以多用一用。