# logcall.py
from functools import wraps
def logged(func):
print('Adding logging to', func.__name__)
@wraps(func)
def wrapper(*args,**kwargs):
print('Calling', func.__name__)
return func(*args,**kwargs)
return wrapper
# logcall.py
from functools import wraps
...
def logformat(fmt):
def logged(func):
print('Adding logging to', func.__name__)
@wraps(func)
def wrapper(*args, **kwargs):
print(fmt.format(func=func))
return func(*args, **kwargs)
return wrapper
return logged
The earlier @logged
decorator can be rewritten as follows:
logged = logformat('Calling {func.__name__}')
You can get the code to work if you interchange the order of the decorators. For example:
from logcall import logged
class Spam:
@logged
def instance_method(self):
pass
@classmethod
@logged
def class_method(cls):
pass
@staticmethod
@logged
def static_method():
pass
@property
@logged
def property_method(self):
pass
Ponder why it doesn't work in the original order. Is there any way to make
the @logged
decorator work regardless of the order in which its applied?
# validate.py
...
from inspect import signature
from functools import wraps
def validated(func):
sig = signature(func)
# Gather the function annotations
annotations = dict(func.__annotations__)
# Get the return annotation (if any)
retcheck = annotations.pop('return', None)
@wraps(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
errors = []
# Enforce argument checks
for name, validator in annotations.items():
try:
validator.check(bound.arguments[name])
except Exception as e:
errors.append(f' {name}: {e}')
if errors:
raise TypeError('Bad Arguments\n' + '\n'.join(errors))
result = func(*args, **kwargs)
# Enforce return check (if any)
if retcheck:
try:
retcheck.check(result)
except Exception as e:
raise TypeError(f'Bad return: {e}') from None
return result
return wrapper
def enforce(**annotations):
retcheck = annotations.pop('return_', None)
def decorate(func):
sig = signature(func)
@wraps(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
errors = []
# Enforce argument checks
for name, validator in annotations.items():
try:
validator.check(bound.arguments[name])
except Exception as e:
errors.append(f' {name}: {e}')
if errors:
raise TypeError('Bad Arguments\n' + '\n'.join(errors))
result = func(*args, **kwargs)
if retcheck:
try:
retcheck.check(result)
except Exception as e:
raise TypeError(f'Bad return: {e}') from None
return result
return wrapper
return decorate