[Python-modules-commits] [pydbus] 01/03: Import pydbus_0.6.0.orig.tar.gz

Alberto Caso acaso-guest at moszumanska.debian.org
Wed Dec 28 10:42:49 UTC 2016


This is an automated email from the git hooks/post-receive script.

acaso-guest pushed a commit to branch master
in repository pydbus.

commit 9b3b40cf5b3ae18767621154ea6ce8ecfd40461d
Author: Alberto Caso <alberto.caso at juntaex.es>
Date:   Wed Dec 28 11:30:31 2016 +0100

    Import pydbus_0.6.0.orig.tar.gz
---
 .gitignore                                         |   3 -
 .travis.yml                                        |  24 ++++
 MANIFEST.in                                        |   1 +
 Makefile                                           |   9 ++
 README.rst                                         |  46 +++++--
 doc/tutorial.rst                                   |  29 +++--
 {pydbus/examples => examples}/__init__.py          |   0
 .../examples => examples}/clientserver/__init__.py |   0
 .../examples => examples}/clientserver/client.py   |   0
 .../examples => examples}/clientserver/server.py   |   4 +-
 examples/notifications_server.py                   |  65 ++++++++++
 {pydbus/examples => examples/polkit}/__init__.py   |   0
 examples/polkit/dbus.conf                          |  15 +++
 examples/polkit/service.py                         |  27 ++++
 {pydbus/examples => examples}/systemctl.py         |   1 -
 pydbus/__init__.py                                 |   5 +-
 pydbus/_inspect3.py                                |   6 +
 pydbus/auto_names.py                               |   4 +
 pydbus/bus.py                                      |  59 ++++++---
 pydbus/bus_names.py                                |  25 ++--
 pydbus/method_call_context.py                      |  34 +++++
 pydbus/proxy.py                                    |  31 +++--
 pydbus/proxy_method.py                             |  23 +++-
 pydbus/proxy_property.py                           |   4 +-
 pydbus/publication.py                              |  13 +-
 pydbus/registration.py                             | 117 +++++++++++-------
 pydbus/request_name.py                             |  29 +++++
 pydbus/subscription.py                             |   4 +-
 pydbus/tests/context.py                            |  24 ----
 pydbus/tests/publish.py                            |  62 ----------
 pydbus/timeout.py                                  |  15 +++
 setup.py                                           |  15 ++-
 signal-spammer/Makefile                            |   2 -
 signal-spammer/spammer.cpp                         | 137 ---------------------
 {pydbus/tests => tests}/__init__.py                |   0
 tests/context.py                                   |  39 ++++++
 {pydbus/tests => tests}/gnome_music.py             |   4 +-
 {pydbus/tests => tests}/identifier.py              |   0
 tests/publish.py                                   |  62 ++++++++++
 {pydbus/tests => tests}/publish_multiface.py       |  35 +++---
 tests/publish_properties.py                        |  71 +++++++++++
 tests/py2.7-ubuntu-14.04.dockerfile                |  11 ++
 tests/py2.7-ubuntu-16.04.dockerfile                |  11 ++
 tests/py3.4-ubuntu-14.04.dockerfile                |  11 ++
 tests/py3.5-ubuntu-16.04.dockerfile                |  11 ++
 tests/run.sh                                       |  18 +++
 46 files changed, 731 insertions(+), 375 deletions(-)

diff --git a/.gitignore b/.gitignore
index ed4c188..414b218 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,6 +51,3 @@ coverage.xml
 
 # Sphinx documentation
 docs/_build/
-
-# Custom
-signal-spammer/spammer
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..2c3691e
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,24 @@
+sudo: required
+
+env:
+  - distribution: ubuntu
+    version: 16.04
+    python: 3.5
+  - distribution: ubuntu
+    version: 16.04
+    python: 2.7
+  - distribution: ubuntu
+    version: 14.04
+    python: 3.4
+  - distribution: ubuntu
+    version: 14.04
+    python: 2.7
+
+services:
+  - docker
+
+before_install:
+  - "docker pull ${distribution}:${version}"
+
+script:
+  - "docker build --file=tests/py${python}-${distribution}-${version}.dockerfile ."
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..1aba38f
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include LICENSE
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..feb4988
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+packages: wheel sdist
+
+wheel:
+	rm -Rf build
+	./setup.py bdist_wheel
+
+sdist:
+	rm -Rf build
+	./setup.py sdist
diff --git a/README.rst b/README.rst
index b7ee90e..2538983 100644
--- a/README.rst
+++ b/README.rst
@@ -1,13 +1,23 @@
 pydbus
 ======
+.. image:: https://travis-ci.org/LEW21/pydbus.svg?branch=master
+    :target: https://travis-ci.org/LEW21/pydbus
+.. image:: https://badge.fury.io/py/pydbus.svg
+    :target: https://badge.fury.io/py/pydbus
 
 Pythonic DBus library.
 
