顯示具有 python 標籤的文章。 顯示所有文章
顯示具有 python 標籤的文章。 顯示所有文章

2019年4月11日 星期四

Python Tip - Examples of initialize variables with sequence number in different ways

Purpose

It is used to collect sample code for python's coding tips of initialization variables.

A. Variables with constants

test_init_vars.py

#!/usr/bin/env python

a=0
b=1
c=2
d=3
e=4
f=5
print(a, b, c, d, e, f)

B. Use range()

test_init_vars_by_range.py

#!/usr/bin/env python

a, b, c, d, e, f = range(6)

print(a, b, c, d, e, f)

C. Use enum since python 3.4 supported

test_init_vars_by_enum.py

#!/usr/bin/env python3.6

from enum import IntEnum,auto

class Vars(IntEnum):
    a=0
    b=auto()
    c=auto()
    d=auto()
    e=auto()
    f=auto()

print("{}, {}, {}, {}, {}, {}".format(Vars.a, Vars.b, Vars.c, Vars.d, Vars.e, Vars.f))

Result

$ python2.7 test_init_vars.py
(0, 1, 2, 3, 4, 5)
$ python3.6 test_init_vars.py
0 1 2 3 4 5

$ python2.7 test_init_vars_by_range.py
(0, 1, 2, 3, 4, 5)
$ python3.6 test_init_vars_by_range.py
0 1 2 3 4 5

$ python2.7 test_init_vars_by_enum.py
Traceback (most recent call last):
  File "test_init_vars_by_enum.py", line 3, in
    from enum import IntEnum,auto
ImportError: cannot import name auto

$ python3.6 test_init_vars_by_enum.py
0, 1, 2, 3, 4, 5

References

2014年7月2日 星期三

如何將 python 程式編為 windows 64 位元 可執行檔?



之前, 曾寫過一些 python 平台API無關工具在 linux 上跑。
最近不想重寫,有下列需求:

  • 要在 windows 上直接跑,不用在自行安裝 python
  • 要能支援 32bit 及 64bit 的 windows
記得近年曾研究過 Dropbox 的 windows/mac client 的python 用法,
所以使用 py2exe 在 windows 8.1 64bit 做了一個實驗,

範例程式:

hello.py
# ---------------------
print "Hello World!"
# ---------------------

setup.py
# ---------------------
from distutils.core import setup
import py2exe

setup(console=['hello.py'])
# ---------------------

使用下列指令:

 python setup.py py2exe 

很方便就能產生可執行檔,如上圖產生 hello.exe。

以下是 dist 目錄下的檔案清單:


2012年5月20日 星期日

python + shellcode 實測 64/32

0520-shellcode_py

最近看到Shellcode文章,手癢把它改相容64bit Ubuntu及32bit cygwin 了。

由此下載原始碼修改過smc.py


Ubuntu 11.10 (GNU/Linux 3.0.0-12-virtual x86_64)
使用Python 2.7.2+,進行測試

Linux pylab 3.0.0-12-virtual #20-Ubuntu SMP Fri Oct 7 18:19:02 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux
# python smc.py
linux2_add64(99, 1) = 100


Linux xubuntu 2.6.35-32-generic #65-Ubuntu SMP i686 GNU/Linux
使用Python 2.6.6,進行測試

$ python smc.py
linux2_add32(99, 1) = 100


Windows XP+cygwin+gcc(4.5.3) + python 2.7.2
使用dump_machine_code.sh, ASM 的結果:

add.o:     file format pe-i386
Disassembly of section .text:
00000000 <_add>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   8b 45 0c                mov    0xc(%ebp),%eax
   6:   03 45 08                add    0x8(%ebp),%eax
   9:   5d                      pop    %ebp
   a:   c3                      ret
   b:   90                      nop
cygwin中,執行smc.py結果
$ python smc.py
cygwin_add32(99, 1) = 100


載入平台相依的libc

if sys.platform == "cygwin":
    libc = cdll.LoadLibrary("/bin/cygwin1.dll")
else:
    libc = CDLL('libc.so.6')


判斷32或64位元的方法之一


