3 Twistedクイックレシピ

DNSの逆引き名前解決


インターネットの根幹をなすプロトコルの一つにDNSがあります.ドメイン名からIPアドレスを検索する正引きに比べて,IPアドレスからドメイン名を検査するDNSの逆引きはとても重い(遅い)処理です.これは、失敗することが多く,タイムアウトするまで応答を待つためです.検索を開始してから検索結果を得るまで,数秒や数十秒,数分も待たされることもあります.また,その待ち時間のほとんどは,ネットワークからの応答を待っているだけです.

DNSの逆引きは,Webサーバのログ解析などで使用されます.WebサーバのアクセスログのIPアドレスから,ドメイン名を逆引きすることで,どのようなドメイン,ISPからアクセスされているのかを特定することができます.

DNSの逆引きを行うサンプルコードが次のコードになります.
from twisted.names import client
from twisted.internet import reactor,defer
import sys
import fileinput

# (5) DNSの逆引きの結果の処理
def handleResult(results):
    # DNSの逆引きの結果を格納した配列から
    # ここの結果を取り出す
    for result in results:
        success, r = result
        if success and r[0]:
            # (6)逆引きの結果を取得して標準出力に表示
            print r[0][0].payload.name
    # reactorを終了
    reactor.stop()

if __name__ == "__main__":
    deferredList = []
    # (1) 引数で指定したファイルから1行ずつ処理
    for addr in fileinput.input(sys.argv[1]):
        # IPアドレスを 1.2.3.4の形式から逆引きするために
        # 4.3.2.1.in-addr.arpaに変換
        addr = addr.strip()
        ptr = '.'.join(addr.split('.')[::-1]) + '.in-addr.arpa'
        # (2) DNSの逆引きを実行
        c = client.lookupPointer(ptr)
        # (3) 結果をまとめて処理するためにdeferredオブジェクトを
        # 配列に格納
        deferredList.append(c)
    # (4) DeferredListにより結果をまとめて処理する
    defer.DeferredList(deferredList, consumeErrors=True).addCallback(
        handleResult)
    # reactorの開始
    reactor.run()



テキストファイルに改行区切りで検索したいIPアドレスを記述します.
1.2.3.4
5.6.7.8
9.10.11.12


データが保存できたら,次のコマンドをコンソールから実行します.
python dns_reverse_lookup.py data.txt


数秒待つと,次の結果が標準出力に出力されます.今回の例では,検索した3つのIPアドレスのうちの一つが逆引き名前解決ができなかったので,結果は二つだけになります.
some.example.com
another.examle.com


通常,DNSの逆引きを効率的に行うためには,複数のスレッドで同時に実行する必要があります.しかし,スレッドを多く作成すると同期の問題が発生したり,待機状態のスレッドが大量にできるなど,期待するほど高速になりません.サンプルコードでは,名前解決を非同期で行うことで大量の逆引きを行っても一つのスレッドで完結するので,同期の問題を回避できます.また,逆引きの最大の遅延は,逆引きにもっとも時間がかかったIPアドレスになります.

それでは,サンプルコードを見ていきます.DNSのプロトコルを扱うTwistedのライブラリはtwisted.namesです.今回はDNSの検索を行うクライアントを使用するので,twisted.names.clientモジュールをインポートしています.

まず,コードの(1)でコマンド引数で与えられたファイルに対して,一行ずつ読みこんで,検索するIPアドレスを取得します.DNSの逆引きにはIPアドレスを直接指定できないので,逆引きで使用できる形式に変換します.

実際の逆引きを行うtwisted.names.clientモジュールの関数はlookupPointerですコードの(2).twisted.names.clientモジュールにはlookupPointer以外にも,SMTPサーバのホスト名を検索するlookupMailExchangeなど,DNSが提供している検索機能を使用するための関数が定義されています.