-It's based on PyGI_, the Python GObject Introspection bindings, which is the recommended way to use GLib from Python. Unfortunately, PyGI is not packaged on pypi, so you need to install it from your distribution's repository (usually called python-gi, python-gobject or pygobject3).
+Changelog: https://github.com/LEW21/pydbus/releases
 
-It's pythonic!
+Requirements
+------------
+* Python 2.7+ - but works best on 3.4+ (help system is nicer there)
+* PyGI_ (not packaged on pypi, you need to install it from your distribution's repository - it's usually called python-gi, python-gobject or pygobject)
+* GLib_ 2.46+ and girepository_ 1.46+ (Ubuntu 16.04+) - for object publication support
 
-And now, it can also publish objects! Changelog: https://github.com/LEW21/pydbus/releases
+.. _PyGI: https://wiki.gnome.org/Projects/PyGObject
+.. _GLib: https://developer.gnome.org/glib/
+.. _girepository: https://wiki.gnome.org/Projects/GObjectIntrospection
 
 Examples
 --------
@@ -35,23 +45,35 @@ List systemd units
 	for unit in systemd.ListUnits():
 	    print(unit)
 
+Start or stop systemd unit
+~~~~~~~~~~~~~~~~~~
+.. code-block:: python
+
+	from pydbus import SystemBus
+
+	bus = SystemBus()
+	systemd = bus.get(".systemd1")
+
+	job1 = systemd.StopUnit("ssh.service", "fail")
+	job2 = systemd.StartUnit("ssh.service", "fail")
+
 Watch for new systemd jobs
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 .. code-block:: python
 
 	from pydbus import SystemBus
-	from gi.repository import GObject
+	from gi.repository import GLib
 
 	bus = SystemBus()
 	systemd = bus.get(".systemd1")
 
 	systemd.JobNew.connect(print)
-	GObject.MainLoop().run()
+	GLib.MainLoop().run()
 
 	# or
 
 	systemd.onJobNew = print
-	GObject.MainLoop().run()
+	GLib.MainLoop().run()
 
 View object's API
 ~~~~~~~~~~~~~~~~~
@@ -69,13 +91,23 @@ More examples & documentation
 
 The Tutorial_ contains more examples and docs.
 
-.. _PyGI: https://wiki.gnome.org/PyGObject
 .. _Tutorial: https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst
 
 Copyright Information
 ---------------------
 
+Copyright (C) 2014, 2015, 2016 Linus Lewandowski <linus at lew21.net>
+
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
diff --git a/doc/tutorial.rst b/doc/tutorial.rst
index 456917c..7474de3 100644
--- a/doc/tutorial.rst
+++ b/doc/tutorial.rst
@@ -2,14 +2,14 @@
 pydbus tutorial
 ===============
 
-:Author: `Janusz Lewandowski`_
+:Author: `Linus Lewandowski`_
 :Based on: python-dbus tutorial by Simon McVittie, `Collabora Ltd.`_ (2006-06-14)
-:Date: 2016-04-08
+:Date: 2016-09-26
 
 .. _`Collabora Ltd.`: http://www.collabora.co.uk/
-.. _`Janusz Lewandowski`: http://lew21.net/
+.. _`Linus Lewandowski`: http://lew21.net/
 
-This tutorial requires Python 2.7 or up, and ``pydbus`` 0.5 or up.
+This tutorial requires Python 2.7 or up, and ``pydbus`` 0.6 or up.
 
 .. contents::
 
@@ -86,13 +86,16 @@ 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.
 
-The only main loop supported by ``pydbus`` is GLib's GObject.MainLoop.
+The only main loop supported by ``pydbus`` is GLib.MainLoop.
+
+GLib.MainLoop
+-------------
 
 To create the loop object use::
 
-    from gi.repository import GObject
+    from gi.repository import GLib
 
-    loop = GObject.MainLoop()
+    loop = GLib.MainLoop()
 
 To execute the loop use::
 
@@ -185,7 +188,7 @@ in another, it won't work.
 See also
 ~~~~~~~~
 
-See the examples in ``pydbus/examples/systemctl.py`` and ``pydbus/tests/gnome_music.py``.
+See the examples in ``examples/systemctl.py`` and ``tests/gnome_music.py``.
 
 Interfaces
 ----------
@@ -313,9 +316,9 @@ method, that can be used as a context manager.
 See also
 ~~~~~~~~
 
-See the example in ``pydbus/examples/clientserver/server.py``.
+See the example in ``examples/clientserver/server.py``.
 
-.. _bus.own_name:
+.. _bus.request_name:
 .. _bus.register_object:
 
 Lower level API
@@ -324,8 +327,8 @@ Lower level API
 Sometimes, you can't just publish everything in one call, you need more control
 over the process of binding a name and exporting single objects.
 
