FastAPIで画像をアップロードしてダウンロードする

FastAPIを使って、画像をアップロードして、OpenCVで変換とかして、返却するというのをやりたかったのだが、イマイチな部分もある気はするけど、メモがてらに残しておく。一応、アップロードされたファイルは一旦、一時ファイルに保存している(保存しなくても良いかもしれないけど…)。そんで、process(img)の中で、画像のimgのnumpy arrayを何か変換して、戻した画像のimg_convertedのnumpy arrayを作る。あとは、img_convertedはオンメモリでcv2.imencodeで、png形式にして、Responseで返せば、画像がダウンロードされる。画像はマルチパートでアップロードする感じだけど、FastAPIのhttp://localhost:8000/docs/で送り方を確認すればよいはず。

from fastapi import FastAPI, UploadFile, File, HTTPException, Response
import cv2
import shutil
from pathlib import Path
from tempfile import NamedTemporaryFile

app = FastAPI()


@app.post("/convert/")
def convert(file: UploadFile = File(...)):
    filepath = save_upload_file_tmp(file)
    try:
        img = cv2.imread(str(filepath))
        img_converted = process(img) # OpenCVの変換処理とか
        _, img_enc = cv2.imencode('.png', img_converted)
        return Response(content=img_enc.tostring(), media_type='image/png')
    except Exception as e:
        raise HTTPException(status_code=500, detail='Failed to convert an image.')
    finally:
        filepath.unlink()


def save_upload_file_tmp(upload_file: UploadFile) -> Path:
    try:
        suffix = Path(upload_file.filename).suffix
        with NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
            shutil.copyfileobj(upload_file.file, tmp)
        tmp_path = Path(tmp.name)
    finally:
        upload_file.file.close()
    return tmp_path

FastAPIでアップロードされたファイルの保存はこれを参考にした。

Python 3.7のインストールに失敗するときに

Ubunut 18.04にpyenvで以下のような感じでPython 3.7のインストールに失敗するときには

$ pyenv install 3.7.3
 Downloading Python-3.7.3.tar.xz…
 -> https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tar.xz
 Installing Python-3.7.3…
 BUILD FAILED (Ubuntu 18.04 using python-build 1.2.11-11-g7dd50144)
 Inspect or clean up the working tree at /tmp/python-build.20190516184525.2792
 Results logged to /tmp/python-build.20190516184525.2792.log
 Last 10 log lines:
   File "/tmp/tmp97wtapkd/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/commands/init.py", line 6, in 
   File "/tmp/tmp97wtapkd/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/commands/completion.py", line 6, in 
   File "/tmp/tmp97wtapkd/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/cli/base_command.py", line 20, in 
   File "/tmp/tmp97wtapkd/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/download.py", line 37, in 
   File "/tmp/tmp97wtapkd/pip-19.0.3-py2.py3-none-any.whl/pip/_internal/utils/glibc.py", line 3, in 
   File "/tmp/python-build.20190516184525.2792/Python-3.7.3/Lib/ctypes/init.py", line 7, in 
     from _ctypes import Union, Structure, Array
 ModuleNotFoundError: No module named '_ctypes'
 Makefile:1130: recipe for target 'install' failed
 make: *** [install] Error 1

以下のような感じで、libffi-devを入れておく。

$ sudo apt install libffi-dev

Easter Eggs in Python

役に立たなそうな隠しコマンド的なやつたち。

Hello World

>>> import __hello__
Hello world!

The Zen of Python

>>> 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!

Antigravity

>>> import antigravity     

Braces is Not a chance…

>>> from __future__ import braces
File "<stdin>", line 1
SyntaxError: not a chance