【Python】Poetry で設定してFastAPIを使う環境構築

# はじめに

いろいろ version up をしたいのですが、ここではpoetry を使ったpython のコード開発の方法をはじめるための調査結果をメモしておきます。
おじさんがやりたかったのは、

1. WSL2(Ubuntu20.04), Linux (Ubuntu22.04)で開発。ターミナルとVisual Studio Code で。
1. python の仮想環境とパッケージ管理ができるのか
1. そのままDockerイメージ作れる

というこれまで virtualenv を用いていた環境をそのまま poetry, pyproject.toml で管理できるようにしたい、というのが目的です。
特に仮想環境が含まれるているのかよく分からない状態だったので、試してみました。できるかな。下記に作業結果を置いてあります。

https://github.com/torupati/my_test_fastapi_poetry_docker

# 内容

## 準備

poetry はインストールしなければなりません。本家の指示に従い、インストールするスクリプトを直接実行します。

```
$ curl -sSL https://install.python-poetry.org | python3 -

$ poetry --version
Poetry (version 1.3.1)
```

https://python-poetry.org/docs/


## 新規作成してみる

```
$ poetry new poetry-demo
Created package poetry_demo in poetry-demo
$ tree poetry-demo/
poetry-demo/
├── README.md
├── poetry_demo
│   └── __init__.py
├── pyproject.toml
└── tests
    └── __init__.py

2 directories, 4 files
```

他に、pyproject.toml を生成するのに

```
$ poetry init -n
```
として作ることもできるようにです。

### 仮想環境に入る

```poetry shell``` と打つだけで、自動的に activate してくれました。

```
$ poetry shell
Creating virtualenv poetry-demo-2afA8rmG-py3.8 in /home/x77/.cache/pypoetry/virtualenvs
Spawning shell within /home/x77/.cache/pypoetry/virtualenvs/poetry-demo-2afA8rmG-py3.8
. /home/x77/.cache/pypoetry/virtualenvs/poetry-demo-2afA8rmG-py3.8/bin/activate
$ . /home/x77/.cache/pypoetry/virtualenvs/poetry-demo-2afA8rmG-py3.8/bin/activate
(poetry-demo-py3.8) $
```

```$exit``` でもとに戻れました。

どうやら仮想環境は、ホームディレクトリ直下に作られているようです。ここのcache_dir に当たります。

```
(poetry-demo-py3.8) $ poetry config --list
cache-dir = "/home/x77/.cache/pypoetry"
experimental.new-installer = true
experimental.system-git-client = false
installer.max-workers = null
installer.no-binary = null
installer.parallel = true
virtualenvs.create = true
virtualenvs.in-project = null
virtualenvs.options.always-copy = false
virtualenvs.options.no-pip = false
virtualenvs.options.no-setuptools = false
virtualenvs.options.system-site-packages = false
virtualenvs.path = "{cache-dir}/virtualenvs"  # /home/x77/.cache/pypoetry/virtualenvs
virtualenvs.prefer-active-python = false
virtualenvs.prompt = "{project_name}-py{python_version}"
```

### project 配下に .venv を作りたい場合

virtualenvs.in-project を true にします。上記のように初期設定では null になっているので。

```
$ poetry config virtualenvs.in-project true
```

### パッケージ管理（追加）

何もパッケージをインストールしていない状態ですが、```poetry install``` を実行すると、peotry.lock ファイルが生成されます。パッケージの情報は pyproject.toml に書くこともできるようですが、poetry.lock があれば、そちらが優先されるそうです。レポジトリに置いたりするときは、poetry.lock を置くのが良さそうです。

```
(poetry-demo-py3.8) $ poetry install
Updating dependencies
Resolving dependencies... (0.1s)

Writing lock file

Installing the current project: poetry-demo (0.1.0)
(poetry-demo-py3.8) $ ls
README.md  poetry.lock  poetry_demo  pyproject.toml  tests
```

この時点ではpoetry.lock の中身は空です。このあといろいろインストールされていくものが追加されていきます。(追記)