twisted.names.clientモジュールだけでなく,一般的にTwistedのクライアントモジュールで一つの接続に対して一つの処理しか行わないものは,一連の処理をラップしたユーティリティ関数が定義されています.ユーティリティ関数を使うことで,定型的な処理を記述しなくてもよくなります.

lookupPointer関数はdeferredオブジェクトを返します.deferredオブジェクトを返すことから,lookupPointer関数は非同期に実行されて,結果をdeferredオブジェクトに登録したコールバック関数で処理できることおが分かります.コードの(3)では,deferredオブジェクトを一旦,リストに保存しています.これは,コードの(4)でdefer.DeferredListに登録するためです.

defer.DeferredListとは,複数のdeferreオブジェクトを一つのコールバック関数で処理するためのクラスです.複数のdeferredオブジェクトをリストにいれて,すべての処理が終了したらコールバック関数が呼ばれます.defer.DeferredListのコンストラクタのconsumeErrorsは,deferredオブジェクトの中でエラーが発生した場合,このDeferredListの中で処理するかどうかのフラグです.

defer.DeferredListにコールバック関数を登録する場合も,通常のdeferredオブジェクトと同様にaddCallback関数で登録します.ここでは,handleResult関数を指定しています(コードの(5)).handleResultは一つの引数,resultsをとっています.resultsはリストになっています.リストの各要素は,処理が成功したかどうかを示すブーリアンのフラグと実際の結果を格納したタプルになります.

DNSの逆引きの結果を表示しているのがコードの(6)です.結果は,twised.names.dns.RRHeaderオブジェクトになります.RRHeaderオブジェクトのpayload.nameでドメイン名を取得できます.



メールの送信


インターネットを介したメールでのやりとりは一般的になりました.メールの送信も個人対個人だけでなく,サーバから人の手を通さずに送られてくることが多くなりました.Webでのショッピングだけみても,ユーザ登録,商品の購入,商品の発送などそれぞれの状況に応じて確認メールが送信されます.

メール送信では,一つのメールアドレスに対してだけメールを送信する場合は問題になることは少ないです.しかし,一度に大量のメールアドレスに対して送信する場合は,個々のアドレスへのメール送信の遅延が問題になることがあります.そのほとんどが,名前解決の遅延であったり,メールサーバの応答待ちの状態です.

ここでは,一つのメールアドレスに対してのメールの送信方法を説明します.
# -*- coding: shift-jis -*-
from twisted.mail import smtp
from twisted.internet import reactor, defer
from email.Header import Header
from email.Message import Message


ENCODING = "iso-2022-jp"

# (1)メールのメッセージの組み立て
def createMessage(sender, recipient, subject, body):
    message = Message()
    message["From"] = sender
    message["To"] = recipient
    message["subject"] = Header(subject.encode(ENCODING), ENCODING,
                                header_name="Subject")
    message.set_payload(body.encode(ENCODING), ENCODING)
    return message.as_string()

# (4)処理が成功した時のコールバック関数
def handleSuccess(result):
    print result
    reactor.stop()

# (5) 処理が失敗したときのコールバック関数
def handleError(error):
    print error.getErrorMessage()
    reactor.stop()

if __name__ == "__main__":
    import sys
    smtpserver, sender, recipient = sys.argv[1:4]
    # メール送信用のデータを作成
    message = createMessage(sender, recipient, u"テストメッセージ",
                            u"これは、Twistedでのメール送信テスト用の\r\n"
                            u"メッセージです。")
    # (2) メールを送信
    deferred = smtp.sendmail(smtpserver, sender, recipient, message)
    # (3) コールバック関数とエラーバック関数の登録
    deferred.addCallback(handleSuccess).addErrback(handleError)

    # reactorの開始
    reactor.run()


このコードをmailsend.pyという名前で保存して,次のコマンドを実行します.
python mailsend.py 私のSMTPサーバ 送信者のメールアドレス 受信者のメールアドレス


