<filename>DbC.py<gh_stars>0
"""
Design-by-Contract (DbC)

Use @pre(pre_checker_function) and @post(post_checker_function)
to check pre- and post- conditions of a function using checker functions

Use @invariant_safe(invariant_function) to check that methods don't break the
 class invariant

Use @checker_for(assertion_level) to mark a function/method as a checker
function that you intend to call stand-alone (rather than via the decorator).

Checker functions (whether for pre-conditions, post-conditions, invariants or
checks) should use good-old-fashioned assert statements, or should raise
PostConditionError, PreConditionError or InvariantError as appropriate.
Since PostConditionError, PreConditionError and InvariantError are subclasses
of AssertionError, your test code can use "except AssertionError" to catch all
pre-, post-, invariant and check failures.
  _____________________
_| Enabling Assertions |______________________________________________________

In order for the contracts to be checked, you will need to turn on
ASSERTION_ALL (or one of the other levels) -- this can be done using an
environment variable:
    export ASSERTION_LEVEL=ALL
or explicitly via assignment:
    import DbC
    DbC.ASSERTION_LEVEL = DbC.ASSERTION_ALL
______________________________________________________________________________
"""

import os
import sys
import jazzy

# From the Eiffel documentation ...
# no : assertions have no run-time effect.
# require : monitor preconditions only, on routine entry.
# ensure : preconditions on entry, postconditions on exit.
# invariant : same as ensure, plus class invariant on both entry and exit.
# all : same as invariant, plus check instructions.

(
    ASSERTION_NONE,
    ASSERTION_REQUIRE,
    ASSERTION_ENSURE,
    ASSERTION_WEAK_INVARIANT,
    ASSERTION_INVARIANT,
    ASSERTION_CHECK,
    ASSERTION_PARANOID
) = range(7)
ASSERTION_ALL = ASSERTION_CHECK

# The difference between ASSERTION_INVARIANT and ASSERTION_WEAK_INVARIANT is
# that "weak" level won't call the checker function on exit if an exception
# has been raised by the decorated function. This allows you to detect any
# exceptions in the usual way BUT won't guarantee that the invariant has not
# been violated. The "non-weak" level gives priority to the detection of
# invariant violations. If an exception is raised by the decorated function,
# the checker function is still called on exit (via a `finally` clause). If
# the checker function subsequently raises an exception this will cause the
# original exception to be discarded in Python2.x.

_ASSERTION_NAME_MAP = {
    'ASSERTION_NONE': ASSERTION_NONE,
    'ASSERTION_REQUIRE': ASSERTION_REQUIRE,
    'ASSERTION_ENSURE': ASSERTION_ENSURE,
    'ASSERTION_WEAK_INVARIANT': ASSERTION_WEAK_INVARIANT,
    'ASSERTION_INVARIANT': ASSERTION_INVARIANT,
    'ASSERTION_CHECK': ASSERTION_CHECK,
    'ASSERTION_PARANOID': ASSERTION_PARANOID,
    'ASSERTION_ALL': ASSERTION_ALL,
    'NONE': ASSERTION_NONE,
    'REQUIRE': ASSERTION_REQUIRE,
    'ENSURE': ASSERTION_ENSURE,
    'INVARIANT': ASSERTION_INVARIANT,
    'CHECK': ASSERTION_CHECK,
    'PARANOID': ASSERTION_PARANOID,
    'ALL': ASSERTION_ALL,
    '0': ASSERTION_NONE,
    '1': ASSERTION_REQUIRE,
    '2': ASSERTION_ENSURE,
    '3': ASSERTION_WEAK_INVARIANT,
    '4': ASSERTION_INVARIANT,
    '5': ASSERTION_CHECK,
    '6': ASSERTION_PARANOID,
    None: ASSERTION_NONE,
}

# Turn off Design-by-Contract by default
ASSERTION_LEVEL = _ASSERTION_NAME_MAP.get(os.getenv('ASSERTION_LEVEL'), 0)

_DO_NOTHING = lambda *args: None


def _paranoid(value):
    assert value is None, u'checker functions should not return a value'


def _poparg(name, arglis, kwargs, dflts):
    'Pop a named argument from the keyword dictionary or the lists.'
    if name in kwargs:
        rval = kwargs.pop(name)
    elif arglis:
        rval = arglis.pop(0)
    else:
        rval = dflts.pop(0)
    return rval


