PyQt: The Garbage Collection

最近在写一个PyQt程序的时候,遇到一个奇怪的现象,slot或者process总是奇怪地消失了,但是调试信息里什么也不会提示,经过艰苦的
排查后,发现了如下的问题:

参考:

  1. http://stackoverflow.com/questions/20035604/how-to-prevent-python-garbage-collection-for-anonymous-objects
  2. http://ralsina.me/weblog/posts/BB990.html
  3. http://stackoverflow.com/questions/31286855/pyqt-drops-all-of-signals-and-slots-after-using-chained-invoke-like-pyobj-setupu
  4. http://stackoverflow.com/questions/17211797/does-pyqt4-signal-connect-keep-objects-live

ByteCode Instrumentation

Bytecode Instrumentation 定义

我们首先来看看,什么叫“Instrumentation”?Instrumentation这个词有很多意思,在维基百科中,它是这样解释的:

Instrumentation is the use of measuring instruments to monitor and control a process. It is the art and science of measurement and control of process variables within a production, laboratory, or manufacturing area.

似乎中文中没有一个合适的词来描述,意思就是“使用计量器具去监控和控制一个流程”就可以被叫做“Instrumentation”。比如下面这个很
直观的器具就被用来做Instrumentation了。

Instrumentation

什么是ByteCode Instrumentation

Bytecode instrumentation (BCI) is a technique in which bytecode in injected directly into a Java class to achieve some purpose that the class did not originally support.

