YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

api.aiのdefault fallback intentをFirebase Functionsで受ける

はじめに

こんにちは、Slack Bot界のアラーキーです。今日はAlphaGoが柯潔との3局対決2勝目で中押し勝ちで2勝目を挙げましたね。Machine Learningの発展はすごいなあという素人の感想ですが、そんなMachine Learningの力をBot開発にも簡単にもたらしてくれるサービスの一つがapi.aiです。api.ai自体の説明は公式サイトや他のサイトに譲るとして、今日はapi.aiのdefault fallback intentをFirebase Functionsで実装したFulfillmentで受けるという簡単なデモを試したので、その作業ログを残します。

チャットボット AIとロボットの進化が変革する未来

チャットボット AIとロボットの進化が変革する未来

前提

まずは用語の説明から。

  • api.ai: 様々なチャットシステム用Bot(Actions on Google、Slack、LINE、Facebook Messanger)などに対応しているチャットボットミドルウェア
    • Agent: あるひとまとまりの応答をするNLU(Natural Language Understanding、自然言語理解)モジュール。複数のチャットアプリケーションと同時に連携が可能。
    • Intent: ユーザーからの特定のクエリに対する応答のマッピング。このintentをいかに多くの応答に対して作成できるかがapi.aiの肝。
    • Fulfillment: api.aiの先に用意する自前の応答システム。通常のbotシステムであればチャットシステムから直接メッセージデータを受け取るが、api.aiを利用する場合は前段となるapi.aiで捌けなかったものや複雑なトランザクションが発生するものをここで受ける。

api.ai自体を利用する場合は他にもEntityやContextといったものとapi.aiで内蔵している自然言語理解のトレーニング機構を使っていかに効率よくルールを作成していくかが肝心なのですが、今日は対応するIntentがなにもない場合のフォールバック先であるDefault Fallback Intentを自分で用意したFirebase Functionsのエンドポイントで受ける、ということをまずやってみます。

Firebase Functionsでのエンドポイントの作成

Firebaseプロジェクトの作成

Firebase FunctionsのGetting Startedのドキュメントを読めば簡単にできると思いますが、簡単にコマンドログだけ書くと

$ npm install -g firebase-tools
$ firebase login
$ firebase init functions
$ cd functions
$ npm install --save firebase-functions
$ npm install --save firebase-admin@4.2.1
$ tree -a -L 2 .
.
├── .firebaserc
├── firebase.json
└── functions
    ├── index.js
    ├── node_modules
    └── package.json

作成された firebase.json は空のオブジェクトが書いてあるだけですが、作成するFirebase Functionsのファイルを functions 以下に置くのであればそのままで大丈夫。functionsディレクトリの中のindex.jsに実際のエンドポイントの処理を書いていきます。

index.jsの作成

index.js をこんな感じで雑に作ります。Slackのみをつなげているため、dataの中にslackしかありませんが、つなげているアプリケーションが複数あればそれに応じてassistantfacebookなどのメッセージも追加することで対応できます。api.aiから飛んでくるリクエストと、そこに返すべきレスポンスのJSONスキーマに関しては api.ai のWebhookのドキュメントに書いてある。

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

const slack_message = {
    "text": "hello slack from firebase"
}

exports.sampleBotAgent = functions.https.onRequest((req, res) => {
    console.log(req);
    res.status(200).send({
        "speech": "hello from firebase",
        "displayText": "hello from firebase",
        "data": {"slack": slack_message},
        "source": "foo"
    })
});

デプロイ

これで完了です。デプロイします。プロジェクトルート直下で firebase deploy コマンドを実行します。

