Can I create an @synchronized decorator that knows the method object?

I am trying to create an @synchronized wrapper that creates one Lock for each object and makes method calls thread safe. I can only do this if I can access the .im_self method of the method in the wrapped method.

class B: def f(self): pass assert inspect.ismethod( Bf ) # OK assert inspect.ismethod( B().f ) # OK print Bf # <unbound method Bf> print B().f # <bound method Bf of <__main__.B instance at 0x7fa2055e67e8>> def synchronized(func): # func is not bound or unbound! print func # <function f at 0x7fa20561b9b0> !!!! assert inspect.ismethod(func) # FAIL # ... allocate one lock per C instance return func class C: @synchronized def f(self): pass 

(1) What is confusing is that the func parameter passed to my decorator changes type before it is passed to the wrapper generator. It seems rude and unnecessary. Why is this happening?

(2) Is there some kind of decorator sorceress with which I can make method calls on a mutex-ed object (i.e. one lock for each object, not a class).

UPDATE: There are many examples of @synchronized (lock) wrappers. However, really, I want @synchronized (self). I can solve it like this:

  def synchronizedMethod(func): def _synchronized(*args, **kw): self = args[0] lock = oneLockPerObject(self) with lock: return func(*args, **kw) return _synchronized 

However, since it is much more efficient, I would prefer:

  def synchronizedMethod(func): lock = oneLockPerObject(func.im_self) def _synchronized(*args, **kw): with lock: return func(*args, **kw) return _synchronized 

Is it possible?

+5
source share
3 answers

Go read:

and in particular:

The wrapt module contains the @synchronized decorator described there.

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 the context manager, for example, using:

 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:

Also at the conference you can see a report on how this is implemented:

+18
source

(1) What is confusing is that the func parameter passed to my decorator changes the type before it is passed to the wrapper generator. This one seems rude and unnecessary. Why is this happening?

This is not true! Rather, function objects (and other descriptors) create their __get__ results when this method calls them - and that the result is a method object!

But what lives in class __dict__ is always a descriptor - in particular, a function object! Check this...:

 >>> class X(object): ... def x(self): pass ... >>> X.__dict__['x'] <function x at 0x10fe04e60> >>> type(X.__dict__['x']) <type 'function'> 

Cm? No method objects anywhere at all !)

Therefore, no im_self around or during the decoration - and you will need to go with an alternative idea based on introspection.

+3
source

You cannot get self during decoration because the decorator is applied during function definition. No self ; in fact, the class does not yet exist.

If you want to keep your lock in the instance (which may need to be meaningful for each instance), ya can do this:

 def synchronizedMethod(func): def _synchronized(self, *args, **kw): if not hasttr(self, "_lock"): self._lock = oneLockPerObject(self) with self._lock: return func(self, *args, **kw) return _synchronized 

You can also generate a lock in the __init__() method in a base class of some type and store it on the instance in the same way. This simplifies your decorator because you do not need to check for the presence of the self._lock attribute.

+2
source

Source: https://habr.com/ru/post/1216653/


All Articles