这是我在网上[2](http://www.ibm.com/developerworks/cn/java/j-rtm2/index.html)看到的一个定义,里面对“bytecode instrumentation”的翻译就是“字节码插装”,但是这里有个缺陷就是,针对字节码的插装,并不只限于Java语言,其他基于字节码的语言(比如.Net/Python)也是可以插装的。

而且可以进一步延伸的是,除了有字节码插装(bytecode instrumentation)之外,也有源代码级别的插装(sourcecode instrumentation)。他们的使用场景是不一样的,比如字节码插装主要发生在程序运行时,当然编译时也是可以字节码插装的。而源代码级别的插装主要是发生在编译时。

参考:

  1. http://www.ibm.com/developerworks/cn/java/j-rtm2/index.html
  2. https://technoga.wordpress.com/2012/12/01/what-is-bytecode-instrumentation/#2
  3. https://github.com/neuroo/equip
  4. http://security.coverity.com/blog/2014/Nov/understanding-python-bytecode.html

Python ByteCode in Details

First Look of bytecode

我们先来看一个代码:

class A:
def __init__(self):
pass
def __repr__(self):
return 'A()'
a = A()
print a

很容易理解,首先声明一个old style的类A,里头声明了2个方法,然后把A初始化成对象并赋值给a,最后打印a对象。

我们可以把这段代码编译成python code对象:

co2 = compile("""class A:
def __init__(self):
pass
def __repr__(self):
return 'A()'
a = A()
print a""", "class-A-input", "exec")

co2
Out[114]: <code object <module> at 00000000040B8630, file "class-A-input", line 1>

然后我们使用内置的dis模块把生成的co2对象显示为opcode:

import dis

dis.dis(co2)

1 0 LOAD_CONST 0 ('A')
3 LOAD_CONST 3 (())
6 LOAD_CONST 1 (<code object A at 00000000040B8530, file "class-A-input", line 1>)
9 MAKE_FUNCTION 0
12 CALL_FUNCTION 0
15 BUILD_CLASS
16 STORE_NAME 0 (A)

6 19 LOAD_NAME 0 (A)
22 CALL_FUNCTION 0
25 STORE_NAME 1 (a)

7 28 LOAD_NAME 1 (a)
31 PRINT_ITEM
32 PRINT_NEWLINE
33 LOAD_CONST 2 (None)
36 RETURN_VALUE

以上就是在python编译好的bytecode,以便后面在python VM中运行。

ByteCode解析:

  • 最左边的一列(1, 6, 7)是源代码里的行号,比如1就代表的是第1行。
  • 第二列(0,3,6,9……36)表示的是OPCODE表的index
  • 第三列(LOAD_CONST,MAKE_FUNCTION)是具体的OPCODE,这个OPCODE只是用来帮助人类阅读用的,python VM执行的还是前面的index
  • 第四列(0,3,1)等指的是OPARGS的index,这个index不是从一个全局表里取的,而是基于前面的OPCODE
    • 比如:LOAD_CONST是从co_consts中取,LOAD_NAME是从co_conames中取
  • 第五列是OPARGS的辅助提示,只是用来帮助人类阅读
参考:
  1. http://security.coverity.com/blog/2014/Nov/understanding-python-bytecode.html
  2. http://akaptur.com/blog/categories/python-internals/
  3. http://eli.thegreenplace.net/tag/python-internals

Windows CTRL C Event and the workaround

参考:

模拟方案
  1. https://groups.google.com/forum/#!topic/qbzr/IIHVFZ7nJ2s
  2. https://03255835288185997573.googlegroups.com/attach/d18031297c011b70/stop-subprocess-win32-573.patch?part=0.1&view=1&vt=ANaJVrEjLEPxrT41e9rqB8xbnMLFagAr7UR43c1-EHL-_22nmm8WoWynjacx2d3fFdjnRp-MmrrLeYWScsRSKhymFoR-BdQ8_xiuLSck2h1MeiQIIuOZz9M
  3. https://github.com/jcanalesc/Everton-Pulseras/blob/master/winctrlc.py
  4. http://stackoverflow.com/questions/7085604/sending-c-to-python-subprocess-objects-on-windows
  5. https://bugs.launchpad.net/qbzr/+bug/277082
  6. http://svn.smedbergs.us/python-processes/trunk/
  7. https://groups.google.com/forum/#!topic/qbzr/kVi-CgvUzUM
WIN原生方案
  1. https://bugreports.qt.io/browse/QTBUG-24619
  2. http://stanislavs.org/stopping-command-line-applications-programatically-with-ctrl-c-events-from-net/
  3. http://www.qtcentre.org/threads/36911-Can-t-send-a-Ctrl-C-to-a-QProcess-%28Win32%29
  4. https://hg.python.org/cpython/file/default/Lib/test/test_os.py
  5. https://hg.python.org/cpython/file/44253ce374fc/Lib/test/win_console_handler.py
  6. https://hg.python.org/cpython/rev/384d2189a96a/
  7. https://bugs.python.org/issue9524
  8. https://docs.python.org/2/library/signal.html
  9. https://bitbucket.org/weyou/winpexpect/src/74fa12e233f43136248292a578a107f2e0e56775/lib/winpexpect.py?at=default
  10. http://stackoverflow.com/questions/13024532/simulate-ctrl-c-keyboard-interrupt-in-python-while-working-in-linux
  11. http://stackoverflow.com/questions/4669204/send-ctrl-c-to-remote-processes-started-via-subprocess-popen-and-ssh
  12. http://pastebin.com/tNCLbevw
其他
  1. http://doc.qt.io/qt-5/qprocess-members.html
  2. https://docs.python.org/2/library/thread.html#thread.interrupt_main
  3. https://launchpad.net/qbzr

Basic AWK

About AWK

由Alfred Aho, Peter Weinberger, and Brian Kernighan发明,专门用来做“文本处理”的解释 型语言。awk的最基本的功能就是在文本文件中按照制定的pattern按行搜索, awk在行级别 上完成指定的行为,awk持续处理输入的行直到文件的终端。

awk与其他的编程语言有很大的不同,因为awk是数据驱动的(经常需要在awk中描述你想要处理的 数据,然后在数据里找到你想要的结果),大部分其他的语言是过程的(Procedural),你必须 详细描述程序中的每一步需要怎么工作。

awk编程的时候,首先你需要定义一系列规则(rules),每一个rule就是一个模式(pattern) 和一个行为(action)。模式主要是用来指定“要搜索什么”,行为用来指定找到之后的操作。

awk的语法

一个rule = 一个pattern后面紧跟一个action,action需要用括号,rule用断行分割

pattern { action }
pattern { action }

如何运行awk

awk 'program' input-file1 input-file2 ...

如何包含awk的program太长,可以把awk放在文件中运行

awk -f 'program-file' input-file1 input-file2 ...

这里用单引号'包围program是为了让awk程序在被shell调用时,不会把awk program中的特殊 字符当作shell中的特殊字符。另外也可以让shell把program作为单参数传给awk,从而可以让 program写在多行中。

如果awk程序在运行时不带参数,如下:

awk 'program'

awk将会把program附加到标准输入(standard input),直到使用ctrl + d表示end-of-file 为止。

self-contained awk script

这种awk脚本的写法是在文件的顶端使用如下格式:

#!/usr/bin/awk -f
...your program

awk是一种解释型的语言,所以在文件的第一行以#!开头,然后放上awk解析器的全路径,后面 跟的是一个可选的参数。然后在外部执行这个文件的时候(需要先用chmod +x yourfile.awk),操作系统会先寻找解析器,然后使用找到的解析器+参数列表运行后面的程序(program)。

参数列表: - 第一个参数是awk program的全路径(full path) - 后面的参数是awk提供的,或者是inputfiles等

#!/usr/bin/awk后面,通常只能跟一个参数,再多的参数会被OS(操作系统)自动忽略。

最后ARGV[0]并不是可靠的,在不同的OS中可能返回不同的值。

shell引号规则

  • 被引号括住的项目,可以自由拼接,最终被shell合并为一个
  • 在单个的字符前加\表示该字符将会被引号当作纯字符,而不会被shell解释为特殊字符
  • 单引号'将会屏蔽任何放在其中的字符,这些字符将会逐个被传给shell而不会被解释为特殊 字符
  • 双引号“””屏蔽期间的大部分字符,但是会做变量和命令行的替换

    • 因为双引号中的项目有可能被shell处理,因此,放在双引号中的字符如果跟shell中的特 殊一样,需要手动的屏蔽(escape)。 比如$, `, \\, "必须加反斜杠\\进行屏蔽。比如下面代码如果用单引号是:
    awk 'BEGIN { print "Don\\47t Panic!" }'

    如果要用双引号就是:

    awk "BEGIN { print \"Don't Panic\"}"
  • 空字串(null string)将会被删除,如果跟非空字串放在一起

    • awk -F "" 'program' files
    • awk -F"" 'program' files
    • 第二种形式是错的,双引号将会被删掉,然后program错误地变成了awk的参数

如何避免但双引号的混用

  • 使用反斜杠\\屏蔽
  • 使用八进位的屏蔽符,比如awk 'BEGIN { print "Here is a double quote <\42>" }'
  • 使用命令行变量替换awk -v sq="'" 'BEGIN { print "Here is a single quote <" sq ">" }'

什么时候使用awk

  • 处理元数据(raw data),然后生成报告的时候
  • 需要更小体积的程序(program),awk通常是最小的

awk解析器的命令行参数

  • 设置域分隔符(field separator)
    • -F fs
    • --field-separator fs
  • 设置awk程序的源文件
    • -f source-file
    • --file source-file
  • 命令行传入的临时变量的赋值
    • -v var=val
      • - v可以使用多次,传入多个变量
    • --assign var=val
  • W gawk-opt,提供一些awk参考实现里的额外的命令行参数
  • --表明命令行的尾端,后面的命令行参数将会被忽略
  • 设置使用单字节字符
    • -b
    • --characters-as-bytes
  • 使用GNU兼容模式的awk
    • -c
    • --traditional
  • 查看版权信息
    • -C
    • --copyright
  • 查看全局变量,类型和最终的值
    • -d[file],中间没有空格
    • --dump-variables[=file]
  • 调试
    • -D[file]
    • --debug[=file]
  • 混合命令行awk程序和文件awk程序
    • -e program-text
    • --source program-text
  • 执行文件awk
    • -E file
    • --exec file
    • -f类似,但是该参数差异在于:
      • 代表着命令行参数的终点
      • 命令行变量var=value赋值是不允许
  • 编译awk程序源代码为可迁移的通用模板文件,通常用于多语言环境
    • -g
    • --gen-pot
  • 帮助
    • -h
    • --help
  • 导入库文件
    • -i source_file
    • include source-file
    • 库文件只会被导入一次
  • 导入扩展程序进入awk
    • l ext
    • --load ext
    • awk将会扫描AWKLIBPATH去寻找扩展
    • 扩展的初始化的代码为dl_load()
    • 跟在代码中使用@load类似
  • 是否要求平台迁移性
    • -L[value]
    • --lint[=value]
    • 中间没有空格
  • 使用高精度浮点数
    • -M
    • --bignum
  • 自动翻译16进制和8进制的数字
    • -n
    • --non-decimal-data
  • 强制使用本地的数字格式
    • -N
    • --use-lc-numeric
  • 使用格式良好的打印
    • -o[file]
    • --pretty-print[=file]
  • 优化
    • -O
    • --optimize
  • 调优
    • -p[file]
    • --profile[=file]
  • POSIX兼容
    • -P
    • --posix
  • 允许内部表达式
    • -r
    • --re-interval
  • 使用沙盒运行awk
    • -S
    • --sandbox
  • 与老的awk兼容检查
    • -t
    • --lint-old
  • 版本
    • -V
    • --version

Tornado Basic and Demo

Long Polling with tornado

Tornado的异步架构可以对HTTP的long polling得心应手,所谓的long polling是一种技术用来
处理实时提醒或者构建高性能的多人聊天室。早期实现这种技术是使用在客户端定时更新的方法,
但是带来的挑战是“实时更新”的“频率”必须足够的快,但是太快的客户端请求又会使服务器不堪
重负。

所以现在使用一种叫做“Server Push”的技术,要求在“网络应用”端在有状态(state)更新(
update)时,实时推送给客户端。在现在的常见的server端push技术中,需要跟客户端的浏览器
有很好的兼容,所以最流行的技术使用的是,通过客户端发起的连接去模拟“Server Push”。这类
有客户端发起的用来server push的连接,叫做“Long Polling”或者“Comet Request”。

Long Polling技术是客户端发起一个连接,并保持在open状态,然后客户端等待服务器端“推送”
状态更新。当服务器端推送更新完成后,将会关闭该连接。然后,客户端打开另外一个新的连接,
等待服务器端的另外一次推送。

Benefits of Long Polling

  • 减少服务器负载
  • 服务端只在客户端发起连接后,推送状态更新
  • 浏览器只需要支持AJAX
  • 相比其他的推送技术,“Long Polling”得到更广泛的应用
  • 参考实现:
    • Google Doc使用Long Polling实现实时文档编辑与协作
    • Twitter使用long polling去更新用户的提醒
    • Facebook使用long polling实现聊天

Tornado最佳代码结构:

Tornado示例站点

  1. https://github.com/ajdavis/motor-blog
  2. https://github.com/viewfinderco/viewfinder/
  3. https://github.com/ipython/ipython/tree/master/IPython/html
  4. https://github.com/cdkr/tornado-cantas
  5. https://github.com/nellessen/tornado-shortener
  6. https://github.com/livid/v2ex
  7. https://github.com/rande/python-element
  8. http://thomas.rabaix.net/under-the-hood
  9. https://github.com/abdelouahabb/essog
  10. https://github.com/mitotic/graphterm
  11. https://github.com/peterbe/worklog
  12. https://github.com/peterbe/toocool
  13. https://github.com/martinrusev/amon
  14. https://github.com/tornadoweb/tornado
  15. https://github.com/peterbe/tornado_gists
  16. https://github.com/bgolub/tornado-blog
  17. https://github.com/peterbe/tiler
  18. https://bitbucket.org/black_rez/lola21/
  19. https://github.com/finiteloop/socialcookbook
  20. https://github.com/familonet/
  21. https://github.com/GovLab/OpenData500

参考:

Python and Signals

  1. http://www.qtcentre.org/threads/36911-Can-t-send-a-Ctrl-C-to-a-QProcess-(Win32)
  2. http://doc.qt.io/qt-5/qprocess.html#terminate
  3. http://forum.qt.io/topic/27478/qprocess-terminate-can-not-stop-windows-console-application-with-setconsolectrlhandler
  4. http://forum.qt.io/topic/27478/qprocess-terminate-can-not-stop-windows-console-application-with-setconsolectrlhandler/4
  5. http://bytes.com/topic/python/answers/801937-sending-cntrl-c
  6. http://www.gossamer-threads.com/lists/python/python/546571
  7. https://msdn.microsoft.com/en-us/library/windows/desktop/ms683155.aspx
  8. http://www.qtcentre.org/threads/36911-Can-t-send-a-Ctrl-C-to-a-QProcess-(Win32)
  9. http://stackoverflow.com/questions/7632589/getting-realtime-output-from-ffmpeg-to-be-used-in-progress-bar-pyqt4-stdout
  10. http://doc.qt.io/qt-4.8/qprocess.html

Selion自动化测试框架介绍

发现的一个好玩的新的基于selenium(browser based automation framework)的新型的框架,
开发和维护者来自于Paypal公司。

项目地址:https://github.com/paypal/SeLion

Selion的基本介绍:

客户端

  • 客户端基于TestNG进行管理,并且支持DataProvider进行数据注入
  • 支持selenium的POP(Page Object Pattern)开发模式,好玩的是支持使用类DSL语言使用YAML
    格式预定义page object

服务端

  • 服务端基于Grid进行管理,并支持对hub和node的自定义

文档

注意

  • 该项目依然处于开发状态