2014-04-30

Animes in the 1st quarter of 2014

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

ヒーローごっこで通すと思いきや、途中から想定外の展開に。以降、安っぽくなった感がある。彼女さんには最後のどんでん返しを期待したけど不発。ただ瑞希は良い女で惚れそう。ミネミラの歌はどれも良かった。

エロスはともかく、肝心のヒロインと主人公に魅力を感じない。むしろサブキャラの方が良い味を出している。主に紗矢華とか紗矢華とか、王女も結構いける。

陰陽道物としてはオリジナリティーを感じる。しかし全体的にキャラの魅力がイマイチ、話も最後の件でご都合主義に見える。ただ鈴鹿はツボったので次回があれば見ると思う。

初見で「あの夏で待ってる」との既視感が半端ない。突っ込みたい部分も色々と有るが、結果的には名作になったと思う。耐性のない厨房が見たら妙な病気に罹らないか心配になるレベル(褒め言葉)。

典型的な気持ちだけ主人公なので見ててイライラする。メカも話も特に魅力を感じないし、これ以上面白くなりそうにないので後期があっても見ない予感。

キャラも粒ぞろいで嫌いではないが、もう二押しくらい足りない。ちなみに喫煙者は第三話だけでも見るべき。非喫煙者から普段どう思われているか知っておいて損は無い。てか喫煙者に対する扱いが私と同じ過ぎて笑った。

ヒロインに萌えられず、主人公も空気。結果、たんぽぽちゃん一人が際立つ結果に。声もハマってるし、もうこの娘がヒロインでいいわ。むしろ後半のシリアス部は要らない。

絵は好みじゃ無いが、まさかのパロディー・下ネタ満載で良い意味で期待を裏切ってくれた。ただその反面、元アイドルという設定が殆ど活かせてない気はする。

OP が神。後は内容が OP にどれだけ近づけるかだったが、流石にそれは難しかった模様。個人的にはアタリの作品だったが、最終二話の作画だけは許せない。

前作までの面白さが感じられない。今回ばかりは話数に対してキャラが多過ぎたように思う。個人的に好きなシリーズなので、やるなら時間を掛けてゆっくりやって欲しい。

エロが売りのはずだが、何故かエロスが感じられない。胸タプ音に拘りが感じられなくもないが、キャラに萌えられないのが問題。唯一萌えられる姫神にはおっぱい無いし。

元ネタを知らないので特に思い入れも無いが、見るのが苦痛で無かったことは良かった。逆に言うと、見ても見なくても良い消費型作品の典型例。

2014-03-31

Python: Portablize Sphinx

そろそろ、以前から気になっていた Sphinx を触ってみようかと。スタンドアロン版ソフトウェアをこよなく愛す私は、当然のようにスタンドアロン版を選択。(以降、スタンドアロン版インストーラー SphinxInstaller-1.2.20131210-py2.7-win32.zip を使用)

しかし使ってみて、コレジャナイ・・・orz。うん、私が悪かった。「スタンドアロン」ではなくて、「ポータブル」と言うべきだった。上記はスタンドアロンではあるが、ポータブルではない。具体的には、インストールしたディレクトリを別の場所に移動すると動かなくなる。これでは zip で固めてメンバーへ配布、という訳にはいかない。

調べると、bin/sphinx-*-script.py 中に絶対パスが記述されているのが原因のようだ。特に shebang 行がまずい。shebang は bin/sphinx-*.exe が bin/sphinx-*-script.py を実行するために使用するのだと思うが、shebang は絶対パスでないと動かない。

ここで諦めようとも思ったが、何か悔しいので「起動時に毎回 sphinx-*-script.py を書き換える」という力技に出てみた。

$SPHINX_HOME/sphinx.bat:

@echo off
setlocal

set _dp0=%~dp0
set SPHINX_HOME=%_dp0:~0,-1%
set _dp0=
set PYTHON_HOME=%SPHINX_HOME%\python
set PATH=%PATH%;%SPHINX_HOME%\bin;%PYTHON_HOME%;%PYTHON_HOME%\Scripts

