Log decorator for methods - python

I'm trying to create a decorator which logs time when method was started, name of the method and arguments of this method.
This decorator works in some situations but I want to make it work in as much situations as possible. So I want to make it work both as a normal method and as a class method.
def log(func):
#wraps(func)
def wrapper(*args, **kwargs):
log(func.__name__ + '; ARGS: {}'.format(','.join(str(args))))
return func(*args, **kwargs)
return wrapper
There are some problems:
It does not work for class methods. To make it work in a class I would have to put self as a first argument here def wrapper(*args, **kwargs): and here return func(*args, **kwargs).
I want to make it work for all types of arguments (lists,strings,ints,dicts etc.). This works only for strings and ints (respectively for floats etc.)
The weird thing is that if I use it for example on a method which takes two integers as an arguments, it prints this to the log: name_of_function; ARGS: (,5,,, ,6,)
I would appreciate if you help me to solve any of these problems.

Does this help?
def func_detail(func):
def func_wrapper(*args,**kwargs):
print(func.__name__)
print(*args)
print(kwargs)
return func(*args,**kwargs)
return func_wrapper
#func_detail
def foo(a,b,c,**kwargs):
return 1
class Person(object):
#func_detail
def __init__(self,name,age,**kwargs):
self.name = name
self.age = age
self.email = kwargs.get('email',None)
me = Person('taesu',24,email='me#me.com')
print(foo(1,1.1,'4',test='me'))

Related

Python: PyCharm IDE method chaining suggestions not working when using decorators

Given below code:
def method_friendly_decorator(method_to_decorate):
def wrapper(self, *args, **kwargs):
print('I got args! Look:'.join(*args, **kwargs))
method_to_decorate(self, *args, **kwargs)
return self
return wrapper
class DecorateMe(object):
def __init__(self):
self.first_name: str = ''
self.last_name: str = ''
#method_friendly_decorator
def print_first_name(self, first_name: str):
self.first_name = first_name
print(f'My first name is {first_name}')
return self
#method_friendly_decorator
def print_last_name(self, last_name: str):
self.last_name = last_name
print(f'My last name is {last_name}')
return self
#method_friendly_decorator
def print_full_name(self):
print(f'My full name is {self.first_name} {self.last_name}')
return self
I'd like to be able to do:
def test_naming():
DecorateMe().print_first_name('Test').print_last_name('User')
When I do not use the decorators on the functions in the DecorateMe class, I can see the suggested functions available, like below:
But, if I keep the method decorators, then I don't get the suggestions any longer:
I'm wondering if this is just "how it works" or if I am doing something wrong here?
Keeping in mind that, when using the decorators, the method chain still works if you know the exact method name to use, but to me, that's defeating the purpose since the whole idea is to be able to see the available methods.
You can define the return type for your wrapper like this:
def method_friendly_decorator(method_to_decorate):
def wrapper(self, *args, **kwargs) -> "DecorateMe": # Define return type here
print('I got args! Look:'.join(*args, **kwargs))
return method_to_decorate(self, *args, **kwargs)
return wrapper
Put DecorateMe in quotes to not get NameError: name 'DecorateMe' is not defined. This is called a forward reference, from PEP 484
Side note: Your wrapper is hard-coded to always return self, making the return statement in each method ignored. It should return the return value of the decorated function as I did above

Wrapper for Python Decorators

