dbus/python _dbus.py, 1.14, 1.15 dbus_bindings.pyx, 1.15, 1.16 service.py, 1.15, 1.16

Robert McQueen robot101 at freedesktop.org
Mon Nov 7 04:14:55 PST 2005


Update of /cvs/dbus/dbus/python
In directory gabe:/tmp/cvs-serv22655/python

Modified Files:
	_dbus.py dbus_bindings.pyx service.py 
Log Message:
2005-11-07  Robert McQueen  <robot101 at debian.org>

	* python/_dbus.py: Add WeakReferenceDictionary cache of dbus.Bus
	instances to stop madness of creating new instances representing
	the same bus connection all the time, rendering any tracking of
	match rules and bus names quite meaningless. Caught a bug where
	the private argument to SessionBus() and friends was being passed
	in as use_default_mainloop by mistake. Still some problems with
	multiple dbus_binding.Connection instances representing the same
	low-level connection (eg when you use both SessionBus() and
	StarterBus() in same process), but it's a lot better now than it
	was.

	* python/dbus_bindings.pyx: Add constants with the return values
	for bus_request_name().

	* python/service.py: Store bus name instances in a per-dbus.Bus cache
	and retrieve the same instances for the same name, so deletion can be
	done with refcounting. Also now throws some kind of error if you
	don't actually get the name you requested, unlike previously...

	* test/python/test-client.py: Add tests for instance caching of buses
	and bus name objects.

Index: _dbus.py
===================================================================
RCS file: /cvs/dbus/dbus/python/_dbus.py,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -d -r1.14 -r1.15
--- _dbus.py	3 Nov 2005 16:13:52 -0000	1.14
+++ _dbus.py	7 Nov 2005 12:14:53 -0000	1.15
@@ -42,14 +42,14 @@
 """
 
 import dbus
-
 import dbus_bindings
+import weakref
 
 from proxies import *
 from exceptions import *
 from matchrules import *
 
-class Bus:
+class Bus(object):
     """A connection to a DBus daemon.
 
     One of three possible standard buses, the SESSION, SYSTEM,
@@ -65,19 +65,54 @@
     ProxyObjectClass = ProxyObject
 
     START_REPLY_SUCCESS = dbus_bindings.DBUS_START_REPLY_SUCCESS
-    START_REPLY_ALREADY_RUNNING = dbus_bindings.DBUS_START_REPLY_ALREADY_RUNNING 
+    START_REPLY_ALREADY_RUNNING = dbus_bindings.DBUS_START_REPLY_ALREADY_RUNNING
 
-    def __init__(self, bus_type=TYPE_SESSION, use_default_mainloop=True, private=False):
-        self._bus_type = bus_type
-        self._connection = dbus_bindings.bus_get(bus_type, private)
+    _shared_instances = weakref.WeakValueDictionary()
 
-        self._connection.add_filter(self._signal_func)
-        self._match_rule_tree = SignalMatchTree()
+    def __new__(cls, bus_type=TYPE_SESSION, use_default_mainloop=True, private=False):
+        if (not private and bus_type in cls._shared_instances):
+            return cls._shared_instances[bus_type]
+
+        # this is a bit odd, but we create instances of the subtypes
+        # so we can return the shared instances if someone tries to
+        # construct one of them (otherwise we'd eg try and return an
+        # instance of Bus from __new__ in SessionBus). why are there
+        # three ways to construct this class? we just don't know.
+        if bus_type == cls.TYPE_SESSION:
+            subclass = SessionBus
+        elif bus_type == cls.TYPE_SYSTEM:
+            subclass = SystemBus
+        elif bus_type == cls.TYPE_STARTER:
+            subclass = StarterBus
+        else:
+            raise ValueError('invalid bus_type %s' % bus_type)
+
+        bus = object.__new__(subclass)
+
+        bus._bus_type = bus_type
+        bus._bus_names = weakref.WeakValueDictionary()
+        bus._match_rule_tree = SignalMatchTree()
+
+        # FIXME: if you get a starter and a system/session bus connection
+        # in the same process, it's the same underlying connection that
+        # is returned by bus_get, but we initialise it twice
+        bus._connection = dbus_bindings.bus_get(bus_type, private)
+        bus._connection.add_filter(bus._signal_func)
 
         if use_default_mainloop:
             func = getattr(dbus, "_dbus_mainloop_setup_function", None)
-            if func != None:
-                func(self)
+            if func:
+                func(bus)
+
+        if not private:
+            cls._shared_instances[bus_type] = bus
+
+        return bus
+
+    def __init__(self, *args, **keywords):
+        # do nothing here because this can get called multiple times on the
+        # same object if __new__ returns a shared instance
+        pass
 
     def close(self):
         self._connection.close()
@@ -87,20 +122,20 @@
 
     def get_session(private=False):
         """Static method that returns the session bus"""
-        return SessionBus(private)
+        return SessionBus(private=private)
 
     get_session = staticmethod(get_session)
 
     def get_system(private=False):
         """Static method that returns the system bus"""
-        return SystemBus(private)
+        return SystemBus(private=private)
 
     get_system = staticmethod(get_system)
 
 
     def get_starter(private=False):
         """Static method that returns the starter bus"""
-        return StarterBus(private)
+        return StarterBus(private=private)
 
     get_starter = staticmethod(get_starter)
 
@@ -213,24 +248,24 @@
 class SystemBus(Bus):
     """The system-wide message bus
     """