python.exe "%SPHINX_HOME%\bin\setup-portable.py" -x >NUL
if errorlevel 1 (
  echo ERROR: failed to setup
  pause
  exit 1
)
title PortableSphinx
echo *** Welcome to PortableSphinx! ***
echo.
cmd.exe /k

$SPHINX_HOME/bin/setup-portable.py:

#!python.exe

__doc__ = """\
Usage:
  python %s -x
Description:
  Setup PortableSphinx.  This should be executed first whenever the
  Sphinx directory is moved on your PC.
""" % __file__

import filecmp
import glob
import os
import re
import shutil
import sys
import tempfile

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

def _create_tempfile(lines=None):
    path = ''
    try:
        with tempfile.NamedTemporaryFile(delete=False) as fh:
            path = fh.name
            if lines:
                fh.writelines(lines)
        return path
    except StandardError:
        _remove_file(path)
        raise

def _remove_file(path):
    try:
        os.remove(path)
    except StandardError:
        pass

def _read_file(path):
    with open(path) as fh:
        return fh.readlines()

def _write_file(path, lines):
    tmp_path = _create_tempfile(lines)
    try:
        if not filecmp.cmp(tmp_path, path):
            shutil.copyfile(tmp_path, path)
    finally:
        _remove_file(tmp_path)

def _convert_script(lines):
    lines = iter(lines)
    for line in lines:
        yield '#!"%s"\n' % os.path.join(_sphinx_home, 'python', 'python.exe')
        break
    sphinx_home = _sphinx_home.replace('\\', '\\\\\\\\')
    re_library = re.compile(r"^(\s*').+?(?=\\\\(eggs|python)\\\\)", re.I)
    for line in lines:
        yield re_library.sub(r'\1' + sphinx_home, line)

def _setup_bin():
    script_glob = os.path.join(_sphinx_home, 'bin', 'sphinx-*-script.py')
    script_paths = glob.glob(script_glob)
    if not script_paths:
        raise RuntimeError('not found: %s' % script_glob)
    for script_path in script_paths:
        lines = _read_file(script_path)
        _write_file(script_path, _convert_script(lines))

def main():
    status = 1
    args = sys.argv[1:]
    if args and args[0] == '-x':
        _setup_bin()
        print 'OK'
        status = 0
    else:
        print __doc__,
    return status

if __name__ == '__main__':
    sys.exit(main())

スタンドアロン版 Sphinx のインストール先に上記を突っ込んで、毎回 sphinx.bat から起動するようにすれば、擬似 PortableSphinx の出来上がり。後はディレクトリを zip で固めればそのままプロジェクトメンバーに配布できる。今のところ、私が使う範囲で不具合は出ていない。(不具合がないとは言っていない)

さて、実際に Sphinx を使ってみると、確かにこれはなかなか良い。懸念していた日本語ファイル名(CP932)も問題ないし、印刷時の改頁も(組み込み機能にはないものの)制御可能。簡単なブロック図であれば Sphinx 上で生成できる。正に、至れり尽くせり。うん、きっとこれはハヤル。(実際にはずっと前から流行っている)

Sphinx の「ソースを編集 → make → 動作確認」という流れは、昨今のプログラマーには受け入れ難いだろうが、古き良き時代のプログラム開発みたいで老年プログラマーには逆に心地良い。私も最初はウキウキしながら make していたが、デザイン調整などで CSS を書き換える度に make するのは流石に面倒になってきた。人間って本当に我がままだと思う。

ということで、(誰もが考え付くであろう)変更を自動で検知して make してみる。

$SPHINX_HOME/bin/makebot.bat:

@echo off
setlocal

:loop
echo [%~n0] watching... press Ctrl-C to abort
python.exe "%~dp0notify-changed.py" .
if errorlevel 1 exit /b 1
echo [%~n0] detected
call make clean && call make html
echo.
goto loop

$SPHINX_HOME/bin/notify-changed.py:

#!python.exe

__doc__ = """\
Usage:
  python %s DIRECTORY
Description:
  Watch and wait for changes in a DIRECTORY and ends with status 0.
""" % __file__

