"BOKU"のITな日常

62歳・文系システムエンジニアの”BOKU”は日々勉強を楽しんでます

TestCaseの中に「os.path.abspath(__file__)」をコピペした凡ミスを反省(笑)/django3.0

うっかりやってしまって、あまりの凡ミスぶりに自分でも赤面したものです。

f:id:arakan_no_boku:20200419012754j:plain

 

何が失敗だったのか?

 

djangoで学習済のWord2Vecモデルを使った画面の整理をしました。

使い捨てのつもりで、モデル生成もviews.pyに書いていたのをモジュール化して、UnitTestも書いておこう。

そう思ったわけです。

自分が最近使っているフォルダ構成はこんな感じ。

functionとかmoduleを階層化して、階層をとばして結合しない縛りをいれてます。

f:id:arakan_no_boku:20200420002722p:plain

なので。

modulesフォルダに本体である「word2vec.py」を置き、word2Vecモデルを使った何等かの処理クラスはfunctionsフォルダにおき、views.pyではfunnctionsフォルダのクラスだけを使うようにするわけです。

 word2vec.py内部で使う学習済モデルファイル(日本語の場合はja.bin)のパス(今回はjaフォルダ)は、動的に引数で与える想定なので、jaフォルダのパスは使う側のfunctionsのソースおよびTestCase内に書きます。

お試しの時は、「ja.bin」は、views.pyのあるフォルダ直下に「ja」フォルダをきって、そこにおいてましたので、ja.binの絶対パス

os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

 みたいにsettings.pyのBASE_DIRの記述からコピペし手書いていました。

これを、functionやunittestのソースににうつすときに、他のソースごとコピーしてしまった。

これが今回の大失敗です。

 

どのあたりが凡ミスなのか

 

フォルダ階層が違うソースコードに、「os.path.abspath(__file__)」なんて書いて、同じパスが返されるわけがありません。

note.nkmk.mesettings.pyの「BASE_DIR」の記述は以下です。

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

BASE_DIRとは「settings.py」のあるフォルダ・・ですから、当然です。

お試しの時は、settings.pyと同じ階層にあるviews.pyに書いていたので、その部分のコピペで、「os.path.dirname(os.path.dirname(os.path.abspath(__file__)))」と書いても同じパスが保証されるので、手抜きしてコピペしてました。

ところが。

そんなことをすっかり忘れて、ソース丸ごと、コピペしてしまった。

これが問題です。

人は忘れる生き物なのだから、そういう環境依存のことをしてる時は、コメントを書いておけよ・・ということですね。

反省です。 

 

どうすればよかったのか。

 

djangoには「settings.py」で定義された値を読む機能が用意されています。

それを使えばよいだけです。

例えば、BASE_DIRを使いたいなら。

from django.conf import settings

 

basedir = getattr(settings, 'BASE_DIR', None) 

ですね。

このやり方なら、views.pyに書こうが、下の階層のモジュールやテストソース内に書こうが、安定して同じ「BASE_DIR」を取得できます。

だから。

本当なら「ja.bin」のパスも、settings.pyの中に「JA_BIN_PATH」のように独自の名前で定義して、それを読み出せばいいだけです。

例えば、settings.pyで。

JA_BIN = '\\etcdemo\\functions\\modules\\ja\\ja.bin'

のように独自定義を追加しておいて。

self.basedir = getattr(settings, 'BASE_DIR', None)
self.jabin = getattr(settings, 'JA_BIN', None)
self.word2vec = wv.Word2Vec(self.basedir + self.jabin)

のように、ja.binファイルのパスを取得します。

 

まとめ

 

 凡ミスで余計な時間を使いました。

でも、ひとつだけよいことがありました。

それは、そのミスに気づけたことです。

最近、このエッセイを読み直し

xn--97-273ae6a4irb6e2hsoiozc2g4b8082p.com

この中の

美しいコードとは、突き詰めれば、シンプルなコードのことです。

システムを構成する各部分が全てシンプルで、個々の部分が担う責務も最小限に抑えられていて、部分どうしの関連もシンプル、そんなコードです

 に共感して、シンプル化と自動テストをするように書き直してます。

その一環で作業をしていたのですが。

以前のように、単純にword2vecに関する処理本体だけをモジュール化して、views.pyの中でja.binのパスを指定するような中途半端な整理の仕方だと、このミスは顕在化せず、潜在バグとして隠れていたことになります。

そこを「階層をとばした結合をもたない」ために、きちんと分けたから気づけた・・ということなので、それなりに意味があったと思っています。

やっぱ、基本は大事です。

ではでは。