Fork me on GitHub

PyContracts

PyContracts is a Python package that allows to declare constraints on function parameters and return values. It supports a basic type system, variables binding, arithmetic constraints, and has several specialized contracts (notably for Numpy arrays).

Status: PyContracts is very well tested and documented. The last release is version 1.2.0 (November 2011). See changelog.

Why: The purpose of PyContracts is not to turn Python into a statically-typed language (albeit you can be as strict as you wish), but, rather, to avoid the time-consuming and obfuscating checking of various preconditions. In fact, more than the type constraints, I found useful the ability to impose value and size constraints. For example, “I need a list of at least 3 positive numbers” can be expressed as list[>=3](number, >0)). If you find that PyContracts is overkill for you, you might want to try a simpler alternative, such as typecheck. If you find that PyContracts is not enough for you, you probably want to be using Haskell instead of Python.

Contracts can be specified in three ways:

  • Using annotations (for Python 3) —this is perhaps the most intuitive way:

    @contract
    def my_function(a : 'int,>0', b : 'list[N],N>0') -> 'list[N]':
         # Requires b to be a nonempty list, and the return
         # value to have the same length.
         ...
  • Using :type: and :rtype: tags in docstrings. In this way, they will be included in your Sphinx documentation:

    @contract
    def my_function(a, b):
        """ Function description.
            :type a: int,>0
            :type b: list[N],N>0
            :rtype: list[N]
        """
        ...
    
  • Using arguments to the decorators; the least intrusive way:

    @contract(a='int,>0', b='list[N],N>0', returns='list[N]')
    def my_function(a, b):
        ...
    

Moreover, there are utility functions for manual checking of values:

check('array[HxWx3](uint8),H>10,W>10', image)

as well as hooks to extend PyContracts with new contracts types:

new_contract('valid_name', lambda s: isinstance(s, str) and len(s)>0)
check('dict(int: (valid_name, int))', employees)

Support: use the GitHub issue tracker or email me.

Documentation index

Installation

Install PyContracts using:

$ pip install PyContracts

or from GitHub:

$ git clone git://github.com:AndreaCensi/contracts.git
$ cd contracts
$ python setup.py develop
$ nosetests -w src         # run the extensive test suite

The beautiful library pyparsing is required.

Quick tour

The contracts are specified using the type clause of RST-style docstrings (now accepted as standard in the python libraries); or, they can be passed explicitly to the @contract decorator. In this example, PyContracts is smart enough to check that the two parameters a and b are matrices of compatible dimensions. Then, it checks that the result value is of compatible dimensions as well.

import numpy
from contracts import contract

@contract
def matrix_multiply(a,  b):
    ''' Multiplies two matrices together.

        :param a: The first matrix. Must be a 2D array.
         :type a: array[MxN],M>0,N>0

        :param b: The second matrix. Must be of compatible dimensions.
         :type b: array[NxP],P>0

          :rtype: array[MxP]
    '''
    return numpy.dot(a, b)

PyContracts can come in handy when you have operations that could be one-liners if you are sure of the types of the parameters, but doing all the checking adds to the complexity of the code.

In the next example we check that:

  • The two lists have elements of the same type (indicated by the variable x);

  • The returned list has the correct size (the sum of the two lengths).

    @contract(      a='list[ M ](type(x))',
                     b='list[ N ](type(x))',
               returns='list[M+N](type(x))')
    def my_cat_equal(a, b):
        ''' Concatenate two lists together. '''
        return a + b
    

The philosophy is to make the simple cases easy, and the difficult possible, while still retaining readability.

For example, we can either ask for a simple list, or specify more about it using the additional clauses.

Contract expression Meaning
list An instance of list.
list[2] A list of two elements.
list(int) A list of integers.
list(number) A list of numbers.
list[3](number) A list of exactly three numbers.
list[>=3](number) A list of at least three numbers.
list[>=3](number, >0) A list of at least three numbers, greater than 0.

