Pull to refresh

(Python) Парочка полезных декораторов

Reading time3 min
Views972
import should
  
@should.give((5,2),7)
@should.give(("aa","bbb"),"aabbb")
@should.give(([1],[2,3]), [1,2,3])
@should.give((1,1),1) # test
def add(a,b):
  return a+b
  
@should.throw((1,0), Exception)
@should.throw((5,0), ZeroDivisionError)
@should.throw((5,0), TypeError) # test
@should.throw((5,1), TypeError) # test
def div(a,b):
  return a/b



>pythonw -u "should.py"
[!] add(1, 1) should give 1, but got 2.
[!] div(5, 1) should raise TypeError, but raised nothing.
[!] div(5, 0) should raise TypeError, but raised ZeroDivisionError.
>Exit code: 0




Идея в том, чтоб описать тест-кейсы к функции в качестве декоратора. Тогда они будут выполнять двойную функцию:
  • тестирование (автоматическая проверка при запуске кода после изменения)
  • документация (примеры использования функции)


Реализация:

# file: should.py

def render_func_args(f, args):
  return '%s(%s)' % (f.__name__, ', '.join(map(repr,args)))

def _inform(msg):
  print '[!]', msg

def should(args, expected):
  def check(f):
    val = f(*args)
    if val != expected:
      _inform('%s should give %s, but got %s.' % \
        (render_func_args(f, args), repr(expected), repr(val)))    
  def decor(f):
    check(f)
    return f
    
  return decor
  
def should_raise(args, ExcptClass):
  def check(f):
    excpt = None
    try:
      f(*args)
    except Exception, ex:
      excpt = ex
      
    if excpt == None:
      _inform("%s should raise %s, but raised nothing." % \
        (render_func_args(f, args), ExcptClass.__name__))
    elif not isinstance(excpt, ExcptClass):
      _inform("%s should raise %s, but raised %s." % \
        (render_func_args(f, args), ExcptClass.__name__, excpt.__class__.__name__))
  def decor(f):
    check(f)
    return f
  
  return decor
  
throw = should_raise
give = should
  
if __name__=='__main__':
  import should
  
  @should.give((5,2),7)
  @should.give(("aa","bbb"),"aabbb")
  @should.give(([1],[2,3]), [1,2,3])
  @should.give((1,1),1) # test
  def add(a,b):
    return a+b
    
  @should.throw((1,0), Exception)
  @should.throw((5,0), ZeroDivisionError)
  @should.throw((5,0), TypeError) # test
  @should.throw((5,1), TypeError) # test
  def div(a,b):
    return a/b


* This source code was highlighted with Source Code Highlighter.


UPD. Тем кто не знаком с декораторами — ссылка для ознакомления.
Tags:
Hubs:
Total votes 26: ↑21 and ↓5+16
Comments15

Articles