Web2py DAL Quick Reference

Background
The following was referred to here in a post by Iceberg on May 4, 2009 at 9:14 AM on the web2py-users google group. It is a merged version of the old and new DAL info.

I couldn’t find it published anywhere so I am publishing it here so I can refer to it. Some of it is here.

web2pyTM Database Abstraction Layer (DAL)

Quick Reference

define_table, insert, count, delete, update

db = SQLDB('postgres://user:password@hostname/db', pools=10)
db.define_table('person',db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('name','string'))
id= db.person.insert(name='max')
query=(db.person.id==id)
db(query).count()
db(query).delete()
db(query).update(name='Max')
rows = db(query).select(orderby=db.person.name)
for row in rows: print row.name

Examples of uri strings for SQLDB

sqlite://test.db
mysql://user:password@localhost/database
postgres://user:password@localhost/database
mssql://user:password@host/database
firebird://user:password@server:3050/database
oracle://user:password@database

Valid field Types

db.Field(name, 'string')
db.Field(name, 'text')
db.Field(name, 'password')
db.Field(name, 'blob')
db.Field(name, 'upload')
db.Field(name, 'boolean')
db.Field(name, 'integer')
db.Field(name, 'double')
db.Field(name, 'time')
db.Field(name, 'date')
db.Field(name, 'datetime')
db.Field(name, db.referenced_table) # reference field

Valid Field Attributes

length (only for string type, defaults to 32)
default (defaults to None)
required (defaults to False)
notnull (defaults to False)
requires (<a href="http://127.0.0.1:8000/examples/default/api">validator or list of validators</a>, for forms)
comment (for forms)
widget (for forms)
represent (for forms)
readable (for forms)
writable (for forms)
update (default value if the record is updated)
uploadfield (for upload fields)
authorize (for upload fields, function to be used if data can be downloaded, see authentication)
autodelete (for upload fields, if set to true linked uploaded images are removed upon deletion of the record)
label (for forms)

Migrations
Changing the list of fields or field types in a model, triggers an automatic migration, i.e. web2py generates SQL to alter the table accordingly. If the table does not exist it is created. Migration actions are logged in the file sql.log accessibled via the admin/design interface. Migration can be turned off on a per-table basis by passing migrate=False to define_table.
Select Attributes

rows = db(query).select(*fields, orderby=..., left=..., groupby=..., having=..., limitby=..., cache=...)

DAL Shortcuts

db['person']                   ### db.person
db.person['name']              ### db.person.name
db['person']['name']           ### db.person.name
db.person[0]=dict(name='Max')  ### insert
db.person[id]=dict(name='Max') ### update by db.person.id
print db.person[id]            ### select by db.person.id
del db.person[id]              ### delete by db.person.id</pre>

Truncate and Drop a Table

db.person.truncate()
db.person.drop()

**Reference Fields and Inner Joins*

db.define_table('dog',db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('name'))
db.define_table('friendship', db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('person',db.person), db.<a href="http://127.0.0.1:8000/examples/global/vars/Field">Field</a>('dog',db.dog))
db.friendship.insert(person=id, dog=db.dog.insert(name='Snoopy'))
friends=(db.person.id==db.friendship.person)&amp;(db.dog.id==db.friendship.dog)
rows = db(friends).select(db.person.name, db.dog.name)
for row in rows: print row.person.name, 'is friend of', row.dog.name

Left Outer Joins

query=(db.person.id>0)
friends=(db.person.id==db.friendship.person)&(db.dog.id==db.friendship.dog)
rows = db(query).select(db.person.name, db.dog.name, left=db.dog.on(friends))
for row in rows: print row.person.name, 'is friend of', row.dog.name or 'nobody'

Complex Queries

query = (db.person.id==1)|((db.person.id==2)&(db.person.name=='Max'))
query = (db.person.id==db.friendship.person)&(db.dog.id==db.friendship.dog)
query = db.person.name.lower().like('m%')
query = db.person.id.belongs(('max','Max','MAX'))
query = db.person.birth.year()+1==2008
rows = db(query).select()

Nested Selects

