LoginSignup
3
2

More than 5 years have passed since last update.

Azure Functions: JavaScript関数でPythonを動かす

Last updated at Posted at 2019-04-26

はじめに

今回は、Azure FunctionsのJavaScript関数でPythonスクリプトを動かす方法を検証します。
Azure FunctionsにはPython関数(プレビュー)もありますが、JavaScript関数を開発しながらちょっとPythonを動かしたいと思うこともあり、試しということでやってみます。

どうやるか

Azure Functionsでは、Pythonの拡張機能が用意されています。このPythonはFunctions Appの関数として直接使用することはできませんが、コマンドとして使うことはできます。
また、npmにはPythonスクリプトを実行するためのモジュール「python-shell」があり、JavaScriptから外部プロセスとしてPythonスクリプトを実行し、値のやり取りをすることができます。
これらを組み合わせ、Azure Functions上でPythonのスクリプトを実行してみます。
afpj-0101.png

Python拡張機能のインストール

※ 以下、AzureにJavaScriptランタイムのFunctions Appが作成されている前提での検証となります。
Functions AppにPython (今回は3.6.4)をインストールします。
AzureポータルでPythonをインストールするFunctions Appに移動し、「プラットフォーム機能」の「高度なツール(Kudu)」からKuduに移動します。
afpj-0102.png
Kuduの「Site extensions」で「Gallery」タブから32bit版の「Python 3.6.4 x86」を探し、+ボタンをクリックしてインストールします。
afpj-0103.png
しばらく経つと「Site extensions」の「Installed」タブにインストールした拡張機能が表示されます。説明欄に「python.exe」の場所が記載されているので、コピーして控えておきます。
afpj-0104.png

関数プロジェクトとPython仮想環境の作成

関数の作成はVisual Studio Code(VS Code)で行います。関数のローカルデバッグを行うため、Python仮想環境も一緒に作成します。
VS Codeを開き、Azure Functions拡張機能で新しいJavaScriptの関数プロジェクトを作成します。
afpj-0105.png
ターミナルを開き、Pythonの「venv」で関数プロジェクトの「.env」内にPythonの仮想環境を作成します。
afpj-0106.png
作成した仮想環境がFunctions App上にデプロイされないように「.funcignore」にPython仮想環境のパスを追加しておきます。
afpj-0107.png

関数の作成

関数プロジェクトにHTTPトリガーの関数を作成します。
ターミナルでPythonスクリプトを動かす関数のディレクトリに移動して、npmの初期化と「python-shell」のインストールを行います。

npm init -y
npm i --save python-shell

リクエストを受ける関数の「index.js」を下記コードに書き換えます。
スクリプトは、HTTPトリガーのリクエストデータを「python-shell」でPythonスクリプト「test.py」の標準入力に流し、Pythonスクリプトの処理結果を標準出力から取り出してレスポンスするようになっています。
Pythonスクリプトを実行する「python.exe」は、ローカルとFunctions Appでパスが異なるため、Functions Appにデプロイされない「local.setting.json」の有無で環境を判別してパスを設定しています。

/* モジュール読み込み */
const { PythonShell } = require("python-shell")
const fs = require("fs")
const path = require("path")

module.exports = async function (context, req) {

    // 実行するpyファイルの設定
    var scriptName = "test.py"

    // pyスクリプトを実行
    var result = await executePy(context, scriptName, req)

    // 結果の出力
    context.res = {
        body: result
    };

};


/* Pythonの実行 */
// python.exeの場所指定
var pythonPath
if (fs.existsSync(path.resolve(__dirname, "../local.settings.json"))) {
    // ローカルデバッグのPython仮想環境
    pythonPath = path.resolve(__dirname, "../.env/Scripts/python.exe")
}
else {
    // Functions AppのPython拡張機能
    pythonPath = "D:\\home\\python364x86\\python.exe" 
}

async function executePy(context, scriptName, inputData) {

    return new Promise((resolve, reject) => {

        // PythonShellの諸設定
        var options = {
            "mode": "text",
            "pythonPath": pythonPath,
            "scriptPath": __dirname
        };

        // PythonShell
        var pyScript = new PythonShell(scriptName, options)

        // Pythonにデータ送信
        pyScript.send(JSON.stringify(inputData))

        // 結果の受信
        var result
        pyScript.on('message', function (message) {
            context.log(message);
            result = message
        });

        // スクリプトの終了
        pyScript.end(function (err, code, signal) {
            if (err) {
                reject(err)
            }
            else {
                resolve(result)
            }
        });
    })
}

「index.js」と同じディレクトリ内に実行するPythonスクリプト「test.py」を作成し、下記コードに書き換えます。
Pythonスクリプトでの処理は、リクエスト本文、またはリクエストパラメータの「name」キーを読み取って、「Hello {name}」を返すという単純なものとしています。

# coding: utf-8

import sys
import json

# JavaScriptからのデータを入力
req = json.loads(sys.stdin.readline())

if "body" in req and "name" in req["body"]:
    name = req["body"]["name"]
elif "query" in req and "name" in req["query"]:
    name = req["query"]["name"]
else: 
    name = "World"

print(f"Hello {name}")

afpj-0108.png

テスト

関数が完成したら、VS Codeでローカルデバッグをします。
VS CodeでF5キーを押してデバッグモードを起動し、Azure Functions Core Toolsの起動後に表示されるローカルホストのURLをコピーし、URLをPostmanでリクエストします。
リクエストパラメータの「name」に「Azure」を付加してリクエストすると、「Hello Azure」というレスポンスが得られました。
afpj-0109.png
ローカルでの動作が確認できたので、関数をFunctions Appにデプロイします。
VS CodeのAzure Functions拡張機能でPython拡張機能をインストールしたFunctions Appに対してデプロイを実行します。
afpj-0110.png
Azureポータルでデプロイした関数のURLを取得します。
afpj-0111.png
ローカルデバッグと同様に、Functions Appの関数に対してPostmanでリクエストします。
リクエストパラメータの「name」に「Azure」を付加してリクエストすると、「Hello Azure」というレスポンスが得られ、Functions App上でも正常にPythonスクリプトが実行されました。
afpj-0112.png

まとめ

Azure FunctionsのJavaScript関数でPythonスクリプトを動かすというのは思いのほか簡単にできることがわかりました。
「python-shell」モジュールによりPythonスクリプトの書き方には制限が出てしまいますが、ラッパーをかませるなどすればその問題も解決できそうです。
JavaScript関数であればDurable Functionsも使えるので、この仕組みと組み合わせてみるのも面白そうです。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2