python-pydbus package is retired on c9s for CS-556
This commit is contained in:
		
							parent
							
								
									61e067dc0b
								
							
						
					
					
						commit
						1119bc770b
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1 +0,0 @@ | |||||||
| /pydbus-0.6.0.tar.gz |  | ||||||
| @ -1,35 +0,0 @@ | |||||||
| From 5fe65a35e0e7106347639f0258206fadb451c439 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Hiroaki KAWAI <hiroaki.kawai@gmail.com> |  | ||||||
| Date: Wed, 1 Feb 2017 18:00:33 +0900 |  | ||||||
| Subject: [PATCH 1/3] make direction attribute conforming to introspect.dtd |  | ||||||
| 
 |  | ||||||
| direction attribute defaults to "in" as |  | ||||||
| in the DTD(*1), direction attribute is defined as following: |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| <!ATTRLIST arg direction (in|out) "in"> |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| *1) http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd |  | ||||||
| ---
 |  | ||||||
|  pydbus/proxy_method.py | 4 ++-- |  | ||||||
|  1 file changed, 2 insertions(+), 2 deletions(-) |  | ||||||
| 
 |  | ||||||
| diff --git a/pydbus/proxy_method.py b/pydbus/proxy_method.py
 |  | ||||||
| index 8798edd..3e6e6ee 100644
 |  | ||||||
| --- a/pydbus/proxy_method.py
 |  | ||||||
| +++ b/pydbus/proxy_method.py
 |  | ||||||
| @@ -33,8 +33,8 @@ class ProxyMethod(object):
 |  | ||||||
|  		self.__name__ = method.attrib["name"] |  | ||||||
|  		self.__qualname__ = self._iface_name + "." + self.__name__ |  | ||||||
|   |  | ||||||
| -		self._inargs  = [(arg.attrib.get("name", ""), arg.attrib["type"]) for arg in method if arg.tag == "arg" and arg.attrib["direction"] == "in"]
 |  | ||||||
| -		self._outargs = [arg.attrib["type"] for arg in method if arg.tag == "arg" and arg.attrib["direction"] == "out"]
 |  | ||||||
| +		self._inargs  = [(arg.attrib.get("name", ""), arg.attrib["type"]) for arg in method if arg.tag == "arg" and arg.attrib.get("direction", "in") == "in"]
 |  | ||||||
| +		self._outargs = [arg.attrib["type"] for arg in method if arg.tag == "arg" and arg.attrib.get("direction", "in") == "out"]
 |  | ||||||
|  		self._sinargs  = "(" + "".join(x[1] for x in self._inargs) + ")" |  | ||||||
|  		self._soutargs = "(" + "".join(self._outargs) + ")" |  | ||||||
|   |  | ||||||
| -- 
 |  | ||||||
| 2.13.5 |  | ||||||
| 
 |  | ||||||
| @ -1,201 +0,0 @@ | |||||||
| From 31d6dd7893a5e1bb9eb14bfcee861a5b62f64960 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Vendula Poncova <vponcova@redhat.com> |  | ||||||
| Date: Thu, 27 Jul 2017 18:41:29 +0200 |  | ||||||
| Subject: [PATCH 2/3] Support asynchronous calls (#58) |  | ||||||
| 
 |  | ||||||
| Added support for asynchronous calls of methods. A method is called |  | ||||||
| synchronously unless its callback parameter is specified. A callback |  | ||||||
| is a function f(*args, returned=None, error=None), where args is |  | ||||||
| callback_args specified in the method call, returned is a return |  | ||||||
| value of the method and error is an exception raised by the method. |  | ||||||
| 
 |  | ||||||
| Example of an asynchronous call: |  | ||||||
| 
 |  | ||||||
| def func(x, y, returned=None, error=None): |  | ||||||
|   pass |  | ||||||
| 
 |  | ||||||
| proxy.Method(a, b, callback=func, callback_args=(x, y)) |  | ||||||
| ---
 |  | ||||||
|  doc/tutorial.rst       | 11 ++++++++- |  | ||||||
|  pydbus/proxy_method.py | 44 ++++++++++++++++++++++++++++++----- |  | ||||||
|  tests/publish_async.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ |  | ||||||
|  tests/run.sh           |  1 + |  | ||||||
|  4 files changed, 112 insertions(+), 7 deletions(-) |  | ||||||
|  create mode 100644 tests/publish_async.py |  | ||||||
| 
 |  | ||||||
| diff --git a/doc/tutorial.rst b/doc/tutorial.rst
 |  | ||||||
| index 7474de3..b8479cf 100644
 |  | ||||||
| --- a/doc/tutorial.rst
 |  | ||||||
| +++ b/doc/tutorial.rst
 |  | ||||||
| @@ -84,7 +84,8 @@ All objects have methods, properties and signals.
 |  | ||||||
|  Setting up an event loop |  | ||||||
|  ======================== |  | ||||||
|   |  | ||||||
| -To handle signals emitted by exported objects, or to export your own objects, you need to setup an event loop.
 |  | ||||||
| +To handle signals emitted by exported objects, to asynchronously call methods
 |  | ||||||
| +or to export your own objects, you need to setup an event loop.
 |  | ||||||
|   |  | ||||||
|  The only main loop supported by ``pydbus`` is GLib.MainLoop. |  | ||||||
|   |  | ||||||
| @@ -156,6 +157,14 @@ To call a method::
 |  | ||||||
|   |  | ||||||
|      dev.Disconnect() |  | ||||||
|   |  | ||||||
| +To asynchronously call a method::
 |  | ||||||
| +
 |  | ||||||
| +    def print_result(returned=None, error=None):
 |  | ||||||
| +        print(returned, error)
 |  | ||||||
| +
 |  | ||||||
| +    dev.GetAppliedConnection(0, callback=print_result)
 |  | ||||||
| +    loop.run()
 |  | ||||||
| +
 |  | ||||||
|  To read a property:: |  | ||||||
|   |  | ||||||
|      print(dev.Autoconnect) |  | ||||||
| diff --git a/pydbus/proxy_method.py b/pydbus/proxy_method.py
 |  | ||||||
| index 3e6e6ee..442fe07 100644
 |  | ||||||
| --- a/pydbus/proxy_method.py
 |  | ||||||
| +++ b/pydbus/proxy_method.py
 |  | ||||||
| @@ -65,15 +65,34 @@ class ProxyMethod(object):
 |  | ||||||
|   |  | ||||||
|  		# Python 2 sux |  | ||||||
|  		for kwarg in kwargs: |  | ||||||
| -			if kwarg not in ("timeout",):
 |  | ||||||
| +			if kwarg not in ("timeout", "callback", "callback_args"):
 |  | ||||||
|  				raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg)) |  | ||||||
|  		timeout = kwargs.get("timeout", None) |  | ||||||
| +		callback = kwargs.get("callback", None)
 |  | ||||||
