pdbのデバッガコマンド

とりあえず自分用に。あとでちゃんとまとめ直す...かも。

コマンド

h(elp) [command]
コマンドのヘルプを表示。引数なしで利用可能コマンド一覧を表示|
s(tep)
一行実行(ステップイン)
n(ext)
一行実行(ステップオーバー)|
unt(il)
(Python2.6から)行番号が現在行より大きくなるか、現在のフレームから戻るまで実行|
r(eturn)
returnされるまで実行(ステップアウト)
c(ontinue)
次のブレークポイントまで実行
j(ump) lineno
次に実行する行を指定。最も底のフレームでのみ実行可能。forやfinallyなどの中には飛び込めない
l(list) [first[, last]]
ソースコードを表示。引数なし 前後11行。引数一つ その行から11行。引数二つ 与えられた範囲行。
a(rgs)
現在の関数の引数リストをプリント
p expression
現在のコンテキストでexpressionを評価しプリント
pp expression
pprintで例外の値が整形される以外はpと同じ
w(where)
スタックトレースの表示
u(up)
フレームの移動
d(down)
フレームの移動
b(break [[filename
] lineno | function [, condition]]:ブレークポイントを設置

break (ファイル名:) 行番号(, 条件式) または 関数名(, 条件式)
filename: hoge.py:50 のようにするとhoge.pyの50行目にブレークポイント設置
lineno 現在のファイルの指定した場所に設置
function その関数の中の最初の実行可能文に設置
第二引数を指定した場合、その値は式で、その評価値が真でなければブレークポイントは有効にならない(条件付きブレークポイント

tbreak

一時的なブレークポイント。引数はbreakと同じ。

disable [bpnumber [bpnumber ...]]

ブレークポイントを一時無効化

enable [bpnumber [bpnumber ...]]

ブレークポイントを有効化

cl(ear) [filename:lineno | bpnumber [bpnumber ...]]

ブレークポイントを削除

ignore bpnumber [count]

与えられたブレークポイントに通過カウントを設定
countを省略すると0に設定される
通過カウントがゼロになるとブレークポイントが機能する状態になる
(つまりゼロになるまでは無視し続け、通過するたびにカウントを1減らす)

condition bpnumber [condition]

指定したブレークポイントに条件を設定する
引数を与えなければ逆にそのブレークポイントの条件を解除する

commands [bpnumber]

(Python2.5から)
コマンドのリストを指定

alias [name [command]]

コマンドにエイリアスをつける

unalias name

指定したエイリアスを削除

[!]statement

その場でstatementを実行。
listなどといったデバッガコマンドと名前がかぶるものは感嘆符をつけることで
実行可能となる

run [args ...]

(Python2.6から)
デバッグ中のプログラムを再実行
引数が与えられるとshlexで分割され、結果が新しいsys.argvとして使われる。
ヒストリー、ブレークポイント、アクション、デバッガーオプションが引き継がれる
restartはrunのエイリアス

q(uit)

実行中のプログラムを中断してデバッガを終了

その他いろいろ
  • 何も入力しないでエンターをタイプすると、直前のコマンドを繰り返す
  • 複数コマンドを実行する場合はセミコロン2つで分割
  • .pdbrcをホームディレクトリまたはカレントディレクトリに置くことで初期設定が可能

ホームの.pdbrc > カレントの.pdbrcの順に読み込まれる

Ubuntuを12.04にupgradeしたらvirtualenv環境で'cannot import urandom'と出るようになった

Ubuntu 12.04 がリリースされたということで、僕も 11.10 から 12.04 にアップグレードしてみた。とりあえず仕事に支障がないかひと通り確認してみたところ、とある仕事用のvirtualenv環境をactivateした状態で、テストを走らせようとしたところ

ImportError cannot import urandom

と出るようになってしまった。で、twitterで同じようなお悩みを持った人はいないかなーと検索かけてみたところすぐに見つかり無事解決した。

解決方法としては、そのvirtualenv環境は諦めて、同じ内容の新しいvirtualenv環境を作ればいいだけ。ただし厄介なのが pip freeze すらこのエラーを吐くので、事前に pip freeze でテキストファイルに使用しているライブラリの一覧を出力してなかった場合は、一個一個地道にインストールするしかない、と思う。

DjangoのモデルのManagerクラスのメソッドをメソッドチェインさせる

※追記:django-model-utils というパッケージの PassThroughManager が同じようなことしてくれるみたいです
Model Managers — django-model-utils 2.3a1 documentation


例えば下記のような、書籍名・本棚登録済みかのフラグ・出版日を持ったモデルクラスがあるとする

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=255)
    registered_flg = models.BooleanField(default=False)
    published_date = models.DateTimeField()

ここで、「本棚登録済みの本のみを抽出」とか「出版済みの本のみを抽出」とかしたい場合、マネージャクラスを使う。

class BookManager(models.Manager):
    def registered(self):
        return self.filter(registered_flg=True)

    def published(self):
        return self.filter(published_date__lte=datetime.datetime.now())

class Book(models.Model):
    ...(中略)
    objects = BookManager()

さてこれで目的は果たせたわけだけど、ここでさらに「すでに出版されていて尚且つ本棚登録済みのもの」のみを抽出したい場合どうすればいいだろう。

Managerのメソッドの戻り値に対して別のManagerメソッドをチェイン出来ない

>>> Book.objects.published().registered()

これはAttributeErrorになる。publishedメソッドが返すのはQuerySet。当然registeredなんてメソッドは持っていない。もちろんQuerySetのfilterメソッドをチェインすることは出来るけど...

>>> Book.objects.published().filter(registered_flg=True)

だからと言ってBookManagerに

def registered_and_published(self):
    return self.filter(published_date__lte=datetime.datetime.now(),
                       registered_flg=True)

なんてメソッドを追加するのもちょっと…メソッドの数が増えてくると面倒な事になる。出来ればメソッドチェインさせたい。

QuerySetのカスタマイズ

QuerySetをカスタマイズすることで解決できる

class BookQuerySet(models.query.QuerySet):
    def registered(self):
        return self.filter(registered_flg=True)

    def published(self):
        return self.filter(published_date__lte=datetime.datetime.now())


class BookManager(models.Manager):
    def get_query_set(self):
        return BookQuerySet(self.model)

    def registered(self):
        return self.get_query_set().registered()

    def published(self):
        return self.get_query_set().published()


class Book(models.Model):
    name = models.CharField(max_length=255)
    registered_flg = models.BooleanField(default=False)
    published_date = models.DateTimeField()

    objects = BookManager()

コレでほぼOKなんだけど、このままだとBookQuerySetにメソッドを追加するたびにBookManagerにもメソッド追加しないといけない。それは冗長なので、BookMangerに__getattr__を持たせるとコードがすっきりする。これでBookQuerySetにいくらメソッドを増やそうが、__getattr__が上手いことやってくれる。

bpythonとかで補完が効かなくなるけど、まあそこはしょうがないか。

最終的なコードはこんな感じ
import datetime
from django.db import models


class BookQuerySet(models.query.QuerySet):
    def registered(self):
        return self.filter(registered_flg=True)

    def published(self):
        return self.filter(published_date__lte=datetime.datetime.now())


class BookManager(models.Manager):
    def get_query_set(self):
        return BookQuerySet(self.model)

    def __getattr__(self, name):
        return getattr(self.get_query_set(), name)


class Book(models.Model):
    name = models.CharField(max_length=255)
    registered_flg = models.BooleanField(default=False)
    published_date = models.DateTimeField()

    objects = BookManager()