Why Pisces?

Pisces is a Python web framework that was written with testing in mind. It was birthed as a reaction to the typical workflow in a Django project, which I've come to dislike as it encourages you to make your code tightly coupled and less testable.

What's wrong with Django?

The majority of my issues with Django can be summed up with one code block pulled from the Sprint.ly code base which is indicative of how I've seen things work elsewhere.

@render_to('accounts/invoices.html', ajaxable=False)
def invoices(request):
    account = get_object_or_404(Account, user=request.user)
    transactions = Transaction.objects.filter(
        account=account, status__in=[Transaction.STATUS_FAILED,

    return {
        'account': account,
        'transactions': transactions

Clocking in at 14 lines of code, it encompasses an impressive amount of work. This block of code assures the user is logged in, checks the access credentials to verify they are an account admin, fetches their account and transactions and returns a data structure which is passed off to a template for rendering. To sum it up, we have presentation concerns, database access, ACL checking, HTTP layers, possible JSON serialization, and authentication all layered in here.

A simple test that we might want to validate is that our Transaction fetching doesn't return STATUS_REFUNDED entries. The test setup for this is a database, various HTTP handlers, a django Request object which has an associated User object. We'll also need to setup particular users with the right access levels and either short circuit the templating rendering, or we'll have to do HTML based matches to find whether or not the entry was returned. This is a lot of effort to test something which should be as simple as throwing a few things into a database and validating they return when we make the call.

This gets even harrier when we're trying to test something like if we throw a 404 if the account isn't found. To test this, we need an entire in-memory database (slow) or add in layers of complex mocking (brittle) to actually test what we want.

How is pisces different?

Pisces addresses this problem through breaking this out into service classes which represent business logic and backend queries that need to be run from Endpoints, which ferry input parameters from the HTTP layer into these calls. You can find an example of what this looks like in the README.

With pisces, we express the dependencies we have on other systems through dependency injection. This has a strong advantage in that it allows you to transparently provide fake implementations of services. In the example provided in pisces's README, we use a Redis backed data storage layer. For view testing, it would be much easier if we didn't have to even have Redis installed. To accomplish this, we can simply provide a class with the relevant methods and use a dict-based datastore to achieve the same affect.

These faked data stores aren't only useful in testing. One fantastic use for faking data access with in-memory data structures is it allows you to get someone cranking on the UI of your app with a dumb data store while you work through a more sophiticated implementation. It also allows you to validate that your thinking of the problem is correct without spending large amounts of time with the details of the implementation.

What's next? How can I help?

Pisces is definitely in its infancy, born from a few weeks of thinking and a weekend of hacking. Its API is still shaky and there is a lot that remains to be done. In no particular order, the things I want to see happen is:

  • An abstraction on serialization to support other formats (including HTML rendering)
  • A more fully-baked routing scheme that better escapes regular expressions and supports nesting
  • Examples and tutorials about implementing common patterns in web development such as authentication.
  • Email me to tell me this is of interest to you. Give me your opinion and feedback. justin@abrah.ms
© 2012 - 2023 · Home — Theme Simpleness