import sys
from math import log
def is64bit():
    return log(sys.maxsize, 2) == 63


小結


這技巧可以使用smc主程式,隨意在執行時修改程式碼的方法,也提供一條路讓python直接與硬體溝通。

如這是一個24小時不能停機的程式,意味著筆者可利用這方法做出不需要重新執行python主程式來動態載入修改過的函式庫。

當然這種方法也會影響Python 跨平台的可攜性,
解決方法只能為各種所需執行平台編繹對應的版本(若其平台安裝有gcc,則可以在執行檢查有無對應的platform_func.o 檔案,動態產生並載入它,類似pyc的做法)。

2012年1月30日 星期一

python 與 Redis 資料庫初探

image

個最近想提高公司網站的效能,之前為求先縮短第一版開發的時程,先用MySQL來實作session store,始終覺得有殺雞用牛刀的感覺。便著手研究目前非常流行的Redis,它是一種No SQL及In-Memory 的資料庫,就一個倶有Key-Value結構等特性的資料庫而言,十分適合用來做Session Store使用。


安裝

為Redis 的網站說明做的不錯,照著下載區及其安裝說明,三步驟安裝:

$ wget http://redis.googlecode.com/files/redis-2.4.6.tar.gz
$ tar xzf redis-2.4.6.tar.gz
$ cd redis-2.4.6
$ make

網站上建議使用2.4 版,筆者下載2.4.6 穩定版來安裝。

目前開發環境在Mac OS X Lion及 Ubuntu 11.10上,安裝都十分順利;但無法在cygwin中編譯成功。安裝完成後,預設無密碼。直接執行 redis-server 啟動:

/home/ubuntu# redis-server /usr/local/etc/redis.conf
[1264] 30 Jan 14:35:26 * Server started, Redis version 2.4.6
[1264] 30 Jan 14:35:26 * DB loaded from disk: 0 seconds
[1264] 30 Jan 14:35:26 * The server is now ready to accept connections on port 6379
[1264] 30 Jan 14:35:26 - DB 0: 1 keys (0 volatile) in 4 slots HT.
[1264] 30 Jan 14:35:26 - 0 clients connected (0 slaves), 717560 bytes in use
[1264] 30 Jan 14:35:31 - DB 0: 1 keys (0 volatile) in 4 slots HT.
[1264] 30 Jan 14:35:31 - 0 clients connected (0 slaves), 717560 bytes in use

接著使用redis-cli 便可直接連上redis-server。(詳參照data types intro)

$ redis-cli
redis 127.0.0.1:6379> get a
"1"
redis 127.0.0.1:6379> set a 2
OK
redis 127.0.0.1:6379> get a
"2"
redis 127.0.0.1:6379> incr a
(integer) 3
redis 127.0.0.1:6379> get a
"3"
redis 127.0.0.1:6379> quit

python redis.py

sudo pip install redis
sudo easy_install redis

使用python import redis 來測試看看

ubuntu@redis:~$ python
Python 2.7.2+ (default, Oct  4 2011, 20:06:09)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import redis
>>> r = redis.StrictRedis(host='localhost', port=6379, db=0)
>>> r.get('a')
'3'
>>> r.set('b', 10)
True
>>> r.incr('b')
11
>>> r.get('b')
'11'
>>>

就session store或engine 有關django的目前找下列三種:








尚未進行測試,之後會陸續評估看看那個較為適合目前的使用情境。

小結

redis安裝起來,十分簡單,也很容易使用。

目前在github 上,有許多python 相關的函式庫可供使用,值得花些時間學習看看。

相關問題?

如何停止redis-server?

強烈建議請使用shutdown 命令,來完成將記憶體中尚未資料儲存至硬碟中,否則資料可能遺失。

原因為redis 設定中,有命令save 命令在特定時間內有變更多少筆鍵值才會進行儲存。

預設設定檔如下:

#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving at all commenting all the "save" lines.
save 900 1
save 300 10
save 60 10000

安全性?

在redis.conf 中,可利用requirepass 來設定,就官方文件要求需要設定強度較高且長度較長的密碼,

