diff options
Diffstat (limited to 'python')
-rw-r--r-- | python/_dbus.py | 75 | ||||
-rw-r--r-- | python/dbus_bindings.pyx | 9 | ||||
-rw-r--r-- | python/service.py | 62 |
3 files changed, 114 insertions, 32 deletions
diff --git a/python/_dbus.py b/python/_dbus.py index bb4c0428..2376f172 100644 --- a/python/_dbus.py +++ b/python/_dbus.py @@ -42,14 +42,14 @@ print(dbus_object.ListServices()) """ 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 @@ class Bus: 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 + + _shared_instances = weakref.WeakValueDictionary() + + 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) - 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) + bus._bus_type = bus_type + bus._bus_names = weakref.WeakValueDictionary() + bus._match_rule_tree = SignalMatchTree() - self._connection.add_filter(self._signal_func) - self._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 @@ class Bus: 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 Bus: 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 diff --git a/python/dbus_bindings.pyx b/python/dbus_bindings.pyx index a96a5adc..75e448ee 100644 --- a/python/dbus_bindings.pyx +++ b/python/dbus_bindings.pyx @@ -1748,8 +1748,13 @@ def bus_register(Connection connection): 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 diff --git a/python/service.py b/python/service.py index 14a2d6d3..d1973b04 100644 --- a/python/service.py +++ b/python/service.py @@ -6,19 +6,60 @@ from exceptions import UnknownMethodException 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 @@ class BusName: 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 @@ class Object(Interface): def __repr__(self): return '<dbus.service.Object %s on %r at %#x>' % (self._object_path, self._name, id(self)) __str__ = __repr__ + |