2012年11月6日火曜日

Mercurial 拡張を作成してみた

Mercurial の拡張に初挑戦。

思ったより簡単に実装できてよかったです。

今回の題材

今回作ったのは archive コマンドの拡張で指定範囲のリビジョンで変更したファイルを対象とするというもの。 きっと既にあると思いつつも、拡張を作ってみたかったこともあり、着手することにした。

hg archive のオプションに --change REV1:REV2 を追加するというものだ。

参考

主に参考にしたのは、

上の場所を主軸として、今回は wrapcommand を使ったので、そ の場合の構成は Mercurial のソースリポジトリ中、 hgext/largefile を参考にした。

また、同じくMercurial ソース中のhgext/commands.py に標準 のコマンドが定義されているので、ラップした archive コマン ド (実態はPython の関数) の引数を調べるのに使った。

wrapcommand

既存のコマンドを拡張する場合には mercurial.commands.wrapcommand を使うようだ。

どうやら、コールバック uisetup() が呼び出されたタイミング でラッピングするらしい。

wrapcommand の戻り値が cmdtable の要素 (= 三つ組のタプル) に相当するようなので、そのタプルの 2 つ目にオプションの定 義を追加する。

ソースコード

# coding: utf-8

from mercurial import commands, scmutil, extensions
from mercurial.i18n import _

def overridearchive(orig, ui, repo, dest, **opts):
    # get sorted revision numbers
    if opts.has_key('change'):
        older, newer= scmutil.revpair(repo, opts['change'])
        older_ctx = repo[older]
        newer_ctx = repo[newer]
    if newer_ctx.rev() == None: # if unary
        newer_ctx = older_ctx
    if newer_ctx.rev() < older_ctx.rev():
        older_ctx, newer_ctx = newer_ctx, older_ctx

    # get changed files between the revisions
    files = set()
    for revno in range(older_ctx.rev(), newer_ctx.rev() + 1):
        for f in repo.changectx(revno).files():
            files.add(f)
    files = sorted(files)

    # show changed files
    if ui.verbose:
        for f in files:
            ui.write(f + '\n')

    # arrange opts to be passed into the archive command
    del opts['change']
    opts['include'] = list(files) # OVERWRITES

    return orig(ui, repo, dest, **opts)


def uisetup(ui):
    entry = extensions.wrapcommand(commands.table, 'archive', overridearchive)
    addopt = [('', 'change', [], _('REV1:REV2 format'))]
    entry[1].extend(addopt)

2012年10月6日土曜日

PL/SQL のオブファスケータ

Oracle には、wrap という PL/SQL のオブファスケータがあるので、極力公開したくな い実装を配布する場合は、使ってみるといいかもしれない。

先日紹介した PL/Unit でもこの方法が採られていて、PACKAGEつまりインターフェイスは 公開されているが、PACKAGE BODYつまり実装は公開されていない。(しかし、PACKAGE BODYは配布されている)

wrap iname=source.sql oname=obfuscated.sql

というように使う。 oname に指定したファイルとして難読化されたプログラムテキストが書き出される。

詳細は、PL/SQLのソース・コードの不明瞭化 を参照のこと。

2012年9月11日火曜日

rst2blogger その2

rst2bloggerでBloggerに投稿する際にpタグが消えてしまう現象ですが、rst2bloggerのソースを変更することで対応できました。

cli.py

#post_to_update.content = atom.data.Content(type='html', text=post_content)
post_to_update.content = atom.data.Content(type='text', text=post_content)

rst2blogger

原典管理どうしよう、と思いつつも、reSTで書けると聞いて導入してみます。
若干ソースコードを変更しつつ導入したので、メモ。

導入

gdata

Google+ と連携(よく分からない)している場合、アップロード時にエラーが発生するようです。 Issue 578 にあるように変更します。
圧縮ファイルをダウンロードして、src/gdata/blogger/data.py を変更。
#BLOG_ID2_PATTERN = re.compile('tag:blogger.com,1999:user-(\d+)\.blog-(\d+)')
BLOG_ID2_PATTERN = re.compile('tag:blogger.com,1999:user-(g?\d+)\.blog-(\d+)')
で、インストール。
python setup.py install

rst2blogger

