世界の中心で改めて「WindowsはXXX!」と叫ぶ

またかと言われそうだが、俺はWinがすかん。その理由はたくさんあるが、イチバンのそれがCP932とかいう内部エンコーディング。今回、久々にその地雷を踏んでしまった。足を離して木っ端微塵になる前に、自戒を込めてグチる。

普通な欧米人には理解できないであろう変な文字列(ex:日本語)をインターネットな世界で一緒くたに扱うのにUTF-8は、いまやごく普通であり常識である。そう、何もかもUTF-8でやっていればみんなハッピー、誰も困らないのである。実際AndroidもiOSもMacもLinuxもとっくにそうなっている。が、WinだけがいまだにCP932という「無意味どころか害でしかない」エンコーディングを改めようとしていない。結果、それをそのままファイルに書き出し→インターネットな処理系でそれを読む→機種依存文字の変換不可→DEAD … ある時期までは「よくある話」だった。

が、それって20年も前のこと。それをこの期に及んで食らうとは迂闊だった。更にタチが悪いのは、こういうのを「致命的な問題」と言っても、まず意味がその相手に通じないこと。「手遅れか!」と気づいた時はもう遅い。

まとめ:MSなServerとかSQLとかなシステム = 対人地雷。いずれ絶滅するが、それまでは探知に努め踏まないよう注意すること.。

Possible mission

20年以上も前に作られた「ある」システムの「ある」部分を作り直せとの命令が下った。ここで最初の難敵は「ビッグエンディアンなバイナリファイル×多数」。要はその当時のMotorolaな処理系(弔)がファイルに吐き出したCの構造体で、これをそのまま最新のリトルエンディアンな環境で編集せよというのである。

このような「どこの誰がいつ作ったのかわからんフォーマットへの後方互換」は、いちばんやりたくないところだが仕方ない。最初はそこ(読み書き)だけCで書いてPythonから呼ぼうかとも思ったが、エンディアンの整合を考えただけでウンザリ。何かいい手はないか? と調べていて行き着いたのがctypes.BigEndianStructure。こいつで構造体のメンバーを定義してインスタンスを作れば、エンディアンの違いを(殆ど)意識する事なくプログラミングができる。名称からしてそのものズバリな、こういうのをハナから使えるところはさすがPythonである。

ただ、だがしかし …

こういうのって、そもそもエンディアンの意味とCの構造体とが理解できてなきゃ無理=「書く」手間より「読む」手間の方がかかる。なので、Cプログラミングの経験のない若者には敷居が高い。するとやはり、このワタクシを始めとする「Cで稼いできた旧きエンジニア」ならではの仕事なのかなあ、と思うと複雑な心境。俺もビッグエンディアンと共に絶滅種入りか? … おおコワ!

「データクラス」への期待

これの続き。

Pythonのdictは柔軟で扱いやすいが、一方で値の型が把握しづらい(注1)。他人が書いたコードは言わずもがな、自分の書いたそれもすぐ忘れて「?」となる。またその入れ子やら新たなキーの追加や削除やらも容易だが、こういうの(注2)をやりだすと状況は更に悪化する。Cの構造体のようなカッチリとしたデータ構造を記述するにはどうしたら良いのか? Python3.7から導入された「データクラス」は、そうした要望に対する回答のひとつである。

何よりdictではできない「継承」が使えるだけでも有り難い。実をいうとその存在に気づくまで、俺は通常のクラスでこれをやっていた。そういう意味では「データクラス=データ保持に特化したクラス」と捉えて差し支えない(注3)が、便利な機能が多数盛り込まれているので「これからは遍くこれで」と考えている。

惜しいかな現時点ではクラス変数に不正な型を代入しても動いてしまうし、PyCharmでも警告が出ない。が、これらが実現されるのはそう遠い先の事ではないと思う。尤もそうなるともはやPythonは「動的片付き言語」と呼べなくなるのでは? だが、俺的にはその方がずっといい。

注1: 変数では有効なtypehintがdictのキーには使えない
注2: Pythonに限った話ではなくPHPとかJavascriptとかも同様である
注3: 実際データクラスはclassへのデコレータで記述する

型あるものいつかは助かる

このワタクシも「アセンブラで育ちCで稼いだ世代」なもので最初は「動的片付き言語」が苦手だった。が食うに困って何事も勉強とPHPとかJavascriptとかを使うようになって以来「アレはアレ、コレはコレ」という切り分けができるようになって幾年月、ここ暫くはPythonにすっかり軸足が移っている。ちょいプロにはこれが最適である。

だがその規模が大きくなってくるとやはり「前に自分の書いたコードが理解できない」というクライシスにしばしば陥る。またそうなると、変数や関数の型が曖昧なところが更なる混乱を招く。