因若redis在較高效能的機器上,同時大量執行的密碼驗證(auth)命令時,可達15K次每秒,可能在極短時間內破解強度較弱的密碼。

就筆者看來,最好使用 iptables 或host ACL方法,只授權必要使用redis server的相關伺服器接入。

Ubuntu 11.10 執行警告訊息

[1063] 30 Jan 14:17:26 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

請務必設定vm.overcommit_memory=1的值,不可忽略。

2011年12月30日 星期五

阿班私房菜-Design Pattern(DP)初探使用Python (1)


Design Patterns: Elements of Reusable Object-Oriented Software

最近使用Python 寫程式,心血來潮,使用 (DP) 套用看看。
以 Factory worker 為例。
首先建立一個物件Worker 包含姓名、屬性等等。
class Worker:
    def __init__(self, name=None):
        self.name = capfirst(name) if name else "The worker"
    def getName(self):
        return self.name
    def attrib(self):        return "Worker"
    def lookandfeel(self):
        print "%s is a %s" % (self.name, self.attrib())

Designer與Engineer繼承它,並覆寫attrib() Method。
class Designer(Worker):
    def attrib(self):        return "Designer"
class Engineer(Worker):
    def attrib(self):        return "Engineer"

當使用各自物件來建立,並執行lookandfeel() 時,會呼叫各自的attrib method。
以下使用 factory pattern 來實作僱用workers 的容器物件。
# factory pattern
class Factory:
    type_designer = 1
    type_engineer = 2
    def __init__(self):
        self.workers = []
    def employ(self, worker_type, name=None):
        if worker_type == Factory.type_designer:
            self.workers.append(Designer(name))
        elif worker_type == Factory.type_engineer:
            self.workers.append(Engineer(name))
    def workerCount(self):
        return len(self.workers)
    def browse(self):
        for a in self.workers:
            a.lookandfeel()
z = Factory()
z.employ(Factory.type_designer)
z.employ(Factory.type_designer, 'bill')
z.employ(Factory.type_engineer, 'sue')
z.browse()

執行結果:
$ python factory_dp1.py
The worker is a Designer
Bill is a Designer
Sue is a Engineer

承上篇重構一文:我們來重構一下它,並加入singleton pattern(利用@staticmethod 來實作)。
class Factory:
    # class static variable
    type_designer = "1"
    type_engineer = "2"
    _Factoryself = None
    _factorydict = {
           type_designer: Designer,
           type_engineer: Engineer,
        }
    def __init__(self):
        self.workers = []

    @staticmethod
    def singleton():
        if Factory._Factoryself == None:
            Factory._Factoryself = Factory()
        return Factory._Factoryself



    def employ(self, worker_type, name=None):
        caller = Factory._factorydict.get(worker_type)
        if caller:
            self.workers.append(caller(name))
        return None



    def workerCount(self):
        return len(self.workers)

    def browse(self):
        i = 0
        for a in self.workers:
            print " - Worker[%s]:%s" % (i, a.getLookandfeel())
            i+=1

def main():
    f = Factory.singleton()
    f2 = Factory.singleton()
    if f == f2:
        print "* Singleton pattern of Factory"



    print "* Factory pattern to employ"
    f.employ(Factory.type_designer)
    f.employ(Factory.type_designer, 'Bill')
    f.employ(Factory.type_engineer, 'Sue')
    print "* Factory pattern to browse"
    f.browse()
    print "* The factory has %s workers." % f.workerCount()

if __name__ == "__main__":
    main()

小結

使用python 來寫DP程式,功能強大,自由度高;
但這意味,寫其程式時,要有其紀律,遵守一定規範(coding convention)來寫;
不然程式會變得非常難讀及維護。 最近上列範例程式均可在github-class_dp中找到。
上述範例還可進一步,重構或修改為工廠人員管理系統的雛形,留待有興趣的讀者們來添加!

2011年11月30日 星期三

阿班私房菜-重構(Refactoring)的小技巧(1)


最近在看一本書"The Pragmatic Programmer",裡有不少與重構相關的範例,值得參考,有與趣人可以買來看看。因此書,筆者心血來潮,分享一下自己的開發經驗。


