Python Web サービス開発者: 第 5 回: Python SOAP ライブラリー 第 1 回

Web サービスのコラムニスト Mike Olson と Uche Ogbuji は、2 回シリーズの第 1 回に当たるこの記事で、Python で使用できる各種の SOAP インプリメンテーションについて論じ、詳しいコード例を提供します。

Mike OlsonFourthought, Inc.

Mike Olson 氏は、企業向けの知識管理アプリケーションのための XML ソリューションを専門とするソフトウェア・ベンダーおよびコンサルタント会社である、Fourthought Inc. のコンサルタントであり、共同設立者です。Fourthoughtは、XML ミドルウェア用のオープン・ソース・プラットフォームである 4Suite を開発しています。



Uche Ogbuji, Consultant, Fourthought, Inc.

Uche photoUche Ogbuji 氏は、Fourthought, Inc. のコンサルタント兼共同設立者です。この会社は、企業のナレッジ・マネジメントのための XML ソリューションを専門とするソフトウェア・ベンダー兼コンサルタント会社です。Fourthought では、XML、RDF、およびナレッジ・マネジメント・アプリケーション用のオープン・ソース・プラットフォームである 4Suite を開発しています。Ogbuji 氏は、ナイジェリア出身のコンピューター・エンジニア兼ライターで、現在は、米国コロラド州ボールダーに住み、そこで働いています。



2001年 9月 01日

過去 3 回の連載で、私たちは 4Suite Server を使用して Web サービス・インプリメンテーションを開発し、4Suite Server の SOAP サポートを利用してきました。(参考文献を参照してください。) Python 用には、他の SOAP インプリメンテーションも存在しています。確かに SOAP は、Python のオープン・ソース活動の中で、かなり人気のあるコーナーのようです。この記事では、Soap.py の動作に注目したいと思います。他のオープン・ソース SOAP プロジェクトの更新については、補足記事を参照してください。それにしても、Python SOAP モジュールの命名については、なんとかしてほしいものです。似通っていて、混乱を招くような名前がたくさんあることを考えると、異なるプロジェクト間での話し合いは、あまり行なわれていないようです。私たちは最近、SOAPy と SOAP.py についてかなりの時間を費やして勉強したにもかかわらず、これらの選択肢を同僚に説明しているときに、それぞれの特徴を思い出せなくて困ったことがあります。補足記事にも述べられているように、実際のライブラリーの中でモジュール名を使用する必要がある場合、この問題はさらにややこしくなります。

このコラムにおける過去 3 回の連載では、4Suite SOAP を扱いました。この記事と次回の記事では、SOAP.py プロジェクトおよび SOAPy プロジェクトの例を示します。これらは、開発が凍結された時点では、類似のプロジェクト中で最も進捗していたようです。W3C の XML プロトコル作業グループが SOAP 1.2 という草案を作成していますが、異なるプラットフォームや言語間で共通した SOAP インプリメンテーションのレベルは、まだ SOAP 1.1 にとどまっており、それも以前のバージョンが強く反映されています。今日 SOAP のバージョンの拡散によって生じている複雑さは、SOAP が約束した単純さを上回っています。

SOAP.py を使用したクライアントおよびサーバー

SOAP.py は、基本的な事柄を守備範囲としています。Web Services Description Language (WSDL) あるいはその他のアドオンはありませんが、Python で SOAP クライアントおよびサーバーをインプリメントするための透過的サポートを備えています。さらに、このパッケージのかなり気の利いた機能もインフラストラクチャーに関連するものです。SOAP.py は、暗号化された SOAP 伝送のために Secure Sockets Layer (SSL) をサポートしているのです。この機能を使用するためには、M2Crypto をセットアップしておく必要があります。M2Crypto は、RSA や DSA から HTTP や S/MIME まで、各種の暗号ツールおよびフォーマットを対象とするライブラリーです。今回の記事では、SOAP.py に関する SSL サポートについては検討しないことにします。

