import unittest
from contracts import new_contract, check, Contract, contract
from contracts.library.extensions import identifier_expression
from .utils import check_contracts_fail, check_contracts_ok
from contracts.main import can_be_used_as_a_type, Storage
from contracts.syntax import ParsingTmp
# The different patterns
[docs]def ok2(x): # @UnusedVariable
return True
[docs]def fail1(x):
raise ValueError('message')
[docs]def fail2(x): # @UnusedVariable
return False
[docs]def invalid_callable1(x): # @UnusedVariable
return 'ciao'
# generates a new name:
[docs]def cname():
TestNewContract.counter += 1
return 'GeneratedContract%d' % TestNewContract.counter
[docs]class TestNewContract(unittest.TestCase):
counter = 0
#def setup(self):
# Storage.string2contract = {}
[docs] def test_inverted_args(self):
self.assertRaises(ValueError, new_contract, ok1, 'list')
[docs] def test_wrong_args(self):
self.assertRaises(ValueError, new_contract, 'my13', 2)
[docs] def test_invalid_callable(self):
self.assertRaises(ValueError, new_contract, 'new', lambda: None)
[docs] def test_parsing_error(self):
self.assertRaises(ValueError, new_contract, 'new', '>>')
[docs] def test_parsing_error2(self):
# parsing error (unknown spec)
self.assertRaises(ValueError, new_contract, 'new', 'unknown')
[docs] def test_invalid_names(self):
# invalid names:
alphabet = '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'
for x in alphabet.split():
self.assertRaises(ValueError, new_contract, x, 'list')
self.assertRaises(ValueError, new_contract, x.lower(), 'list')
self.assertRaises(ValueError, new_contract, 'list', 'list[N]')
self.assertRaises(ValueError, new_contract, '2acdca', 'list[N]')
self.assertRaises(ValueError, new_contract, '_', 'list[N]')
[docs] def test_valid(self):
c = new_contract('my_list', 'list[2]')
assert isinstance(c, Contract)
check('tuple(my_list, my_list)', ([1, 2], [1, 2]))
check_contracts_fail('tuple(my_list, my_list)', ([1, 2], [1, 2, 3]))
[docs] def test_separate_context(self):
new_contract('my_list2', 'list[N]')
check('tuple(my_list2, my_list2)', ([1, 2], [1, 2]))
check('tuple(my_list2, my_list2)', ([1, 2], [1, 2, 3]))
[docs] def test_renaming(self):
self.assertNotEqual(ok1, ok2)
new_contract('my7', ok1)
self.assertRaises(ValueError, new_contract, 'my7', ok2)
[docs] def test_allow_renaming_if_equal1(self):
new_contract('my8', ok1)
new_contract('my8', ok1)
[docs] def test_allow_renaming_if_equal2(self):
new_contract('my8b', 'list[3]')
new_contract('my8b', 'list[3]')
[docs] def test_callable1(self):
c = cname()
new_contract(c, ok2)
check('list(%s)' % c, [0])
[docs] def test_callable2(self):
c = cname()
new_contract(c, ok2)
check('list(%s)' % c, [0])
[docs] def test_callable3(self):
c = cname()
new_contract(c, fail1)
check_contracts_fail('list(%s)' % c, [0])
[docs] def test_callable4(self):
c = cname()
new_contract(c, fail2)
check_contracts_fail('list(%s)' % c, [0])
[docs] def test_invalid_callable2(self):
c = cname()
new_contract(c, invalid_callable1)
self.assertRaises(ValueError, check, 'list(%s)' % c, [0])
[docs] def test_other_pass(self):
class Ex1(Exception):
pass
def invalid(x):
raise Ex1()
c = cname()
new_contract(c, invalid)
self.assertRaises(Ex1, check, 'list(%s)' % c, [0])
[docs] def test_callable(self):
class MyTest_ok(object):
def __call__(self, x): # @UnusedVariable
return True
o = MyTest_ok()
assert o('value') == True
new_contract(cname(), o)
[docs] def test_callable_old_style(self):
class MyTest_ok():
def __call__(self, x): # @UnusedVariable
return True
o = MyTest_ok()
assert o('value') == True
new_contract(cname(), o)
[docs] def test_class_as_contract1(self):
# This should be interpreted as a type
# init(x,y) so it is not mistaken for a valid callable
class NewStyleClass(object):
def __init__(self, x, y): # @UnusedVariable
pass
new_contract(cname(), NewStyleClass)
[docs] def test_class_as_contract2(self):
# old sytle class
class OldStyleClass():
def __init__(self, x, y): # @UnusedVariable
pass
new_contract(cname(), OldStyleClass)
[docs] def test_class_as_contract3(self):
class NewStyleClass(object):
def __init__(self, x, y): # @UnusedVariable
pass
@contract(x=NewStyleClass)
def f(x):
pass
[docs] def test_class_as_contract4(self):
class OldStyleClass():
def __init__(self, x, y): # @UnusedVariable
pass
@contract(x=OldStyleClass)
def f(x):
pass
[docs] def test_callable_5(self):
class MyTest_ok(object):
def f(self, x): # @UnusedVariable
return True
o = MyTest_ok()
assert o.f('value') == True
new_contract(cname(), o.f)
# TODO: removed after checking in class methods
# def test_callable_invalid(self):
# class MyTest_fail(object):
# def __call__(self, x, y): # @UnusedVariable
# return True
#
# self.assertRaises(ValueError, new_contract, cname(), MyTest_fail())
[docs] def test_lambda_2(self):
new_contract(cname(), lambda x: True) # @UnusedVariable
new_contract(cname(), lambda x: None) # @UnusedVariable
[docs] def test_lambda_invalid(self):
f = lambda: True # @UnusedVariable
self.assertRaises(ValueError, new_contract, cname(), f)
[docs] def test_idioms(self):
Storage.string2contract = {} # TODO remove this hack
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])
self.assertRaises(ValueError, color.fail, [0, 0, 1])
# 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
"""
pass
@contract
def fill_gradient(colors):
""" Use a gradient to fill the area.
:type colors: list(color)
"""
pass
[docs] def test_as_decorator(self):
@new_contract
def even(x):
return x % 2 == 0
from contracts import parse
p = parse('even')
p.check(2)
p.check(4)
p.fail(3)
p.check(2.0)
[docs] def test_as_decorator_multiple(self):
@new_contract
@contract(x='int')
def even2(x):
return x % 2 == 0
from contracts import parse
p = parse('even2')
p.check(2)
p.fail(3)
p.fail(2.0) # now fails
[docs] def test_types_as_contracts(self):
c = cname()
new_contract(c, str)
check_contracts_ok(c, '')
check_contracts_fail(c, 1)
[docs] def test_types_as_contracts2(self):
c = cname()
new_contract(c, int)
check_contracts_ok(c, 1)
check_contracts_fail(c, '')
[docs] def test_well_recognized(self):
class OldStyleClass():
def __init__(self, x, y): # @UnusedVariable
pass
assert can_be_used_as_a_type(OldStyleClass)
class NewStyleClass():
def __init__(self, x, y): # @UnusedVariable
pass
assert can_be_used_as_a_type(NewStyleClass)
[docs] def test_as_decorator_with_args(self):
from contracts import parse
@new_contract
def greater_than(value, thresh):
return value > thresh
p = parse('greater_than(5)')
p.check(6)
p.fail(5)
[docs] def test_as_decorator_with_kwargs(self):
from contracts import parse
@new_contract
def less_than(value, thresh=1):
return value < thresh
p = parse('less_than(thresh=2)')
p.check(1)
p.fail(2)
[docs] def test_as_decorator_with_args_and_kwargs(self):
from contracts import parse
@new_contract
def less_than_all(value, a, b, c=1, d=5):
return value < min(a, b, c, d)
p = parse('less_than_all(3, 4, c=5)')
p.check(2)
p.fail(3)
[docs] def test_capital_name1(self):
# some problems with identifiers starting with capitals
new_contract('SEn', 'int')
check_contracts_ok('SEn', 1)
check_contracts_fail('SEn', 2.0)
[docs] def test_capital_name2(self):
new_contract('Sen', 'int')
check_contracts_ok('Sen', 1)
check_contracts_fail('Sen', 2.0)
examples_valid = ['aa', 'a_', 'a2', 'a_2', 'list2', 'dict2', 'int2',
'float2', 'point2', 'A2', 'array2', 'unit_length', 'SE2', 'SE3', 'S1', 'S2', 'axis_angle']
for k in ParsingTmp.keywords:
examples_valid.append('%s2' % k)
examples_valid.append('%s_' % k)
[docs]def check_valid_identifier(e):
check_valid_identifier.__dict__['description'] = \
'check_valid_identifier(%r)' % e
identifier_expression.parseString(e, parseAll=True) #@UndefinedVariable
new_contract(e, '*')
check(e, 42)
[docs]def test_valid_identifiers():
for e in examples_valid:
yield check_valid_identifier, e