=== modified file 'mandos' --- mandos 2012-05-05 10:52:11 +0000 +++ mandos 2012-05-06 16:13:00 +0000 @@ -1021,32 +1021,48 @@ variant_level=variant_level) -class AlternateDBusNamesMetaclass(DBusObjectWithProperties - .__metaclass__): - """Applied to an empty subclass of a D-Bus object, this metaclass - will add additional D-Bus attributes matching a certain pattern. +def alternate_dbus_interfaces(alt_interface_names, deprecate=True): + """A class decorator; applied to a subclass of + dbus.service.Object, it will add alternate D-Bus attributes with + interface names according to the "alt_interface_names" mapping. + Usage: + + @alternate_dbus_names({"org.example.Interface": + "net.example.AlternateInterface"}) + class SampleDBusObject(dbus.service.Object): + @dbus.service.method("org.example.Interface") + def SampleDBusMethod(): + pass + + The above "SampleDBusMethod" on "SampleDBusObject" will be + reachable via two interfaces: "org.example.Interface" and + "net.example.AlternateInterface", the latter of which will have + its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to + "true", unless "deprecate" is passed with a False value. + + This works for methods and signals, and also for D-Bus properties + (from DBusObjectWithProperties) and interfaces (from the + dbus_interface_annotations decorator). """ - def __new__(mcs, name, bases, attr): - # Go through all the base classes which could have D-Bus - # methods, signals, or properties in them - old_interface_names = [] - for base in (b for b in bases - if issubclass(b, dbus.service.Object)): - # Go though all attributes of the base class - for attrname, attribute in inspect.getmembers(base): + def wrapper(cls): + for orig_interface_name, alt_interface_name in ( + alt_interface_names.iteritems()): + attr = {} + interface_names = set() + # Go though all attributes of the class + for attrname, attribute in inspect.getmembers(cls): # Ignore non-D-Bus attributes, and D-Bus attributes # with the wrong interface name if (not hasattr(attribute, "_dbus_interface") or not attribute._dbus_interface - .startswith("se.recompile.Mandos")): + .startswith(orig_interface_name)): continue # Create an alternate D-Bus interface name based on # the current name alt_interface = (attribute._dbus_interface - .replace("se.recompile.Mandos", - "se.bsnet.fukt.Mandos")) - if alt_interface != attribute._dbus_interface: - old_interface_names.append(alt_interface) + .replace(orig_interface_name, + alt_interface_name)) + interface_names.add(alt_interface) # Is this a D-Bus signal? if getattr(attribute, "_dbus_is_signal", False): # Extract the original non-method function by @@ -1074,8 +1090,9 @@ except AttributeError: pass # Define a creator of a function to call both the - # old and new functions, so both the old and new - # signals gets sent when the function is called + # original and alternate functions, so both the + # original and alternate signals gets sent when + # the function is called def fixscope(func1, func2): """This function is a scope container to pass func1 and func2 to the "call_both" function @@ -1088,8 +1105,7 @@ return call_both # Create the "call_both" function and add it to # the class - attr[attrname] = fixscope(attribute, - new_function) + attr[attrname] = fixscope(attribute, new_function) # Is this a D-Bus method? elif getattr(attribute, "_dbus_is_method", False): # Create a new, but exactly alike, function @@ -1151,20 +1167,31 @@ attribute.func_name, attribute.func_defaults, attribute.func_closure))) - # Deprecate all old interfaces - iname="_AlternateDBusNamesMetaclass_interface_annotation{0}" - for old_interface_name in old_interface_names: - @dbus_interface_annotations(old_interface_name) - def func(self): - return { "org.freedesktop.DBus.Deprecated": "true" } - # Find an unused name - for aname in (iname.format(i) for i in itertools.count()): - if aname not in attr: - attr[aname] = func - break - return type.__new__(mcs, name, bases, attr) - - + if deprecate: + # Deprecate all alternate interfaces + iname="_AlternateDBusNames_interface_annotation{0}" + for interface_name in interface_names: + @dbus_interface_annotations(interface_name) + def func(self): + return { "org.freedesktop.DBus.Deprecated": + "true" } + # Find an unused name + for aname in (iname.format(i) + for i in itertools.count()): + if aname not in attr: + attr[aname] = func + break + if interface_names: + # Replace the class with a new subclass of it with + # methods, signals, etc. as created above. + cls = type(b"{0}Alternate".format(cls.__name__), + (cls,), attr) + return cls + return wrapper + + +@alternate_dbus_interfaces({"se.recompile.Mandos": + "se.bsnet.fukt.Mandos"}) class ClientDBus(Client, DBusObjectWithProperties): """A Client class using D-Bus @@ -1604,10 +1631,6 @@ self._pipe.send(('setattr', name, value)) -class ClientDBusTransitional(ClientDBus): - __metaclass__ = AlternateDBusNamesMetaclass - - class ClientHandler(socketserver.BaseRequestHandler, object): """A class to handle client connections. @@ -2337,8 +2360,7 @@ client_class = Client if use_dbus: - client_class = functools.partial(ClientDBusTransitional, - bus = bus) + client_class = functools.partial(ClientDBus, bus = bus) client_settings = Client.config_parser(client_config) old_client_settings = {} @@ -2456,6 +2478,8 @@ signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) if use_dbus: + @alternate_dbus_interfaces({"se.recompile.Mandos": + "se.bsnet.fukt.Mandos"}) class MandosDBusService(DBusObjectWithProperties): """A D-Bus proxy object""" def __init__(self): @@ -2515,9 +2539,7 @@ del _interface - class MandosDBusServiceTransitional(MandosDBusService): - __metaclass__ = AlternateDBusNamesMetaclass - mandos_dbus_service = MandosDBusServiceTransitional() + mandos_dbus_service = MandosDBusService() def cleanup(): "Cleanup function; run on exit"