| +		callback_args = kwargs.get("callback_args", tuple())
 |  | ||||||
| +
 |  | ||||||
| +		call_args = (
 |  | ||||||
| +			instance._bus_name,
 |  | ||||||
| +			instance._path,
 |  | ||||||
| +			self._iface_name,
 |  | ||||||
| +			self.__name__,
 |  | ||||||
| +			GLib.Variant(self._sinargs, args),
 |  | ||||||
| +			GLib.VariantType.new(self._soutargs),
 |  | ||||||
| +			0,
 |  | ||||||
| +			timeout_to_glib(timeout),
 |  | ||||||
| +			None
 |  | ||||||
| +		)
 |  | ||||||
| +
 |  | ||||||
| +		if callback:
 |  | ||||||
| +			call_args += (self._finish_async_call, (callback, callback_args))
 |  | ||||||
| +			instance._bus.con.call(*call_args)
 |  | ||||||
| +			return None
 |  | ||||||
| +		else:
 |  | ||||||
| +			ret = instance._bus.con.call_sync(*call_args)
 |  | ||||||
| +			return self._unpack_return(ret)
 |  | ||||||
|   |  | ||||||
| -		ret = instance._bus.con.call_sync(
 |  | ||||||
| -			instance._bus_name, instance._path,
 |  | ||||||
| -			self._iface_name, self.__name__, GLib.Variant(self._sinargs, args), GLib.VariantType.new(self._soutargs),
 |  | ||||||
| -			0, timeout_to_glib(timeout), None).unpack()
 |  | ||||||
| -
 |  | ||||||
| +	def _unpack_return(self, values):
 |  | ||||||
| +		ret = values.unpack()
 |  | ||||||
|  		if len(self._outargs) == 0: |  | ||||||
|  			return None |  | ||||||
|  		elif len(self._outargs) == 1: |  | ||||||
| @@ -81,6 +100,19 @@ class ProxyMethod(object):
 |  | ||||||
|  		else: |  | ||||||
|  			return ret |  | ||||||
|   |  | ||||||
| +	def _finish_async_call(self, source, result, user_data):
 |  | ||||||
| +		error = None
 |  | ||||||
| +		return_args = None
 |  | ||||||
| +
 |  | ||||||
| +		try:
 |  | ||||||
| +			ret = source.call_finish(result)
 |  | ||||||
| +			return_args = self._unpack_return(ret)
 |  | ||||||
| +		except Exception as err:
 |  | ||||||
| +			error = err
 |  | ||||||
| +
 |  | ||||||
| +		callback, callback_args = user_data
 |  | ||||||
| +		callback(*callback_args, returned=return_args, error=error)
 |  | ||||||
| +
 |  | ||||||
|  	def __get__(self, instance, owner): |  | ||||||
|  		if instance is None: |  | ||||||
|  			return self |  | ||||||
| diff --git a/tests/publish_async.py b/tests/publish_async.py
 |  | ||||||
| new file mode 100644 |  | ||||||
| index 0000000..3f79b62
 |  | ||||||
| --- /dev/null
 |  | ||||||
| +++ b/tests/publish_async.py
 |  | ||||||
| @@ -0,0 +1,63 @@
 |  | ||||||
| +from pydbus import SessionBus
 |  | ||||||
| +from gi.repository import GLib
 |  | ||||||
| +from threading import Thread
 |  | ||||||
| +import sys
 |  | ||||||
| +
 |  | ||||||
| +done = 0
 |  | ||||||
| +loop = GLib.MainLoop()
 |  | ||||||
| +
 |  | ||||||
| +class TestObject(object):
 |  | ||||||
| +	'''
 |  | ||||||
| +<node>
 |  | ||||||
| +	<interface name='net.lew21.pydbus.tests.publish_async'>
 |  | ||||||
| +		<method name='HelloWorld'>
 |  | ||||||
| +			<arg type='i' name='x' direction='in'/>
 |  | ||||||
| +			<arg type='s' name='response' direction='out'/>
 |  | ||||||
| +		</method>
 |  | ||||||
| +	</interface>
 |  | ||||||
| +</node>
 |  | ||||||
| +	'''
 |  | ||||||
| +	def __init__(self, id):
 |  | ||||||
| +		self.id = id
 |  | ||||||
| +
 |  | ||||||
| +	def HelloWorld(self, x):
 |  | ||||||
| +		res = self.id + ": " + str(x)
 |  | ||||||
| +		print(res)
 |  | ||||||
| +		return res
 |  | ||||||
| +
 |  | ||||||
| +bus = SessionBus()
 |  | ||||||
| +
 |  | ||||||