pip からインストールできますが、自分的にはちょっと不都合があるので修正します。
そのままの状態でアップロードすると、見出しのレベルが h4 から始まってしまいます。 対して、Blogger のエディタから選択できる見出しレベルは h2 から。
そういうわけで、ソースコードをいじります。 対象は、rst2blogger/cli.py
#post_title, post_content = rst2post.format_post(options.filename)
post_title, post_content = rst2post.format_post(options.filename, 2)
その後、インストール。

気になるところ

rst2html.py なら p 要素で囲まれる部分が、全て br 表現になってしまう。 どうしようもないのだろうか?

2012年9月10日月曜日

PL/SQL テスティングフレームワーク

結論: PL/Unit が一番お手軽っぽいよ。utPLSQL もよさそうですよ。

種々のテスティングフレームワーク

Wikipedia の ユニットテスト・フレームワーク一覧 にあるように、PL/SQL にも色々なテスティングフレームワークがあるようだ。

今回はPL/Unitを使うことにしたが、少しだけ別のフレームワークも見てみた。

utPLSQL

最低限のテスト実装量は PL/Unit と同程度で済みそう。 また、アサーション関係の関数が型毎に分かれていないため、覚える量が少ないのも利点。

テスト対象のパッケージ中にテストを各スタイルにも対応できるように、テスト関係の プロシージャ/関数にプリフィックスを設定できるようになっている。 (まぁ、特に問題がなければ、テスト対象とテストはパッケージを分ける方が好きだけど)

他にも色々な機能が備わっているようだし、使ってみたい内容だ。

最終リリース日が2005年6月2日と古いが、PL/SQL の言語仕様がさほど変わっていないからかもしれない。 (ちなみに PL/Unit は2006年2月11日。大して変わらない)

PLUTO

大変にクセの有りそうなフレームワーク。

外見だけを乱暴に書くと、テストをパッケージではなくてTYPEオブジェクトに記述するようになっている。 まあ、それは良いとして…

テスト実装に必要なタイプ量が多い。 pluto_util_object の実体化やらそのメンバを呼び出すところやら、毎回書かないといけなさそう。

リファレンスっぽいものを見つけられていない。チュートリアルはあるんだけど… テスト実装するのが大変そうに感じてしまう。

最終リリース日は2008年7月18日。

ruby-plsql-spec

Ruby で頑張る、ややDSL感溢れるフレームワーク。

PL/SQL だけでは環境を構築できないわけですが、Ruby 好きならいいのかも? 環境構築に関しては、多分 gem さえ入っていれば何とかなったりするんじゃないでしょうか。

最終リリース日は2012年4月16日。

PL/Unit による PL/SQL コードのユニットテスト

PL/Unitというテスティングフレームワークを見つけたので使ってみた。結構お手軽にテストできたので、紹介してみる。
意外とこの PL/Unit を紹介しているページが無いのが残念だ。 しかし、手順は簡単なので、PL/SQL でプログラミングすることがある人には試してもらいたい。(品質的な意味で)

準備

PL/Unit - Test Driven Development for Oracle から ZIP ファイルをダウンロードした後、中身の install_plunit.sql を実行。
インストールのページ にもあるように、データディクショナリを参照するため、権限を持ったユーザで接続する。 結果として、plunit というパッケージが作成される。

テンプレ

最低一つのパッケージが必要。
PACKAGE には setup, teardown, t_{テスト名} を列挙。 PACKAGE BODY には t_{テスト名} の定義を列挙。
teardown には rollback; を入れるといいと思う。

PACKAGE

create or replace package TEST_HOGEHOGE is
begin
    procedure setup;
    procedure teardown;

    procedure t_order_and_cancel;
    procedure t_order_and_modify;
    procedure t_order_cant_modify_after_shipping;
end;
/
show errors

PACKAGE BODY

create or replace package body TEST_HOGEHOGE is
    -- ヘルパー関数など
begin
    procedure setup is
    begin
        update Stock set amount=10 where item_no = '...';
    end;

    procedure teardown is
    begin
        rollback;
    end;

    procedure t_order_and_cancel is
        result number;
    begin
        result := process_order(MY_ORDER_PKEY);
        plunit.assert_equals(0, result, 'order successes');

        plunit.assert_query_equals_value(
                'select amount from Stock where item_no=''hogehoge'' ',
                3,
                '3 hogehoges are still in stock');

        result := process_cancel(MY_ORDER_PKEY);
        plunit.assert_equals(0, result, 'cancel successes');
        -- :
    end;
end;
/
show errors

テストスイート起動