import fnmatch
import os
import sys
import time

_include_patterns = ('*.conf', '*.css', '*.css_t', '*.html', '*.py', '*.rst')
_exclude_patterns = ('_*',)

def _fnmatch_any(name, patterns):
    for pattern in patterns:
        if fnmatch.fnmatch(name, pattern):
            return True
    return False

def _filter_name(names, includes=None, excludes=None):
    if includes:
        names = [x for x in names if _fnmatch_any(x, includes)]
    if excludes:
        names = [x for x in names if not _fnmatch_any(x, excludes)]
    return names

def _filter_dir(names):
    return _filter_name(names, excludes=_exclude_patterns)

def _filter_file(names):
    return _filter_name(names,
                        includes=_include_patterns,
                        excludes=_exclude_patterns)

def _find_files(path):
    for dirpath, dirnames, filenames in os.walk(path):
        dirnames[:] = _filter_dir(dirnames)
        for filename in _filter_file(filenames):
            yield os.path.join(dirpath, filename)

def _iter_filestats(path):
    for filepath in _find_files(path):
        yield filepath, int(os.stat(filepath).st_mtime)

def _diff_filestats(before, after):
    for filepath, mtime in after.iteritems():
        if mtime != before.get(filepath, 0):
            return True
    return False

def _watch_directory(path, interval=1):
    if not os.path.isdir(path):
        return "ERROR: directory not found: `%s'" % path
    before = dict(_iter_filestats(path))
    while True:
        time.sleep(interval)
        after = dict(_iter_filestats(path))
        if _diff_filestats(before, after):
            break
        before = after
    return 0

def main():
    status = 1
    args = sys.argv[1:]
    if args:
        try:
            status = _watch_directory(args[0])
        except KeyboardInterrupt:
            pass
    else:
        print __doc__,
    return status

if __name__ == '__main__':
    sys.exit(main())

pywin32 辺りを使えばファイル変更を検知する API が使えたりするのかも知れないが、生憎 Sphinx には pywin32 が添付されていないので、力技で実装。数千くらいのファイルなら問題ないはず(数万とかは知らない)。後は Ctrl-C で終了すると「バッチ ジョブを終了しますか (Y/N)?」とか聞かれるのがウザいが、これは Windows BAT のクソ仕様の問題だし、基本 makebot しっ放しなので我慢する。

取りあえず、これで私の Sphinx on Windows 環境は幸せになった。

2014-02-24