-In this case, you can use ``bus.own_name()`` and ``bus.register_object()`` yourself.
-See ``help(bus.own_name)`` and ``help(bus.register_object)`` for details.
+In this case, you can use ``bus.request_name()`` and ``bus.register_object()`` yourself.
+See ``help(bus.request_name)`` and ``help(bus.register_object)`` for details.
 
 .. --------------------------------------------------------------------
 
@@ -380,7 +383,7 @@ License for this document
 
 Copyright 2006-2007 `Collabora Ltd.`_
 
-Copyright 2016 `Janusz Lewandowski`_
+Copyright 2016 `Linus Lewandowski`_
 
 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
diff --git a/pydbus/examples/__init__.py b/examples/__init__.py
similarity index 100%
copy from pydbus/examples/__init__.py
copy to examples/__init__.py
diff --git a/pydbus/examples/clientserver/__init__.py b/examples/clientserver/__init__.py
similarity index 100%
rename from pydbus/examples/clientserver/__init__.py
rename to examples/clientserver/__init__.py
diff --git a/pydbus/examples/clientserver/client.py b/examples/clientserver/client.py
similarity index 100%
rename from pydbus/examples/clientserver/client.py
rename to examples/clientserver/client.py
diff --git a/pydbus/examples/clientserver/server.py b/examples/clientserver/server.py
similarity index 94%
rename from pydbus/examples/clientserver/server.py
rename to examples/clientserver/server.py
index 4021117..25485ae 100644
--- a/pydbus/examples/clientserver/server.py
+++ b/examples/clientserver/server.py
@@ -5,10 +5,10 @@
 # Python DBUS Test Server
 # runs until the Quit() method is called via DBUS
 
-from gi.repository import GObject
+from gi.repository import GLib
 from pydbus import SessionBus
 
