Carp::Always, Devel::KYTProf, Devel::REPL, Carp::REPL

Carp::Always
– エラーやスタックトレースの可視化
– useするだけ
warnまたはdieが出た時に、スタックトレースを引数付きで表示

Devel::KYTProf
– SQLの可視化
– useするだけ

Devel::REPL
– プロンプトで構文や式の動作を確認
– re.plコマンド

Carp::REPL
– プログラムの途中でプロンプトを起動
– dieしたところで使うなら、 perl -MCarp::REPL script.plまたはuseするだけ
– rep関数を使って任意の場所で起動

うーん、これは困った。。ログ出力か。。

Perl debug

#!/usr/bin/perl --

use strict;
use warnings;

my $message = 'Hello';
my @nums = (1, 2, 3);
my %scores = (math => 80, english => 77);

my $twice = twice(5);

$DB::single = 1;

for my $num (@nums){
	if ($num == 2) { $DB::single = 1}
	print "$num\n";
}

sub twice {
	my $num = shift;

	return $num * 2;
}

[vagrant@localhost cgi-bin]$ perl -d app.cgi

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h’ for help, or `man perldebug’ for more help.

main::(app.cgi:6): my $message = ‘Hello’;
DB<1> q

-dオプションでデバッガの起動ということだが、うむ、イマイチよくわからん。
q でデバック終了。
n でシングルステップ

違う例で見てみる。

#!/usr/bin/perl —

use strict;
use warnings;

my $num = 3;
for(my $i=1;$i<=3;$i++){ print $num*$i."\n"; } [/perl]

[vagrant@localhost cgi-bin]$ perl app.cgi
3
6
9
[vagrant@localhost cgi-bin]$ perl -d app.cgi

Loading DB routines from perl5db.pl version 1.32
Editor support available.

Enter h or `h h’ for help, or `man perldebug’ for more help.

main::(app.cgi:6):      my $num = 3;
  DB<1> n
main::(app.cgi:9):      }
  DB<1> n
main::(app.cgi:7):      for(my $i=1;$i<=3;$i++){
  DB<1> n
main::(app.cgi:8):              print $num*$i.”\n”;
  DB<1> n
3
main::(app.cgi:8):              print $num*$i.”\n”;
  DB<1> n
6
main::(app.cgi:8):              print $num*$i.”\n”;
  DB<1> n
9
Debugged program terminated.  Use q to quit or R to restart,
  use o inhibit_exit to avoid stopping after program termination,
  h q, h R or h o to get additional info.
  DB<1> q

なるほど、1行ずつ見ていくのね。

Dynamic slice

import sys

def remove_html_markup(s):
	tag = False
	quote = False
	out = ""

	for c in s:
		if c == '<' and not quote:
			tag = True
		elif c == '>' and not quote:
			tag = False
		elif (c == '"' or c == "'") and tag:
			quote = not quote
		elif not tag:
			out = out + c

	return out

def traceit(frame, event, arg):
	if event == "line":
		filename = frame.f_code.co_filename
		lineno = frame.f_lineno
		print filename, lineno

	return traceit

sys.settrace(traceit)
void bug(){
	int x;
	assert x == 0;
}
resume = True
while resume:
	command = input_command()
	process(command)

command_index = 0

def input_command():

	commands = ["open", "save", "quit"]
	global command_index
	command = commands[command_index]
	command_index = command_index + 1
	return command

saved_commands = []
def input_command():
	command = original_input_command()
	return command

def process(command):
	global resume
	print repr(command)

	if command.startswitch('g'):
		resume = False

while resume:
	command = input_command()
	process(command)

zip

class ZIPCode:
	def __init__(self, zip):
		self._zip = zip
		self.checkRep()

	def zip(self):
		return self._zip()

	def checkRep(self):
		assert len(self.zip()) == 5
		for i in range(0, 5):
			assert '0' <= self.zip()[i] <= '9'
import re

def test(s):

	if re.search("]*>", s) >= 0:
		return "FAIL"
	else
		return "PASS"

def ddmin(s):
	assert test(s) == "FAIL"

	n = 2
	while len(s) >= 2:
		start = 0
		subset_length = len(s) / n
		some_complement_is_failing = False

		while start < len(s):
			compleent = s[:start] + [start + subset_length:]
			if test(complement) == "FAIL":
				s = complement
				n = max(n - 1, 2)
				some_complement_is_failing = True
				break

			start = start + subset_length

		if not some_complement_is_failing:
			n = min( n * 2, len(s))
			if n == len(s):
				break

	return s
import random

def fuzzer():
	string_length = int(random.random() * 1024)
	out = ""
	for i in range(0, string_length):
		c = chr(int(random.random() * 96 + 32))
		out = out + c

	return out
print fuzzer()
patches = [1, 2, 3, 4, 5, 6, 7, 8]

def test(s):
	print repr(s), len(s),
	if 3 in s and 6 in s:
		print "FAIL"
		return "FAIL"
	else:
		print "PASS"
		return "PASS"

print ddmin(patches, test)

Debugging

def debug(command, my_arg, my_locals):
	global stepping
	global breakpoints

	if command.find(' ') > 0:
		arg = command.split(' ')[1]
	else:
		arg = None

	if command.startswith('s'):
		stepping = True
		return True

def traceit(frame, event, arg):
	global stepping
	global breakpoints

	if event == 'Line':
		if stepping or breaking.has_key(frame.f_lineno):
			resume = False
			while not resume:
				print event, frame.f_lineno, frame.f_code.co_name, frame.f_locals
				command = input_command()
				resume = debug(command, arg, frame.f_locals)
		return traceit

Assertions is the most powerful debugging tool, automating debugging tool.

def test_square_root():
	assert square_root(4) == 2
	assert square_root(g) == 3
	# and so on
import math

def square_root(x):
	assert x >= 0
	y = math.sqrt(x)
	assert y * y == x
	return y

testing square root

import math
random

def square_root(x):
	assert x >= 0
	y = math.sqrt(x)
	assert y * y == x
	return y

for i in range(1, 1000):
	r = random.random() * 10000
	try:
		z = square_root(r)
	except:
		print r, math.sqrt(r) * math.sqrt(r)
		break

print "Done!"
import math
import random

def square_root(x, eps=10e-7):
    assert x >= 0
    y = math.sqrt(x)
    assert abs(y * y - x) < eps
    return y

for i in range(1, 1000):
    r = random.random() * 1000
    z = square_root(r)

print "Done!"
class Time:
	def __init__(self, h = 0, m = 0, s = 0):
		self._hours = h
		self._minutes = m
		self._seconds = s

	def hours(self):
		return self._hours

	def minutes(self):
		return self._minutes

	def seconds(self):
		return self._seconds

	def __repr__(self):
		return "{:2d}{:2d}{:2d}".format(
			self.hours(), self.minutes(), self.seconds())

t = Time(13, 0, 0)
print t