def _unvarargs(names, args, kwargs, dflts):
    'Convert varargs/keyword/default args to a flat tuple.'
    arglis = list(args)
    rval = tuple(_poparg(name, arglis, kwargs, dflts) for name in names)
    return rval + tuple(arglis)


#   ________________________
# _| PRE-CONDITION CHECKING |_________________________________________________
# aka REQUIRE
#
# You can decorate your functions and methods with:
#  @pre(checker)
# to call the given checker with the same arguments as the original function
# if ASSERTION_LEVEL >= ASSERTION_REQUIRE then the checker will be called
# prior to calling the original function
# if ASSERTION_LEVEL < ASSERTION_REQUIRE then the checker will not be called
#
# The checker should assert that all the required pre-conditions have been met
#
# The checker MUST NOT alter the arguments to the original method;
# it should just check that they are correct.
# The checker SHOULD NOT return a value (because it will be ignored)
#
def pre(checker):

    def pre_decorator(func):

        paranoid = _paranoid \
            if ASSERTION_LEVEL >= ASSERTION_PARANOID \
            else _DO_NOTHING

        def check_pre_condition(*args, **kwargs):
            # Call the checker
            paranoid(checker(*args, **kwargs))
            # Call the function
            return func(*args, **kwargs)

        if ASSERTION_LEVEL >= ASSERTION_REQUIRE:
            return check_pre_condition
        else:
            return func

    return pre_decorator


class PreConditionError(AssertionError):
    pass


def assertPreCondition(expr, *args):
    if not expr:
        prefix = u'PreCondition failure:'
        tup = jazzy.tjoin(prefix, *args)
        message = u' '.join(jazzy.iter_to_unicode(tup))
        raise PreConditionError(message)


#   _________________________
# _| POST-CONDITION CHECKING |________________________________________________
# aka ENSURE
#
# You can decorate your functions and methods with:
#  @post(checker)
# to call the given checker with one additional argument than the original
# function. This additional argument is the return value of the original
# function. For example, if your function is:
#   def fraction(number, denom): ...
# your post checker should be defined thus:
#   def post_fraction(number, denom, result): ...
#
# If your function returns multiple values, result will be a tuple.
# If your function doesn't return anything, result will be None.
# So your post checker function MUST always take one more named parameter
# than the original function.
#
# It is highly recommended that the parameter names are the same between the
# post checker and the original function.
#
# if ASSERTION_LEVEL >= ASSERTION_ENSURE then the checker will be called
# after calling the original function passing the checker the return value
# from the original function.
# if ASSERTION_LEVEL < ASSERTION_ENSURE then the checker will not be called
#
# The checker should assert that all the required post-conditions have been met
#
# The checker MUST NOT alter the arguments to the original method;
# it should just check that they are correct.
# The checker SHOULD NOT return a value (because it will be ignored)
#
def post(checker):

    def post_decorator(func):

        paranoid = _paranoid \
            if ASSERTION_LEVEL >= ASSERTION_PARANOID \
            else _DO_NOTHING

        def check_post_condition(*args, **kwargs):
            # Call the function
            rval = func(*args, **kwargs)
            # Convert varargs to a flat tuple
            # - this is necessary because we need to add the result to the
            #   argument list to pass to the checker
            kcopy = dict(**kwargs)
            try:
                code = func.func_code
            except AttributeError:
                code = func.__code__
            argnames = code.co_varnames[:code.co_argcount]
            try:
                dflts = func.func_defaults
            except AttributeError:
                dflts = func.__defaults__
            postargs = _unvarargs(
                argnames, args, kcopy, list(dflts or []))
            # Append the result to the flat argument list
            postargs = postargs + (rval,)
            # Call the checker
            paranoid(checker(*postargs, **kcopy))
            return rval

        if ASSERTION_LEVEL >= ASSERTION_ENSURE:
            return check_post_condition
        else:
            return func

    return post_decorator


class PostConditionError(AssertionError):
    pass


def assertPostCondition(expr, *args):
    if not expr:
        prefix = u'PostCondition failure:'
        tup = jazzy.tjoin(prefix, *args)
        message = u' '.join(jazzy.iter_to_unicode(tup))
        raise PostConditionError(message)