在專案開發程式的過程,常會因時間壓力或市場考量,不得不加入很多非計畫中的程式碼。
開發時間越久,就漸漸難以維護,增加新功能更為秏時;當人力不足以達成出貨時程,便開始增加人力,最後日積月累,專案程式變成一個大怪物;直至某一日砍掉重練。


而筆者近年來,使用重構方法,使得自己不用一直為解專案bug而加班。

重構(Refactoring) 英文是一個現在進行式,表示這個動作需要一直不斷發生。


其中,"重構"的最重要的基石就是自動化測試程式,如每當重構程式碼中重覆的部分時,執行一下自動化測試程式,便可很快的確認程式修改後的正確性。

接下來我們來看一些筆者常用的小技巧,以下範例使用Python語言。

def show_animal_info0(animal):
    if animal == 'dog':
        print("The dog has four legs")
    elif animal == 'chick':
        print("The chick has two legs")
    else:
        print("There is an unknown animal")
show_animal_info0("dog")
show_animal_info0("chick")
show_animal_info0("cat")


重構if-else

通常if-else會越加越長,慢慢變得難以除錯。
animal_info_dict = {
        "dog":   "four legs",
        "chick": "two legs"
        }
def show_animal_info1(animal):
    try:
        info = animal_info_dict[animal]
        print("The %s has %s." % (animal, info))
    except:
        print("There is unknown animal")
用dict結構表格取代if-else, 也可用來取代switch


重構重覆呼叫函式

這種函式大部分由copy and paste而來。
show_animal_info0("dog")
show_animal_info0("chick")
show_animal_info0("cat")
def main():
    animals = ["dog", "chick", "cat"]
    for name in animals:
        show_animal_info1(name)
if __name__== "__main__":
    main()

重構屬性
紅色legs部分也是重覆程式碼的狀況之一
animal_info_dict = {
        "dog":   "four legs",
        "chick": "two legs"
        }
def show_animal_info1(animal):
    try:
        info = animal_info_dict[animal]
        print("The %s has %s." % (animal, info))
    except:
        print("There is unknown animal")
animal_info_dict = {
        "dog":   {"leg": "four"},
        "chick": {"leg": "two"},
        "cat":   {"leg": "four"}
        }
def show_animal_info1(animal):
    try:
        info = animal_info_dict[animal]
        legs = info['leg']
        print("The %s has %s legs." % (animal, legs))
    except:
        print("There is unknown animal.")


自動化測試程式

要進行自動化測試,以本範例為例:

1. 將邏輯與資料顯示動作分離

def show_animal_info1(animal):
    try:
        info = animal_info_dict[animal]
        legs = info['leg']
        print("The %s has %s legs." % (animal, legs))
    except:
        print("There is unknown animal.")
def get_animal_info1(animal):
    try:
        info = animal_info_dict[animal]
        legs = info['leg']
        msg = "The %s has %s legs." % (animal, legs)
    except:
        msg = "There is unknown animal."
    return msg

2. 比對預期函式回傳值結果

使用pyunit 來實作自動測試
import unittest
from code_for_automation import *
class DefaultTestCase(unittest.TestCase):
    def testTestDog(self):
        sample="The dog has four legs."
        msg = get_animal_info1('dog')
        assert msg == sample , 'test dog message'
    def testTestChick(self):
        sample="The chick has two legs."
        msg = get_animal_info1('chick')
        assert msg == sample , 'test chick message'
    def testTestCat(self):
        sample="The cat has four legs."
        msg = get_animal_info1('cat')
        assert msg == sample , 'test cat message'
if __name__ == '__main__':
    myTestSuite = unittest.makeSuite(DefaultTestCase,'test')
    runner = unittest.TextTestRunner()
    runner.run(myTestSuite)

3. 統計測試結果
$ python 05_automation.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK
以上範例程式均可由tip1下載

結論

當有了自動化測試程式之後,可提高自行重構的信心,又不用花費手動一個個輸入測試的時間。
本文雖以python為例,但觀念是相通的,其他語言也有其自動測試函式庫可供使用,歡迎不吝指教。