「私のSMTPサーバ」,「送信者のメールアドレス」,「受信者のメールアドレス」はお使いの環境に応じて指定してください.次は,筆者の環境でのテスト結果です.
 
python mailsend.py localhost from@example.com to@example.com
(1, [('to@example.com', 250, 'Ok')])


もし,SMTPサーバの指定が間違っている場合などは,次のようにlocalhost2というサーバを見付けられなかったというエラーメッセージが英語で出力されます.
python mailsend.py localhost2 ohtani@ariel-networks.com liris.pp@gmail.com
DNS lookup failed: address 'localhost2' not found: (-2, 'Name or service not known').


それでは,コードを見ながら説明します.メールに関連するプロトコルはtwisted.mailモジュールが扱っています.twisted.mailモジュールの中でメールの送信はtwisted.mail.smtpが処理します.twisted.mail.smtpは,SMTPのサーバ(受信)とクライアント(送信)の機能を含んでいます.メールの送信では,twisted.mail.smtpモジュールのsendmail関数で処理します.

twisted.mail.smtpのsendmail関数はメールの送信だけを処理します.この関数を利用するために,プログラマは自分でメールメッセージを組み立てる必要があります.メールメッセージの作成方法は,標準ライブラリを使ったメール送信と全く同じです(コードの(1)).email.Message.Messageクラスのオブジェクトを作成して,From(送信元),To(送信先),Subject(メールのタイトル)と本文を指定します.ここでは,単純にするために最小限の項目だけを設定しています.メールのタイトルや本文は,古いメールクライアントでも処理できるように文字コードはiso-2022-jp(JIS)でエンコードしています.

コードの(2)では,twisted.mail.smtp.sendmail関数をコールして,メールの送信を行います.sendmail関数は,SMTPのサーバ名,送信者のメールアドレス,受信者のメールアドレス,そして,メールのメッセージ文字列を引数にとります.DNSの逆引きのサンプルと同じようにこの関数もdeferredオブジェクトを返します.今回は,返ってきたdeferredオブジェクトに対して,コールバック関数とエラーバック関数を指定します(コードの(3)).

コードの(4)は,sendmail関数が非同期に実行されて,成功したときに処理される関数です.ここでは,結果(result)をそのまま出力してreactorを終了しています.

コードの(5)はエラーが発生したときに呼び出される関数です.エラーが発生すると,Twistedではtwisted.python.failure.Failureオブジェクト(ここでは,errorという変数)がエラーバック関数に渡されてます.このオブジェクトのgetErrorMessage()で簡潔なエラーメッセージを出力できます.通常の例外処理のように,error.printTraceback()を実行することで,トレースバックを出力することもできます.


メールの受信


メールの送信の次にメールの受信について見てみます.メールの受信にはTwistedを使ったSMTPサーバを直接記述することもできますが,ここでは,POP3によるメールの受信方法を説明します.

メールを受信するためのサンプルコードになります.
from twisted.mail import pop3client
from twisted.internet import reactor, defer, protocol
import sys

# (1)POP3のプロトコルを処理するためのPOP3Clientプロトコルクラスの派生クラスを定義
class POP3AccessProtocol(pop3client.POP3Client):
    # (2) おまじない
    allowInsecureLogin = True
    startedTLS = True

    # (3)全ての処理はserverGreetingから始まります。
    def serverGreeting(self, greeting):
        pop3client.POP3Client.serverGreeting(self, greeting)
               
        d = self.login(self.factory.username, self.factory.password)
        d.addCallback(self._loggedIn)
        d.chainDeferred(self.factory.deferred)


    # (4) ログイン処理が成功すると呼ばれるコールバック関数
    def _loggedIn(self, result):
        return self.listSize().addCallback(
            self._gotListSize)

    # (5) リストのサイズを取得したときに呼ばれるコールバック関数
    def _gotListSize(self, sizes):
        messageRetrievers = [self.retrieve(i).addCallback(
            self._gotMessageLine) for i in xrange(len(sizes))]
        return defer.DeferredList(messageRetrievers).addCallback(
            self._quit)
   
    def _quit(self, results):
        print results
        return self.quit()

    def _gotMessageLine(self, messageData):
        for m in messageData:
            print m

