かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

brew pyenv から anyenv の pyenv に乗り換えたら python インストールでエラーになるようになった

brew pyenv を消して anyenv の pyenv で Python をインストールしようとしたら BUILD FAILED というエラーが出るようになってしまった…

環境

  • MacOS Big Sur (11.6.1)
  • anyenv 1.1.2
  • pyenv 2.2.3-1-g423de9ae

結論

  1. Xcode のバージョンが古かったので最新の v13.1 入れ直したらエラー pyenv 経由での Python がビルドできるようになった
  2. anyenv 内の pyenv 内の python が使われるようにするには .zshrceval "$(pyenv init --path)" が必要だった

brew pyenv から anyenv の pyenv に乗り換える

pyenv でインストールしたバージョンを削除

$ pyenv versions
$ pyenv uninstall <VERSION>

brew でインストールした pyenv を削除

$ brew uninstall pyenv

Users/USER_NAME/.pyenv フォルダを削除
.zshrc から brew pyenv へのパスを削除

- export PYENV_ROOT="$HOME/.pyenv"
- export PATH="$PYENV_ROOT/bin:$PATH"
- eval "$(pyenv init -)"

anyenv pyenv をインストール

$ anyenv install pyenv
Install pyenv succeeded!
Please reload your profile (exec $SHELL -l) or open a new session.

# シェルを再起動
$ exec $SHELL -l
# pyenv の確認
$ pyenv -v 
pyenv 2.2.3-1-g423de9ae

anyenv の pyenv でインストールしようとしたらビルドでエラーになる

$ pyenv install 3.10.1
python-build: use tcl-tk from homebrew
python-build: use readline from homebrew
python-build: use zlib from xcode sdk

BUILD FAILED (OS X 11.6.1 using python-build 2.2.3-1-g423de9ae)

なんらかの原因で python-build がコケてしまうっぽい。

X code をアップデートする

  1. Download and install the latest Xcode 12 beta
  2. Go to > Preferences > Locations > Command Line Tools > Choose Xcode 12.0.

cf. Unable to build Python on macOS Big Sur with Xcode 12 beta · Issue #1643 · pyenv/pyenv · GitHub

GitHub の issue にあったコメントには Xcode 12.0 と書かれてましたが現在の最新は 13.1 で、local に入っている Xcode は 11.5 でした。
Xcode (11.5) 経由で使っている zlib が怪しいっぽいので、手っ取り早く Xcode を v13.1 にアップデートします。

https://developer.apple.com/download/all/ から最新の X code をダウンロードする。(サイズでかい…)

# シェルを再起動
$ exec $SHELL -l
# pyenv でのインストールを再挑戦
$ pyenv install 3.10.1
python-build: use openssl@1.1 from homebrew
python-build: use readline from homebrew
Downloading Python-3.10.1.tar.xz...
-> https://www.python.org/ftp/python/3.10.1/Python-3.10.1.tar.xz
Installing Python-3.10.1...
python-build: use tcl-tk from homebrew
python-build: use readline from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.10.1 to /Users/kikiki/.anyenv/envs/pyenv/versions/3.10.1

$ pyenv versions
* system (set by /Users/USER_NAME/.anyenv/envs/pyenv/version)
  3.10.1
$ pyenv global 3.10.1

₍ ᐢ. ̫ .ᐢ ₎ よし

pyenv global が効いてない

pyenv global で設定した Python が設定されていることを確認するために別途ターミナルを開いてみた所…

$ python --version
Python 2.7.16
$ pyenv versions
  system
* 3.10.1 (set by /Users/USER_NAME/.anyenv/envs/pyenv/version)
$ which python
/usr/bin/python

anyenv 内の pyenv ではなく local にデフォルトで入っていた Python が使われているようです…
なんでや工藤…

eval "$(pyenv init --path)" が必要だった

homebrew の時は eval "$(pyenv init -)" だったけど、anyenv の pyenv は eval "$(pyenv init --path)" が無いとダメっぽい
.zshrc に書きを追記します

# pyenv
eval "$(pyenv init --path)"
$ exec $SHELL -l
$ python --version
Python 3.10.1
$ which python
/Users/USER_NAME/.anyenv/envs/pyenv/shims/python

₍ ᐢ. ̫ .ᐢ ₎ 今度こそよし!!!

所感

教訓。
年末年始の忙しい時にスナック感覚で折角だから〜と環境大掃除しようとすると死ぬ。


[参考]

Heroku に Flask のアプリをデプロイしたメモ

ちょいちょいと試せる localhost でないサーバーで検証したいことがあったので Flask のアプリを Heroku にデプロイしてみました。(DB無し)

Flask アプリの準備

# 仮想環境を作成
$ python -m venv .venv
# 仮想環境を実行
$ source .venv/bin/activate
# Flask をインストール
(.venv) pip install Flask

app.py

from flask import Flask

@app.route('/')
def hello_heroku():
  return "Hello flask APP!"

if __name__ == '__main__':
  app.run(debug=True)

Gunicorn の設定を作成する

Heroku に Python アプリをデプロイするには Gunicorn を使う必要があるっぽい

The Django and Flask web frameworks feature convenient built-in web servers, but these blocking servers only process a single request at a time. If you deploy with one of these servers on Heroku, your dyno resources will be underutilized and your application will feel unresponsive.

Gunicorn is a pure-Python HTTP server for WSGI applications. It allows you to run any Python application concurrently by running multiple Python processes within a single dyno. It provides a perfect balance of performance, flexibility, and configuration simplicity.
cf. Deploying Python Applications with Gunicorn | Heroku Dev Center


