Monday, February 2, 2009

Python Typechecker

Excited about the superdynamicness of Python, I decided to build a simple typechecking system for it, as a friend of mine mentioned he did quite some time ago:

types.py

#!/usr/bin/python

def typechecker(types, args):
return not False in [types[i] in type(args[i]).__mro__ for i in xrange(len(types))]

def typed(return_type, *types):
def typedf(f):
def ret(*args):
if len(args) != len(types):
raise TypeError, 'Invalid arguments; requires %u arguments.' % len(types)
elif not typechecker(types, args):
raise TypeError, 'Invalid arguments (%s); requires: %s' % ([type(arg) for arg in args], types)
r = f(*args)
if not (return_type is None) and not (return_type in type(r).__mro__):
raise TypeError, 'Invalid return type (%s); requires: %s' % (type(r), return_type)
return r
return ret
return typedf

Here's an example of its use:

if __name__ == '__main__':
@typed(basestring, basestring, int, int)
def safe_str_getslice(string, start, length):
return string[start:length]


print safe_str_getslice('Pastafarianism', 0, 5)
print safe_str_getslice('Pastafarianism', 0, 5.1)
Pasta
Traceback (most recent call last):
File "types.py", line 28, in <module>
print safe_str_getslice('Pastafarianism', 0, 5.1)
File "types.py", line 12, in ret
raise TypeError, 'Invalid arguments (%s); requires: %s' % ([type(arg) for arg in args], types)
TypeError: Invalid arguments ([<type 'str'>, <type 'int'>, <type 'float'>]); requires: (<type 'basestring'>, <type 'int'>, <type 'int'>)

Friday, January 16, 2009

mmap_test.py

Another program out of boredem. For my CSE451 class, I was reading about IPC, and SHM and mmap() were discussed in the text. Thus, I looked into Python's support, and it looks like mmap() is included. The program here is essentially a "deranged cat," which uses two processes for no reason:

mmap_test.py

#!/usr/bin/python
from mmap import mmap
from os import fork, waitpid, WEXITSTATUS
from sys import exit, stdin

map = mmap(-1, 1024)
map.seek(0)
map.write_byte(chr(0))

pid = fork()
if pid == 0:
while True:
map.seek(0)
b = map.read_byte()
while b == chr(0):
map.seek(0)
b = map.read_byte()
if b == chr(3):
break
else:
print map.readline().rstrip()
map.seek(0)
map.write_byte(chr(0))
exit(0)._exit()
else:
while True:
st = stdin.readline(1022)
if st == '':
map.seek(0)
map.write_byte(chr(3))
break
else:
map.seek(1)
map.write(st)
map.seek(0)
map.write_byte(chr(1))

map.seek(0)
while map.read_byte() == chr(1):
map.seek(0)

id, status = waitpid(pid, 0)
map.close()
print 'Child exited with', WEXITSTATUS(status)

Tuesday, January 13, 2009

txp.pl/ctxp.pl

With a little bit of free time today, I decided to write something similar to TextExpander in Perl. Using getc and setc, it'll put and read from the clipboard of your GUI, respectively:

txp.pl

#!/usr/bin/perl -w
use strict;
use warnings;
use utf8;
use Clipboard;
use DBI;

my $usage = ($0 eq 'ctxp.pl' and "Usage: $0 key\n" or "Usage: $0 [ set key value | setf key | setf key | unset key | get key | getc key | list ]\n");

scalar @ARGV || die $usage;
my $action = shift @ARGV;

my $dbh = DBI->connect('dbi:SQLite:dbname=' . $ENV{HOME} . '/.txp_db', '', '');
$dbh->do('CREATE TABLE IF NOT EXISTS expanders(id INTEGER PRIMARY KEY AUTOINCREMENT, key VARCHAR(64), value TEXT)');