-    def __init__(self, use_default_mainloop=True, private=False):
-        Bus.__init__(self, Bus.TYPE_SYSTEM, use_default_mainloop, private)
+    def __new__(cls, use_default_mainloop=True, private=False):
+        return Bus.__new__(cls, Bus.TYPE_SYSTEM, use_default_mainloop, private)
 
 class SessionBus(Bus):
     """The session (current login) message bus
     """
-    def __init__(self, use_default_mainloop=True, private=False):
-        Bus.__init__(self, Bus.TYPE_SESSION, use_default_mainloop, private)
+    def __new__(cls, use_default_mainloop=True, private=False):
+        return Bus.__new__(cls, Bus.TYPE_SESSION, use_default_mainloop, private)
 
 class StarterBus(Bus):
     """The bus that activated this process (if
     this process was launched by DBus activation)
     """
-    def __init__(self, use_default_mainloop=True, private=False):
-        Bus.__init__(self, Bus.TYPE_STARTER, use_default_mainloop, private)
+    def __new__(cls, use_default_mainloop=True, private=False):
+        return Bus.__new__(cls, Bus.TYPE_STARTER, use_default_mainloop, private)
 
 class Interface:
-    """An inteface into a remote object
+    """An interface into a remote object
 
     An Interface can be used to wrap ProxyObjects
     so that calls can be routed to their correct

Index: dbus_bindings.pyx
===================================================================
RCS file: /cvs/dbus/dbus/python/dbus_bindings.pyx,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- dbus_bindings.pyx	4 Nov 2005 12:17:54 -0000	1.15
+++ dbus_bindings.pyx	7 Nov 2005 12:14:53 -0000	1.16
@@ -1748,8 +1748,13 @@
 
     return retval
 
-SERVICE_FLAG_PROHIBIT_REPLACEMENT = 0x1
-SERVICE_FLAG_REPLACE_EXISTING     = 0x2
+NAME_FLAG_PROHIBIT_REPLACEMENT = 0x1
+NAME_FLAG_REPLACE_EXISTING     = 0x2
+
+REQUEST_NAME_REPLY_PRIMARY_OWNER = 1
+REQUEST_NAME_REPLY_IN_QUEUE      = 2
+REQUEST_NAME_REPLY_EXISTS        = 3
+REQUEST_NAME_REPLY_ALREADY_OWNER = 4
 
 def bus_request_name(Connection connection, service_name, flags=0):
     cdef DBusError error

Index: service.py
===================================================================
RCS file: /cvs/dbus/dbus/python/service.py,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- service.py	4 Nov 2005 12:17:59 -0000	1.15
+++ service.py	7 Nov 2005 12:14:53 -0000	1.16
@@ -6,19 +6,60 @@
 from decorators import method
 from decorators import signal
 
-class BusName:
+class BusName(object):
     """A base class for exporting your own Named Services across the Bus
     """
-    def __init__(self, named_service, bus=None):
-        self._named_service = named_service
-                             
+    def __new__(cls, name, bus=None):
+        # get default bus
         if bus == None:
-            # Get the default bus
-            self._bus = _dbus.Bus()
+            bus = _dbus.Bus()
+
+        # see if this name is already defined, return it if so
+        if name in bus._bus_names:
+            return bus._bus_names[name]
+
+        # otherwise register the name
+        retval = dbus_bindings.bus_request_name(bus.get_connection(), name)
+        print retval
+        # TODO: more intelligent tracking of bus name states?
+        if retval == dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
+            pass
+        elif retval == dbus_bindings.REQUEST_NAME_REPLY_IN_QUEUE:
+            # you can't arrive at this state via the high-level bindings
+            # because you can't put flags in, but... who knows?
+            print "joined queue for %s" % name
+            pass
+        elif retval == dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
+            raise dbus_bindings.DBusException('requested name %s already exists' % name)
+        elif retval == dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
+            # if this is a shared bus which is being used by someone
+            # else in this process, this can happen legitimately
+            print "already owner of %s" % name
+            pass
         else:
-            self._bus = bus
+            raise dbus_bindings.DBusException('requesting name %s returned unexpected value %s' % (name, retval))
 
-        dbus_bindings.bus_request_name(self._bus.get_connection(), named_service)
+        # and create the object
+        bus_name = object.__new__(cls)
+        bus_name._bus = bus
+        bus_name._name = name
+
+        # cache instance
+        bus._bus_names[name] = bus_name
+
+        return bus_name
+
+    # do nothing because this is called whether or not the bus name
+    # object was retrieved from the cache or created new
+    def __init__(self, *args, **keywords):
+        pass
+
+    # we can delete the low-level name here because these objects
+    # are guaranteed to exist only once for each bus name
+    def __del__(self):
+        # FIXME: we don't have this function yet :)
+        #dbus_bindings.bus_release_name(self._bus.get_connection(), self._named_service)
+        pass
 
     def get_bus(self):
         """Get the Bus this Service is on"""
@@ -26,10 +67,10 @@
 
     def get_name(self):
         """Get the name of this service"""
-        return self._named_service
+        return self._name
 
     def __repr__(self):
-        return '<dbus.service.BusName %s on %r at %#x>' % (self._named_service, self._bus, id(self))
+        return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
     __str__ = __repr__
 
 
@@ -322,3 +363,4 @@
     def __repr__(self):
         return '<dbus.service.Object %s on %r at %#x>' % (self._object_path, self._name, id(self))
     __str__ = __repr__
+



More information about the dbus-commit mailing list