Check condition before method call - python

I have a class named Server which can be started and stopped. Certain methods should not be called unless the Server is started, in which case a NotConnectedException should be raised. Is there a way to call a method before every method in a class and determine if class variable _started is set to True?
I tried using a decorator, but the decorator function does not have access to the class variable. I was trying to do something like this:
class Server(object):
_started = False
def started(self):
if(self._started == False):
raise NotConnectedException
#started
def doServerAction(self):
...

Remember what decorators are:
#decorate
def foo(...):
...
is exactly equivalent to:
def foo(...):
...
foo = decorate(foo)
The decorator is called on the function, so calling the first parameter self makes no sense. Also, the decorator is called on the function when it is defined, and whatever it returns is used in place of the function. So even if your started decorator didn't throw an AttributeError by trying to access the _started attribute of a function, it would then return None, making all your methods set to None, and thus not even be callable.
What you want is something like this:
import functools
def started(func):
#functools.wraps(func)
def wrapper(self, *args, **kwargs):
if not self._started:
raise ...
else:
return func(self, *args, **kwargs)
return wrapper
Almost all decorators are of this form; they take a function, create a wrapper that does something "around" the received function, and then return the wrapper. The use of functools.wraps here is a convenience if you ever end up working with this code in an interactive interpreter session; it automatically updates the wrapper function with the name and docstring of the original function, which makes the decorated functions "look like" the original function a bit more.
It's irrelevant whether this is defined inside the class or not.

Related

How to set a repr for a function itself? [duplicate]

This question already has answers here:
Possible to change a function's repr in python?
(4 answers)
Closed 2 years ago.
__repr__ is used to return a string representation of an object, but in Python a function is also an object itself, and can have attributes.
How do I set the __repr__ of a function?
I see here that an attribute can be set for a function outside the function, but typically one sets a __repr__ within the object definition itself, so I'd like to set the repr within the function definition itself.
My use case is that I am using tenacity to retry a networking function with exponential backoff, and I want to log the (informative) name of the function I have called last.
retry_mysql_exception_types = (InterfaceError, OperationalError, TimeoutError, ConnectionResetError)
def return_last_retry_outcome(retry_state):
"""return the result of the last call attempt"""
return retry_state.outcome.result()
def my_before_sleep(retry_state):
print("Retrying {}: attempt {} ended with: {}\n".format(retry_state.fn, retry_state.attempt_number, retry_state.outcome))
#tenacity.retry(wait=tenacity.wait_random_exponential(multiplier=1, max=1200),
stop=tenacity.stop_after_attempt(30),
retry=tenacity.retry_if_exception_type(retry_mysql_exception_types),
retry_error_callback=return_last_retry_outcome,
before_sleep=my_before_sleep)
def connect_with_retries(my_database_config):
connection = mysql.connector.connect(**my_database_config)
return connection
Currently retry_state.fn displays something like <function <lambda> at 0x1100f6ee0> like #chepner says, but I'd like to add more information to it.
You could use a decorator that returns a class with the __call__ and __repr__ set:
class CustomReprFunc:
def __init__(self, f, custom_repr):
self.f = f
self.custom_repr = custom_repr
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
def __repr__(self):
return self.custom_repr(self.f)
def set_repr(custom_repr):
def set_repr_decorator(f):
return CustomReprFunc(f, custom_repr)
return set_repr_decorator
#set_repr(lambda f: f.__name__)
def func(a):
return a
print(repr(func))
I think a custom decorator could help:
import functools
class reprable:
"""Decorates a function with a repr method.
Example:
>>> #reprable
... def foo():
... '''Does something cool.'''
... return 4
...
>>> foo()
4
>>> foo.__name__
'foo'
>>> foo.__doc__
'Does something cool.'
>>> repr(foo)
'foo: Does something cool.'
>>> type(foo)
<class '__main__.reprable'>
"""
def __init__(self, wrapped):
self._wrapped = wrapped
functools.update_wrapper(self, wrapped)
def __call__(self, *args, **kwargs):
return self._wrapped(*args, **kwargs)
def __repr__(self):
return f'{self._wrapped.__name__}: {self._wrapped.__doc__}'
Demo: http://tpcg.io/uTbSDepz.
It's already set.
>>> repr(lambda x:x)
'<function <lambda> at 0x1100f6ee0>'
The problem is that the function type is immutable, so you can't just assign a new function to function.__repr__, and you also can't create a subtype of function in order to override __repr__. (Not that creating instances of the subclass would be easy, even if it were possible to define it.)
You can't do this for actual functions; the function type is immutable, and already defines a __repr__, and __repr__ is looked up on the type, not the instance, so changing __repr__ on a given function doesn't change behavior.
While probably not useful in this case, you can make your own callable class (analogous to C++ functors), and those can define their own __repr__. For example:
class myfunction:
#staticmethod # Avoids need to receive unused self
def __call__(your, args, here):
... do stuff and return as if it were a function ...
#classmethod # Know about class, but again, instance is useless
def __repr__(cls):
return f'{cls.__name__}(a, b, c)'
which you could convert to a singleton instance of the class (making it equivalent to a plain function in how you use it) at the end by just doing:
myfunction = myfunction()
to replace the class with a single instance of the class.
Note: In real code, I'd almost certainly just change where I'm printing it to print in a more useful way without modifying the function. This doesn't have much overhead over a plain function or a wrapped plain function (since we put the function itself in __call__ rather than wrapping, making it faster, but requiring a separate class for each "friendly repr function"), but it's just not the job of the function to decide how to represent itself in a human-friendly way; that's your job, based on the situation.
You can change retry_state.fn to retry_state.__name__. I use many decorators like this. If you add a decorator, it will be called each time a function of interest is called.
def display_function(func):
""" This decorator prints before and after running """
#functools.wraps(func)
def function_wrapper(*args, **kwargs):
print(f'\nNow: Calling {func.__name__}.')
entity = func(*args, **kwargs)
print(f'Done: Calling {func.__name__}.\n')
return entity
return function_wrapper
Additionally, the retrying module in python allows you to do some of what you're doing by default. I often use a decorator:
import retrying
#retrying.retry(wait_exponential_multiplier=1000, wait_exponential_max=10000)