PyContracts supports the use of variables. There are two kinds of variables: lower-case letters (a, b, ...) are general-purpose variables, while upper-case letters (A, B, ...) are constrained to bind to integer types; they are meant to represent sizes and shapes. Moreover, PyContracts can do arithmetic and comparisons.

Contract expression Meaning
tuple(list[N], list[N]) A tuple with two lists of the same length.
tuple(list[N], list[M]), N<M A tuple with two lists, the first one being shorter.
list[>0](type(x)) A non-empty list containing elements of all the same type.
tuple(list(type(x)), list(type(x))) A tuple with two lists containing objects of the same type.

For the complete reference to the available contract expressions, see Language reference.

PyContracts API

This is a discussion of the PyContracts API.

  • See contracts for a detailed list of this module’s public interface.
  • See Language reference for a description of the domain specific language used to describe the contracts.

Using the @contract decorator.

The decorator contracts() is considered the main way to define constraints. It is quite flexible, and it is smart enough to support functions with variable number of arguments and keyword arguments.

There are three ways to specify the contracts. In order of precedence:

  • As arguments to this decorator.
  • As Python 3 function annotations.
  • Using :type: and :rtype: tags in the function’s docstring.

PyContracts will try these options in order. Note that, in any case, only one of these options are chosen. For example, you cannot use both annotations and docstring for the same function: if annotations are found, the docstring is not considered.

Using decorator arguments

contract() accepts a list of keyword arguments. Each keyword should correspond to one function argument, plus the special name returns is reserved for describing the return value. An example use would be:

from contracts import contract

@contract(a='int,>0', b='list[N],N>0', returns='list[N]')
def my_function(a, b):
    ...

The values can be either:

  • Strings using PyContracts‘ DSL language (see Language reference)

  • Python types — in this case PyContracts will do a simple isinstance() check. This is slightly more clear if the contract is simple:

    @contract(a=int, b=float, returns=float)
    def my_function(a, b):
        return a + b
    

Using Python annotations

The same rules apply. In this case the syntax would look like this:

from contracts import contract

@contract
def my_function(a:'int,>0', b:'list[N],N>0') -> 'list[N]':
    ...

Using functions docstrings

The Python standard library seems to have standardized on the :param:, :type:, :return:, :rtype: tags to document functions, and tools like Sphinx can interpret those tags to produce pretty documentation.

PyContracts can read contracts declared using the :type: and :rtype: tags. In this way, your function becomes automatically more robust and better documented.

Here is an example use:

from contracts import contract

@contract
def my_function(a, b):
  """ Function description.

      :param a: first number
      :type a: int,>0
      :param b: description of b
      :type b: list[N],N>0

      :return: a list
      :rtype: list[N]               """
  ...

Note

By convention, those annotations must be parsable as reStructuredText. If the contract string has special RST characters in it, like *, you can include it in double ticks. PyContracts will remove the double ticks before interpreting the string.

For example, the two annotations in this docstring are equivalent for PyContracts, but the latter is better for Sphinx:

""" My function

    :param a: First parameter
    :type a: list(tuple(str,*))

    :param b: First parameter
    :type b: ``list(tuple(str,*))``
"""

Decorating a function explicitly

The public interface of PyContracts contains a function decorate() that can be used to decorate functions after they are defined. For example:

from contracts import decorate

def f(a):
    a.pop()
    return a

f2 = decorate(f, a='list[N],N>1', returns='list[N-1]')

f2([]) # raises exception before pop() is executed

Manual API

In addition to the @contract() decorator, PyContracts offers an interface for checking expressions explicitly.

The check() function allows you to check that some value respect a given contract.

from contracts import check

check('tuple(int,int)', (2, 2) )

check() might raise:

ContractSyntaxError
If there are parsing errors.
ContractNotRespected
If the value does not respect the contract constraints.

Both exceptions are instances of ContractException. Usually you want to use check() as a better assert(), so you don’t want to catch those exceptions. The error message should be descriptive enough.

For example, the message generated by

check('tuple(int, tuple(*,str))', (0, (1, 2)))

is

