moto+boto3のテストでOSError、NoCredentialErrorが出る場合

現象

moto == 1.3.7,
boto3 == 1.9.130
環境でテスト実行時に発生。

urllib3.exceptions.NewConnectionError: <botocore.awsrequest.AWSHTTPConnection object at xxxxxxxxxxxxxxxx>: Failed to establish a new connection: [WinError 10051] 到達できないネットワークでソケット操作を実行しようとしました。

テストのときにboto3がモックできておらず、AWSに通信が行ってしまっているが、認証情報不足でErrorになっているっぽい。

原因

開発環境にAWS接続用環境変数が存在しなかったため。
ダミーでもいいので何らかの値がないとmotoが動かない。
コマンドラインから、 AWS CLIaws configure を実行して、何らかの値をセットしておく。
(AWS CLIが入っていない場合は、

docs.aws.amazon.com

を参照。)

パソコン変えてからこれが発生して苦しんだ…初期設定はだいじ。

Windows環境でpipenvコマンドが認識されないとき

pip install pipenv したのに

C:\Users\foo\var\ >pipenv
'pipenv' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。

になるときの対処法。

stackoverflow.com

stackoverflowに対処法があった。
原因は virtualenv の競合とのこと。

pip uninstall virtualenv

pip uninstall pipenv

pip install pipenv

で解決。

dependabotのcompatibilityとは何か

dependabotがGitHubに買収された ので、これを機に最近dependabotの利用を始めた方も多いはず。
しかしいざdependabotからPRが飛んで来ると見方に困ったので記録に残します。

f:id:komajou:20190529202945p:plain

タイトルの通り、「compatibility」って書いてるけどこれ何??状態になった。
公式の説明はこちら。
https://dependabot.com/compatibility-score/

まとめると、
「①dependabotを導入していて ②同じ内容のアップデートをdependabotが作成した を満たす世界中のプロジェクトのうち、
更新後のCIテストがグリーンになった確率」。

具体的にいくつのプロジェクトを母数としているかは、PRのnoteに含まれるdependabotのバッジをクリックすることで確認することができる。
こんな感じ。
https://dependabot.com/compatibility-score/?dependency-name=django&package-manager=pip&previous-version=2.1.7&new-version=2.2.1

↑のページにはCIテストで失敗している公開リポジトリ一覧も表示される。
もしそれらが同じ原因で失敗しているなら、何が悪かったのかの原因究明の役に立つかも。

というわけで、dependabotの言う互換性(compatibility)とは、他のプロジェクトでの移行成功確率の実績値でした。
コードベースで機械的に何かを判別しているわけではない。
なので、リファレンスに倣ってお作法通りの使い方をしている場合は、スコアが高ければおそらく大丈夫といえるでしょう。
そうじゃないときは、スコアを過信すると事故があるかもしれない。
かもしれない、しか言えないけど。

この説明でいくと、ときどきでる compatibility unknown は、同じ内容の更新を反映したプロジェクトが他にない or 母数が少ないことが原因だと推測。

serverless-python-requirementsで変名Pipfileを読むことはできない

タイトルの通り。
serverless-python-requirements で無理をしたかったができなかった話。

serverless-python-requirementsはpipだけでなくpipenvにも対応している。

custom:
  pythonRequirements:
    usePipenv: true

でpipenvのPipfileを読むことが可能になるが、

  • serverless.ymlと同じディレクトリにある
  • ファイル名が Pipfile である

を満たす場合のみ、pipenvを使うことができる。
つまり、「変名Pipfileを別のディレクトリに置いておく」などはできないのである。
しょんぼりなのじゃ。

どうしてもそういうことをやりたい場合は、requirements.txt を毎回ジェネレートする方法でごり押し可能。
serverless.ymlは以下のように設定する。

custom:
  pythonRequirements:
    fileName: ../requirements.txt

そして、slsの前にpipenvをlockして、requirements.txtをジェネレートする。

$ pipenv lock -r > requirements.txt

タイ・チェンマイに行ってきた

タイトルの通りですが、2019年1月末にチェンマイに行ってました。

チェンマイはご飯もうまいし治安もよい、観光名所も多い、さらにショッピングモールが多い上に遅くまでやっているという、一日中いろんなことして遊べる最高の観光地です。

良いとこだったよ!の紹介をしたいので、自分の記録も兼ねてまとめてみました。

続きを読む

Python+Django+MySQL環境でMySQLdbがModuleNotFoundErrorになったときの対処

こんなエラーログ

  File "/usr/local/lib/python3.6/site-packages/django/db/backends/mysql/base.py", line 15, in <module>
    import MySQLdb as Database
ModuleNotFoundError: No module named 'MySQLdb'

対処方法

使っているsettingファイルに、以下のコードブロックを挿入する。

import pymysql
pymysql.install_as_MySQLdb()

Python3系でMySQLに接続するときに出るエラーとのこと。
ログが ModuleNotFoundError を出すので、問題の本質を見失う…

参考

https://codelab.website/python3-django-mysql/

Django Rest FrameworkのSerializerでバリデーションをして、ついでにkey名を変更したいとき

Modelから取得したレコードをシリアライズするSerializerでは、入出力するkey(カラム)名が異なる場合、sourceオプションで入力元のカラム名を指定すればよい。
しかし、dictを整形したりvalidationしたいがためだけにSerializerを使う場合、これが逆にする必要があるようだった。
言い換えると、プロパティ名に入力key名、sourceオプションに出力key名を指定すれば実現可能。

Python 3.6.8、Django 2.1.7で検証。

from rest_framework import serializers

class TestSerializer(serializers.Serializer):
    user_name = serializers.CharField(source='name')
    user_age = serializers.IntegerField(source='age')
data = {
    'user_name': 'たろう',
    'user_age': 24
}

serializer = TestSerializer(data=data)

serializer.is_valid()
>>> True

serializer.data
>>> {'user_name': 'たろう', 'user_age': 24}

serializer.validated_data
>>> OrderedDict([('name', 'たろう'), ('age', 24)])

serializer.data じゃなくて serializer.validated_data を使うのも忘れずに。

serializer.data は source -> property で
serializer.validated_data は property -> source
になると考えると覚えやすい。

ちなみにエラーになるデータはこんな感じです。

data = {
    'name': 'たろう',
    'age': 24
}

serializer = TestSerializer(data=data)

serializer.is_valid()
>>> False

serializer.errors
>>> {'user_name': [ErrorDetail(string='この項目は必須です。',  code='required')],  
     'user_age': [ErrorDetail(string='この項目は必須です。', code='required')]}

この使い方の是非はしらない!