sub set_or_add {
my ($dbh, $key, $value) = @_;
if(scalar @{$dbh->selectall_arrayref('SELECT id FROM expanders WHERE key = ?', undef, $key)}) {
$dbh->do('UPDATE expanders SET value = ? WHERE key = ?', undef, $value, $key);
}
else {
$dbh->do('INSERT INTO expanders(key, value) VALUES(?, ?)', undef, $key, $value);
}
}
sub unset {
my ($dbh, $key) = @_;
my $sth = $dbh->prepare('DELETE FROM expanders WHERE key = ?');
$sth->execute($key);
return $sth->rows;
}
sub get {
my ($dbh, $key) = @_;
my $value = undef;
my $row = $dbh->selectrow_arrayref('SELECT * FROM expanders WHERE key = ?', undef, $key);
($value = ${$row}[2]) if($row);
return $value;
}

if($0 eq 'ctxp.pl') {
my $key = $action;
my $value = get($dbh, $key);
Clipboard->copy($value) if($value);
}
elsif($action eq 'set' and scalar @ARGV == 2) {
my ($key, $value) = @ARGV;
chomp($value);
set_or_add($dbh, $key, $value);
}
elsif($action eq 'setf' and scalar @ARGV == 1) {
my $key = shift @ARGV;
my @lines = <STDIN>;
my $value = join('', @lines);
chomp($value);
set_or_add($dbh, $key, $value);
}
elsif($action eq 'setc' and scalar @ARGV == 1) {
my $key = shift @ARGV;
my $value = Clipboard->paste();
chomp($value);
set_or_add($dbh, $key, $value);
}
elsif($action eq 'unset' and scalar @ARGV == 1) {
my $key = shift @ARGV;
my $count = unset($dbh, $key);
print "$count rows affected.\n";
}
elsif($action eq 'get' and scalar @ARGV == 1) {
my $key = shift @ARGV;
my $value = get($dbh, $key);
if($value) {
print "$value\n";
}
else {
print STDERR "No matches found for $key\n";
}
}
elsif($action eq 'getc' and scalar @ARGV == 1) {
my $key = shift @ARGV;
my $value = get($dbh, $key);
if($value) {
print "$value\n";
Clipboard->copy($value);
}
else {
print STDERR "No matches found for $key\n";
}
}
elsif($action eq 'list' and scalar @ARGV == 0) {
my $rows = $dbh->selectall_arrayref('SELECT * FROM expanders');
if(scalar @{$rows}) {
(print ${$_}[1] . ' => ' . ${$_}[2] . "\n") foreach(@{$rows});
}
else {
print STDERR "No mappings present.\n";
}
}
else {
print STDERR $usage;
}




$dbh->disconnect();
% perl txp.pl list
cows => How now brown cow.
nante => 何てね
% perl txp.pl unset cows
1 rows affected.
% echo 'Foos are bars.' | perl txp.pl setf foo
% perl txp.pl list
nante => 何てね
foo => Foos are bars.
% perl txp.pl set argv "Arrrgh."
% perl txp.pl list
nante => 何てね
foo => Foos are bars.
argv => Arrrgh.

safecp

After destroying an rc.lua for Awesome, I was motivated to write this simple script. It skips overwriting all files, unlike cp:

safecp

#!/usr/bin/python
from os import path
from sys import stderr, exit, argv
from shutil import copy, copytree

copymethod = copy
if len(argv) == 3:
src, dest = argv[1:3]
src, dest = unicode(src, 'utf8'), unicode(dest, 'utf8')
if path.exists(src):
if path.isdir(src):
print >>stderr, u'Copying directory recursively:', src
copymethod = copytree
if (not path.exists(dest)) or path.isdir(dest):
copymethod(src, dest)
else:
print >>stderr, u'Not overwriting existing file:', dest
exit(3)
else:
print >>stderr, u'File not found:', src
exit(2)
elif len(argv) > 3:
dest = unicode(argv.pop(), 'utf8')
src = argv[1:]
if path.isdir(dest):
for s in src:
s = unicode(s, 'utf8')
copymethod = copy
target = path.join(dest, path.basename(s))
if path.isdir(s):
print >>stderr, u'Copying directory recursively:', s
copymethod = copytree
if not path.exists(target):
copymethod(s, target)
else:
print >>stderr, u'Not overwriting existing file or directory:', target
else:
print >>stderr, u'Destination must be directory:', dest
exit(2)
else:
print >>stderr, u'Usage: %s src1 [ src2 [ src3 [ ... ]]] dest' % argv[0]
exit(1)