#   ____________________
# _| INVARIANT CHECKING |_____________________________________________________
#
# You can decorate your methods with
#  @invariant_safe(checker)
# to call the given checker on entry to AND on exit from the decorated method
# The checker method should take only one parameter (self)
# if ASSERTION_LEVEL >= ASSERTION_INVARIANT then the checker will be called
# if ASSERTION_LEVEL < ASSERTION_INVARIANT then the checker will not be called
#
def invariant_safe(checker):

    def invariant_decorator(func):

        paranoid = _paranoid \
            if ASSERTION_LEVEL >= ASSERTION_PARANOID \
            else _DO_NOTHING

        def check_weak_invariant(*args, **kwargs):
            # Call the checker with the first argument only
            paranoid(checker(*args[:1]))
            # Call the function
            rval = func(*args, **kwargs)
            # Call the checker again
            paranoid(checker(*args[:1]))
            return rval

        def check_invariant(*args, **kwargs):
            # Call the checker with the first argument only
            paranoid(checker(*args[:1]))
            rval = None
            try:
                # Call the function
                rval = func(*args, **kwargs)
            finally:
                # Call the checker again
                paranoid(checker(*args[:1]))
            return rval

        if ASSERTION_LEVEL >= ASSERTION_INVARIANT:
            return check_invariant
        elif ASSERTION_LEVEL >= ASSERTION_WEAK_INVARIANT:
            return check_weak_invariant
        else:
            return func

    return invariant_decorator


class InvariantError(AssertionError):
    pass


def assertInvariant(expr, *args):
    if not expr:
        prefix = u'Invariant violation:'
        tup = jazzy.tjoin(prefix, *args)
        message = u' '.join(jazzy.iter_to_unicode(tup))
        raise InvariantError(message)


#   __________
# _| CHECKERS |_______________________________________________________________
# Enable functions depending on ASSERTION_LEVEL
#
# @checker_for(ASSERTION_INVARIANT)
# def my_checker(self):
#   assert self.something is not None

def checker_for(assertion_level):
    'Enable a function when ASSERTION_LEVEL is at or above a threshold.'
    def inner(func):
        if ASSERTION_LEVEL >= assertion_level:
            return func
        else:
            return lambda *args: None
    return inner


<filename>DbC.py<gh_stars>0
"""
Design-by-Contract (DbC)

Use @pre(pre_checker_function) and @post(post_checker_function)
to check pre- and post- conditions of a function using checker functions

Use @invariant_safe(invariant_function) to check that methods don't break the
 class invariant

Use @checker_for(assertion_level) to mark a function/method as a checker
function that you intend to call stand-alone (rather than via the decorator).

Checker functions (whether for pre-conditions, post-conditions, invariants or
checks) should use good-old-fashioned assert statements, or should raise
PostConditionError, PreConditionError or InvariantError as appropriate.
Since PostConditionError, PreConditionError and InvariantError are subclasses
of AssertionError, your test code can use "except AssertionError" to catch all
pre-, post-, invariant and check failures.
  _____________________
_| Enabling Assertions |______________________________________________________

In order for the contracts to be checked, you will need to turn on
ASSERTION_ALL (or one of the other levels) -- this can be done using an
environment variable:
    export ASSERTION_LEVEL=ALL
or explicitly via assignment:
    import DbC
    DbC.ASSERTION_LEVEL = DbC.ASSERTION_ALL
______________________________________________________________________________
"""

import os
import sys
import jazzy

# From the Eiffel documentation ...
# no : assertions have no run-time effect.
# require : monitor preconditions only, on routine entry.
# ensure : preconditions on entry, postconditions on exit.
# invariant : same as ensure, plus class invariant on both entry and exit.
# all : same as invariant, plus check instructions.

(
    ASSERTION_NONE,
    ASSERTION_REQUIRE,
    ASSERTION_ENSURE,
    ASSERTION_WEAK_INVARIANT,
    ASSERTION_INVARIANT,
    ASSERTION_CHECK,
    ASSERTION_PARANOID
) = range(7)
ASSERTION_ALL = ASSERTION_CHECK

# The difference between ASSERTION_INVARIANT and ASSERTION_WEAK_INVARIANT is
# that "weak" level won't call the checker function on exit if an exception
# has been raised by the decorated function. This allows you to detect any
# exceptions in the usual way BUT won't guarantee that the invariant has not
# been violated. The "non-weak" level gives priority to the detection of
# invariant violations. If an exception is raised by the decorated function,
# the checker function is still called on exit (via a `finally` clause). If
# the checker function subsequently raises an exception this will cause the
# original exception to be discarded in Python2.x.

