mod_wsgi, flask 與 python3

我是在 OpenShift 上遇到的情況,用 python3 + flask 寫的 web app 沒辦法啟動,用 rhc tail 出現這樣的錯誤訊息:

‘module’ object has no attribute ‘__loader__’

用 Google 找,在 StackOverflow 上找到這篇 flask – cannot setup apache 2.2 with mod_wsgi and python 3.3? ,然後跟著提供的線索看到這篇:Problem debugging Flask under Python 3.3 ,謝謝 Eric 提供的 workaround 。

workaround 是這樣的,在初使化 Flask(name) 時,加上 instance_path 參數,所以我就改為 Flask(name, os.path.join(os.environ[‘OPENSHIFT_PYTHON_DIR’], ‘instance’)) ,就解決了。

問題是出在 flask.Flask 的初始函數裡,Flask.init 在 instance_path 參數為 None 時,會呼叫 auto_find_instance_path() 來取得 instance_path 的值,而 auto_find_instance_path() 又呼叫 flask.helpers 的 find_packages(),find_packages() 使用了 pkgutil.get_loader() 來尋找 module 而導致了錯誤發生。

我在想,或許添加適當的 init.py 也可以解決,但就懶得做實驗了,先這樣。

puddletag 的編碼轉換

puddletag 跟 Windows 上的 mp3tag 軟體一樣都是編輯 MP3 id3 資訊的工具,Linux 上同類的軟體有 easytag。這套軟體的介面基本上就是向 mp3tag 看齊,有使用過 mp3tag 的話,使用上應該不太有困難。

除了介面與 mp3tag 相似以外,我覺得 puddletag 最大的特色是 action/function 這功能,你可以用 action 跟 function 組合出各式各項快捷的新組合,讓你在編輯上能更方便,例如可以對歌曲重新編 track 並且填入 album 。

我在使用時,遇到有亂碼的問題,我大概知道是編碼的問題,之前是使用 big5 填 id3 tag v1,所以如果沒 id3 tag v2 的欄位時,會拿 id3 tag v1 的欄位來用,這時候因為不知道要從何種編碼轉為 unicode ,就會有亂碼的發生。本來我已經捲起袖子要寫 Plugin 來解決問題了,後來看了 puddletag 本身提供的 function,發現已經有 “Convert from non-standard encoding” 這個 function,只是裏面的選項沒有 big5 可以選。

追蹤 puddletag 的原始碼,這個 function 是定義在 /usr/share/pyshared/puddlestuff/functions.py 裡,內容大致是這樣:

因此,只要在 “&Encoding, combo,” 後加上 “big5,” 就可以多出 big5 的選項,日文就是 shiftjis,簡體中文就是 gb2312。有了這選項,編碼轉換的功能也可以順利運作了。為了避免更動原始碼,就寫了一個 plugin function,其實只是增加多的編碼,網址在 https://github.com/elleryq/puddletag_pluginfunction_extended_enconvert

依照片拍攝日期來分類到”年/月/日”資料夾裡

剛好要整理照片,又懶得手動整理,就寫了 script 來處理。照片裡的資訊是放在圖片裡,也就是 EXIF,網路上已經有好心人寫好 exifread 模組,所以只要用 pip 裝上使用即可。想說只是簡單的程式,就沒使用 argparse 來解析參數了,第一個參數帶 *.jpg 或 *.png,第二個參數帶要複製過去的資料夾,例如 python photo_classifier.py *.jpg d:\tmp 即可。

Python patterns – Visitor

開始來看 Python patterns,第一個看的是 Visitor。

  1. __mpro__ :這個內建的隱藏屬性可以列出父類別以及其上的所有類別,程式利用這個來取得繼承樹,並進行訪問。這部分的說明可以參考 What does “mro()” do in Python? – Stack Overflow
  2. 程式利用 getattr() 先去查看類別是否有實作 visit_xxx 方法,如果有就呼叫,如果沒有,才呼叫 generic_visit 的方法。

這跟 Visitor Pattern 似乎不太一樣,作者也在 Extrinsic Visitor Pattern in Python with support for Inheritance – Peter Hoffmann 裡說了,這是一個變形過的 Pattern。