| +with bus.publish("net.lew21.pydbus.tests.publish_async", TestObject("Obj")):
 |  | ||||||
| +	remote = bus.get("net.lew21.pydbus.tests.publish_async")
 |  | ||||||
| +
 |  | ||||||
| +	def callback(x, returned=None, error=None):
 |  | ||||||
| +		print("asyn: " + returned)
 |  | ||||||
| +		assert (returned is not None)
 |  | ||||||
| +		assert(error is None)
 |  | ||||||
| +		assert(x == int(returned.split()[1]))
 |  | ||||||
| +
 |  | ||||||
| +		global done
 |  | ||||||
| +		done += 1
 |  | ||||||
| +		if done == 3:
 |  | ||||||
| +			loop.quit()
 |  | ||||||
| +
 |  | ||||||
| +	def t1_func():
 |  | ||||||
| +		remote.HelloWorld(1, callback=callback, callback_args=(1,))
 |  | ||||||
| +		remote.HelloWorld(2, callback=callback, callback_args=(2,))
 |  | ||||||
| +		print("sync: " + remote.HelloWorld(3))
 |  | ||||||
| +		remote.HelloWorld(4, callback=callback, callback_args=(4,))
 |  | ||||||
| +
 |  | ||||||
| +	t1 = Thread(None, t1_func)
 |  | ||||||
| +	t1.daemon = True
 |  | ||||||
| +
 |  | ||||||
| +	def handle_timeout():
 |  | ||||||
| +		print("ERROR: Timeout.")
 |  | ||||||
| +		sys.exit(1)
 |  | ||||||
| +
 |  | ||||||
| +	GLib.timeout_add_seconds(2, handle_timeout)
 |  | ||||||
| +
 |  | ||||||
| +	t1.start()
 |  | ||||||
| +
 |  | ||||||
| +	loop.run()
 |  | ||||||
| +
 |  | ||||||
| +	t1.join()
 |  | ||||||
| diff --git a/tests/run.sh b/tests/run.sh
 |  | ||||||
| index 8d93644..271c58a 100755
 |  | ||||||
| --- a/tests/run.sh
 |  | ||||||
| +++ b/tests/run.sh
 |  | ||||||
| @@ -15,4 +15,5 @@ then
 |  | ||||||
|  	"$PYTHON" $TESTS_DIR/publish.py |  | ||||||
|  	"$PYTHON" $TESTS_DIR/publish_properties.py |  | ||||||
|  	"$PYTHON" $TESTS_DIR/publish_multiface.py |  | ||||||
| +	"$PYTHON" $TESTS_DIR/publish_async.py
 |  | ||||||
|  fi |  | ||||||
| -- 
 |  | ||||||
| 2.13.5 |  | ||||||
| 
 |  | ||||||
| @ -1,491 +0,0 @@ | |||||||
| From 773858e1afd21cdf3ceef2cd35509f0b4882bf16 Mon Sep 17 00:00:00 2001 |  | ||||||
| From: Vendula Poncova <vponcova@redhat.com> |  | ||||||
| Date: Tue, 1 Aug 2017 16:54:24 +0200 |  | ||||||
| Subject: [PATCH 3/3] Support transformation between D-Bus errors and |  | ||||||
|  exceptions. |  | ||||||
| 
 |  | ||||||
| Exceptions can be registered with decorators, raised in a remote |  | ||||||
| method and recreated after return from the remote call. |  | ||||||
| ---
 |  | ||||||
|  doc/tutorial.rst       |  47 ++++++++++++++++++ |  | ||||||
|  pydbus/error.py        |  97 ++++++++++++++++++++++++++++++++++++ |  | ||||||
|  pydbus/proxy_method.py |  18 +++++-- |  | ||||||
|  pydbus/registration.py |  16 ++++-- |  | ||||||
|  tests/error.py         |  67 +++++++++++++++++++++++++ |  | ||||||
|  tests/publish_error.py | 132 +++++++++++++++++++++++++++++++++++++++++++++++++ |  | ||||||
|  tests/run.sh           |   2 + |  | ||||||
|  7 files changed, 371 insertions(+), 8 deletions(-) |  | ||||||
|  create mode 100644 pydbus/error.py |  | ||||||
|  create mode 100644 tests/error.py |  | ||||||
|  create mode 100644 tests/publish_error.py |  | ||||||
| 
 |  | ||||||
| diff --git a/doc/tutorial.rst b/doc/tutorial.rst
 |  | ||||||
| index b8479cf..7fe55e1 100644
 |  | ||||||
| --- a/doc/tutorial.rst
 |  | ||||||
| +++ b/doc/tutorial.rst
 |  | ||||||
| @@ -341,6 +341,53 @@ See ``help(bus.request_name)`` and ``help(bus.register_object)`` for details.
 |  | ||||||
|   |  | ||||||
|  .. -------------------------------------------------------------------- |  | ||||||
|   |  | ||||||
| +Error handling
 |  | ||||||
| +==============
 |  | ||||||
| +
 |  | ||||||
| +You can map D-Bus errors to your exception classes for better error handling.
 |  | ||||||
| +To handle D-Bus errors, use the ``@map_error`` decorator::
 |  | ||||||
| +
 |  | ||||||
| +    from pydbus.error import map_error
 |  | ||||||
| +
 |  | ||||||
| +    @map_error("org.freedesktop.DBus.Error.InvalidArgs")
 |  | ||||||
| +    class InvalidArgsException(Exception):
 |  | ||||||
| +        pass
 |  | ||||||
| +
 |  | ||||||
| +    try:
 |  | ||||||
| +        ...
 |  | ||||||
| +    catch InvalidArgsException as e:
 |  | ||||||
| +        print(e)
 |  | ||||||
| +
 |  | ||||||
| +To register new D-Bus errors, use the ``@register_error`` decorator::
 |  | ||||||