Java: Import a certificate with `keytool'

ここ最近、Java のセキュリティ名目での やんちゃ ぶりには、振り回されている人も多いのではないだろうか。私も、Java 7 Update 40 以降でアプレット署名に自己証明書が使えなくなり、その対応に煩わされた。

クライアント PC に証明書をインポートする作業は、keytool で自動化できる。自己証明書の時はそれで問題なかったのだが、正規の証明書に変えた途端、上手くいかなくなった。具体的には、証明書をインポートしてもアプレット実行時に出てくるダイアログを抑止できない。

証明書の作り方に問題があるのかも知れないが、生憎とそこは別の人の仕事。まずは渡された証明書で何とかする方法を探してみる。(恐らくは証明書の Common Name が怪しいと睨んでいるが、未検証)

私を混乱させたのは下記の挙動。

  1. Java コントロールパネルから証明書をインポートしても、アプレット実行時にダイアログが出てきてしまう。
  2. ダイアログで「次回から表示しない」を選択して実行すると、ダイアログは二度と出てこない。
  3. (1)と(2)についてそれぞれ証明書をエクスポートすると、両者は完全に一致する。

つまりエクスポートした証明書は全く同じなのに、ダイアログが出る or 出ない、という違いが出る。インポート方法を色々と試してみたが、結局(2)以外でダイアログを抑止することはできなかった。最後に、ダメ元で気になっていたことを試してみた。実は(1)と(2)では、1 つだけ違いがある。それがキーストア中の「別名」(alias)表示だ。これも keytool で見ることができる。

Java コントロールパネルから証明書をインポートした場合:

別名: deploymentusercertnullnullnulljava.util.random@1a36121

ダイアログで「次回から表示しない」を選択した場合:

別名: deploymentusercert$tsflag$loc=http//example.com:80##docbase:http//example.com:80java.util.random@10acb9b

後者に、怪しげな呪文が見て取れる。ダメ元で後者の「別名」でインポートしてみると・・・、

やったよ、ビンゴ! :-D

もうね、何でこんな仕様なのかと。確かに前述のダイアログには「上記の発行者と場所」とあったが、まさか「場所」をこんな方法で覚えるとか難度が高過ぎだろう。

色々と試した結果、「別名」は次の形式が必要なようだ。(Java 7 Update 51 にて確認)

  • $tsflag$loc=http//example.com:80##docbase:http//example.com:80
  • 「http//example.com:80」は適宜変更。ポートは省略不可。SSL なら「https//example.com:443」とする。
  • パースに問題なさそうな文字列であれば、先頭・末尾に追加可能。途中に入れることも可能だが、やらない方が無難だと思う。

以上を踏まえて、証明書インポート BAT の例。

importcert.bat:

@echo off
setlocal

set SITE_URL=http//example.com:80
set CERT_FILE=%~dp0mycert.cer
set KS_EMPTY_FILE=%~dp0trusted.certs.empty
set KS_ALIAS=deploymentusercert$tsflag$loc=%SITE_URL%##docbase:%SITE_URL%

set JAVA_VERSION=7
set JAVA_HOME=%ProgramFiles(x86)%\Java\jre%JAVA_VERSION%
if not exist "%JAVA_HOME%" (
  set JAVA_HOME=%ProgramFiles%\Java\jre%JAVA_VERSION%
)
set keytool=%JAVA_HOME%\bin\keytool.exe

set LocalLow=%USERPROFILE%\AppData\LocalLow
if not exist "%LocalLow%" (
  set LocalLow=%AppData%
)
set ks_dir=%LocalLow%\Sun\Java\Deployment\security
set ks_file=%ks_dir%\trusted.certs

set result=1

if not exist "%keytool%" (
  echo ERROR: not found: "%keytool%"
  goto exit
)
if not exist "%CERT_FILE%" (
  echo ERROR: not found: "%CERT_FILE%"
  goto exit
)
if not exist "%ks_file%" (
  mkdir "%ks_dir%" 2>NUL
  copy /v "%KS_EMPTY_FILE%" "%ks_file%"
  if not exist "%ks_file%" (
    echo ERROR: not found: "%ks_file%"
    goto exit
  )
)

"%keytool%" -list ^
            -alias "%KS_ALIAS%" ^
            -keystore "%ks_file%" ^
            -storepass "" >NUL
if not errorlevel 1 (
  echo already imported.
  goto success
)

"%keytool%" -importcert -v ^
            -alias "%KS_ALIAS%" ^
            -file "%CERT_FILE%" ^
            -keystore "%ks_file%" ^
            -storepass "" ^
            -noprompt
set result=%ERRORLEVEL%
if %result% neq 0 (
  echo ERROR: keytool.exe: code=%result%
  goto exit
)

:success
set result=0

:exit
if %result% equ 0 (
  echo OK
) else (
  echo Failed
)
pause
exit /b %result%

実行には上記 BAT に加え、下記ファイルが必要。

  • mycert.cer (インポートする証明書)
  • trusted.certs.empty (空のキーストアファイル)

詳細は BAT を解読して貰うとして、ファイル「trusted.certs.empty」については説明が必要だと思う。

keytool は、キーストアファイルが存在しなければ新たにファイルを作成する。しかしこの時、何故かパスワードを強要してくる(-storepass "" は効かない)ため、パスワードなしキーストアを作ることができない。一方 Java コントロールパネルから証明書をインポートすると、パスワードなしキーストアが作成される。つまり、Java コントロールパネルはパスワードなしキーストアを使うくせに、keytool からはそれを作ることができないのだ。もうほんと、この仕様を作った奴はタヒねと言いたい。

trusted.certs.empty は、予め作成した空のパスワードなしキーストアだ。Java コントロールパネルで適当な証明書をインポート → 削除すれば作成できる(ファイル場所は %ks_file% を参照)。私が確認した限り、Windows XP と Windows 8 とでファイルは完全に一致したので、今後の互換性も問題ないだろう。

下記は、インポート済み証明書を一覧表示する BAT の例。「別名」もこれで確認できる。

listcerts.bat:

@echo off
setlocal

set JAVA_VERSION=7
set JAVA_HOME=%ProgramFiles(x86)%\Java\jre%JAVA_VERSION%
if not exist "%JAVA_HOME%" (
  set JAVA_HOME=%ProgramFiles%\Java\jre%JAVA_VERSION%
)
set keytool=%JAVA_HOME%\bin\keytool.exe

set LocalLow=%USERPROFILE%\AppData\LocalLow
if not exist "%LocalLow%" (
  set LocalLow=%AppData%
)
set ks_dir=%LocalLow%\Sun\Java\Deployment\security
set ks_file=%ks_dir%\trusted.certs

set result=1

if not exist "%keytool%" (
  echo ERROR: not found: "%keytool%"
  goto exit
)
if not exist "%ks_file%" (
  echo ERROR: not found: "%ks_file%"
  goto exit
)

"%keytool%" -list -v ^
            -keystore "%ks_file%" ^
            -storepass ""
set result=%ERRORLEVEL%
if %result% neq 0 (
  echo ERROR: keytool.exe: code=%result%
)

:exit
pause
exit /b %result%

今回はこれで解決だが、問題は、今後の互換性は保証されない ということ。

事実、Java 7 Update 45 → 51 で「別名」の形式が変わった。具体的には、u51 で「##docbase:...」が増えた。このせいで、u45 で証明書をインポートしていても、u51 にアップデートすると再びダイアログが出てきてしまう。ここまで来ると、分かってて嫌がらせをしているとしか思えない。(u51 アップデート時に出てくるダイアログで「セキュリティ・プロンプトの復元」に初期でチェックが入っているのも、これを隠すための陰謀だと思っている)

そろそろ、世界中の IT エンジニアは Java の横暴に NO! って言っても良い頃だと思う。

参考:


2014-04-28 追記

Java 7 Update 55 にて、更にマジックワード「##from」が増えた模様。

  • $tsflag$loc=http//example.com:80##docbase:http//example.com:80##from:http//example.com:80

2014-02-01

Animes in the 4th quarter of 2013

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

前期からの視聴。光る素材は有ったのに、前期同様それを活かせずに終わってしまった。話が完全にキャラ負けした感じ。キャラデザは良かっただけに、話さえ良ければ化けたと思う。

前期からの視聴だが、相変わらずキャラは秀逸。ただ話は区切りの悪い終わり方と、(原作は知らないが)重要な章が端折られているように見えた。尤も前期の教訓で話の方は期待してはいなかったが、何故 12 話で作ったのか疑問。ただ、OP/ED は良い。

絵は綺麗だが、その他は平凡。エロゲ原作はこのパターンが多い気がする。まあエロゲだし、エロを取ったら(略)。結局、ジョルトのルールは最後まで良く分からなかった。

シリーズ通しての視聴。今回はごった煮らしいが、各話の繋がり(時系列)が良く分からなかった。個人的には臥煙伊豆湖がもっと出てきても良かった。あの手の頼れる存在は安心感を生む。金髪幼女は忍野メメの代わりにはならなかったと思う。あと、そろそろ超速字幕は何とかして欲しい。いちいち巻き戻して読むのが面倒。

リタイアせずに最後までは見られたが、何がやりたかったのか良く分からなかった。メカ? 家族愛? 時を越えた愛? いずれにせよ、特に感動もなく終わった。

  • 俺の脳内選択肢が、学園ラブコメを全力で邪魔している http://noucome.jp/

タイトルから食傷気味で、絵も全く好みでなく、脳内選択肢のバカ設定を知り、もう嫌な予感しかしなかったが意外に期待を裏切ってくれた。設定だけでなく主人公の周りも全てバカなのが良かった。これなら安心して続編を見られる(続くかは知らない)。

2013-12-30

Struts2: ScopeInterceptor: Deadlock in session lock

もう数年前の話になる。Web アプリケーションの負荷テストをすると稀にエラーになることがあった。リクエストから 30 秒後にエラーになり、ログには「Deadlock in session lock」と出る。この文字列から ScopeInterceptor に辿り着き、コードを読んで不具合と判断、特に使っていなかった ScopeInterceptor を外すことで対処した。

当時は同様の事例が見つからなかったが、最近になって検索すると次が出てきた。

コードを読んだだけで動作は未確認だが、最新版でも直っていないようだ。

確か ScopeInterceptor は初期設定で組み込まれているはずで、負荷テストをやれば普通に当たりそうに思うのだが、何故これまで問題になっていないのか謎。

2013-11-24

Animes in the 3rd quarter of 2013

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

キャラデザや OP からして某作の二番煎じっぽいが、初話でいい意味で裏切られた。ロボット物と三枚目キャラの組み合わせは新鮮。しかし結局、その後の話が初話を超えることは無かった。キャラの魅力は十分で、特に女子キャラ 2 名は良い味を出しているので、初話の感じで通していたら大化けしたと思う。

女子向けということで期待はしておらず、そしてほぼ予想通り。イケメンキャラが程よく絡むという女子向けの定番で、男子が見てもつまらないない作品。少女マンガ原作は当たりが多いが、この系統の女子向けは基本スルーで良いような気がしてきた。

前期に引き続き視聴。浜地だけが目当てだったのに、数える程しか出て来なかった。しかもチョイ役で。まあ女子向けだから需要が無のは分かってはいたが、浜地が出ないなら私にとって見る価値が無かった。

ヒロインが十数人の義理兄弟から惚れられる、という男女逆なら昔見たパターン。ヒロインが兄弟を拒まない所に違和感を感じまくるが、よく考えたらこれが男女逆だったらそれが普通だし、異性から見るとこんなにもバカバカしく見えるということが良く分かった作品だった。

原作は大よそ既読。アニメで見返しても、女神篇からの話は違和感がある。特定のヒロインの魅力は増したが、それ以外が総潰れに。流石に新キャラを増やすのが面倒になって整理をしたのかと勘ぐってしまう。OP は好きなのだが、それ故に是非ネイティブに歌って欲しかった。ただ早見沙織の頑張りは認める。

一見して「まどマギ」にインスパイアされた作品なんだろうな、と。タロットとか対消滅とかネタ的には良いと思うが、そこから先が膨らませきれずに終わった印象。物理をネタにしている割に、実は消えてませんでしたとか、まとめ方も理不尽。作り方次第で化けた可能性を感じたので、勿体ない気がする

それなりに期待していたが、このシリーズの特徴「主人公にイイ格好させる程につまらなくなる」の典型になった。格好つけは本編に任せて、黒子たちにバカをやらせてるだけで良かったのに、どうしてこうなった。ブレンダなどは残念キャラの良い資質を持っていたのに。無論、フェブリたんが可愛いかったのは認める。

問答無用の話題作。結果、時系列で評価が↑↓↑と変化。巨人が自然法則を無視しているのが分かった時点で一旦評価が下がったが、それを受け入れれば話として非常に面白い。やはり「先が読めない」というのは重要な要素だと再認識。原作を読まなずにいて良かった。だがそれ故、じっくり一年かけてやって欲しかったとも思う。

京アニなので視聴。絵は京アニらしさが感じられ、標準以上の出来になっているとは思う。のだが、それ以上の感想が出て来ない。もっと何か、京アニにしか出来ない何かを見てみたかった。まあ私の期待が大き過ぎるのは認める所なので、もっと長い目で見守ろうとは思う。

本が主題な割に、深い話は特に出ずに薄っぺらく終わった印象。もっとビブリオマニア的なディープな話を期待していたのだが、本関連の肩書きを持つだけの単なる変態たちだった。ただ何故か秋月マキシを気に入ったので、良しとする。何かシャイニングな感じと声色の組み合わせが何処かのツボに入った模様。

最大の問題は、声優がひどい。まるで素人が当てているようにしか聞こえない。だがそれさえ乗り越えれば、結構面白い。ネタが絶妙というか、誰もが学生時に経験したであろうネタが多く、ツッコミのノリも含めて慣れると癖になる。ただ、殆んどの人はそこまで到達できなかったと思われるのが残念。

今期の伏兵。正直、完全にナメてた。最早これは「ガッチャマン」という名の完全新作と思った方が良い。 世界観に感心したのは久しぶり。ヒロインの性格さえ受け入れられれば、問題なく楽しめると思う。うつつたんも可愛かったし、OP・ED も良い。最近の内田真礼はなかなか良い仕事をする。

初見で全く期待していなかったが、良い意味で裏切ってくれた。まさか恋愛ではなくコメディ作品だったとは。ボケ役とツッコミ役が上手くハマっていて、各キャラの声も違和感が無い。非常に良く纏まっている作品だと思う。今期はコメディ作品の当たりが多くて私得なクールだった。

シリーズの勉強と思っての視聴だったが、これ見てもたぶん本編のことは勉強できないよね? しかしこれはこれで需要が有りそうな感じはするので次回も見るとは思うが、正直私には面白さが分からない。これは本編をやり込んだ人向けのファンディスク的な作品のような気がする。つまり私は予選落ちしてるっぽい。

絵的に好みでないため期待はしていなかった。各キャラが弱めなので、もう少しギャグ方向に振るか萌え要素が欲しい。ヒロインは最後まで薄いし、唯一キャラの強い実質ヒロインに萌えるのは無理だったし。寧ろ小動物の演技の方が記憶に残った。やっぱり中の人(斉藤千和)の力は侮れない。

前期からの視聴。相変わらず暖かい作品だが、やや無理やりいい話にしている感もなくはない。新キャラはキャラが弱すぎて従来四人に全く馴染めてないが、のりえが強化されていて全体としてはプラマイゼロ。ていうか最早、私はのりえを見るためにこの作品を見ていると言っても過言ではない。

2013-10-31

StandardError hides KeyboardInterrupt in Python 2.4

私は、Python で次のようなコードを書くことがある。

import datetime

def validate_date_string(s):
    try:
        assert len(s) == 8
        datetime.date(int(s[:4]), int(s[4:6]), int(s[6:]))
        return True
    except StandardError:
        return False

def test():
    assert validate_date_string('20120229') # leap year
    assert not validate_date_string('20130229') # not leap year

StandardError を捕捉するのは、Google 先生の教えによる。

  • Never use catch-all except: statements, or catch Exception or StandardError, unless you are re-raising the exception or in the outermost block in your thread (and printing an error message). Python is very tolerant in this regard and except: will really catch everything including misspelled names, sys.exit() calls, Ctrl+C interrupts, unittest failures and all kinds of other exceptions that you simply don't want to catch.

しかしここには、Python のバージョンによって例外のクラス階層が異なる、という重要な情報が抜けている。

実際、Python 2.4 以前は KeyboardInterruput が StarndardError のサブクラスになっているため、上記のコードは try ブロック中で Ctrl-C を受けると意図通りに動かない。具体的には、Ctrl-C で終了できず、無条件に False が返ってしまう。

Exception
 +-- SystemExit
 +-- StopIteration
 +-- StandardError
 |    +-- KeyboardInterrupt
<...snip...>
BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- Exception
      +-- GeneratorExit
      +-- StopIteration
      +-- StandardError
<...snip...>

Python 2.4 以前でも動くようにするには、StandardError を処理する に、KeyboardInterruput を明示的に処理する必要がある。

def validate_date_string(s):
    try:
        assert len(s) == 8
        datetime.date(int(s[:4]), int(s[4:6]), int(s[6:]))
        return True
    except KeyboardInterrupt:
        raise
    except StandardError:
        return False

Python は保守的な言語だと思っていたので、まさかこんな所に罠があるとは思わなかった。これでまた 1 つ、Python が嫌いになった。:-p