テストの実施に際し、PL/Unit のサイトにはApollo Proというフリーのツールを使うように書いてあるが、WinXP SP3 (32bit)の環境では動かなかった。
コマンドラインからの起動は、SQL*Plusから以下のようにすれば良い。
execute plunit.run_all_tests(owner=>'SCOTT', package_name=>'TEST_HOGEHOGE', show_output=>true);

実行すると、t_ 関数毎にテストの成否が列挙される。
で、仕上げに「The following tests failed:」ということで、失敗したテストが表示される。 実用上それほど問題にはならないが、この内容、最後に失敗したテストだけが表示されているようだ。 テスト数が多いと、失敗したテストを探すのに若干手間取るかもしれない。

2012年8月19日日曜日

Django + uWSGI + NGINX 環境構築メモ on Ubuntu 12.04

Ubuntu 12.04 での環境構築をメモ。

色々なところからの寄せ集めだが、メインとなる情報源は How to Setup a Linux, Nginx, uWSGI, Python, Django Server。 それと前後する作業を追記している。

バージョン

(この前提条件を書いてないと、バージョンごとに設定ファイルの場所が意外と変わってたりして、混乱するんだよね…)
  • Ubuntu 12.04
  • Python 2.7.3 (with virtualenv, virtualenvwrapper)
  • Django >= 1.4
  • uWSGI 1.2.5
  • NGINX 1.2.2.1 (precise)

インストール

Ubuntu 12.04

省略

Python 2.7.3

virtualenv, virtualenvwrapper は、pip からインストール。
使い方は 2009年版Python開発環境を整えよう などを参考にさせていただいた。

以下は例。
$ # PyPy の環境 mypypy を新規に作成する。
$ mkvirtualenv  -v  --python=/usr/bin/pypy  mypypy
$
$ # mypypy 環境に切り替える (Tab 補完が効くのね)
$ workon  mypypy
(mypypy)$ pip  install  django
(mypypy)$
(mypypy)$ # mypypy 環境から離れる
(mypypy)$ deactivate
virtualenv を格納するディレクトリは、環境変数 WORKON_HOME に設定する。(デフォルトは $HOME/.virtualenvs)
あと、virtualenvwrapper.sh か virtualenvwrapper_lazy.sh のどちらかを、virtualenvwrapper の Shell Startup File にもあるように、.bashrc の末尾に追加しておく必要がある。
export WORKON_HOME=$HOME/devel/virtualenvs
source `which virtualenvwrapper.sh`
追補。
上記リンク先には環境変数 PROJECT_HOME なるものが設定されているけど、mkproject などのプロジェクト系コマンドを使わないなら設定する必要はないようだ。
ちなみに mkproject  hoge すると、mkvirtualenv hoge & workon hoge & プロジェクトフォルダを PROJECT_HOME 下に作る & cdproject hoge …という一連の処理をしてくれるようだ。(New virtualenvwrapper commands より)
多機能ではあるが、使う場面があるかなー?

あと上では PyPy の環境を作っているけど、あくまでコマンド例として記載しただけ。
PyPy の場合はいくつかパッチを当てたりしないといけないようなので、動作検証は全くしていない。

Django

1.4 とそれ以前では django-admin.py startproject したときのディレクトリ構造が微妙に違っている。
1.4 形式では wsgi.py が作成されるようになっているので、後述する uWSGI の起動部分で手間がかからない。

uWSGI

「みゅーうぃすきー」って発音するのか??
まあとにかく、こちらは apt-get でインストール。
$ sudo  apt-get  install  uwsgi
Python の pip でインストールする方法が書かれているものもあったけど、virtualenv 下でのインストールやら起動の仕方が分からなかったので上記の方法をとった。
なんかつまづきそうだったし。まぁ、virtualenv 外にインストールされるような気がするけど。

NGINX

apt-get するんだけど、Ubuntu のリポジトリが古いバージョンのままなので、NGINX 公式が提供しているリポジトリを追加する。
下の手順は、公式のダウンロードページに全て書いてある。
  1. NGINX 公式の公開キーをダウンロードする。(これね)
  2. $ sudo  apt-key  add  nginx_ signing.key
  3. $ sudo  vim  /etc/apt/sources.list
    deb http://nginx.org/packages/ubuntu/ precise nginx
    deb-src http://nginx.org/packages/ubuntu/ precise nginx
  4. $ sudo  apt-get  update
  5. $ sudo  apt-get  install  nginx