# (6) POP3AccessProtocol用のFactoryクラス
class POP3AccessFactory(protocol.ClientFactory):
    protocol = POP3AccessProtocol
    def __init__(self, username, password):
        self.username = username
        self.password = password
        self.deferred = defer.Deferred()

    def clientConnectionFailed(self, connection, reason):
        print reason
        self.deferred.errback(reason)

def handleSuccess(*args):
    print "done"
    reactor.stop()
   
def handleError(error):
    print str(error)
    reactor.stop()

if __name__ == "__main__":
    server, username, password = sys.argv[1:4]
    factory = POP3AccessFactory(username, password)
    factory.deferred.addCallback(
        handleSuccess).addErrback(
        handleError);
    reactor.connectTCP(server, 110, factory)
    reactor.run()


DNSの逆引きやメールの送信のコードに比べて量が多くなっていることがわかると思います.これは,一つの接続で複数の処理を行う(複数のメールを受信する)ために,ユーティリティ関数が使えないからです.

このコードをpopaccess.pyで保存して,次のコマンドを実行してください.
python popaccess.py POPサーバ名 ユーザID パスワード


「POPサーバ名」,「ユーザID」,「パスワード」はお使いの環境に合わせて変更してください.処理が正常に終了すると,すべてのメールの内容が標準出力に出力されます.

コードの(1)では,POP3のプロトコルを処理するためのクラスを定義しています.POP3のプロトコルを0から記述することもできますが,twisted.mail.pop3client.POP3Clientクラスを利用すると便利です.コードの(2)はおまじないです.

コードの(3)でPOP3ClientクラスのserverGreeting関数をオーバーロードしています.この関数はPOPサーバにアクセスして,ハンドシェイク処理が終了したときに呼び出されます.この関数が呼ばれると準備完了の状態になるので,ここから自分の行いたい処理を記述します.POP3へのアクセスの場合,ここでPOP3Clientクラスのlogin関数を実行してログインします.POP3Clientはlogin以外にもlistSize,listUidl,retrieve,quitなどのPOP3コマンドを扱うためのメソッドを提供しています.

ログイン処理に使用するユーザIDやパスワードなど,プロトコルオブジェクトのライフサイクルを越えたデータはFactoryクラスに保存しています.login処理自体も非同期で行われるので,ログイン後に行いたい処理は_loggedIn関数に記述して,コールバックに追加します.

serverGreetingの最後の行のchainDeferredは,引数で指定したdeferredオブジェクトのコールバックチェーンにに割り込みをかけて,実行するコールバックの順番を変更するためのものです.chainDeferredは複雑です.ここでは,おまじないだと思っていてください.

POPサーバへのログインが成功すると_loggedIn関数がコールされます.この関数の中で次に行う処理を記述します.ここでは,POP3ClientのlistSize関数を実行してメールボックスにあるメールの数を取得しています.listSize関数もdeferredオブジェクトを返すので,このオブジェクトに次に行う処理(コールバック関数)を登録します.

コードの(5)では取得したリストサイズに対して,retrieveでメッセージを取得しています.retrieve関数を実行して取得したdeferredオブジェクトは,defer.DeferredListクラスに登録して,すべてのメッセージが取得できたら,接続を終了しています.

コード5の(6)のPOP3AccessFactoryクラスは,POP3AccessProtocol用のFactoryクラスです.初期化関数の中で,Protocolオブジェクトの生存期間(接続が確立してから切断されるまでの期間)を越えたデータを設定しています.ここでは,ユーザ名とパスワード,エラーが発生したときに正常に終了できるように,deferredオブジェクトの作成しています.


定期実行


