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

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

コミット時にpdb消し忘れてないかチェックするhg hook

チェック用のスクリプト

こんな感じのスクリプト作成

#!/usr/bin/env python

import os
import sys


COMMAND = r"find . -name '*.py' | xargs grep -lr '{pattern}'"
INFO = '{filename}: pdb found.'


def check(pattern):
    return os.popen(COMMAND.format(pattern=pattern)).readlines()


def display_warning(lines):
    for line in lines:
        print >> sys.stderr, INFO.format(filename=line.strip('\n'))


def check_pdb(*args, **kwargs):
    lines = check('pdb.set_trace')
    if lines:
        display_warning(lines)
        sys.exit(1)

~/.hgext/hookpdb/check.py みたいな感じで配置。

.hgrcに追記

[hook]
pretxncommit.pdb = python:~/.hgext/hookpdb/check.py:check_pdb

試してみる

pdb仕込みっぱなしのファイルがある状態でコミットしようとすると…

$ hg ci
./hoge.py: pdb found.
transaction abort!
rollback completed
note: commit message saved in .hg/last-message.txt

うん、できてる。けど、ファイル多いともっさりがキニナルような…

追記

ありがとうございますありがとうございます!
描き直してみました。

#!/usr/bin/env python

import os
import sys
from fnmatch import fnmatch


COMMAND = r"grep -l {pattern} {file}"
INFO = '{filename}: debugger found.'
PATTERNS = ['pdb.set_trace', 'import debug']


def check(repo, file, patterns):
    patterns = ['-e "{ptn}"'.format(ptn=pattern) for pattern in patterns]
    pattern = ' '.join(patterns)
    file = os.path.join(repo.root, file)
    return os.popen(COMMAND.format(pattern=pattern, file=file)).read()


def display_warning(lines):
    for line in lines:
        print >> sys.stderr, INFO.format(filename=line.strip('\n'))


def get_pyfiles(repo, node):
    return [f for f in repo[node].files() if fnmatch(f, '*.py')]


def check_pdb(ui, repo, hooktype, node=None, source=None, **kwargs):
    lines = [f for f in get_pyfiles(repo, node) if check(repo, f, PATTERNS)]
    if lines:
        display_warning(lines)
        sys.exit(1)

さらに修正

リポジトリルートで実行しないとgrep時にnot foundになってしまっていたので絶対パスgrepするように修正しました。

さらにさらに修正

ゆとりな僕はpdbよりもdebugモジュールを使いがちなのでパターンを複数受け取れるように修正しました。もうGistでやれよと思います。

インスタンスに動的にメソッドを追加する

追記:
記事を公開してからおんなじような記事あったりするかなと思ってググったら2年前にIanさんがほとんど同じ内容書いてた。
Pythonでメソッドをクラスまたはインスタンスに動的に追加する - Ian Lewis
しかも僕その記事はてブしてたよ…

Interactive Shell で色々試してる時なんかにインスタンスにメソッドを追加したくなる時がある。

>>> class Person(object):
...     def __init__(self, name):
...         self._name = name
...
>>> alice = Person("Alice")
>>> bob = Person("Bob")

こんなクラスがあって、幾つかインスタンスを作っているとする。で、self._nameを取得するメソッドが欲しくなったとしよう。

>>> def get_name(self):
...     return self._name

これを単純にインスタンスのメンバーとして代入してもうまくいかない

>>> alice.get_name = get_name
>>> alice.get_name()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: get_name() takes exactly 1 argument (0 given)

これはalice.get_nameがただの関数だから。インスタンスメソッドではない。

>>> type(alice.get_name)
<type 'function'>

コレジャナイので一旦消します。

>>> del alice.get_name

さて、じゃあどうすればいいかというと

>>> Person.get_name = get_name

というようにクラスの方に追加してやる。ただ、これだと当然ながら全てのインスタンスに影響が出る

>>> alice.get_name()
'Alice'
>>> bob.get_name()
'Bob'

影響が出るというか、実はaliceもbobもget_nameなんて持ってない。持ってないんだけど、クラスの方を見に行ったらそっちにあったからそれを呼び出してるだけ。

>>> del alice.get_name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: get_name
>>> alice.__dict__
{'_name': 'Alice'}

というわけでまあこれでいいんだけど、もしある特定のインスタンスにだけ設定したいなんて場合にはどうすればいいか…というのが今回の本題。結論としては、types.MethodTypeを使う。

>>> del Person.get_name
>>> import types
>>> alice.get_name = types.MethodType(get_name, alice)
>>> alice.get_name()
'Alice'
>>> bob.get_name()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'get_name'

第一引数にメソッドとして登録したい関数、第二引数にインスタンスを渡してやると、それらを紐付けてくれる。これでaliceはget_nameメソッドを持ってるけど、bobは持っていないという状態にすることができる。用途が思いつかないけど気になったから調べてみただけ。