Thu 21 Apr 2005
Another thing I really like about python is the fact that the Gnome community has a wealth of brain power on the subject. I got a lot of good feedback from James and Johan and have been able to whip up a test case to brainstorm on how I am going to implement the methods and signal piece of the bindings. If anyone has any comments on ways I could do this stuff better please chime in. Here is the prototype:
class meta(type):
def __init__(cls, name, bases, dct):
print 'Init is called: ' + str(cls) + str(name) + str(bases) + str(dct)
method_list = []
for func in dct.values():
try:
method_name = func._dbus_method_name()
method_list.append(method_name)
except:
pass
print method_list
super(meta, cls).__init__(name, bases, dct)
def method(func):
def decorator(self, *args):
func(self, *args)
def _dbus_method_name():
return func.__name__
decorator._dbus_method_name = _dbus_method_name
return decorator
class bar:
__metaclass__ = meta
@method
def my_method(self):
print 'my_method'
x = bar()
x.my_method()
Ok so my main concerns is using the try/except block at top. While this works, is it cleaner to check for the _dbus_method_name member or is relying on the exception acceptibly pythonic? I could also just make it a property of the decorator object thereby avoiding the method call.
Perhaps I should explain what we are looking at here. I have created a method decorator that is used to tag methods as being exported. In that decorator I add a method that I can call on later to make a list of exported methods. At the top is a metaclass whoes purpose is to check all the decorators in classes that inherit from this metaclass. Upon the class bar being realized and its __metaclass__ being set to meta, the metaclass’ __init__ method gets called (__new__ gets called first but I don’t use it here). bar’s member dictionarty gets passed to init which we iterate over the values of. Upon finding an object with the member _dbus_method_name we add that to the list of exported methods. If this were the real bindings we would then attach this this as an attribute of the class which would then be used to bind the methods on the bus when the class is instantiated. The advantage here is that we don’t need to introspect every time an object is created but only when the class is first realized.
In the actual bindings I will be putting together a hash that points the interface and method objects so that we can have methods that overide each other. The code will also construct the dbus xml introspection format so that we can support the Introspectable interface. All of this will be hiddent to the user with the exception of having to use the @dbus.method and @dbus.signal decorators. It really is a nice solution.
As far as the backend structure goes I will be creating a dbus.MetaObject class which dbus.Object will use as its metaclass. The dbus.Object itself will use the decorators to implement DBus’ standard interfaces. Inheritting from dbus.Object will work almost the same way it does now except that one would use the decorators instead of the list in the constructor to export methods. I’m still debating if I should create class decorators for specifying classpaths or just leave it as is where we pass them into the constructor of dbus.Object. I don’t want to go overboard and make everything into a decorator even if I think they are pretty cool.
[read this post in: ar de es fr it ja ko pt ru zh-CN ]
April 21st, 2005 at 8:46 pm
def method(func): def decorator(self, *args): func(self, *args) def _dbus_method_name(): return func.__name__ decorator._dbus_method_name = _dbus_method_name return decoratorDon’t forget to return the value of func(self, *args)! Also, is it intentional to accept *args but not **kwargs?
method_list = [] for func in dct.values(): try: method_name = func._dbus_method_name() method_list.append(method_name) except: passYeah I don’t like the try-except here either. Changing it to be an “except AttributeError” would help a little, but it would be best as:
try: dbus_method_name = func._dbus_method_name except AttributeError: pass else: method_list.append(dbus_method_name())April 21st, 2005 at 11:39 pm
No reason for not passing keywords. This was just a quick hack so I could wrap my head around decorators and metaclasses. I wanted to brainstorm the design and see how things could possibly fit together. I think with the try/except block I’m just going to get the function object’s dictionary and do a has_key(). It is much cleaner that way. Also I don’t think I will need a function there and will instead pass in some structured data.
April 22nd, 2005 at 12:38 am
Note that you can assign arbitrary attributes to a function, so in your method() decorator, you could just as easily pass through the original function object, setting _dbus_method_name on it.
Also, is there any reason for attaching a function to the method object, rather than just attaching the information directly as an attribute? I’d probably do something like “func.is_dbus_method = True”, then in your metaclass check if getattr(func, ‘is_dbus_method’, False) is true.
April 22nd, 2005 at 12:57 am
Ah, excelent. No reason for the function. I think I needed some coffee at that point
Now that I see how this all fits together it looks like I have everything I need to do what I want. Thanks for all the info.