これまでのおはなし:SOAP ユーティリティーは、Python のオープン・ソース活動の中で、かなり人気のあるコーナーのようです。SOAP のプロジェクトとその現状についてまとめておきます。まず、出演者たちを紹介します。

  • 4Suite SOAP: Fourthought によって管理されています
  • SOAPy: Adam Elman によって管理されています
  • SOAP.py: Web Services for Python プロジェクトによって進められているプロジェクトです
  • soaplib: Secret Labs によるものです
  • Orchard: Ken MacLeod によるものです
  • PySOAP: Dave Warner によって管理されています

4Suite SOAP は私たち自身によるインプリメンテーションであり、このコラムの最近の 3 回の連載で使用したものです (リンクについては、参考文献を参照してください)。これは、現在、活発に開発が行なわれています。

SOAPy は、2001 年 4 月にポストされたもので、現在はまだアルファ版以前の段階であり、活発に開発が行なわれているようには見受けられません。

SOAP.py の開発は凍結されています。SOAP.py は actzero という会社の支援を受けたプロジェクトでしたが、この会社は、営業を停止しています。新しい開発者および保守担当者に、ボランティアとして参加するように呼びかけが行なわれています。

soaplib の開発は延期されているようです。Secret Labs がこのところ抱えている作業の膨大さ考えれば、無理もなさそうです。Fredrik Lundh が経営するこのスウェーデンの会社は、Python サークルでは、Fredrik の「f」と「robot」の勤勉さをもじって「eff-bot」と呼ばれ、Python Association 会議のメンバーとなっています。Secret Labs は、Python のカーネルおよび重要なアドオン・モジュールである PythonWare、最先端の Python IDE である PythonWorks、Python Imaging Library、さらにその他の多くの便利グッズを作成しています (そのうちの多くは、Python-URL Web ログに毎日のように書き込まれています)。

Orchard はデータ管理フレームワークであり、基本的には共通インターフェースでさまざまなデータ・フォーマットを管理するために使用されます。これは SOAP サーバーへのリモート・プロシージャー・コールで、ノードと呼ばれる Orchard データ項目を送信するための基本的な方法として、SOAP クライアントをインプリメントしています。

PySOAP は Dave Warner の教会管理的スイートに組み込むことを目的としたプロジェクトですが、まだ 1 つもファイルをリリースしておらず、休眠プロジェクトのようです。

インストール

ディストリビューション (この記事の執筆時点では、SOAPpy 0.9.7 が最新リリースです) をダウンロードし、ファイルをアンパックし、その際に作成されたディレクトリーに移動し、 SOAP.py というファイルを適切な場所にコピーしてください。言うまでもなく、この「適切な」というのがくせ者です。こうした SOAP ライブラリーの多くで、モジュール名として、大文字と小文字をなんらかの形で組み合わせた「soap.py」という名前が使用されていますので、注意が必要です。もちろん UNIX ユーザーは、大文字と小文字が完全に一致しないように気を付ければよいのですが、Windows ユーザーは、「SOAP.py」と「soap.py」の衝突でひどい目に合うこともあり得ます。Orchard の SOAP.py にも、かち合う名前がありますが、モジュールが Orchard パッケージのもとにまとめられるように慎重に配慮されているため、この問題はうまく回避されています。

こうした配慮がなされていない場合には、読者のすべての Python SOAP モジュールのインストールで、異なるパッケージ名を使用することをお勧めします。私たちの場合、PYTHONPATH の中に適切なディレクトリーを見つけ、WebServices というパッケージを作成して、その中に SOAP.py を置きました。したがって、Linux の場合には次のようになります。

$ mkdir ~/lib/python/WebServices
$ touch ~/lib/python/WebServices/__init__.py
$ cp SOAPpy097/SOAP.py ~/lib/python/WebServices