How to run a method before/after all class function calls with arguments passed?

There are some interesting ways to run a method before every method in a class in questions such as Python: Do something for any method of a class?
However that solution doesn't let us pass arguments.
There's a decorator solution on Catch "before/after function call" events for all functions in class but I don't want to have to go back and decorate all my classes.
Is there a way to run a pre/post operation that's dependent on the arguments passed for every invocation of an object's method?
Example:
class Stuff(object):
def do_stuff(self, stuff):
print(stuff)
a = Stuff()
a.do_stuff('foobar')
"Pre operation for foobar"
"foobar"
"Post operation for foobar"
So I figured it out after a lot of experimentation.
Basically in the metaclass' __new__ you can iterate through every method in the class' namespace and swap out every method in the class being created with a new version that runs the before logic, the function itself, and the after logic. Here's a sample:
class TestMeta(type):
def __new__(mcl, name, bases, nmspc):
def replaced_fnc(fn):
def new_test(*args, **kwargs):
# do whatever for before function run
result = fn(*args, **kwargs)
# do whatever for after function run
return result
return new_test
for i in nmspc:
if callable(nmspc[i]):
nmspc[i] = replaced_fnc(nmspc[i])
return (super(TestMeta, mcl).__new__(mcl, name, bases, nmspc))
Note that if you use this code as is it will run the pre/post operation for init and other builtin functions as well.

Evaluation of decorator argument cause NameError

