mk-toolブログ

エンジニアと家のことをごちゃごちゃと書いてます

【Python】issubclassでTrueを期待するのにFalseが返ってくる

概要

issubclassを使っていて、Trueを期待するのにFalseが返って来て頭がおかしくなりそうになった。 時間をかけた割に大したことを得られなかった悲しさをこのブログにぶつける。

本題

ある復刻プロジェクトをやっているのだが、そのシステムではdynamoDBを利用している。 dynamoDBを利用するためにはbotoというライブラリを利用しているが、せっかくやるのでbotoboto3にアップデートすることに決めた。

botoを利用する際は、さらにbotoの扱いを抽象化するdynamodb_mapperというものを利用していたが、dynamodb_mapperは最終リリースが2013年であり、更にboto3に対応していないため自分でboto3用のmapperを書かなければいけなかった。

とりあえず動かせそうなmapperを作成して動作確認をしている時に問題が起きた。 ここでmapperのクラス名はDynamoDBModelで、以下のようにしてimportしている。

from dynamodb_mapper.model import DynamoDBModel

そして、今回model_boto3というファイルでmapperを作成したので、以下のようにimport文を書き換える。

from dynamodb_mapper.model_boto3 import DynamoDBModel

新mapperを作ったが、class名はもちろんのことメソッドのインターフェースを変えていないところが自身のこだわったというか、意識した部分だ(コードをきれいにしたらmapperは公開しようと思う)。 しかし、このことが災いを引き起こすなんて思ってもみなかった。

プロジェクトで実際にdynamoDBを利用するためのModelでは、DynamoDBModelクラスを継承している。 以下のようなかんじで。

class Hoge(DynamoDBModel)

このように継承しているのは、システム中で利用しているDBはdynamoDBだけでなくMySQLも利用しており、ModelがDynamoDBModelを継承しているか、MySQL用のクラスを継承しているかによって処理を分けるためだ。

さて、この処理を分ける部分だが、以下のようにしてDynamoDBModelを継承しているかを判断していた。DynamoDBModelのimportではdynamodb_mapper.model_boto3を指定しているし、issubclass()で指定しているDynamoDBModelの部分にマウスポインタを当てると、vscodeがこの型はdynamodb_mapper.model_boto3DynamoDBModelですよって教えてくれているし、これで完璧でしょ、と思った。しかしながら現実はfalse。

issubclass([Hogeのインスタンス], (DynamoDBModel,))

issubclassの使い方が間違っているのかな、と思ったのだが全然間違ってなさそうだし、DynamoDBModeldynamodb_mapper.model_boto3なはずだし、何が悪いのかがさっぱりわからなかった。

と、悩んだり調べたりして2時間経った時にDynamoDBModelはカーソルを乗っけるとdynamodb_mapper.model_boto3って出てくるけど実は旧DynamoDBModelのdynamodb_mapper.modelを参照しているのではないかと思った。 案の定、以下のようにするとtrueが返って来た。

issubclass([Hogeのインスタンス], (dynamodb_mapper.model_boto3.DynamoDBModel,))

実際、こんなフルパスで型指定をしないので旧DynamoDBModelは駆逐して新DynamoDBModelしかない状態にしておいたり、そもそもDynamoDBBoto3Modelみたいな名前にして今後開発を進めて行こうと思った。

関係ないがこの件について調べてみると、pythonのissubclassでハマるという記事があり、抽象クラスに対してはissubclass()は継承していてもfalseを返してくる、という豆知識も学んだ。 時間をかけた割には得たものが小さすぎるが、次へ進もう。

参考

https://takemasa5.tumblr.com/post/8455564961/python%E3%81%AEissubclass%E3%81%A7%E3%83%8F%E3%83%9E%E3%82%8B
takemasa5.tumblr.com

【Laravel】TokenMismatchExceptionのエラーが出た

概要

LaravelをAPIサーバーとして利用したが、POST時にTokenMismatchExceptionと出てきたのでメモ。

対応方法

CSRFのチェックを外すためにapp\http\Middleware\VerifyCsrfToken.phpの中に以下を記載。

protected $except = [
  'api/*' 
];

しかしこれはセキュアではないためしっかりとCSRFの対策をしなければいけない。 このWeb APIってCSRF対策出来てますか?って質問にこたえよう を読むとどういったAPIサーバにおけるCSRFの対策の仕方がわかる。

参考

https://stackoverflow.com/questions/37383165/tokenmismatchexception-for-api-in-laravel-5-2-31 https://qiita.com/rana_kualu/items/3f9d0d6b9a363fd2108e https://qiita.com/maruloop/items/e14d02299bd136f4b1fc

【Python】htmlファイルの改行とタブを削除するやつ

概要

htmlビルダーを利用して仕事をしているのだが複雑になってくると最終的に出力されるhtmlと、デザイナーが作ったこういう風に作ってください、というモックのhtmlとちゃんとあっているかを目で確認しないといけないのは非常に面倒である。Diffツールを使おうにも改行とかタブの数が若干違う、ということで差分として出されてしまうし、どうしようかと思った。 (もしかしてまともなエンジニアは何かツールを使っていたりする…?) でもまぁ、自分でミニマムに使えるので十分、というかんじなのでツールを探さずに話を進める。

出力されているhtmlの比較にはDiffツールを使うのだが、改行とかタブの数とかをいちいち直すのも手間なので、いっそhtmlタグごとに改行してタグの前方に着いているタブ(2スペや4スペ)を削除してから、Diffツールにかければ差分もまだ少なくなるかな、というのでpythonで作った。

<div>
  <ul>
    <li><span class="aaa">aaa</span></li>
    <li><span class="bbb">bbb</span></li>
  </ul>
</div>
<div>
<ul>
<li>
<span class="aaa">
aaa
</span>
</li>
<li>
<span class="bbb">
bbb
</span>
</li>
</ul>
</div>

以下がコードである。flatten.pyという名前ででも保存して、以下のように実行すると同じフォルダにテキストファイルが出力(ファイル名はもとのtxtファイルの先頭に_(アンダースコア)が付与された名前)されるので、htmlビルダーで出力した用とデザイナーが作った用で2回実行して出力された2ファイルをDiffにかければ使える。

python3 flatten.py hogehoge.txt
python3 flatten.py fugafuga.txt
import sys
import re

argvs = sys.argv

f = open(argvs[1])
file_contents = f.read()
f.close()

replaced_val = file_contents.replace("    ", "")
replaced_val = re.sub(r'\t', "", replaced_val)

replaced_val = re.sub(r'(<!--.*?>)', '', replaced_val)
replaced_val = re.sub(r'(<.*?>)', r'\1\n', replaced_val) # \1はマッチした文字の再利用(※1)
replaced_val = re.sub(r'(\n){2,10}', '\n', replaced_val) # 10である必要はないけど

file = open('_' + argvs[1] + '.txt', 'w')
file.write(replaced_val)

感想

すごく雑だけど、チェックしやすくなったー!

参考

※1 正規表現で一致した文字列を取得し、置換のときに再利用する on Python3

【Typescript】[ts] Generators are only available when targeting ECMAScript 2015 or higher.がでた

redux-sagaを利用する際に、

export function* helloSaga() : void{
  console.log("hello saga");
}

を書いたら、

[ts] Generators are only available when targeting ECMAScript 2015 or higher.

って言われた。

"target": "es6",

を書いてあげればOK。