django i18n 實戰 (二)


續上篇django i18n 實戰,筆者作了一些小實驗,以方便中文翻譯的進行。

整段文章中文化


在前篇提到的使用django.po 來編修翻譯的方法,在此處便不太適用。
因為這類文字時常修修補補,非常不易維護。
目前使用的解決方法,使用不同locale目錄來放置不同語言版本template。
en test.html
test2.html
zh-tw test.html
test2.html

取得目錄及template名稱
from django.utils import translation
def getLocaleResourcePathName(templateName):
    cur_language = None
    try:
        cur_language = translation.get_language()
    except:
        logger.exception('getLocaleResourcePath')
    if cur_language == 'zh-tw':
        return "%s/%s" % (cur_language, templateName)
    return “en/%s”  % templateName
其中translate.get_language() ,傳會目前要求取用此頁瀏灠器所使用的語系。


在javascript中需翻譯的文字

解法1: 為將瀏灠器目前所使用的語系,存入cookie中,以便javascript實作中文化。
def setLangCookie(request):
    params = {}
    c = Context(params)
    t = loader.get_template(pageForJS)
    response = HttpResponse(t.render( c ))
    try:
        cur_language = translation.get_language()
        response.set_cookie('lang', cur_language)
    except:
        logger.exception('set_cookie')
    return response

解法2: 同上一解法為將所需要翻譯文字集中至同一檔案,依瀏灠器目前所使用的語系,代換為i18n.js所在路徑。

簡易bash腳本檔案i18n.sh


續前篇,將相關命令包裝為腳本檔以方便使用。

如: sh  i18n.sh  u
 (即更新django.po檔案,附加新增文字)

#!/bin/sh


ACTION="$1"
TARGET="$2"

usage() {
        echo "syntax: |update|compile>"
}

case $ACTION in
initial)
        django-admin.py makemessages -l zh_TW "$TARGET"
        ret=$?
;;
u*|update)
        django-admin.py makemessages -a
        ret=$?
;;
c*|compile)
        django-admin.py compilemessages
        ret=$?
;;
*)
        usage
        ret=1
;;
esac
exit $ret

備註:其中django-admin.py,如使用環境在Mac OS X 使用brew install gettext卻找不到xgettext 和 msgfmt相關檔案時,請使用下列命令產生正確連結:
 brew link gettext


2011年11月29日 星期二

django i18n 實戰

image

最近在為的上線前專案做中文化的處理,在此留下一心得記錄。
就django 中文化相關工具而言,主要是將gettext中的兩個命令:
  • xgettext包裝成django-admin.py makemessages, 用以將程式及腳本檔中所定義的Key字串取出放入至django.po
  • msgfmt包裝成django-admin.py compilemessages, 用來把django.po 轉為二進位的django.mo檔案。
Key 字串
以底線(_)為常見的gettext key字串
在下列命令為常使用的兩種:
from django.utils.translation import gettext_lazy as _
筆者使用下列語法:
from django.utils.translation import ugettext as _
為什麼筆者不使用 gettext_lazy呢?
django 在更新po檔案時,會將相近字詞直接複製到附加的詞組中,時常牛頭不對馬嘴,不如手動編修。

demo 專案為例
請進入與settings.py同層的專案目錄
$ django-admin.py makemessages –l zh_TW
processing language zh_TW
正確產生自動下列檔案django.po
locale/zh_TW/LC_MESSAGES/django.po
如出現下列錯誤:
$ django-admin.py makemessages -l zh_TW
Error: This script should be run from the Django SVN tree or your project or app tree. If you did indeed run it from the SVN checkout or your project or application, maybe you are just missing the conf/locale (in the django tree) or locale (for project and application) directory? It is not created automatically, you have to create it by hand if you want to enable i18n for your project or application.
請建立locale目錄,再試一次。

更新po檔案
每當新增一些個需要中文化字串,並不需要將它們動手加入django.po
可使用下列指令,直接將新字串附加至原有的django.po:
$ django-admin.py makemessages –a
編譯mo
$ django-admin.py compilemessages
註:在測試時,如有重新mo檔案,請重新執行django。