2 番目のコマンドは重要ですので、注意してください。このコマンドは、WebServices ディレクトリーに Python パッケージとしてのマークを付けるために、__init__.py ファイルを設定しています。このコードを Windows に組み込む必要がある場合には、空ファイルになんらかのコメントを入れる必要があると思われます。Windows ツールの中には、空ファイルの作成を拒絶するものがあるからです。

しだいに肌になじんできます

一般に入手可能な SOAP サーバー用に、すでにいくつかのアクティブなレジストリーが存在しています。最も人気があるのは、おそらく XMethods でしょう。もちろんこれは、派手な宣伝とは裏腹に、SOAP の実情を知るうえで格好の手引きともなります。現在出回っている公用の Web サービスは、まだお遊び程度のものにすぎず、私たちの華々しいニュー・モデルによってはやし立てられる程の価値はありませんが、これはまた別の話です。そこで、SOAP クライアントとしての SOAP.py の使用をデモし、テストする目的で、公用サービスを選択することにします。

あるいは、選択を試みる、というべきかもしれません。著者たちが最初に試したサービスは、健康管理プロバイダーのロケーターでした。これは、現時点における SOAP のインターオペラビリティーの落とし穴を露呈するもので、次のようなメッセージを出して失敗してしまいました。

 WebServices.SOAP.faultType: <Fault soap:Client: 
Server did not recognize the value of
HTTP Header SOAPAction: "".>

うーん。SOAPAction は、アクセスされたサービスを通知するための HTTP ヘッダーです。これは SOAP 要求では必須のヘッダーですが、この必須ヘッダーを (空の引用符のペアで) 設定したにもかかわらず、上のエラーが繰り返されたのです。著者たちは、これが、ほとんどの MS SOAP インプリメンテーションで起こることを発見しました。これらのサービスをいくつか試した結果、Delphi インプリメンテーションが SOAP.py との相性が最もよかったのですが、それでも、リストなどの複雑なタイプを戻すサービスを試したときには (Delphi でインプリメントされたサービスであるにもかからわず)、SOAP.py で問題が起こり、たとえば、データを含まない WebServices.SOAP.typedArrayType インスタンスが戻されたりしました。

結局私たちは、漫画「タンタン」シリーズで登場人物のハドック船長による、年代物の悪態を戻す Web サービスを選ぶことにしました (実のところ、ほとんどの Web サービスはこの程度のものなのです)。リスト 1 (curse.py) が、このプログラムです。

リスト 1: Curse を生成する SOAP サービスにアクセスするための SOAP.py プログラム
#!/usr/bin/env python

#http://xmethods.net/detail.html?id=175

import sys

#Import the SOAP.py machinery
from WebServices import SOAP

remote = SOAP.SOAPProxy(
"http://www.tankebolaget.se/scripts/Haddock.exe/soap/IHaddock",
  namespace="urn:HaddockIntf-IHaddock",
  soapaction="urn:HaddockIntf-IHaddock#Curse"
)

try:
  lang = sys.argv[1]
except IndexError:
  lang = "us"

result = remote.Curse(LangCode=lang)

print "What captain Haddock had to say: "%s""%result

全部まとめて

ライブラリーをインポートした後で、remote というプロキシー・オブジェクトをセットアップします。このオブジェクトは、メソッド呼び出しをリモート SOAP メッセージに変換します。このプロキシー・オブジェクトの初期化指定子は、リモート要求を制御するキー・パラメーターとして、サーバーの URI (「endpoint」と呼ばれます)、要求エレメントの XML ネーム・スペース (RPC としての SOAP は、XML 基盤へのリップ・サービスをここで提供します)、および SOAPAction のヘッダー値を受け取ります。

次に、メソッド引数を決定します。この Web サービスの場合は、Haddock 船長が吐くセリフの言語として、スウェーデン語 ("se") または英語 (奇妙なことに、"en" ではなく "us") を指定します。