Lately, I have started using decorators more extensively. For a project, I require different decorators which I built like in the schema below:
def param_check(fn=None, arg1=None, arg2=None):
def deco(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
#this is the only part that really changes
result = func(arg1, arg2)
return fn(result, *args, **kwargs)
return wrapper
if callable(fn): return deco(fn)
return deco
def func(arg1, arg2):
...do something...
.
.
...return something
However, since this is a lot of repeating code I was wondering what would be the most pythonic way to build a wrapper function that only takes in a function, and then returns a new decorator? I tried several ways but had no success. I was wondering if someone knows how to do that.
I think you just want to add another layer: the outermost layer will take an argument, and that's what gets called inside wrapper, not a hard-coded reference to func.
def decorator_maker(decfun):
def parameterized_decorator(fn=None, arg1=None, arg2=None):
def deco(fn):
#wraps(fn)
def wrapper(*args, **kwargs):
#this is the only part that really changes
result = decfun(arg1, arg2)
return fn(result, *args, **kwargs)
return wrapper
if callable(fn): return deco(fn)
return deco
return parameterized_decorator
def func(arg1, arg2):
pass
param_check = decorator_maker(func)
# Or...
# #decorator_maker
# def func(arg1, arg2):
# ...
#param_check(3, 4)
def f(result):
...

Have python find and evaluate all decorated functions in codebase

I'm trying to have functions in my code base that are callable by a unique identifier, for this I created a decorator that registers the function
global FUNCTION_REGISTRY
FUNCTION_REGISTRY = {}
class FunctionAlreadyRegistered(Exception):
pass
def register_function(function_identifier, function):
global FUNCTION_REGISTRY
if function_identifier not in FUNCTION_REGISTRY:
FUNCTION_REGISTRY[function_identifier] = function
else:
raise FunctionAlreadyRegistered
def call_registered_function(function_identifier, *args, **kwargs):
global FUNCTION_REGISTRY
if function_identifier in FUNCTION_REGISTRY:
FUNCTION_REGISTRY[function_identifier](*args, **kwargs)
class EventHandler(object):
def __init__(self, identifier):
self.identifier = identifier
def __call__(self, original_func):
decorator_self = self
def wrappee(*args, **kwargs):
original_func(*args, **kwargs)
print "REGISTER FUNCTION", decorator_self.identifier
register_function(decorator_self.identifier, wrappee)
return wrappee
and then I apply this decorator like this
#EventHandler(identifier="something.task-created-handler")
def task_created_event_handler(pk=None, *args, **kwargs):
pass
this allows me to easily do the following
call_registered_function("something.task-created-handler",argument="value")
however this decorator is only evaluated when some other file imports this file intentionally, I'd like to evaluate all this decorator through my codebase to register all identified functions
Is there a way to get python to evaluate all this decorators when the runtime starts in a seamless way?

Using Python decorators to add a method to a method

I want to be able to call a method according to some standard format:
outputs = obj.meth(in_0, in_1, ...)
, where outputs is a tuple of arrays, and each input is an array.
However, in most instances, I only return one array, and don't want to be forced to return a tuple of length 1 just for the sake of the standard format. (My actual formatting problem is more complicated but lets stick with this explanation for now.)
I want to be able to define a class like:
class _SomeClass(object):
def __init__(self):
self._amount_to_add = 1
#single_return_format
def add_one(self, x):
return x+self._amount_to_add
And then be able to call it as follows:
obj = _SomeClass()
assert obj.add_one(3) == 4
assert obj.add_one.standard_format(3)==(4, )
Question is: how do I define the decorator to allow this behaviour?
I tried:
def single_return_format(fcn):
fcn.standard_format = lambda *args: (fcn(*args), )
return fcn
, but it fails on the line with the second assert with:
TypeError: add_one() takes exactly 2 arguments (1 given)
Because the add_one requires "self" as an argument, and the the object has not even been created yet at the time the decorator modifies the function.
So Stack, how can I do this?
Notes:
1) I know I could do this with base-classes and inheritance instead, but that becomes a problem when you have more than one method in the class that you want to decorate this way.
2) The actual problem comes from using theano - the standard format is outputs, updates = fcn(*inputs), but most functions don't return any updates, so you want to be able to define those functions in a natural way, but still have the option of calling them according to this standard interface.
That's indeed a problem, because the way the "bound" method is retrieved from the function doesn't consider this way.
I see two ways:
You could just wrap the function:
def single_return_format(fcn):
# TODO Do some functools.wraps here...
return lambda *args, **kwargs: (fcn(*args, **kwargs), )
No fooling around with .standard_format, but a mere replacement of the function. So the function can define itself as returning one value, but can only be called as returning the tuple.
If this is not what you want, you can define a class for decorating methods which overrides __get__ and does the wrapping in a "live fashion". Of course, it can as well redefine __call__ so that it is usable for (standalone, non-method) functions as well.
To get exactly what you want you'd have to write a non-data descriptor and a set of wrapper classes for your functions. The reason for this is that the process of getting functions from objects as methods is highly optimised and it's not possible to hijack this mechanism. Instead you have to write your own classes that simulate this mechanism -- which will slow down your code if you are making lots of small method calls.
The very best way I can think to get the desired functionality is not to use any of the methods that you describe, but rather write a wrapper function that you use when needed to call a normal function in the standard format. eg.
def vectorise(method, *args, **kwargs):
return tuple(method(arg, **kwargs) for arg in args)
obj = _SomeClass()
result = vectorise(obj.add_one, 1, 2, 3)
Indeed, this is how numpy takes functions that operate on one argument and turns them into a function that works on arrays.
import numpy
def add_one(x):
return x + 1
arr = numpy.vectorize(add_one)([1, 2, 3])
If you really, really want to use non-data descriptors then following will work. Be warned these method calls are considerably slower. On my computer a normal method call takes 188 nanoseconds versus 1.53 microseconds for a "simple" method call -- a ten-fold difference. And vectorise call takes half the time a standard_form call does. The vast majority of that time is the lookup of the methods. The actual method calls are quite fast.
class simple_form:
"""Allows a simple function to be called in a standard way."""
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self.func
return SimpleFormMethod(self.func, instance)
class MethodBase:
"""Provides support for getting the string representation of methods."""
def __init__(self, func, instance):
self.func = func
self.instance = instance
def _format(self):
return "<bound {method_class} {obj_class}.{func} of {obj}>".format(
method_class=self.__class__.__name__,
obj_class=self.instance.__class__.__name__,
func=self.func.__name__,
obj=self.instance)
def __str__(self):
return self._format()
def __repr__(self):
return self._format()
class SimpleFormMethod(MethodBase):
def __call__(self, *args, **kwargs):
return self.func(self.instance, *args, **kwargs)
#property
def standard_form(self):
return StandardFormMethod(self.func, self.instance)
class StandardFormMethod(MethodBase):
def __call__(self, *args, **kwargs):
return tuple(self.func(self.instance, arg, **kwargs) for arg in args)
class Number(object):
def __init__(self, value):
self.value = value
def add_to(self, *values):
return tuple(val + self.value for val in values)
#simple_form
def divide_into(self, value):
return value / self.value
num = Number(2)
print("normal method access:", num.add_to, sep="\n")
print("simple form method access:", num.divide_into, sep="\n")
print("standard form method access:", num.divide_into.standard_form, sep="\n")
print("access to underlying function:", Number.divide_into, sep="\n")
print("simple example usage:", num.divide_into(3))
print("standard example usage:", num.divide_into.standard_form(*range(3)))
Dunes gave the correct answer. I've stripped it down to bare bones so that it solves the problem in the question. The stripped-down code is here:
class single_return_format(object):
def __init__(self, func):
self._func = func
def __get__(self, instance, owner):
return SimpleFormMethod(instance, self._func)
class SimpleFormMethod(object):
def __init__(self, instance, func):
self._instance = instance
self._func = func
def __call__(self, *args, **kwargs):
return self._func(self._instance, *args, **kwargs)
#property
def standard_format(self):
return lambda *args, **kwargs: (self._func(self._instance, *args, **kwargs), )
class _SomeClass(object):
def __init__(self):
self._amount_to_add = 1
#single_return_format
def add_one(self, x):
return x+self._amount_to_add
obj = _SomeClass()
assert obj.add_one(3) == 4
assert obj.add_one.standard_format(3) == (4, )

