AndreaCensi / contracts

PyContracts is a Python package that allows to declare constraints on function parameters and return values. Contracts can be specified using Python3 annotations, or inside a docstring. PyContracts supports a basic type system, variables binding, arithmetic constraints, and has several specialized contracts and an extension API.
http://andreacensi.github.io/contracts/
Other
398 stars 62 forks source link

.. image:: https://circleci.com/gh/AndreaCensi/contracts.svg?style=svg :target: https://circleci.com/gh/AndreaCensi/contracts

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).

As a quick intro, please see this presentation about PyContracts_.

.. _this presentation about PyContracts: http://censi.mit.edu/pub/research/201410-pycontracts/201410-pycontracts.pdf

.. image:: http://censi.mit.edu/pub/research/201410-pycontracts/201410-pycontracts.border.png :height: 100px :target: http://censi.mit.edu/pub/research/201410-pycontracts/201410-pycontracts.pdf :alt: A presentation about PyContracts

.. container:: brief_summary

A brief summary follows. See the full documentation at: <http://andreacensi.github.com/contracts/>

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.

Specifying contracts: Contracts can be specified in three ways:

  1. Using the @contract decorator: ::

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

  2. Using annotations (for Python 3): ::

    @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.
       ...
  3. Using docstrings, with the :type: and :rtype: tags: ::

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

.. In any case, PyContracts will include the spec in the __doc__ attribute.

Deployment: In production, all checks can be disabled using the function contracts.disable_all(), so the performance hit is 0.

Extensions: You can extend PyContracts with new contracts types: ::

new_contract('valid_name', lambda s: isinstance(s, str) and len(s)>0)
@contract(names='dict(int: (valid_name, int))')
def process_accounting(records):
    ...

Any Python type is a contract: ::

@contract(a=int, # simple contract
          b='int,>0' # more complicated
          )
def f(a, b):
    ...

Enforcing interfaces: ContractsMeta is a metaclass, like ABCMeta, which propagates contracts to the subclasses: ::

from contracts import contract, ContractsMeta, with_metaclass

class Base(with_metaclass(ContractsMeta, object)):

    @abstractmethod
    @contract(probability='float,>=0,<=1')
    def sample(self, probability):
        pass

class Derived(Base):
    # The contract above is automatically enforced, 
    # without this class having to know about PyContracts at all!
    def sample(self, probability):
        ....

Numpy: There is special support for Numpy: ::

@contract(image='array[HxWx3](uint8),H>10,W>10')
def recolor(image):
    ...

Status: The syntax is stable and it won't be changed. PyContracts is very well tested on Python 2.x.

Status on Python 3.x: We reached feature parity! Everything works on Python 3 now.

Contributors:

(Please let me know if I forgot anybody.)

.. _Jonathan Sharpe: http://jonathansharpe.me.uk/

.. Chris Beaumont: http://chrisbeaumont.org/ .. asharp: https://github.com/asharp .. Maarten Derickx: http://mderickx.nl/ .. Ryan Heimbuch: https://github.com/ryanheimbuch-wf .. Calen Pennington: https://github.com/cpennington .. Adam Palay: https://github.com/adampalay .. William Furr: http://www.ccs.neu.edu/home/furrwf/ .. Karol Kuczmarski: http://xion.org.pl/ .. Brett Graham: https://github.com/braingram .. Dennis Kempin: https://github.com/denniskempin .. _Andy Hayden: http://careers.stackoverflow.com/hayd

.. _typecheck: http://oakwinter.com/code/typecheck/ .. _Haskell: http://www.haskell.org/