附錄
  • 在使用 blocktrans 及 endblocktrans時,因為採用python 內建dict 變數代換功能,所以只能使用-層。範例如下:
{% blocktrans %} {{user.username}} 進行統
統操作中,… {% endblocktrans %}
其中 {{user.username}}將無法正確代換,需改為 {{username}} 一層dict 結構。對照python 原字串展開範例:
userinfo={'username': _('Bill')}
"%(user.username)s 進行統
統操作中,…" % userinfo


結論


gettext 廣泛使用在開源軟體之中,而django-admin加以包裝,讓它在使用上更加方便。
若對如何包裝gettext有興趣,可參閱python腳本檔如下:
/usr/lib/python2.7/site-packages/django/core/management/commands/makemessages.py
/usr/lib/python2.7/site-packages/django/core/management/commands/compilemessages.py

2011年10月31日 星期一

Python+Django 輕鬆寫Web AP


近期工作需要在較短時間內,開發出Web端的程式,因筆者擅長Python,最後決定使用python+django 的組合。
另外在FreeNas 8 中也是使用 python+django來實作Web 控制介面,所以學會django,也可日後若為FreeNas添加新功能做準備。

(註:Google App Engine也採用django,但內建的版本已封裝一層,但有網上教學如何使用原來的django套件方法。)

環境設定

本次採用python 2.7+django 1.3.1
django 授權BSD License(筆者愛用BSD授權)

Cygwin
  • 下載python-2.7.2 重新編繹
  • 開啟Cygwin Terminal 或Mintty for Cygwin
  • 安裝pip
    • easy_install pip
  • 使用pip來管理python套件
    • pip install django

Mac OS X Lion
  • 內建python 2.7.1
  • 開啟Terminal
  • 安裝pip
    • sudo easy_install pip
  • 使用pip來管理python套件
    • sudo pip install django

如何建立專案?

此部分主要參考django文件(原文),加入筆者心得
Django首次使用產生主網站(如:mystie)

django-admin.py startproject mysite

註:在Mac OS X使用django-admin.py startproject 如出permission denied, 請將修改權限

chmod +x django-admin.py

