Sunday, December 31, 2006

Flow Testing - Initial Ideas

I'm toying with the idea of a new way to write tests. This is a cross between state and interaction based testing, and also tries to make setting up the test environments easier. I call it "Flow Testing" as it tests how the various states of the test target flow from one to the next.

The idea is several-fold. We begin on the idea that we are testing names, because everything in Python starts with a name. You will test different things about names, such as type, what attributes or other properties apply to certain conditions, etc.

Flow testing is very tree-oriented. For every name, you define the state you expect to begin, and branch into the state you expect for various properties. For example, you may have a state for the name 'foo' and a branch state for 'foo.bar'. These are branches within one state, while wthere are other branches across states called Interaction Branches. These states define the interactions that lead to them and the states expected.

The example below defines a test for a simple module that should have a simple function. It defines that the name foo should be a module and that the state of its 'bar' attribute should be a callable. It then defines the state of a call to bar, and details it under more specific conditions of the interaction (specific arguments given).

import flowtest

import foo


class test_foo(flowtest.State):
targetType = flowtest.module
targetHasAttributes = ('bar',)

class test_bar(flowtest.State):
targetType = flowtest.callable

class testReturn(flowtest.State):
targetType = basestring

class testReturn(flowtest.State):
flowtest.callArguments()
flowtest.callArguments('hi')

targetContains = u"hello"

class testReturn(flowtest.State):
flowtest.callArguments('bye')

targetType = basestring
targetContains = u"goodbye"


This is just an idea, and does not work or exist yet. Any comments? If it seems solid, I'd like to start working on it.

Saturday, December 23, 2006

Holidays

I am on vacation. It is very, very nice.

Even before this, things have been slow. On the edge of worryingly slow, but still not slow enough that I've had to fret over anything. I've been enjoying some relaxation and time with my growing baby boy and my beatiful wife.

We've taken the holiday with her family and our friends in sunny North Carolina, where I have been enjoying warm, sunny weather to start my winter. I actually miss the cold.

Although not much has happened externally, a lot has happened internally. I am using the down time to organize myself, my plans, and prepare movements forward for proper exploration of some plans I've had on the back burner for a while now.

More of my time has been able to be spent on more recreational activities, as well, and that is nice. Reading has been lost to me for some time, and I've picked up a few books I've enjoyed. I plan to start another blog, which will be focused on reviews of literature developers, gamers, and other geeks will enjoy, but not necessarily literature about those topics. I want to bring other people in on this one, so if anyone is interested in reviewing good books, new or old, contact me (ironfroggy (ta) gmail (tod) com).

Speaking of the holidays and recreation: I got a Wii! I've been loving it. I have the WiiSports that came with the system, and the new Zelda, but I actually play the WiiSports more, so far. It is so fun. I've been taking the system around to friends' houses and we all have sore arms from so much bowling, the funest game for multiple people and one controller (and maybe my favorite one besides that). Anyway, when I get some good online games, I can't wait to play against some others in the community.

My Mii number is 3456 8115 5527 0431.

So far I can't bowl over a 175, the words "fowl ball" have lost all meaning, and I can sleep easier under the delusion that a higher score is better, even in golf. Zelda is slow going to start, but I'm enjoying it. Kind of obvious how it wasn't started as a Wii game, however. Most of the time the wiimote is just used to make a little fairy (with, so far, no mention in the game what-so-ever) fly around. And I can fish.

I will possibly move away from blogspot soon. I need more flexability.

Friday, December 08, 2006

ExtSession 0.3

I released ExtSession 0.3 today. Added error reporting through the result objects, iteration over remote objects, cleaned up much of the code, and did a some bugfixing.

Also included a tutorial on extsession usage, so hopefully some of the people that had difficulty can read that and understand how to use it.

Go and get it.

EDIT: Fixed a broken link! What a way to mess up a release, no matter how small.

Wednesday, December 06, 2006

Announce: ExtSession

As a support package for some other things, I've written a little module called ExtSession. The basic idea is to have an easy way to control a python session running in another process. You can simply instansiate the ExtSession class, and call the execute() method. The results are asyncronous-like, but will be improved later. Very likely support will be added to get Deferreds. For now, you can simply poll or wait. The results of each execute() call (best done one line at a time) are seperated and returned individually of output from other commands, making it very easy to work with over a session's lifetime.

Following is a stripped pydoc pull of the docstrings. I hope someone finds this as useful as I think I will.

NAME
extsession

FILE
/home/calvin/extsession/extsession.py

DESCRIPTION
ExtSession Module
For executing code in an external interpreter.

There are many cases where operations are either intensive or blocking by
waiting, and in both situations the extsession module can be used to control
a python session in a seperate process for the purposes of executing code
in parellel without the dangers associated with threads or the complexities
in dealing with asyncronous frameworks.

The core of extsession is the ExtSession class. See its help for details.