-loop = GObject.MainLoop()
+loop = GLib.MainLoop()
 
 class MyDBUSService(object):
 	"""
diff --git a/examples/notifications_server.py b/examples/notifications_server.py
new file mode 100644
index 0000000..3151974
--- /dev/null
+++ b/examples/notifications_server.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+
+from gi.repository import GLib
+from pydbus import SessionBus
+from pydbus.generic import signal
+
+class Notifications(object):
+	"""
+	<node>
+		<interface name="org.freedesktop.Notifications">
+			<signal name="NotificationClosed">
+				<arg direction="out" type="u" name="id"/>
+				<arg direction="out" type="u" name="reason"/>
+			</signal>
+			<signal name="ActionInvoked">
+				<arg direction="out" type="u" name="id"/>
+				<arg direction="out" type="s" name="action_key"/>
+			</signal>
+			<method name="Notify">
+				<arg direction="out" type="u"/>
+				<arg direction="in" type="s" name="app_name"/>
+				<arg direction="in" type="u" name="replaces_id"/>
+				<arg direction="in" type="s" name="app_icon"/>
+				<arg direction="in" type="s" name="summary"/>
+				<arg direction="in" type="s" name="body"/>
+				<arg direction="in" type="as" name="actions"/>
+				<arg direction="in" type="a{sv}" name="hints"/>
+				<arg direction="in" type="i" name="timeout"/>
+			</method>
+			<method name="CloseNotification">
+				<arg direction="in" type="u" name="id"/>
+			</method>
+			<method name="GetCapabilities">
+				<arg direction="out" type="as" name="caps"/>
+			</method>
+			<method name="GetServerInformation">
+				<arg direction="out" type="s" name="name"/>
+				<arg direction="out" type="s" name="vendor"/>
+				<arg direction="out" type="s" name="version"/>
+				<arg direction="out" type="s" name="spec_version"/>
+			</method>
+		</interface>
+	</node>
+	"""
+
+	NotificationClosed = signal()
+	ActionInvoked = signal()
+
+	def Notify(self, app_name, replaces_id, app_icon, summary, body, actions, hints, timeout):
+		print("Notification: {} {} {} {} {} {} {} {}".format(app_name, replaces_id, app_icon, summary, body, actions, hints, timeout))
+		return 4 # chosen by fair dice roll. guaranteed to be random.
+
+	def CloseNotification(self, id):
+		pass
+
+	def GetCapabilities(self):
+		return []
+
+	def GetServerInformation(self):
+		return ("pydbus.examples.notifications_server", "pydbus", "?", "1.1")
+
+bus = SessionBus()
+bus.publish("org.freedesktop.Notifications", Notifications())
+loop = GLib.MainLoop()
+loop.run()
diff --git a/pydbus/examples/__init__.py b/examples/polkit/__init__.py
similarity index 100%
rename from pydbus/examples/__init__.py
rename to examples/polkit/__init__.py
diff --git a/examples/polkit/dbus.conf b/examples/polkit/dbus.conf
new file mode 100644
index 0000000..44965a5
--- /dev/null
+++ b/examples/polkit/dbus.conf
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<!-- Copy/symlink this file as /etc/dbus-1/system.d/pydbus-example.conf (or whatever.conf) -->
+<busconfig>
+	<policy user="root">
+		<allow own="net.lew21.pydbus.PolkitExample"/>
+	</policy>
+
+	<policy context="default">
+		<allow send_destination="net.lew21.pydbus.PolkitExample"/>
+
+		<allow receive_sender="net.lew21.pydbus.PolkitExample"/>
+	</policy>
+</busconfig>
diff --git a/examples/polkit/service.py b/examples/polkit/service.py
new file mode 100644
index 0000000..f80a6a5
--- /dev/null
+++ b/examples/polkit/service.py
@@ -0,0 +1,27 @@
+from pydbus import SystemBus
+from gi.repository import GLib
+import logging
+
+loop = GLib.MainLoop()
+
+class TestObject(object):
+	dbus = '''
+<node>
+	<interface name='net.lew21.pydbus.PolkitExample'>
+		<method name='TestAuth'>
+			<arg type='b' name='interactive' direction='in'/>
+			<arg type='s' name='response' direction='out'/>
+		</method>
+	</interface>
+</node>
+	'''
+	def TestAuth(self, interactive, dbus_context):
+		if dbus_context.is_authorized('org.freedesktop.policykit.exec', {'polkit.icon': 'abcd', 'aaaa': 'zzzz'}, interactive=interactive):
+			return "OK"
+		else:
+			return "Forbidden"
+
+with SystemBus() as bus:
+	with bus.publish("net.lew21.pydbus.PolkitExample", TestObject()):
+		logging.info("Started.")
+		loop.run()
diff --git a/pydbus/examples/systemctl.py b/examples/systemctl.py
similarity index 96%
rename from pydbus/examples/systemctl.py
rename to examples/systemctl.py
index 1e82dbc..e9c70a7 100644
--- a/pydbus/examples/systemctl.py
+++ b/examples/systemctl.py
@@ -1,5 +1,4 @@
 from pydbus import SystemBus
-from gi.repository import Gio
 
 bus = SystemBus()
 
diff --git a/pydbus/__init__.py b/pydbus/__init__.py
index 45fe1b7..5576517 100644
--- a/pydbus/__init__.py
+++ b/pydbus/__init__.py
@@ -1,3 +1,4 @@
-from .bus import SystemBus, SessionBus
+from .bus import SystemBus, SessionBus, connect
+from gi.repository.GLib import Variant
 
-__all__ = ["SystemBus", "SessionBus"]
+__all__ = ["SystemBus", "SessionBus", "connect", "Variant"]
diff --git a/pydbus/_inspect3.py b/pydbus/_inspect3.py
index a33b1e2..b0a2f22 100644
--- a/pydbus/_inspect3.py
+++ b/pydbus/_inspect3.py
@@ -1,4 +1,5 @@
 from collections import OrderedDict
+from inspect import getargspec
 
 class _empty:
 	pass
@@ -15,8 +16,13 @@ class Parameter:
 
 	POSITIONAL_ONLY = 0
 	POSITIONAL_OR_KEYWORD = 1
+	KEYWORD_ONLY = 999
 
 	def __init__(self, name, kind, default=_empty, annotation=_empty):
 		self.name = name
 		self.kind = kind
 		self.annotation = annotation
+
+def signature(f):
+	parameters = [Parameter(arg, Parameter.POSITIONAL_OR_KEYWORD) for arg in getargspec(f).args]
+	return Signature(parameters = parameters)
diff --git a/pydbus/auto_names.py b/pydbus/auto_names.py
index cd4c293..06c6c18 100644
--- a/pydbus/auto_names.py
+++ b/pydbus/auto_names.py
@@ -1,9 +1,13 @@
+from gi.repository import Gio
 
 def auto_bus_name(bus_name):
 	if bus_name[0] == ".":
 		#Default namespace
 		bus_name = "org.freedesktop" + bus_name
 
+	if not Gio.dbus_is_name(bus_name):
+		raise ValueError("invalid bus name")
+
 	return bus_name
 
 def auto_object_path(bus_name, object_path=None):
diff --git a/pydbus/bus.py b/pydbus/bus.py
index b010ed0..6726390 100644
--- a/pydbus/bus.py
+++ b/pydbus/bus.py
@@ -1,35 +1,60 @@
 from gi.repository import Gio
 from .proxy import ProxyMixin
+from .request_name import RequestNameMixin
 from .bus_names import OwnMixin, WatchMixin
 from .subscription import SubscriptionMixin
 from .registration import RegistrationMixin
 from .publication import PublicationMixin
 
-class Bus(ProxyMixin, OwnMixin, WatchMixin, SubscriptionMixin, RegistrationMixin, PublicationMixin):
+def pydbus_property(self):
+	try:
+		return self._pydbus
+	except AttributeError:
+		self._pydbus = Bus(self)
+		return self._pydbus
+
+Gio.DBusConnection.pydbus = property(pydbus_property)
+
+def bus_get(type):
+	return Gio.bus_get_sync(type, None).pydbus
+
+def connect(address):
+	c = Gio.DBusConnection.new_for_address_sync(address, Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT | Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, None, None)
+	c.pydbus.autoclose = True
+	return c.pydbus
+
+class Bus(ProxyMixin, RequestNameMixin, OwnMixin, WatchMixin, SubscriptionMixin, RegistrationMixin, PublicationMixin):
 	Type = Gio.BusType
 
-	def __init__(self, type, timeout=1000):
-		self.con = Gio.bus_get_sync(type, None)
-		self.timeout = timeout
+	def __init__(self, gio_con):
+		self.con = gio_con
+		self.autoclose = False
 
 	def __enter__(self):
 		return self
 
 	def __exit__(self, exc_type, exc_value, traceback):
-		self.con = None
+		if self.autoclose:
+			self.con.close_sync(None)
+
+	@property
+	def dbus(self):
+		try:
+			return self._dbus
+		except AttributeError:
+			self._dbus = self.get(".DBus")[""]
+			return self._dbus
+
+	@property
+	def polkit_authority(self):
+		try:
+			return self._polkit_authority
+		except AttributeError:
+			self._polkit_authority = self.get(".PolicyKit1", "Authority")[""]
+			return self._polkit_authority
 
 def SystemBus():
-	return Bus(Bus.Type.SYSTEM)
+	return bus_get(Bus.Type.SYSTEM)
 
 def SessionBus():
-	return Bus(Bus.Type.SESSION)
-
-if __name__ == "__main__":
-	import sys
-	title = sys.argv[1] if len(sys.argv) >= 2 else "Hello World!"
-	message = sys.argv[2] if len(sys.argv) >= 3 else 'pydbus works :)'
-
-	bus = SessionBus()
-	notifications = bus.get('.Notifications') # org.freedesktop automatically prepended
-	notifications[""].Notify('test', 0, 'dialog-information', title, message, [], {}, 5000)
-	# [""] is not required, but makes us compatible with Gnome 2030.
+	return bus_get(Bus.Type.SESSION)
diff --git a/pydbus/bus_names.py b/pydbus/bus_names.py
index 290627a..3b27431 100644
--- a/pydbus/bus_names.py
+++ b/pydbus/bus_names.py
@@ -1,5 +1,6 @@
 from gi.repository import Gio
 from .exitable import ExitableWithAliases
+import warnings
 
 class NameOwner(ExitableWithAliases("unown")):
 	Flags = Gio.BusNameOwnerFlags
@@ -22,13 +23,13 @@ class OwnMixin(object):
 	NameOwnerFlags = NameOwner.Flags
 
 	def own_name(self, name, flags=0, name_aquired=None, name_lost=None):
-		"""Asynchronously aquires a bus name.
+		"""[DEPRECATED] Asynchronously aquires a bus name.
 
 		Starts acquiring name on the bus specified by bus_type and calls
 		name_acquired and name_lost when the name is acquired respectively lost.
 
-		To receive name_aquired and name_lost callbacks, you need GLib main loop.
-		You can execute it with GObject.MainLoop().run().
+		To receive name_aquired and name_lost callbacks, you need an event loop.
+		https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst#setting-up-an-event-loop
 
 		Parameters
 		----------
@@ -50,6 +51,8 @@ class OwnMixin(object):
 		See https://developer.gnome.org/gio/2.44/gio-Owning-Bus-Names.html#g-bus-own-name
 		for more information.
 		"""