プログラムの中でタスクを定期的に実行したいことや,タイマーを使って実行したいことがあります.Twsitedでは,これらの機能をサポートしています.

数秒後にタスクを実行したい場合は,twisted.internet.reactor.callLater関数を使用します.次のコードは1.2秒後にdoMytask関数を実行しています.callLaterの第1引数は何秒後に実行するか指定します.第2引数は,実行する関数です.第3引数以降は登録した関数の引数として渡されます.
from twisted.internet import reactor

def doMyTask(data):
    print data

reactor.callLater(1.2, doMyTask, "called later")
reactor.run()


次のコードは定期的にタスクを実行します.twisted.internet.task.LoopingCallで定期実行するオブジェクトを登録します.第1引数は定期的に実行する関数で,第2引数以降は登録した関数の引数として渡されます.そして,startメソッドに定期実行する間隔を指定して実行します.
from twisted.internet import reactor,task

def doEverySecond(data):
    print data

myTask = task.LoopingCall(doEverySecond, "Call Every Second")
myTask.start(1.0)

reactor.run()


TwistedでつくるWebサーバ


Twistedは強力なWebサーバ機能を持っています.AppleのiCalサーバもTwistedのWebサーバ機能を使っています.また,Twistedのホームページ自体もTwistedで構築されています.

Twistedの正式なプロジェクトとして提供しているWebフレームワークは2種類あります.twisted.webとtwisted.web2です.twisted.webは,HTTP 1.0をサポートしています.twisted.web2は,HTTP 1.1をサポートしています.

Twistedのプロジェクトとして2種類あるので混乱するかもしれません.twisted.webは,twistedの持つ非同期プログラミングのメリットを十分発揮できない作りになっています.具体的には,一つのリクエストがくると,レスポンスを返すまでスレッド全体をブロックしてしまいます.deferredなどの機能もサポートしていないため,データベースを非同期で処理することができません.

twisted.web2は,HTTP 1.1のサポートとともにtwisted.webでの問題点を解消するために設計段階からやりなおしています.現在はまだ,バージョン0.2でAPIも安定しているとはいえませんが,twisted.webにはない面白い機能があります.ここでは,twisted.web2を使って,Webサーバを作ってみましょう.

静的ファイルをフィードするWebサーバ


まず,簡単なWebサーバとして,特定のディレクトリにあるファイルをフィードするWebサーバを作ってみます.次のコードをsimplewebserver.pyとして保存します.
from twisted.web2 import server, static
from twisted.web2.channel import http
from twisted.internet import reactor

# 指定したディレクトリをフィードするためのリソースを作成
toplevel = static.File(".")
# 作成したリソースをWebサイトのトップに指定
site = server.Site(toplevel)

# ポート番号1234でリッスンするWebサーバを作成
reactor.listenTCP(1234,
                  http.HTTPFactory(site))
reactor.run()


次のコマンドで実行します.
python simplewebserver.py


次の図がWebブラウザでhttp://localhost:1234/にアクセスしたときの画面です.index.htmlがあれば,それを表示します.なければ単にディレクトリにあるファイルをリストします.


このコードでは,static.Fileで指定したディレクトリ以下にあるファイルをフィードするためのリソースを作成しています.twisted.web2では,すべてのURLがリソース(twisted.web2.resource.Resource)という単位で扱われます.リソースはツリー構造になっており,ツリー構造はそのままURLにマッピングされています.ツリー構造にはツリーのルートが必要です.それが,twisted.web2.server.Siteになります.ここにツリーのトップを指定して,listenTCPでリッスンします.

WSGIへの招待


twisted.web2の面白い機能の一つとしてWSGI(Python Web Server Gateway Interface: PEP333)があります.これは,Webサーバが変わってもPythonで書かれたWebアプリケーションやフレームワークが動作できるようにするためのインターフェースを定義しています.WSGIをサポートしているフレームワークには,最近はやりのTurboGearsやDjango,Zope3があります.ここでは,Twistedのサイトでも使用されているtrac(wikiとバグトラッキングシステム,ソースコード管理システムのサブバージョンを統合したもの)を例に,Twisted上でtracを動作させてみます.tracはSoftware Design2006年12月号のtracの特集を参照してください.