| +
 |  | ||||||
| +    from pydbus.error import register_error
 |  | ||||||
| +
 |  | ||||||
| +    @map_error("net.lew21.pydbus.TutorialExample.MyError", MY_DOMAIN, MY_EXCEPTION_CODE)
 |  | ||||||
| +    class MyException(Exception):
 |  | ||||||
| +        pass
 |  | ||||||
| +
 |  | ||||||
| +Then you can raise ``MyException`` from the D-Bus method of the remote object::
 |  | ||||||
| +
 |  | ||||||
| +    def Method():
 |  | ||||||
| +        raise MyException("Message")
 |  | ||||||
| +
 |  | ||||||
| +And catch the same exception on the client side::
 |  | ||||||
| +
 |  | ||||||
| +    try:
 |  | ||||||
| +        proxy.Method()
 |  | ||||||
| +    catch MyException as e:
 |  | ||||||
| +        print(e)
 |  | ||||||
| +
 |  | ||||||
| +To handle all unknown D-Bus errors, use the ``@map_by_default`` decorator to specify the default exception::
 |  | ||||||
| +
 |  | ||||||
| +    from pydbus.error import map_by_default
 |  | ||||||
| +
 |  | ||||||
| +    @map_by_default
 |  | ||||||
| +    class DefaultException(Exception):
 |  | ||||||
| +        pass
 |  | ||||||
| +
 |  | ||||||
| +.. --------------------------------------------------------------------
 |  | ||||||
| +
 |  | ||||||
|  Data types |  | ||||||
|  ========== |  | ||||||
|   |  | ||||||
| diff --git a/pydbus/error.py b/pydbus/error.py
 |  | ||||||
| new file mode 100644 |  | ||||||
| index 0000000..aaa3510
 |  | ||||||
| --- /dev/null
 |  | ||||||
| +++ b/pydbus/error.py
 |  | ||||||
| @@ -0,0 +1,97 @@
 |  | ||||||
| +from gi.repository import GLib, Gio
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +def register_error(name, domain, code):
 |  | ||||||
| +	"""Register and map decorated exception class to a DBus error."""
 |  | ||||||
| +	def decorated(cls):
 |  | ||||||
| +		error_registration.register_error(cls, name, domain, code)
 |  | ||||||
| +		return cls
 |  | ||||||
| +
 |  | ||||||
| +	return decorated
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +def map_error(error_name):
 |  | ||||||
| +	"""Map decorated exception class to a DBus error."""
 |  | ||||||
| +	def decorated(cls):
 |  | ||||||
| +		error_registration.map_error(cls, error_name)
 |  | ||||||
| +		return cls
 |  | ||||||
| +
 |  | ||||||
| +	return decorated
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +def map_by_default(cls):
 |  | ||||||
| +	"""Map decorated exception class to all unknown DBus errors."""
 |  | ||||||
| +	error_registration.map_by_default(cls)
 |  | ||||||
| +	return cls
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class ErrorRegistration(object):
 |  | ||||||
| +	"""Class for mapping exceptions to DBus errors."""
 |  | ||||||
| +
 |  | ||||||
| +	_default = None
 |  | ||||||
| +	_map = dict()
 |  | ||||||
| +	_reversed_map = dict()
 |  | ||||||
| +
 |  | ||||||
| +	def map_by_default(self, exception_cls):
 |  | ||||||
| +		"""Set the exception class as a default."""
 |  | ||||||
| +		self._default = exception_cls
 |  | ||||||
| +
 |  | ||||||
| +	def map_error(self, exception_cls, name):
 |  | ||||||
| +		"""Map the exception class to a DBus name."""
 |  | ||||||
| +		self._map[name] = exception_cls
 |  | ||||||
| +		self._reversed_map[exception_cls] = name
 |  | ||||||
| +
 |  | ||||||
| +	def register_error(self, exception_cls, name, domain, code):
 |  | ||||||
| +		"""Map and register the exception class to a DBus name."""
 |  | ||||||
| +		self.map_error(exception_cls, name)
 |  | ||||||
| +		return Gio.DBusError.register_error(domain, code, name)
 |  | ||||||
| +
 |  | ||||||
| +	def is_registered_exception(self, obj):
 |  | ||||||
| +		"""Is the exception registered?"""
 |  | ||||||
| +		return obj.__class__ in self._reversed_map
 |  | ||||||
| +
 |  | ||||||
| +	def get_dbus_name(self, obj):
 |  | ||||||
| +		"""Get the DBus name of the exception."""
 |  | ||||||
| +		return self._reversed_map.get(obj.__class__)
 |  | ||||||
| +
 |  | ||||||
| +	def get_exception_class(self, name):
 |  | ||||||
| +		"""Get the exception class mapped to the DBus name."""
 |  | ||||||
| +		return self._map.get(name, self._default)
 |  | ||||||
| +
 |  | ||||||
| +	def transform_message(self, name, message):
 |  | ||||||
| +		"""Transform the message of the exception."""
 |  | ||||||
| +		prefix = "{}:{}: ".format("GDBus.Error", name)
 |  | ||||||
| +
 |  | ||||||
| +		if message.startswith(prefix):
 |  | ||||||
| +			return message[len(prefix):]
 |  | ||||||
| +
 |  | ||||||
| +		return message
 |  | ||||||
| +
 |  | ||||||
| +	def transform_exception(self, e):
 |  | ||||||
| +		"""Transform the remote error to the exception."""
 |  | ||||||
| +		if not isinstance(e, GLib.Error):
 |  | ||||||
| +			return e
 |  | ||||||
| +
 |  | ||||||
| +		if not Gio.DBusError.is_remote_error(e):
 |  | ||||||
| +			return e
 |  | ||||||