_ASSERTION_NAME_MAP = {
    'ASSERTION_NONE': ASSERTION_NONE,
    'ASSERTION_REQUIRE': ASSERTION_REQUIRE,
    'ASSERTION_ENSURE': ASSERTION_ENSURE,
    'ASSERTION_WEAK_INVARIANT': ASSERTION_WEAK_INVARIANT,
    'ASSERTION_INVARIANT': ASSERTION_INVARIANT,
    'ASSERTION_CHECK': ASSERTION_CHECK,
    'ASSERTION_PARANOID': ASSERTION_PARANOID,
    'ASSERTION_ALL': ASSERTION_ALL,
    'NONE': ASSERTION_NONE,
    'REQUIRE': ASSERTION_REQUIRE,
    'ENSURE': ASSERTION_ENSURE,
    'INVARIANT': ASSERTION_INVARIANT,
    'CHECK': ASSERTION_CHECK,
    'PARANOID': ASSERTION_PARANOID,
    'ALL': ASSERTION_ALL,
    '0': ASSERTION_NONE,
    '1': ASSERTION_REQUIRE,
    '2': ASSERTION_ENSURE,
    '3': ASSERTION_WEAK_INVARIANT,
    '4': ASSERTION_INVARIANT,
    '5': ASSERTION_CHECK,
    '6': ASSERTION_PARANOID,
    None: ASSERTION_NONE,
}

# Turn off Design-by-Contract by default
ASSERTION_LEVEL = _ASSERTION_NAME_MAP.get(os.getenv('ASSERTION_LEVEL'), 0)

_DO_NOTHING = lambda *args: None


def _paranoid(value):
    assert value is None, u'checker functions should not return a value'


def _poparg(name, arglis, kwargs, dflts):
    'Pop a named argument from the keyword dictionary or the lists.'
    if name in kwargs:
        rval = kwargs.pop(name)
    elif arglis:
        rval = arglis.pop(0)
    else:
        rval = dflts.pop(0)
    return rval


def _unvarargs(names, args, kwargs, dflts):
    'Convert varargs/keyword/default args to a flat tuple.'
    arglis = list(args)
    rval = tuple(_poparg(name, arglis, kwargs, dflts) for name in names)
    return rval + tuple(arglis)


#   ________________________
# _| PRE-CONDITION CHECKING |_________________________________________________
# aka REQUIRE
#
# You can decorate your functions and methods with:
#  @pre(checker)
# to call the given checker with the same arguments as the original function
# if ASSERTION_LEVEL >= ASSERTION_REQUIRE then the checker will be called
# prior to calling the original function
# if ASSERTION_LEVEL < ASSERTION_REQUIRE then the checker will not be called
#
# The checker should assert that all the required pre-conditions have been met
#
# The checker MUST NOT alter the arguments to the original method;
# it should just check that they are correct.
# The checker SHOULD NOT return a value (because it will be ignored)
#
def pre(checker):

    def pre_decorator(func):

        paranoid = _paranoid \
            if ASSERTION_LEVEL >= ASSERTION_PARANOID \
            else _DO_NOTHING

        def check_pre_condition(*args, **kwargs):
            # Call the checker
            paranoid(checker(*args, **kwargs))
            # Call the function
            return func(*args, **kwargs)

        if ASSERTION_LEVEL >= ASSERTION_REQUIRE:
            return check_pre_condition
        else:
            return func

    return pre_decorator


class PreConditionError(AssertionError):
    pass


def assertPreCondition(expr, *args):
    if not expr:
        prefix = u'PreCondition failure:'
        tup = jazzy.tjoin(prefix, *args)
        message = u' '.join(jazzy.iter_to_unicode(tup))
        raise PreConditionError(message)


