Overview

mannequin is very simple.

It is a small library that helps you create declarative models for your own libraries and applications using Python class definitions. Declarative models are a nice way to define the structure of your data or objects. Using Python classes for this keeps it natural and familiar.

If you’ve ever encountered the Python web-framework Django you might be familiar with it’s Models or Forms. Django uses this declarative technique to allow you to naturally define tables in your database:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

Or the types and ordering of fields of your web-forms:

from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

There are other database libraries that use this technique to represent database schemas like SQLAlchemy and Axiom.

Let’s look at a theoretical example of unpacking binary data from structures. With the standard struct module this can be a painful exercise. Given some imagined packet structure, unpacking a binary stream into the various fields is cumbersome:

header = struct.unpack('B',data[0])
length = struct.unpack('B',data[1])
typeID = struct.unpack('!I',data[2:6])
param1 = struct.unpack('!H',data[6:8])
param2 = struct.unpack('!H',data[8:10])
param3 = struct.unpack('!H',data[10:12])
param4 = struct.unpack('!H',data[12:14])
name = struct.unpack('20s',data[14:38])
checksum = struct.unpack('!I',data[38:42])
footer = struct.unpack('B',data[42])

Yikes! Even if we ask struct to unpack all of the fields at once, we are then relegated to numerical indexing. We can use namedtuple but we still have the feeling that there has to be a better way:

fields = struct.unpack('!BBI4H20sIB', data)

fields[0] # get the header

# this might be a more comfortable alternative, perhaps:

(header, length, typeID, param1, param2,
param3, param4, name_string, checksum, footer,
) = struct.unpack("!2B I 4H 24s I B", data)

One could imagine a library that uses the same sort of class-based schema delcaratives that Django does to solve this problem. Here is a hypothetical definition of the same packet structure as above:

class TCPPacket(PacketModel):

    endian = BIG

    header = fields.Byte()
    length = fields.Byte()
    type = fields.Integer()
    params = fields.List(4, fields.Short())
    name = fields.String(20)
    checksum = fields.Integer()
    footer = fields.Byte()

The obvious advantage here is readability. But there are some other not so obvious advantages. The fact that this packet declaration is a class means that it can be subclassed into more specific implementations, perhaps adding additional fields. If we were implementing an application protocol we could implement the header of our protocol in a base class and use that in the actual implementation of our various packet types.

Another advantage is that it keeps the handling of each specific packet close to the structure definition. Each class declarative can contain methods specific to usage inside your application.

Since we are using Field objects to define the types of our various packet fields we also gain the ability to do implicit validation on data. For example, if we had an application protocol that featured an authentication mechanism the Field classes can work harder for us than in the TCPPacket example:

class UsernameField(fields.String):
    def __init__(self):
        # UsernameField provides an implicit length to String
        super(UsernameField, self).__init__(32)

    def clean(self, value):
        try:
            # lookup user in database
            # and return it
            return User.objects.get(username=cleaned)
        except User.DoesNotExist, e:
            # ValidationError indicates this field failed
            # to clean
            raise ValidationError(e.message)

    def validate(self, cleaned):
        # recieves actual user instance from self.clean()
        if not user.active:
            msg = "%s is not a currently activated user." % cleaned.username
            raise ValidationError(msg) # indicate failure to validate

class PasswordField(fields.String):
    def __init__(self):
        # PasswordField provides an implicit length to String
        super(UsernameField, self).__init__(32)

    def validate(self, cleaned):
        user = self.parent.user
        try:
            # check the password for the user
            user.check_password(cleaned)
            # authenticate user if no exception
            user.authenticate()
        except AuthenticationError, e:
            raise ValidationError(e.message)

class LoginPacket(MyAppPacket):
    # MyAppPacket provides MyApp's protocol header fields
    user = UsernameField() # verifies user exists in database
    password = PasswordField() # authenticates user on validation

Getting Started

The easiest way to get started is to checkout the examples in the source repository. It may be beneficial to read about Models and Fields. You may also enjoy the tutorial which describes how to use mannequin to create a declarative XML parser.

Read the Writing Models documentation to get started.

Indices and tables