contracts.ContractNotRespected: Expected a string, got 'int'.
 context: checking: str                       for value: Instance of int: 2
 context: checking: tuple(*,str)              for value: Instance of tuple: (1, 2)
 context: checking: tuple(int,tuple(*,str))   for value: Instance of tuple: (0, (1, 2))

The message shows the recursive matches of expressions, and should be enough to diagnose the problem. Nevertheless, you can add a third argument to specify a message that will be included in the error, if one is thrown. For example:

score = (2, None)
check('tuple(int,int)', score, 'Player score must be a tuple of 2 int.' )

Interpreting error messages

In the case you are using variables, the matching “context” is shown. The context is the set of variables which have already bound to values.

For example, suppose you want a tuple with two lists of equal length. This can be expressed as:

value = ([0,1],[1,2,3]) # invalid
check('tuple(list[N], list[N])', value)

What happens behind the scenes is that PyContracts first matches the first list, binds the variable N to the length of that list (=2), then checks that list[N] describes the second list. Now, however, N has already been bound, and the matching fails. The error you would see is:

contracts.ContractNotRespected: Expected that 'N' = 2, got 3.
checking: N                        (N=2)   for value: Instance of int: 3
checking: list[N]                  (N=2)   for value: Instance of list: [1, 2, 3]
checking: tuple(list[N],list[N])           for value: Instance of tuple: ([0, 1], [1, 2, 3])

PyContracts tries hard to give you a readable message even with complicated contracts. For example, consider the complicated expression:

check('int|str|tuple(str,int)', ('a', 'b'))

Because there is an OR (|), PyContracts will have to explain to you why none of the three OR clauses was satisfied.

contracts.interface.ContractNotRespected: Could not satisfy any of the 3 clauses in int|str|tuple(str,int).
 ---- Clause #1:   int
 | Expected type 'int', got 'tuple'.
 | checking: int      for value: Instance of tuple: ('a', 'b')
 ---- Clause #2:   str
 | Expected a string, got 'tuple'.
 | checking: str      for value: Instance of tuple: ('a', 'b')
 ---- Clause #3:   tuple(str,int)
 | Expected type 'int', got 'str'.
 | checking: int                 for value: Instance of str: 'b'
 | checking: tuple(str,int)      for value: Instance of tuple: ('a', 'b')
 ------- (end clauses) -------
checking: int|str|tuple(str,int)      for value: Instance of tuple: ('a', 'b')

Explicit parsing and checking

The results of parsing are cached internally, there is no performance penalty in using check() often.

If you wish, you can also separate the parsing and checking phases. The function parse() will return a Contract object, which in turn has a Contract.check() function you can call.

from contracts import parse

contract = parse('>0')  # May raise ContractSyntaxError
contract.check(2)       # May raise ContractNotRespected

Checking multiple values

The most interesting uses of PyContracts are when it is used to check that multiple objects respect mutual constraints.

You can use check_multiple() to check that a series of values respect a series of contracts, that might reference each other’s variables. The argument to check_multiple() must be a list of tuples with two elements (contract, value).

In the next example, we are defining a table. We want data to be a list of lists with equal length, and that row_labels and col_labels have the expected length, coherent with data. PyContracts allows you to do all this checks with a remarkably clear, concise, and readable notation.

from contracts import check_multiple

data = [[1,2,3],
        [4,5,6]]
row_labels = ['first season', 'second season']
col_labels = ['Team1', 'Team2', 'Team3']

check_multiple([('list[C](str),C>0', col_labels),
                ('list[R](str),R>0', row_labels),
                ('list[R](list[C](numbers))', data) ],
               'I expect col_labels, row_labels, and data to '
               'have coherent dimensions.')

Examining the Contract objects

If you want to examine the contract object, use Contract.__repr__() for eval()uable expression, or Contract.__str__() to get back a representation using PyContracts‘ language.

from contracts import parse

contract = parse(' list[N](int), N > 0')

print("%s" % contract)   # => 'list[N](int),N>0'
print("%r" % contract)
# => And([List(BindVariable('N',int),CheckType(int)),
#         CheckOrder(VariableRef('N'),'>',SimpleRValue(0))])

