edit-icon download-icon

Python ハンドラ

最終更新日: Mar 12, 2019

Function Compute で Python を使用するには、Python 関数をハンドラとして定義する必要があります。現在の Python ランタイム環境では、共通ハンドラ または HTTP トリガーハンドラ をサポートしています。HTTP トリガーを使用すると、HTTP リクエストをより効率的に処理できます。HTTP トリガーを持つハンドラを除き、他のすべての Python でコーディングされた関数ハンドラは同じです。

現在、Function Compute は Python 2.7 (runtime = python2.7) と Python 3.6 (runtime = python3) をサポートしています。詳細は、「Python ランタイム環境」 をご参照ください。

内容

このトピックでは、共通ハンドラ、および HTTP トリガーハンドラについて説明します。

共通ハンドラ

簡単な関数の定義は次のとおりです。

  1. def my_handler(event, context):
  2. return 'hello world'

関数名

  • my_handler: 関数名は、関数が生成されるときに定義される “handler” フィールドと一致しなければなりません。たとえば、handler が main.my_handler と指定された場合、Function Compute は main.py で定義された my_handler 関数を取得します。

event パラメーター

  • event パラメーターは、関数を呼び出すときに渡されます。Python 2.7 では、このパラメーターは str 型です。Python 3 では bytes 型です。関数の入力パラメーターです。
  • このパラメーターは、関数によって解析しません。処理の必要に応じて変換できます。たとえば、JSON 文字列が渡された場合、パラメーターを dict 型に変換できます。例:定義済み event:
    1. {
    2. "key": "value"
    3. }
    関数コード:
    1. # -*- coding: utf-8 -*-
    2. import json
    3. def my_handler(event, context):
    4. evt = json.loads(event)
    5. return evt['key']
    value パラメーターが返されます。

context パラメーター

  • context パラメーターには、関数が実行されるときに生成されるリクエスト ID や一時的な AccessKey などの情報が含まれます。コーディングの際に、これら情報を使用できます。このパラメーターは FCContext 型です。
  • context パラメーターは次のように定義できます。
  1. class Credentials:
  2. def __init__(self, access_key_id, access_key_secret, security_token):
  3. self.access_key_id = access_key_id
  4. self.access_key_secret = access_key_secret
  5. self.security_token = security_token
  6. class ServiceMeta:
  7. def __init__(self, service_name, log_project, log_store):
  8. self.name = service_name
  9. self.log_project = log_project
  10. self.log_store = log_store
  11. self.qualifier = qualifier
  12. self.version_id = version_id
  13. class FunctionMeta:
  14. def __init__(self, name, handler, memory, timeout):
  15. self.name = name
  16. self.handler = handler
  17. self.memory = memory
  18. self.timeout = timeout
  19. class FCContext:
  20. def __init__(self, account_id, request_id, credentials, function_meta, service_meta, region):
  21. self.request_id = request_id
  22. self.credentials = credentials
  23. self.function = function_meta
  24. self.service = service_meta
  25. self.region = region
  26. self.account_id = account_id

前述の関数の主要パラメーターには次のものがあります。

  • request_id: 呼び出しの一意の ID を示します。例外が発生した場合は、このパラメーターをメモして、トラブルシューティングを行うことができます。
  • function: 関数名、ハンドラ、関数メモリ、タイムアウト時間など、この関数に関する基本情報を示します。
  • credentials: Function Compute が、 サービスロールを想定して得られるキーを示します。これらのキーは、生成後 15 分で失効します。このパラメーターを使用して、 Object Storage Service (OSS) などの特定のサービスにアクセスできます。これにより、AccessKey が関数コードにハードコーディングされることを防ぎます。
  • service: 現在の関数が提供するサービスの詳細を指定します。たとえば、このパラメーターには、サービス名、および Log Service のログプロジェクトと Logstore、サービスのバージョンを示す修飾子、および versionId を含めることができます。
  • region: この関数が適用される cn-shanghai などのリージョンを示します。
  • accountId: Alibaba Cloud アカウントの ID を示します。

次のコードでは、OSS にファイルをアップロードするために Security Token Service (STS) が使用されています。

  1. import json
  2. import oss2
  3. def my_handler(event, context):
  4. evt = json.loads(event)
  5. creds = context.credentials
  6. # you must enter the security_token
  7. auth = oss2.StsAuth(creds.access_key_id, creds.access_key_secret, creds.security_token)
  8. bucket = oss2.Bucket(auth, evt['endpoint'], evt['bucket'])
  9. bucket.put_object(evt['objectName'], evt['message'])
  10. return 'success'

注意:キーを使用して OSS にアクセスできるようにするには、一時キーに、テンポラリトークンを使用する必要があります。

HTTP トリガーハンドラ