$ firebase deploy --only functions
=== Deploying to 'sample-bot-agent'...

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
i  runtimeconfig: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
✔  runtimeconfig: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (1.55 KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: updating function sampleBotAgent...
✔  functions[sampleBotAgent]: Successful update operation.
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/samplebot-agent/overview
Function URL (sampleBotAgent): https://us-central1-samplebot-agent.cloudfunctions.net/sampleBotAgent

ログの最後のFunction URLというのが作成されたエンドポイントのURLなのでこれをコピーします。

api.aiの設定

Agentの作成

api.aiに初めてログインするとすぐにAgentの作成画面に飛ばされると思います。すでにAgentがあるけれど新規に作る場合には、左ペインのエージェント名の右側にあるメニューボタンを押すと “Create new agent” というメニューがでてくるのでそこで作成。適当にAgentの名前やdescriptionを書きます。Sample Dataは無いと思うので無視。言語は英語が一番ちゃんと動きますが、日本語もちょっとは使えます。目下改良中の模様。今回はどうせDefault Fallback Intentしか作らないのであまり関係ないです。Google Projectはデフォルトで作ってくれるものでいいと思うので無視。

f:id:ymotongpoo:20170526002606p:plain

Slackとの連携

Agentは複数のチャットアプリケーションと連携できますが、今回はSlackのみと連携します。左ペインのメニューのIntegrationsを押して、Slackのトグルスイッチをオンにします。 f:id:ymotongpoo:20170526004756p:plain

するとSlackの各種トークンを設定するダイアログが出てくるので各々設定します。Slack側でもBotの設定をする必要がありますが、そのあたりは次のドキュメントにスクリーンショット付きで書いてあるのでそちらに譲ります。 先のダイアログ内のOAuth URLとEvent Request URLはSlack側のBotの設定で必要となります。その設定手順も次のドキュメントに書いてあります。

Fulfillmentの設定

AgentができてIntegrationができたのでFulfillmentを設定します。左ペインのメニュー内のFulfillmentを選択して設定画面にいきます。 f:id:ymotongpoo:20170526003017p:plain

WebhookのEnabledをオンにするといくつかフォームが出てきますが、ここのURLに先ほどコピーしたFirebase FunctionsのURLを貼ります。あとは設定しなくてOKです。 f:id:ymotongpoo:20170526003156p:plain

default fallback intentの設定

Fulfillmentの設定が終わったのでいよいよdefault fallback intentの設定です。初期値ではDefault Fallback IntentとDefault Welcome Intentがあると思います。 f:id:ymotongpoo:20170526003813p:plain

このDefault Fallback Intentを開くといくつかText Responseが設定されていることと思います。 f:id:ymotongpoo:20170526003953p:plain

このText Responseをまず全部消します。全消しです。Actionはそのまま “input.unknown” にしておいてください。その後、画面下にある Fulfillment という文字を押します。ここがわかりにくいですがこの Fulfillment の文字を押すと “Use webhook” のチェックボックスが出てくるのでチェックします。 f:id:ymotongpoo:20170526004257p:plain

これで設定完了です。

Slackで試す

適当なチャンネルに先ほど Agent と連携させた Bot を招待して、適当に書き込んでみます。 f:id:ymotongpoo:20170526005711p:plain

無事に動作しました!あとはapi.ai側のIntentを拡充させて、Fallback側での動作を諸々と考えるだけですね!楽しくなってきました!

おわりに

チャットシステムが普及し、様々な場面で文字・音声チャットが使われることが増えてきました。自分用のBotもより柔軟なものにできるように自分もいろいろ試そうと思います。

参照

macOSでパスワード付きzipを作成する

はじめに

いつから行われているのかわからないけれど、IT業界では「パスワード ハ オッテ ゴレンラクイタシマス」という呪文を唱え、パスワード付きzipとそのパスワードを2通のメールで送る慣習がある。普段は自分からそういうzipファイルを送ることはないのだけれども、今日たまたまどうしてもそうする必要があった。

zipcloak

で、調べてみたら、macOSでもzipcloakコマンドというのがあって、それを使えばパスワード付きzipを作れるということを今日知った。情弱乙。

$ zip -r path-to-dir/ archive.zip
$ zipcloak archive.zip
Enter password:
Verify password:

zipコマンドでまず普通のzipファイルを作成して、zipcloakコマンドでそれをキーフレーズで暗号化するという流れ。地味に便利だった。

zip -e

記事を公開してから力武さんにzipコマンド自体のオプションを教えてもらった。

$ man zip
...
       -e
       --encrypt
              Encrypt the contents of the zip archive using a password which is entered on the terminal in response to a prompt (this will not be echoed; if standard error is not a  tty,  zip  will  exit
              with an error).  The password prompt is repeated to save the user from typing errors.

ほんとだ、情弱すぎた。というかmanを読んでないのが悪い。

$ zip -e -r path-to-dir/ archive.zip
Enter password:
Verify password:

できた。

AMP Conf in NYC recap

はじめに

3月7日、8日にニューヨークでAMP Confという初のAMPチーム主催のAMPに関するカンファレンスが開催されました。

当日のツイートなど

リアルタイムで #ampconf ハッシュタグが付いていて良さそうなものをRTしつつ、自分でもメモとしてツイートしてました。改めてまとめて次のリンクに置いておきます。 twitter.com

当日のライブ配信の録画

当日はプレゼンはすべてライブ配信されて、その録画が残っています。各発表ごとの動画もいずれ公式チャンネルで公開される予定にはなっていますが、とりあえず公開されている録画の動画を貼っておきます。途中、画像だけで止まっているところがありますが、それはコーヒーブレイクなどで発表がない時間帯のものなので、適宜飛ばして再生して下さい。

多くのAMP運用企業からの発表があり、実際PinterestTumblrがとんでもない量のAMPページをリリースしている/することを報告していたり、wegoがどうやってAMPページと通常ページを両立させているかを共有していたり、その他もろもろこれまでAMPサイトを運用してきた知見が語られています。多くの日本のAMP対応サービス企業も言及されていて、特にYahoo! JapanのAMP対応は会場でも大きな話題となりました。

  • 1日目

www.youtube.com

  • 2日目

www.youtube.com

新しく発表されたこと

AMP Start

これまでAMPページを作る際にテンプレートがなくて苦労された方もいるかもしれません。AMP Startではそんな需要に対応すべく、様々な種類のページ向けにレスポンシブなテンプレートを用意しています。またページテンプレートだけではなく、ボタンやナビゲーションといったUIコンポーネントも提供しています。

AMP Start自体もオープンソースになっているので、もしいい感じのサンプルがあったらどんどんコントリビュートして下さい。

amp-bind

これまでAMPでは「一切のカスタムのJavaScriptが利用できない」ということが大きな制約となっていて、その制限によって利用を諦めた方も多いのではないかと思います。それはAMPを作った当初の思想の反映で「迂闊なJavaScriptのおかげで表示が遅くなることが多いので、まずは一番厳しい制限としてJavaScriptを書かせない」となったのですが、amp-listamp-sidebar などを使ってインタラクティブな動作もできるようにしてきました。

しかし、それでもまだEコマースサイトなどでは機能として足りないということで、状態管理とデータバインドなどに関するメソッドを提供し、かなり制限はあるものの、JavaScriptも少しは書けるようにしようとしたのがこの amp-bind です。(実際には amp-state などと組み合わせて使います)

www.ampproject.org

実際に amp-bind がどのような動作をするかは AMP by Example に簡単な例があるので、是非確認してみてください。

ampbyexample.com

発表の動画では amp-bindチューリング完全であることから、2048っぽいゲームをデモとして作って見せていましたが、発表者のWilliamが「頼むから実際のサイトでこんな実装しないでね」と言っていたのが印象的でした。

Google AMP Cacheでの高速化施策

現状ではAMPは機能を制限することでクライアントサイドレンダリングの高速化を図っているわけですが、これをされに推し進めて高速化を図る計画がAMP ProjectのTechnical LeadのMalteより発表されました。その施策は次の2つです。

  1. AMP JSのService Workerでのキャッシュ
  2. AMP Cacheサーバーでのサーバーサイドレンダリング

1のService WorkerでのAMP JSのキャッシュですが、現在Google AMP Cacheはcdn.ampproject.orgというドメインでホストされています。またAMP JSもこのドメインからホストされています。なので、このAMP JSでService Workerをインストールさせて、AMP JSをローカルにキャッシュさせて、AMPページにアクセスする際にそれを読み込ませるようにさせることでリモートにAMP JSを取りに行っていた通信の時間分速くなります。

2のサーバサイドレンダリングに関しては、現状AMPページにあるボイラープレートに対するクライアントサイドの読み込みフローを幾ばくかサーバサイドに任せて、読み込んだ瞬間にレンダリング計算が終わっている状態にしたい、というのが狙いです。

Ampersand by Cloudflare

www.cloudflare.com

これまでは外部から利用可能なAMPキャッシュはGoogle AMP Cacheしかありませんでしたが、今回CloudflareからAmpersandという独自ドメイン運用できるAMPキャッシュが公開されました。またそれに合わせて、見た目をカスタマイズできるAMPビューワーのSDKの公開もアナウンスしました。

codelab

2日目の午後はAMPのcodelabが開催されました。どれも新しくつくられたcodelabで、最新の情報を盛り込んだ内容になっています。

総括

ニューヨークでの開催でしたが、多くの国からの参加者が来ていて、参加者がすべてAMP対応企業だったこともあり、活発に意見交換がされていた印象でした。AMPはまだまだ発展途上のフレームワークですが、クライアントサイドにとどまらない点が他のフレームワークとの違いだと思うので、その特徴を活かしてよりモバイルウェブが良い方向に動くように発展していくきっかけになるように、これからもAMPがいい形で成熟していくことを願っています。

wheelのありがたさとAnacondaへの要望

はじめに

こんにちは、Python界のラファエル・ナダルです。全豪オープンテニス、盛り上がりましたね。さて、先日次のようなエントリーを立て続けに書いたんですが、「なぜAnacondaに関しての記述がないのか」という突っ込みをもらったので、参照用にメモを残しておきます。

なおこの記事の作成にあたっては @aodag に数多くのアドバイスをいただきました。この場を借りて感謝。

TL;DR

condaの開発者はPyPAともっとコミュニケーションとってほしい。

前提

この記事はPythonを触り始めたばかりだけど、パッケージ管理ツール等々のスタンダードがどのようになっているかなど、経緯がわからず混乱した人向けに現状を把握するための補助として書いています。読み物として読んでもらえれば幸いです。

予備知識1:Pythonでの標準策定プロセス

PythonにはPEP(Python Enhancement Proposal)というものがあり、Pythonの言語仕様そのもの、実装の変更、3rd partyなどで提案されてから広く受け入れられた機能などを標準に取り込むための提案はこのPEPの形式で提出され、PEP Editorsに承認されたものが標準に取り込まれます。

つまりPEPで明文化された仕様が決定されれば、提案元の3rd partyパッケージの実装依存にならず、その仕様を汲みさらに便利な機能を追加した新たな3rd partyを安心して見守れる、というわけです。このサイクルによってPythonの標準機能は発展しています。

予備知識2:パッケージングツールとパッケージインストーラ

  • パッケージングツール : PyPIなどで配布するモジュールをパッケージするためのツール。パッケージ作者以外は特に意識することはありません。
    • eg) setuptools, wheel
  • パッケージインストーラ : PyPIなどで配布されているモジュールをインストールするためのツール。通常はこちらを使うことばかり。裏側で何をしているか意識することは少ないです。
    • eg) easy_install, pip

