Skip to content Skip to sidebar Skip to footer

Synchronize Access To Python Object

I am looking for a general and simple way to synchronize the methods of a python class that does not use asynchronous calls by itself. Some possibilities come to my mind: First, th

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:

http://pymotw.com/2/Queue/

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"