如果要 Python visitor 的範例,可以參考 PythonWise: Visitor Design PatternThe Visitor Pattern in Python

補充:後來看了 ast module 以後,其實作者提的就跟 ast module 裡一模一樣。主要還是應用在 Tree 上。

Fork gedit-git-plugin

gedit-git-plugin 這個 plugin 不錯,就類似 vim-gitgutter 的功能,但是它用的是 libgit2-glib,這個還真麻煩,所以就 fork 出來改,看可不可以擺脫 libgit2-glib。

為此,我先讀了 vim-gitgutter 的程式,vim-gitgutter 主要是在 GitGutter 函式裡,這邊會先去 git diff,然後解析結果,再去作畫面的處理,gedit-git-plugin 也很類似。

程式主要是在 viewactivatable.py ,在啟用 plugin 的時候,會觸發 do_activate(),這時會去繫結相關的事件到程式裡。主要的處理在 update_location(),這裡就調用 libgit2-glib 函式庫去取得 git repository,然後再取得當前檔案的 blob ,最後再去呼叫 update();update() 是在做檔案比對,根據比對結果去顯示,這裡的比對用的是 python 內建的 difflib,而不是用 git diff 的結果。

分析完,大致知道要處理的部份就在 update_location(),只要這裡將使用 libgit2-glib 改為用 subprocess 呼叫 git 取得結果就可以了。第一個要做的事情是取得當前檔案的位置,這個只要套原來的程式,呼叫 self.location.get_path() 就可以。接著要去找 .git 資料夾,這邊我參考了 repo 裡找 .repo 的函式去找 .git 。基於以上兩者,可以取得相對於 .git 所在路徑的檔案路徑,也就是假設 .git 是在 /home/user/project1/.git ,編輯的是 /home/user/project1/dir1/readme,就可以得出 dir1/readme,接著只要用 git show HEAD:dir1/readme 就可以取得原始 blob 內容。依照原來程式的邏輯,把原始 blob 內容替換為 git show 程式的輸出之後,就可以擺脫 libgit2-glib 了!

最後,將 git.plugin 裡的 Loader=python3 改為 python ,再更新 README 並補上 setup.py 就收工了。現在就不用 libgit2-glib 啦~

我 fork 出來的專案放在 github 上。

gedit-git-plugin 二三事