mysite 目錄結構
manage.pydjango命令列工具
settings.pydjango專案設定腳本
urls.py用來設定urls 對應網頁進入點(可參原文說明
__init__.py空的內容則用來告訴python這是一個module,並可用來指定mysite模組初始化動作


啟動mystie專案

若只要本機內可供取用,請執行下行命令(server 會啟動在127.0.0.1:8000)
python manage.py runserver

參數語法
python manage.py runserver [:]

若欲使外部可連接請指定IP為0.0.0.0,表所有外部電腦均可使用此server系統IP連接。
python manage.py runserver 0.0.0.0:8080

註:不可單獨指定IP而沒有指定port
$ python manage.py runserver 0.0.0.0
Error: "0.0.0.0" is not a valid port number or address:port pair.


執行結果
~ mysite$ python manage.py runserver 0.0.0.0:8080
Validating models...

0 errors found
Django version 1.3.1, using settings 'mysite.settings'
Development server is running at http://0.0.0.0:8080/
Quit the server with CONTROL-C.


使用瀏覽器連接到localhost:8080










結論

與之前試過nodejs+express,有異曲同工之妙;只在使用的語言不同。
若網友想寫類似Web應用程式,可選擇慣用,產能倍增。
除此之外,便是主機代管服務的選擇,也將會影響你的可用的組合。
因筆者較熟悉python,用django較為得心應手,而nodejs 到處使用callback的方式不太習慣。
最近django網站首頁所列一句話:
"Django makes it easier to build better Web apps more quickly and with less code."
也是筆者採用的主因之一。
本篇旨在記錄一下,學習心得,歡迎交流。


2011年8月13日 星期六

使用Python 直接載入 C 函式庫進行多平台測試

(圖示為Python.org 版權所有)

最近有個朋友聊起TDD (Test Driven Programming) 測試導向程式設計,測試導向的優點:
便是能減少重覆測試所需要人工測試時間,當網友若修改程式庫時,
敢對進行程式優化,也較對修改有把握,因為可利用自動測試的程式來立刻驗證。
他最常使用C來寫程式,但是使用C來寫測試函式,就算附加CPPUnit 之類,還是需要比較多時間實作。
所以筆者寫了本篇心得,使用Python 來直接對C函式庫進行測試。

本次範例包括下列:

檔案名稱
說明
test.c 編繹為函式庫libtest.so或libtest.dll
main.c 編譯連結測試main()
main.py 用來載入測試函式庫的python 腳本檔
Makefile 執行編譯及測試

test.c
#include <stdio.h>
#define UBOUND 10  // 上限
#define LBOUND 1 // 下限
int foo(int r)
{
        if (r > UBOUND)
                return UBOUND;
        if (r < LBOUND)
                return LBOUND;
        printf("foo(%d) executed.", r);
        return r;
}

main.py部分程式範例:

此腳本主要使用cdll.LoadLibrary來載入所要進行測試函式庫

from ctypes import *
foolib = cdll.LoadLibrary(libname)

接著使用下列函式來驗查超出上限、下限及正常值。
(註:為求易懂並無使用其他測試函式庫。網友們可在網上找尋到許多python 測試函式庫,來縮短開發時程。)
def verify_lbound():
    p = 0
    r = foolib.foo(p)
    return verify_ifeq(1, r, p,"case lbound")
def verify_ubound():
    p = 11
    r = foolib.foo(p)
    return verify_ifeq(10, r, p, "case ubound")
def verify_general():
    p = 5
    r = foolib.foo(p)
    return verify_ifeq(p, r, p, "case general")


多平台編繹測試

在Makefile中,我們可使用uname –s 來取得使用平台的資訊,
再利用 ifeq 來實作平台不同的部分,以本次的範例而言,
平台編繹的主要在windows 使用函式庫名為.dll及執行程式副檔名為.exe,
而在Linux或Mac中,函式庫名要前綴lib,後置.so,可參考下方框紅色部分
KNAMEFULL  = $(shell uname -s | sed 's/_.*//g')
# cygwin on windows xp
# CYGWIN_NT-5.1
KNAME = $(KNAMEFULL)
ifeq ($(KNAME),CYGWIN)
EXEEXT = .exe
DLLEXT = .dll
else
EXEEXT =
DLLEXT = .so
endif


編繹並執行測試:

Makefile檔案在 all: 加上 run,以用表示編繹 foo$(EXEEXT),
然後,從run程式段來看,在Linux或Mac中, export LD_LIBRARY_PATH=.
來通知載入程式,函式庫在目前目錄中。
緊接著,執行  foo$(EXEEXT) ,驗證二進位連結無誤,
再執行python 腳本測試,如有任何錯誤產生,顯示make失敗錯誤。
all: run

run: foo$(EXEEXT)
        @ export LD_LIBRARY_PATH=.; ./$^ ; \
          if [ $$? -eq 3 ]; then \
          echo "Binary linking passed" ; \
          python main.py $(LIBNAME)$(DLLEXT) ; \
          if [ $$? -ne 0 ] ; then \
                exit 1; \
          fi \
        else \
          echo "Binary link failed" ; \
        fi


測試結果:
$ make
cc -c -o main.o main.c  -Wall
cc -c -o test.o test.c  -Wall
cc -o libtest.so -shared test.o
cc -o foo main.o -ltest -L.
foo(3) executed.Binary linking passed
case ubound:verified return value(10) from test.foo(11) [Passed]
case lbound:verified return value(1) from test.foo(0) [Passed]
foo(5) executed.case general:verified return value(5) from test.foo(5) [Passed]
total/passed cases: 3/3
Python load libtest.so [Passed]

下列平台經過測試無誤:
- Ubuntu 10.10: Python 2.6.6
- Win XP SP3: CYGWIN_NT-5.1 + Python 2.6.5
- Snowleopard 10.6.8: Xcode4.0.2+Python 2.6.1
本次所有範例可從源碼GitHub使用git下載,歡迎交流經驗或提供建議。

2011年7月13日 星期三

Python使用pyGUI實作動態LineChart




源起小筆最近忙著實作 Ubuntu 的前端UI, 因要需求最短時間做出 Prototype,
所以便選用 Python + PyGUI組合。另外PyGUI的文件相對來說比較詳細,也是選用的主因之一。

程式主要功能:

  • 背景執行緒+亂數產生器並紀錄到資料區,通知元作重繪。
  • 前景Linechart View元件,以資料區繪製線圖。
當小筆完成時,看到圖形動態繪出,隨時間移動變化時,非常感動。

如有興趣,可參考小筆的JuluCharts源碼GitHub分享,歡迎交流經驗或提供建議(JuluCharts的Wiki)

2011年5月14日 星期六

解決Python 跨平台相容性問題 (Mac OS X)

最近因工作需要使用Python來寫技術支援的程式,且其能在Tiger(10.4+), Leopard(10.5.5+), Snowlepoard (10.6.2+)正確執行。


測試開發用環境
Snowleopard 10.6.7 內建  python 2.6.1,
Leopard 10.5.8 內建 python 2.5.1
Tiger 10.4.8 內建 python 2.3.5

開發過程中,遇到下列問題:
Exception處理
try:

    open("/var/log/testexcept.log", w)
except IOError as (errno, strerror):
    print "Error %d: %s" % (errno, strerror)

上列程式,在Leopard 發生Exception錯誤。
$ python except_invalid.py
except_invalid.py:4: Warning: 'as' will become a reserved keyword in Python 2.6
  File "except_invalid.py", line 4
    except IOError as (errno, strerror):
                    ^
SyntaxError: invalid syntax
就Mac OS X 平台來說,"as" 是Python 2.6的保留字

解決方案:
try:
    open("/var/log/test.log", 'w')
except IOError, v:
    try:
       (code, msg) = v
    except:
       code = 0
       msg
    print "I/O Error:%d %s" % (code , msg)

執行結果:
$ python except_ok.py
I/O Error:13 Permission denied

readPlist/writePlist 不支援10.4


查了一下python plistlib 函式 python 2.5.1 and 2.6.1 都有相關函式
Python 2.5.1 (r251:54863, Jun 17 2009, 20:37:34)     ==>
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import plistlib
>>> dir(plistlib)
['Data', 'Dict', 'DumbXMLWriter', 'PLISTHEADER', 'Plist', 'PlistParser', 'PlistWriter', 'StringIO', '_InternalDict', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '_controlCharPat', '_dateFromString', '_dateParser', '_dateToString', '_encodeBase64', '_escapeAndEncode', 'binascii', 'datetime', 're', 'readPlist', 'readPlistFromResource', 'readPlistFromString', 'writePlist', 'writePlistToResource', 'writePlistToString']

python 2.3.5 沒有所需函式
Python 2.3.5 (#1, Jul 25 2006, 00:38:48)
[GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import plistlib
>>> dir(plistlib)
['Data', 'Date', 'Dict', 'DumbXMLWriter', 'False', 'INDENT', 'PLISTHEADER', 'Plist', 'PlistParser', 'PlistWriter', 'True', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '_encode', 'bool', 'sys']

解決方案:

使用Plist()的函式 fromFile(file) and write(file)
import plistlib
pl = plistlib.Plist()
file="/Applications/iTunes.app/Contents/version.plist"
dict = pl.fromFile(file)
print dict['CFBundleShortVersionString']
dict['CFBundleShortVersionString'] = "10.1"
dict.write("my.plist")

2010年6月7日 星期一

存取Web Service Interface 使用 python SOAP 函式庫(1)


請先下載下列檔案


設定開發環境

  • 安裝 python-2.6.5.msi
  • 安裝 setuptools-0.6c11.win32-py2.6.exe
  • 安裝 python-suds-0.3.9.tar.gz

解壓縮 python-suds-0.3.9.tar.gz 到本機磁碟
Ex:
 C:\python-suds-0.3.9

cd  C:\python-suds-0.3.9
python setup.py install