The latter is useful if you are hacking PyContracts and you want to check in detail what is going on.

Examining bound variables

If you are using binding expression, you can examine what variables were bound using the return values. The functions check() and Contract.check() will return a Context object.

context = check('list[N](str), N>0', ['a','b','c'])

print('N' in context)  # => True
print(context['N'])    # => 3

Extending PyContracts with new contracts

Defines a new contract type. The second parameter can be either a string or a callable function.

  • If it is a string, it is interpreted as contract expression; the given identifier will become an alias for that expression.

  • If it is a callable, it must accept one parameter, and either:

    • return True or None, to signify it accepts.
    • return False or raise ValueError, to signify it doesn’t.

    If ValueError is raised, its message is used in the error.

This function returns a Contract object. It might be useful to check right away if the declaration is what you meant, using Contract.check() and Contract.fail().

For example, suppose that you are writing a graphical application and that many of your functions need arguments representing colors. It might be a good idea to declare once and for all what is a color, and then reuse that definition. For example:

color = new_contract('color', 'list[3](number,>=0,<=1)')
# Make sure we got it right
color.check([0,0,0])
color.check([0,0,1])
color.fail([0,0])
color.fail([0,0,2])

# Now use ``color`` in other contracts.
@contract
def fill_area(inside, border):
    """ Fill the area inside the current figure.

        :type border: color
        :type inside: color              """
    ...

@contract
def fill_gradient(colors):
    """ Use a gradient to fill the area.

        :type colors: list[>=2](color)     """
    ...

Using PyContracts in your own PyParsing-based language

You can use contract_expression to add a contract expression to your PyParsing-based grammar:

from contracts import contract_expression

from pyparsing import Literal, Optional

my_grammar = ... + Optional(Literal('respects') + contract_expression)

Language reference

This section describes PyContracts‘ language for specifying contracts.

Simple types

The simplest kind of contract consists in declaring the type of a value. These are the supported primitive types:

dict, list, tuple, float, int, number, array, bool, None

Moreover, there are two kinds of wild-cards:

*
Matches any object.
#
Never matches anything — useful for debugging.

Variables

PyContracts allows to use variables to bind to sub-expressions and reuse later (it is simpler than it sounds).

There are two kinds of variables. The first kind is denoted by an uppercase letter, and it is constrained to bind to integer values.

A B C D E F G H I J K L M N O P Q R S T U W V X Y Z

Lower-case letters denote general-purpose variables that can bind to any type:

a b c d e f g h i j k l m n o p q r s t u w v x y z

Note

The reason for having specialized variables for integers is to encourage a writing style in which uppercase letters denote lengths, shapes, etc. Moreover, an error will be thrown if they do not bind to integers, which helps in catching mistakes.

Logical expressions

Two logical operators are supported:

AND

Expressed with a comma (,).

For example, the expression

check("contract1,contract2", value)

is roughly equivalent to

check("contract1", value) and check("contract2", value)

except that variables in the contracts are evaluated in the same context.

OR

Expressed with a pipe symbol (|).

The semantic is that the first matching expression is chosen.

The expression:

check("contract1|contract2", value)

is roughly equivalent to:

if value respects "contract1":
   return True
elif value respects "contract2":
   return True
else:
   return False

The AND has precedence over OR. For example, the expression

a,b|c

is evaluated the same as

(a, b) | c
Examples of contracts with logical operators
Contract expression Meaning
None|int Either None or an integer.

Equality

This section must still be written.

Arithmetic and comparison

This section must still be written.

Details of variable binding

This section must still be written.

Lists

You can specify that the value must be a list, and specify optional constraints for its length and for its elements.

The general syntax is:

list
list[length_contract]
list(elements_contract)
list[length_contract](elements_contract)
Examples of list contracts
Contract expression Meaning
list An instance of list.
list[2] A list of two elements.
list(int) A list of integers.
list(number) A list of numbers.
list[3](number) A list of exactly three numbers.
list[>=3](number) A list of at least three numbers.
list[>=3](number, >0) A list of at least three numbers, greater than 0.
tuple(list[N], list[N]) A tuple of two lists of the same length.