```
$ cat poetry.lock
# This file is automatically @generated by Poetry and should not be changed by hand.
package = []

[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "123b456bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

いざ、使用するパッケージを追加してみます。

```
(poetry-demo-py3.8) $ poetry add fastapi uvicorn
```

仮想環境で実行しているせいか、インストールしたversion は poetry.lock に書かれています。pyproject.toml も以下のように更新されていました。

```
[tool.poetry.dependencies]
python = "^3.8"
fastapi = "^0.89.0"
uvicorn = "^0.20.0"
```

### 環境の確認

現在のpython の version とか、仮想環境がどこにあったかな、とか分からなくなった時の確認方法としてメモ。

```[bash]
$ poetry env info

Virtualenv
Python:         3.8.10
Implementation: CPython
Path:           /home/x77/Github/xxx/my_try_webapi/.venv
Executable:     /home/x77/Github/xxxt/my_try_webapi/.venv/bin/python
Valid:          True

System
Platform:   linux
OS:         posix
Python:     3.8.10
Path:       /usr
Executable: /usr/bin/python3.8
```

### サーバ起動

app.py を作ります。

```Python:app.py
from fastapi import FastAPI
import os

app = FastAPI()

@app.get('/')
def index():
    return {'Hello': 'World'}

@app.get('/users/{user_id}')
def read_item(user_id: int):
    return {'user_id': user_id}
```

いつものように起動します。

```
$ uvicorn app:app
INFO:     Started server process [10491]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```

ブラウザで http://localhost:8000 や http://localhost:8000/users/1000 で動作確認できます。

## パッケージ(wheel)を作る

これはコマンド一発でできました。

```
(my-try-webapi-py3.8)$ poetry build
(my-try-webapi-py3.8)$ ls dist/
my_try_webapi-0.1.0-py3-none-any.whl  my_try_webapi-0.1.0.tar.gz
```


## コンテナを作る

いまどきは、直接serverless 環境にdeploy できるようなのですが、ここでは一応 docker コンテナを作ることにします。

### コンテナ内でpoetry がインストール

下記のエラーに遭遇。涙。

```
 => ERROR [3/8] RUN curl -sSL https://install.python-poetry.org/ | python3 -                                                    5.8s
------
 > [3/8] RUN curl -sSL https://install.python-poetry.org/ | python3 -:
#0 5.697 Retrieving Poetry metadata
#0 5.697
#0 5.697 # Welcome to Poetry!
#0 5.697
#0 5.697 This will download and install the latest version of Poetry,
#0 5.697 a dependency and package manager for Python.
#0 5.697
#0 5.697 It will add the `poetry` command to Poetry's bin directory, located at:
#0 5.697
#0 5.697 /opt/poetry/bin
#0 5.697
#0 5.697 You can uninstall at any time by executing this script with the --uninstall option,
#0 5.697 and these changes will be reverted.
#0 5.697
#0 5.697 Installing Poetry (1.3.2)
#0 5.697 Installing Poetry (1.3.2): Creating environment
#0 5.697 Installing Poetry (1.3.2): An error occurred. Removing partial environment.
```

調べてみると、素直に pip で入れることができるようなので、以下のようにしたら動いた。

```
ENV POETRY_VERSION=1.3.1
RUN pip install "poetry==$POETRY_VERSION"
```

そのあと、build とfinal を分けるなどありますが、とりあえず、強引に下記のようにしたら動きました。

```Dockerfile:Dockerfile
FROM python:3.10-slim as base
ENV PYTHONFAULTHANDLER=1 \
    PYTHONHASHSEED=random \
    PYTHONUNBUFFERED=1

WORKDIR /app

FROM base as builder
ENV PIP_DEFAULT_TIMEOUT=100 \
    PIP_DISABLE_PIP_VERSION_CHECK=1 \
    PIP_NO_CACHE_DIR=1 \
    POETRY_VERSION=1.3.1
RUN pip install "poetry==$POETRY_VERSION"

COPY pyproject.toml poetry.lock README.md ./
COPY my_try_webapi ./my_try_webapi
RUN poetry config virtualenvs.in-project true && \
    poetry install --only=main --no-root && \
    poetry build