I have a decorator that takes one argument:
def supportSlice(ret_type=None):
...
and I want to apply it to a member function of a class, with the class itself as the argument:
class BitArray:
#supportSlice(ret_type=BitArray)
def __getitem__(self, idx):
...
But I got an NameError when evaluating #supportSlice(ret_type=BitArray) said "name 'BitArray' is not defined". It seems like the decorator isn't evaluated in the environment I expected.
My question is when exactly a decorator of a member function is evaluated? And also is there any walk-around to achieve the goal I described above?
At the time the decorator runs (inside class BitArray's body), the name BitArray is not defined yet! A class's name gets defined after its body is done and its metaclass (usually type) builds the class object.
The simplest work-around is to delay the determination of ret_type to later...:
def supportSlice(ret_type=None):
def wrapper(func):
def wrapfunc(self, *a, **k):
if ret_type is None:
rt = type(self)
else:
rt = ret_type
# rest here, using rt
return wrapfunc
return wrapper
(it's simpler of course if you don't need to force a ret_type different from type(self) so you can use an arg-less decorator and thereby lose one level of function nesting:).

How to decorate an object method?

I need to decorate a object's method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied with argv), so a same object could be decorated 3 times, 2 times, or not be decorated at all.
Here is some context, the program is a puzzle solver, the main behavior is to find a solution for the puzzle automatically, by automatically I mean without user intervention. And here is where the decoration gets to play, one of the things I want to is draw a graph of what happened during the execution, but I want to do so only when the flag --draw-graph is used.
Here is what I've tried:
class GraphDecorator(object):
def __init__(self, wrappee):
self.wrappee = wrappee
def method(self):
# do my stuff here
self.wrappee.method()
# do more of stuff here
def __getattr__(self,attr):
return getattr(self.wrappee,attr)
And why it did NOT work:
It did not work because of the way I built the application, when a method that did not exist in my Decorator class was called it felt back to the implementation of the decorated class, the problem is that the application always started invoking the method run that did not need to be decorated, so the undecorated fall back was used and from inside the undecorated form it always called undecorated methods, what I needed was to replace the method from the object, not to proxy it:
# method responsible to replace the undecorated form by the decorated one
def graphDecorator(obj):
old_method = obj.method
def method(self):
# do my stuff here
old_method()
# do more of my stuff
setattr(obj,'method',method) # replace with the decorated form
And here is my problem, the decorated form does not receive self when it is called resulting on a TypeError because of the wrong number of arguments.
The problem was that I couldn't use func(self) as a method. The reason is that setattr() method does not bound the function, and the function acts like it a static method - not a class method -, thanks to the introspective nature of python I've able to come up with this solution:
def decorator(obj):
old_func = obj.func # can't call 'by name' because of recursion
def decorated_func(self):
# do my stuff here
old_func() # does not need pass obj
# do some othere stuff here
# here is the magic, this get the type of a 'normal method' of a class
method = type(obj.func)
# this bounds the method to the object, so self is passed by default
obj.func = method(decorated_func, obj)
I think this is the best way to decorate a object's method at runtime, though it would be nice to find a way to call method() directly, without the line method = type(obj.func)
You might want to use __getattribute__ instead of __getattr__ (the latter being only called if "standard" lookup fails):
class GraphDecorator(object):
def __init__(self, wrappee):
self.__wrappee = wrappee
def method(self):
# do my stuff here
self.wrappe.method()
# do more of stuff here
def __getattribute__(self, name):
try:
wrappee = object.__getattribute__(self, "_GraphDecorator__wrappee")
return getattr(wrappee, name)
except AttributeError:
return object.__getattribute__(self, name)
I need to decorate a object's method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied with argv), so a same object could be decorated 3 times, 2 times, or not be decorated at all.
The above is unfortunately incorrect, and what you are trying to do is unnecessary.
You can do this at runtime like so. Example:
import sys
args = sys.argv[1:]
class MyClass(object):
pass
if args[0]=='--decorateWithFoo':
MyClass = decoratorFoo(MyClass)
if args[1]=='--decorateWithBar'
MyClass = decoratorBar(MyClass)
The syntax:
#deco
define something
Is the same thing as:
define something
something = deco(something)
You could also make a decorator factory #makeDecorator(command_line_arguments)
"It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program"
The don't use decorators. Decorators are only syntactical support for wrappers, you can just as well use normal function/method calls instead.

Is it possible to determine which class a function is a method of?

For example, if I'm decorating a method like so
def my_decorator(fn):
# Do something based on the class that fn is a method of
def decorated_fn(*args, **kwargs):
fn(*args, **kwargs)
return decorated_fn
class MyClass(object):
#my_decorator
def my_method(self, param):
print "foo"
Is it possible in my_decorator to determine where fn came from?
Short answer: No.
Longer answer: You can do it by mucking about in the stack trace (see the inspect module) but it's not a great idea.
Full answer: At the time the function gets decorated, it's still an unbound function. Try the following:
def my_dec(fn):
print dir(fn) # Has "func_code" and "func_name"
return fn
class A(object):
#my_dec
def test(self):
pass
print dir(A.test) # Has "im_class" and "im_self"
You can see that the raw function gets passed to the decorator, while the bound function is available after the class is declared.
The way to accomplish this is to just the function decorator in conjunction with either a metaclass or a class decorator. In either case, the function decorator can set a flag on the function, and the metaclass or class decorator can look for it and do the appropriate thing.
No. You'll have to defer it until decorated_fn() is called.

Categories

Resources