同事都用 gedit ,只有我是用 vim。vim 有個 vim-gitgutter 的 plugin,可以直接顯示你目前的 code 跟 git repository 裡的 code 之間的差異點,用 ]h 或 [h 就可以快速搜尋,我想 gedit 應該也有。找了一下,就找到這個 gedit-git-plugin

安裝這個 plugin 相當簡單,依照說明去做,很快就可以裝好,但是安裝他背後所依賴的 library 就稍嫌麻煩了。他所需要的 library 有 libgit2, libgit2-glib。libgit2 有人已經包好在 ppa,只要 sudo add-apt-repository ppa:pressureman/ppa 再 sudo apt-get update && sudo apt-get install libgit2 即可。libgit2-glib 沒有人包,只能自己包,於是就又重練了一次打包 debian packages,真的太久沒包會忘記。大致列一下步驟,下次可以快速的回憶起來。

  1. 用 git 把 libgit2-glib clone 下來,輸入指令壓縮為 tarball:git archive –prefix=libgit2-glib-0.0.6/ –format=tar master | gzip > ../libgit2-glib-0.0.6.tar.gz
  2. 到上一層目錄之後,解壓縮,照理會解開到 libgit2-glib-0.0.6 ,接著切到該目錄下。
  3. 用 dh_make -f ../libgit2-glib-0.0.6.tar.gz 產生 debian 目錄。
  4. 修改 debian/control:這裡要修改的重點有 Depends、Homepage、Description、Package name
  5. 修改 debian/changelog:這邊就是加上變更的說明,調整名稱等等的,如果要上傳到 ppa,那個 unstable 要改為 ubuntu 欽定的名稱,像是 precise、raring 等等的。記得有 dch 指令可以做編修,用編輯器是比較快。
  6. 修改 debian/rules:內容出乎意料的簡單,在修改時,則是用 override_ prefix 去修你想調整的 target。例如:想改安裝地點,就用
    override_dh_install:
        $(MAKE) DESTDIR=$(PWD)/debian/tmp install

    想調整 configure,就用

    override_dh_auto_configure:
        dh_auto_configure -- --enable-python=yes

    想忽略自動測試,就用

    override_dh_auto_test-indep:

    這樣子,這也是手冊上建議的作法。

  7. 安裝相關的套件:python-gi-dev、python3.2-dev、libgirepository1.0-dev、libgio2.0-cil-dev、libgit2-dev、gobject-introspection。
  8. 好了就可以用 dpkg-buildpackages -b 來建立 debian package 了。

建立完之後,把該裝的都裝好,發現 gedit plugin 還是不能用,說找不到 python3。

結論是,這是一篇用來回憶打包 debian package 的文。

 

參考資料:

activemq 的 persistent

因為希望 ActiveMQ Queue 裡的東西在重新啟動後還能存在,就特別做了實驗,結果真的 Queue 裡的東西會消失。

查了很多的文件,也試了不少設定,像是在 broker 裡設 persistent=”true” (預設本來就是 persistent) 或是加了 jdbcPersistentAdapter 等,都不行,後來才發現是 stompest.async 這邊在送的時候,得特別在 headers 裡加上 {‘persistent’: ‘true’} 才可以讓訊息保存在 Queue 裡。

程式一樣是用官方的範例,主要修改的地方在 producer.py:[python] yield client.send(self.QUEUE, json.dumps(
{‘count’: j}), headers={‘persistent’: ‘true’},
receipt=’message-%d’ % j)
[/python]

這次找資料也找到不少解說 ActiveMQ 的文件:

activemq 與 python stompest.async 的安裝

簡單的進行幾個 MQ 的比較之後,決定用 ActiveMQ,原因是因為它使用 Java 編寫,在佈署上顯的比較簡單,只要下載下來,有 JDK 就沒問題了。

要搭配 STOMP 的話,得修改 conf/activemq.xml ,找到 transportConnectors 節段,加入:
<transportConnector name="stomp" uri="stomp://0.0.0.0:61613"/>

以前景模式啟動的話,是輸入 bin/activemq console,當作 daemon 啟動的話,是輸入 bin/activemq start 。
用 STOMP 的話,依照 Apache ActiveMQ 網站上的說法,會比較缺少安全性,所以會建議搭配 SSL 或 NIO [2]。

Python 的部份,我是直接使用 stompest.async,它是建構在 twisted 之上,這個用 pip 安裝就可以。範例我是參考 stompest.async examples

範例沒什麼問題,接下來就是要熟悉跟了解如何使用了。

參考資料:

  1. STOMP protocol
  2. activemq Unknown data type: 69

將 python package 快速打包為 debian package – stdeb

這兩天看到 clonedigger,這是一個可以找尋程式裡是否有相似重複片段的工具,在找到重複的片段以後,就可以加以重整。老樣子去 ubuntu ppa 找了一下,發現沒有人打包,那麼就只能透過 east_install/pip 來安裝或是自己打包了。

這次就試著自己打包看看。

一般 python package 都會提供 setup.py ,這是使用 python 內建的 module:distutils 來建立的,在官方 distutils 的文件有提到使用方法。裏面就已經提供了打包為 RPM package 的選項,但是就沒有 debian packages。上網找了一下,這篇 Python distutils – does anyone know how to use it? 提供了不少可以參考的文件,循線找到這篇 Standard way to create debian packages for distributing python programs? ,裏面提到了 stdebstdeb 可以透過 easy_install/pip 來安裝,很幸運的是我的 Ubuntu 12.04 剛好有打包這個進去,所以只要用 apt-get 安裝 python-stdeb 就可以了。

接著,就可以參考 stdeb 文件,用 python setup.py –command-packages=stdeb.command bdist_deb 建立 debian package 了,打包好的 debian package 可以在當前目錄的 deb_dist 下找到。