Stonelee's Blog

如无必要,勿增实体

基于签名的数据传输

分享到: 更多

来自PyCon上的演讲:

Hidden Treasures of the Standard Library

发送方:

  • pickle序列化
  • 生成md5签名
  • 生成包,格式”签名\n数据长度\n数据”

数据传输

接收方:

  • 解析数据包
  • 根据解析出来的数据生成签名
  • 将新生成的签名与数据包中的签名对比,如果正确说明无篡改, 否则抛出异常
  • pickle反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/usr/bin/env python
# -*- coding: utf-8 -*-

__revision__ = '0.1'
__author__ = 'lxd'

import hmac
from StringIO import StringIO
import pickle

def create_signature(key, msg):
    h = hmac.new(key)
    h.update(msg)
    return h.hexdigest()

##########send##########
def send_buffer(signature, msg):
    #模拟数据传输
    return StringIO('%s\n%d\n%s' %(signature, len(msg), msg))

def send_msg(key, message):
    msg = pickle.dumps(message)
    signature = create_signature(key, msg)
    return send_buffer(signature, msg)

##########get##########
def parse_buffer(buffer):
    signature = buffer.readline().rstrip()#strip \n
    msg_length = int(buffer.readline())
    msg = buffer.read(msg_length)
    return signature, msg

def check_signature(signature, key, msg):
    new_signature = create_signature(key, msg)
    if new_signature == signature:
        return msg
    else:
        raise ValueError('signature error')

def get_msg(key, buffer):
    signature, msg = parse_buffer(buffer)
    message = check_signature(signature, key, msg)
    return pickle.loads(message)

msg = 'some_message'
key = 'some_key'
buffer = send_msg(key, msg)
print get_msg(key, buffer)

你可能所不知道的python中有用的特性

分享到: 更多

来自stackoverflow中python标签下按投票数排列第一位的问题: Hidden features of Python

题记:我们没有黑魔法,我们是可读的。

Python is a dynamically and strongly typed programming language that encourages readability.

1.链式操作符,要注意执行顺序问题,方法是加括号。

1
2
3
4
5
>>> x = 5
>>> 10 < x < 20
False
>>> x < 10 < x*10 < 100
True

2.debug regex

1
2
3
4
5
6
7
8
9
10
>>> re.compile("""
 ^              # start of a line
 \[font         # the font tag
 (?:=(?P<size>  # optional [font=+size]
 [-+][0-9]{1,2} # size specification
 ))?
 \]             # end of tag
 (.*?)          # text between the tags
 \[/font\]      # end of the tag
 """, re.DEBUG|re.VERBOSE|re.DOTALL)

另附name match:

1
2
3
4
>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'

3.generator object

可以节省内存,但是只能使用一次

4.同时yield列表中的项和序号。

1
2
>>> a = ['a', 'b', 'c', 'd', 'e']
>>> for index, item in enumerate(a): print index, item

5.iter() can take a callable argument

1
2
3
def seek_next_line(f):
    for c in iter(lambda: f.read(1),'\n'):
        pass

6.Decorators

下面的评论中有人说这已经不算太hidden了

7.yield中send的使用

8.

1
2
3
a = [1,2,3,4,5]
>>> a[::2]  # iterate over the whole list in 2-increments
[1,3,5]

反转

1
2
>>> a[::-1]
[5,4,3,2,1]

对比

1
2
>>> list(reversed(range(4)))#这种方法返回的是iterator
[3, 2, 1, 0]

9.默认参数可能会变

1
2
3
4
5
6
7
8
9
10
>>> def foo(x=[]):
...     x.append(1)
...     print x
...
>>> foo()
[1]
>>> foo()
[1, 1]
>>> foo()
[1, 1, 1]

Instead, you should use a sentinel value denoting “not given” and replace with the mutable you’d like as default:

1
2
3
4
5
6
7
8
9
>>> def foo(x=None):
...     if x is None:
...         x = []
...     x.append(1)
...     print x
>>> foo()
[1]
>>> foo()
[1]

默认参数不过是函数的一个属性foo.func_defaults而已,因此对于list类型的可能会变动

10.defaultdict可以增强dict