Sequences

The contract seq has the same syntax as list but matches any sequence.

seq
seq[length_contract]
seq(elements_contract)
seq[length_contract](elements_contract)

Tuples

You can either specify a length (with square brackets), or specify a contract for each element:

tuple

tuple[length]

tuple(element1, ..., elementN)

In the latter case, you are also specifying implicitly the number of elements.

Note

The syntax for tuples is somewhat a special case. While list(int,>0) signifies a list of positive integers (or empty list), tuple(int, >0) means a tuple with exactly 2 elements, the first of which should be an integer, and the second must be positive (but not necessarily integer.)

Examples of tuple contracts
Contract expression Meaning
tuple A tuple.
tuple[2] A tuple with two elements.
tuple(*,*) A tuple with two elements.
tuple(int) A tuple with one integer element.
tuple(int, int) A tuple with two integer elements.
tuple[>=2] A tuple with at least two elements.

Dictionaries

For dictionary, you can specify a length contract, as well as a contract for its keys and values:

dict

dict[length_contract]

dict(key_contract: value_contract)

dict[length_contract](key_contract: value_contract)
Examples of dict contracts
Contract expression Meaning
dict Any dictionary.
dict[2] A dictionary with two elements.
dict(*: *) Any dictionary.
dict(*: int) A dictionary whose values are integers.
dict(str: *) A dictionary whose keys are strings
dict(str: tuple(type(x),type(y)) ), x!=y A dictionary whose keys are strings, and whose values are tuples with two elements, of two different types x and y.

Mappings

The contract map has the same syntax as dict but matches any mapping.

map

map[length_contract]

map(key_contract: value_contract)

map[length_contract](key_contract: value_contract)

Numpy arrays

The support for Numpy arrays was one of the motivations for me to develop PyContracts. Numpy is one of the best and most useful Python packages around. It supports the ndarray datatype which allows for operations with tensors of arbitrary dimensions.

All of that is very powerful, but it might be a bit confusing, especially because Numpy tends to be very liberal when it gets to operations between arrays. For example, the operation A * B, if A is a 2D matrix, is well defined when B is a scalar, a vector, or a matrix. Sometimes you want to be certain of your assumptions, otherwise you risk of ignoring powerful bugs.

Note

Researchers tend to be a bit paranoid, because often you don’t know what to expect out of your algorithms. A stupid bug can lead to an exciting (false) discovery!

So PyContracts offers several shortcuts for Numpy arrays, and the error messages tend to be more descriptive.

The general syntax looks like this:

array

array[shape_contract]

array(numpy_contract)

array[shape_contract](numpy_contract)

Shape contracts

A shape contract is specified using a special syntax of the kind:

dimension1 x dimension2 x dimension3

dimension1 x dimension2 x dimension3 x ...

The ellipsis is part of the syntax and specifies that more dimensions are allowed.

Each dimension can be specified as a number, with variables, or as a contract.

Examples of array shape contracts
Contract expression Meaning
array Any array.
array[3] A 1D array with 3 elements (shape=(3,)).
array[3x2] A 2D array with 3 rows and 2 columns (shape=(3,2)).
array[3 x ...] A nD array with 3 rows and arbitrary other dimensions.
array[3xN], N>=2 A 2D array with 3 rows and at least 2 columns.
array[NxN], N>0 A square matrix.
array[NxNx...], N>0 A tensor with the first two dimensions of equal size.
list(array[NxN]), N>0 A list of square matrices of the same size.

Numpy-specific contracts

A numpy contract is special and implemented separately from the other PyContracts contract. Right now, we support:

Datatype contracts

These specify the datatype of the array. Available:

uint8 uint16 uint32 uint64 int8 int16 int32  int64 float32 float64
Arithmetic comparisons

These have the semantics that all elements must satisfy the contract. They appear similar to the normal PyContracts comparison:

>0   <0   <=1   =0
Examples of array elements contracts
Contract expression Meaning
array Any array.
array(>=0) An array with nonnegative elements.
array(int8,>=0) An array with nonnegative integer elements.
array[NxN](float,>=0,<=1) A square matrix with float elements in the interval [0,1].
array[NxN](int,(0|1)) A square matrix with each element equal either to 0 or 1 (this could model a directed graph).

Other examples

Here is an example that first uses new_contract() to specify a domain-specific array type (rgb, rgba), then uses PyContracts to be sure that the two given images and the result values have coherent dimensions. Notice how the contracts serve very well as documentation.

from contracts import contract, new_contract

new_contract('rgb',  'array[HxWx3](uint8),H>0,W>0')
new_contract('rgba', 'array[HxWx4](uint8),H>0,W>0')

@contract
def blend_images(image1, image2):
    '''
         Blends two images together.

         :param image1: The first image to blend.
         :type image1: rgb,array[HxWx*]

         :param image2: The second image to blend. Can have an alpha layer.
         :type image2: (rgb|rgba),array[HxWx*]

         :return: The blended image.
         :rtype: rgb,array[HxWx3]
    '''
    ...

If you want to have a function that accepts a list of images, you would write:

@contract
def blend_images(images):
    '''
         Blends a series of images together.

         :type images: list[N](rgb,array[HxWx*]), N>=2

         :rtype: rgb,array[HxWx3]
    '''
    ...

Advanced topic: the context isolate operator

PyContracts has a special construct $(...) used to separate the context of variables. The semantics is that the variables assigned inside $(...) are not propagated outside.

Examples of $() contracts
Contract expression Meaning
list(tuple(type(x),type(y)),x!=y) A list whose elements are tuples with two elements, of two different types x and y (x and y are common among the list elements ).
list( $( tuple(type(x),type(y)),x!=y) ) A list whose elements are tuples with two elements, of two different types x and y. (x and y are not joined among the list elements ).

PyContracts public API reference

This section provides a reference to PyContracts‘ public interface.

Decorating a function

contracts.contract(*arg, **kwargs)

Decorator for adding contracts to functions.

It is smart enough to support functions with variable number of arguments and keyword arguments.

There are three ways to specify the contracts. In order of precedence:

  • As arguments to this decorator. For example:

    @contract(a='int,>0',b='list[N],N>0',returns='list[N]')
    def my_function(a, b):
        # ...
        pass
    
  • As annotations (supported only in Python 3):

    @contract
    def my_function(a:'int,>0', b:'list[N],N>0') -> 'list[N]': 
        # ...
        pass
  • Using :type: and :rtype: tags in the function’s docstring:

    @contract
    def my_function(a, b): 
        """ Function description.
            :type a: int,>0
            :type b: list[N],N>0
            :rtype: list[N]
        """
        pass
    

Signature and docstrings: The signature of the decorated function is conserved. By default, the docstring is modified by adding :type: and :rtype: definitions. To avoid that, pass modify_docstring=False as a parameter.

Contracts evaluation: Note that all contracts for the arguments and the return values are evaluated in the same context. This make it possible to use common variables in the contract expression. For example, in the example above, the return value is constrained to be a list of the same length (N) as the parameter b.

Using docstrings Note that, by convention, those annotations must be parseable as RestructuredText. This is relevant if you are using Sphinx. If the contract string has special RST characters in it, like *, you can include it in double ticks. PyContracts will remove the double ticks before interpreting the string.

For example, the two annotations in this docstring are equivalent for PyContracts, but the latter is better for Sphinx:

""" My function 

    :param a: First parameter
    :type a: list(tuple(str,*))
    
    :param b: First parameter
    :type b: ``list(tuple(str,*))``
"""
Raise :ContractException, if arguments are not coherent
Raise :ContractSyntaxError
contracts.decorate(function, modify_docstring=True, **kwargs)

An explicit way to decorate a given function. The decorator decorate() calls this function internally.

Enabling/disabling

contracts.disable_all()

Disables all contracts checks.

Exceptions

class contracts.ContractException

The base class for the exceptions thrown by this module.

class contracts.ContractSyntaxError(error, where=None)

Exception thrown when there is a syntax error in the contracts.

