ふにゃるんv2

もとは、http://d.hatena.ne.jp/Wacky/

pysvnを使って、ソースコードのリポジトリ情報を取得してみましょう

Subversionを使ってソースコードをバージョン管理しているんですが、これを使って(主に自分用の)自動計測の真似事でも出来ないかな?と思った訳です。で、Subversion APIを使ってみようと思いました。

これを見ると、SubversionにアクセスするAPIがあり、Python向けのバインディングとして pysvn があるようですので、これを使って情報を取得してみましょう。

ダウンロード

最初迷ったのが、Subversionの本家ダウンロードとpysvnのダウンロードの、どちらからライブラリをダウンロードした方が良いのか?という点でした。


Subversion本家を見ると「svn-win32-1.5.5_py25.zip」ってのがあって、コメントに「Python 2.5 bindings for Subversion on Win32.」って書いてあるんですね。
こっちでもOKなのかなぁ?と思って試しにダウンロードして調べてみたら、こっちはセットアップ(setup.pyとか)がありませんでした。


で、pysvnの方は、セットアップが準備されているようですので、そちらを使った方が楽だと思います。
自分は、'py26-pysvn-svn153-1.6.2-1067.exe'をダウンロード&インストールしました。
(インストールは、ファイルをダブルクリックするだけ)

資料

SubversionのリポジトリをアクセスするAPIの資料としては、以下があるようです。

後は、以下でゲットできるSubversionのCHMファイル中の「第8章 開発者の情報」が良いのかなぁ?と思っています。


見る限り、リファレンスを見ながら いじくり回していたら、何とかなるかなぁ?というレベルでした。
それでは、以降で簡単なコードを示して見ます。

ファイルの情報を得る

まず、作業中のファイルの情報を得ましょう。
あるファイルの情報を得るには、'pysvn.Client.info'メソッドでOKのようです。

#!/bin/env python
# -*- encoding: cp932 -*-

import pysvn
import time
client = pysvn.Client()

fpath = r"F:\Wacky\test2\Python\20090104.pysvn\test1.py"
o = client.info(fpath)
for i in o:
    if i.find("time") >= 0:
        print i, ":", time.ctime(o[i])
    elif i.find("revision") >= 0:
        print i, ":", o[i].number
    else:
        print i,":", o[i]

実行すると、以下のような感じになります。

F:\Wacky\test2\Python\20090104.pysvn>python test3.py
is_absent : 0
properties_time : Thu Jan 01 09:00:00 1970
conflict_old : None
commit_author : wacky
schedule : normal
repos : file:///C:/svn/code_sample2
copy_from_url : None
is_copied : 0
lock_owner : None
copy_from_revision : -1
property_reject_file : None
lock_creation_date : 0.0
conflict_work : None
kind : file
lock_token : None
text_time : Sun Jan 11 23:58:35 2009
lock_comment : None
is_deleted : 0
uuid : None
url : file:///C:/svn/code_sample2/trunk/Python/20090104.pysvn/test1.py
checksum : f3501216e850cf2db0b0f3138814ae77
commit_revision : 47
name : test1.py
conflict_new : None
commit_time : Mon Jan 12 14:24:15 2009
revision : 47

ちなみに、日時(time)の取得時、time.ctimeメソッドを使って浮動小数点数→日時変換しています。

ファイル行数を得る

あるファイルの行数(LOC)を得るには、'pysvn.Client.cat'メソッドで取得したテキスト文字列から、string.splitlines メソッドを使って改行数をカウントすれば良いでしょう。

#!/bin/env python
# -*- encoding: cp932 -*-

import pysvn
client = pysvn.Client()

fpath = r"F:\Wacky\test2\Python\20090104.pysvn\test1.py"
txt = client.cat(fpath)
print "LOC:", len(txt.splitlines())

実行すると、以下のようになります。

F:\Wacky\test2\Python\20090104.pysvn>python test4.py
LOC: 41