EXPOSE 80
CMD ["/app/.venv/bin/uvicorn", "my_try_webapi.main:app", "--host", "0.0.0.0", "--port", "80"]
```

build します。

```
$ docker build . -t hogehoge/mytrywebapi:test
```

```docker images ``` でできているか確認したのち、実行します。ポートを公開するのを忘れずに。

```
(my-try-webapi-py3.8) xt77$ docker run --rm -p 80:80 --name testapi1 -d hogehoge/mytrywebapi:test
707b5052567d1dba02172428ec333bb1ece1dxxxxxxxxxxxxxxxxxxxxxxxxx
(my-try-webapi-py3.8) xt77$ docker logs testapi1 -f
INFO:     Started server process [1]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
```

### poetry を用いてDocker build をどのように行うのか

下記に長い議論があるがとても参考になります。個人的には、build をスマートにして、最終盤に必要なものだけコピーする、といういつものように使いたいのですが、まだスマートにできていません。poetry そのものをインストールするのに大変だった。スマートにできたらいいなぁ。

https://stackoverflow.com/questions/53835198/integrating-python-poetry-with-docker

https://github.com/python-poetry/poetry/discussions/1879?sort=new

https://motamemo.com/linux/linux-tips/linux-ubuntu-apt-update-not-be-applied-error/

https://docs.docker.jp/engine/articles/dockerfile_best-practice.html

### Docker-compose

これは以前に書いたのと同じにできると思います。

https://qiita.com/XPT60/items/cd3bf801041a51fbf067


# まとめ

とりあえず、poetry で環境構築してFastAPIを使い、開発をすることはできそうな感じでした。作業途中なのですが、明日からまた普通の会社人になるので、メモを挙げておきます。

### 参考にしたページ

感謝です。

- https://rinatz.github.io/python-book/ch04-06-poetry/

- [Poetry はじめました - チーム開発における Python の開発環境]( https://tech.isid.co.jp/entry/2022/08/22/Poetry_%E3%81%AF%E3%81%98%E3%82%81%E3%81%BE%E3%81%97%E3%81%9F_-_%E3%83%81%E3%83%BC%E3%83%A0%E9%96%8B%E7%99%BA%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B_Python_%E3%81%AE%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83): Github Actions での使い方などノウハウが満載。

- https://dev.to/nimishverma/a-guide-to-start-a-fastapi-poetry-serverless-project-142d
AWS serverless にdeploy している。

### 参考になるかも

こちら、とても勉強できそうなのでメモ

https://github.com/svx/poetry-fastapi-docker

https://iij.github.io/bootcamp/server-app/fastapi/

https://fastapi.tiangolo.com/ja/tutorial/

## その他後日談

その後、scipy をpoetry add しようとしたら、scipy が対応するpython の version に制限があり、poetry init -n で作った設定だとpython の制限がゆるくてerror になることがありました。そこでは、素直に

```
$ poetry add scipy
Using version ^1.10.1 for scipy

Updating dependencies
Resolving dependencies... (0.0s)

The current project's Python requirement (>=3.8,<4.0) is not compatible with some of the required packages Python requirement:
  - scipy requires Python <3.12,>=3.8, so it will not be satisfied for Python >=3.12,<4.0

Because no versions of scipy match >1.10.1,<2.0.0
 and scipy (1.10.1) requires Python <3.12,>=3.8, scipy is forbidden.
So, because depthcompletion depends on scipy (^1.10.1), version solving failed.

  ? Check your dependencies Python requirement: The Python requirement can be specified via the `python` or `markers` properties

    For scipy, a possible solution would be to set the `python` property to ">=3.8,<3.12"
```

なので、素直に、pyproject.toml でversion 指定を変更しました。それで問題なくpoetry add できました。

```toml:pytproject.toml
[tool.poetry.dependencies]
python = ">=3.8, <3.12"
#python = "^3.8"
```

https://stackoverflow.com/questions/73116647/why-cant-i-install-a-python-package-with-the-python-requirement-3-8-3-11-i


### 追記

- virtualenvs.in-project を追記 (2023/01/15)
- poetry.lock, poetry build, Docker 作る試みを追加(2023/02/23)
- poetry env info を追記(2023/02/25)
- Docker build/run を追記(2023/02/25)
- Github に公開(2023/02/25)
- モジュールに対応したpython のバージョン指定(2023/03/26)