Decorator classes in Python

I want to construct classes for use as decorators with the following principles intact:
It should be possible to stack multiple such class decorators on top off 1 function.
The resulting function name pointer should be indistinguishable from the same function without a decorator, save maybe for just which type/class it is.
Ordering off the decorators should not be relevant unless actually mandated by the decorators. Ie. independent decorators could be applied in any order.
This is for a Django project, and the specific case I am working on now the method needs 2 decorators, and to appear as a normal python function:
#AccessCheck
#AutoTemplate
def view(request, item_id) {}
#AutoTemplate changes the function so that instead of returning a HttpResponse, it just returns a dictionary for use in the context. A RequestContext is used, and the template name is inferred from the method name and module.
#AccessCheck adds additional checks on the user based on the item_id.
I am guessing it's just to get the constructor right and copy the appropriate attributes, but which attributes are these?
The following decorator won't work as I describe:
class NullDecl (object):
def __init__ (self, func):
self.func = func
def __call__ (self, * args):
return self.func (*args)
As demonstrated by the following code:
#NullDecl
#NullDecl
def decorated():
pass
def pure():
pass
# results in set(['func_closure', 'func_dict', '__get__', 'func_name',
# 'func_defaults', '__name__', 'func_code', 'func_doc', 'func_globals'])
print set(dir(pure)) - set(dir(decorated));
Additionally, try and add "print func.name" in the NullDecl constructor, and it will work for the first decorator, but not the second - as name will be missing.
Refined eduffy's answer a bit, and it seems to work pretty well:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for n in set(dir(func)) - set(dir(self)):
setattr(self, n, getattr(func, n))
def __call__ (self, * args):
return self.func (*args)
def __repr__(self):
return self.func
A do-nothing decorator class would look like this:
class NullDecl (object):
def __init__ (self, func):
self.func = func
for name in set(dir(func)) - set(dir(self)):
setattr(self, name, getattr(func, name))
def __call__ (self, *args):
return self.func (*args)
And then you can apply it normally:
#NullDecl
def myFunc (x,y,z):
return (x+y)/z
The decorator module helps you writing signature-preserving decorators.
And the PythonDecoratorLibrary might provide useful examples for decorators.
To create a decorator that wraps functions in a matter that make them indistinguishable from the original function, use functools.wraps.
Example:
def mydecorator(func):
#functools.wraps(func):
def _mydecorator(*args, **kwargs):
do_something()
try:
return func(*args, **kwargs)
finally:
clean_up()
return _mydecorator
# ... and with parameters
def mydecorator(param1, param2):
def _mydecorator(func):
#functools.wraps(func)
def __mydecorator(*args, **kwargs):
do_something(param1, param2)
try:
return func(*args, **kwargs)
finally:
clean_up()
return __mydecorator
return _mydecorator
(my personal preference is to create decorators using functions, not classes)
The ordering of decorators is as follows:
#d1
#d2
def func():
pass
# is equivalent to
def func():
pass
func = d1(d2(func))

Categories