Also, look at the exteval and extexec functions for quick, blocking code
execution in a fresh interpreter. (note: these functions do not execute in
parellel, only in a seperate process, but blocking the original)

CLASSES
__builtin__.object
ExtEval
ExtResults
ExtSession
Session

class ExtEval(__builtin__.object)
| Evaluates expressions in a new process with an asyncronous result.
|
| The result attribute evaluates to the result of the expression, or access
| raises a ValueError, if the result is not ready.
|
| Methods defined here:
|
| __init__(self, expr)
|
| ----------------------------------------------------------------------
| Properties defined here:
|
| result
| = _get_result(self)
| Get the result only if its ready.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __dict__ =
| dictionary for instance variables (if defined)
|
| __weakref__ =
| list of weak references to the object (if defined)

class ExtResults(__builtin__.object)
| Represents results from an operation in another process.
|
| Methods defined here:
|
| __init__(self, stdout, stderr, done)
|
| read(self, size=None)
| Read data from stdout of process from operation.
| Raises ValueError if the operation is not complete.
|
| wait(self)
| Return only when the operation is complete.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __dict__ =
| dictionary for instance variables (if defined)
|
| __weakref__ =
| list of weak references to the object (if defined)

class ExtSession(__builtin__.object)
| Controls a python session in another process.
|
| Methods defined here:
|
| __getitem__(self, name)
|
| __init__(self)
|
| execute(self, source)
| Execute arbitrary python source.
| Returns an ExtResults object to access the results.
|
| exit(self, code=0)
|
| readcodes(self)
|
| readline(self)
|
| writeline(self, line)
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __dict__ =
| dictionary for instance variables (if defined)
|
| __weakref__ =
| list of weak references to the object (if defined)

class Session(__builtin__.object)
| Manages the session in this process. Used by ExtSession in spawned
| interpreters. Can also be used for a very light sandbox in the same
| process.
|
| Methods defined here:
|
| __init__(self)
|
| execute(self, source, stdout_fn=None, stderr_fn=None, done_fn=None)
| Executes code in a semi-controlled environment and redirects output
| to given stdout and stderr filenames, or random temp locations. Writes
| a code to the file at done_fn when finished. A code of 'DONE' is
| expected. When a code appears in the done file, the stdout and stderr
| files are ready for reading and are complete.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __dict__ =
| dictionary for instance variables (if defined)
|
| __weakref__ =
| list of weak references to the object (if defined)

FUNCTIONS
exteval(expression)
Creates a new process running a new python interpreter, evaluates the
given expression, and returns the result. The result must be basic types,
but may expand in the future to any pickle-capable type.

extexec(source)
Creates a new process, executes the source in a new python interpreter,
and returns a tuple of the stdout and stderr captured.

sleep(...)
sleep(seconds)

Delay execution for a given number of seconds. The argument may be
a floating point number for subsecond precision.

Tuesday, December 05, 2006

Statement Functions

At a small suggestion in #python, I wrote up a simple module that allows the use of many python statements in places requiring statements. This post serves as the announcement and documentation. You can find the release here.

The pattern is the statement's keyword appended with a single underscore, so the first, of course, is print_. The example writes 'some+text' to an IOString for a URL query string. This mostly follows what it seems the print function will be in py3k.

print_("some", "text", outfile=query_iostring, sep="+", end="")

An obvious second choice was to wrap if statements. They take a condition value, and expect a truth value or callback an an optional else value or callback. Values and callbacks are named if_true, cb_true, if_false, and cb_false.

if_(raw_input("Continue?")=="Y", cb_true=play_game, cb_false=quit)

Of course, often your else might be an error case, so raising an exception could be useful.

raise_(TypeError("Wrong datatype!"))

When you do have exceptions, you often want to handle them and we can even do this in expressions now. Maybe we just want to report the error. The try_ function takes keywords for except_, else_, and finally_ callbacks. All take exception type, exception, and traceback arguments, with else_ and _finally also taking first arguments as the result of the attempted callable.

try_(some_func, except_=lambda e,et,tb:print_(et, ':', e))

Errors also can come from failed assertions.

assert_(lambda: True == False)

Loops are also supported. for_ is mostly like map, but it also takes an else_ argument which is called with the last value from the iterator. while_ takes a condition callable, either a for_each callable or an iterator. The condition is given the result of the last loop.

for_("abc", print_, lambda i: print_("done"))

while_(lambda last:last<99, xrange(100), lambda_ i: print_("done"))

Finally, some simple ones:

pass_(10, foo="a") # returns ((10,), {'foo': 'a'})

exec_("a = b", globals(), locals())

I don't recommend using this a whole lot, but in the cases its useful, enjoy.
I write here about programming, how to program better, things I think are neat and are related to programming. I might write other things at my personal website.

I am happily employed by the excellent Caktus Group, located in beautiful and friendly Carrboro, NC, where I work with Python, Django, and Javascript.

Blog Archive