WebAPIでpythonコードを実行できるシステムを作った話

# なぜ作ったのか
時々バッチ処理やスレイピングなど長時間動作させたいプログラムを書くことがあります
もちろんローカルで実行すれば良いのですが
重たい処理であったり長い時間実行する必要がある場合、PCに負荷がかかるのを避けるためです

>「＋そのコードを保存出来る」という機能を実装すれば
AWS Lambda的なサーバーレスコンピューティングサービスが出来るのでは？！と思いました

作りました

https://qiita.com/HawkClaws/items/14032426758cd04f739d

# 作ったもの
WebAPIのBodyにpythonのコードを書いてPOSTするとが実行されて結果が返されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/b574ec64-1c07-151b-2265-b8dd488a83a3.png)

# 技術スタック
- 言語
python

- フレームワーク
django

- デプロイ先
Heroku

# コード
Djangoで作っているので、url.pyとview.pyのみ載せます
Djangoに関する情報は探せばいっぱい出てくると思うのでそちらを参照ください


``` python:url.py
path('wakeup/',views.wakeup ,name='wakeup'),
path('api-exec/',views.api_exec ,name='api-exec'),
path('api-exec-async/',views.api_exec_async ,name='api-exec-async'),
path('api-exec-async-log/',views.api_exec_async_log ,name='api-exec-async-log'),
```

``` python:view.py
api_exec_async_data = {}

@csrf_exempt
def api_exec(request):
    """
    POSTされたPythonのコードを同期実行します
    """
    try:
        data = {}
        code = request.body.decode('utf-8')
        # exec(code, data)
    except Exception as e:
        print(traceback.format_exc())
        return HttpResponse(traceback.format_exc(), status=400)

    exec(code, data)
    if 'exec_result' in data.keys():
        return HttpResponse(data['exec_result'])
    else:
        return HttpResponse('')

@csrf_exempt
def api_exec_async(request):
    """
    POSTされたPythonのコードを非同期実行します
    """
    try:
        code = request.body.decode('utf-8')
    except Exception as e:
        print(traceback.format_exc())
        return HttpResponse(traceback.format_exc(), status=400)

    t1 = threading.Thread(target=exec, args=([code,api_exec_async_data]))
    t1.start()

    return HttpResponse('OK')

def api_exec_async_log(request):
    """
    非同期実行のログを取得します
    """
    if 'exec_result' in api_exec_async_data.keys():
        return HttpResponse(api_exec_async_data['exec_result'])
    else:
        return HttpResponse('')   

def wakeup(request):
    """
    djangoの起動を維持します
    """    
    return HttpResponse("")

```

# 使い方
- api-exec pythonのコードが同期的に実行され、結果が返ります
- api_exec_async pythonのコードが非同期的に実行されます
結果取得は難しいので、DBを用意するなどしてそこにデータを投入するようにします
- api_exec_async_log 非同期実行時のログを取得します


# 実際に使ってみる

#### 同期実行　
exec_resultに結果を入れると、レスポンスとして実行結果が返ります
また、printでサーバサイドにログとして出力されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/b574ec64-1c07-151b-2265-b8dd488a83a3.png)
* * *  

#### 非同期実行
exec_resultに結果・ログを入れると「api_exec_async_log」で結果・ログが取得できるようになります
ただ、消滅してしまう可能性もあるため、実行中フラグなどに使用するのが良いと思います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/85bfbdbb-6930-1598-9a1d-09d75ba3aa94.png)
* * *  
#### 非同期実行時のログ取得
非同期実行時にexec_resultに入っている値を取得できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/51574953-a6a5-830f-6e44-bd68b404c34a.png)

# 非同期実行時の実行結果の保存
「非同期実行時は結果の取得が困難なので、DBを用意してデータを投入します」と書きましたが
RealtimeDatabaseのRestApiがおすすめです（コレについては別記事を書くかもしれません・・・？）
何気なく使っていますが、importも使用できます
もちろん、環境に対応するライブラリがあることが条件になります

実行WebAPI（RealtimeDatabaseのRestApiを使用してデータを保存）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/f1573b16-91a6-0e5b-7e7d-6702a0d18eaa.png)


RealtimeDatabaseの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/911989/3c9add70-d77c-6a0c-27a2-2347e249c1d6.png)


# この記事を書いていて思ったこと
pythonコードを実行できるWebAPI
「＋そのコードを保存出来る」という機能を実装すれば
AWS Lambda的なサーバーレスコンピューティングサービスが出来るのでは？！と思いました
作れたら、また記事を書こうかと思います

# まとめ
外部から入力されたコードがかんたんに実行できるところが
さすが動的言語だなぁと思いました
それくらいですかね・・・？

書き忘れていましたが、Herokuの無料枠では起動から３０分なにもアクセスがないとスリープモードに入ります
なので、wakeupというAPIを用意したのですが・・・
重たい処理を動かす際はfor文のなかにwakeupAPIを叩くように工夫する必要があったります

以上です