Python2.7のunittestのassertRaisesはコンテキストマネージャーとして使用出来る

テスト対象

def add(x, y):
    return x + y

適当すぎだけどご勘弁。異なった型同士を足そうとすれば当然TypeError吐きますよね。

本題

Python2.7以前のunittestのassertRaisesは assertRaises(exception, callback, *args, **kwargs) というように呼び出す必要があった。

class SomeTest(unittest.TestCase):
    def test_add(self):
        # 正しい書き方
        self.assertRaises(TypeError, add, (2, [1, 2, 3]))
        # 間違った書き方
        self.assertRaises(TypeError, add(2, [1, 2, 3]))

このテストを実行すると間違った書き方のところでassertRaisesがTypeErrorを捕らえきれずに普通にテストが落ちます。でも、見た目的に間違った書き方のほうが感覚的にはしっくりするよなーっていうのが本音。

Python2.7以降のassertRaisesはコンテキストマネージャーとして使える

で、python2.7からはassertRaisesがコンテキストマネージャーとして使えるよう変更が加えられた。これにより、より直感的なこのような書き方が可能になった。

class SomeTest(unittest.TestCase):
    def test_add(self):
        with self.assertRaises(TypeError):
            add(2, [1, 2, 3])

実にスッキリしていてわかりやすい。まさにPythonicってやつですね。

Grubのデフォルト起動OSを変更する

あまり使わなくなったWin/Ubuntuデュアルブートマシンを同居人が使いたいということで、デフォルト起動OSをWindowsになるようにしなくてはいけなくなった。で、いちおうぐぐってみたらすぐ見つかったんだけど、同じくらいのタイミングでこんなアドバイスをいただいた。

ぐぐって見つけた記事はmenu.lstを編集すると書いてたけど、どうもそういうファイルはなくて、knzm2011さんの言うとおりに

$ sudo grub-update

して /boot/grub/grub.cfg を編集した。このファイルのはじめの方に

set default="0"

と書かれている行があるのだけど、この数値を変更すればいいみたい。OS選択画面にて Windows 7 は上から5番目だったので

set default="4"

として再起動したら、OS選択画面にて無事 windows 7 の行がデフォルトで選択されている状態になった。めでたしめでたし。

Django 1.4 で新しくプロジェクトを作成した際にしておくsettings.pyの設定

django 1.4 からはプロジェクトを作成した際のディレクトリ構成が変わったのでどのように設定するのがいいかを、まだ数の少ないネット上のサンプルを参考にしつつまとめてみた。

ちなみに以下の様な構成を想定してます。

└── project_root
    ├── app1
    │   ├── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── app2
    │   ├── __init__.py
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── manage.py
    └── package_root
        ├── __init__.py
        ├── settings.py
        ├── site_media
        │   ├── media
        │   └── static
        ├── static
        ├── templates
        ├── urls.py
        └── wsgi.py

settings.pyを編集

序盤は公式ドキュメントのチュートリアルにも出てくるような内容だけど一応。path周りが今回のメインだけど、正直この構成がベストプラクティスなのかはわからない。

データベース設定
# とりあえず冒頭で os モジュールをインポートしておく
import os

# 中略

# データベースの設定。とりあえずsqlite3。
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'example.sqlite',
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
タイムゾーン言語コードの設定を変更
TIME_ZONE = 'Asia/Tokyo'
LANGUAGE_CODE = 'ja'
各種Pathの指定
# プロジェクトのルート。
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
# パッケージのルート(settings.pyの入っているディレクトリ)
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))

# ユーザーからアップロードされた画像などを保存する場所
MEDIA_ROOT = os.path.join(PACKAGE_ROOT, 'site_media', 'media')
MEDIA_URL = '/site_media/media/'
# js/css/imgなどの静的ファイルの保存場所
STATIC_ROOT = os.path.join(PACKAGE_ROOT, 'site_media', 'static')
STATIC_URL = '/site_media/static/'
# 特定のアプリと関係のないstaticファイルの格納場所
STATICFILES_DIRS = (
    os.path.join(PACKAGE_ROOT, 'static'),
)
# テンプレートファイルの格納場所
TEMPLATE_DIRS = (
    os.path.join(PACKAGE_ROOT, 'templates'),
)

os.path関連の解説

os.path周りのちょっとした解説をしておきます。ほんとちょっとだけ。__file__ == /home/kk6/hoge.py だとします。

絶対パスの表示
os.path.abspath(__file__)  # /home/kk6/hoge.py
ファイルのディレクトリの表示
os.path.dirname(__file__)  # /home/kk6
1階層上のディレクトリを表す
os.pardir  # '..'
カレントディレクトリ
os.curdir  # '.'
ディレクトリ間のセパレータ
os.sep  # '/'
ファイル名と拡張子のセパレータ
os.extsep  # '.'

以上を踏まえた上で

# これだとなんかモヤモヤするので
os.path.join(os.path.dirname(__file__), os.pardir)  # /home/kk6/..

# 絶対パスにする
os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)  # /home

結局...

os モジュールをしっかり理解すればそれで済むことに気づいた。