"Test dekoratora argtest w wywołaniach funkcji"

import sys
from argtest import rangetest, typetest, valuetest

def fails(test):
    try:    result = test()
    except: print('[%r]' % sys.exception())
    else:   print('?%s?' % result)

print('--------------------------------------------------------------------')

# Canned use cases: ranges, types

@rangetest(m=(1, 12), d=(1, 31), y=(1900, 2013))
def date(m, d, y):
    print(f'date = {m}/{d}/{y}')

date(1, 2, 1960)
fails(lambda: date(1, 2, 3))

@typetest(a=int, c=float)
def sum(a, b, c, d):
    print(a + b + c + d)

sum(1, 2, 3.0, 4)
sum(1, d=4, b=2, c=3.0)
fails(lambda: sum('hack', 2, 99, 4))
fails(lambda: sum(1, d=4, b=2, c=99))

print('--------------------------------------------------------------------')

# Dowolne/mieszane testy

@valuetest(word1=str.islower, word2=(lambda x: x[0].isupper()))
def msg(word1='sixth', word2='Edition', label='The'):
    print('%s %s %s' % (label, word1, word2))

msg()  # word1 i word2 są domyślne
msg('kod', 'Hakować')
fails(lambda: msg('Python', 'Dekoratory'))
fails(lambda: msg('python', word2='decorators'))

print('--------------------------------------------------------------------')

# Ręczne testy typu i zakresu

@valuetest(A=lambda x: isinstance(x, int), B=lambda x: x > 0 and x < 10)
def manual(A, B):
    print(A + B)

manual(100, 2)
fails(lambda: manual(1.99, 2))
fails(lambda: manual(100, 20))

print('--------------------------------------------------------------------')

# Zagnieżdżanie: uruchomienie obu testów poprzez zagnieżdżenie
# funkcji pośredniczącej w oryginalnej.
# Problem: zewnętrzne poziomy nie sprawdzają argumentów
# pozycyjnych z powodu innej sygnatury funkcji pośredniczącej.
# Gdy trace=True, wszystkie X oprócz ostatniego są klasyfikowane
# jako domyślne z powodu sygnatury funkcji pośredniczącej.


@rangetest(X=(1, 10))
@typetest(Z=str)                      # Argumenty pozycyjne weryfikuje tylko najgłębszy poziom
def nester(X, Y, Z):
    return('%s-%s-%s' % (X, Y, Z))

print(nester(1, 2, 'lp6e'))                # Oryginalna funkcja jest wykonywana poprawnie
fails(lambda: nester(1, 2, 3))             # Wywołana zagnieżdżona funkcja typetest: argumenty pozycyjne
fails(lambda: nester(1, 2, Z=3))           # Wywołana zagnieżdżona funkcja typetest: słowa kluczowe
fails(lambda: nester(0, 2, 'lp6e'))        # <== Zewnętrzna funkcja rangetest nie jest wykonywana: argumenty pozycyjne
fails(lambda: nester(X=0, Y=2, Z='lp6e'))  # Zewnętrzna funkcja rangetest jest wykonywana: słowa kluczowe