tracのセットアップ


まず,次のコマンドを実行して,mytracというプロジェクトを作成します.コマンドを実行するとインタラクティブにプロジェクト名などきかれますが,ここでは全てデフォルト(すべてリターンキーを押すだけ)のまま進めます.
trac-admin mytrac initenv

WSGI経由でtracを起動


次にtracをWSGI経由で利用できるようにします.
import trac.web.main
import trac.web.standalone

from twisted.web2.wsgi import WSGIResource
from twisted.web2 import server
from twisted.web2.channel import http
from twisted.internet import reactor


# (1) WSGIResourceを継承したTracのリソースを作成
class TracResource(WSGIResource):
    # (2) TracResourceの初期化
    # tracのプロジェクトへのパスを指定する
    def __init__(self, path):
        self.path = path
        WSGIResource.__init__(self,
                              self.tracApplication)
    # (3) WSGIのインターフェースの実装
    def tracApplication(self, environ, start_response):
        environ['trac.env_path'] = self.path
        environ['trac.base_url'] = 'http://localhost:1234/trac'
        return trac.web.main.dispatch_request(environ, start_response)

# (4) TracResourceをWebサーバのルートになるようにして1234のポートでリッスン
reactor.listenTCP(1234,
                  http.HTTPFactory(server.Site(TracResource("mytrac"))))
reactor.run()

WSGIのアプリケーションを扱うのは,twisted.web2.wsgi.WSGIResourceです.ここでは,WSGIResourceを継承したTracResourceを定義します(コードの(1)).正確には,WSGIResourceは通常のResourceではなく,ちょっと特殊なリソースです.

コードの(2)はTracResourceの初期化を行います.初期化には,tracのリポジトリへのパスを指定します.そして,WSGIResourceのコンストラクタを呼び出しています.コンストラクタには,WSGIのアプリケーション(WSGIが規定したインターフェースの実装)を指定します.ここでは,tracApplication関数になります.

コードの(3)のWSGIのインターフェースを実装しているtracApplication関数を定義しています.tracでは,リポジトリへのパスとURLを辞書に格納してtrac.web.main.dispatch_resquest関数(tracのWSGIインターフェースの実装)を呼び出しているだけです.

最後にsimplewebserver.pyと同様にポート番号1234でリッスンして(コードの(4)),reactorを開始しています.

次のコマンドを実行して,ブラウザでアクセスしてみましょう.
python mytrac.py


次のような画面が表示されれば成功です.


tracの認証機能


通常のtracではベーシック認証によるユーザ認証ができます.mytrac.pyではまだ,認証機能をサポートしていません.次に,先ほどのコードを改造してベーシック認証をサポートしたものが,次です.
import trac.web.main
import trac.web.standalone

from twisted.web2.wsgi import WSGIResource
from twisted.web2 import server
from twisted.web2.channel import http
from twisted.internet import reactor
import sha


class TwistedAuthenticator(trac.web.auth.BasicAuthentication):
    def __init__(self, htpasswd, realm):
        trac.web.auth.BasicAuthentication.__init__(self, htpasswd, realm)
        self.htpasswd = htpasswd
       
    def test(self, user, password):
        # Re-load the password file because it might have changed since we loaded it.
        self.load(self.htpasswd)
       
        the_hash = self.hash.get(user)
        if the_hash is None:
            return False
       
        # Try old-style roundup passwords
        if sha.new(password).hexdigest() == the_hash:
            return True
       
        return trac.web.auth.BasicAuthentication.test(self, user, password)

class TracResource(WSGIResource):

    def __init__(self, path, htpasswd):
        self.path = path
        self.htpasswd = htpasswd
        self.auths = {"twisted": TwistedAuthenticator(self.htpasswd, 'trac'),
                      "*": TwistedAuthenticator(self.htpasswd, 'trac')}
        WSGIResource.__init__(self,
                              self.authTracApplication)

    def authTracApplication(self, environ, start_response):
        path_info = environ.get('PATH_INFO', '')
        path_parts = filter(None, path_info.split('/'))
        if path_parts and path_parts[0] == 'login':
            env_name = path_parts[0]
            if env_name:
                auth = self.auths.get(env_name, self.auths.get("*"))
                if auth:
                    remote_user = auth.do_auth(environ, start_response)
                if not remote_user:
                    return []
                environ['REMOTE_USER'] = remote_user
        return self.tracApplication(environ, start_response)
       
       
    def tracApplication(self, environ, start_response):
        environ['trac.env_path'] = self.path
        environ['trac.base_url'] = 'http://localhost:1234/trac'
        return trac.web.main.dispatch_request(environ, start_response)



reactor.listenTCP(1234,
                  http.HTTPFactory(server.Site(TracResource("wsgi", "./htpasswd"))))
reactor.run()



twisted自体が認証フレームワークをもっていますが,ここでは,tracが提供している認証機能を使っています.次のコマンドを実行して,htpasswdファイルを作成してます。
# htpasswd2 -c htpasswd ユーザID
New password:
Re-type new password:


その後、次のコマンドを実行します.
python mytrac.py


http://localhost:1234/loginにブラウザからアクセスすると,次のように認証ダイアログが表示されます.


twistdによる起動


さて,以上でtracを起動するためのHTTPサーバのコードができました.実際の運用では,このサーバをUnixのデーモンやWindowsのサービスとして起動すると思います.Twistedでは,twistdというヘルパープログラムが用意されていて,このプログラムを使うと容易に自分で作成したプログラムをデーモンとして動作させたり,Windowsのサービスに登録できます.

twistdで動作させるためには,次のコードを作成します.
# (1) mytracから、TracResourceをインポート
from mytrac import TracResource

from twisted.web2.server import Site
from twisted.web2.channel.http import HTTPFactory
from twisted.web2.static import File
from twisted.application.service import Application
from twisted.application.internet import TCPServer

# (2) アプリケーションオブジェクトを作成
application = Application('Trac')

# (3)Trac用のResourceオブジェクトの作成
root = TracResource("mytrac", "htpasswd")

# (4) サービスとして、HTTPサーバをアプリケーションに登録
TCPServer(1234, HTTPFactory(Site(root))).setServiceParent(application)


まず,最初の行で,先ほどで作成したモジュールをインポートしています(コードの(1)).次にコードの(2)でtwisted.application.service.Applicationのオブジェクトを作成しています.これは,twistdがこのスクリプトをロードするときに,アプリケーション(サービス)として認識するためのものです.コードの(3)でResourceオブジェクトとtwisted.web2のルートリソースオブジェクトを作成しています.コードの(4)では,ポート番号1234でHTTPサーバを作成し,アプリケーションに登録します.

これを次のコマンドで実行します.
twistd -y mytracservice.py


コマンドを実行すると,バックグラウンドでプロセスが実行されます.twistdには,--pidfileで作成するPIDファイル名を変更したり,--logfileで出力するログファイルを変更できます.特権ポートでリッスンするときにuidやgidを変更したい場合はそれぞれ,--uid,--gidで変更することができます.


最後に


Twistedはとても幅の広いフレームワークです.今回紹介しなかった,UDPやUnixソケット,データベース,ZopeInterfaceによるコンポーネントアーキテクチャなど,さまざまな機能が提供されています.すべての機能を使わなくても,自分の興味のある部分から非同期プログラミングを始めることができます.この機会に是非一度非同期プログラミングの世界に触れてみてください.


Comments