Synchronize Access To Python Object
Solution 1:
If you really need to, you can use the black magic of python metaclasses to dynamically add a decorator to each method of the class at class creation time. The following is a quick example of how you might do this. It creates a generic synchronizer metaclass, which you then subclass to create synchronizers for each specific class. Finally you subclass the original class that you want to synchronize and apply the synchronizer metaclass to it. Note I'm using python 3 metaclass syntax.
from threading import RLock
#
# Generic synchronizer
#
class SynchroMeta(type):
def __init__(cls, name, bases, dct):
super(SynchroMeta, cls).__init__(name, bases, dct)
dct['__lock__'] = RLock()
def sync_decorator(f):
def inner(*args, **kwargs):
with dct['__lock__']:
print("Synchronized call")
return f(*args, **kwargs)
return inner
for b in bases:
if b.__name__ == cls.sync_object_name:
for name, obj in b.__dict__.items():
# Synchronize any callables, but avoid special functions
if hasattr(obj, '__call__') and not name.startswith('__'):
print("Decorating: ", name)
setattr(b, name, sync_decorator(obj))
#
# Class you want to synchronize
#
class MyClass:
def __init__(self, v):
self.value = v
def print_value(self):
print("MyClass.value: ", self.value)
#
# Specific synchronizer for "MyClass" type
#
class MyClassSynchro(SynchroMeta):
sync_object_name = "MyClass"
#
# Wrapper that uses the specific synchronizer metaclass
#
class MyClassWrapper(MyClass, metaclass=MyClassSynchro):
pass
if __name__ == "__main__":
w = MyClassWrapper('hello')
w.print_value()
Solution 2:
I went with the SynchronizeProxy, and it seems to work so far. Since this solution comes closest to what I need, I will select my answer as a solution. If I experience any problems, I will update this answer.
#!/usr/bin/env python
import types
from pprint import pformat
from threading import RLock
class SynchronizeMethodWrapper:
"""
Wrapper object for a method to be called.
"""
def __init__( self, obj, func, name, rlock ):
self.obj, self.func, self.name = obj, func, name
self.rlock = rlock
assert obj is not None
assert func is not None
assert name is not None
def __call__( self, *args, **kwds ):
"""
This method gets called before a method is called to sync access to the core object.
"""
with self.rlock:
rval = self.func(*args, **kwds)
return rval
class SynchronizeProxy(object):
"""
Proxy object that synchronizes access to a core object methods and attributes that don't start with _.
"""
def __init__( self, core ):
self._obj = core
self.rlock = RLock()
def __getattribute__( self, name ):
"""
Return a proxy wrapper object if this is a method call.
"""
if name.startswith('_'):
return object.__getattribute__(self, name)
else:
att = getattr(self._obj, name)
if type(att) is types.MethodType:
return SynchronizeMethodWrapper(self, att, name, object.__getattribute__(self, "rlock"))
else:
return att
def __setitem__( self, key, value ):
"""
Delegate [] syntax.
"""
name = '__setitem__'
with self.rlock:
att = getattr(self._obj, name)
pmeth = SynchronizeMethodWrapper(self, att, name, self.rlock)
pmeth(key, value)
Solution 3:
You could use a queue to proxy the calls to that class:
Update:
Actually now that I think about it a queue might not be the best solution because you'd probably have to adapt the class at least a little bit to work with the queue. If you wan't to use locks you could take a look at threading.Lock() here: http://docs.python.org/2/library/threading.html
Post a Comment for "Synchronize Access To Python Object"