現在は setuptools, wheel, pipvirutualenv とともにPyPA (Python Packaging Authority)で管理されています。

予備知識3:Python Language Moratorium

PEP-3003で決められた言語仕様やビルトインを一定期間フリーズする宣言。Python2系からPython3系に移行するにあたり、言語仕様を一旦フリーズして、Python3.1からはPython2.6へ、Python3.2からはPython2.7へそれぞれ言語仕様をバックポートし、メジャーバージョン移行のための猶予期間をもたせるためのもの。また同時に、Python3.3以降はまた新たに言語仕様の変更や新しいビルトイン等を導入可能とも定めています。

wheelのありがたさ

wheel以前

3rd partyライブラリである setuptools が利用していた egg と呼ばれる形式を使ってバイナリパッケージ(C/APIを利用したpure Pythonでないパッケージを事前ビルドしたもの)を配布していました。この egg 形式はPEPで策定されたものではなく、setuptools が独自に決めたものでした。標準パッケージである distutils や先の setuptoolssetup.py sdist で生成する sdist はソースパッケージの配布形式であり、バイナリパッケージに関してはサポートしていません。(ところで実は現時点でも sdist の仕様が実は決まっていないというのは後で触れます)*1

一方で pip が標準になる以前、は、PyPIにあるパッケージのインストールは easy_install コマンドで行われていました。easy_install コマンドは setuptools もしくはそれをフォークした distribute (のちに setuptoolsにマージ)をインストールすると、そのエントリーポイントのコマンドとして利用できました。しかし後発の pip がパッケージを利用するだけの人からすると便利だったので、徐々に pip のシェアが伸び、パッケージインストールのためのデファクトスタンダードとなり、のちにPythonの標準配布に pip を同梱させる “ensure-pip” のためのPEPが策定されます。