#   _________________________
# _| POST-CONDITION CHECKING |________________________________________________
# aka ENSURE
#
# You can decorate your functions and methods with:
#  @post(checker)
# to call the given checker with one additional argument than the original
# function. This additional argument is the return value of the original
# function. For example, if your function is:
#   def fraction(number, denom): ...
# your post checker should be defined thus:
#   def post_fraction(number, denom, result): ...
#
# If your function returns multiple values, result will be a tuple.
# If your function doesn't return anything, result will be None.
# So your post checker function MUST always take one more named parameter
# than the original function.
#
# It is highly recommended that the parameter names are the same between the
# post checker and the original function.
#
# if ASSERTION_LEVEL >= ASSERTION_ENSURE then the checker will be called
# after calling the original function passing the checker the return value
# from the original function.
# if ASSERTION_LEVEL < ASSERTION_ENSURE then the checker will not be called
#
# The checker should assert that all the required post-conditions have been met
#
# The checker MUST NOT alter the arguments to the original method;
# it should just check that they are correct.
# The checker SHOULD NOT return a value (because it will be ignored)
#
def post(checker):

    def post_decorator(func):

        paranoid = _paranoid \
            if ASSERTION_LEVEL >= ASSERTION_PARANOID \
            else _DO_NOTHING

        def check_post_condition(*args, **kwargs):
            # Call the function
            rval = func(*args, **kwargs)
            # Convert varargs to a flat tuple
            # - this is necessary because we need to add the result to the
            #   argument list to pass to the checker
            kcopy = dict(**kwargs)
            try:
                code = func.func_code
            except AttributeError:
                code = func.__code__
            argnames = code.co_varnames[:code.co_argcount]
            try:
                dflts = func.func_defaults
            except AttributeError:
                dflts = func.__defaults__
            postargs = _unvarargs(
                argnames, args, kcopy, list(dflts or []))
            # Append the result to the flat argument list
            postargs = postargs + (rval,)
            # Call the checker
            paranoid(checker(*postargs, **kcopy))
            return rval

        if ASSERTION_LEVEL >= ASSERTION_ENSURE:
            return check_post_condition
        else:
            return func

    return post_decorator


class PostConditionError(AssertionError):
    pass


def assertPostCondition(expr, *args):
    if not expr:
        prefix = u'PostCondition failure:'
        tup = jazzy.tjoin(prefix, *args)
        message = u' '.join(jazzy.iter_to_unicode(tup))
        raise PostConditionError(message)


#   ____________________
# _| INVARIANT CHECKING |_____________________________________________________
#
# You can decorate your methods with
#  @invariant_safe(checker)
# to call the given checker on entry to AND on exit from the decorated method
# The checker method should take only one parameter (self)
# if ASSERTION_LEVEL >= ASSERTION_INVARIANT then the checker will be called
# if ASSERTION_LEVEL < ASSERTION_INVARIANT then the checker will not be called
#
def invariant_safe(checker):

    def invariant_decorator(func):

        paranoid = _paranoid \
            if ASSERTION_LEVEL >= ASSERTION_PARANOID \
            else _DO_NOTHING

        def check_weak_invariant(*args, **kwargs):
            # Call the checker with the first argument only
            paranoid(checker(*args[:1]))
            # Call the function
            rval = func(*args, **kwargs)
            # Call the checker again
            paranoid(checker(*args[:1]))
            return rval

        def check_invariant(*args, **kwargs):
            # Call the checker with the first argument only
            paranoid(checker(*args[:1]))
            rval = None
            try:
                # Call the function
                rval = func(*args, **kwargs)
            finally:
                # Call the checker again
                paranoid(checker(*args[:1]))
            return rval

        if ASSERTION_LEVEL >= ASSERTION_INVARIANT:
            return check_invariant
        elif ASSERTION_LEVEL >= ASSERTION_WEAK_INVARIANT:
            return check_weak_invariant
        else:
            return func

    return invariant_decorator


class InvariantError(AssertionError):
    pass


def assertInvariant(expr, *args):
    if not expr:
        prefix = u'Invariant violation:'
        tup = jazzy.tjoin(prefix, *args)
        message = u' '.join(jazzy.iter_to_unicode(tup))
        raise InvariantError(message)


#   __________
# _| CHECKERS |_______________________________________________________________
# Enable functions depending on ASSERTION_LEVEL
#
# @checker_for(ASSERTION_INVARIANT)
# def my_checker(self):
#   assert self.something is not None

def checker_for(assertion_level):
    'Enable a function when ASSERTION_LEVEL is at or above a threshold.'
    def inner(func):
        if ASSERTION_LEVEL >= assertion_level:
            return func
        else:
            return lambda *args: None
    return inner