class contracts.ContractNotRespected(contract, error, value, context)

Exception thrown when a value does not respect a contract.

Manually checking values

contracts.check(contract, object, desc=None, **context)

Checks that object satisfies the contract described by contract.

Parameters:
  • contract (str) – The contract string.
  • object (*) – Any object.
  • desc (None|str) – An optional description of the error. If given, it is included in the error message.
contracts.check_multiple(couples, desc=None)

Checks multiple couples of (contract, value) in the same context.

This means that the variables in each contract are shared with the others.

Parameters:
  • couples (list[>0](tuple(str, *))) – A list of tuple (contract, value) to check.
  • desc (None|str) – An optional description of the error. If given, it is included in the error message.
contracts.parse(spec)

spec can be either a Contract, a type, or a contract string. In the latter case, the usual parsing takes place

contracts.new_contract(*args)

Defines a new contract type. Used both as a decorator and as a function.

1) Use as a function. The first parameter must be a string. The second parameter can be either a string or a callable function.

new_contract('new_contract_name', 'list[N]') 
new_contract('new_contract_name', lambda x: isinstance(x, list) )
  • If it is a string, it is interpreted as contract expression; the given identifier will become an alias for that expression.

  • If it is a callable, it must accept one parameter, and either:

    • return True or None, to signify it accepts.
    • return False or raise ValueError or AssertionError, to signify it doesn’t.

    If ValueError is raised, its message is used in the error.

2) Use as a decorator.

Or, it can be used as a decorator (without arguments). The function name is used as the identifier.

@new_contract
def new_contract_name(x):
    return isinstance(x, list)

This function returns a Contract object. It might be useful to check right away if the declaration is what you meant, using Contract.check() and Contract.fail().

Parameters:
  • identifier (str) – The identifier must be a string not already in use (you cannot redefine list, tuple, etc.).
  • condition (type|callable|str) – Definition of the new contract.
Returns:

The equivalent contract – might be useful for debugging.

Return type:

Contract

class contracts.Contract(where)
check(value)

Checks that the value satisfies this contract.

Raise :ContractNotRespected
fail(value)

Checks that the value does not respect this contract. Raises an exception if it does.

Raise :ValueError
__repr__()

Returns a string representation of a contract that can be evaluated by Python’s eval().

It must hold that: eval(contract.__repr__()) == contract. This is checked in the unit-tests.

Example:

>>> from contracts import parse
>>> contract = parse('list[N]')
>>> contract.__repr__()
"List(BindVariable('N',int),None)"

All the symbols you need to eval() the expression are in contracts.library.

>>> from contracts.library import *
>>> contract == eval("%r"%contract)
True
__str__()

Returns a string representation of a contract that can be reparsed by contracts.parse().

It must hold that: parse(str(contract)) == contract. This is checked in the unit-tests.

Example:

>>> from contracts import parse
>>> spec = 'list[N]' 
>>> contract = parse(spec)
>>> contract
List(BindVariable('N',int),None)
>>> str(contract) == spec
True

The expressions generated by Contract.__str__() will be exactly the same as what was parsed (this is checked in the unittests as well) if and only if the expression is “minimal”. If it isn’t (there is whitespace or redundant symbols), the returned expression will be an equivalent minimal one.

Example with extra parenthesis and whitespace:

>>> from contracts import parse
>>> verbose_spec = 'list[((N))]( int, > 0)' 
>>> contract = parse(verbose_spec)
>>> str(contract)
'list[N](int,>0)'

Example that removes extra parentheses around arithmetic operators:

>>> verbose_spec = '=1+(1*2)+(2+4)' 
>>> str(parse(verbose_spec))
'=1+1*2+2+4'

This is an example with logical operators precedence. The AND operator , (comma) has more precedence than the OR (|).

>>> verbose_spec = '(a|(b,c)),e' 
>>> str(parse(verbose_spec))
'(a|b,c),e'

Not that only the outer parenthesis is kept as it is the only one needed.

Miscellaneous

contracts.contract_expression

A PyParsing expression that can be used to include contracts expression in your own PyParsing grammar.