| |
| I used to think python doctests were the greatest thing since sliced bread. I no longer think this and I've returned to the basics (most of the time): plain old unittests. So let me explain why.
This is the crux of the matter: documents are supposed to be documents and readable as such. Too often I see doctests that stray from readability in favor of including chunks of code which achieve testability - test set up, tear down, white space stripping ... For example, let's look at a part of README.txt in zope.event (and the Zope camp definitely embraces doctests):
The package has a list of subscribers. Application code can manage
subscriptions by manipulating this list. For the examples here, we'll
save the current contents away and empty the list. We'll restore the
contents when we're done with our examples.
>>> import zope.event
>>> old_subscribers = zope.event.subscribers[:]
>>> del zope.event.subscribers[:]
This isn't documentation! You're wasting time (your time and mine) explaining something to me that has nothing to with how to use the interface. doctest does provide facilities for executing setUp and tearDown functions in regular python modules prior to executing a test, but I seldom see this approach taken. Why? Well I guess this requires more work, and it seems the appeal with doctests is doing less work. I can relate, and even took a crack once at automating specification of setUp/tearDown functions: http://xix.python-hosting.com/file/trunk/xix/utils/test.py
.
And while we're on the subject of code I've written, here's another example of a bad doctest:
>>> action.addMessageCategoryCallback(thread1, t1)
>>> action.addMessageCategoryCallback(thread2, t2)
>>> action.addMessageCategoryCallback(metaname, mname)
>>> from loggrok.log import LogStream
>>> stream = LogStream(fname)
>>> stream.messageParser = parser
>>> stream.action = action
>>> for entry in stream:
... continue
...
====
THREAD1 C
D E
F G
H I
====
THREAD1 J K
====
THREAD2 L M
N
====
WTF? This isn't a document! This is a plain old unittest, disguised as a document! Stop wasting my time!
Okay, so I've been a little harsh. I've tried hard to rant as little as possible on this blog, but today I've fallen short of that goal. But please don't get what I'm saying wrong. I'm not asserting that doctests are useless and you should never use them. (Har har! Get it? "I'm not asserting ...") In fact doctests can be great but I don't think they're effective for fully testing an application - since this will always conflict with writing apt and succinct documentation. What they are good for, if I can suggest, is to verify that examples in a documentation don't contain syntactic errors and are up to date with the current code base. Of course some setUp and tearDown routines may be needed to ensure the examples actually work or don't leave the interpreter in bad state when complete, but there are several approaches to not revealing this in the document (for example, put the setUp/tearDown in comments or use the facilities providing by the doctest library to execute such functions).
On side note, it really isn't worth fussing about, at least on a policy level. If someone on your team is writing unittests (be it doctests or old school unittest), it's best to hold your lip and not engage in bickering about what format they should be in. What matters most is that the application is being tested and the project is approaching good line and combinatorial branch coverage. | |
|
| Manipulating an object's world coordinates with OpenGL is tricky. This is an operation which involves translating the screen coordinates to a position in the world. The "tricky" part of this is determining the depth of the object in the range [0,1] to use as the wz argument to gluUnProject (a function that does the hard screen coordinate to world coordinate transformation for us). Unfortunately, googling for gluUnProject yields some very bad results. For example the NeHe tutorial gives a wretched example using glReadPixels with GL_DEPTH_COMPONENT as the format. This is a really, really stupid idea. First of all, imagine an object is in focus (I've selected it as the target from a drop down menu, or some other way), and then I click on some arbitrary point on the screen. Well, guess what kids? I get the window depth at that point (not the point corresponding to the object I'm interested in). Even if I point directly over the object, it's easy for the mouse to race ahead of the object while dragging it across the screen, and (boom!) the window z depth is 1.0, or the far clipping plane, and my object flies of the friggin' screen. Beautiful! Other links that crop up literally hard code the wz depth value which only makes sense if you're manipulating an object sitting directly on the near clipping plane. So, here's how you do it, and it's very simple. To determine the window depth of the object, first do the reverse of gluUnProject (that is, gluProject) based on the coordinates of the object in focus. Here's my final (working) code in python:
viewport = (GLint * 4)()
mvmatrix = (GLdouble * 16)()
projmatrix = (GLdouble * 16)()
glGetIntegerv(GL_VIEWPORT, viewport)
glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix)
glGetDoublev(GL_PROJECTION_MATRIX, projmatrix)
wx = GLdouble()
wy = GLdouble()
wz = GLdouble()
sz = GLdouble()
ex = target.pos.x
ey = target.pos.y
ez = target.pos.z
gluProject(ex, ey, ez, mvmatrix, projmatrix, viewport, wx, wy, sz)
gluUnProject(x, y, sz,
mvmatrix, projmatrix, viewport, wx, wy, wz)
...
target.pos.x = wx.value
target.pos.y = wy.value
| |
|
| The following is a simple server that exposes two commands (Echo and Foo). The responder for Foo, in addition to returning an empty dict, will try to call Echo on the client.
ampbidir.py
from twisted.internet import reactor
from twisted.protocols import amp
class Foo(amp.Command):
response = []
class Echo(amp.Command):
arguments = [('value', amp.String())]
response = [('value', amp.String())]
class TheServer(amp.AMP):
def foo(self):
def e(v):
print '[TheServer]', self.transport.getPeer(), 'echoed', v
self.callRemote(Echo, value='hello').addCallback(e)
return {}
Foo.responder(foo)
def echo(self, value):
print 'Echo:', value
return {'value':value}
Echo.responder(echo)
def main():
from twisted.internet import reactor
from twisted.internet.protocol import Factory
pf = Factory()
pf.protocol = TheServer
reactor.listenTCP(1234, pf)
reactor.run()
if __name__ == '__main__':
main()
The client will simply start up and call Foo to demonstrate bidirectional support in amp:
client.py
from ampbidir import TheServer, Foo
from twisted.internet.protocol import ClientCreator
from twisted.internet import reactor
client = ClientCreator(reactor, TheServer).connectTCP(
'127.0.0.1', 1234).addCallback(lambda p: p.callRemote(Foo))
reactor.run()
In the above example, we expose Foo and Echo from the client to the server by creating the client with the protocol TheServer, rather than the generic amp.AMP which exposes nothing, of course.
| |
|
|
from twisted.python import filepath
import mmpython as mmp
import time
EXT = '.mp3'
TEMP = '%2.2d_%s_-_%s_-_%s' + EXT
NESTED = True
def rp(s):
return s.replace('\x00', '').replace(' ', '_').replace(':','').encode('utf-8')
def job(desc):
info = mmp.parse(desc.path)
if not info:
print 'WARNING: no info found on', desc
return 'No info found on: %r' % desc
name = TEMP % (int(info.trackno), rp(info.title), rp(info.artist), rp(info.album))
print 'Parsed', desc.basename(), 'as', name
if NESTED:
fp = filepath.FilePath(rp(info.artist), rp(info.album))
tgt = tgt_dir
for c in (rp(info.artist), rp(info.album)):
tgt = tgt.child(c)
try:
tgt.createDirectory()
except:
pass
tgt = tgt.child(name.encode('utf-8'))
else:
tgt = tgt_dir.child(name.encode('utf-8'))
return desc.copyTo(tgt)
def main():
for desc in [f for f in root.walk() if f.isfile()]:
job(desc)
if __name__ == '__main__':
import sys
root = filepath.FilePath(sys.argv[1])
try:
tgt_dir = filepath.FilePath(sys.argv[2])
except:
tgt_dir = filepath.FilePath('_ip-recover-%d' % int(time.time() * 1000000))
tgt_dir.createDirectory()
main()
| |
|
| Up until now, the easy way to write Python 2.4 function decorators that takes arguments before the decoratorated function has been a complete mystery to me. Writing a decorator that only takes its wrapped function as the first agument is pretty straight-forward. Alas, just a function that wraps another. Take for example, a function that transforms another into a curryable function:
from xix.utils.python import Curried
# a utility function to give the decorator the same metainfo as the decorated
def _applymeta(wrapper, func):
wrapper.__dict__.update(func.__dict__)
wrapper.__doc__ = func.__doc__
wrapper.__name__ = func.__name__
return wrapper
# the decorator definition
def curryable(func):
def decorator(*args, **kwargs):
curried = Curried(func)
return curried(*args, **kwargs)
return _applymeta(decorator, func)
A decorator that takes additional arguments before the wrapped function can be though of as a decorator decorator, and so can involve three levels of nested functions - 1) a function that wraps the decorator (taking the initial argument), 2) the decorator/wrapper, and 3) the inner wrapper function:
def foo(*dpargs, **dkwargs):
def decorator(func):
def inner(*pargs, **kwargs):
print 'pargs:', pargs
print 'kwargs:', kwargs
print 'decor pargs:', dpargs
print 'decor kwargs:', dkwargs
return func(*pargs, **kwargs)
return _applymeta(inner, func)
return decorator
Applying:
@foo(1,'a',k=23)
def mydef(a,b,z=34):
"""Documentation for mydef"""
print a, b
| |
|
|