Design-by-Contract (DbC)  Use @pre(pre_checker_function) and @post(post_checker_function) to check pre- and post- conditions of a function using checker functions  Use @invariant_safe(invariant_function) to check that methods don't break the  class invariant  Use @checker_for(assertion_level) to mark a function/method as a checker function that you intend to call stand-alone (rather than via the decorator).  Checker functions (whether for pre-conditions, post-conditions, invariants or checks) should use good-old-fashioned assert statements, or should raise PostConditionError, PreConditionError or InvariantError as appropriate. Since PostConditionError, PreConditionError and InvariantError are subclasses of AssertionError, your test code can use "except AssertionError" to catch all pre-, post-, invariant and check failures.   _____________________ _| Enabling Assertions |______________________________________________________  In order for the contracts to be checked, you will need to turn on ASSERTION_ALL (or one of the other levels) -- this can be done using an environment variable:     export ASSERTION_LEVEL=ALL or explicitly via assignment:     import DbC     DbC.ASSERTION_LEVEL = DbC.ASSERTION_ALL ______________________________________________________________________________ # From the Eiffel documentation ... # no : assertions have no run-time effect. # require : monitor preconditions only, on routine entry. # ensure : preconditions on entry, postconditions on exit. # invariant : same as ensure, plus class invariant on both entry and exit. # all : same as invariant, plus check instructions. # The difference between ASSERTION_INVARIANT and ASSERTION_WEAK_INVARIANT is # that "weak" level won't call the checker function on exit if an exception # has been raised by the decorated function. This allows you to detect any # exceptions in the usual way BUT won't guarantee that the invariant has not # been violated. The "non-weak" level gives priority to the detection of # invariant violations. If an exception is raised by the decorated function, # the checker function is still called on exit (via a `finally` clause). If # the checker function subsequently raises an exception this will cause the # original exception to be discarded in Python2.x. # Turn off Design-by-Contract by default #   ________________________ # _| PRE-CONDITION CHECKING |_________________________________________________ # aka REQUIRE # # You can decorate your functions and methods with: #  @pre(checker) # to call the given checker with the same arguments as the original function # if ASSERTION_LEVEL >= ASSERTION_REQUIRE then the checker will be called # prior to calling the original function # if ASSERTION_LEVEL < ASSERTION_REQUIRE then the checker will not be called # # The checker should assert that all the required pre-conditions have been met # # The checker MUST NOT alter the arguments to the original method; # it should just check that they are correct. # The checker SHOULD NOT return a value (because it will be ignored) # # Call the checker # Call the function #   _________________________ # _| POST-CONDITION CHECKING |________________________________________________ # aka ENSURE # # You can decorate your functions and methods with: #  @post(checker) # to call the given checker with one additional argument than the original # function. This additional argument is the return value of the original # function. For example, if your function is: #   def fraction(number, denom): ... # your post checker should be defined thus: #   def post_fraction(number, denom, result): ... # # If your function returns multiple values, result will be a tuple. # If your function doesn't return anything, result will be None. # So your post checker function MUST always take one more named parameter # than the original function. # # It is highly recommended that the parameter names are the same between the # post checker and the original function. # # if ASSERTION_LEVEL >= ASSERTION_ENSURE then the checker will be called # after calling the original function passing the checker the return value # from the original function. # if ASSERTION_LEVEL < ASSERTION_ENSURE then the checker will not be called # # The checker should assert that all the required post-conditions have been met # # The checker MUST NOT alter the arguments to the original method; # it should just check that they are correct. # The checker SHOULD NOT return a value (because it will be ignored) # # Call the function # Convert varargs to a flat tuple # - this is necessary because we need to add the result to the #   argument list to pass to the checker # Append the result to the flat argument list # Call the checker #   ____________________ # _| INVARIANT CHECKING |_____________________________________________________ # # You can decorate your methods with #  @invariant_safe(checker) # to call the given checker on entry to AND on exit from the decorated method # The checker method should take only one parameter (self) # if ASSERTION_LEVEL >= ASSERTION_INVARIANT then the checker will be called # if ASSERTION_LEVEL < ASSERTION_INVARIANT then the checker will not be called # # Call the checker with the first argument only # Call the function # Call the checker again # Call the checker with the first argument only # Call the function # Call the checker again #   __________ # _| CHECKERS |_______________________________________________________________ # Enable functions depending on ASSERTION_LEVEL # # @checker_for(ASSERTION_INVARIANT) # def my_checker(self): #   assert self.something is not None