The SoCal Piggies had their tenth meeting at USC (Salvatori Computer Science Center) on January 17th 2006 at 7:00 PM. The attendance record was broken! Twelve people attended: 10 Piggies -- Daniel Arbuckle, Grig Gheorghiu, Greg Abbas, Diane Trout, Titus Brown, Brandon King, Mark Kohler, Howard Golden, George Bullis and Michael Carter -- and 2 guests (Mike and David) courtesy of Michael Carter. A special welcome to newcomers Greg, Brandon and Michael.
The first presenter was Howard Golden, who talked about [http://equi4.com/metakit.html Metakit], an efficient embedded database library with a small footprint. Metakit, according to its Web site, fills the gap between flat-file, relational, object-oriented, and tree-structured databases, supporting relational joins, serialization, nested structures, and instant schema evolution. Howard's talk was based on a Dr. Dobb's article: [http://devnet.developerpipeline.com/documents/s=9849/q=1/ddj0412j/0412j.html "Relational Algebra & Metakit"] by Brian Kelley.
Metakit databases (called "storages") can be either on disk or in-memory, the latter allowing for easy experimentation and debugging. Metakit tables are called "views", which are combinations of rows and columns. A view may be an entire table, a portion of a table, or the result of operations performed on tables. One particular thing about Metakit is that it is based on a column-oriented data model. But enough talk, let's see some code. The attractive thing about Metakit from a Python developer perspective is that it offers a very "pythonic" interface for manipulating tables. Here is Listing One from the online article mentioned above:
import metakit
# create an in-memory storage
st = metakit.storage()
# create a simple base view
view = st.getas("test[first:S,last:S,age:I]")
# add some data
view.append(("Joe", "Schmoe", 32))
view.append({"first":"Moe", "last":"Schmoe", "age":26})
class Loader:
def __init__(self, first, last, age):
self.first = first
self.last = last
self.age = age
row = Loader("Zoe", "Schmoe", 22)
view.append(row)
# show the view
metakit.dump(view)The getas method is your friend when you need to create, modify or delete tables.
Metakit's greatest strength consists in making it very easy to do relational operations such as joins, projections, restrictions and sorts. Joins in particular can be very tricky to accomplish in ordinary SQL syntax. With Metakit, they become more manageable and, Python being Python, even fun. Here is an example (Listing Six in the article):
"""This example shows various uses of metakit joins"""
import metakit
st = metakit.storage()
# simple join between two tables
# select a,b,c from vw1,vw2 where vw1.a=vw2.a
vw = st.getas("test1[a:I,b:S]")
vw.append((1, "view1"))
vw2 = st.getas("test2[a:I,c:S]")
vw2.append((1, "view2"))
vw2.append((2, "view2"))
vw2.append((2, "view1"))
print "vw"
metakit.dump(vw)
print
print "vw2"
metakit.dump(vw2)
# perform a simple join on the a column
print
print "select * from vw1, vw2 where vw1.a=vw2.a"
result = vw.join(vw2, vw.a)
metakit.dump(result)
# result should be:
#a b c
# - ----- -----
# 1 view1 view2
# - ----- -----
# Total: 1 rows
# more complicated join, essentially select vw1.a, vw1.b from vw1,vw2
# where vw1.b=vw2.c in this case we need to rename vw2's c column
# to "b" so we can use the join
print
print "select vw1.a, vw1.b from vw1,vw2 where vw1.b=vw2.c"
result = vw.join(vw2.rename("c", "b"), vw.b)
metakit.dump(result)
# result should be
#a b
# - -----
# 1 view1
# - -----
# Total: 1 rows
print
print "select * from vw right outer join vw2 where vw.a=vw2.a"
print " this keeps all data from all rows in vw2"
print " note the last two rows have no corresponding match in vw"
print " but they are kept anyway"
result = vw2.join(vw, vw2.a, outer=1)
metakit.dump(result)One caveat about Metakit is that it doesn't scale very well, and its read/write model is based on a single writer/multiple readers. On the other hand, it does support transactions, so it is a worthy candidate for your consideration the next time you find yourself in need of a small, simple, embedded database for your project.
The next presenter was Grig Gheorghiu, who demo-ed [http://www.openqa.org/selenium/ Selenium], a testing tool for Web applications. The main difference between Selenium and other HTTP test tools is that Selenium uses an actual browser driven via Javascript to play back testing scripts. This means that Selenium has some unique features not available in any other Web test tool:
- it can test client-side functionality implemented in Javascript
- it can be used cross-platform and cross-browser, which makes it a perfect tool for browser compatibility testing
There is a large and growing number of articles on Selenium available on the Web, and a good starting place is the [http://wiki.openqa.org/display/SEL/Getting+Started Getting Started] page on the Selenium Wiki. To see Selenium in action, including some Ajax testing, see a [http://awesnob.com/zen/selenium live Selenium test suite] maintained by Luke Closs.
Grig showed some tests he wrote for the application that he and Titus will present at their PyCon tutorial in February. The part of the demo with the most eye-candy was probably the test suite for the [http://pythonpaste.org/comment/commentary/ Commentary] functionality based on Ajax. There is no live demo for that yet, but those interested can read more about it [http://agiletesting.blogspot.com/2006/01/testing-commentary-and-thus-ajax-with.html here].
For the people who attended the talk, I want to clarify something about the Selenium "Driven Mode". If you recall, the example Python script was talking to an XML-RPC server. The Python script also launched a browser and pointed it to a special Web page. There was some confusion in the audience as to how exactly does the Python script control the browser after launching it. I didn't answer the questions properly and the confusion I think persisted. The answer is that the script doesn't control the browser anymore. The browser was pointed to a Web page which contains Javascript code that periodically polls the XML-RPC server for work. The Python script sends commands such as app.verifyTextPresent('selenium.thoughtworks.com','') to the XML-RPC server, and the server posts them to an internal queue. When the browser polls the server for work, the server takes the next command out of the queue and sends it back to the browser, which then executes it just as if it was written in a normal test table. I'm not sure if I managed to clarify the confusion. I tried to explain all this in a [http://agiletesting.blogspot.com/2005/03/web-app-testing-with-python-part-2.html blog post], so maybe it's a bit more clear there.
Here is a [wiki:SeleniumAndAjaxLinks collection of useful Selenium and Ajax links]. Many thanks to Howard for presenting and to Daniel again for hosting the meeting.
For the next meeting, in February, Titus and Grig will give a dry-run of their PyCon tutorial presentation on "Agile development and testing in Python". The full tutorial takes 3 hours, but they will try to cover everything in half the time, while carefully timing every segment of the talk :-).