DjangoやFlaskなどのWebフレームワークを使っているとき、開発時にもサーバーを起動して動作確認をしていると思います。 例えばDjangoであれば python manage.py runserver というコマンドでサーバーを起動できます。 なぜこのサーバーを使わずにgunicornを使うのでしょうか?
理由は簡単に言うと、動作が速いからです。

gunicorn やuWSGI、 waitress といったWSGIサーバーは速く、 安定して動作することを考慮して作られています (何をもって「速い」と言うのかは別の機会に詳しく解説できればと思います)。
なので、一般に公開して多くの人に使ってもらうサーバーなどでは、専用のWSGIサーバーを使うほうが良いです。 「本番環境ではgunicornなど別のWSGIサーバーを使う」と、プラクティスとして覚えておくと良いでしょう。
cf. gunicornでPython製Webアプリケーションを動作させよう(DjangoとFlask) - Make組ブログ

Gunicornのインストール と設定の作成

# gunicorn のインストール
(.venv) pip install gunicorn
# 設定ファイルの作成
(.venv) touch Procfile

Procfile

web: gunicorn app:app 

Heroku へのデプロイ

1. Flask App の依存パッケージのファイルを作成

# Flask App の依存パッケージのファイルを作成
(.venv) pip freeze > requirements.txt
# 仮想環境を終了
(.venv) deactivate

2. git リポジトリの準備

git を使って Heroku にデプロイするので、ここまでのアプリを git 管理下に置きます。

$ git init
# .venv 以外をコミット
$ git add app.py Procfile requirements.txt
$ git commit -m init

3. Heroku CLI のインストール

$ brew install heroku/brew/heroku

4. Heroku にアプリを作成してデプロイする

$ heroku login
# heroku にアプリを作成
$ heroku create <APP_NAME>
Creating ⬢ <APP_NAME>... done
https://<APP_NAME>.herokuapp.com/ | https://git.heroku.com/<APP_NAME>.git
# heroku 側のリポジトリを追加
$ heroku git:remote -a <APP_NAME>
set git remote heroku to https://git.heroku.com/<APP_NAME>
# デプロイ
$ git push heroku main
# アプリの確認
$ heroku open

これで Flask アプリを Hreoku にデプロイすることができました!
めっちゃ簡単〜 ₍ ᐢ. ̫ .ᐢ ₎

4. GitHub に push したら Heroku にデプロイされるようにする

毎回コマンドでデプロイするのはちょい面倒なので GitHub の main リポジトリが更新されたら自動で Heroku にデプロイさるようにします。

まずは Heroku の管理画面にアクセスして、作成したアプリのコンソールを開きます。
f:id:kikiki-kiki:20211017021024p:plain
メニューから「Deploy」を選択

f:id:kikiki-kiki:20211017021122p:plain

  1. Deploy method で GitHub を選択
  2. 接続したいリポジトリを検索
  3. 接続するリポジトリの右にある「Connect」ボタンをクリック

f:id:kikiki-kiki:20211017021417p:plain
Automatic Deploy の欄で、変更をトリガーに Deploy させるブランチを選択して「Enable Automatic Deploys」をクリック

これで GitHub との接続が完了して、選択しブランチに push したり PR をマージしたりで変更があると自動的に Heroku にデプロイされるようになりました!
簡単で助かる〜 ₍ ᐢ. ̫ .ᐢ ₎

所管

Heroku あまり使ってなかったのですが、めちゃめちゃ楽に Deploy できるので実験用途などで積極的に使っていこうと思いました。
おわり。


[参考]

これはフラスコ

Flask Flask-JWT-Extended がよしなに返してくれるエラーをカスタマイズしたい

Flask で書かれた API を使ったフロントの開発をしていました。
JWT が unauthorized になった時のエラーだけ他のエラーと形式が異なり、エラーコードも 401 で他のエラーと被ってしまっていて、フロントでエラーハンドリングが面倒なことになってしまっていたので、API 側に越境してエラー関連をカスタマイズしたメモ。

Python はなんもわからんマンが書いています。ご了承ください。

Flask-JWT-Extended がよしなにエラーを返すようになっていた。

API 側の実装を見にいくと、Flask-JWT-Extended というライブラリが使われており、API@jwt_required() デコレーターが有り、この中で JWT が unauthorized になるとライブラリがよしなにエラーを返却しているようでした。

from flask_jwt_extended import jwt_required

@app.route('/user', methods=['GET'])
@jwt_required()
def get_user():
  # 略 このブロック内に JWT unauthorized 時のエラーの実装はなし

@jwt デコレーターで JWT 関連のエラーハンドリングができる

# JWT が期限切れのとき
@jwt.expired_token_loader
def jwt_expired_token_callback(expired_token):
  token_type = expired_token['type']

  if token_type == 'access':
    return make_response(jsonify({
      'status': 'ACCESS_TOKEN_EXPIRED',
      'message': 'token expired',
    }), 401)
  else:
    return make_response(jsonify({
      'status': 'TOKEN_EXPIRED',
      'message': 'token expired',
    }), 401)

# JWT が invalid だったとき
@jwt.invalid_token_loader
def jwt_invalid_callback(error_string):
  return make_response(jsonify({
    'status': 'INVALID_TOKEN',
    'message': 'invalid token',
    'error': error_string
  }), 403)

# JWT の認証エラー
def jwt_unauthorized_callback(error_string):
  return make_response(jsonify({
    'status': 'UNAUTHORIZED',
    'message': 'invalid token',
    'error': error_string
  }), 403)

₍ ᐢ. ̫ .ᐢ ₎ できたっぽい!!

所感

フレームワークでよしなにしてくれるのは良いけど、ちゃんとフレームワークの使い方分かってないと細やかな部分で困りますね。
にしても Python 関連のドキュメント慣れてないからかも知れないけど見ずらいの多くない????


[参考]