最後に、SOAP 呼び出しを行なうために Curse という、打って付けの名前のメソッドをプロキシー・オブジェクトで起動し、その結果を印刷します。次のセッションは、このプログラムを使用したようすを示したものです。

$ python curse.py 
What captain Haddock had to say: "Ectoplasmic Byproduct!"

私たち独自の SOAP サーバー

SOAP.py で SOAP をインプリメントするのは、非常に簡単です。たとえば、フィールドをエミュレートし、ありふれたサービスをインプリメントすることにします。これは、年と月を入力すると、カレンダーをストリングとして印刷するプログラムです。このために必要なサーバー・プログラムは、リスト 2 (calendar-ws.py) に示すとおりです。

リスト 2: カレンダー・サーバーをインプリメントする SOAP.py プログラム
  #!/usr/bin/env python

import sys, calendar

#Import the SOAP.py machinery
from WebServices import SOAP

CAL_NS = "http://uche.ogbuji.net/eg/ws/simple-cal"

class Calendar:
  def getMonth(self, year, month):
    return calendar.month(year, month)

  def getYear(self, year):
    return calendar.calendar(year)


server = SOAP.SOAPServer(("localhost", 8888))
cal = Calendar()
server.registerObject(cal, CAL_NS)

print "Starting server..."
server.serve_forever()

必要なインポートを行なった後で、SOAP 要求エレメントとして予期されるネーム・スペース (CAL_NS) を、私たちのサーバーに対して定義します。次に、SOAP メソッドとして公開されるすべてのメソッドをインプリメントするクラスを定義します。個々の関数を SOAP メソッドとして登録することもできますが、クラス方式を使用したほうが柔軟性が高くなります (特に、呼び出しと呼び出しの間の状態を管理したい場合など)。この Calendar クラスは getMonth というメソッドを定義しています。このメソッドは、Python に組み込まれたカレンダー・モジュールを使用して、1 か月のカレンダーをテキスト形式で戻します。また、1 年分のカレンダーを戻すメソッドも定義しています。

その後で、SOAP サーバー・フレームワークのインスタンスを作成し、ポート 8888 で listen するように指示します。Calendar クラスのインスタンスも作成する必要があります。これは次の行で、SOAP メッセージを処理するために、関連するネーム・スペースを指定して登録されます。最後に、serve_forever メソッドを呼び出します、このメソッドは、プロセスが終了するまで戻りません。

サーバーを実行するためには、別のコマンド・シェルをオープンし、python calendar-ws.py を実行してください。終わった後でプロセスを停止させるには、ctrl-C を使用してください。

SOAP.py を使用して作成されたクライアントを使用して、このサーバーをテストすることもできますが、それは説明するまでもないでしょう。その代わりに、低水準の Python でクライアントを作成して、SOAP 応答を XML ストリングの形で構成し、HTTP メッセージを送るようにしてみましょう。このプログラム (testcal.py) は、リスト 3 に示してあります。

リスト 3: カレンダー・サービスにアクセスするために Python コア・ライブラリーを使用して作成されたクライアント
  import sys, httplib

SERVER_ADDR = "127.0.0.1"
SERVER_PORT = 8888
CAL_NS = "http://uche.ogbuji.net/ws/eg/simple-cal"

BODY_TEMPLATE = """<SOAP-ENV:Envelope
  xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:s="http://uche.ogbuji.net/eg/ws/simple-cal"
  xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/1999/XMLSchema"
  SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <s:getMonth>
      <year xsi:type="xsd:integer">%s</year>
      <month xsi:type="xsd:integer">%s</month>
    </s:getMonth>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>"""