NGINX 用のユーザとグループが作成されているはず。
$ cat  /etc/passwd
というわけで、インストールは完了。

Django プロジェクトを uWSGI + NGINX で動かす


Django プロジェクトを作成し、uWSGI 単体での動作確認まで


virtualenv を作成する(必要に応じて)

以降の作業をする前に、virtualenv を作成しておく。(当然、virtualenv を利用しない場合はこの手順は省略する)
$ mkvirtualenv  -v  --python=`which python`  dj
(dj)$ pip  install  django


Django プロジェクトを作成する

プロジェクトを作成し、runserver で動作することを確認する。
(dj)$ django-admin.py  startproject  django_uwsgi_nginx
(dj)$ cd  django_uwsgi_nginx
(dj)$ python  manage.py  runserver


uWSGI 単体動作用の INI ファイルを作成する

uwsgi_http.ini
[uwsgi]
http = :8000
chdir = <django_uwsgi_nginx の絶対パス
>
module = django_uwsgi_nginx.wsgi
virtualenv = 
<WORKON_HOME(virtualenvsの場所)の絶対パス>/dj
※virtualenv は、利用していない場合は記述しない。

uWSGI を単体で動かす。
上で作成した INI ファイルを指定して起動。http://localhost:8000/ を確認。
$ uwsgi  --ini  uwsgi_http.ini


NGINX を経由させる

uWSGI と NGINX の設定が必要。先に uWSGI の方を設定する。


プロジェクトを /var/www 下へ

まず、プロジェクトディレクトリを /var/www の下に持ってくる。
プロジェクト django_uwsgi_nginx がログインユーザのホーム中にあったため、/var/www の下に cp してくる。
$ sudo  mkdir  /var/www
$ sudo  cp  -R  <django_uwsgi_nginx の絶対パス>  /var/www/
django_uwsgi_nginx
また、nginx ユーザで動かすため、ユーザ nginx、グループ nginx に所有権を移す。
$ sudo  chown  -R  nginx:nginx  /var/www/django_uwsgi_nginx


uWSGI 起動設定

/etc/init/uwsgi.conf に記述する。
新しめの Ubuntu では init プログラムとして upstart というものが採用されているらしく、/etc/init ディレクトリ内に「どの条件(イベント)で uWSGI がどう振る舞うべきか」を記述するらしい。
というか、init 関係の流れってあまり理解していない。要勉強だな…

で、記述する内容は以下のとおり。(太文字は、情報源から変更した部分)

description "uWSGI"start on runlevel [2345]stop on runlevel [06] respawn exec uwsgi --master --processes 4 --die-on-term --uid nginx --gid nginx \--socket /tmp/uwsgi.sock --chmod-socket 660 --vhost --logto /var/log/uwsgi.log \--plugins python,http
ユーザを nginx に、プラグインに http を足している。http は不要のような気がする。(Ubuntu のリポジトリの場合は必要らしいけど、NGINX 公式のリポジトリからインストールしているし)


NGINXのサーバ設定


NGINX の設定ファイルは、/etc/nginx/conf.d の中に *.conf のファイル名で作成する。
情報源によっては /etc/nginx/sites-enabled の中に作成するというものがあったが、今回インストールしたバージョン (1.2.2.1) では存在しなかった。

server {
    listen          80;
    server_name     $hostname;
    location /static {
        alias /var/www/django_uwsgi_nginx/static;
    }
    error_page   404              /404.html;
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    location / {
        include     uwsgi_params;
        uwsgi_pass  unix:/tmp/uwsgi.sock;
        uwsgi_param UWSGI_PYHOME    
<WORKON_HOME(virtualenvsの場所)の絶対パス>/dj;
        uwsgi_param UWSGI_CHDIR     /var/www/django_uwsgi_nginx;
        uwsgi_param UWSGI_MODULE    django_uwsgi_nginx.wsgi:application;
    }    
}
恐らくは、virtualenv を使っていない場合は UWSGI_PYHOME の指定は不要だと思う。

uwsgi_param で uWSGI の設定をしているため、前に書いたような INI ファイルは今回は不要。

設定ファイルを置いたら、configtest をして、文法エラーが無いことも確認する。
その後、uWSGI も NGINX も、サービスを再起動させる。
$ sudo  service  nginx  configtest
$ sudo  service  uwsgi  restart
$ sudo  service  nginx  restart
後は、http://localhost/ にアクセスする。