+		warnings.warn("own_name() is deprecated, use request_name() instead.", DeprecationWarning)
+
 		name_aquired_handler = (lambda con, name: name_aquired()) if name_aquired is not None else None
 		name_lost_handler    = (lambda con, name: name_lost())    if name_lost    is not None else None
 		return NameOwner(self.con, name, flags, name_aquired_handler, name_lost_handler)
@@ -65,8 +68,8 @@ class WatchMixin(object):
 		name_appeared and name_vanished when the name is known to have a owner
 		respectively known to lose its owner.
 
-		To receive name_appeared and name_vanished callbacks, you need GLib main loop.
-		You can execute it with GObject.MainLoop().run().
+		To receive name_appeared and name_vanished callbacks, you need an event loop.
+		https://github.com/LEW21/pydbus/blob/master/doc/tutorial.rst#setting-up-an-event-loop
 
 		Parameters
 		----------
@@ -92,15 +95,3 @@ class WatchMixin(object):
 		name_appeared_handler = (lambda con, name, name_owner: name_appeared(name_owner)) if name_appeared is not None else None
 		name_vanished_handler = (lambda con, name:             name_vanished())           if name_vanished is not None else None
 		return NameWatcher(self.con, name, flags, name_appeared_handler, name_vanished_handler)
-
-if __name__ == "__main__":
-	from . import SessionBus
-	from gi.repository import GObject
-
-	def echo(x):
-		print(x)
-
-	bus = SessionBus()
-	bus.watch_name("com.example", 0, echo)
-	bus.own_name("com.example")
-	GObject.MainLoop().run()
diff --git a/pydbus/method_call_context.py b/pydbus/method_call_context.py
new file mode 100644
index 0000000..a565631
--- /dev/null
+++ b/pydbus/method_call_context.py
@@ -0,0 +1,34 @@
+from gi.repository import GLib
+from collections import namedtuple
+
+AuthorizationResult = namedtuple("AuthorizationResult", "is_authorized is_challenge details")
+
+class MethodCallContext(object):
+	def __init__(self, gdbus_method_invocation):
+		self._mi = gdbus_method_invocation
+
+	@property
+	def bus(self):
+		return self._mi.get_connection().pydbus
+
+	@property
+	def sender(self):
+		return self._mi.get_sender()
+
+	@property
+	def object_path(self):
+		return self._mi.get_object_path()
+
+	@property
+	def interface_name(self):
+		return self._mi.get_interface_name()
+
+	@property
+	def method_name(self):
+		return self._mi.get_method_name()
+
+	def check_authorization(self, action_id, details, interactive=False):
+		return AuthorizationResult(*self.bus.polkit_authority.CheckAuthorization(('system-bus-name', {'name': GLib.Variant("s", self.sender)}), action_id, details, 1 if interactive else 0, ''))
+
+	def is_authorized(self, action_id, details, interactive=False):
+		return self.check_authorization(action_id, details, interactive).is_authorized
diff --git a/pydbus/proxy.py b/pydbus/proxy.py
index be1cdd2..c081113 100644
--- a/pydbus/proxy.py
+++ b/pydbus/proxy.py
@@ -5,11 +5,12 @@ from .auto_names import *
 from .proxy_method import ProxyMethod
 from .proxy_property import ProxyProperty
 from .proxy_signal import ProxySignal, OnSignal