def GetMonth():
    year = 2001
    month = 12
    body = BODY_TEMPLATE%(year, month)
    blen = len(body)
    requestor = httplib.HTTP(SERVER_ADDR, SERVER_PORT)
    requestor.putrequest("POST", "cal-server")
    requestor.putheader("Host", SERVER_ADDR)
    requestor.putheader("Content-Type", "text/plain; charset="utf-8"")
    requestor.putheader("Content-Length", str(blen))
    requestor.putheader("SOAPAction", "http://uche.ogbuji.net/eg/ws/simple-car")
    requestor.endheaders()
    requestor.send(body)
    (status_code, message, reply_headers) = requestor.getreply()
    reply_body = requestor.getfile().read()

    print "status code:", status_code
    print "status message:", message
    print "HTTP reply body:\n", reply_body


if __name__ == "__main__":
    GetMonth()

次のセッションは、このテストを実行したようすを示したものです。

$ python testcal.py 
status code: 200
status message: OK
HTTP reply body:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" 
xmlns:SO
AP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<getMonthResponse SOAP-ENC:root="1">
<Result xsi:type="xsd:string">    December 2001
Mo Tu We Th Fr Sa Su
                1  2
 3  4  5  6  7  8  9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
</Result>
</getMonthResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

イトの点検

self.debug = 0 と書かれている行 (SOAP.py バージョン 0.9.7 では、行 210 です) を見付けて、"0" を "1" に変更すると、実際に交換される SOAP メッセージと、デバッグおよびトレース用のその他キー・データの詳細を得ることができることは、覚えておくと便利でしょう。たとえば、前の curses.py プログラムでデバッグ情報をオンにしてセッションを行なうと、次のようになります。

$ python curse.py 
*** Outgoing HTTP headers **********************************************
POST /scripts/Haddock.exe/soap/IHaddock HTTP/1.0
Host: www.tankebolaget.se
User-agent: SOAP.py 0.9.7 (actzero.com)
Content-type: text/xml; charset="UTF-8"
Content-length: 523
SOAPAction: "urn:HaddockIntf-IHaddock#Curse"
************************************************************************
*** Outgoing SOAP ******************************************************
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/1999/XMLSchema" xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" 
xmlns:SO
AP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:Curse xmlns:ns1="urn:HaddockIntf-IHaddock" SOAP-ENC:root="1">
<LangCode xsi:type="xsd:string">us</LangCode>
</ns1:Curse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
************************************************************************
*** Incoming HTTP headers **********************************************
HTTP/1.? 200 OK
Server: Microsoft-IIS/5.0
Date: Tue, 11 Sep 2001 16:40:19 GMT
Content-Type: text/xml
Content-Length: 528
Content:
************************************************************************
*** Incoming SOAP ******************************************************
<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/1999/XMLSchema" 
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xml
soap.org/soap/encoding/"><SOAP-ENV:Body><NS1:CurseResponse xmlns:NS1="urn:HaddockIntf-
IHaddock" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><NS1:return 
xsi:type="xsd:string">Anacoluthons!</NS1:return></NS1:CurseRespon
se></SOAP-ENV:Body></SOAP-ENV:Envelope>
************************************************************************
What captain Haddock had to say: "Anacoluthons!"

比較のために、これと同じ情報を従来の Python スクリプトまたはプログラムで得るためのコードを示すと、次のようになります。

import calendarreturn calendar.month(2001, 10)

濃縮 SOAP.py

すでに触れたように、SOAP.py のインターオペラビリティーには不都合な点がありますが、デバッグ・データが使用可能なことは、助けになります。 それに、このプロジェクトを継続させるために名乗り出た開発者の中に、このコラムの共著者である Mike Olson が加わっているのです。次回のこのコラムでは、他の Python SOAP インプリメンテーションのうちの 1 つを採り上げる予定です。

参考文献

  • XMethods: SOAP サービスのレジストリーです。
Python 用の SOAP インプリメンテーションIBM による参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services
ArticleID=244257
ArticleTitle=Python Web サービス開発者: 第 5 回: Python SOAP ライブラリー 第 1 回
publish-date=09012001