query = db.person.id.belongs(db()._select(db.friendship.person)

Aggregates

rows=db(friends).select(db.person.name,db.dog.id.count(),groupby=db.dog.id) 
''' Notes:
Aggregate functions: 
    db.table.field.count()
    db.table.field.max() 
    db.table.field.min()
    db.table.field. sum()
'''

Aliases

person=db.person
friendship=db.friendship
puppy=db.dog.with_alias('puppy')
query=(puppy.id==friendhip.dog)&(friendship.person==person.id)
rows=db().select(person.name,puppy.name,left=puppy.on(query))

Caching

rows=db().select(db.person.ALL,cache=(cache.ram,3600))
''' Notes:
cache=(model,cache_timeout) where model can be:
    cache.ram, 
    cache.disk, 
    cache.memcache or 
    user defined caching model
cache_timeout is in seconds.
'''

CSV Input/Output

# CVS Input
db.person.import_from_csv_file(open(filename,’rb’))
# CVS Output
str(rows)

HTML Output

print rows.xml()

Set Field Validators

db.person.name.requires=IS_NOT_IN_DB(db,db.person.name)
db.friendship.person.requires=IS_IN_DB(db,db.person.id,'%(name)s')

Generate and Process a Form from a Model

form = SQLFORM(db.friendship)
if form.accepts(request.vars, session): response.flash='record inserted'
elif form.errors: response.flash='form errors'
''' Note:
The form can then be displayed in a view with:
{{=form}}
'''

SQL + HTML Examples

# Given:
rows=db().select(db.users.ALL)
# Then:
SQLFORM (rows) # turns the rows into a CSS friendly table
SQLFORM(db.users)  # makes an input form for users
SQLFORM(db.users,rows[i]) # makes an edit form for the user in rows[i]
SQLFORM(db.users,rows[i],deletable=True) # makes an edit/delete form for the user in row[i]
SQLFORM(....,fields=['name','email']) # allows defining which fields should be displayed in the form. Only those fields will be validated.
SQLFORM(....,labels=['name':'Your Name']) # allows changing the labels of the listed fields.

''' Notes:
SQLFORM
-------
A form=SQLFORM(…) object has one method of practical interest:
    form.accepts(request.vars,session) 
The above method: 
    1. Processes the input variables (in request.vars) in the session
    2. Returns true if the form is valid, false otherwise. 
- Processed variables are in     form.vars
- Errors are in                  form.errors. 
- The form is modified accordingly. 
- If the form is accepted: 
    accept also performs the appropriate insert/update/delete in the database.

Extended Usage
--------------
The DAL API is automatically exposed in web2py 
    models, 
    controllers and 
    views 
but you can access them anywhere with:
'''
from gluon.sql import * 

Doctest Examples
These are old syntax but good examples. They use:

  • SQLDB instead of DAL and
  • SQLField instead of Field

However I believe they are still useful.

>>> db=SQLDB("sqlite://test.db")
>>> #OR db=SQLDB("mysql://username:password@host:port/dbname")
>>> #OR db=SQLDB("postgres://username:password@host:port/dbname")

# syntax: SQLField('fieldname','fieldtype',length=32,
#                   required=False, default=None,
#                   requires=[IS_EMAIL(error_message='invalid email')])

>>> tmp=db.define_table('users',
      # But notice that SQLField(...) is depreciated and you'd better use db.Field(...) instead
      SQLField('stringfield','string',length=32,required=True),
      SQLField('booleanfield','boolean',default=False),
      SQLField('passwordfield','password'),
      SQLField('textfield','text'),
      SQLField('blobfield','blob'),
      SQLField('uploadfield','upload'),
      SQLField('integerfield','integer'),
      SQLField('doublefield','double'),
      SQLField('datefield','date',default=datetime.date.today()),
      SQLField('timefield','time'),
      SQLField('datetimefield','datetime'),
      migrate='test_user.table')

# Insert a field

>>> db.users.insert(stringfield='a',booleanfield=True,
                   passwordfield='p',textfield='x',blobfield='x',
                   uploadfield=None, integerfield=5,doublefield=3.14,
                   datefield=datetime.date(2001,1,1),
                   timefield=datetime.time(12,30,15),
                   datetimefield=datetime.datetime(2002,2,2,12,30,15))
1

# Drop the table

>>> db.users.drop()

# Examples of insert, select, update, delete

>>> tmp=db.define_table('person',
          SQLField('name'),
          SQLField('birth','date'),
          migrate='test_person.table')
>>> person_id=db.person.insert(name="Marco",birth='2005-06-22')
>>> person_id=db.person.insert(name="Massimo",birth='1971-12-21')
>>> len(db().select(db.person.ALL))
2
>>> me=db(db.person.id==person_id).select()[0] # test select
>>> me.name
'Massimo'
>>> db(db.person.name=='Massimo').update(name='massimo') # test update
>>> db(db.person.name=='Marco').delete() # test delete

# Update a single record

>>> me.update_record(name="Max")
>>> me.name
'Max'

# Examples of complex search conditions

>>> len(db((db.person.name=='Max')&(db.person.birth>> len(db((db.person.name=='Max')|(db.person.birth>> me=db(db.person.id==person_id).select(db.person.name)[0]
>>> me.name
'Max'

# Examples of search conditions using extract from date/datetime/time

>>> len(db(db.person.birth.month()==12).select())
1
>>> len(db(db.person.birth.year()>1900).select())
1

# Example of usage of NULL

>>> len(db(db.person.birth==None).select()) ### test NULL
0
>>> len(db(db.person.birth!=None).select()) ### test NULL
1

# Examples of search conditions using lower, upper, and like

>>> len(db(db.person.name.upper()=='MAX').select())
1
>>> len(db(db.person.name.like('%ax')).select())
1
>>> len(db(db.person.name.upper().like('%AX')).select())
1
>>> len(db(~db.person.name.upper().like('%AX')).select())
0

# orderby, groupby and limitby

>>> people=db().select(db.person.name,orderby=db.person.name)
>>> order=db.person.name|~db.person.birth
>>> people=db().select(db.person.name,orderby=order)
>>> people=db().select(db.person.name,orderby=order,groupby=db.person.name)
>>> people=db().select(db.person.name,orderby=order,limitby=(0,100))

# Example of one 2 many relation

>>> tmp=db.define_table('dog',
          SQLField('name'),
          SQLField('birth','date'),
          SQLField('owner',db.person),
          migrate='test_dog.table')
>>> db.dog.insert(name='Snoopy',birth=None,owner=person_id)
1

# A simple JOIN

>>> len(db(db.dog.owner==db.person.id).select())
1

# Drop tables

>>> db.dog.drop()
>>> db.person.drop()

# Example of many 2 many relation and SQLSet

>>> tmp=db.define_table('author',SQLField('name'),
                        migrate='test_author.table')
>>> tmp=db.define_table('paper',SQLField('title'),
                        migrate='test_paper.table')
>>> tmp=db.define_table('authorship',
        SQLField('author_id',db.author),
        SQLField('paper_id',db.paper),
        migrate='test_authorship.table')
>>> aid=db.author.insert(name='Massimo')
>>> pid=db.paper.insert(title='QCD')
>>> tmp=db.authorship.insert(author_id=aid,paper_id=pid)

# Define a SQLSet

>>> authored_papers=db((db.author.id==db.authorship.author_id)&
                       (db.paper.id==db.authorship.paper_id))
>>> rows=authored_papers.select(db.author.name,db.paper.title)
>>> for row in rows: print row.author.name, row.paper.title
Massimo QCD

# Example of search condition using  belongs

>>> set=(1,2,3)
>>> rows=db(db.paper.id.belongs(set)).select(db.paper.ALL)
>>> print rows[0].title
QCD

# Example of search condition using nested select

>>> nested_select=db()._select(db.authorship.paper_id)
>>> rows=db(db.paper.id.belongs(nested_select)).select(db.paper.ALL)
>>> print rows[0].title
QCD

# Output in csv

>>> str(authored_papers.select(db.author.name,db.paper.title))
author.name,paper.title
Massimo,QCD

# Delete all leftover tables

>>> db.authorship.drop()
>>> db.author.drop()
>>> db.paper.drop()

# Commit or rollback your work

>>> db.commit() # or db.rollback()

''' Notes:
migrate can be:
    False (do not create/alter tables), 
    True (create/alter tables) or 
    a filename (create/alter tables and store migration information in the file).

There are little idiosyncrasies in every  backend like the fact that:
    "user" is not a valid field name in PostgreSQL, or 
    that sqlite3 will ignore the type of a field and allow you to put anything in it despite the declared type.
Each database has its own keywords that may conflict with your tablenames.
Advertisements

Python Cheat Sheet

Contents:
IO   Data Structures  Strings  Admin, os, sys  Control Flow  Big Templates


IO Back to Contents

readFile2String 1 liner

f = open('c:/test.txt', 'r'); s = f.read(); f.close()

readFile2String

f = open('c:/', 'r') #on windows add 'b'==binary
s = f.read()
f.close()

readLines 1 liner

for line in file('fileName.txt').readlines()

readLines

f = open('c:/', 'r') #on windows add 'b'==binary
for line in f:
    # process(line)
f.close()

writeString 1 liner

f = open('c:/', 'w'); f.write('This is a test\n'); f.close()

writeString

f = open('c:/', 'w') #on windows add 'b'==binary
f.write('This is a test\n')  # newline is NOT automatically added!
f.close()

Data Structures Back to Contents

looping dicts

for k, v in knights.iteritems():
    print k, v

looping enumerate sequences

for i, v in enumerate(Seq):
    print i, v

dict from k v lists

dict(zip(keyList, valList))

dict from k v strings

dict(zip(keyStr.split(' '), valString.split(' ')))

dict from obj attribs

dict((name, getattr(ob, name)) for name in dir(ob) if not name.startswith('__'))

map cube(x)

seq = range(10)
def cube(x): return x*x*x
map(cube, seq)

map add(x,y)

seq = range(10)
def add(x, y): return x+y
map(add, seq, seq)

reduce

seq = range(10)
def add(x, y): return x+y
reduce(add, seq)

list2string

s = ''.join(L)

list creations

L = s.split('#')
iota=range(5)
zeros=[0]*5
foos=['foo']*8
L=[0,1,2] + [3,4]
four=L[-1]
three=L[-2]
zero_1=L[:2]
two_3_4=L[2:]
one_2_3=L[1:4]
del(L[1])
listoflists=[ [0]*4 ] *5
listoflists=[[0]*4 for i in range(5)]

list comprehension [simple]

vec = [2, 4, 6]
[3*x for x in vec]

list comprehension [if]

vec = [2, 4, 6]
[3*x for x in vec if x > 3]

list comprehension [->list]

vec = [2, 4, 6]
[[x,x**2] for x in vec]

list comprehension [->dict]

l1=1,2,3,4,5; l2=6,7,8,9,0
dict([(k, v) for k,v in zip(l1,l2)])
# {1: 6, 2: 7, 3: 8, 4: 9, 5: 0}

list comprehension [v1, v2]

vec1 = [2, 4, 6]
vec2 = [4, 3, -9]
[x*y for x in vec1 for y in vec2]
# rightmost for varies first
# [8, 6, -18, 16, 12, -36, 24, 18, -54]

list comprehension [->tuples l1, l2]

l1=1,2,3; l2=6,7,8
[{x:y} for x in l1 for y in l2]
# rightmost for varies first
# [{1: 6}, {1: 7}, {1: 8}, {2: 6}, {2: 7}, {2: 8}, {3: 6}, {3: 7}, {3: 8}]

list comprehension [nested]

mat = [
        [1, 2, 3],
        [4, 5, 6],
        [7, 8, 9],
      ]
[[row[i] for row in mat] for i in range(len(mat[0]))]
# Above list comprehension inverts the matrix
# :)To avoid apprehension when nesting list comprehensions,
#    read them from right to left. :)

zip

x = ['1', '2', '3']
y = ('4', '5', '6')
z = "789"
zip(x, y, z)
# [('1', '4', '7'), ('2', '5', '8'), ('3', '6', '9')]
# returns a list of tuples, where the i-th tuple contains the i-th
#   element from each of the argument sequences or iterables.

sorted dict print formatted

cnid = chordNameIntervalDict= {
    'maj':    (0,  4,  7),
    'min':    (0,  3,  7),
    'aug':    (0,  4,  8),
    'dim':    (0,  3,  6),
    'sus2':   (0,  2,  7),
    'sus4':   (0,  5,  7),
    '6':      (0,  4,  7,  9),
    'm6':     (0,  3,  7,  9),
    '7':      (0,  4,  7, 10),
    'maj7':   (0,  4,  7, 11),
    'm7':     (0,  3,  7, 10),
    'dim7':   (0,  3,  6, 10),
    }

#make the keys be the vals and the vals the keys
icnd = intervalChordNameDict = dict([(cnid[k],k) for k in cnid.keys()])

chordNameL = 'maj,min,aug,dim,sus2,sus4,6,m6,7,maj7,m7,dim7'.split(',')

print 'cnid'
for k in chordNameL:
    print ('%7s %s')%(str(k),str(cnid[k]))

print 'icnd'
for k in sorted(icnd):
    print ('%-16s %s ')%(k, icnd[k])

Strings Back to Contents

concat strings

''.join([xStr, yStr])

concat strings [s = concat(s,x)]

xStr = ''.join([xStr, yStr])

string.replace

s = 'spam bacon ham spam ham bacon spam'
s.replace('spam', 'eggs',1) #one
s.replace('spam', 'eggs')   #all

string2list

L = list(s)

string2list [split]

L = s.split('#')

list2string

s = ''.join(L)

enum_string 2 dict

enum_string = es = "state, county, city_state, city_name, city_url"
ed = dict(zip(es.split(", "), range(len(es))))
print ed
{'county': 1, 'city_name': 3, 'state': 0, 'city_url': 4, 'city_state': 2}

Admin, os, sys Back to Contents

cd

import os
os.chdir(r"F:\1d\GoogleDev\AppEnginePjs")

pwd

import os
os.path.abspath(os.curdir)

sys.path.append

import sys
if not('.' in sys.path): sys.path.append('.')

Timestamp

import datetime
str(datetime.datetime.now()).replace(':', '.')
#out: 2011-05-30 11.32.56.375000   [no colons -> good 4 file names

Control Flow Back to Contents

Conditional Expressions

x = true_value if condition else false_value

try except

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as (errno, strerror):
    print "I/O error({0}): {1}".format(errno, strerror)
except ValueError:
    print "Could not convert data to an integer."
except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

yield

def genBarItems(self):
    rval = ()
    for staffIx, staff in enumerate(self.staves):
        BARS = 1
        for barIx, bar in enumerate(staff[BARS]):
            for itIx, it in enumerate(bar):
                rval = (staffIx, barIx, itIx, it)
                yield rval

Big Templates Back to Contents

class

class BankAccount(object):
    def __init__(self, initial_balance=0):
        self.balance = initial_balance
    def deposit(self, amount):
        self.balance += amount
    def withdraw(self, amount):
        self.balance -= amount
    def overdrawn(self):
        return self.balance < 0
my_account = BankAccount(15)
my_account.withdraw(5)
print my_account.balance

Main

"""example.py DoesSomething to infilename Producing outfilename
Usage:   example.py infileName outfileName
Example: example.py myInFile   myOutfile
"""
import sys

def myFunc(myArg1, myArg2):
    s = raw_input('input a string')
    print s

NUM_ARGS = 2
def main():
    args = sys.argv[1:]
    if len(args) != NUM_ARGS or "-h" in args or "--help" in args:
        print __doc__
        sys.exit(2)
    myFunc(args[0], args[1])

if __name__ == '__main__':
    main()

GUI

# -*- coding: ISO-8859-1 -*-
import sys, time

if not('.' in sys.path): sys.path.append('.')
import midi24txt 

from Tkinter import *
from tkFileDialog import *

# thinking in tkinter http://www.ferg.org/thinking_in_tkinter/all_programs.html

class TheGui:
    def __init__(self, parent):
        #------- frmSetup ----------#
        self.frmSetup = Frame(parent, bd=5)
        self.frmSetup.pack()

        self.inChoices = ('Text', 'Midi')
        self.varRadio = IntVar()

        self.r1 = Radiobutton(self.frmSetup, text="Convert Text INPUT into Midi OUTPUT",
            variable=self.varRadio, value=0, command=self.selRadio)
        self.r1.pack(anchor=W)

        self.r2 = Radiobutton(self.frmSetup, text="Convert Midi INPUT into Text OUTPUT",
            variable=self.varRadio, value=1, command=self.selRadio)
        self.r2.pack(anchor=W)
        #------- frmSetup ----------#

        sep = Frame(parent, width=1, bd=5, bg='black')
        sep.pack(fill=X, expand=1)

        #------- frmIn ----------#
        # http://effbot.org/tkinterbook/tkinter-widget-styling.htm
        self.frmIn = Frame(parent, bd=5)
        self.frmIn.pack()

        self.lblIn = Label(self.frmIn, text='Text Input File Path', width=20)
        self.lblIn.pack(side=LEFT)	

        self.inFilePath = StringVar() # http://effbot.org/tkinterbook/entry.htm
        self.entIn = Entry(self.frmIn, width=20, textvariable=self.inFilePath)
        self.entIn.pack(side=LEFT)

        self.btnIn = Button(self.frmIn, text='Browse', command=self.btnInBrowseClick)
        self.btnIn.pack(side=LEFT)
        #------- frmIn ----------#

        #------- frmOut ----------#
        self.frmOut = Frame(parent, bd=5)
        self.frmOut.pack()

        self.lblOut = Label(self.frmOut, text='Midi Output File Path', width=20)
        self.lblOut.pack(side=LEFT)	

        self.outFilePath = StringVar()
        self.entOut = Entry(self.frmOut, width=20, textvariable=self.outFilePath)
        self.entOut.pack(side=LEFT)	

        self.btnOut = Button(self.frmOut, text='Browse', command=self.btnOutBrowseClick)
        self.btnOut.pack(side=LEFT)
        #------- frmOut ----------#

        sep = Frame(parent, width=1, bd=5, bg='black')
        sep.pack(fill=X, expand=1)

        #------- frmButtons ----------#
        self.frmOut = Frame(parent, bd=5)
        self.frmOut.pack()

        self.btnConvert = Button(self.frmOut, text='Convert', command=self.btnConvertClick)
        self.btnConvert.pack()	

    #------- handle commands ----------#
    def selRadio(self):
        self.lblIn.config(text = self.inChoices[self.varRadio.get()] + ' Input File Path')
        self.lblOut.config(text = self.inChoices[(self.varRadio.get()+1)%2] + ' Output File Path')
        print str(self.varRadio.get())

    def btnInBrowseClick(self):
        rFilepath = askopenfilename(defaultextension='*',
            initialdir='.', initialfile='', parent=self.frmIn, title='select a file')
        self.inFilePath.set(rFilepath)
        print self.entIn.get()

    def btnOutBrowseClick(self):
        rFilepath = asksaveasfilename(defaultextension='*',
            initialdir='.', initialfile='', parent=self.frmIn, title='select a file')
        self.outFilePath.set(rFilepath)
        print self.entOut.get()

    def btnConvertClick(self):
        #defClr = self.btnConvert.cget("bg")
        self.btnConvert.config(relief=SUNKEN)
        self.btnConvert.update()
        #print 'Convert from %s to %s' % (self.inChoices[self.varRadio.get()], self.inChoices[(self.varRadio.get()+1)%2])
        print 'self.varRadio.get()', self.varRadio.get()

        if self.varRadio.get() == 0:
            inputTextFilePath = str(self.inFilePath.get()); outputMidiFilePath = str(self.outFilePath.get())
            print 'midi 4 txt', inputTextFilePath, outputMidiFilePath
            midi24txt.mid4txt(inputTextFilePath, outputMidiFilePath)
        else:
            inputMidiFilePath = str(self.inFilePath.get()); outputTextFilePath = str(self.outFilePath.get())
            print 'midi 2 txt', inputMidiFilePath, outputTextFilePath
            midi24txt.mid2txt(inputMidiFilePath, outputTextFilePath)
        time.sleep(0.5)
        self.btnConvert.config(relief=RAISED)
        self.btnConvert.update()

root = Tk()
root.title("Convert between Midi and Text Files")
#http://infohost.nmt.edu/tcc/help/pubs/tkinter/std-attrs.html#geometry
#http://infohost.nmt.edu/tcc/help/pubs/tkinter/toplevel.html
root.geometry("350x200+10+10")
gui = TheGui(root)
root.mainloop()

cgi

#!/usr/bin/env python
# from: http://webpython.codepoint.net/cgi_file_upload
import cgi, os
import cgitb; cgitb.enable()

try: # Windows needs stdio set for binary mode.
    import msvcrt
    msvcrt.setmode (0, os.O_BINARY) # stdin  = 0
    msvcrt.setmode (1, os.O_BINARY) # stdout = 1
except ImportError:
    pass

form = cgi.FieldStorage()

def upload():
    """ upload the file into files/ """
    fileitem = form['file']

    # Test if the file was uploaded
    if fileitem.filename:

       # strip leading path from file name to avoid
       #    directory traversal attacks
       fn = os.path.basename(fileitem.filename)
       open('files/' + fn, 'wb').write(fileitem.file.read())
       message = 'The file "' + fn +
               '" was uploaded successfully'

    else:
       message = 'No file was uploaded'

    print """\
    Content-Type: text/html\n

%s

    """ % (message,)

def main():
    #cgi.test()
    message = 'from main'
    for k in form.keys():
        if k != 'file':
            message = ''.join([message,
                k+': '+form.getvalue(k)+''])
     print """\
    Content-Type: text/html\n

%s

    """ % (message,)

main()

Make the Eclipse JUnit Test Suite wizard work for JUnit-4

Background.
I recently got back into Java being inspired by the growth of the Android platform and Java’s close link to it. I needed a refresher tutorial on Java and Eclipse and found “Eclipse and Java for Total Beginners” which after getting over my pride and reviewing the first couple of lessons is just what I needed. This very well thought out and produced tutorial also gives an excellent introduction to “Test Driven Development”. Another benefit.

The Problem.
The tutorial is based based on JUnit-3. I installed the more up-to-date JUnit-4 and the Eclipse “JUnit Test Suite” wizard BLEW UP! :..)… I googled “eclipse JUnit 4 Test Suite wizard problem” and concluded that it seems to be well known, so I thought I’d publish my work-around here.

The Work-Around. 
The specific variable names in the work-around are based on the tutorial code. You can see what the tutorial recommended by going to Lesson- 8 at about 10min:50sec. Here is the work-around:

-in Package Explorer View TotalBeginner/test rclick org.totalbeginner.tutorial
    new.other.Java.JUnit.JUnitTestSuite
        wizard dialog comes up
            -next, press Finish to select default of (BookTest, PersonTest)
                creates a new class AllTests
-*** wizard will say ***:  
    Warning: No test classes selected
    *** because i use JUnit-4 Wizard needs JUnit-3 ***
    -click Finish anyway
-Fix wizard generated code
    1. delete everything from the wizard code 
        EXCEPT the package definition in this case:
            package org.totalbeginner.tutorial;
    2. put in something like the following code 
        2.1 here's the code:
            @RunWith(Suite.class)
            @Suite.SuiteClasses(value={BookTest.class, PersonTest.class})
            public class AllTests {}
        2.2 Change/Add to the example "BookTest.class" & "PersonTest.class" items,
            appropriate items for your situation. 
        2.3 use eclipse quickfix to create appropriate import statements
            -click in error code marked with red squiggly underline
            -ctl-1 for quick fix
            -select suggestion that generates the appropriate import
            -continue until no more errors 
            -for this example 2 imports were generated:
                import org.junit.runner.RunWith;
                import org.junit.runners.Suite;