blpapi は Python だけでは動かない── C++ SDK との版合わせと実行時のパス
インフラ

blpapi は Python だけでは動かない── C++ SDK との版合わせと実行時のパス

FTL編集部

Bloomberg のデータを Python から取る案件で、最初に時間を取られるのはコードではなく環境構築です。しかも厄介なことに、手元では動いて、サーバーへ持っていった瞬間に落ちます。原因はほぼ 1 つに絞られるのですが、エラーメッセージからは辿りにくい場所にあります。

本稿では、blpapi の Python SDK が何に依存しているのか、インストール時と実行時で必要なものがどう違うのか、そして落ちたときにどこを見るのかを整理します。記述は Bloomberg が配布している Python SDK のドキュメントに基づいており、参照先は末尾に挙げています。

依存しているのは 2 つ

Python SDK が必須としているのは次の 2 つです。

  • CPython 3.10 以上 — それ未満では動きません
  • Bloomberg C++ SDK — しかも「Python SDK と同じメジャー・マイナーバージョン」であること

加えて、環境によっては C/C++ のコンパイラと、Windows では VC 再頒布可能パッケージが要ります。

重要なのは 2 つ目です。blpapi の Python パッケージは、C++ SDK の共有ライブラリを呼び出す薄い層にすぎません。Python 側だけを入れても、呼び出す本体がなければ動きません。そして「同じメジャー・マイナー」というのは推奨ではなく要件です。Python 側だけを新しくすると、本体との組み合わせが崩れます。

この構造を知らないと、エラーを「Python パッケージの問題」だと思って pip 側を触り続けることになります。実際には C++ SDK の配置か、そこへのパスの問題であることがほとんどです。

blpapi の Python SDK が C++ SDK の共有ライブラリに依存する構造を示した図。インストール時には BLPAPI_ROOT で C++ SDK の場所を指す必要があり、実行時には OS ごとに PATH・LD_LIBRARY_PATH・DYLD_LIBRARY_PATH で共有ライブラリを見つけられる必要があることを表している
必要なものがインストール時と実行時で違う。ここが噛み合わないと本番で落ちる

BLPAPI_ROOT はインストール時だけ

インストールの手順は 2 段階です。まず BLPAPI_ROOT に C++ SDK の場所を設定します。ドキュメントはこれを「include ディレクトリを含むディレクトリ」と定義しており、例として Linux では $HOME/blpapi_cpp_3.x.y.z、Windows では C:\blp\API\APIv3\C++API\v3.x.y.z\ の形が挙げられています。そのうえで、配布物に含まれる setup.py でコンパイルとインストールを行います。全ユーザー向けにするか、実行するユーザーだけに入れるかを選べます。

ここで押さえておきたいのが、BLPAPI_ROOT が必要なのはインストール時だけという点です。ビルドの際にヘッダを探すために使われるもので、実行時には参照されません。

この非対称性が、混乱の元になります。インストールが通ったので環境変数は要らないものと思い、本番のサーバーやコンテナでは BLPAPI_ROOT を設定しない。そこまでは正しいのですが、実行時には別の経路で共有ライブラリを見つける必要があることが抜け落ちます。

実行時に見るのは OS ごとに違う変数

実行時に必要なのは、C++ SDK の共有ライブラリを OS が見つけられることです。ドキュメントは、共有ライブラリをシステムの標準的な場所へ置くか、ライブラリパスを更新するよう指示しています。見る変数は OS ごとに違います。

  • Linux / Unix 系LD_LIBRARY_PATH
  • macOSDYLD_LIBRARY_PATH
  • WindowsPATHblpapi3_32.dll または blpapi3_64.dll を含むディレクトリ)

Windows で 32 ビットと 64 ビットの DLL が分かれている点にも注意が要ります。Python の処理系のビット数と合っていなければ、パスが通っていても読み込めません。

開発機で動いて本番で落ちる理由

ここまでを踏まえると、冒頭の症状の理由が見えます。開発機には Bloomberg Terminal が入っていることが多く、その場合 C++ SDK の共有ライブラリはすでにパスの通った場所にあります。だから何も設定しなくても動きます。

一方、本番のサーバーやコンテナ、CI の実行環境には Terminal は入っていません。Python パッケージだけを requirements.txt から入れても、呼び出す本体がないので落ちます。「開発機で動いたから大丈夫」という確認は、この依存関係に関しては何の保証にもなりません。

したがって、確認は本番と同じ条件で行います。手元で試すなら、Terminal の入っていない環境を用意するか、コンテナの中で動かします。この検証を後回しにすると、本番移行の当日に環境構築からやり直すことになります。

落ちたときにどこを見るか

症状は主に 3 通りに分かれます。読み分けができれば、原因の切り分けは短時間で済みます。

