hypothesis
”Theme: Tools and Techniques
Topic: Testing, Property Testing & hypothesis
Presenter: James Powell james@dutc.io
Date: Friday, October 23, 2020
Time: 9 AM PST
Keywords: Python, data analysis, data engineering, numpy, pandas
print("Let's get started!")
from sys import version_info
print(f'{version_info = }')
from itertools import count
from time import perf_counter
start = perf_counter()
for x in count():
print(f'{x = :>8} ({perf_counter() - start:.2f}s)')
def add(x, y):
return x + y
def add(x, y):
return x + y
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
def add(x, y):
return x + y
def test_add():
pass
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
def add(x, y):
return x + y
def test_add():
assert add(1, 1) == 2
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
def add(x, y):
return x + y
def test_add():
assert add(1, 1) == 2
assert add(2, 2) == 4
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
def add(x, y):
if x == 0 and y == 3:
raise ValueError('undefined for 0 and 3')
return x + y
def test_add():
assert add(1, 1) == 2
assert add(2, 2) == 4
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from random import randrange
def add(x, y):
if x == 0 and y == 3:
raise ValueError('undefined for 0 and 3')
return x + y
@fixture
def x():
return randrange(0, 10)
@fixture
def y():
return randrange(0, 10)
def test_add(x, y):
assert add(x, y) == ...
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from random import randrange
def add(x, y):
if x == 0 and y == 3:
raise ValueError('undefined for 0 and 3')
return x + y
x = y = fixture(lambda: randrange(0, 10))
def test_add(x, y):
assert add(x, y) == x + y
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
def add(x, y):
if x == 0 and y == 3:
raise ValueError('undefined for 0 and 3')
return x + y
x = fixture(lambda: 0)
y = fixture(lambda: 3)
def test_add(x, y):
assert add(x, y) == x + y
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
if x == 0 and y == 3:
raise ValueError('undefined for 0 and 3')
return x + y
@given(x=integers(), y=integers())
def test_add(x, y):
assert add(x, y) == x + y
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
if x == 0 and y == 3:
raise ValueError('undefined for 0 and 3')
return x + y
@given(x=integers(min_value=-10, max_value=10), y=integers(min_value=-10, max_value=10))
def test_add(x, y):
assert add(x, y) == x + y
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
if x == 0 and y == 3:
return x - y
return x + y
@given(x=integers(min_value=-10, max_value=10), y=integers(min_value=-10, max_value=10))
def test_add(x, y):
assert add(x, y) == x + y
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
if x == 0 and y == 3:
return x - y
return x + y
@given(x=(ints:=integers(min_value=-10, max_value=10)), y=ints)
def test_add(x, y):
assert add(x, y) == add(y, x)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
if x == 0 and y == 3:
return x - y
return x + y
@given(x=(ints:=integers(min_value=-10, max_value=10)), y=ints, z=ints)
def test_add(x, y, z):
assert add(x, y) == add(y, x)
assert add(x, add(y, z)) == add(add(x, y), z)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
if x == 0 and y == 3:
return x - y
return x + y
@given(x=(ints:=integers(min_value=-10, max_value=10)), y=ints, z=ints)
def test_add(x, y, z):
assert add(x, y) == add(y, x)
assert add(x, add(y, z)) == add(add(x, y), z)
assert add(x, 0) == x
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
if x == 0 and y == 3:
return x - y
return x + y
@given(x=(ints:=integers(min_value=-10, max_value=10)), y=ints, z=ints)
def test_add(x, y, z):
assert add(x, y) == add(y, x)
assert add(x, add(y, z)) == add(add(x, y), z)
assert add(x, 0) == x
a, b, c = sorted([x, y, z])
assert add(a, b) <= add(a, c)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers
def add(x, y):
return x + y
@given(x=(ints:=integers(min_value=-10, max_value=10)), y=ints, z=ints)
def test_add(x, y, z):
assert add(x, y) == add(y, x)
assert add(x, add(y, z)) == add(add(x, y), z)
assert add(x, 0) == x
a, b, c = sorted([x, y, z])
assert add(a, b) <= add(a, c)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers, floats, one_of
def add(x, y):
return x + y
@given(x=(nums:=one_of(integers(min_value=-10, max_value=10), floats())), y=nums, z=nums)
def test_add(x, y, z):
assert add(x, y) == add(y, x)
assert add(x, add(y, z)) == add(add(x, y), z)
assert add(x, 0) == x
a, b, c = sorted([x, y, z])
assert add(a, b) <= add(a, c)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from hypothesis import given
from hypothesis.strategies import integers, floats, one_of
def add(x, y):
return x + y
@given(
x=(nums:=one_of(integers(min_value=-10, max_value=10), floats(allow_nan=False, allow_infinity=False))),
y=nums, z=nums
)
def test_add(x, y, z):
assert add(x, y) == add(y, x)
assert add(x, add(y, z)) == add(add(x, y), z)
assert add(x, 0) == x
a, b, c = sorted([x, y, z])
assert add(a, b) <= add(a, c)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from math import isclose
from hypothesis import given
from hypothesis.strategies import integers, floats, one_of
def add(x, y):
return x + y
@given(
x=(nums:=one_of(integers(min_value=-10, max_value=10), floats(allow_nan=False, allow_infinity=False))),
y=nums, z=nums
)
def test_add(x, y, z):
assert isclose(add(x, y), add(y, x))
assert isclose(add(x, add(y, z)), add(add(x, y), z))
assert isclose(add(x, 0), x)
a, b, c = sorted([x, y, z])
assert add(a, b) <= add(a, c)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from math import isclose
from hypothesis import given
from hypothesis.strategies import integers, floats, one_of
def add(x, y):
return x + y
@given(
x=(nums:=one_of(integers(min_value=-10, max_value=10), floats(allow_nan=False, allow_infinity=False))),
y=nums, z=nums
)
def test_add(x, y, z):
assert isclose(add(x, y), add(y, x), rel_tol=1e-6)
assert isclose(add(x, add(y, z)), add(add(x, y), z), rel_tol=1e-6)
assert isclose(add(x, 0), x, rel_tol=1e-6)
a, b, c = sorted([x, y, z])
assert add(a, b) <= add(a, c)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
from pytest import fixture
from math import isclose
from hypothesis import given
from hypothesis.strategies import integers, floats, one_of
def add(x, y):
return x + y
@given(
x=(nums:=one_of(integers(min_value=-10, max_value=10), floats(allow_nan=False, allow_infinity=False))),
y=nums, z=nums
)
def test_add(x, y, z):
assert isclose(add(x, y), add(y, x), rel_tol=1e-6)
assert isclose(add(x, add(y, z)), add(add(x, y), z), rel_tol=1e-6)
assert isclose(add(x, 0), x, rel_tol=1e-6)
a, b, c = sorted([x, y, z])
assert add(a, b) <= add(a, c)
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])
Tip: generators are an API simplification mechanism!
Fibonacci sequence: Fib[n] = Fib[n-1] + Fib[n-2]
Fibonacci sequence: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
def fib(n):
if n == 0 or n == 1:
return 1
return fib(n-1) + fib(n-2)
print(f'{fib(10) = }')
def fib(n):
rv = [1, 1]
while True:
if len(rv) == n:
break
rv.append( rv[-1] + rv[-2] )
return rv
print(f'{fib(n=10) = }')
def fib(m):
rv = [1, 1]
while True:
if rv[-1] + rv[-2] >= m:
break
rv.append( rv[-1] + rv[-2] )
return rv
print(f'{fib(m=90) = }')
from time import perf_counter
def fib(t):
rv = [1, 1]
start = perf_counter()
while True:
if perf_counter() - start >= t:
break
rv.append( rv[-1] + rv[-2] )
return rv
print(f'{fib(t=1e-5) = }')
from time import perf_counter
def fib(n=None, m=None, t=None):
rv = [1, 1]
if n is not None:
while True:
if len(rv) == n:
break
rv.append( rv[-1] + rv[-2] )
elif m is not None:
while True:
if rv[-1] + rv[-2] >= m:
break
rv.append( rv[-1] + rv[-2] )
elif t is not None:
start = perf_counter()
while True:
if perf_counter() - start >= t:
break
rv.append( rv[-1] + rv[-2] )
return rv
print(f'{fib(n=10) = }')
print(f'{fib(m=90) = }')
print(f'{fib(t=1e-5) = }')
def fib(a=1, b=1):
while True:
yield a
a, b = b, a + b
for x in fib():
print(f'{x = }')
def fib(a=1, b=1):
while True:
yield a
a, b = b, a + b
from itertools import islice, takewhile
from time import perf_counter
def timed(g, t):
start = perf_counter()
for x in g:
if perf_counter() - start >= t:
break
yield x
fibn = lambda n: islice(fib(), n)
fibm = lambda m: takewhile(lambda x: x < m, fib())
fibt = lambda t: timed(fib(), t)
print(f'{[*fibn(10)] = }')
print(f'{[*fibm(90)] = }')
print(f'{[*fibt(1e-5)] = }')
from hypothesis import given
from hypothesis.strategies import integers
def fib(a=1, b=1):
while True:
yield a
a, b = b, a + b
# nwise(g, n): select overlapping windows of size N from G
from itertools import islice, tee
nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
from itertools import islice
@given(a=integers(), b=integers())
def test_fib(a, b):
for x, y, z in islice(nwise(fib(a, b), 3), 10_000):
assert x + y == z
from pytest import main
if __name__ == '__main__':
main(['-q', __file__])