Injecting class properties via decorators - python

I have a whole bunch of Django TestCases that use the setUp method to initialize some properties that are used throughout numerous tests, the way they're constructed and depend on each other is logic I want to move out of the test cases and reuse
def setUp(self):
self.property_1 = ##some logic
...
I wanted to rewrite these as some convenience wrapper that could be injected into the class with a simple inheritance or decorator, e.g.
#with_property_1(x=1, y=2)
def setUp(self):
...
def with_property_1(**model_kwargs):
def wrapper(f):
def wrapped(*args, **kwargs):
self = args[0]
self.property_1 = ## logic
f(*args, **kwargs)
return wrapped
return wrapper
but the trouble is that PyCharm doesn't recognize that those instance properties exist because nothing inside the TestCase class proper ever sets them. Is there a either another way I can achieve this nicely, or a way to cajole PyCharm into recognizing these properties are legitimate given the existence of the decorator?

Disclaimer: this is untested.
The issue here is that Python cannot just magically put self into context in the decorator (that will not work in any IDE). What you may be forgetting is that self is one of the arguments passed into each class method when you call it. Therefore, it's present in your *args and you can manipulate it.
Here's my trial code:
def with_property_1(**model_kwargs):
def wrapper(f):
def wrapped(*args, **kwargs):
for key, value in model_kwargs.items():
setattr(args[0], key, value)
f(*args, **kwargs)
return wrapped
return wrapper
Explanation:
Iterate over each key/value in your **model_kwargs.
Modify args[0], which should be the self variable, with the kwargs that were provided, using setattr.
Call the function as normal with your updated self variable.

Related

How to check if an object is a method definition?

I'd like to write a decorator that does somewhat different things when it gets a function or a method.
for example, I'd like to write a cache decorator but I don't want to have self as part of the key if it's a method.
def cached(f):
def _internal(*args, **kwargs):
if ismethod(f):
key = create_key(*args[1:], **kwargs) # ignore self from args
else: # this is a regular function
key = create_key(*args, **kwargs)
return actual_cache_mechanism(key, f, *args, **kwargs)
return _internal
class A:
#cached
def b(self, something):
...
#cached
def c(something):
...
the problem is that when #cached is called, it cannot distinguish between methods and functions as both are of type function.
can that even be done? As I'm thinking of it I feel that actually methods have no idea about the context in which they are being defined in...
Thanks!
This is kind of an ugly hack, but you can use obj.__qualname__ to see if obj was defined in a class, by checking if it has a period
if "." in obj.__qualname__":
#obj is a member of an object, so it is a method
I'm not sure if it will work nicely for decorators though, since for this to work the method would need to be defined in the class.
I think it is desirable to avoid such introspecting decorator in the name of good pythonic style.
You can always factor out the function to be cached to accept just the required arguments:
#cached
def func(something):
return ...
class A:
def b(self, something):
self.bvalue = func(something)
For the case mentioned in comments (an object is needed to get the result, but its value does not affect it, e.g. a socket), please refer to these questions: How to ignore a parameter in functools. lru_cache? and Make #lru_cache ignore some of the function arguments

Python Decorators that modifies a bound method and its class' state

I'm trying to write a class method decorator that modifies its class' state. I'm having troubles implementing it at the moment.
Side question: When does a decorator get called? Does it load when the class is instantiated or on during read time when the class read?
What I'm trying to do is this:
class ObjMeta(object):
methods = []
# This should be a decorator that magically updates the 'methods'
# attribute (or list) of this class that's being read by the proxying
# class below.
def method_wrapper(method):
#functools.wraps(method)
def wrapper(*args, **kwargs):
ObjMeta.methods.append(method.__name__)
return method(*args, **kwargs)
return wrapper
# Our methods
#method_wrapper
def method1(self, *args):
return args
#method_wrapper
def method2(self, *args):
return args
class Obj(object):
klass = None
def __init__(self, object_class=ObjMeta):
self.klass = object_class
self._set_methods(object_class)
# We dynamically load the method proxies that calls to our meta class
# that actually contains the methods. It's actually dependent to the
# meta class' methods attribute that contains a list of names of its
# existing methods. This is where I wanted it to be done automagically with
# the help of decorators
def _set_methods(self, object_class):
for method_name in object_class:
setattr(self, method_name, self._proxy_method(method_name))
# Proxies the method that's being called to our meta class
def _proxy_method(self, method_name):
def wrapper(*fargs, **fkwargs):
return getattr(self.klass(*fargs, **fkwargs), method_name)
return wrapper()
I think it's ugly to write a list of methods manually in the class so perhaps a decorator would fix this.
It's for an open-source project I'm working that ports underscore.js to python. I understand that it says I should just use itertools or something. I'm just doing this just for the love of programming and learning. BTW, project is hosted here
Thanks!
There are a few things wrong here.
Anything inside the inner wrapper is called when the method itself is called. Basically, you're replacing the method with that function, which wraps the original. So, your code as it stands would add the method name to the list each time it is called, which probably isn't what you want. Instead, that append should be at the method_wrapper level, ie outside of the inner wrapper. This is called when the method is defined, which happens the first time the module containing the class is imported.
The second thing wrong is that you never actually call the method - you simply return it. Instead of return method you should be returning the value of calling the method with the supplied args - return method(*args, **kwargs).