Monday, November 17, 2008

Kawa-Kun!

Sorry about not updating much (again). I've been writing many programs, but have been too lazy to upload their source to here.

To make up for it, here's a link to the webcomic I've started: Kawa-kun!

Saturday, October 25, 2008

latest.sh

A bit lazy of me, but with my directories containing many entries, it's been easier to keep track of files via modification time. That's just what I've done with this script:

latest.sh

#!/bin/sh
FILE=
DATE=0
if [ $# -gt 0 ]; then
for i in "$@"
do
NDATE=`date -r "$i" '+%s'`
if [[ $NDATE -gt $DATE || -z "$FILE" ]]; then
FILE=$i
DATE=$NDATE
fi
done
echo "$FILE"
else
for i in *
do
NDATE=`date -r "$i" '+%s'`
if [[ $NDATE -gt $DATE || -z "$FILE" ]]; then
FILE=$i
DATE=$NDATE
fi
done
echo "$FILE"
fi

notedb.rb

As the number of notes I've taken for class increases, searching has become increasingly difficult. I wrote this script a little while ago, and it's made keeping track of my notes both easy and efficient.

notedb.rb

#!/usr/bin/ruby
require 'sqlite3'

def init_db(filename)
db = SQLite3::Database.new(filename)
db.execute('CREATE TABLE IF NOT EXISTS notes(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, dir TEXT, mtime TEXT, content TEXT)')
db.execute('PRAGMA auto_vacuum = 1')
return db
end


def recurse_add(db, root)
rdir = Dir.open(root)
rdir.each do |file|
fpath = File.join(root, file)
if File.directory?(fpath)
if file != '.' and file != '..'
recurse_add(db, fpath)
end
elsif File.file?(fpath)
if file =~ /^[0-9]{4,}-[0-1][0-9]-[0-3][0-9]:.*\.tex$/
count = db.get_first_value('SELECT COUNT(*) FROM notes WHERE name = ? AND dir = ?', file, root).to_i
if count == 0
puts 'NEW: ' + file
f = File.open(fpath, 'r')
mtime = f.mtime
db.execute('INSERT INTO notes(name, dir, mtime, content) VALUES(?, ?, ?, ?)', file, root, mtime.strftime('%FT%T%z'), f.read)
f.close
else
f = File.open(fpath, 'r')
mtime = f.mtime
content = f.read
f.close
db.execute('SELECT id, content FROM notes WHERE name = ? AND dir = ?', file, root) do |info|
if info[1] != content
puts 'UPDATE: ' + file
db.execute('UPDATE notes SET content = ?, mtime = ? WHERE id = ?', content, mtime.strftime('%FT%T%z'), info[0])
end
end
end
end
else
puts 'No such file or directory: ' + file
end
end
rdir.close
end


if ARGV.length > 0 and ENV.key?('EDITOR')
db = nil
begin
db = init_db(File.join(ENV['HOME'], '.notedb'))
mode = ARGV.shift
case mode
when 'add'
if ARGV.length > 0
ARGV.each do |file|
puts file
if File.directory?(file)
recurse_add(db, File.expand_path(file))
elsif File.file?(file)
f = File.open(file, 'r')
db.execute('INSERT INTO notes(name, dir, content)', file, File.expand_path(File.dirname(file)), f.read)
f.close
else
$stderr.puts 'No such file or directory: ' + file
end
end
else
$stderr.puts 'Usage: ' + $0 + ' add f1 [ f2 .. fn ]'
end
when 'search'
if ARGV.length > 0
term = Regexp.compile(ARGV.shift)
if ARGV.length > 0
ARGV.each do |name|
db.execute('SELECT name, dir, content FROM notes WHERE dir GLOB ?', '*' + File.expand_path(name) + '*') do |row|
row[2].split(/\n/).each do |line|
if line =~ term
puts row[0] + ':' + line
end
end
end
end
else
db.execute('SELECT name, dir, content FROM notes') do |row|
row[2].split(/\n/).each do |line|
if line =~ term
puts row[0] + ':' + line
end
end
end
end
else
$stderr.puts 'Usage: ' + $0 + ' search term [ dir ]'
end
when 'get'
if ARGV.length > 1
mode = ARGV.shift
case mode
when 'name', 'iname'
qstring = ''
if mode == 'iname'
qstring = 'SELECT name, dir, mtime FROM notes WHERE name LIKE ?'
else
qstring = 'SELECT name, dir, mtime FROM notes WHERE name GLOB ?'
end
ARGV.each do |name|
if mode == 'iname'
name = '%' + name + '%'
else
name = '*' + name + '*'
end
db.execute(qstring, name) do |row|
mtime = DateTime.strptime(row[2])
printf "%-70s (modified %s)\n", row[0], mtime.strftime('%Y-%m-%d')
end
end
when 'dir', 'idir'
qstring = ''
if mode == 'idir'
qstring = 'SELECT name, dir, mtime FROM notes WHERE dir LIKE ?'
else
qstring = 'SELECT name, dir, mtime FROM notes WHERE dir GLOB ?'
end
ARGV.each do |name|
if mode == 'iname'
name = '%' + name + '%'
else
name = '*' + name + '*'
end
db.execute(qstring, name) do |row|
mtime = DateTime.strptime(row[2])
printf "%-70s (modified %s)\n", row[0], mtime.strftime('%Y-%m-%d')
end
end
when 'latest', 'ilatest', 'elatest'
pnewest = DateTime.new
rep = ''
case mode
when 'elatest'
qstring = 'SELECT name, dir, mtime FROM notes WHERE dir = ?'
when 'ilatest'
qstring = 'SELECT name, dir, mtime FROM notes WHERE dir LIKE ?'
when 'latest'
qstring = 'SELECT name, dir, mtime FROM notes WHERE dir GLOB ?'
end
ARGV.each do |name|
case mode
when 'ilatest'
name = '%' + name + '%'
when 'latest'
name = '*' + name + '*'
end
db.execute(qstring, name) do |row|
mtime = DateTime.strptime(row[2])
if mtime > pnewest
pnewest = mtime
rep = row[0]
end
end
end
if rep.length
puts rep
end
else
$stderr.puts 'Usage: ' + $0 + ' get [ name | iname | dir | idir | latest | ilatest | elatest ] match'
end
else
$stderr.puts 'Usage: ' + $0 + ' get [ name | iname | dir | idir | latest | ilatest | elatest ] match'
end
when 'list'
db.execute('SELECT name, mtime FROM notes') do |row|
mtime = DateTime.strptime(row[1])
printf "%-70s (modified %s)\n", row[0], mtime.strftime('%Y-%m-%d')
end
when 'help'
$stderr.puts 'Usage: ' + $0 + ' [ add | search | get | list ]'
$stderr.puts "\tEDITOR must be set: currently \"" + (ENV['EDITOR'] and ENV['EDITOR'] or '') + "\""
else
raise 'Unknown mode: ' + mode
end

ensure
if db
db.close
end
end
else
$stderr.puts 'Usage: ' + $0 + ' [ add | search | get | list ]'
$stderr.puts "\tEDITOR must be set: currently \"" + (ENV['EDITOR'] and ENV['EDITOR'] or '') + "\""
end