Getting Started

Getting up and running with Perjury generators is very simple.

>>> from perjury.generators import smallint
>>> smallint()
3
>>> smallint()
500
>>> [smallint() for x in range(10)]
[48, 2, 71, 64, 3, 46, 99, 33, 80, 3]

Perjury is built around the concept of callables. All generators provided by Perjury implement the same simple interface.

Note

Calling the generator returns a value

Admittedly perjury.generators.smallint() is not a very impressive generator, so lets jump right into some real use cases. For this example we will be generating a generic user. In order to try and keep this generic, lets use the following schema for what our users will look like.

Note

Users of Django will likely recognize the following as a slightly mutated version of Django’s build in User model. While trying to keep this example simple, we also want to showcase an example that solves a real world problem.

  • User
    • username (unique)
    • hashed password
    • is_active
    • is_superuser

The fist thing we’ll need is a username generator. While there are many ways to do this, we’re going to to use a random choice from a predefined set of usernames.

import random

usernames = ['animal', 'beaker', 'fozzie', ... , 'scooter']
def username():
    return random.choice(usernames)

This is a pretty solid solution for most situations. However, in practice, we could quickly run into some issues with uniqueness across the set of generated usernames. Instead of diving down the rabbit hole of ways to implement uniqueness across our username function, lets use Perjury’s built in function decorator perjury.util.unique(). The simplest implementation works for most cases.

>>> from perjury.util import unique
>>> [username() for a in range(3)]
['animal', 'fozzie', 'animal'] # can (and will) return repeat values
>>> unique_username = unique(username)
>>> [unique_username() for a in range(10)]
['animal', 'fozzie', ...] # will enforce uniqueness across return values.

Note

The unique decorator will just work in most cases. It is however important to read the full documentation on how it works, and how you can configure how uniqueness constraints are enforced.

Of course, Perjury comes with a username generator that should work for most needs. The point of this however is to illustrate that Perjury both provides a very broad set of tools to generate content, and can be used to very easily build your own generator for any kind of data.

We’re going to skip over password for now and move onto our other fields. For our is_active boolean field, we will want to generate some random True and False values, but unevenly distributed. Lets generate 1 inactive user for every 3 active users.

import random

def active():
    return bool(random.randint(0, 3))

Perjury comes with an easy to use weighted_choice() generator. For now, we’ll just use our active function.

Next, lets create our function for generating superuser status. Lets be sure that we only generate one superuser. While there are plenty of functional ways to do this, we’d like to take this time to demonstrate how you can easily write stateful generator callables.

from perjury.generators import BaseGenerator

class SuperuserStatus(BaseGenerator):
    superuser_generated = False

    def generator(self):
        if not self.superuser_generated:
            self.superuser_generated = True
            return True
        else:
            return False

And to use it.

>>> superuser_callable = SuperuserStatus()
>>> superuser_callable()
True
>>> superuser_callable()
False
>>> [superuser_callable() for i in range(5)]
[False, False, False, False, False]

So lets pull this all together into a cohesive user generator. Attentive readers will realize that we’ve left out a generator for the password field. For now however, lets look at what our user generator would look like combining all of our code so far.

import random
from perjury.util import unique

usernames = ['animal', 'beaker', 'fozzie', ... , 'scooter']

@unique
def username():
    return random.choice(usernames)

def active():
    return bool(random.randint(0, 3))

class SuperuserStatus(BaseGenerator):
    superuser_generated = False

    def generator(self):
        if not self.superuser_generated:
            self.superuser_generated = True
            return True
        else:
            return False

superuser_callable = SuperuserStatus()

def user_generator():
    user = User(username=username(), is_active=active(), is_superuser=superuser_callable())
    user.set_password('password')
    return user

Now you may see why we skipped over password. In our slightly fictional example model, instead of computing a password hash ourselves, it is much easier to use the built in API call to set_password to set the hashed password.

None of this is very novel at face value. Most programmers with a bit of experience could hammer out the code above in a short period of time. However, this code tends to be tedious at best and often involves a lot of ‘re-inventing the wheel’ type of code. This is where Perjury comes in to save the day. Lets take a look at an implementation both functionally and class-based.

Functionally:

import itertools

from perjury.generators import username, weighted_choice
from perjury.util import unique

unique_username = unique(username)

def user_generator():
    user = User(
        username=unique_username(),
        is_active=weighted_choice({True: 1, False: 3}),
        is_superuser=itertools.chain([True], itertools.repeat(False))
        )
    user.set_password('password')

Class-Based:

import itertools

from perjury.generators import BaseGenerator, username, weighted_choice

class UserGenerator(BaseGenerator):
    unique = True
    key_fn = lambda u: u.username

    def generator(self):
        user = User(
            username=username(),
            is_active=weighted_choice({True: 1, False: 3}),
            is_superuser=itertools.chain([True], itertools.repeat(False))
            )

Perjury does its best to both provide a very broad set of tools, and ensure that its tools can be re-used and modified to suit your content generation needs. Most of the generators found in Perjury are build off of a small set of functional tools included with Perjury along with some thin wrappers around many of the tools python provides.

Project Versions

Previous topic

Welcome to Perjury’s documentation!

Next topic

Base Module

This Page