| +
 |  | ||||||
| +		# Get DBus name of the error.
 |  | ||||||
| +		name = Gio.DBusError.get_remote_error(e)
 |  | ||||||
| +		# Get the exception class.
 |  | ||||||
| +		exception_cls = self.get_exception_class(name)
 |  | ||||||
| +
 |  | ||||||
| +		# Return the original exception.
 |  | ||||||
| +		if not exception_cls:
 |  | ||||||
| +			return e
 |  | ||||||
| +
 |  | ||||||
| +		# Return new exception.
 |  | ||||||
| +		message = self.transform_message(name, e.message)
 |  | ||||||
| +		exception = exception_cls(message)
 |  | ||||||
| +		exception.dbus_name = name
 |  | ||||||
| +		exception.dbus_domain = e.domain
 |  | ||||||
| +		exception.dbus_code = e.code
 |  | ||||||
| +		return exception
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +# Default error registration.
 |  | ||||||
| +error_registration = ErrorRegistration()
 |  | ||||||
| diff --git a/pydbus/proxy_method.py b/pydbus/proxy_method.py
 |  | ||||||
| index 442fe07..a73f9eb 100644
 |  | ||||||
| --- a/pydbus/proxy_method.py
 |  | ||||||
| +++ b/pydbus/proxy_method.py
 |  | ||||||
| @@ -2,6 +2,7 @@ from gi.repository import GLib
 |  | ||||||
|  from .generic import bound_method |  | ||||||
|  from .identifier import filter_identifier |  | ||||||
|  from .timeout import timeout_to_glib |  | ||||||
| +from .error import error_registration
 |  | ||||||
|   |  | ||||||
|  try: |  | ||||||
|  	from inspect import Signature, Parameter |  | ||||||
| @@ -87,9 +88,20 @@ class ProxyMethod(object):
 |  | ||||||
|  			call_args += (self._finish_async_call, (callback, callback_args)) |  | ||||||
|  			instance._bus.con.call(*call_args) |  | ||||||
|  			return None |  | ||||||
| +
 |  | ||||||
|  		else: |  | ||||||
| -			ret = instance._bus.con.call_sync(*call_args)
 |  | ||||||
| -			return self._unpack_return(ret)
 |  | ||||||
| +			result = None
 |  | ||||||
| +			error = None
 |  | ||||||
| +
 |  | ||||||
| +			try:
 |  | ||||||
| +				result = instance._bus.con.call_sync(*call_args)
 |  | ||||||
| +			except Exception as e:
 |  | ||||||
| +				error = error_registration.transform_exception(e)
 |  | ||||||
| +
 |  | ||||||
| +			if error:
 |  | ||||||
| +				raise error
 |  | ||||||
| +
 |  | ||||||
| +			return self._unpack_return(result)
 |  | ||||||
|   |  | ||||||
|  	def _unpack_return(self, values): |  | ||||||
|  		ret = values.unpack() |  | ||||||
| @@ -108,7 +120,7 @@ class ProxyMethod(object):
 |  | ||||||
|  			ret = source.call_finish(result) |  | ||||||
|  			return_args = self._unpack_return(ret) |  | ||||||
|  		except Exception as err: |  | ||||||
| -			error = err
 |  | ||||||
| +			error = error_registration.transform_exception(err)
 |  | ||||||
|   |  | ||||||
|  		callback, callback_args = user_data |  | ||||||
|  		callback(*callback_args, returned=return_args, error=error) |  | ||||||
| diff --git a/pydbus/registration.py b/pydbus/registration.py
 |  | ||||||
| index f531539..1d2cbcb 100644
 |  | ||||||
| --- a/pydbus/registration.py
 |  | ||||||
| +++ b/pydbus/registration.py
 |  | ||||||
| @@ -5,6 +5,7 @@ from . import generic
 |  | ||||||
|  from .exitable import ExitableWithAliases |  | ||||||
|  from functools import partial |  | ||||||
|  from .method_call_context import MethodCallContext |  | ||||||
| +from .error import error_registration
 |  | ||||||
|  import logging |  | ||||||
|   |  | ||||||
|  try: |  | ||||||
| @@ -91,11 +92,16 @@ class ObjectWrapper(ExitableWithAliases("unwrap")):
 |  | ||||||
|  			logger = logging.getLogger(__name__) |  | ||||||
|  			logger.exception("Exception while handling %s.%s()", interface_name, method_name) |  | ||||||
|   |  | ||||||
| -			#TODO Think of a better way to translate Python exception types to DBus error types.
 |  | ||||||
| -			e_type = type(e).__name__
 |  | ||||||
| -			if not "." in e_type:
 |  | ||||||
| -				e_type = "unknown." + e_type
 |  | ||||||
| -			invocation.return_dbus_error(e_type, str(e))
 |  | ||||||
| +			if error_registration.is_registered_exception(e):
 |  | ||||||
| +				name = error_registration.get_dbus_name(e)
 |  | ||||||
| +				invocation.return_dbus_error(name, str(e))
 |  | ||||||
| +			else:
 |  | ||||||
| +				logger.info("name is not registered")
 |  | ||||||
| +				e_type = type(e).__name__
 |  | ||||||
| +				if not "." in e_type:
 |  | ||||||
| +					e_type = "unknown." + e_type
 |  | ||||||
| +
 |  | ||||||
| +				invocation.return_dbus_error(e_type, str(e))
 |  | ||||||
|   |  | ||||||
|  	def Get(self, interface_name, property_name): |  | ||||||
|  		type = self.readable_properties[interface_name + "." + property_name] |  | ||||||
| diff --git a/tests/error.py b/tests/error.py
 |  | ||||||
| new file mode 100644 |  | ||||||
| index 0000000..3ec507d
 |  | ||||||
| --- /dev/null
 |  | ||||||