Inside a decorator-class, access instance of the class which contains the decorated method

I have the following decorator, which saves a configuration file after a method decorated with #saveconfig is called:
class saveconfig(object):
def __init__(self, f):
self.f = f
def __call__(self, *args):
self.f(object, *args)
# Here i want to access "cfg" defined in pbtools
print "Saving configuration"
I'm using this decorator inside the following class. After the method createkvm is called, the configuration object self.cfg should be saved inside the decorator:
class pbtools()
def __init__(self):
self.configfile = open("pbt.properties", 'r+')
# This variable should be available inside my decorator
self.cfg = ConfigObj(infile = self.configfile)
#saveconfig
def createkvm(self):
print "creating kvm"
My problem is that i need to access the object variable self.cfg inside the decorator saveconfig. A first naive approach was to add a parameter to the decorator which holds the object, like #saveconfig(self), but this doesn't work.
How can I access object variables of the method host inside the decorator? Do i have to define the decorator inside the same class to get access?
You have to make your decorator class behave as a descriptor to be able to access the instance:
class saveconfig(object):
def __init__(self, f):
self.f = f
def __get__(self, instance, owner):
def wrapper(*args):
print "Saving configuration"
print instance.cfg
return self.f(instance, *args)
return wrapper
Your code passes object as first parameter to self.f(), where it should pass the pbtools instance.
You are passing object as self to the decorated method. The thing is, you can't easily get self because Python sees the decorated method, which is now an objects, and doesn't consider it a method (that should be passed self when called - or, more generally, that should work as a property returning a bound method). You can work around this, as pointed out by #Sven Marnach.
However, you could easily rewrite this decorator without a class, using a closure (is a bit shorter and also solves the above problem):
def saveconfig(f):
#functools.wraps(f) # to preserve name, docstring, etc.
def wrapper(*args, **kwargs): # **kwargs for compability with functions that use them
f(*args, **kwargs)
# save config
return wrapper
Other notes:
Terminology mixup: There is no class variable in the example. A class variable would be x = ... indented as far as the method definitions and be shared between all instances (specifically, it would be an attribute of the object that is pbtools) - everything on self is an instance attribute.
At class definition time (when you define methods, apply decorators, etc.) there is no self!
You can also use a simple function for what you want:
def saveconfig(f):
# this method replaces the decorated, so `self` will be the pbtools instance
def wrapped(self, *args):
f(self, *args)
# Here i want to access "cfg" defined in pbtools
print "Saving configuration", self.cfg
return wrapped
If saveconfig must be a class then you need Sven's solution.

Decorating a method

In my Python app, I'm using events to communicate between different plugins.
Now, instead of registering the methods to the events manually, I thought I might use decorators to do that for me.
I would like to have it look like this:
#events.listento('event.name')
def myClassMethod(self, event):
...
I have first tried to do it like this:
def listento(to):
def listen_(func):
myEventManager.listen(to, func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
return listen_
When I callmyEventManger.listen('event', self.method)from within the instance, everything is running fine. However, if I use the decorator approach, theselfargument is never passed.
The other approach that I have tried, after searching for a solution on the Internet, is to use a class as a decorator:
class listen(object):
def __init__(self, method):
myEventManager.listen('frontend.route.register', self)
self._method = method
self._name = method.__name__
self._self = None
def __get__(self, instance, owner):
self._self = instance
return self
def __call__(self, *args, **kwargs):
return self._method(self._self, *args, **kwargs)
The problem with this approach is that I don't really understand the concept of__get__, and that I don't know how I'd incorporate the parameters.
Just for testing I have tried using a fixed event to listen to, but with this approach, nothing happens. When I add print statements, I can see that__init__is called.
If I add an additional, "old style" event registration, both__get__and__call__get executed, and the event works, despite the new decorator.
What would be the best way to achieve what I'm looking for, or am I just missing some important concept with decorators?
The decorator approach isn't working because the decorator is being called when the class is constructed, not when the instance is constructed. When you say
class Foo(object):
#some_decorator
def bar(self, *args, **kwargs):
# etc etc
then some_decorator will be called when the class Foo is constructed, and it will be passed an unbound method, not the bound method of an instance. That's why self isn't getting passed.
The second method, on the other hand, could work as long as you only ever create one object of each class you use the decorator on, and if you're a bit clever. If you define listen as above and then define
class Foo(object):
def __init__(self, *args, **kwargs):
self.some_method = self.some_method # SEE BELOW FOR EXPLANATION
# etc etc
#listen
def some_method(self, *args, **kwargs):
# etc etc
Then listen.__get__ would be called when someone tried to call f.some_method directly for some f...but the whole point of your scheme is that no-one's doing that! The event call back mechanism is calling the listen instance directly 'cause that's what it gets passed and the listen instance is calling the unbound method it squirrelled away when it was created. listen.__get__ won't ever get called and the _self parameter is never getting set properly...unless you explicitly access self.some_method yourself, as I did in the __init__ method above. Then listen.__get__ will be called upon instance creation and _self will be set properly.
Problem is (a) this is a horrible, horrible hack and (b) if you try to create two instances of Foo then the second one will overwrite the _self set by the first, because there's still only one listen object being created, and that's associated to the class, not the instance. If you only ever use one Foo instance then you're fine, but if you have to have the event trigger two different Foo's then you'll just have to use your "old style" event registration.
The TL,DR version: decorating a method decorates the unbound method of the class, whereas you want your event manager to get passed the bound method of an instance.
Part of your code is:
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return func
which defines wrapper then completely ignores it and returns func instead. Hard to say whether this is a real problem in your real code because obviously you're not posting that (as proven by typoes such as myEventManagre, myEvnetManager, &c), but if that's what you're doing in your actual code it is obviously part of your problem.

how to pass in dynamic data to decorators

I am trying to write a base crud controller class that does the
following:
class BaseCrudController:
model = ""
field_validation = {}
template_dir = ""
#expose(self.template_dir)
def new(self, *args, **kwargs)
....
#validate(self.field_validation, error_handler=new)
#expose()
def post(self, *args, **kwargs):
...
My intent is to have my controllers extend this base class, set the
model, field_validation, and template locations, and am ready to go.
Unfortunately, decorators (to my understanding), are interpreted when
the function is defined. Hence it won't have access to instance's
value. Is there a way to pass in dynamic data or values from the sub
class?
For example:
class AddressController(BaseCrudController):
model = Address
template_dir = "addressbook.templates.addresses"
When I try to load AddressController, it says "self is not defined". I am assuming that the base class is evaluating the decorator before the sub class is initialized.
Thanks,
Steve
Perhaps using a factory to create the class would be better than subclassing:
def CrudControllerFactory(model, field_validation, template_dir):
class BaseCrudController:
#expose(template_dir)
def new(self, *args, **kwargs)
....
#validate(field_validation, error_handler=new)
#expose()
def post(self, *args, **kwargs):
....
return BaseCrudController
Unfortunately, decorators (to my
understanding), are interpreted when
the function is defined. Hence it
won't have access to instance's value.
Is there a way to pass in dynamic data
or values from the sub class?
The template needs to be called with the name of the relevant attribute; the wrapper can then get that attribute's value dynamically. For example:
import functools
def expose(attname=None):
if attname:
def makewrapper(f):
#functools.wraps(f)
def wrapper(self, *a, **k):
attvalue = getattr(self, attname, None)
...use attvalue as needed...
return wrapper
return makewrapper
else:
...same but without the getattr...
Note that the complication is only because, judging from the code snippets in your Q, you want to allow the expose decorator to be used both with and without an argument (you could move the if attname guard to live within wrapper, but then you'd uselessly repeat the check at each call -- the code within wrapper may also need to be pretty different in the two cases, I imagine -- so, shoehorning two different control flows into one wrapper may be even more complicated). BTW, this is a dubious design decision, IMHO. But, it's quite separate from your actual Q about "dynamic data".
The point is, by using the attribute name as the argument, you empower your decorator to fetch the value dynamically "just in time" when it's needed. Think of it as "an extra level of indirection", that well-known panacea for all difficulties in programming!-)

Categories