+from .timeout import timeout_to_glib
 
 class ProxyMixin(object):
 	__slots__ = ()
 
-	def get(self, bus_name, object_path=None):
+	def get(self, bus_name, object_path=None, **kwargs):
 		"""Get a remote object.
 
 		Parameters
@@ -31,26 +32,38 @@ class ProxyMixin(object):
 		>>> bus.get(".systemd1")[".Manager"]
 		which will give you access to the one specific interface.
 		"""
+		# Python 2 sux
+		for kwarg in kwargs:
+			if kwarg not in ("timeout",):
+				raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg))
+		timeout = kwargs.get("timeout", None)
+
 		bus_name = auto_bus_name(bus_name)
 		object_path = auto_object_path(bus_name, object_path)
 
-		xml = self.con.call_sync(
+		ret = self.con.call_sync(
 			bus_name, object_path,
 			'org.freedesktop.DBus.Introspectable', "Introspect", None, GLib.VariantType.new("(s)"),
-			0, self.timeout, None).unpack()[0]
+			0, timeout_to_glib(timeout), None)
+
+		if not ret:
+			raise KeyError("no such object; you might need to pass object path as the 2nd argument for get()")
 
-		introspection = ET.fromstring(xml)
+		xml, = ret.unpack()
 
-		if len(introspection) == 0:
-			raise KeyError("no such object")
+		try:
+			introspection = ET.fromstring(xml)
+		except:
+			raise KeyError("object provides invalid introspection XML")
 
 		return CompositeInterface(introspection)(self, bus_name, object_path)
 
 class ProxyObject(object):
-	def __init__(self, bus, bus_name, path):
+	def __init__(self, bus, bus_name, path, object=None):
 		self._bus = bus
 		self._bus_name = bus_name
 		self._path = path
+		self._object = object if object else self
 
 def Interface(iface):
 
@@ -90,7 +103,7 @@ def CompositeInterface(introspection):
 			assert(len(matching_bases) == 1)
 
 			iface_class = matching_bases[0]
-			return iface_class(self._bus, self._bus_name, self._path)
+			return iface_class(self._bus, self._bus_name, self._path, self)
 
 		@classmethod
 		def _Introspect(cls):
@@ -101,6 +114,8 @@ def CompositeInterface(introspection):
 					pass
 
 	ifaces = sorted([x for x in introspection if x.tag == "interface"], key=lambda x: int(x.attrib["name"].startswith("org.freedesktop.DBus.")))