| +++ b/tests/error.py
 |  | ||||||
| @@ -0,0 +1,67 @@
 |  | ||||||
| +from pydbus.error import ErrorRegistration
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class ExceptionA(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class ExceptionB(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class ExceptionC(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class ExceptionD(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class ExceptionE(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +def test_error_mapping():
 |  | ||||||
| +	r = ErrorRegistration()
 |  | ||||||
| +	r.map_error(ExceptionA, "net.lew21.pydbus.tests.ErrorA")
 |  | ||||||
| +	r.map_error(ExceptionB, "net.lew21.pydbus.tests.ErrorB")
 |  | ||||||
| +	r.map_error(ExceptionC, "net.lew21.pydbus.tests.ErrorC")
 |  | ||||||
| +
 |  | ||||||
| +	assert r.is_registered_exception(ExceptionA("Test"))
 |  | ||||||
| +	assert r.is_registered_exception(ExceptionB("Test"))
 |  | ||||||
| +	assert r.is_registered_exception(ExceptionC("Test"))
 |  | ||||||
| +	assert not r.is_registered_exception(ExceptionD("Test"))
 |  | ||||||
| +	assert not r.is_registered_exception(ExceptionE("Test"))
 |  | ||||||
| +
 |  | ||||||
| +	assert r.get_dbus_name(ExceptionA("Test")) == "net.lew21.pydbus.tests.ErrorA"
 |  | ||||||
| +	assert r.get_dbus_name(ExceptionB("Test")) == "net.lew21.pydbus.tests.ErrorB"
 |  | ||||||
| +	assert r.get_dbus_name(ExceptionC("Test")) == "net.lew21.pydbus.tests.ErrorC"
 |  | ||||||
| +
 |  | ||||||
| +	assert r.get_exception_class("net.lew21.pydbus.tests.ErrorA") == ExceptionA
 |  | ||||||
| +	assert r.get_exception_class("net.lew21.pydbus.tests.ErrorB") == ExceptionB
 |  | ||||||
| +	assert r.get_exception_class("net.lew21.pydbus.tests.ErrorC") == ExceptionC
 |  | ||||||
| +	assert r.get_exception_class("net.lew21.pydbus.tests.ErrorD") is None
 |  | ||||||
| +	assert r.get_exception_class("net.lew21.pydbus.tests.ErrorE") is None
 |  | ||||||
| +
 |  | ||||||
| +	r.map_by_default(ExceptionD)
 |  | ||||||
| +	assert not r.is_registered_exception(ExceptionD("Test"))
 |  | ||||||
| +	assert r.get_exception_class("net.lew21.pydbus.tests.ErrorD") == ExceptionD
 |  | ||||||
| +	assert r.get_exception_class("net.lew21.pydbus.tests.ErrorE") == ExceptionD
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +def test_transform_message():
 |  | ||||||
| +	r = ErrorRegistration()
 |  | ||||||
| +	n1 = "net.lew21.pydbus.tests.ErrorA"
 |  | ||||||
| +	m1 = "GDBus.Error:net.lew21.pydbus.tests.ErrorA: Message1"
 |  | ||||||
| +
 |  | ||||||
| +	n2 = "net.lew21.pydbus.tests.ErrorB"
 |  | ||||||
| +	m2 = "GDBus.Error:net.lew21.pydbus.tests.ErrorB: Message2"
 |  | ||||||
| +
 |  | ||||||
| +	assert r.transform_message(n1, m1) == "Message1"
 |  | ||||||
| +	assert r.transform_message(n2, m2) == "Message2"
 |  | ||||||
| +	assert r.transform_message(n1, m2) == m2
 |  | ||||||
| +	assert r.transform_message(n2, m1) == m1
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +test_error_mapping()
 |  | ||||||
| +test_transform_message()
 |  | ||||||
| diff --git a/tests/publish_error.py b/tests/publish_error.py
 |  | ||||||
| new file mode 100644 |  | ||||||
| index 0000000..aa8a18a
 |  | ||||||
| --- /dev/null
 |  | ||||||
| +++ b/tests/publish_error.py
 |  | ||||||
| @@ -0,0 +1,132 @@
 |  | ||||||
| +import sys
 |  | ||||||
| +from threading import Thread
 |  | ||||||
| +from gi.repository import GLib, Gio
 |  | ||||||
| +from pydbus import SessionBus
 |  | ||||||
| +from pydbus.error import register_error, map_error, map_by_default, error_registration
 |  | ||||||
| +
 |  | ||||||
| +import logging
 |  | ||||||
| +logger = logging.getLogger('pydbus.registration')
 |  | ||||||
| +logger.disabled = True
 |  | ||||||
| +
 |  | ||||||
| +loop = GLib.MainLoop()
 |  | ||||||
| +DOMAIN = Gio.DBusError.quark()  # TODO: Register new domain.
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +@register_error("net.lew21.pydbus.tests.ErrorA", DOMAIN, 1000)
 |  | ||||||
| +class ExceptionA(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +@register_error("net.lew21.pydbus.tests.ErrorB", DOMAIN, 2000)
 |  | ||||||
| +class ExceptionB(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +@map_error("org.freedesktop.DBus.Error.InvalidArgs")
 |  | ||||||
| +class ExceptionC(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +@map_by_default
 |  | ||||||
| +class ExceptionD(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class ExceptionE(Exception):
 |  | ||||||
| +	pass
 |  | ||||||
| +
 |  | ||||||
| +
 |  | ||||||
| +class TestObject(object):
 |  | ||||||
| +	'''
 |  | ||||||
| +<node>
 |  | ||||||
| +	<interface name='net.lew21.pydbus.tests.TestInterface'>
 |  | ||||||
| +		<method name='RaiseA'>
 |  | ||||||
| +			<arg type='s' name='msg' direction='in'/>
 |  | ||||||
| +		</method>
 |  | ||||||
| +		<method name='RaiseB'>
 |  | ||||||
| +			<arg type='s' name='msg' direction='in'/>
 |  | ||||||
| +		</method>
 |  | ||||||
| +		<method name='RaiseD'>
 |  | ||||||
| +			<arg type='s' name='msg' direction='in'/>
 |  | ||||||
| +		</method>
 |  | ||||||
| +		<method name='RaiseE'>
 |  | ||||||
| +			<arg type='s' name='msg' direction='in'/>
 |  | ||||||
| +		</method>
 |  | ||||||
| +	</interface>
 |  | ||||||
| +</node>
 |  | ||||||
| +	'''
 |  | ||||||
| +
 |  | ||||||
| +	def RaiseA(self, msg):
 |  | ||||||
| +		raise ExceptionA(msg)
 |  | ||||||
| +
 |  | ||||||
| +	def RaiseB(self, msg):
 |  | ||||||
| +		raise ExceptionB(msg)
 |  | ||||||
| +
 |  | ||||||
| +	def RaiseD(self, msg):
 |  | ||||||
| +		raise ExceptionD(msg)
 |  | ||||||
| +
 |  | ||||||
| +	def RaiseE(self, msg):
 |  | ||||||
| +		raise ExceptionE(msg)
 |  | ||||||
| +
 |  | ||||||
| +bus = SessionBus()
 |  | ||||||
| +
 |  | ||||||
| +with bus.publish("net.lew21.pydbus.tests.Test", TestObject()):
 |  | ||||||
| +	remote = bus.get("net.lew21.pydbus.tests.Test")
 |  | ||||||
| +
 |  | ||||||
| +	def t_func():
 |  | ||||||
| +		# Test new registered errors.
 |  | ||||||
| +		try:
 |  | ||||||
| +			remote.RaiseA("Test A")
 |  | ||||||
| +		except ExceptionA as e:
 |  | ||||||
| +			assert str(e) == "Test A"
 |  | ||||||
| +
 |  | ||||||
| +		try:
 |  | ||||||
| +			remote.RaiseB("Test B")
 |  | ||||||
| +		except ExceptionB as e:
 |  | ||||||
| +			assert str(e) == "Test B"
 |  | ||||||
| +
 |  | ||||||
| +		# Test mapped errors.
 |  | ||||||
| +		try:
 |  | ||||||
| +			remote.Get("net.lew21.pydbus.tests.TestInterface", "Foo")
 |  | ||||||
| +		except ExceptionC as e:
 |  | ||||||
| +			assert str(e) == "No such property 'Foo'"
 |  | ||||||
| +
 |  | ||||||
| +		# Test default errors.
 |  | ||||||
| +		try:
 |  | ||||||
| +			remote.RaiseD("Test D")
 |  | ||||||
| +		except ExceptionD as e:
 |  | ||||||
| +			assert str(e) == "Test D"
 |  | ||||||
| +
 |  | ||||||
| +		try:
 |  | ||||||
| +			remote.RaiseE("Test E")
 |  | ||||||
| +		except ExceptionD as e:
 |  | ||||||
| +			assert str(e) == "Test E"
 |  | ||||||
| +
 |  | ||||||
| +		# Test with no default errors.
 |  | ||||||
| +		error_registration.map_by_default(None)
 |  | ||||||
| +
 |  | ||||||
| +		try:
 |  | ||||||
| +			remote.RaiseD("Test D")
 |  | ||||||
| +		except Exception as e:
 |  | ||||||
| +			assert not isinstance(e, ExceptionD)
 |  | ||||||
| +
 |  | ||||||
| +		try:
 |  | ||||||
| +			remote.RaiseE("Test E")
 |  | ||||||
| +		except Exception as e:
 |  | ||||||
| +			assert not isinstance(e, ExceptionD)
 |  | ||||||
| +			assert not isinstance(e, ExceptionE)
 |  | ||||||
| +
 |  | ||||||
| +		loop.quit()
 |  | ||||||
| +
 |  | ||||||
| +	t = Thread(None, t_func)
 |  | ||||||
| +	t.daemon = True
 |  | ||||||
| +
 |  | ||||||
| +	def handle_timeout():
 |  | ||||||
| +		print("ERROR: Timeout.")
 |  | ||||||
| +		sys.exit(1)
 |  | ||||||
| +
 |  | ||||||
| +	GLib.timeout_add_seconds(4, handle_timeout)
 |  | ||||||
| +
 |  | ||||||
| +	t.start()
 |  | ||||||
| +	loop.run()
 |  | ||||||
| +	t.join()
 |  | ||||||
| diff --git a/tests/run.sh b/tests/run.sh
 |  | ||||||
| index 271c58a..a08baf8 100755
 |  | ||||||
| --- a/tests/run.sh
 |  | ||||||
| +++ b/tests/run.sh
 |  | ||||||
| @@ -10,10 +10,12 @@ PYTHON=${1:-python}
 |  | ||||||
|   |  | ||||||
|  "$PYTHON" $TESTS_DIR/context.py |  | ||||||
|  "$PYTHON" $TESTS_DIR/identifier.py |  | ||||||
| +"$PYTHON" $TESTS_DIR/error.py
 |  | ||||||
|  if [ "$2" != "dontpublish" ] |  | ||||||
|  then |  | ||||||
|  	"$PYTHON" $TESTS_DIR/publish.py |  | ||||||
|  	"$PYTHON" $TESTS_DIR/publish_properties.py |  | ||||||
|  	"$PYTHON" $TESTS_DIR/publish_multiface.py |  | ||||||
|  	"$PYTHON" $TESTS_DIR/publish_async.py |  | ||||||
| +	"$PYTHON" $TESTS_DIR/publish_error.py
 |  | ||||||
|  fi |  | ||||||
| -- 
 |  | ||||||
| 2.13.5 |  | ||||||
| 
 |  | ||||||
							
								
								
									
										1
									
								
								dead.package
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								dead.package
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | python-pydbus package is retired on c9s for CS-556 | ||||||
| @ -1,105 +0,0 @@ | |||||||
| %global srcname pydbus |  | ||||||
| 
 |  | ||||||
| Name:           python-%{srcname} |  | ||||||
| Version:        0.6.0 |  | ||||||
| Release:        15%{?dist} |  | ||||||
| Summary:        Pythonic DBus library |  | ||||||
| 
 |  | ||||||
| License:        LGPLv2+ |  | ||||||
| URL:            https://pypi.python.org/pypi/pydbus |  | ||||||
| Source0:        https://files.pythonhosted.org/packages/source/%(n=%{srcname}; echo ${n:0:1})/%{srcname}/%{srcname}-%{version}.tar.gz |  | ||||||
| 
 |  | ||||||
| # upstream fix, not yet in release |  | ||||||
| # https://github.com/LEW21/pydbus/commit/ff792feb45bbdc0dd6a9ff7453825e34b6554865 |  | ||||||
| Patch1: 0001-make-direction-attribute-conforming-to-introspect.dt.patch |  | ||||||
| 
 |  | ||||||
| # patch submitted for upstream inclusion, not yet merged |  | ||||||
| # https://github.com/LEW21/pydbus/pull/63 |  | ||||||
| Patch2: 0002-Support-asynchronous-calls-58.patch |  | ||||||
| 
 |  | ||||||
| # patch submitted for upstream inclusion, not yet merged |  | ||||||
| # https://github.com/LEW21/pydbus/pull/64 |  | ||||||
| Patch3: 0003-Support-transformation-between-D-Bus-errors-and-exce.patch |  | ||||||
| 
 |  | ||||||
| BuildArch:      noarch |  | ||||||
| 
 |  | ||||||
| %global _description \ |  | ||||||
| The pydbus module provides pythonic DBUS bindings.\ |  | ||||||
| It is based on PyGI, the Python GObject Introspection bindings,\ |  | ||||||
| which is the recommended way to use GLib from Python. |  | ||||||
| 
 |  | ||||||
| %description %{_description} |  | ||||||
| 
 |  | ||||||
| %package -n python3-%{srcname} |  | ||||||
| Summary:        %{summary} |  | ||||||
| BuildRequires:  python3-devel |  | ||||||
| BuildRequires:  python3-setuptools |  | ||||||
| Requires:       python3-gobject-base |  | ||||||
| %{?python_provide:%python_provide python3-%{srcname}} |  | ||||||
| 
 |  | ||||||
| %description -n python3-%{srcname} %{_description} |  | ||||||
| 
 |  | ||||||
| Python 3 version. |  | ||||||
| 
 |  | ||||||
| %prep |  | ||||||
| %autosetup -n %{srcname}-%{version} -p1 |  | ||||||
| 
 |  | ||||||
| %build |  | ||||||
| %py3_build |  | ||||||
| 
 |  | ||||||
| %install |  | ||||||
| %py3_install |  | ||||||
| 
 |  | ||||||
| %files -n python3-%{srcname} |  | ||||||
| %license LICENSE |  | ||||||
| %doc README.rst |  | ||||||
| %{python3_sitelib}/%{srcname}-*.egg-info/ |  | ||||||
| %{python3_sitelib}/%{srcname}/ |  | ||||||
| 
 |  | ||||||
| %changelog |  | ||||||
| * Wed Jan 27 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-15 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Wed Jul 29 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-14 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Sat May 23 2020 Miro Hrončok <mhroncok@redhat.com> - 0.6.0-13 |  | ||||||
| - Rebuilt for Python 3.9 |  | ||||||
| 
 |  | ||||||
| * Thu Jan 30 2020 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-12 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Thu Oct 03 2019 Miro Hrončok <mhroncok@redhat.com> - 0.6.0-11 |  | ||||||
| - Rebuilt for Python 3.8.0rc1 (#1748018) |  | ||||||
| 
 |  | ||||||
| * Fri Aug 16 2019 Miro Hrončok <mhroncok@redhat.com> - 0.6.0-10 |  | ||||||
| - Rebuilt for Python 3.8 |  | ||||||
| 
 |  | ||||||
| * Fri Jul 26 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-9 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Sat Feb 02 2019 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-8 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Sat Jul 14 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-7 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Fri Jun 15 2018 Miro Hrončok <mhroncok@redhat.com> - 0.6.0-6 |  | ||||||
| - Rebuilt for Python 3.7 |  | ||||||
| 
 |  | ||||||
| * Fri Feb 09 2018 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-5 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Tue Jan 23 2018 Vendula Poncova <vponcova@redhat.com> - 0.6.0-4 |  | ||||||
| - Drop the python2 support. |  | ||||||
| 
 |  | ||||||
| * Tue Sep 05 2017 Martin Kolman <mkolman@redhat.com> - 0.6.0-3 |  | ||||||
| - add patch for DTD fix |  | ||||||
| - add patch with support for asynchronous calls (vponcova) |  | ||||||
| - add patch with support for transformation between D-Bus errors and exceptions (vponcova) |  | ||||||
| 
 |  | ||||||
| * Thu Jul 27 2017 Fedora Release Engineering <releng@fedoraproject.org> - 0.6.0-2 |  | ||||||
| - Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild |  | ||||||
| 
 |  | ||||||
| * Wed Feb 15 2017 Martin Kolman <mkolman@redhat.com> - 0.6.0-1 |  | ||||||
| - Initial package |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user