1 つ目は、共有ライブラリが見つからない場合です。import blpapi の時点で、ライブラリを読み込めない旨のエラーになります。これは上に挙げた OS ごとの変数を確認します。

2 つ目が、ドキュメントにも明記されている紛らわしいものです。Import Error: No module named _internals というメッセージが出た場合、対処は別のディレクトリへ移動してから python を起動することです。SDK のソースを展開したディレクトリの中で Python を起動すると、インストール済みのパッケージではなくソースのほうを読んでしまうために起きます。パスの問題でもバージョンの問題でもありません。

3 つ目がバージョンの不一致です。読み込みは通るのに、実行中に予期しない挙動やクラッシュが出ます。Python SDK と C++ SDK のメジャー・マイナーが揃っているかを確認します。

この 3 つ目が最も見つけにくい理由は、依存関係の一覧に C++ 側が出てこないことです。pip freezerequirements.txt に並ぶのは Python パッケージの版だけで、実行環境に置かれた C++ SDK の版は現れません。つまり、Python 側の依存を固定しても、環境ごとに違う C++ SDK が組み合わさる状態は防げません。C++ SDK の版も、イメージのタグや構成管理の対象として明示的に固定します。

起動時に自己診断を入れておくと、この切り分けを人がやらずに済みます。確認するのは 4 つです。Python の版が要件を満たしているか。import が通るか。通らない場合、メッセージに _internals が含まれていればディレクトリの問題、そうでなければ共有ライブラリの問題として、見るべき環境変数の名前と現在の値をそのままエラーに出す。最後に、SDK が公開しているバージョン情報を Python 側と C++ 側で取り、メジャー・マイナーが揃っているかを見ます。

ここで揃っていなければ、起動を止めます。本番でデータが取れないことに後から気づくより、起動時点で理由付きで落ちたほうが復旧は早くなります。

コンテナと CI に載せるとき

コンテナ化する場合、C++ SDK をイメージに含める必要があります。ここで判断が要るのは、SDK の配布物をどこから持ってくるかです。公開のパッケージリポジトリから pip で解決できる部分ではないため、取得したアーカイブを社内のアーティファクトリポジトリなどへ置き、ビルド時にそこから取る形になります。

イメージの中では、共有ライブラリを標準的なライブラリディレクトリへ置くか、環境変数で指します。前者のほうが、実行時の設定漏れが起きにくくなります。Linux なら共有ライブラリの探索先として登録しておけば、コンテナを動かす側が LD_LIBRARY_PATH を意識せずに済みます。

ビルド定義では、C++ SDK と Python SDK の版をそれぞれ引数として持たせ、1 か所で管理します。BLPAPI_ROOT を設定するのはビルドの工程だけで、出来上がったイメージには残しません。実行時に参照されない変数を残すと、次に読む人が「実行時にも要るもの」と誤解します。

CI についても同じです。テストの段階で import blpapi が通ることを確かめておけば、本番移行で環境構築に戻る事態を避けられます。Bloomberg への接続そのものは CI では試せませんが、読み込みが通るかどうかだけでも確認する価値があります。落ちる原因の大半は接続以前の段階にあるためです。

まとめ

blpapi の Python SDK は、C++ SDK の共有ライブラリを呼ぶ薄い層です。必要なのは CPython 3.10 以上と、メジャー・マイナーの揃った C++ SDK の 2 つ。BLPAPI_ROOT はインストール時にだけ要り、実行時は OS ごとに PATHLD_LIBRARY_PATHDYLD_LIBRARY_PATH のいずれかで共有ライブラリを見つけられる必要があります。開発機に Terminal が入っていると何も設定せずに動いてしまうため、検証は Terminal のない環境で行います。落ちたときは、共有ライブラリ・_internals の ImportError・バージョン不一致の 3 つに読み分けます。起動時の自己診断を入れておけば、この切り分けは自動化できます。

金融テクノロジー総合研究所では、Bloomberg API を用いたデータ基盤の設計・実装と、本番環境への移行支援を受託しています。既存実装がサーバーで動かない、といった切り分けからもご相談いただけます。Bloomberg API 開発の詳細もあわせてご覧ください。ご相談はお問い合わせよりご連絡ください。

参考資料

  • blpapi-python(CPython 3.10 以上と C++ SDK の同一メジャー・マイナー要件、BLPAPI_ROOT の定義とインストール手順、LD_LIBRARY_PATH / DYLD_LIBRARY_PATH / PATH の指定、No module named _internals の対処)
  • Bloomberg API Library(各言語の SDK と C++ SDK の配布元)
  • Bloomberg Open API Core Developer Guide(PDF)(BLPAPI の製品構成と、対応言語ごとの実装形態)
記事一覧に戻る