そこである時期から変数の定義にはtypehintを、関数の定義にはキーワード引数をと心掛けるようになって状況は大きく改善した。尤もそれらはPythonのというよりむしろ「Pycharmの助け」あってというところだが、結果良ければ全て良しである。なので以前に書いたコードを片っ端からこれらで書き換えた = ここまでは意図したとおりだった … が、ひとつ面倒なやつが残った。

dp = {
    'name': 'sekitakovich',
    'age': 18,
    'valid': True,
    'at': dt.now(),
}

こういうCのstructure的な書き方をしている部分が綺麗に書けなくて困っていたんだが、Python3.7のドキュメントで目にした「dataclasses」なるキーワードからこれが一気に氷解した。

# 詳細については改めて!

lightsail $3.50USDのヤレヤレ

諸般の事情で急遽、本サイトを引っ越すことに。とはいえこれだけのためにまたハウジングだホスティングだはあり得ないので、噂のlightsailを選択。このテのサービスを利用するのは初めてということで、まずは最小プラン(20GB SSD)でインスタンスを作成。

# 実はこれがドタバタの始まり

静的IPのアタッチとDNSの設定を済ませ、旧サーバからコンテンツ一式をインポートしたところで引っ越し完了! なるほどこれはカンタンでいい! と感動した。そして以後それは、ずっと快適に動作していた … いた?

気づけばそれは「データベース接続確立エラー」で止まっていた。焦ってあれこれ調べた結果、原因はストレージが「No left space」なためと判明。なるほど、いまどきストレージの容量が20GBのサーバなんてない。だがそこで更に調べてみると、EC2ではできるLVMの拡張がlightsailでは不可という事実 orz… ここから悪戦苦闘の数時間。

結論からいうと「インスタンスのスナップショットからもっとでかいインスタンスを新たに作る」で再開には成功したが、それは「今回2度めのサーバの引っ越し=上位プランへの変更」でもあった。まんまとハメられた感はあれど、最初からこっちを選んでいればこんな騒ぎをせずに済んだのである。

ただ、lightsailでストレージの利用状況を知るにはSSHでdfしかない(よね?)。もしこういうのに不慣れな人がlightsailでwordpressをと考えて、最小プランを選択した末に同じ状況に陥ったらどうなるのだろう? せめて「Near full」な警告を出すとか、上位プランへの移行ウィザードとかが必要なんでは?

ま、それでも他の激安VPSとかよりはずっと快適でいいんだが。疲れたわ。

FlaskとPyInstallerの微妙な関係

Flaskでテンプレートからのレンダリング(PHPみたいなもの)、及び静的なファイル(JavaScriptとか画像とか)を扱うのは容易である。が、これをPyInstallerで一本の実行形式ファイルにしようとしてハマった。以下はそれを解決した際の備忘録。環境は Raspbian Strech / python 3.7.2 / pyinstaller 3.4。

1. 望みの動作

フォルダとファイルの配置が

.
├── sample.py
├── Static
│   ├── css
│   │   └── sample.css
│   ├── images
│   │   └── sample.png
│   └── js
│       └── sample.js
└── Templates
    └── sample.html

な時、sample.py中で

app = Flask(__name__, template_folder=’Templates’, static_folder=’Static’)
html = render_template(‘sample.html’, argment=’moromoro’)

として動作確認したものをPyInstallerでパッケージ化したい。

2. 最初に結論

$ pyinstaller sample.py -F –add-data ‘Templates:Templates’ –add-data ‘Static:Static’

3. 解説(というほどのモノデハナイ)

上記sample.pyをPython(インタプリタ)で実行すると「pwd=sample.pyの置かれたフォルダ」となり、template_folder/static_folder共にそこからの相対パスになる。ところがこれらをPyInstallerでパッケージにすると「pwd=sample.pyとは無関係な一時的パス(/tmp/_MEI?????とか)」となる。結果レンダリングの開始で「テンプレートがないぞ!」となり、無情にもブラウザにはエラー500が表示される。

これを回避するのに手っとり早いのはtemplate_folder/static_folderを絶対パスとする事だが、これだとフォルダ配置に制限が加わるし、何より格好悪い。そこでこれらフォルダをそっくりパッケージに含めてやれば万事解決。PyInstallerの–add-dataオプションがそれ。

4. 注意点

当然の事ながら上記フォルダ内のファイルはそのままパッケージに含まれるので、あまり巨大なファイルを配置するとパッケージも巨大化する(=展開できなくなる可能性アリ)。

これらパスは実行終了と共に消失する。なので保存目的でこれらパスにファイルを作ったりしないこと。

pi2B+で作成に20分、出来上がったファイルが200MB超、起動に5分(!)。理由は不明。

Windowsだと–add-dataの書式が’xxxx;xxxx’となる模様。

# 疲れたわ …