October 26th, 2009 Add Your Comments

I ran across something today that I found to be extremely interesting. Basically, the authors of Should_DSL, http://pypi.python.org/pypi/should_dsl/1.2.1, have attempted and succeeded in creating pseudo-operators based on operator-functions within an object.

Let me elaborate.

The goal of Should-DSL is to write should expectations in Python as clear and readable as possible, using “almost” natural language (with limitations from Python language).

And to be honest, they’ve got it all right.

Python limits function calls to callable objects through the () notation. However, custom functors can also have their own __call__ method invoked when an operator or a reverse operator is used on them. Take for example the following code

class WeirdFunc(object):
    def __init__(self, func):
        self.func = func
    def __radd__(self, arg):
        return self(arg)
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

@WeirdFunc
def one(other):
    return other+1

print "'3+one' will evaluate to %s"%(3+one)

Will yield ‘3+one’ will evaluate to 4

So taking this to a whole new level, the folks at Should_DSL have created their assertion operators to be as semantic as possible. The syntax taking form to look something like

from should_dsl import *
import unittest

class UsingShouldExample(unittest.TestCase):
...     def test_showing_should_not_be_works(self):
...         'hello world!' |should_not_be| 'Hello World!'
...
...     def test_showing_should_have_fails(self):
...         [1, 2, 3] |should_have| 5
...
...     def test_showing_should_have_works(self):
...         'hello world!' |should_have| 'world'
...
...     def test_showing_should_not_have_fails(self):
...         {'one': 1, 'two': 2} |should_not_have| 'two'
...
...     def test_showing_should_not_have_works(self):
...         ["that's", 'all', 'folks'] |should_not_have| 'that'

from cStringIO import StringIO
runner = unittest.TextTestRunner(stream=StringIO())
suite = unittest.TestLoader().loadTestsFromTestCase(UsingShouldExample)
runner.run(suite)

Notice that in place of self.assert*, the tests instead uses x |should_[not]_be| y, which while verbose, is intuitive when placed in the context of running tests (which are by convention much more tolerant of verbosity).

So how do they do it? The key here is that the ‘|’ operator can be functionalized as the __or__ and __xor__ method of any given object. Thus we can cache the values passed to __or__ (the second argument) and __xor__ (the first argument) and then pass that back to the __call__ method.

We can easily prototype a decorator class for this pseudo-operator object

class Operator(object):
    def __init__(self, func, count=2):
        self._func_ = func
        self._args_ = []
        self._count_ = count
    def __ror__(self, first_arg):
        self._args_.append(first_arg)
        return self
    def __or__(self, arg):
        self._args_.append(arg)
        if len(self._args_) >= self._count_:
            return self(*self._args_)
        else:
            raise RuntimeError("Incorrect number of parameters")
    def __call__(self, *args, **kwargs):
        self._args_ = []
        return self._func_(*args, **kwargs)

Here the variables are cached in the obj._args_ list, which is unpacked as the parameters of the __call__ method in the second ‘|’. An example usage would be as follows:

@Operator # Turns the plus function into the |plus| operator #
def plus(first, second):
    return first+second

print 1 |plus| 2
# Will return 3

Happy Coding