HTTP トリガーハンドラは、共通ハンドラとは異なります。つまり、HTTP トリガーが設定された関数のハンドラは、普通の関数のハンドラと異なります。

  • メソッド 1. ユーザーが関数を作成します。Function Compute は、 関数を呼び出して、リクエストを処理し、レスポンスを返します。

    1. HELLO_WORLD = b"Hello world!\n"
    2. def handler(environ, start_response):
    3. context = environ['fc.context']
    4. request_uri = environ['fc.request_uri']
    5. for k, v in environ.items():
    6. if k.startswith("HTTP_"):
    7. # process custom request headers
    8. pass
    9. # get request_body
    10. try:
    11. request_body_size = int(environ.get('CONTENT_LENGTH', 0))
    12. except (ValueError):
    13. request_body_size = 0
    14. request_body = environ['wsgi.input'].read(request_body_size)
    15. # get request_method
    16. request_method = environ['REQUEST_METHOD']
    17. # get path info
    18. path_info = environ['PATH_INFO']
    19. # get server_protocol
    20. server_protocol = environ['SERVER_PROTOCOL']
    21. # get content_type
    22. try:
    23. content_type = environ['CONTENT_TYPE']
    24. except (KeyError):
    25. content_type = " "
    26. # get query_string
    27. try:
    28. query_string = environ['QUERY_STRING']
    29. except (KeyError):
    30. query_string = " "
    31. print 'request_body: {}'.format(request_body)
    32. print 'method: {}\n path: {}\n query_string: {}\n server_protocol: {}\n'.format(request_method, path_info, query_string, server_protocol)
    33. # do something here
    34. status = '200 OK'
    35. response_headers = [('Content-type', 'text/plain')]
    36. start_response(status, response_headers)
    37. # return value must be iterable
    38. return [HELLO_WORLD]
  • メソッド 2. ユーザーは、呼び出し可能なクラスオブジェクトを作成します。Function Compute は、オブジェクトを呼び出し、リクエストを処理し、レスポンスを返します。

    1. HELLO_WORLD = b"Hello world!\n"
    2. class AppClass:
    3. """Produce the same output, but using a class
    4. """
    5. def __init__(self, environ, start_response):
    6. self.environ = environ
    7. self.start = start_response
    8. def __iter__(self):
    9. status = '200 OK'
    10. response_headers = [('Content-type', 'text/plain')]
    11. self.start(status, response_headers)
    12. yield HELLO_WORLD
    13. def handler(environ, start_response):
    14. return AppClass(environ, start_response)

Python ランタイム環境の関数シグネチャは、Python Web Server Gateway Interface (WSGI) に準拠しています。

入力パラメーター

  • environ: このパラメーターは、すべてのクライアント関連情報を格納する Python 辞書を表します。詳細は、「Parameter environ」 をご参照ください。2 つのユーザー定義キー、 fc.contextfc.request_uri が Function Compute に追加されています。

    • fc.context: 共通ハンドラの context パラメーターとして機能します。

    • fc.request_uri: リクエストの URL を示します。このパラメーターは string 型です。

    注意:environ の HTTP_Variables フィールドは、リクエストヘッダーを含みます。たとえば、 リクエストヘッダーが 'x-Custom-key':'value' の場合、environ パラメーターは、environ['HTTP_X_CUSTOM_KEY']='value' になります。WSGI によれば、リクエストヘッダー内のキーは、key = "HTTP_" + k.upper().replace("-","_") として処理されます。

  • start_response:start_response callable は、Function Compute のランタイム環境で提供されます。2 つの必須の位置パラメーターと 1つのオプションパラメーターがあります。詳細については、「the-start-response-callable」 をご参照ください。次の例では、3 つのパラメーターの名前は status、response_headers、exc_info としています。必要に応じて他の名前を使用することができます。

    1. # Provided by Function Compute runtime.
    2. # status: a string such as '200 OK' or '403 FORBIDDEN'
    3. # return: must be a write(body_data) callable
    4. def start_response(status, response_headers, exc_info=None):
    5. ...
    • status: HTTP レスポンスステータスを示します。このパラメーターは string 型です。
    • response_headers: HTTP リクエストヘッダーを示します。(header_name, header_value) 形式のタプルを含むリストです。

    • exc_info (optional): サーバーがクライアントに返す必要のある情報を示します。

アプリケーションオブジェクトは、environ パラメーターに基づき、サービスロジックの実行を完了した後、結果をサーバーに返さなければなりません。HTTP レスポンスに、status、headers、および body パラメーターを含める必要があります。したがって、アプリケーションが body 値を返す前に、start_response() 関数を呼び出して、status と headers の値をサーバーに返さなければなりません。これにより、アプリケーションが body 値の送信を開始することがサーバーに通知されます。

Function Compute に WSGI ベースの Web フレームワークをデプロイする

前述の メソッド 2 の例では、Function Compute の Python ランタイム環境で、 Flask と Django を使用して、 WSGI ベースの Web フレームワーク上に構築されたプロジェクトを実行できることを示しています。以下は Flask を例として使用しています。

  1. from flask import Flask
  2. from flask import request
  3. from flask import make_response
  4. app = Flask(__name__)
  5. @app.route('/', methods=['GET', 'POST'])
  6. def home():
  7. resp = make_response('<h1>Home</h1>', 200)
  8. return resp
  9. @app.route('/signin', methods=['GET'])
  10. def signin_form():
  11. # service_name,function_name in action url need to be replaced
  12. html = '''<form action="/2016-08-15/proxy/service_name/func_name/signin" method="post">
  13. <p><input name="username"></p>
  14. <p><input name="password" type="password"></p>
  15. <p><button type="submit">Sign In</button></p>
  16. </form>'''
  17. resp = make_response(html, 200)
  18. return resp
  19. @app.route('/signin', methods=['POST'])
  20. def signin():
  21. if request.form['username'] == 'admin' and request.form['password'] == 'password':
  22. html = '<h3>Hello, admin!</h3>'
  23. else:
  24. html = '<h3>Bad username or password.</h3>'
  25. resp = make_response(html, 200)
  26. return resp
  27. @app.route('/signin2', methods=['GET'])
  28. def signin2():
  29. if request.args.get('username') == 'admin' and request.args.get('password') == 'password':
  30. html = '<h3>Hello2, admin!</h3>'
  31. else:
  32. html = '<h3>Bad username or password.</h3>'
  33. resp = make_response(html, 200)
  34. return resp
  35. def handler(environ, start_response):
  36. # do something here
  37. return app(environ, start_response)