Skip to main content

Flexible, concise preconditions.

Project description

preconditions - A precondition decorator utility which relies on parameter-name equivalence for conciseness and consistency.

Examples

First let’s take a tour of examples. All examples assume the preconditions decorator has been imported:

from preconditions import preconditions

Basic type checking

The double application function requires that the i parameter is an int, which is verified by a single predicate (the lambda expression):

@preconditions(lambda i: isinstance(i, int))
def double(i):
    return 2*i

Multiple predicates

Multiple predicates may be specified:

@preconditions(
    lambda i: isinstance(i, int),
    lambda i: i > 0,
    )
def double(i):
    return 2*i

Note that this is functionally equivalent to this single predicate version:

@preconditions(
    lambda i: isinstance(i, int) and i > 0,
    )
def double(i):
    return 2*i

The multi-predicate version should (eventually) have more specific error reporting for a failure, while the single predicate version may be more efficient.

Multiple arguments

Multiple predicates can express preconditions for multiple arguments:

@preconditions(
    lambda s: isinstance(s, unicode),
    lambda n: isinstance(n, int) and n >= 0,
    )
def repeat(s, n):
    return s*n

However, a single predicate can express preconditions for multiple arguments. This allows relational preconditions:

@preconditions(
    lambda a, b: a <= b
    )
def strict_range(a, b):
    return range(a, b)

Method preconditions

Predicates can be expressed for methods, including relations to self. For example, a Monotonic instance ensures that each call to .set must pass a value larger than any previous call:

class Monotonic (object):
    def __init__(self):
        self.v = 0

    @preconditions(lambda self, v: v > self.v)
    def set(self, v):
        self.v = v

Preconditions can be applied to special methods, such as __new__, __init__, __call__, etc…

class LinearRange (tuple):
    @preconditions(
           lambda a: isinstance(a, float),
           lambda b: isinstance(b, float),
           lambda a, b: a < b,
           )
    def __new__(cls, a, b):
        return super(OrderedTuple, cls).__new__(cls, (a, b))

    @preconditions(lambda w: 0 <= w < 1.0)
    def __call__(self, w):
        lo, hi = self
        return w * (hi - lo) + lo

    @preconditions(lambda x: self[0] <= x < self[1])
    def invert(self, x):
        lo, hi = self
        return (x - lo) / (hi - lo)

Concepts

An application function may be guarded with precondition predicates. These predicates are callables passed to the preconditions decorator. Consider this code:

@preconditions(
    lambda a: isinstance(a, float) and a >= 0,
    lambda b: isinstance(b, float) and b >= 0,
    )
def area(a, b):
    return a*b

The application function is area, and it has two predicates defined with lambda, each of which ensures one of the arguments is a non-negative float.

Parameter Name Equivalence

The parameter names in a predicate must match parameter names in the application function. This is known as parameter name equivalence [1].

One exception to this rule is for default parameters within predicates. Default parameters may be used to associate some state at predicate definition time. For example:

scores = {}

@preconditions(
    lambda color, _colors=['RED', 'GREEN', 'BLUE']: color in _colors
    )
def get_color_score(color):
    return scores[color]

This feature may be most convenient when there’s a need to remember a local loop variable.

Similar Projects

covenant - Code contracts for Python 3.

Project details


Release history Release notifications | RSS feed

This version

0.1

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

preconditions-0.1.tar.gz (5.2 kB view details)

Uploaded Source

File details

Details for the file preconditions-0.1.tar.gz.

File metadata

File hashes

Hashes for preconditions-0.1.tar.gz
Algorithm Hash digest
SHA256 e783f97bee1e3302745a67575ce788fc98874968e1c9343b7e0cdfd5d4c13527
MD5 c61bf4e2f5052ea8ff6903fa656a2e23
BLAKE2b-256 5d19324c00d1399e5997c25d49ece9f9f68868621f06ea1f553671229d28601d

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page