1
2
3
4
5
6
7
>>> s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
>>> d = defaultdict(list)
>>> for k, v in s:
...     d[k].append(v)
...
>>> d.items()
[('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]

相当于

1
2
3
>>> d = {}
>>> for k, v in s:
...     d.setdefault(k, []).append(v)

对于列表中的每个key,如果该defaultdict中不存在,会使用default_factory来创建该值

11.for..else

对于丑陋的代码

1
2
3
4
5
6
7
found = False
for i in foo:
    if i == 0:
        found = True
        break
if not found:
    print("i was never 0")

可以使用下面代码来替代

1
2
3
4
5
for i in foo:
    if i == 0:
        break
else:
    print("i was never 0")

else在for循环从来没有break时调用,有人提议这个关键字else实际上用finally更合适。

12.值替换,“,”隐含的构成了一个tuple

1
2
3
4
5
6
7
8
>>> a = 10
>>> b = 5
>>> a, b
(10, 5)

>>> a, b = b, a
>>> a, b
(5, 10)

菜鸟制作电子印章经验小结

分享到: 更多

在这个神奇的国度,有时候需要制作电子印章来应急。 本人是PS菜鸟,现学现卖,将电子印章的制作方案总结如下。

特别注明:本文仅用于研究和学习,禁止用于非法用途或其他目的,本人对此不负任何法律责任,一切违法后果与本人无关。

1.软件印章大师制作印章

新建一个圆形章,根据要制作的印章样式调整文字大小和位置,颜色选红色,默认生成的是蓝色印章。导出为bmp文件。

2.Photoshop做旧

  • 打开之前的bmp文件
  • alt+鼠标滚轮可以缩放图片
  • 用“魔棒”工具选中文字和印章部分,按住shift可以多选。
  • 滤镜-画笔描边-喷溅,增加边缘的毛刺,夸张点即可,缩小后才能显示出效果来
  • 滤镜-模糊-高斯模糊,明显减淡颜色

其余技巧:

  • 油漆桶,填充比较暗比较浅的红色,显示做旧的效果。
  • 滤镜-纹理-颗粒-柔和,适当加点小点
  • 滤镜-锐化-智能锐化,适当加点小白点
  • 如果觉得白点太均匀了,用铅笔自己描写点
  • 减淡工具,减淡半边

3.导出gif白色为透明

文件-存储为Web和设备所用格式-GIF-选中透明度-选中颜色表右下角的白色小块-将选中的颜色映射为透明-存储

4.word中插入改图片

插入图片-文字环绕-衬于文字下方,将图片大小设为4.2厘米,适当旋转,放到合适的文档位置。

制作电子印章和一般的作图不一样,印章越旧、越模糊,显得越真实。

python调用VBA心得

分享到: 更多

接上篇,在继续优化过程中发现几个问题,记述如下:

更改表格某行的字体,如果此表格中有合并单元格,会报错:

1
"无法访问此集合中单独的行,因为表格有纵向合并的单元格。"

经研究,不能使用如下语句

1
word.Selection.Tables[0].Rows[0]

解决方案:

1
2
3
word.Selection.Tables[0].Cell(1,1).Select()
word.Selection.SelectRow()
word.Selectin.Range.Font.Size=18

很多操作都可以用“先选择再操作”的方法来间接实现。

但是要注意,使用Select前后一定要保存好鼠标的位置

1
pos = word.Selection.Range.Start

在操作完后要恢复鼠标。

1
word.Selection.SetRange(pos, pos)

如果想使用constants里的word常量,应该使用makepy

1
word = win32com.client.gencache.EnsureDispatch('Word.Application')

然后就可以像这样调用了

1
word.Selection.InsertBreak(constants.wdPageBreak)

这里有篇讲COM高级用法的文章,值得一看

Selection返回的貌似是刚操作的元素

Tables(1)和Tables[ 0]是一样的

对于大的文件,会在执行VBA的过程中显示“拼写或语法错误太多”这样的对话框,这会影响脚本的正常运行,解决方法是在打开文件后

1
2
word.ActiveDocument.GrammarChecked = True
word.ActiveDocument.SpellingChecked = True

性能优化

在文档很大的情况下,word的自动分页等可视化步骤会极大地减慢速度,因此需要将不必要的功能暂时去掉。

1
2
3
word.ActiveDocument.View = constants.wdNormalView
word.ActiveDocument.Application.Options.Pagination = False
word.ActiveDocument.Application.ScreenUpdating = False

原来完整格式化一段表格需要100多s,现在只需58s了,有意思的是,如果将word的Visible设为False,速度反而有所下降。估计是操作系统自动将后台操作程序的CPU和内存使用优先级下调的缘故。

多Excel文件导入到一个word

分享到: 更多

需求

多个Excel分布在不同文件夹下,每个Excel又有多个sheet表,现在需要把这些Excel一起汇总到一个word里。

探索

直接复制粘贴既麻烦又无聊,上网搜所相关方法竟然没有,难道我这个需求很少见吗?找到一个国外软件ExcelConverter(售价$49.9),发现转换后货币格式的文字完全变成乱码。还是自己写吧,编码测试花了两个小时搞定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Last Update:

'''covert many excel files to one word file
'''

__revision__ = '0.1'
__author__ = 'lxd'

from win32com.client import Dispatch
import os

wdPageBreak = 7

def isExcelFile(name):
    return os.path.isfile(name) and os.path.splitext(name)[1] == '.xls'

def filesInDir(dir):
    #generate absolute paths of files in a directory
    for root, dirs, files in os.walk(dir):
        for file in files:
            yield os.path.join(root, file)

def findExcels(names):
    for name in names:
        if isExcelFile(name):
            yield name
        else:
            for file in filesInDir(name):
                if isExcelFile(file):
                    yield file

class ExcelToWord(object):
    def __init__(self, excelNames, wordName):
        self._createExcel()
        self.excelNames = findExcels(excelNames)

        self._createWord()
        self.word.Documents.Open(wordName)

    def _createExcel(self):
        self.excel = Dispatch('Excel.Application')
        #self.excel.Visible = True
        self.excel.DisplayAlerts = False

    def _createWord(self):
        self.word = Dispatch('Word.Application')
        self.word.Visible = True
        self.word.DisplayAlerts = False

    def process(self):
        for excelName in self.excelNames:
            print 'coverting', excelName
            self.excel.Workbooks.Open(excelName)
            for i in range(self.excel.Worksheets.count):
                self.excel.Worksheets(i+1).Select()#start from 1
                self._copy()
                self._paste()
            self.excel.Workbooks.Close()
        self.excel.Quit()
        print 'covert is over'

    def _copy(self):
        # select all contents in one sheet and copy
        self.excel.Cells.Select()
        self.excel.Selection.Copy()

    def _paste(self):
        #paste to one word file and add a page break
        self.word.Selection.Paste()
        #self.word.Selection.TypeParagraph()
        self.word.Selection.InsertBreak(wdPageBreak)

if __name__ == '__main__':
    excelNames = [
                u'c:\\users\\lxd\\desktop\\文档',
                u'c:\\users\\lxd\\desktop\\汇总.xls',]
    wordName = r'c:\users\lxd\desktop\1.doc'
    e = ExcelToWord(excelNames, wordName)
    e.process()

Yield使用心得

分享到: 更多

看高手的源码经常可以看到yield,特地查了一下用法,总结如下:

包含yield表达式的函数是特殊的函数,叫做生成器函数(generator function),被调用时将返回一个迭代器(iterator),调用时可以使用next或send(msg)。它的用法与return相似,区别在于它会记住上次迭代的状态,继续执行。

send(msg)与next()的区别在于send可以传递参数给yield表达式,这时传递的参数会作为yield表达式的值,而yield的参数是返回给调用者的值。初始调用时必须先next()或send(None),否则会报错。

举个例子:

首先生成一个迭代器f,f.next()会使生成器函数执行到yield,生成一个值然后挂起。

然后f.next()或f.send(msg)会在生成器函数内部返回值,执行到下一个yield,生成值后挂起

然后f.next()或f.send(msg)会在生成器函数内部返回值,意图执行到下一个yield,但是后面没有yield了,所以抛出异常。

使用yield可以有效简化代码,并减少空间浪费。

举个简单例子:列表中的每个元素+1

传统写法:

1
2
3
4
5
def addlist(alist):
    r = []
    for i in alist:
        r.append(i+1)
    return r

yield写法:

1
2
3
def addlist(alist):
    for i in alist:
        yield i + 1

当然对于这种简单的问题:

1
[i+1 for i in alist]

参考资料:

GAE中使用自定义filter,实现文字截断

分享到: 更多

来自于 http://daily.profeth.de/2008/04/using-custom-django-template-helpers.html,原网站被墙,故记录于此

目录结构:

templatefilters.py实现只显示固定数目文字的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
from google.appengine.ext import webapp
register = webapp.template.create_template_register()

def truncate(value,maxsize,stopper = '...'):
    """ truncates a string to a given maximum
        size and appends the stopper if needed """
    stoplen = len(stopper)
    if len(value) > maxsize and maxsize > stoplen:
       return value[:(maxsize-stoplen)] + stopper
    else:
       return value[:maxsize]

register.filter(truncate)

base.py中注册此filter

1
webapp.template.register_template_library('common.templatefilters')

html中可以使用该filter了

1
{ { somevalue|truncate:20 } }

基于twisted的SSL服务器开发

分享到: 更多

基于OpenSSL建立自己的CA和签发证书

参考自 http://rhythm-zju.blog.163.com/blog/static/310042008015115718637/

建立CA目录

1
2
3
$ mkdir -p ./demoCA/{private,newcerts}
$ touch ./demoCA/index.txt
$ echo 01 > ./demoCA/serial

生成CA证书的RSA密钥

1
$ openssl genrsa -des3 -out ./demoCA/private/cakey.pem 2048

生成 CA 证书请求

1
$ openssl req -new -days 365 -key ./demoCA/private/cakey.pem -out careq.pem

对 CA 证书请求进行自签名

1
$ openssl ca -selfsign -in careq.pem -out cacert.pem

这样我们就建立了一个私有根CA,包括密钥文件cakey.pem和由此签名的CA根证书文件cacert.pem

签发证书:

生成客户密钥:

1
$ openssl genrsa -des3 -out userkey.pem

生成客户证书:

1
$ openssl req -new -days 365 -key userkey.pem -out userreq.pem

签发客户证书:

1
$ openssl ca -in userreq.pem -out usercert.pem

注意CA证书和客户证书的国家组织等信息要对应相同,人名邮箱之类的不能相同 我在ubuntu上生成的证书,放到windows上也可以使用。

基于SSL的twisted服务器:

参考自 http://twistedmatrix.com/documents/current/core/howto/ssl.html 只要注意将生成的key和证书文件改名后发到相应的文件夹下,然后照搬上面的例子就可以了。

todo:基于SSL的客户端数据发送程序实现

项目需要客户端SSL验证后不断向服务器发送数据,参考 这篇文档发现只能发送一条数据,貌似需要自己开发SSL连接,留下记号,等待研究~

Hg版本管理最优实践

分享到: 更多

使用hg不短时间了,但是一直作为个备份工具来用,今天看到几位牛人的hg实践,很受启发,总结如下

上图来源于 A successful Git branching model,利用git来实践基于branch/merge版本管理。

首先整个项目要有两个主分支:master和develop,其中master是产品发布的主分支,要做到足够稳定,不能轻易修改;而develop是程序员用的开发分支,每日构建要在这个分支上做,也要做到稳定。开发完成后merge到master上并打上版本号tag。

三个辅助分支:release、feature和hotfix。

一个新的功能就是一个新的feature分支。

  • May branch off from: develop
  • Must merge back into: develop
  • Branch naming convention: anything except master, develop, release-*, or hotfix-*

功能完成,准备发布,这时启用release分支,来修改小bug,为发布做准备

  • May branch off from: develop
  • Must merge back into: develop and master
  • Branch naming convention: release-*

已上线的产品修改bug,启用hotfix分支

  • May branch off from: master
  • Must merge back into: develop and master
  • Branch naming convention: hotfix-*

应该说整个流程还是比较繁琐的,该文档是基于git的,git flow是一个比较靠谱的简化流程工具,推荐使用。

我用的是hg,发现也有一个类似的简化工具:hg flow,试用了一下感觉不错~

使用方法: 在没有版本管理的工程中

1
2
hg init#建立hg仓库
hg flow init#初始化hg flow,自动生成default(master)和develop分支

要新增功能的时候,name随意

1
hg flow feature start <name>#新建功能分支,会自动切换到该分支上

新增功能

使用TortoiseHg的 Repository Explorer查看目前所处分支状况, 使用TortoiseHg的commit提交功能

功能完成的时候

1
hg flow feature finish <name>#功能完成后使用,会自动merge到develop分支上

如果要添加新的功能:重复上两个步骤

如果同时开了多个功能,切换不同功能开发的时候要先切换到该功能的分支

1
hg flow feature change <name>#切换当前分支

到达预定的功能发布点,name:release-版本号

1
2
hg flow release start <name>
hg flow release finish <name>

只在修改小型bug时使用,如果在hotfix上新建feature的话会导致直接从develop上开分支,hotfix的更改会暂时隐藏,只有在hotfix finish的时候才能merge。 name:hotfix-版本号.hotfix号

1
2
hg flow hotfix start <name>
hg flow hotfix finish <name>

feature和release要一块使用 hotfix自己单独使用,不需要再release了

Orbited中最好不要使用demjson,会导致twisted报错

分享到: 更多

Orbited是一个非常好用的推(comet)服务器,它内置了twisted作为异步服务器,集成了MorbidQ来作为消息队列服务器,对于服务端直接推数据到客户端的应用参考 这篇文档就可以了。

实际编码中问题来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class DataProducer(StompClientFactory):
    def recv_connected(self, msg):
        print 'Connected; producing data'
        self.timer = LoopingCall(self.send_data)
        self.timer.start(1)

    def recv_message(self,msg):
        pass

    def send_data(self):
        data = {'type':'num', 'num':random()}
        self.send(CHANNEL_NAME, json.encode(data))

def main():
    reactor.connectTCP('localhost', 61613, DataProducer())
    reactor.run()

执行程序的话会报错:

1
TypeError:Data must not be unicode

看twisted源码:

1
2
if isinstance(data, unicode): # no, really, I mean it
    raise TypeError("Data must not be unicode")

看注释的意思,好像作者非常强调这个类型啊,不让改就不改呗

再看orbited.json源码: 提供了cjson、simplejson、demjson的调用

在我本机上实际上调用的是demjson,它序列化的对象是unicode类型的,而我常用的json.dumps()返回的对象是str类型,符合twisted的需要,因此直接使用后者即可。

关于这个bug我已经 提交了,Orbited开发者很活跃,应该很快就会有回应。