Skip to content

Instantly share code, notes, and snippets.

@inirudebwoy
Last active August 29, 2015 14:15
Show Gist options
  • Save inirudebwoy/521ec2a2f7792a017176 to your computer and use it in GitHub Desktop.
Save inirudebwoy/521ec2a2f7792a017176 to your computer and use it in GitHub Desktop.
Decorator logging calls with specified arguments, useful when deprecating API's
import logging
from inspect import getouterframes, currentframe
from functools import wraps
logger = logging.getLogger(__name__)
def _get_caller_details(curframe):
"""
Helper retrieving caller details from current frame
:param curframe: frame object
:rtype: tuple of 3
"""
calframe = getouterframes(curframe, 2)
return calframe[1][3], calframe[1][2], calframe[1][1]
def _log_caller_details(curframe):
"""
Helper logging details of caller.
:param curframe: frame object
"""
caller_name, caller_line, \
caller_file_path = _get_caller_details(curframe)
logger.warning('Caller name: %s' % caller_name)
logger.warning('Caller line: %s' % caller_line)
logger.warning('Caller file path: %s' % caller_file_path)
def _log_found_arguments(fn, curframe, found_args):
"""
Helper logging call arguments
:param fn: function object
:param curframe: frame object
:param found_args: list arguments to function call
"""
logger.warning('Method %s called with args: %s' %
(fn.func_name, list(found_args)))
_log_caller_details(curframe)
def _check_args_kwargs(fn, args_seek_list, pos_args, named_args):
"""
Helper checking if positional arguments or keyword arguments
include ones on args_seek_list.
:param fn: function object
:param args_seek_list: list of names of arguments
:param pos_args: tuple of positional arguments, *args
:param named_args: dictionary of keyword arguments, **kwargs
:type args_seek_list: list of strings
:type pos_args: tuple
:type named_args: dict
:rtype: list of strings
"""
args_seek_set = set(args_seek_list)
# map passed values onto function arguments in order
# to filter arguments with default values
pos_args_dict = dict(
zip(fn.func_code.co_varnames[:fn.func_code.co_argcount],
pos_args))
return list(args_seek_set.intersection(pos_args_dict.keys())) + \
list(args_seek_set.intersection(named_args.keys()))
def api_change_logger(arg_check_list):
"""
Decorator function. Function or method decorated will be
checked for any calls with arguments passed to this decorator.
example:
@api_change_logger(['coconut_weight'])
def swallow(coconut_weight=1):
...
Every call of this function with this attribute will be logged.
Calls with default value will not be reported.
:param arg_check_list: list of names of arguments to look for
:type arg_check_list: list of strings
"""
def wrapper(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
found_arguments = _check_args_kwargs(fn, arg_check_list, args,
kwargs)
if found_arguments:
_log_found_arguments(fn, currentframe(), found_arguments)
return fn(*args, **kwargs)
return wrapped
return wrapper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment