Possible to create a @synchronized decorator that's aware of a method's object?
Go read:
- https://github.com/GrahamDumpleton/wrapt/tree/develop/blog
and in particular:
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/07-the-missing-synchronized-decorator.md
- https://github.com/GrahamDumpleton/wrapt/blob/develop/blog/08-the-synchronized-decorator-as-context-manager.md
The wrapt
module then contains the @synchronized
decorator described there.
- https://pypi.python.org/pypi/wrapt
The full implementation is flexible enough to do:
@synchronized # lock bound to function1
def function1():
pass
@synchronized # lock bound to function2
def function2():
pass
@synchronized # lock bound to Class
class Class(object):
@synchronized # lock bound to instance of Class
def function_im(self):
pass
@synchronized # lock bound to Class
@classmethod
def function_cm(cls):
pass
@synchronized # lock bound to function_sm
@staticmethod
def function_sm():
pass
Along with context manager like usage as well:
class Object(object):
@synchronized
def function_im_1(self):
pass
def function_im_2(self):
with synchronized(self):
pass
Further information and examples can also be found in:
- http://wrapt.readthedocs.org/en/latest/examples.html
There is also a conference talk you can watch on how this is implemented at:
- https://www.youtube.com/watch?v=EB6AH-85zfY&t=1s
(1) What's confusing is that the func parameter passed to my decorator changes type before it gets passed into the wrapper-generator. This seem is rude and unnecessary. Why does this happen?
It doesn't! Rather, function objects (and other descriptors) produce their __get__
's results when that method of theirs is called -- and that result is the method object!
But what lives in the class
's __dict__
is always the descriptor -- specifically, the function object! Check it out...:
>>> class X(object):
... def x(self): pass
...
>>> X.__dict__['x']
<function x at 0x10fe04e60>
>>> type(X.__dict__['x'])
<type 'function'>
See? No method objects around anywhere at all!-)
Therefore, no im_self
around either, at decoration time -- and you'll need to go with your introspection-based alternative idea.
You can't get self
at decoration time because the decorator is applied at function definition time. No self
exists yet; in fact, the class doesn't exist yet.
If you're willing to store your lock on the instance (which is arguably where a per-instance value should go) then this might do ya:
def synchronizedMethod(func):
def _synchronized(self, *args, **kw):
if not hasattr(self, "_lock"): self._lock = oneLockPerObject(self)
with self._lock: return func(self, *args, **kw)
return _synchronized
You could also generate the lock in your __init__()
method on a base class of some sort, and store it on the instance in the same way. That simplifies your decorator because you don't have to check for the existence of the self._lock
attribute.