Pythonパッケージ管理@Windows(2021年8月版)
最近 pipenv から poetry に乗り換えることにした。
その備忘録を兼ねて、なぜ乗り換えることにしたのかまとめておく。
※普段の開発は Windows10 で行うが、運用環境は AWS Lambda などのコンテナ環境を前提とする。
※ 前提とする環境は Python 3.9.6(x64)/3.8.8(x64), pip 21.2.4, pipenv 2021.5.29, poetry 1.1.8
※ 複数 Python をインストールして、 py.exe
を使ってプロジェクトごとにバージョンは切り替えている。どのバージョンの python.exe
も PATH は通してない。
パッケージ管理の歴史などはこちらを参考に。
pipenv の不満点
最新だといくつか解決してるっぽいが、解決できてないものもある。
- pipenv 環境下でコマンド履歴が使えない。
- 地味にストレス。
-
.venv\Scripts\activate
で venv 環境を有効化すれば履歴が使える。 -
SET PIPENV_VERBOSITY=-1
がないと毎回警告が出る。
- docker イメージビルド時に
pipenv sync --system
が使えない。 → v2020.11.4 で修正されたようだ- 実際に稼働するのは docker 上だったりするのでバージョン固定でインストールしたいができない。仕方ないので
pipenv install --system
を使うが、冪等性はいまいち。 - Support
--system
topipenv sync
· Issue #2227 · pypa/pipenv · GitHub
- 実際に稼働するのは docker 上だったりするのでバージョン固定でインストールしたいができない。仕方ないので
-
pipenv install mysqlclient==1.4.6
で失敗する。- wheel を探してきてインストールすれば解決可能だった。
- pip 20.x あたりで解決したっぽい
よく言われる lock が遅いのはそこまで感じたことがない。
上の不満点を解決できるのが poetry だった。
pipenv → poetry による改善
-
py -3.9 -m poetry shell
でコマンド履歴は使える。 -
poetry.lock
をコピーしておけばpoetry install --no-dev
でバージョン固定でインストールできる。 - wheel がないとインストールできないライブラリも問題なくインストール可能
poetry を使った開発の始め方
python はインストール済みという前提で書く。以下のコマンドを実行して python のバージョンが表示されれば問題ない。
py -3.9 --version
--> Python 3.9.6
ちなみにこれから記述する手順を全部手動で実行するのは面倒なので、最後にバッチファイルを用意しておいた。新しく入ったメンバーにはリポジトリクローン後、そのバッチを実行してもらえば開発環境は準備できる。
pip のアップグレード
まずは以下のコマンドで pip を最新版にアップグレードする。
py -3.9 -m pip install --upgrade pip
poetry のインストール
次に poetry をインストールする。
py -3.9 -m pip install poetry
py -3.9 -m poetry config virtualenvs.in-project true
2つ目のコマンドは同じディレクトリに仮想環境を作成するための設定、ほぼ必須。
poetry プロジェクト開始
poetry プロジェクトは以下のコマンドで開始する。
py -3.9 -m poetry init
これだけだと pyproject.toml
が作成されるだけで仮想環境は作成されない。
shell
コマンドや add
コマンドでも仮想環境は作成されるが、手動で作成しておく。
py -3.9 -m venv .venv
仮想環境のアクティベートは
py -3.9 -m poetry shell
または
.venv\Scripts\activate
のどちらかで実行できる。
仮想環境へのライブラリインストール
仮想環境は pip のアップグレードも poetry のインストールもされていないのでインストールしておく。
pip install --upgrade pip
pip install poetry
poetry install
poetry install
コマンドは pipenv といろいろと挙動が異なる。
- 開発用ライブラリもインストールする。
pipenv sync --dev
と同じ動き。- 開発用ライブラリをインストールしたくない場合は
poetry install --no-dev
とする。
- 開発用ライブラリをインストールしたくない場合は
-
poetry.lock
があった場合、そちらを優先してインストールする。なければpyproject.toml
を使用してインストールする。-
pipenv install
がPipfile
を使い、pipenv sync
がPipfile.lock
を使うのとは異なる。
-
おまけ
python 開発時にインストールしておいたほうがいいだろうライブラリもインストールしておく。 poetry add -D
とすると開発用としてインストールされる。
poetry add -D pytest flake8 black mypy
その他
その他 Python に役立つかもしれないメモ。
py.exe
いわゆる pyenv の Windows 版。インストーラを使って Python をインストールしたら使えるはず。インストーラでは launcher
と表示される。
py -3.9 -m pip --version
py -3.9 -m venv .venv
のようにバージョン指定しつつ -m
で通常の Python コマンドを続ける。
venv の作成場所変更
- pipenv の場合は
SET PIPENV_VENV_IN_PROJECT=true
- Poetry の場合は
poetry config virtualenvs.in-project true
pipenv の開始手順
SET PIPENV_VENV_IN_PROJECT=true
py -3.9 -m pipenv --python 3.9
py -3.9 -m pipenv shell
python -m pip install --upgrade pip
pip install pipenv
poetry の開始手順
py -3.9 -m poetry config virtualenvs.in-project true
py -3.9 -m poetry init
py -3.9 -m poetry shell
python -m pip install --upgrade pip
pip install poetry
requirements.txt 出力
pip 向けに requirements.txt を生成するコマンドは以下の通り。
poetry export -f requirements.txt --output requirements.txt
反対に requirements.txt からインストールする方法はないっぽい。
仮想環境を作成しない
Docker イメージなんかで使うかもしれない。
poetry config virtualenvs.create false
poetry 環境構築バッチ
毎回こういう手順を行うのも面倒なのでバッチを作成した。
@echo off
@setlocal enabledelayedexpansion
SET CURRENT=%~dp0
SET PYTHON_BASE_DIR=%CURRENT%
CD /d %PYTHON_BASE_DIR%
SET VENV_DIR=%PYTHON_BASE_DIR%\.venv
SET VENV_BAT=%VENV_DIR%\Scripts\activate
SET PYTHON_VER=3.9
SET PIP_VER=21.2.4
SET POETRY_VER=1.1.8
REM set variable for ColorText, CALL :ColorText %GREEN% "message"
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set "DEL=%%a"
)
SET GREEN=0a
SET RED=0C
SET BLUE=09
:CLEAN_VENV
IF "%1" equ "clean" (
CALL :ColorText %BLUE% "Clean venv."
IF EXIST "%VENV_DIR%" RMDIR /S /Q "%VENV_DIR%"
SHIFT
)
:SETUP_PYTHON
IF NOT EXIST %VENV_DIR%\* (
CALL :CHECK_SYSTEM_PYTHON
IF NOT !ERRORLEVEL! equ 0 GOTO :END
CALL :CHECK_SYSTEM_PIP
IF NOT !ERRORLEVEL! equ 0 GOTO :END
CALL :CHECK_SYSTEM_POETRY
IF NOT !ERRORLEVEL! equ 0 GOTO :END
CALL :ACTIVATE_VENV
CALL :CHECK_VENV_PIP
IF NOT !ERRORLEVEL! equ 0 GOTO :END
CALL :CHECK_VENV_POETRY
IF NOT !ERRORLEVEL! equ 0 GOTO :END
CALL :CHECK_SYNC
IF NOT !ERRORLEVEL! equ 0 GOTO :END
)
cmd /k %VENV_BAT%
EXIT
:END
cmd /k
EXIT
:CHECK_SYSTEM_PYTHON
CALL :CHECK_BIN "Python" py %PYTHON_VER%
EXIT /b %ERRORLEVEL%
:CHECK_SYSTEM_PIP
CALL :CHECK_PYSCRIPT "pip" pip %PIP_VER% "pip install --upgrade pip"
EXIT /b %ERRORLEVEL%
:CHECK_SYSTEM_POETRY
CALL :CHECK_PYSCRIPT "poetry" poetry %POETRY_VER% "pip install poetry"
CALL :ColorText %BLUE% "Configuring poetry."
py -%PYTHON_VER% -m poetry config virtualenvs.in-project true
CALL :ColorText %GREEN% " Done."
EXIT /b %ERRORLEVEL%
:CHECK_PYSCRIPT
CALL :ColorText %BLUE% "Checking System %~1 version..."
py -%PYTHON_VER% -m %2 --version | find /i "%3" > nul
IF %ERRORLEVEL% equ 0 (
CALL :ColorText %GREEN% " OK. %~1 is ready"
EXIT /b 0
)
IF "%~4" equ "" (
CALL :ColorText %RED% " NG, Please install %~1 v%3"
EXIT /b -1
) ELSE (
CALL :ColorText %RED% " NG, Now trying to install %~1 v%3"
py -%PYTHON_VER% -m %~4
)
EXIT /b %ERRORLEVEL%
:CHECK_BIN
CALL :ColorText %BLUE% "Checking %~1 version..."
where %2 2> nul > nul
IF %ERRORLEVEL% equ 0 (
IF "%2" equ "py" (
py -%PYTHON_VER% --version | find /i "%3" > nul
) ELSE (
%2 --version | find /i "%3" > nul
)
IF !ERRORLEVEL! equ 0 (
CALL :ColorText %GREEN% " OK. %~1 is ready"
EXIT /b 0
)
)
IF "%~4" equ "" (
CALL :ColorText %RED% " NG, Please install %~1 v%3"
EXIT /b -1
) ELSE (
CALL :ColorText %RED% " NG, Now trying to install %~1 v%3"
%~4
)
EXIT /b %ERRORLEVEL%
:ACTIVATE_VENV
IF NOT EXIST .venv (
CALL :ColorText %BLUE% "Creating python venv."
py -%PYTHON_VER% -m venv .venv
CALL :ColorText %BLUE% " Done."
)
CALL :ColorText %BLUE% "Activating Python venv."
CALL %VENV_BAT%
CALL :ColorText %GREEN% "Python venv is activated."
EXIT /b %ERRORLEVEL%
:CHECK_VENV_PIP
CALL :CHECK_BIN "pip" pip %PIP_VER% "python -m pip install --upgrade pip"
EXIT /b %ERRORLEVEL%
:CHECK_VENV_POETRY
CALL :CHECK_BIN "poetry" poetry %POETRY_VER% "pip install poetry"
EXIT /b %ERRORLEVEL%
:CHECK_SYNC
CALL :ColorText %BLUE% "Synchronizing venv library."
IF EXIST poetry.lock (
poetry install
) ELSE (
IF EXIST pyproject.toml (
poetry install
) ELSE (
poetry init
CALL :ColorText %BLUE% "Install libraries for developing."
poetry add -D pytest flake8 black mypy
)
)
IF NOT %ERRORLEVEL% equ 0 (
CALL :ColorText %RED% " Sync has error!!"
) ELSE (
CALL :ColorText %GREEN% " Done."
)
EXIT /b %ERRORLEVEL%
:ColorText
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1
ECHO.
EXIT /b 0
Discussion