+	if not ifaces:
+		raise KeyError("object does not export any interfaces; you might need to pass object path as the 2nd argument for get()")
 	CompositeObject.__bases__ = tuple(Interface(iface) for iface in ifaces)
 	CompositeObject.__name__ = "<CompositeObject>"
 	CompositeObject.__qualname__ = "<CompositeObject>(" + "+".join(x.__name__ for x in CompositeObject.__bases__) + ")"
diff --git a/pydbus/proxy_method.py b/pydbus/proxy_method.py
index f5a9f30..8798edd 100644
--- a/pydbus/proxy_method.py
+++ b/pydbus/proxy_method.py
@@ -1,6 +1,7 @@
 from gi.repository import GLib
 from .generic import bound_method
 from .identifier import filter_identifier
+from .timeout import timeout_to_glib
 
 try:
 	from inspect import Signature, Parameter
@@ -32,14 +33,14 @@ class ProxyMethod(object):
 		self.__name__ = method.attrib["name"]
 		self.__qualname__ = self._iface_name + "." + self.__name__
 
-		inargs  = [(arg.attrib.get("name", ""), arg.attrib["type"]) for arg in method if arg.tag == "arg" and arg.attrib["direction"] == "in"]
+		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._sinargs  = "(" + "".join(x[1] for x in inargs) + ")"
+		self._sinargs  = "(" + "".join(x[1] for x in self._inargs) + ")"
 		self._soutargs = "(" + "".join(self._outargs) + ")"
 
 		self_param = Parameter("self", Parameter.POSITIONAL_ONLY)
 		pos_params = []
-		for i, a in enumerate(inargs):
+		for i, a in enumerate(self._inargs):
 			name = filter_identifier(a[0])
 
 			if not name:
@@ -55,11 +56,23 @@ class ProxyMethod(object):
 		if put_signature_in_doc:
 			self.__doc__ = self.__name__ + str(self.__signature__)
 
-	def __call__(self, instance, *args):
+	def __call__(self, instance, *args, **kwargs):
+		argdiff = len(args) - len(self._inargs)
+		if argdiff < 0:
+			raise TypeError(self.__qualname__ + " missing {} required positional argument(s)".format(-argdiff))
+		elif argdiff > 0:
+			raise TypeError(self.__qualname__ + " takes {} positional argument(s) but {} was/were given".format(len(self._inargs), len(args)))
+
+		# Python 2 sux
+		for kwarg in kwargs:
+			if kwarg not in ("timeout",):
+				raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg))
+		timeout = kwargs.get("timeout", None)
+
 		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, instance._bus.timeout, None).unpack()
+			0, timeout_to_glib(timeout), None).unpack()
 
 		if len(self._outargs) == 0:
 			return None
diff --git a/pydbus/proxy_property.py b/pydbus/proxy_property.py
index f7c3bf7..e06a17d 100644
--- a/pydbus/proxy_property.py
+++ b/pydbus/proxy_property.py
@@ -19,13 +19,13 @@ class ProxyProperty(object):
 		if not self._readable:
 			raise AttributeError("unreadable attribute")
 
-		return instance["org.freedesktop.DBus.Properties"].Get(self._iface_name, self.__name__)
+		return instance._object["org.freedesktop.DBus.Properties"].Get(self._iface_name, self.__name__)
 
 	def __set__(self, instance, value):
 		if instance is None or not self._writeable:
 			raise AttributeError("can't set attribute")
 
-		instance["org.freedesktop.DBus.Properties"].Set(self._iface_name, self.__name__, GLib.Variant(self._type, value))
+		instance._object["org.freedesktop.DBus.Properties"].Set(self._iface_name, self.__name__, GLib.Variant(self._type, value))
 
 	def __repr__(self):
 		return "<property " + self.__qualname__ + " at 0x" + format(id(self), "x") + ">"
diff --git a/pydbus/publication.py b/pydbus/publication.py
index 10cbb32..ef03825 100644
--- a/pydbus/publication.py
+++ b/pydbus/publication.py
@@ -5,9 +5,15 @@ from .auto_names import *
 class Publication(ExitableWithAliases("unpublish")):
 	__slots__ = ()
 
-	def __init__(self, bus, bus_name, *objects):
+	def __init__(self, bus, bus_name, *objects, **kwargs): # allow_replacement=True, replace=False
+		# Python 2 sux
+		for kwarg in kwargs:
+			if kwarg not in ("allow_replacement", "replace",):
+				raise TypeError(self.__qualname__ + " got an unexpected keyword argument '{}'".format(kwarg))
+		allow_replacement = kwargs.get("allow_replacement", True)
+		replace = kwargs.get("replace", False)
+
 		bus_name = auto_bus_name(bus_name)
-		self._at_exit(bus.own_name(bus_name).__exit__)
 
 		for object_info in objects:
 			path, object, node_info = (None, None, None)
