DjangoでImageFieldを持つフォームのテスト

DjangoでImageFieldを持ったFormがあるとします。こんな感じ。ModelFormでもいい(というか実際のコードはそっちで書いてる)。

class UploadForm(forms.Form):
    title = forms.CharField()
    photo = forms.ImageField()

これをテストする際にStringIOで適当に作ったダミーを食わせたら「画像じゃないよ!」って怒られた。ダミー画像をテスト用に置いておくのもなんだかなぁ…と思ってたら、そうだ、PIL使ってるんだからPILで生成すればいいじゃないか。

#-*- coding:utf-8 -*-
import io

from PIL import Image
from django.test import TestCase
from django.core.files.uploadedfile import SimpleUploadedFile


class UploadFormTest(TestCase):
    def _get_form_class(self):
        from .forms import UploadForm
        return UploadForm

    def _make_dummy_image(self):
        file_obj = io.BytesIO()
        im = Image.new('RGBA', size=(10, 10), color=(256, 0, 0))
        im.save(file_obj, 'png')
        file_obj.name = 'test.png'
        file_obj.seek(0)
        return file_obj

    def test_it(self):
        img = self._make_dummy_image()
        Form = self._get_form_class()
        form = Form(
            data={'title': 'test title'},
            files={'photo': SimpleUploadedFile(
                img.name,
                img.read(),
                content_type='image/png',
            )},
        )
        self.assertTrue(form.is_valid())

ちなみに環境は Python 3.3 + Django 1.7.0.a です。ソースを追ってみると、form.is_valid() を呼ぶと内部で self.ImageField.to_python() が呼ばれ、さらにそのメソッド内で from django.utils.image import Image; Image.open() しているんですね。

というわけで一年以上ぶりの記事でした。

cajonでjavascriptを分割管理

JavaScript module loader for the browser that can load CommonJS/node and AMD modules

requirejs/cajon · GitHub

cajon(カホンって読むっぽい。ペルー発祥の打楽器の名前らしい)なんてものを見つけた。require.js上に構築されてるとかなんとか。require.jsもよく知らないんだけど、これはこれで簡単だしいいかも。

構成

.
├── index.html
└── js
            ├── cajon.js
            ├── controller.js
            ├── jquery.js
            ├── lib.js
            ├── model.js
            ├── template.js
            └── view.js

index.html

<!DOCTYPE HTML>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>cajon test</title>
  <script src="js/cajon.js"></script>
  <script>
    require.config({
      baseUrl: 'js'
    });
    require(['jquery', 'lib'], function($, lib) {
        $.each(lib, function() {
          $('<li>').text(this.say()).appendTo('#hoge');
        });
    });
  </script>
</head>
<body>
  <ul id="hoge">
  </ul>
</body>
</html>

lib.js

module.exports = {
  template: require('./template'),
  model: require('./model'),
  controller: require('./controller'),
  view: require('./view')
};

model.js

module.exports = {
  say: function() {
    return "I'm model.js"
  }
};

view.js

module.exports = {
  say: function() {
    return "I'm view.js"
  }
};

controller.js

module.exports = {
  say: function() {
    return "I'm controller.js"
  }
};

template.js

module.exports = {
  say: function() {
    return "I'm template.js"
  }
};

ブラウザでhtmlを開くと下のように表示される。

f:id:kk6:20120922000441p:plain

ちゃんとlib.js経由でmodel.js/view.js/controller.js/template.jsがそれぞれ読み込まれているのがわかる。

Ubuntu serverに入れてるDropboxが変なメッセージを出すようになった

ちょっと前くらいからこんなメッセージが出るようになった

$ Unable to monitor entire Dropbox folder hierarchy. Please run "echo 100000 | sudo tee /proc/sys/fs/inotify/max_user_watches" and restart Dropbox to correct the problem.

わけもわからず言われるがままコマンド叩いてたんだけど、毎日ログインVM起動する度に表示されてていい加減鬱陶しくなったので調べた。

/proc/sys/fs/inotify/max_user_watches とは

引用の引用

/proc インターフェース

以下のインターフェースは、inotify で消費されるカーネルメモリの総量を制限するの
に使用できる:

/proc/sys/fs/inotify/max_queued_events

このファイルの値は、アプリケーションが inotify_init(2) を呼び出すときに使用さ
れ、対応する inotify インスタンスについてキューに入れられるイベントの数の上限
を設定する。この制限を超えたイベントは破棄されるが、 IN_Q_OVERFLOW イベントが
常に生成される。

/proc/sys/fs/inotify/max_user_instances

1 つの実ユーザ ID に対して生成できる inotify インスタンスの数の上限を指定す
る。

/proc/sys/fs/inotify/max_user_watches

作成可能な監視対象の数の実 UID 単位の上限を指定する。

lsyncdで上限ファイル数を超えた時の対処策 - Unix的なアレ

だそうです。要するに監視対象のファイル数が上限をオーバーしたってことか。

/etc/sysctl.conf

/proc/sys/fs/inotify/max_user_wathces は直接書き換えても再起動の度にリセットされるらしいので、/etc/sysctl.conf を編集する

こいつはカーネルパラメータを設定するファイルだそうです。そんな事言われたらインフラ知識からっきしな僕は怖くて手が震えてしまうんですががが。
[ThinkIT] 第5回:カーネルをチューニングする (1/4)

管理者権限で開く。

$ sudo vi /etc/sysctl.conf

以下の行を追加する

fs.inotify.max_user_watches = 100000

追加後にこの設定を反映させるために以下のコマンドを叩く

$ sudo sysctl -p

これでたぶん直ったはず。