ところがここで問題がありました。egg はすでにデファクトになっていたものの、作成した egg はメジャーバージョンの違いはおろか、マイナーバージョンが違っただけで動作しなかったため、パッケージ作成者は細かなバージョン単位での egg を作成する必要がありました。利用者も自分が利用しているPythonのマイナーバージョンにあった egg がないとインストールすることができませんでした。理由はわからないですが、おそらくこのような不都合から*2 pipsdist 形式のパッケージを一度手元に落としてきた後、それをビルドして bdist_egg とした後にインストールするという処理を行っていました。せっかくの egg が台無しです。

wheel

そんな状況がある一方で、wheelの開発がされていました。wheelはパッケージングツールであり、それで生成されるパッケージはwhl形式となっています。wheelがなぜ開発されたのか、その理由は筆者が本家サイトに書いてくれているので読んでみると面白いです。

読めばわかりますがwheeleggが抱えていた問題を解決するための仕様であるPEP-427やPEP-376を元に作られました。

No one has to rewrite setup.py for their own or the 20k+ other packages on PyPi unless a different build system does a better job.

Wheel が目指しているのは銀の弾丸ではなく、少しずつ既存の方法を解決していく地道な解決策であることが読み取れます。

何よりも大きいのはメジャーバージョンの違いも含めて、1つの whlでインストールされることです。そして、pipもバージョン7.0よりwhl形式に対応しています。このおかげで pip を利用したパッケージのインストールが高速になりましたし、Pythonのバージョン違いも神経質にならずに済みます。またバイナリインストールが可能なので、pipsdistからビルドする際にあったような、依存ライブラリがない、という状況も改善されました。これはPEP-427やPEP-376だけでなく、複数のLinuxディストリビューションでも動作するようにPEP-513で規定したことによります。(manylinux1

このような経緯でpipwheelを支える多くの仕様が標準で決められ、必要なものもいま議論されています。一つ大きなピースがあるとすれば sdist の仕様が実はPEPで決まっていないというところです。これも現在PEP-516、PEP-517で議論中、PEP-518はすでに策定済みとなっています。これが決まって setuptools による実装依存の仕様がなくなり、もろもろのPEPを実装しているPyPA管理下の distlib が対応していけば、いよいよ setuptools から解放されます。すべてPEPで定義された仕様を元にパッケージングができるようになり、これでPyPIにあるものがすべてWheelになれば皆が知らないうちに幸せになるわけです。

このサイトにあるように、すでに主要なライブラリのWheel化はかなり進んでいます。データサイエンス系のライブラリ(numpyscipyscikit-learnなど)もWheel化しています。Pythonしか使わないのであれば、データサイエンティストの皆様もwheelのおかげで特にインストールされている共有ライブラリなどを気にせずにパッケージをpipでインストールできるようになっています。pip の後ろで多くの人たちが議論を重ね、今あるものを壊れないように作り替えてきました。(pip のダウンロードが wheel のおかげで高速化されることを実装される前から知ってた人がどれくらいいましたか?意識しないでも壊れることなく動いていたのは彼らの努力のおかげです)

Anaconda

condaパッケージ

しかしながら、データサイエンティストの方々はまずAnaconda(conda)をインストールするという流れが最近あります。まあPython以外にも、そもそもランタイムが異なるRを使ってみたり、Scalaを使ってみたりすることもある人なら納得はできます。

しかしMinicondaならどうか?これならPythoncondaしか入れないのであれば別に virtualenvpip とできることは変わらないのではないか、と思いますがここでややこしい事情が出てきます。先の sdist のフォーマットです。たとえばここにあるAnacondaのパッケージを見てましょう。

たとえばJupyterの場合のパッケージはこれです。(Pythonのバージョンに応じたtarballがある)

これを展開してみるとこうなります。

% tree jupyter-conda
jupyter-conda
├── info
│   ├── files
│   ├── index.json
│   ├── meta
│   └── requires
└── lib
    └── python3.5
        └── site-packages
            ├── __pycache__
            │   └── jupyter.cpython-35.pyc
            ├── jupyter-1.0.0-py3.5.egg-info
            └── jupyter.py

見たことない形をしています。これは setuptools で吐かれる sdist や、wheelとは異なる形式です。*3ではいったい conda の形式はなんなんでしょうか。Anacondaの公式ドキュメントにパッケージのビルドについて書いてあります。

conda はパッケージインストーラであると同時にパッケージングツールでもあり、 conda ではまったく独自のパッケージング方法とホスティング方法を使ってパッケージを配布、インストールしているのです。これは現状 wheel互換性はありません

conda パッケージはPyPIにアップロードされているパッケージを元に作成することも、スクラッチから作ることもできますが、いずれにせよこれはContinuum Analytics社の独自仕様です。

なぜこんなことになっているのか

これは conda の開発履歴とPEPの策定時期を見てみると理由が見えてきそうです。まず conda の開発履歴をたどってみると、GitのログやChangelogから、condaのバージョン1.0が 2012年9-10月ころにはあったと書いてあります。

% git log --reverse --pretty="%h %cd - %s" | head -5
c9aea053 Mon Oct 15 17:14:59 2012 -0500 - first commit
acd8144f Mon Oct 15 17:16:45 2012 -0500 - add conda files
e40c59cb Mon Oct 15 17:17:29 2012 -0500 - add git ignore
967bb3a8 Mon Oct 15 17:29:01 2012 -0500 - since the already released version of conda is 1.0, this should be 1.1
b1120484 Mon Oct 15 17:47:43 2012 -0500 - remove pyc files

開発はもう少し前からされていたことを考えても、開発に着手したのは2012年前半と見てよいでしょう。一方で、wheelを標準とするためのPEPであるPEP-427が作られたのが2012年9月です。実際にドラフトとして採用されるまでの期間を考えるとそれより前には書き始めていたわけです。実はこの頃、Pythonのパッケージ管理方法の議論が活発でした。その様子は Python-Dev のメーリングリストを見てみるとよくわかります。

この頃の話を詳細にすると、またこれはこれで非常に長くなるのでもろもろ割愛して要点だけ記すと

  • Python 2.7でPython2系の終了が決まっていて、Language Moratorium後の初のバージョンPython 3.3が出る時期だったので、パッケージングについての議論が活発になっていた。(Python 3.3.0のリリースは2012年9月)
  • setuptoolsdistribute の対立が大きくなり、 distribute がメインストリームになりつつあった。(2013年に distributesetuptools にマージし、 setuptools はその後 pipvirtualenv も管理しているPyPAへ移管された。)

このような状況だったので conda の開発陣が業を煮やして自分たちでパッケージマネージャを作りたくなった気持ちもわからないではないです。実際、そのような気持ちだったであろうことは彼らの資料から見て取れます。*4

でもやっぱりcondaには歩み寄って欲しい

とはいえ、もう今は wheel がPEPで策定され標準パッケージング形式で決定していて、setuptoolspip でなんとなくでしか決まっていなかった sdist の仕様もきまりつつあり、すべて標準で決まってるんです。それだけじゃなくて、入れたパッケージをどう管理するか(誰がどういう理由でインストールしたかとか)とか、細かいパッケージの仕様まで長い年月をかけて標準で決めてきました。またパッケージの配布に関してもAPIがPEPで定義されています。これらは長い時間かけてPythonコミュニティが今動いているものをなるべく壊さないようにしつつ、コミュニティ全体がもっと便利にPythonを使えるように、と決めてられてきたものです。

このような状況を断片的にでもリアルタイムで見てきたので、いま conda がパッケージングに関してその流れを分断している状況は、個人的には悲しくもあり腹立たしくありという気持ちです。

conda が便利なのはわかりますが、勝手に独自パッケージを作って独自方式で配布するのではなく、パッケージを wheel 互換にしてくれて、PyPIにも whl 形式でパッケージをあげてもらえるような流れを作って欲しいと思います。実際に先にContinuum Analytics社のTravis氏のスライドで不満が漏らされていた numpy ですが、いまは whl 形式のパッケージが配布されているので、何の苦労もなく pip ですんなりインストールすることが出来ます。 conda の開発者が積極的にPEPの策定や wheel の作成と配布に関わってくれれば、コミュニティ全体でその恩恵にあずかれるので、そうなることを願うばかりです。*5

参考リンク

この記事にある細かな話は @aodag が過去のPyCon JPの発表で事細かに歴史的経緯含めて説明してくれているので、知りたい人はそちらもどうぞ。

PyPA(Python Packaging Authority)でパッケージング関連ツールの履歴を整理してくれています。

またPyPAで「現在推奨のパッケージツール」に関するドキュメントを常に更新し続けてくれています。迷ったらここを読みましょう。

最新のパッケージング動向を知りたいという人は各MLなどを追ってみましょう。

関連PEPとか

Appendix

  • Q1: buildoutについて触れられていませんが?
  • Q2: 開発環境の切り替えについて知りたいです。
  • Q3: 各OS(macOSWindowsLinuxディストリビューション等)でのパッケージ管理ツール経由でしかパッケージを入れたくありません
    • A3: その場合新しいバージョンのパッケージは登録されるまで待つしかないですが、それも一つの方法だと思います。いずれにせよそれらもwheelベースになるものと思われます。

コメントに関して

wheelのありがたさとAnacondaへの要望 - YAMAGUCHI::weblog

要望があるならcondaの開発者に直接伝えるべき (=>https://github.com/conda/conda/issues)。こんなとこで日本語で書いても1ミリも伝わらない。コミュニケーションについて非難している本人が一番コミュニケーションをとっていない。

2017/02/02 18:54
b.hatena.ne.jp

conda のパッケージのビルドに関するissuesはそこじゃなくて conda-build のレポジトリだし、記事公開時にすでにこの記事で書いたようなissueは上がってます。コミュニケーション云々いうなら、コメント欄開けてるので、記事コメントに直接書いてください。

*1:buildoutなどのZope関連のツールの話もあるのですが、これ以上書いていくととてつもない分量になるのでこの記事では書きません。

*2:pipでeggのインストールがサポートされなかった理由をかなり調べてみたけれど、決定的な理由が見つからなかった。

*3:比較したい人はこちらでどうぞ https://gist.github.com/ymotongpoo/7068beddbc7cf8a2015338c64425ca06

*4:ちなみにスライドの作者のTravis E. Oliphant氏はSciPyの作者

*5:自分はデータサイエンス系のこと以外にもPythonを使うので、そのためだけにcondaを入れたくはない。

Pythonの仮想環境構築 2017.01版

はじめに

こんにちは、Python界のテリー・ギリアムです。こんな記事を見かけて、Pythonの開発環境を作るのが面倒という認識が広まるのは良くないなあと思って書きました。ただの突っ込み記事です。

qiita.com

そのツールほんとに要りますか?

出だしにこんなセクションタイトルがありました。

その仮想環境本当に必要ですか?

たしかに仮想環境要らないひとは要らないよねっていうのは同意です。その場合、入ってるPythonのsite-packagesにどんどんパッケージがインストールされるだけなので、手動で消せる人はそれでいいし、そもそもパッケージのバージョンとか知るかって人はそのままパッケージインストールすればいいと思います。

とはいえ、複数のプロジェクトでパッケージのバージョンがぶつかったら困る人とかいるし、そういう人は仮想環境を使うことになるでしょう。で、件の記事ではいろいろなツールを紹介していますが、そもそもそんなにツール要りますか?というのが本記事の主旨です。

The Zen of Python

PythonにはThe Zen of Pythonという、Python自体の考え方や、それから派生したPythonで開発する上での指標となるものがあります。見たことがない人は次のコマンドで確認してください。

$ python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

今回の話につながるところでいくと、次の3つがあります。

  • Simple is better than complex. (シンプルなのは複雑であることよりも良い)
  • There should be one– and preferably only one –obvious way to do it.(1つだけ、できれば唯一、の明らかな方法があれば良い)
  • If the implementation is hard to explain, it’s a bad idea.(その実装の説明が難しいなら、そのアイデアは良くない)

ツールに関してもそうです。シンプルなものがあればそれを使おう、なるべくシンプルな構成にしよう、という考え方です。

virtualenv (venv)があればたいていのことは済む

pyenvを便利に使ってる人はいるんでしょう。しかし、pyenvのpython-buildは便利かもしれませんが、そんなに細かくPythonをインストールする必要がある人ってどれだけいるんでしたっけ?

pyenvはPythonのインストールから含めてオールインワンの実行環境を提供したいという目的があるのでしょうが、そもそもPythonのマイナーバージョンのアップデートはそこまで大きくなく、マイナーバージョンのリリースもかなり慎重です。実際マイナーバージョンでの下位互換性は高く、マイナーバージョンのアップデートも1年に1回程度です。公式のリリース情報のページで確認できます。

プロジェクトで稼働させているバージョンを固定させているならそのバージョンだけ入れればよいわけですし、マイナーバージョンごとにアップデートを行うにしても1年に1度あるかないか、さらに攻めた環境でマイクロバージョンごとにインストールするにしてもたかが知れています。(マイナーバージョンは大概がセキュリティフィックスか明らかなバグ修正。)であれば、pyenvでPythonを新規にインストールする必要ってどれくらいあるんでしょう。マイクロバージョンごとに確認しなければいけないライブラリ作者ぐらいでしょうけれど。

Pythonのインストール自体は configuremakemake installをするだけなので、かなり簡単にビルドできます。(pyenvでインストールを実行している内容もこれです。先ほどのThe Zen of Pythonでいえば “Explicit is better than implicit.” の項に当てはまる内容ではないでしょうか。)

で、本題の「どのツールが必要なの」という点ですが、基本的に virtualenv (venv) があれば事足ります。

venvのみを使う場合

venvはそもそもvirtualenvというツールがPython2系で広く使われてデファクトスタンダードになった時期がPythonのLanguage Moratoriumの期間に重なり、その時期にPython3ではこれを標準に入れようという動きから取り込まれたものです。

ですので、基本的には同じものと考えてもらって問題ありません。またパッケージ管理ツールに関しても現状ではpipに落ち着き、後ろで使われるパッケージングもwheelで安定したので、この辺はpipがデフォルトでインストールされる最近のPythonでは特に気にする必要はありません。

もしこの辺のいきさつを知りたい場合は次のエントリを参照してください。(古い話なので、そこまで知りたい人でなければ特に気にしなくてよいです。)

ただし、venvはPython3以降でしか使えません。Python3以降のみで使う場合には venv で事足りる、という認識でよいと思います。たまに例外もありますが、その場合は virtualenv を使えばよいでしょう。(Debian系では venv に必要な ensure-pip が設定されてないバイナリがインストールされることがあるのでその場合は python3-pip を入れてpipで virtualenv だけ入れればよいです。)

venv が入っていれば次のコマンドで仮想環境を作成できます。

$ python3 -m venv hoge
$ . hoge/bin/activate

virtualenvを使う場合

Python2系とPython3系両方でうまくやりたい場合は virtualenv を使えば良いです。基本的に venvvirtualenv の中からPython3系だけでうまくやるために必要なものだけ切り出してるので、基本的にできることは変わりませんし、メジャーバージョンを切り替えるような場合は virtualenv 一択となります。

virtualenvはPython本体のバージョン切替には使えませんし

先のエントリではPython本体のバージョン切り替えができないと書かれていますが、ふつうにPythonのバージョンの切り替えもできますよ。virtualenv のオプションを確認してみましょう。

  -p PYTHON_EXE, --python=PYTHON_EXE
                        The Python interpreter to use, e.g.,
                        --python=python2.5 will use the python2.5 interpreter
                        to create the new environment.  The default is the
                        python2 interpreter on your path (e.g.
                        /usr/bin/python2)

たとえば、Python2系がデフォルトの環境で、virtualenvでPython3系の環境を使いたいとき。

% python -V
Python 2.7.9
% python3 -V
Python 3.4.3
% virtualenv --version
1.11.6
% virtualenv -p python3.4 hoge
Running virtualenv with interpreter /usr/bin/python3.4
Using base prefix '/usr'
New python executable in hoge/bin/python3.4
Not overwriting existing python script hoge/bin/python (you must use hoge/bin/python3.4)
Installing setuptools, pip...done.
% which python
/home/ymotongpoo/hoge/bin/python
% python -V
Python 3.4.3
% deactivate
% python -V
Python 2.7.9

ちゃんとPython2系がデフォルトの環境でPython3.4の環境を作れていますね。

virtualenvwrapperについて

virtualenvwrappervirtualenv での環境作成や環境の切り替えを便利にするためのラッパーツールなので、内側で何をやってるかわかれば、使う使わないは本人次第です。 virtualenvwrapper でやっていることは次の内容だけです。

  • mkvirtualenv では特定のディレクトリ以下(eg. $HOME/.virtualenvs)で virtualenv のオプションをそのまま受け取って環境を作成する
  • workon では deactivate を実行し、指定した環境の bin/activate を実行する
  • rmvirtualenv では、mkvirtualenvで作成した仮想環境のディレクトリを削除する。

ディレクトリの移動などが本当に面倒」という人であれば使えばよいと思います。

まとめ

巷にいろいろとツールがあり、それらを紹介するエントリーがたくさん見つかるので、何か複雑なように見えますが、その実はシンプルです。デファクトスタンダードを使って、それをみんなでよくしていこう、というPythonのエコシステムに乗っかるほうがいろいろと便利ですよ。

2017.01.30 追記

ブコメTwitterでの反応を元にコメント。