ログ情報を得る

過去のチェックインのログを得るには、'pysvn.Client.log'メソッドで取得できるようです。

#!/bin/env python
# -*- encoding: cp932 -*-

import pysvn
import time
client = pysvn.Client()

fpath = r"F:\Wacky\test2\Python\20090104.pysvn\test1.py"
entry_list = client.log(fpath)
print "entry_list count:", len(entry_list)
for v in entry_list:
    print "-----"
    print "revision:", v["revision"].number
    print "author:", v["author"]
    print "date:", time.ctime(v["date"])
    print "message:[%s]" % v["message"]

実行すると、以下のようになります。

F:\Wacky\test2\Python\20090104.pysvn>python test5.py
entry_list count: 2
-----
revision: 47
author: wacky
date: Mon Jan 12 14:24:15 2009
message:[hello python
]
-----
revision: 46
author: wacky
date: Sun Jan 04 19:34:24 2009
message:[]

リポジトリの全ファイルリスト出力

リポジトリの全ファイルリストを出力するには、'pysvn.Client.root_url_from_path'メソッドでリポジトリのルートを取得して、'pysvn.Client.ls'メソッドでファイルリストを得ればOKみたいです。

#!/bin/env python
# -*- encoding: cp932 -*-

import pysvn
client = pysvn.Client()

def ls_walk(ary):
    unification = { 
        0x2014: 0x2015, # HORIZONTAL BAR 
        0xFF5E: 0x301C, # WAVE DASH 
        0x2225: 0x2016, # DOUBLE VERTICAL LINE 
        0x22EF: 0x2026, # HORIZONTAL ELLIPSIS 
        0xFF0D: 0x2212, # MINUS SIGN 
        0xFFE0: 0x00A2, # CENT SIGN 
        0xFFE1: 0x00A3, # POUND SIGN 
        0xFFE2: 0x00AC # NOT SIGN 
    }
    
    for v in ary:
        print "name:", v["name"].translate(unification)
        if v["kind"] == pysvn.node_kind.dir:
            ls_walk(client.ls(v["name"]))

print "list tree"
fpath = r"F:\Wacky\test2\Python\20090104.pysvn\test1.py"
rpath = client.root_url_from_path(fpath)
print rpath

entry_list = client.ls(rpath)
ls_walk(entry_list)

実行すると、以下のような感じになります。

...
name: file:///C:/svn/code_sample2/trunk/VS/20081124.HyperEstraierのバインデ
ィングを作る2/test_app2/Program.cs
name: file:///C:/svn/code_sample2/trunk/VS/20081124.HyperEstraierのバインデ
ィングを作る2/test_app2/Properties
name: file:///C:/svn/code_sample2/trunk/VS/20081124.HyperEstraierのバインデ
ィングを作る2/test_app2/Properties/AssemblyInfo.cs
name: file:///C:/svn/code_sample2/trunk/VS/20081124.HyperEstraierのバインデ
ィングを作る2/test_app2/test_app2.csproj


リポジトリのパスを得る時、単にprint表示せずに translate メソッドを使って変換表示しています(「print "name:", v["name"].translate(unification)」の所)。
これは、unicode文字列からcp932に変換する際の一種のテクニックです。詳しくは、以下をどうぞ。

感想

意外と簡単にリポジトリを操作出来たのでビックリしました。
これで情報をグラフ化したら、面白い計測データが取れるかな?


余談ですが、開発における自動計測(metrics)収集を行いたいなら、(周囲が導入に理解があるとか個人で運用実施可能なら)TracとかRedmineを入れてしまった方が現実的だと思います。
商用ならVSTS(TFS), ClearCaseですかね?VSTSは 一度使ってみたいなぁ。CodePlexがTFSで構築されているそうなので、Team Explorer Client入れれば体験可能かなぁ?
(90日評価版はあるけど、空気のように日々使わないと本気で導入しようという意識付けが働かないんですよね)