@@ -25,6 +31,9 @@ class Publication(ExitableWithAliases("unpublish")):
 			path = auto_object_path(bus_name, path)
 			self._at_exit(bus.register_object(path, object, node_info).__exit__)
 
+		# Request name only after registering all the objects.
+		self._at_exit(bus.request_name(bus_name, allow_replacement=allow_replacement, replace=replace).__exit__)
+
 class PublicationMixin(object):
 	__slots__ = ()
 
diff --git a/pydbus/registration.py b/pydbus/registration.py
index ccdbc9d..f531539 100644
--- a/pydbus/registration.py
+++ b/pydbus/registration.py
@@ -3,9 +3,17 @@ import sys, traceback
 from gi.repository import GLib, Gio
 from . import generic
 from .exitable import ExitableWithAliases
+from functools import partial
+from .method_call_context import MethodCallContext
+import logging
+
+try:
+	from inspect import signature, Parameter
+except:
+	from ._inspect3 import signature, Parameter
 
 class ObjectWrapper(ExitableWithAliases("unwrap")):
-	__slots__ = ["object", "outargs", "property_types"]
+	__slots__ = ["object", "outargs", "readable_properties", "writable_properties"]
 
 	def __init__(self, object, interfaces):
 		self.object = object
@@ -15,10 +23,14 @@ class ObjectWrapper(ExitableWithAliases("unwrap")):
 			for method in iface.methods:
 				self.outargs[iface.name + "." + method.name] = [arg.signature for arg in method.out_args]
 
-		self.property_types = {}
+		self.readable_properties = {}
+		self.writable_properties = {}
 		for iface in interfaces:
 			for prop in iface.properties:
-				self.property_types[iface.name + "." + prop.name] = prop.signature
+				if prop.flags & Gio.DBusPropertyInfoFlags.READABLE:
+					self.readable_properties[iface.name + "." + prop.name] = prop.signature
+				if prop.flags & Gio.DBusPropertyInfoFlags.WRITABLE:
+					self.writable_properties[iface.name + "." + prop.name] = prop.signature
 
 		for iface in interfaces:
 			for signal in iface.signals:
@@ -30,7 +42,7 @@ class ObjectWrapper(ExitableWithAliases("unwrap")):
 		if "org.freedesktop.DBus.Properties" not in (iface.name for iface in interfaces):
 			try:
 				def onPropertiesChanged(iface, changed, invalidated):
-					changed = {key: GLib.Variant(self.property_types[iface + "." + key], val) for key, val in changed.items()}
+					changed = {key: GLib.Variant(self.readable_properties[iface + "." + key], val) for key, val in changed.items()}
 					args = GLib.Variant("(sa{sv}as)", (iface, changed, invalidated))
 					self.SignalEmitted("org.freedesktop.DBus.Properties", "PropertiesChanged", args)
 				self._at_exit(object.PropertiesChanged.connect(onPropertiesChanged).__exit__)
@@ -41,67 +53,88 @@ class ObjectWrapper(ExitableWithAliases("unwrap")):
 
 	def call_method(self, connection, sender, object_path, interface_name, method_name, parameters, invocation):
 		try:
-			outargs = self.outargs[interface_name + "." + method_name]
-			soutargs = "(" + "".join(outargs) + ")"
-
-			method = getattr(self.object, method_name)
-
-			result = method(*parameters)
-
-			#if len(outargs) == 1:
-			#	result = (result,)
+			try:
+				outargs = self.outargs[interface_name + "." + method_name]
+				method = getattr(self.object, method_name)
+			except KeyError:
+				if interface_name == "org.freedesktop.DBus.Properties":
+					if method_name == "Get":
+						method = self.Get
+						outargs = ["v"]
+					elif method_name == "GetAll":
+						method = self.GetAll
+						outargs = ["a{sv}"]
+					elif method_name == "Set":
+						method = self.Set
+						outargs = []
+					else:
+						raise
+				else:
+					raise
+
+			sig = signature(method)
+
+			kwargs = {}
+			if "dbus_context" in sig.parameters and sig.parameters["dbus_context"].kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
+				kwargs["dbus_context"] = MethodCallContext(invocation)
+
+			result = method(*parameters, **kwargs)
 
 			if len(outargs) == 0:
 				invocation.return_value(None)
 			elif len(outargs) == 1:
-				invocation.return_value(GLib.Variant(soutargs, (result,)))
+				invocation.return_value(GLib.Variant("(" + "".join(outargs) + ")", (result,)))
 			else:
-				invocation.return_value(GLib.Variant(soutargs, result))
+				invocation.return_value(GLib.Variant("(" + "".join(outargs) + ")", result))
 
 		except Exception as e:
+			logger = logging.getLogger(__name__)
+			logger.exception("Exception while handling %s.%s()", interface_name, method_name)
+
... 808 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pydbus.git



More information about the Python-modules-commits mailing list