Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 62 additions & 19 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,23 +1,66 @@
A Python Library for Generating dbus-python Client Code
=======================================================

Introduction
Function
--------
This library contains a function, invoker_builder, that consumes
an XML specification of a D-Bus interface and yields a function suitable
for use in a new_class method invocation. ::

>>> builder = invoker_builder(spec)
>>> Klass = types.new_class("Klass", bases=(object,), exec_body=builder)

This call yields a Python class, called Klass, with two static class
members, "Methods" and "Properties". The "Methods" class has a static
method corresponding to each method defined in the interface. Each method
takes a proxy object as its first argument followed by any number of
keyword arguments, corresponding to the arguments of the method. The
"Properties" class has a static class corresponding to every property
defined in the interface. Each property class may have a static Get() or
Set() method, depending on its attributes.

For example, assuming the interface defines a read only Version property,
the following call should return a value. ::

>>> Klass.Properties.Version.Get()

However, since Version is a read only property, the following call should
fail with an AttributeError. ::

>>> Klass.Properties.Version.Set("42")

Similarly, if the interface defines a method, Create, with one argument,
name, a STRING, the following call should succeed. ::

>>> Klass.Methods.Create(proxy, name="name")

The Create method invokes the method on the proxy object, passing force,
transformed into a dbus-python String type.

On the other hand, the invocation will raise a DPClientError exception if
the method is called with an argument with an incorrect type as, ::

>>> Klass.Methods.Create(proxy, name=false)

or with an incorrect keyword as, ::

>>> Klass.Methods.Create(proxy, force=false)

or without all the necessary keywords as, ::

>>> Klass.Methods.Create(proxy)

Errors
------
This library exports one exception type, DPClientError. It constitutes a bug
if an error of any other type is propagated during class generation or when
the methods of the class are executed.

Dependencies
------------
* dbus-python
* into-dbus-python

Requirements
------------
This library contains a method that consumes an XML specification of
a D-Bus interface and yields a Pythonic interface for invoking methods and
obtaining properties on that interface using the dbus-python library.

Methods
-------

* dbus_python_invoker_builder:
This functions consumes the spec for a single interface and returns a class
that contains dbus-python dependent code to invoke methods on D-Bus objects.
The client chooses the class name. Each generated class contains two static
class members, "Methods" and "Properties". The "Methods" class has a number
of static methods corresponding to every method defined in the interface.
Each method takes a proxy object as its first argument followed by any
number of keyword arguments, corresponding to the arguments of the method.
The "Properties" class has a number of static classes corresponding to every
property defined in the interface. Each property class has a static Get() or
Set() method.
This library is not compatible with Python 2.
2 changes: 1 addition & 1 deletion src/dbus_python_client_gen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
Top-level classes and methods.
"""

from ._invokers import dbus_python_invoker_builder
from ._invokers import invoker_builder
22 changes: 19 additions & 3 deletions src/dbus_python_client_gen/_invokers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import types
import dbus

from into_dbus_python import IntoDPError
from into_dbus_python import xformers

from ._errors import DPClientGenerationError
Expand Down Expand Up @@ -245,21 +246,36 @@ def build_method(spec):
arg_names = [e.attrib["name"] for e in inargs]

signature = "".join(e.attrib["type"] for e in inargs)

# This function is expected to work for all legal signatures,
# and the attributes are expected to contain only legal signatures.
# Thus, it should never fail.
func = xformers(signature)

def dbus_func(proxy_object, **kwargs): # pragma: no cover
"""
The method proper.

:raises DPClientRuntimeError:
"""
if frozenset(arg_names) != frozenset(kwargs.keys()):
raise DPClientRuntimeError("Key mismatch: %s != %s" %
(", ".join(arg_names), ", ".join(kwargs.keys())))
args = \
[v for (k, v) in \
sorted(kwargs.items(), key=lambda x: arg_names.index(x[0]))]
xformed_args = func(args)

try:
xformed_args = func(args)
except IntoDPError as err:
raise DPClientRuntimeError() from err

dbus_method = getattr(proxy_object, name)
return dbus_method(*xformed_args, dbus_interface=interface_name)

try:
return dbus_method(*xformed_args, dbus_interface=interface_name)
except dbus.DBusException as err:
raise DPClientRuntimeError() from err

return dbus_func

Expand All @@ -275,7 +291,7 @@ def dbus_func(proxy_object, **kwargs): # pragma: no cover
return builder


def dbus_python_invoker_builder(spec):
def invoker_builder(spec):
"""
Returns a function that builds a method interface based on 'spec'.

Expand Down
4 changes: 2 additions & 2 deletions tests/test_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import xml.etree.ElementTree as ET

from dbus_python_client_gen import dbus_python_invoker_builder
from dbus_python_client_gen import invoker_builder

from dbus_python_client_gen._invokers import method_builder
from dbus_python_client_gen._invokers import prop_builder
Expand Down Expand Up @@ -91,7 +91,7 @@ def _testKlass(self):
fields, "Properties" and "Methods".
"""
for name, spec in self._data.items():
builder = dbus_python_invoker_builder(spec)
builder = invoker_builder(spec)
klass = types.new_class(name, bases=(object,), exec_body=builder)
self.assertTrue(hasattr(klass, "Properties"))
properties = getattr(klass, "Properties")
Expand Down