summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore71
-rw-r--r--.travis.yml57
-rw-r--r--AUTHORS35
-rw-r--r--COPYING579
-rw-r--r--ChangeLog2
-rw-r--r--Makefile.am483
-rw-r--r--NEWS704
-rw-r--r--README35
-rwxr-xr-xautogen.sh37
-rw-r--r--configure.ac167
-rw-r--r--dbus-gmain/.gitignore19
-rw-r--r--dbus-gmain/AUTHORS15
-rw-r--r--dbus-gmain/CONTRIBUTING.md (renamed from CONTRIBUTING.md)0
-rw-r--r--dbus-gmain/COPYING551
-rw-r--r--dbus-gmain/Makefile.am82
-rw-r--r--dbus-gmain/README.md (renamed from README.md)0
-rw-r--r--dbus-gmain/dbus-gmain.c (renamed from dbus-gmain.c)0
-rw-r--r--dbus-gmain/dbus-gmain.h (renamed from dbus-gmain.h)0
-rw-r--r--dbus-gmain/tests/30574.c (renamed from tests/30574.c)0
-rw-r--r--dbus-gmain/tests/test-thread-client.c (renamed from tests/test-thread-client.c)0
-rw-r--r--dbus-gmain/tests/test-thread-server.c (renamed from tests/test-thread-server.c)0
-rw-r--r--dbus-gmain/tests/test-thread.h (renamed from tests/test-thread.h)0
-rw-r--r--dbus-gmain/tests/util.c (renamed from tests/util.c)0
-rw-r--r--dbus-gmain/tests/util.h (renamed from tests/util.h)0
-rw-r--r--dbus-python.pc.in12
-rw-r--r--dbus/__init__.py96
-rw-r--r--dbus/_compat.py6
-rw-r--r--dbus/_dbus.py230
-rw-r--r--dbus/_expat_introspect_parser.py85
-rw-r--r--dbus/bus.py445
-rw-r--r--dbus/connection.py667
-rw-r--r--dbus/decorators.py354
-rw-r--r--dbus/exceptions.py134
-rw-r--r--dbus/gi_service.py85
-rw-r--r--dbus/glib.py51
-rw-r--r--dbus/gobject_service.py86
-rw-r--r--dbus/lowlevel.py36
-rw-r--r--dbus/mainloop/__init__.py62
-rw-r--r--dbus/mainloop/glib.py41
-rw-r--r--dbus/proxies.py567
-rw-r--r--dbus/server.py117
-rw-r--r--dbus/service.py835
-rw-r--r--dbus/types.py14
-rw-r--r--dbus_bindings/abstract.c841
-rw-r--r--dbus_bindings/bus.c199
-rw-r--r--dbus_bindings/bytes.c304
-rw-r--r--dbus_bindings/compat-internal.h32
-rw-r--r--dbus_bindings/conn-internal.h67
-rw-r--r--dbus_bindings/conn-methods.c1068
-rw-r--r--dbus_bindings/conn.c500
-rw-r--r--dbus_bindings/containers.c811
-rw-r--r--dbus_bindings/dbus_bindings-internal.h296
-rw-r--r--dbus_bindings/debug.c96
-rw-r--r--dbus_bindings/exceptions.c104
-rw-r--r--dbus_bindings/float.c156
-rw-r--r--dbus_bindings/generic.c61
-rw-r--r--dbus_bindings/int.c784
-rw-r--r--dbus_bindings/libdbusconn.c126
-rw-r--r--dbus_bindings/mainloop.c207
-rw-r--r--dbus_bindings/message-append.c1294
-rw-r--r--dbus_bindings/message-get-args.c558
-rw-r--r--dbus_bindings/message-internal.h49
-rw-r--r--dbus_bindings/message.c1112
-rw-r--r--dbus_bindings/module.c432
-rw-r--r--dbus_bindings/pending-call.c294
-rw-r--r--dbus_bindings/server.c618
-rw-r--r--dbus_bindings/signature.c255
-rw-r--r--dbus_bindings/string.c375
-rw-r--r--dbus_bindings/types-internal.h112
-rw-r--r--dbus_bindings/unixfd.c252
-rw-r--r--dbus_bindings/validation.c245
-rw-r--r--dbus_glib_bindings/module.c215
-rw-r--r--doc/API_CHANGES.txt124
-rw-r--r--doc/HACKING.txt74
-rw-r--r--doc/PY3PORT.txt222
-rw-r--r--doc/_static/.gitignore0
-rw-r--r--doc/conf.py165
-rw-r--r--doc/dbus.bus.rst7
-rw-r--r--doc/dbus.connection.rst7
-rw-r--r--doc/dbus.decorators.rst7
-rw-r--r--doc/dbus.exceptions.rst7
-rw-r--r--doc/dbus.gi_service.rst7
-rw-r--r--doc/dbus.glib.rst7
-rw-r--r--doc/dbus.gobject_service.rst36
-rw-r--r--doc/dbus.lowlevel.rst7
-rw-r--r--doc/dbus.mainloop.rst18
-rw-r--r--doc/dbus.proxies.rst7
-rw-r--r--doc/dbus.rst35
-rw-r--r--doc/dbus.server.rst7
-rw-r--r--doc/dbus.service.rst7
-rw-r--r--doc/dbus.types.rst7
-rw-r--r--doc/index.rst53
-rw-r--r--doc/news.rst5
-rw-r--r--doc/redirects138
-rw-r--r--doc/redirects.py43
-rw-r--r--doc/tutorial.txt716
-rwxr-xr-xexamples/example-async-client.py120
-rwxr-xr-xexamples/example-client.py78
-rwxr-xr-xexamples/example-service.py83
-rwxr-xr-xexamples/example-signal-emitter.py69
-rwxr-xr-xexamples/example-signal-recipient.py99
-rwxr-xr-xexamples/gconf-proxy-client.py37
-rwxr-xr-xexamples/gconf-proxy-service2.py72
-rwxr-xr-xexamples/list-system-services.py69
-rwxr-xr-xexamples/unix-fd-client.py76
-rwxr-xr-xexamples/unix-fd-service.py75
-rw-r--r--include/dbus/dbus-python.h119
-rw-r--r--m4/ax_python_devel.m4327
-rwxr-xr-xsetup.py110
-rw-r--r--test/TestSuitePythonService.service.in3
-rwxr-xr-xtest/cross-test-client.py421
-rwxr-xr-xtest/cross-test-server.py343
-rw-r--r--test/crosstest.py44
-rw-r--r--test/dbus_py_test.c152
-rw-r--r--test/import-repeatedly.c25
-rwxr-xr-xtest/run-test.sh143
-rwxr-xr-xtest/test-client.py639
-rwxr-xr-xtest/test-exception-py2.py70
-rwxr-xr-xtest/test-exception-py3.py39
-rwxr-xr-xtest/test-p2p.py109
-rwxr-xr-xtest/test-server.py76
-rwxr-xr-xtest/test-service.py387
-rwxr-xr-xtest/test-signals.py173
-rwxr-xr-xtest/test-standalone.py548
-rwxr-xr-xtest/test-unusable-main-loop.py47
-rw-r--r--test/tmp-session-bus.conf.in21
-rwxr-xr-xtest/wait-for-name.py57
-rwxr-xr-xtest/with-session-bus.sh103
-rw-r--r--tools/check-c-style.sh17
-rw-r--r--tools/check-coding-style.mk26
-rw-r--r--tools/check-py-style.sh18
-rw-r--r--tools/check-whitespace.sh17
-rwxr-xr-xtools/ci-build.sh145
-rwxr-xr-xtools/travis-ci-setup.sh53
134 files changed, 24310 insertions, 629 deletions
diff --git a/.gitignore b/.gitignore
index ba527ff..371fd93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,19 +1,62 @@
-*.a
-*.gcda
-*.gcno
-*.lineno
+*.html
+*.la
*.lo
*.o
-*~
+*.orig
+*.pyc
+*.pyo
+*.rej
+.*.sw?
.deps/
+.dirstamp
.libs/
-/Makefile
-/Makefile.in
-/libdbus-gmain.la
+/INSTALL
+/aclocal.m4
+/api/
+/autom4te.cache/
+/build-aux/
+/build/
+/compile
+/config.cache
+/config.guess
+/config.h
+/config.h.in*
+/config.log
+/config.status
+/config.sub
+/configure
+/dbus-python-*.tar.gz
+/dbus-python-*.tar.gz.asc
+/dbus-python.pc
+/dbus/.doc-needs-rebuild-stamp
+/dbus/_version.py
+/dbus_python.egg-info/
+/depcomp
+/dist/
+/doc/_build/
+/install-sh
+/libtool
+/ltmain.sh
+/m4/ax_*.m4
+/m4/libtool.m4
+/m4/ltoptions.m4
+/m4/ltsugar.m4
+/m4/ltversion.m4
+/m4/lt~obsolete.m4
+/m4/pkg.m4
+/missing
+/py-compile
+/stamp-h1
/test-suite.log
-/tests/*.log
-/tests/*.trs
-/tests/libtest.la
-/tests/test-30574
-/tests/test-thread-client
-/tests/test-thread-server
+/test/*.trs
+/test/TestSuitePythonService.service
+/test/cross-*.log
+/test/monitor.log
+/test/run-test.log
+/test/test-*.log
+/test/test-import-repeatedly
+/test/tmp-session-bus.conf
+Makefile
+Makefile.in
+__pycache__/
+test-service.log
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..fce7573
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,57 @@
+# Copyright © 2016 Simon McVittie
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+sudo: required
+dist: trusty
+language: python
+cache:
+ pip: true
+ directories:
+ - $HOME/.ccache
+install:
+ - test -z "$dbus_ci_system_python" || deactivate
+ - tools/travis-ci-setup.sh
+python:
+ - "3.5"
+ - "3.5-dev"
+ - "3.6"
+ - "3.6-dev"
+ - "nightly"
+script:
+ - PATH="/usr/lib/ccache:$PATH" ./tools/ci-build.sh
+
+matrix:
+ include:
+ - python: "2.7"
+ env:
+ dbus_ci_system_python=python
+ - python: "2.7"
+ env:
+ dbus_ci_system_python=python-dbg
+ - python: "3.4"
+ env:
+ dbus_ci_system_python=python3
+ - python: "3.4"
+ env:
+ dbus_ci_system_python=python3-dbg
+
+# vim:set sw=2 sts=2 et:
diff --git a/AUTHORS b/AUTHORS
index 83c7766..aa914eb 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,15 +1,20 @@
-Alexander Larsson
-Anders Carlsson
-Carlos Garnacho Parro
-Christian Dywan
-Colin Walters
-Havoc Pennington
-James Willcox
-Kristian Hogsberg
-Marc-Andre Lureau
-Mikael Hallendal
-Mike Gorse
-Richard Hult
-Ross Burton
-Steve Frécinaux
-Tobias Mueller
+Olivier Andrieu <oliv__a@users.sourceforge.net>
+Philip Blundell <pb@nexus.co.uk>
+Anders Carlsson <andersca@gnome.org>
+Kristian Hogsberg <krh@redhat.com>
+Alex Larsson <alexl@redhat.com>
+Robert McQueen <robot101@debian.org>
+Simon McVittie <simon.mcvittie@collabora.co.uk>
+Michael Meeks <michael@ximian.com>
+Osvaldo Santana Neto <osvaldo.santana@indt.org.br>
+Seth Nickell <seth@gnome.org>
+John (J5) Palmieri <johnp@redhat.com>
+Havoc Pennington <hp@redhat.com>
+Harri Porten <porten@kde.org>
+Matthew Rickard <mjricka@epoch.ncsc.mil>
+Zack Rusin <zack@kde.org>
+Joe Shaw <joe@assbarn.com>
+Colin Walters <walters@verbum.org>
+David Zeuthen <david@fubar.dk>
+
+(See also dbus-gmain/AUTHORS.)
diff --git a/COPYING b/COPYING
index d89d965..c037e8f 100644
--- a/COPYING
+++ b/COPYING
@@ -1,551 +1,28 @@
-The D-Bus GLib main loop bindings are licensed to you under your choice
-of the Academic Free License version 2.1, or the GNU General Public
-License version 2. Both licenses are included here.
-
-In SPDX terms, this is:
-
-SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
-
-------------------------------------------------------------------------
-
-The Academic Free License
-v. 2.1
-
-This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
-
-Licensed under the Academic Free License version 2.1
-
-1) Grant of Copyright License. Licensor hereby grants You a
-world-wide, royalty-free, non-exclusive, perpetual, sublicenseable
-license to do the following:
-
-a) to reproduce the Original Work in copies;
-
-b) to prepare derivative works ("Derivative Works") based upon the Original Work;
-
-c) to distribute copies of the Original Work and Derivative Works to the public;
-
-d) to perform the Original Work publicly; and
-
-e) to display the Original Work publicly.
-
-2) Grant of Patent License. Licensor hereby grants You a world-wide,
-royalty-free, non-exclusive, perpetual, sublicenseable license, under
-patent claims owned or controlled by the Licensor that are embodied in
-the Original Work as furnished by the Licensor, to make, use, sell and
-offer for sale the Original Work and Derivative Works.
-
-3) Grant of Source Code License. The term "Source Code" means the
-preferred form of the Original Work for making modifications to it and
-all available documentation describing how to modify the Original
-Work. Licensor hereby agrees to provide a machine-readable copy of the
-Source Code of the Original Work along with each copy of the Original
-Work that Licensor distributes. Licensor reserves the right to satisfy
-this obligation by placing a machine-readable copy of the Source Code
-in an information repository reasonably calculated to permit
-inexpensive and convenient access by You for as long as Licensor
-continues to distribute the Original Work, and by publishing the
-address of that information repository in a notice immediately
-following the copyright notice that applies to the Original Work.
-
-4) Exclusions From License Grant. Neither the names of Licensor, nor
-the names of any contributors to the Original Work, nor any of their
-trademarks or service marks, may be used to endorse or promote
-products derived from this Original Work without express prior written
-permission of the Licensor. Nothing in this License shall be deemed to
-grant any rights to trademarks, copyrights, patents, trade secrets or
-any other intellectual property of Licensor except as expressly stated
-herein. No patent license is granted to make, use, sell or offer to
-sell embodiments of any patent claims other than the licensed claims
-defined in Section 2. No right is granted to the trademarks of
-Licensor even if such marks are included in the Original Work. Nothing
-in this License shall be interpreted to prohibit Licensor from
-licensing under different terms from this License any Original Work
-that Licensor otherwise would have a right to license.
-
-5) This section intentionally omitted.
-
-6) Attribution Rights. You must retain, in the Source Code of any
-Derivative Works that You create, all copyright, patent or trademark
-notices from the Source Code of the Original Work, as well as any
-notices of licensing and any descriptive text identified therein as an
-"Attribution Notice." You must cause the Source Code for any
-Derivative Works that You create to carry a prominent Attribution
-Notice reasonably calculated to inform recipients that You have
-modified the Original Work.
-
-7) Warranty of Provenance and Disclaimer of Warranty. Licensor
-warrants that the copyright in and to the Original Work and the patent
-rights granted herein by Licensor are owned by the Licensor or are
-sublicensed to You under the terms of this License with the permission
-of the contributor(s) of those copyrights and patent rights. Except as
-expressly stated in the immediately proceeding sentence, the Original
-Work is provided under this License on an "AS IS" BASIS and WITHOUT
-WARRANTY, either express or implied, including, without limitation,
-the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
-WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential
-part of this License. No license to Original Work is granted hereunder
-except under this disclaimer.
-
-8) Limitation of Liability. Under no circumstances and under no legal
-theory, whether in tort (including negligence), contract, or
-otherwise, shall the Licensor be liable to any person for any direct,
-indirect, special, incidental, or consequential damages of any
-character arising as a result of this License or the use of the
-Original Work including, without limitation, damages for loss of
-goodwill, work stoppage, computer failure or malfunction, or any and
-all other commercial damages or losses. This limitation of liability
-shall not apply to liability for death or personal injury resulting
-from Licensor's negligence to the extent applicable law prohibits such
-limitation. Some jurisdictions do not allow the exclusion or
-limitation of incidental or consequential damages, so this exclusion
-and limitation may not apply to You.
-
-9) Acceptance and Termination. If You distribute copies of the
-Original Work or a Derivative Work, You must make a reasonable effort
-under the circumstances to obtain the express assent of recipients to
-the terms of this License. Nothing else but this License (or another
-written agreement between Licensor and You) grants You permission to
-create Derivative Works based upon the Original Work or to exercise
-any of the rights granted in Section 1 herein, and any attempt to do
-so except under the terms of this License (or another written
-agreement between Licensor and You) is expressly prohibited by
-U.S. copyright law, the equivalent laws of other countries, and by
-international treaty. Therefore, by exercising any of the rights
-granted to You in Section 1 herein, You indicate Your acceptance of
-this License and all of its terms and conditions.
-
-10) Termination for Patent Action. This License shall terminate
-automatically and You may no longer exercise any of the rights granted
-to You by this License as of the date You commence an action,
-including a cross-claim or counterclaim, against Licensor or any
-licensee alleging that the Original Work infringes a patent. This
-termination provision shall not apply for an action alleging patent
-infringement by combinations of the Original Work with other software
-or hardware.
-
-11) Jurisdiction, Venue and Governing Law. Any action or suit relating
-to this License may be brought only in the courts of a jurisdiction
-wherein the Licensor resides or in which Licensor conducts its primary
-business, and under the laws of that jurisdiction excluding its
-conflict-of-law provisions. The application of the United Nations
-Convention on Contracts for the International Sale of Goods is
-expressly excluded. Any use of the Original Work outside the scope of
-this License or after its termination shall be subject to the
-requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101
-et seq., the equivalent laws of other countries, and international
-treaty. This section shall survive the termination of this License.
-
-12) Attorneys Fees. In any action to enforce the terms of this License
-or seeking damages relating thereto, the prevailing party shall be
-entitled to recover its costs and expenses, including, without
-limitation, reasonable attorneys' fees and costs incurred in
-connection with such action, including any appeal of such action. This
-section shall survive the termination of this License.
-
-13) Miscellaneous. This License represents the complete agreement
-concerning the subject matter hereof. If any provision of this License
-is held to be unenforceable, such provision shall be reformed only to
-the extent necessary to make it enforceable.
-
-14) Definition of "You" in This License. "You" throughout this
-License, whether in upper or lower case, means an individual or a
-legal entity exercising rights under, and complying with all of the
-terms of, this License. For legal entities, "You" includes any entity
-that controls, is controlled by, or is under common control with
-you. For purposes of this definition, "control" means (i) the power,
-direct or indirect, to cause the direction or management of such
-entity, whether by contract or otherwise, or (ii) ownership of fifty
-percent (50%) or more of the outstanding shares, or (iii) beneficial
-ownership of such entity.
-
-15) Right to Use. You may use the Original Work in all ways not
-otherwise restricted or conditioned by this License or by law, and
-Licensor promises not to interfere with or be responsible for such
-uses by You.
-
-This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights
-reserved. Permission is hereby granted to copy and distribute this
-license without modification. This license may not be modified without
-the express written permission of its copyright owner.
-
-
---
-END OF ACADEMIC FREE LICENSE. The following is intended to describe the essential
-differences between the Academic Free License (AFL) version 1.0 and other
-open source licenses:
-
-The Academic Free License is similar to the BSD, MIT, UoI/NCSA and Apache
-licenses in many respects but it is intended to solve a few problems with
-those licenses.
-
-* The AFL is written so as to make it clear what software is being
-licensed (by the inclusion of a statement following the copyright notice
-in the software). This way, the license functions better than a template
-license. The BSD, MIT and UoI/NCSA licenses apply to unidentified software.
-
-* The AFL contains a complete copyright grant to the software. The BSD
-and Apache licenses are vague and incomplete in that respect.
-
-* The AFL contains a complete patent grant to the software. The BSD, MIT,
-UoI/NCSA and Apache licenses rely on an implied patent license and contain
-no explicit patent grant.
-
-* The AFL makes it clear that no trademark rights are granted to the
-licensor's trademarks. The Apache license contains such a provision, but the
-BSD, MIT and UoI/NCSA licenses do not.
-
-* The AFL includes the warranty by the licensor that it either owns the
-copyright or that it is distributing the software under a license. None of
-the other licenses contain that warranty. All other warranties are disclaimed,
-as is the case for the other licenses.
-
-* The AFL is itself copyrighted (with the right granted to copy and distribute
-without modification). This ensures that the owner of the copyright to the
-license will control changes. The Apache license contains a copyright notice,
-but the BSD, MIT and UoI/NCSA licenses do not.
-
-------------------------------------------------------------------------
-
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
+As of version 0.82.4, dbus-python itself is released under the following
+permissive non-copyleft license (the same one that was proposed for
+D-Bus core but wasn't achieved):
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use, copy,
+ modify, merge, publish, distribute, sublicense, and/or sell copies
+ of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+Copyright holders and licensing are indicated in the source files.
+
+The dbus-gmain/ subdirectory is under a different license: see
+dbus-gmain/COPYING for details.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..a8ccf81
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,2 @@
+In git versions of dbus-python, please use 'git-log' instead of referring to
+ChangeLog. A changelog is generated from the git history during 'make dist'.
diff --git a/Makefile.am b/Makefile.am
index 63a1a2a..fd480d4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,82 +1,467 @@
-# SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+abs_top_srcdir = @abs_top_srcdir@
+abs_top_builddir = @abs_top_builddir@
+installed_testdir = ${libexecdir}/installed-tests/${PACKAGE_TARNAME}
+installed_test_testdir = ${installed_testdir}/test
+installed_test_metadir = ${datadir}/installed-tests/${PACKAGE_TARNAME}
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = dbus-gmain .
+
+CLEANFILES = \
+ test/test-service.log \
+ $(NULL)
EXTRA_DIST = \
+ .travis.yml \
AUTHORS \
- CONTRIBUTING.md \
COPYING \
- README.md \
+ ChangeLog \
+ dbus-python.pc.in \
+ doc/_static/.gitignore \
+ examples/example-async-client.py \
+ examples/example-client.py \
+ examples/example-service.py \
+ examples/example-signal-emitter.py \
+ examples/example-signal-recipient.py \
+ examples/gconf-proxy-client.py \
+ examples/gconf-proxy-service2.py \
+ examples/list-system-services.py \
+ examples/unix-fd-client.py \
+ examples/unix-fd-service.py \
+ setup.py \
+ test/TestSuitePythonService.service.in \
+ test/tmp-session-bus.conf.in \
+ tools/check-c-style.sh \
+ tools/check-coding-style.mk \
+ tools/check-py-style.sh \
+ tools/check-whitespace.sh
+ tools/ci-build.sh \
$(NULL)
+# === C code ===
+
AM_CPPFLAGS = \
- -I$(top_srcdir) \
- -I$(top_builddir) \
+ -include config.h \
+ -I$(top_srcdir)/include \
$(DBUS_CFLAGS) \
$(GLIB_CFLAGS) \
+ $(PYTHON_CPPFLAGS) \
+ $(NULL)
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(NULL)
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(NULL)
+
+pymod_ldflags = \
+ -module \
+ -avoid-version \
+ $(NULL)
+pymod_libadd = \
+ $(NULL)
+
+if WINDOWS
+# Win32 DLLs can't have undefined symbols (so this needs explicit linking
+# against the Python DLL), and Python expects extensions to be *.pyd
+# instead of *.dll
+pymod_ldflags += \
+ -no-undefined \
+ -shrext ".pyd" \
+ $(NULL)
+pymod_libadd += \
+ $(PYTHON_LIBS) \
+ $(NULL)
+endif
+
+pyexec_LTLIBRARIES = \
+ _dbus_bindings.la \
+ _dbus_glib_bindings.la \
+ $(NULL)
+
+if ENABLE_INSTALLED_TESTS
+nobase_installed_test_LTLIBRARIES = test/dbus_py_test.la
+else
+noinst_LTLIBRARIES = test/dbus_py_test.la
+endif
+
+_dbus_bindings_la_LDFLAGS = \
+ $(pymod_ldflags) \
+ -export-symbols-regex \(PyInit__\|init_\)dbus_bindings \
+ $(AM_LDFLAGS) \
+ $(NULL)
+_dbus_bindings_la_LIBADD = \
+ $(pymod_libadd) \
+ $(DBUS_LIBS) \
+ $(NULL)
+_dbus_bindings_la_SOURCES = \
+ dbus_bindings/abstract.c \
+ dbus_bindings/bus.c \
+ dbus_bindings/bytes.c \
+ dbus_bindings/compat-internal.h \
+ dbus_bindings/conn.c \
+ dbus_bindings/conn-internal.h \
+ dbus_bindings/conn-methods.c \
+ dbus_bindings/containers.c \
+ dbus_bindings/dbus_bindings-internal.h \
+ dbus_bindings/debug.c \
+ dbus_bindings/exceptions.c \
+ dbus_bindings/float.c \
+ dbus_bindings/generic.c \
+ dbus_bindings/int.c \
+ dbus_bindings/unixfd.c \
+ dbus_bindings/libdbusconn.c \
+ dbus_bindings/mainloop.c \
+ dbus_bindings/message-append.c \
+ dbus_bindings/message.c \
+ dbus_bindings/message-get-args.c \
+ dbus_bindings/message-internal.h \
+ dbus_bindings/module.c \
+ dbus_bindings/pending-call.c \
+ dbus_bindings/server.c \
+ dbus_bindings/signature.c \
+ dbus_bindings/string.c \
+ dbus_bindings/types-internal.h \
+ dbus_bindings/validation.c \
$(NULL)
-noinst_LTLIBRARIES = \
- libdbus-gmain.la \
- tests/libtest.la \
+dbus-gmain/libdbus-gmain.la:
+ $(MAKE) -C dbus-gmain
+
+_dbus_glib_bindings_la_LDFLAGS = \
+ $(pymod_ldflags) \
+ -export-symbols-regex \(PyInit__\|init_\)dbus_glib_bindings \
+ $(AM_LDFLAGS) \
+ $(NULL)
+_dbus_glib_bindings_la_LIBADD = \
+ dbus-gmain/libdbus-gmain.la \
+ $(pymod_libadd) \
+ $(DBUS_LIBS) \
+ $(NULL)
+_dbus_glib_bindings_la_SOURCES = \
+ dbus_glib_bindings/module.c \
$(NULL)
-libdbus_gmain_la_SOURCES = \
- dbus-gmain.c \
- dbus-gmain.h \
+# unconditionally add an -rpath to force Libtool to build a shared library
+test_dbus_py_test_la_LDFLAGS = \
+ $(pymod_ldflags) \
+ $(AM_LDFLAGS) \
+ -rpath $(installed_testdir) \
+ $(NULL)
+test_dbus_py_test_la_LIBADD = $(DBUS_LIBS)
+test_dbus_py_test_la_SOURCES = \
+ include/dbus/dbus-python.h \
+ test/dbus_py_test.c \
$(NULL)
-libdbus_gmain_la_LIBADD = $(DBUS_LIBS) $(GLIB_LIBS)
-libdbus_gmain_la_LDFLAGS = -no-undefined
+# === dbus package ===
+
+nobase_python_PYTHON = \
+ dbus/bus.py \
+ dbus/connection.py \
+ dbus/_compat.py \
+ dbus/_dbus.py \
+ dbus/decorators.py \
+ dbus/exceptions.py \
+ dbus/_expat_introspect_parser.py \
+ dbus/gi_service.py \
+ dbus/glib.py \
+ dbus/__init__.py \
+ dbus/lowlevel.py \
+ dbus/mainloop/__init__.py \
+ dbus/mainloop/glib.py \
+ dbus/proxies.py \
+ dbus/server.py \
+ dbus/service.py \
+ dbus/types.py
+
+if !HAVE_PYTHON_3
+nobase_python_PYTHON += \
+ dbus/gobject_service.py \
+ $(NULL)
+endif
+
+check_py_sources = $(nobase_python_PYTHON)
+include $(top_srcdir)/tools/check-coding-style.mk
+
+# === Devel stuff ===
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = dbus-python.pc
+
+dbusincludedir = $(includedir)/dbus-1.0/dbus
+dbusinclude_HEADERS = include/dbus/dbus-python.h
+
+# === Tests ===
+
+cross-test-compile: all
+
+cross-test-server:
+ $(AM_TESTS_ENVIRONMENT) $(PYTHON) $(top_srcdir)/test/cross-test-server.py
+cross-test-client:
+ $(AM_TESTS_ENVIRONMENT) $(PYTHON) $(top_srcdir)/test/cross-test-client.py
+
+AM_TESTS_ENVIRONMENT = \
+ export DBUS_TOP_SRCDIR="$(abs_top_srcdir)"; \
+ export DBUS_TOP_BUILDDIR="$(abs_top_builddir)"; \
+ export DBUS_TEST_TMPDIR="$(abs_top_builddir)/test"; \
+ export DBUS_TEST_UNINSTALLED=1; \
+ export DBUS_PYTHON_VERSION='$(PACKAGE_VERSION)'; \
+ export PYTHONPATH="$(abs_top_srcdir):$(abs_top_srcdir)/test:$(abs_top_builddir)/.libs:$(abs_top_builddir)/test/.libs"; \
+ export PYTHON='$(PYTHON)'; \
+ export DBUS_FATAL_WARNINGS=1; \
+ $(NULL)
+
+TEST_EXTENSIONS = .sh .py
+
+LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh
+SH_LOG_DRIVER = $(LOG_DRIVER)
+PY_LOG_DRIVER = $(LOG_DRIVER)
+
+LOG_COMPILER = $(DBUS_RUN_SESSION) \
+ --config-file=$(top_builddir)/test/tmp-session-bus.conf \
+ --
+installed_log_compiler = $(DBUS_RUN_SESSION) \
+ --config-file=$(installed_testdir)/test/tmp-session-bus.conf \
+ --
-tests_libtest_la_SOURCES = \
- tests/util.c \
- tests/util.h \
+installed_log_compiler += \
+ env \
+ PYTHON=$(PYTHON) \
+ DBUS_TOP_SRCDIR=$(installed_testdir) \
+ DBUS_TOP_BUILDDIR=$(installed_testdir) \
$(NULL)
-tests_libtest_la_LIBADD = $(DBUS_LIBS) $(GLIB_LIBS)
-tests_libtest_la_LDFLAGS = -no-undefined
+SH_LOG_COMPILER = $(LOG_COMPILER) $(SHELL)
+PY_LOG_COMPILER = $(LOG_COMPILER) $(PYTHON)
+
+dist_test_sh = \
+ test/run-test.sh \
+ $(NULL)
+
+dist_test_py = \
+ test/test-client.py \
+ test/test-p2p.py \
+ test/test-signals.py \
+ test/test-standalone.py \
+ test/test-unusable-main-loop.py \
+ $(NULL)
+
+dist_test_extra_python = \
+ test/crosstest.py \
+ $(NULL)
+
+dist_test_extra_scripts = \
+ test/cross-test-client.py \
+ test/cross-test-server.py \
+ test/test-service.py \
+ test/wait-for-name.py \
+ test/with-session-bus.sh \
+ $(NULL)
+
+if HAVE_PYTHON_3
+dist_test_py += test/test-exception-py3.py
+else
+dist_test_py += test/test-exception-py2.py
+endif
+
+test_programs = \
+ test/test-import-repeatedly \
+ $(NULL)
+
+test_test_import_repeatedly_SOURCES = test/import-repeatedly.c
+test_test_import_repeatedly_CPPFLAGS = $(PYTHON_CPPFLAGS)
+test_test_import_repeatedly_LDADD = $(PYTHON_LIBS) $(PYTHON_EXTRA_LIBS)
+test_test_import_repeatedly_LDFLAGS = $(PYTHON_EXTRA_LDFLAGS)
TESTS = \
- tests/test-30574 \
+ $(dist_test_py) \
+ $(dist_test_sh) \
+ $(test_programs) \
$(NULL)
-noinst_PROGRAMS = \
- tests/test-30574 \
- tests/test-thread-server \
- tests/test-thread-client \
+noinst_DATA = \
+ test/TestSuitePythonService.service \
+ test/tmp-session-bus.conf \
$(NULL)
+CLEANFILES += $(noinst_DATA)
-tests_test_thread_server_SOURCES = \
- tests/test-thread-server.c \
- tests/test-thread.h \
+installed_test_test_data = \
+ test/installable/TestSuitePythonService.service \
+ test/installable/tmp-session-bus.conf \
$(NULL)
-tests_test_thread_server_LDADD = \
- libdbus-gmain.la \
- tests/libtest.la \
- $(GLIB_THREADS_LIBS) \
- $(GLIB_LIBS) \
- $(DBUS_LIBS) \
+CLEANFILES += $(installed_test_test_data)
+
+$(noinst_DATA): test/%: test/%.in
+ @$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)$(SED) \
+ -e 's|[@]PYTHON[@]|$(PYTHON)|g' \
+ -e 's|[@]G_TEST_SRCDIR[@]|$(abs_top_srcdir)|g' \
+ -e 's|[@]G_TEST_BUILDDIR[@]|$(abs_top_builddir)|g' \
+ $< > $@
+
+$(installed_test_test_data): test/installable/%: test/%.in
+ @$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN)$(SED) \
+ -e 's|[@]PYTHON[@]|$(PYTHON)|g' \
+ -e 's|[@]G_TEST_SRCDIR[@]|$(installed_testdir)|g' \
+ -e 's|[@]G_TEST_BUILDDIR[@]|$(installed_testdir)|g' \
+ $< > $@
+
+if ENABLE_INSTALLED_TESTS
+nobase_installed_test_PROGRAMS = $(test_programs)
+nobase_dist_installed_test_SCRIPTS = \
+ $(dist_test_py) \
+ $(dist_test_sh) \
+ $(dist_test_extra_scripts) \
+ $(NULL)
+nobase_dist_installed_test_PYTHON = \
+ $(dist_test_extra_python) \
$(NULL)
+installed_test_test_DATA = $(installed_test_test_data)
+installed_test_meta_DATA = $(installed_test_metadata)
+else
+noinst_PROGRAMS = $(test_programs)
+dist_noinst_SCRIPTS = \
+ $(dist_test_py) \
+ $(dist_test_sh) \
+ $(dist_test_extra_scripts) \
+ $(NULL)
+endif
-tests_test_thread_client_SOURCES = \
- tests/test-thread-client.c \
- tests/test-thread.h \
+installed_test_metadata = \
+ $(patsubst %,%.test,$(dist_test_py)) \
+ $(patsubst %,%.test,$(dist_test_sh)) \
+ $(patsubst %,%.test,$(test_programs)) \
$(NULL)
-tests_test_thread_client_LDADD = \
- libdbus-gmain.la \
- tests/libtest.la \
- $(GLIB_THREADS_LIBS) \
- $(GLIB_LIBS) \
- $(DBUS_LIBS) \
+CLEANFILES += $(installed_test_metadata)
+
+$(patsubst %,%.test,$(dist_test_py)): %.test: Makefile
+ @$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN) (echo '[Test]' > $@.tmp; \
+ echo 'Type=session' >> $@.tmp; \
+ echo 'Exec=$(installed_log_compiler) $(PYTHON) $(installed_testdir)/$*' >> $@.tmp; \
+ mv $@.tmp $@)
+
+$(patsubst %,%.test,$(dist_test_sh)): %.test: Makefile
+ @$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN) (echo '[Test]' > $@.tmp; \
+ echo 'Type=session' >> $@.tmp; \
+ echo 'Exec=$(installed_log_compiler) $(installed_testdir)/$*' >> $@.tmp; \
+ mv $@.tmp $@)
+
+$(patsubst %,%.test,$(test_programs)): %.test: Makefile
+ @$(MKDIR_P) $(dir $@)
+ $(AM_V_GEN) (echo '[Test]' > $@.tmp; \
+ echo 'Type=session' >> $@.tmp; \
+ echo 'Exec=$(installed_log_compiler) $(installed_testdir)/$*$(EXEEXT)' >> $@.tmp; \
+ mv $@.tmp $@)
+
+# === Documentation ===
+
+@GENERATE_CHANGELOG_RULES@
+
+dist-hook: dist-ChangeLog
+ echo $(VERSION) > $(distdir)/.version
+ touch $(distdir)/MANIFEST
+ touch $(distdir)/MANIFEST.in
+ ( cd $(distdir) && $(PYTHON) setup.py egg_info )
+ cp $(distdir)/dbus_python.egg-info/PKG-INFO $(distdir)
+ ( cd $(distdir) && find -type d -o -print ) | \
+ LC_ALL=C sort | \
+ $(SED) -e 's|^\./||' \
+ > $(distdir)/MANIFEST
+ sed -e 's/.*/include &/' < $(distdir)/MANIFEST > $(distdir)/MANIFEST.in
+ cp $(distdir)/MANIFEST $(distdir)/dbus_python.egg-info/SOURCES.txt
+
+maintainer-upload:
+ rsync -tvpP --chmod=ugo=r $(DIST_ARCHIVES) $(DIST_ARCHIVES:%=%.asc) \
+ dbus.freedesktop.org:/srv/dbus.freedesktop.org/www/releases/dbus-python/
+
+sphinx_sources = \
+ doc/API_CHANGES.txt \
+ doc/conf.py \
+ doc/dbus.bus.rst \
+ doc/dbus.connection.rst \
+ doc/dbus.decorators.rst \
+ doc/dbus.exceptions.rst \
+ doc/dbus.gi_service.rst \
+ doc/dbus.glib.rst \
+ doc/dbus.gobject_service.rst \
+ doc/dbus.lowlevel.rst \
+ doc/dbus.mainloop.rst \
+ doc/dbus.proxies.rst \
+ doc/dbus.rst \
+ doc/dbus.server.rst \
+ doc/dbus.service.rst \
+ doc/dbus.types.rst \
+ doc/HACKING.txt \
+ doc/index.rst \
+ doc/news.rst \
+ doc/PY3PORT.txt \
+ doc/tutorial.txt \
+ NEWS \
+ README \
$(NULL)
+EXTRA_DIST += $(sphinx_sources)
-tests_test_30574_SOURCES = \
- tests/30574.c \
+# A hack used for the HTML documentation on dbus.freedesktop.org
+EXTRA_DIST += \
+ doc/redirects \
+ doc/redirects.py \
$(NULL)
-tests_test_30574_LDADD = \
- libdbus-gmain.la \
- tests/libtest.la \
- $(GLIB_LIBS) \
- $(DBUS_LIBS) \
+
+install-data-local: install-data-local-sphinx
+uninstall-local: uninstall-local-sphinx
+
+if ENABLE_DOCUMENTATION
+all: doc/_build/.stamp
+
+doc/_build/.stamp: $(nobase_python_PYTHON) \
+ _dbus_bindings.la \
+ _dbus_glib_bindings.la \
+ $(sphinx_sources) \
+ Makefile \
+ $(NULL)
+ rm -rf doc/_build
+ $(MKDIR_P) doc/_build
+ abs_top_srcdir='$(abs_top_srcdir)' \
+ abs_top_builddir='$(abs_top_builddir)' \
+ DBUS_PYTHON_NO_DEPRECATED=1 \
+ $(PYTHON) -m sphinx -b html $(abs_top_srcdir)/doc doc/_build
+ touch $@
+
+maintainer-update-website: doc/_build/.stamp
+ DBUS_TOP_SRCDIR="$(abs_top_srcdir)" \
+ $(PYTHON) $(srcdir)/doc/redirects.py
+ rsync -rtvzPp --chmod=Dg+s,ug+rwX,o=rX doc/_build/ \
+ dbus.freedesktop.org:/srv/dbus.freedesktop.org/www/doc/dbus-python/
+
+install-data-local-sphinx: doc/_build/.stamp
+ $(mkinstalldirs) $(DESTDIR)$(htmldir)
+ cp -R doc/_build/* $(DESTDIR)$(htmldir)
+
+uninstall-local-sphinx:
+ rm -fr $(DESTDIR)$(htmldir)
+else
+maintainer-update-website:
+ @echo "*** Not updating the API docs on the website - install sphinx"
+ @echo "*** and configure with --enable-api-docs"
+install-data-local-sphinx:
+ @:
+uninstall-local-sphinx:
+ @:
+endif
+
+clean-local:
+ rm -rf doc/_build
+
+check_c_sources = \
+ $(_dbus_bindings_la_SOURCES) \
+ $(_dbus_glib_bindings_la_SOURCES) \
$(NULL)
-LOG_COMPILER = $(DBUS_RUN_SESSION) --
+include $(top_srcdir)/tools/check-coding-style.mk
+
+.PHONY: cross-test-compile cross-test-server cross-test-client \
+ always-rebuild maintainer-update-website \
+ maintainer-upload
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..49bd4ac
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,704 @@
+D-Bus Python Bindings 1.2.10 (UNRELEASED)
+=========================================
+
+Fixes:
+
+• Avoid a long-standing race condition in the automated tests
+ (Debian #898158)
+
+D-Bus Python Bindings 1.2.8 (2018-05-04)
+========================================
+
+The “cursed ice surface” release.
+
+Dependencies:
+
+• Documentation requires Sphinx and the readthedocs theme
+• Documentation no longer requires epydoc
+
+Enhancements:
+
+• Build documentation with Sphinx instead of epydoc
+
+• Remove obsolete COMPAT.txt, documenting compatibility breaks in
+ versions over a decade old
+
+Fixes:
+
+• Make sure $(builddir)/test exists before creating .test files there
+
+• Add PKG-INFO and egg_info to dist tarballs so they can be uploaded
+ to PyPI again
+
+D-Bus Python Bindings 1.2.6 (2018-01-29)
+========================================
+
+The “doppler radar” release.
+
+Dependencies:
+
+• When using Python 2, version 2.7 is now required. Python 2.6 security
+ support ended in 2013.
+• When using Python 3, version 3.4 or later is now required.
+ Python 3.2 security support ended in 2016, and Python 3.3 security
+ support ended in 2017.
+• Most unit tests now require the tap.py module from PyPI.
+• The deprecated dbus-glib library is no longer required. A bundled copy
+ of its main loop integration code is included instead.
+• GLib version 2.40 or later is required.
+• libdbus version 1.8 or later is required.
+
+Enhancements:
+
+• AX_PYTHON_DEVEL is now used to find the CPPFLAGS, LDFLAGS and libraries
+ needed to link test-import-repeatedly to libpython, which should reduce
+ the number of wheels reinvented here.
+
+• Remove workarounds for Python 2.6 limitations
+
+• All unit tests now produce structured output (TAP)
+
+Fixes:
+
+• Fix a NULL dereference in constructing a Server if the underlying C
+ function fails
+
+• Silence compiler warnings triggered by the Python headers under gcc 7
+
+• Avoid __gtype__ appearing in documentation, for reproducible builds
+
+• Rename _dbus_bindings/ and _dbus_glib_bindings/ source directories to
+ dbus_bindings/ and dbus_glib_bindings/ to avoid an ImportWarning
+ appearing in the API documentation, which made the documentation build
+ non-reproducible
+
+D-Bus Python Bindings 1.2.4 (2016-03-06)
+========================================
+
+The “75,000 microchips” release.
+
+Enhancements:
+
+• Continous integration metadata for travis-ci.org is now available.
+ Similar to dbus, this is split into .travis.yml (Travis-specifics)
+ and tools/ci-build.sh (intended to be useful for any CI framework,
+ although it does include various workarounds for travis-ci oddities).
+ (Simon McVittie)
+
+Fixes:
+
+• Make dbus.version a tuple again, not a list, for consistent sorting.
+ This was a regression in 1.2.2. (Debian #816729, Simon McVittie)
+
+• Use inspect.signature() instead of inspect.getargspec() on Python
+ versions that have it. inspect.getargspec() is deprecated in recent
+ Python 3 and seems to have disappeared from 3.6 nightly builds.
+ (Simon McVittie)
+
+• Make the tests pass in "narrow" Python builds where unicode objects
+ are UTF-16, rather than the UCS-4 used in Linux distributions.
+ (fd.o #57140, Simon McVittie)
+
+• Always include headers in a consistent order (Debian #749133, Simon McVittie)
+
+• Include config.h in all C code that we compile. This is necessary
+ on platforms where it might contain something like "#define _GNU_SOURCE"
+ or "#define inline __inline".
+ (Simon McVittie)
+
+D-Bus Python Bindings 1.2.2 (2016-02-22)
+========================================
+
+The “mind fray” release.
+
+Versioning changes:
+
+• dbus-python releases now have an even micro version (1.2.0, 1.2.2),
+ and snapshots from git have an odd micro version (1.2.1).
+
+Dependencies:
+
+• Building from git (but not from tarballs) now requires
+ macros from the GNU Autoconf Archive, for example the autoconf-archive
+ package in Debian or Fedora derivatives.
+
+• Building from git (but not from tarballs) now requires Automake 1.13
+ or later.
+
+• The automated tests and some examples now require PyGI (the gi module),
+ not PyGObject 2 (the deprecated glib and gobject modules).
+
+Enhancements:
+
+• There is now a setuptools setup.py, allowing dbus-python to be installed
+ into a virtualenv using pip from a standard Automake source release. This
+ requires pre-existing system-wide installations of the normal build
+ dependencies (pkg-config, libdbus, dbus-glib, a C compiler) and has
+ some limitations. For system-wide installations and development,
+ please use the Autoconf/Automake build system directly.
+ (fd.o #55439; Simon McVittie)
+
+• dbus-python now uses the common compiler warnings from AX_COMPILER_FLAGS
+ (Simon McVittie)
+
+• The automated tests can now be installed as GNOME-style "installed tests",
+ and should be somewhat more reliable (Simon McVittie)
+
+Fixes:
+
+• ``from dbus.service import *`` now imports FallbackObject
+ (fd.o #85720; Ben Longbons)
+
+• The GConf-related examples work again (fd.o #85720; Ben Longbons)
+
+• Consistently make examples executable, and install them all
+ (fd.o #85720; Ben Longbons)
+
+• Search PATH for an appropriately-versioned pythonX.Y-config, or as a last
+ resort python-config, if there isn't a ${PYTHON}-config in the
+ same directory as ${PYTHON} (fd.o #92085; Yamashita, Yuu)
+
+• Add support for the Automake 1.13 parallel test driver (Simon McVittie)
+
+• Skip building API documentation if "import epydoc" fails (Simon McVittie)
+
+D-Bus Python Bindings 1.2.0 (2013-05-07)
+========================================
+
+The "compile like it's 1998" release.
+
+Dependencies:
+
+• libdbus 1.6 or later is now required.
+
+Enhancements:
+
+• Unicode Corrigendum 9: when used with a suitable version of libdbus
+ (1.6.10 or later, or 1.7.2 or later), noncharacters in strings are
+ now accepted
+
+Fixes:
+
+• Support DBusException('something with non—ASCII') under Python 2
+ (Michael Vogt, smcv; fd.o #55899)
+
+• Correct some misleading wording in COPYING which was written under the
+ assumption that libdbus could actually be relicensed to MIT/X11
+ (Thiago Macieira)
+
+• Avoid variable-length arrays, because MSVC++ is still stuck in 1998
+ (based on patches from Christoph Höger, fd.o #51725)
+
+• Remove unnecessary uses of stdint.h (fd.o #51725)
+
+• Add support for Unix compilers not supporting 'inline', for completeness
+
+• Use GObject.__class__ instead of GObjectMeta, which can no longer be
+ imported from gi.repository.GObject in pygobject 3.8
+
+• Fix autoreconfiscation on Automake 1.13 (Marko Lindqvist, fd.o #59006)
+
+D-Bus Python Bindings 1.1.1 (2012-06-25)
+========================================
+
+The "Lemonade Sky" release.
+
+Dependencies:
+
+• libdbus 1.6 or later is now recommended. It is not strictly required yet.
+
+Fixes:
+
+• Validate UTF-8 according to the rules libdbus uses, falling back to our
+ own (inefficient) implementation if not compiled against dbus >= 1.6
+ (fd.o #40817)
+
+• Under Python 3, in the absence of introspection or signature='...',
+ pass dbus.ObjectPath or dbus.Signature arguments with the obvious
+ signature 'o' or 'g', not 's'. This previously only worked in Python 2.
+ (fd.o #50740)
+
+D-Bus Python Bindings 1.1.0 (2012-05-09)
+========================================
+
+The “eaten by spiders” release.
+
+Deprecations:
+
+• dbus.gobject_service is deprecated. Use dbus.gi_service and PyGI in new code.
+
+API changes:
+
+• dbus.gobject_service works in legacy PyGObject 2 applications again,
+ like it did before 1.0. The down side is that it doesn't work in all PyGI
+ applications any more, unlike 1.0. In PyGI applications, depend on
+ dbus-python >= 1.1 and use dbus.gi_service instead - its API is the same.
+ (fd.o #48904, Debian #670516)
+
+• dbus.gobject_service has been removed from Python 3 builds altogether.
+
+Enhancements:
+
+• Use DBusBasicValue from libdbus 1.5, if available, rather than reinventing it
+
+Fixes:
+
+• Put sockets for the regression tests in /tmp, not the builddir, fixing
+ test failures in a really long builddir (fd.o #46747)
+
+• Fix a reference leak in dbus_py_variant_level_set (fd.o #47108)
+
+• Modify AM_CHECK_PYTHON_HEADERS so the "another way" works with Python 3
+
+D-Bus Python Bindings 1.0.0 (2012-01-24)
+========================================
+
+The "never trust a .0 release?" release.
+
+Dependencies:
+
+* libdbus 1.4 or later is now required.
+
+* Python 2.6 or later is now required. If Python 3 is used, it must be
+ version 3.2 or later.
+
+* GNU make (or, at your own risk, another make with the GNU $(patsubst)
+ extension) is now required.
+
+API changes:
+
+* dbus_bindings, which was never meant to be public API and has been
+ deprecated for nearly 5 years, has finally been removed.
+
+* The repr() of every dbus-python object is now unicode.
+
+* The Python 3 API is not the same as the Python 2 API; see PY3PORT.rst
+ for details.
+
+• dbus.gobject_service uses PyGI, not PyGObject. (This was not meant to be
+ an incompatible change, but unfortunately, it was. It was reverted in 1.1.0.)
+
+Enhancements:
+
+* Python 3 compatibility (fd.o #26420, Barry Warsaw)
+
+* MethodCallMessage and SignalMessage now have a more useful repr()
+ (Barry Warsaw)
+
+Fixes:
+
+* OOM while appending a unicode object to a message no longer leaks a string
+ (Barry Warsaw)
+
+* If libdbus somehow gives us invalid UTF-8, don't crash (Barry Warsaw)
+
+* Fix rst2html failure in non-UTF-8 locales (Alexandre Rostovtsev)
+
+D-Bus Python Bindings 0.84.0 (2011-05-25)
+=========================================
+
+The "Comrade Bill Bartram's Egalitarian Anti-Imperialist Soviet Stout" release.
+
+Enhancements:
+
+* fd.o #30812: add the UnixFd type, and support Unix fd passing if
+ compiled against a new enough libdbus (Elvis Pfützenreuter)
+
+* fd.o #34342: add Connection.set_allow_anonymous(bool) (Scott Tsai)
+
+* fd.o #21017: add configure arguments PYTHON_INCLUDES and PYTHON_LIBS which
+ can be used to override $PYTHON-config (Simon McVittie, based on a patch from
+ Robert Schwebel)
+
+Fixes:
+
+* fd.o #35626: clear ProxyObject's pending introspection queue after
+ execution (Scott Tsai)
+
+* fd.o #22560: remove duplicate code from example-async-client (Simon McVittie)
+
+* fd.o #36206: allow signature='x' among ProxyObject method arguments
+ (Simon McVittie)
+
+D-Bus Python Bindings 0.83.2 (2010-12-02)
+=========================================
+
+Dependencies:
+
+* libdbus 1.2 is still supported, but libdbus >= 1.4 is recommended.
+
+Fixes:
+
+* Make BusConnection.list_activatable_names actually call ListActivatableNames,
+ not ListNames (Johan Sandelin)
+
+* Don't override CFLAGS when adding compiler warnings
+ (Louis-Francis Ratté-Boulianne)
+
+* Fix compilation on platforms where Py_ssize_t is larger than int, like x86-64
+ (Elvis Pfützenreuter)
+
+* fd.o #21831: deserialize empty byte arrays with byte_arrays=True as
+ ByteArray(''), not ByteArray('None') (Simon McVittie)
+
+* fd.o #23278, #25105: fix crashes when trying to append more struct entries
+ than the signature allows with libdbus 1.4 (Simon McVittie)
+
+* fd.o #23831: fix crashes when an embedded Python interpreter imports dbus,
+ is finalized, is re-initialized, and re-imports dbus (Simon McVittie)
+
+D-Bus Python Bindings 0.83.1 (2010-02-18)
+=========================================
+
+Fixes:
+
+* fd.o #21172: avoid some deprecation warnings in Python 2.6
+
+* fd.o #15013: add dbus.lowlevel.MESSAGE_TYPE_SIGNAL etc., for those who care
+ about message types at a low level
+
+* When removing signal matches, clean up internal state, avoiding a memory
+ leak in long-lived Python processes that connect to signals from arbitrarily
+ many object paths (fd.o #17551, thanks to Marco Pesenti Gritti)
+
+* When setting the sender of a message, allow it to be org.freedesktop.DBus
+ so you can implement a D-Bus daemon in pure Python (patch from Huang Peng)
+
+D-Bus Python Bindings 0.83.0 (2008-07-23)
+=========================================
+
+Features:
+
+* Add bindings for DBusServer (thanks to Mathias Hasselmann, Huang Peng;
+ fd.o #14322, #15514).
+
+* Omit the service's traceback from certain D-Bus errors: specifically, those
+ that were probably deliberately raised as part of an API. Subclasses
+ of DBusException that indicate programmer error can turn the traceback
+ back on if it seems likely to be useful.
+
+Fixes:
+
+* Don't emit spurious Error messages if libdbus gives object-path handlers
+ a message that isn't a method call (most likely because of binding to a
+ locally emitted signal, as in fd.o #14199).
+
+* Make multiple filters added by Connection.add_message_filter work
+ (fd.o #15547, thanks to Huang Peng).
+
+* Make the API docs build correctly when out-of-tree
+
+* Require dbus 1.0 so we can get rid of DBUS_API_SUBJECT_TO_CHANGE
+
+D-Bus Python Bindings 0.82.4 (2007-12-10)
+=========================================
+
+Fixes:
+
+* supplying reply_handler but not error_handler raises
+ MissingReplyHandlerException instead of MissingErrorHandlerException,
+ and vice versa (fd.o #12304, patch from René Neumann)
+* Using non-recursive make for dbus/ directory should fix builds in some
+ environments (fd.o #12741)
+
+Licensing:
+
+* Everything is now under the same MIT/X11 license used for Collabora code in
+ the previous release
+* Added copyright headers to some files that were still missing them
+
+D-Bus Python Bindings 0.82.3 (2007-09-27)
+=========================================
+
+Fixes:
+
+* Out-of-tree builds with an absolute $(srcdir) can now build docs and run tests
+* Closing private dbus.Bus no longer raises KeyError (fd.o #12096)
+* async_err_cb(MyException()) now works (fd.o #12403)
+* dbus.service.Object.remove_from_connection no longer claims that multiple
+ exports aren't possible (fd.o #12432)
+* Setting _dbus_error_name as a class attribute of DBusException subclasses
+ works again
+
+Deprecations:
+
+* dbus.Bus(private=True) (use dbus.bus.BusConnection in new code, dbus.Bus
+ basically just adds the shared-connection behaviour)
+
+Licensing:
+
+* Code for which Collabora is the only copyright holder is now under the
+ same permissive MIT/X11 license under which dbus core is being relicensed
+ (this allows everything the old license would have allowed, and more)
+
+D-Bus Python Bindings 0.82.2 (2007-08-01)
+=========================================
+
+Incompatibility with 0.82.1:
+
+* If you pass the timeout argument to call_async or an asynchronous proxy
+ method call and expect it to be in milliseconds, you should change the
+ argument to be in seconds, and require dbus-python >= 0.82.2.
+
+ This feature didn't work at all in versions prior to 0.82.1, so any code
+ that works with 0.82.0 or earlier is unaffected.
+
+Features:
+
+* @dbus.service.method supports a rel_path_keyword argument for the benefit
+ of fallback objects, which provides the method implementation with the path
+ of the object within the exported subtree. For instance, if you have a
+ fallback object exported at /Fallback, and you call a method that has
+ rel_path_keyword='rel_path' on /Fallback and on /Fallback/Some/Where, the
+ method implementation will be called with rel_path='/' and with
+ rel_path='/Some/Where' respectively. (fd.o #11623)
+
+* If you have epydoc version 3 (currently in beta), API documention is now
+ generated by default.
+
+Fixes:
+
+* As mentioned under "Incompatibilities" above, Connection.call_async()
+ measures timeouts in seconds, as was always intended.
+ This means that calls through a proxy object with a reply_handler and
+ error_handler will measure the timeout in seconds too.
+
+* Introspect() now works on objects exported in more than one location.
+ (fd.o #11794)
+
+* Building against Python 2.4 on non-Debian-derived distributions, or a
+ non-default Python version on Gentoo, should work again (revenge
+ of fd.o #11282, thanks Eyal Ben David).
+
+D-Bus Python Bindings 0.82.1 (2007-07-11)
+=========================================
+
+The "double precision" release.
+
+Fixes:
+
+* Parse the timeout correctly in send_message_with_reply() and
+ send_message_with_reply_and_block(), fixing the use of non-default timeouts
+ (bugs.fd.o #11489)
+* The tutorial no longer uses interactive-Python syntax, as it confused users.
+ (bugs.fd.o #11209)
+* When making a call via a proxy object with ignore_reply=True, also get the
+ necessary introspection data asynchronously. This can avoid deadlocks in
+ some cases, such as calling methods in the same process (though this is not
+ recommended, for efficiency and sanity reasons).
+* dbus.lowlevel exposes enough constants to write correct filter functions.
+* We don't use dbus_watch_get_fd() (deprecated in libdbus) unless our libdbus
+ is too old to have the modern replacement, dbus_watch_get_unix_fd().
+
+Deprecations:
+
+* Omitting the bus argument in the BusName constructor is deprecated.
+ The fact that it uses the globally shared connection to the session bus by
+ default is uncomfortably subtle.
+
+D-Bus Python Bindings 0.82.0 (2007-06-19)
+=========================================
+
+Features:
+
+* dbus.service.Object can start off with no Connection or object path, and
+ become exported later. If suitable class attributes are set, objects can
+ even be exported on multiple connections, or with multiple object-paths,
+ or both.
+
+* dbus.service.FallbackObject implements a whole subtree of object-path space
+ (fd.o #9295).
+
+* ``@method`` accepts a parameter ``connection_keyword`` so methods can find
+ out which connection to use for any follow-up actions.
+
+* ``@signal`` has a new parameter ``rel_path_keyword`` which gets the path at
+ which to emit the signal, relative to the path of the FallbackObject.
+ ``path_keyword`` is now deprecated, and will raise an exception if used
+ on an object with ``SUPPORTS_MULTIPLE_OBJECT_PATHS``, including any
+ ``FallbackObject``.
+
+Fixes:
+
+* In watch_name_owner, only the desired name is watched!
+
+* When cleaning up signal matches, errors are ignored. This avoids using up
+ scarce pending-call allowance on dbus-daemon < 1.1, and emitting error
+ messages if we get disconnected.
+
+* Signal handlers which are bound to a unique name are automatically
+ disconnected when the unique name goes away, reducing the likelihood that
+ applications will leak signal matches.
+
+* Some corrections were made to the tutorial (@service and @method take a
+ parameter dbus_interface, not just interface; fd.o #11209).
+
+* ${PYTHON}-config is used to get the Python include path (patch from
+ Sebastien Bacher/Ubuntu, fd.o #11282).
+
+D-Bus Python Bindings 0.81.1 (4 June 2007)
+==========================================
+
+Features:
+
+* When an Error message on the bus is represented as a DBusException, the
+ error name is copied into the exception and can be retrieved by
+ get_dbus_name(). Exception handlers should use this instead of looking at
+ the stringified form of the exception, unless backwards compatibility
+ is needed.
+* DBusException objects now get all arguments from the Error message, not
+ just the first (although there will usually only be one). Use the 'args'
+ attribute if you need to retrieve them.
+* The Connection, BusConnection and Bus classes have a method
+ list_exported_child_objects(path: str) -> list of str, which wraps
+ dbus_connection_list_registered()
+* You can remove objects from D-Bus before they become unreferenced, by
+ using dbus.service.Object.remove_from_connection()
+ (https://bugs.freedesktop.org/show_bug.cgi?id=10457)
+
+Bug fixes:
+
+* Don't deadlock when removing a signal match that tracks name-owner changes.
+ (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=426412)
+* Include child nodes in introspection using list_exported_child_objects()
+
+D-Bus Python Bindings 0.81.0 (9 May 2007)
+=========================================
+
+The 'series of tubes' release
+-----------------------------
+
+This is a feature release with support for non-bus-daemon connections
+and improved GObject integration.
+
+Features:
+
+* Bus has a superclass dbus.bus.BusConnection (a connection to a bus daemon,
+ but without the shared-connection semantics or any deprecated API)
+ for the benefit of those wanting to subclass bus daemon connections
+
+* BusConnection has a superclass dbus.connection.Connection (a
+ connection without a bus daemon) for use in peer-to-peer situations,
+ or distributed pseudo-bus situations without a bus daemon such as
+ Telepathy's Tubes API
+
+* dbus.gobject_service.ExportedGObject is like dbus.service.Object, but
+ is also a subclass of GObject (with the necessary metaclass magic to
+ make this work). Until someone has verified that the GObject side of
+ things works as expected too, I consider this API to be potentially
+ subject to change!
+
+* Connection and BusConnection have gained a number of useful methods,
+ including watch_name_owner (track name owner changes asynchronously,
+ avoiding race conditions), call_blocking and call_async (blocking and
+ asynchronous method calls without going via a proxy - note that these
+ are semi-low-level interfaces which don't do introspection), and
+ list_names, list_activatable_names and get_name_owner which are
+ simple wrappers for the corresponding org.freedesktop.DBus methods
+
+* dbus.Interface (now also available at dbus.proxies.Interface)
+ and dbus.proxies.ProxyObject now have some reasonably obvious properties.
+
+Deprecations:
+
+* All keyword arguments called named_service are deprecated in favour of an
+ argument called bus_name (to be compatible with both older and newer
+ dbus-python, you should pass these positional arguments).
+
+* The bus keyword argument to dbus.proxies.ProxyObject is deprecated in
+ favour of an argument called conn, because proxies will work on non-bus
+ connections now (again, for maximum compatibility you should use a
+ positional argument for this).
+
+* No warning is raised for this, but I consider calling any remote method
+ on a ProxyObject or Interface whose name is either alllowercase or
+ lower_case_with_underscores to be deprecated, and reserve the right
+ to add properties or methods of this form in future releases - use
+ ProxyObject.get_dbus_method if you must call a remote method named in
+ this way. Methods named following TheUsualDBusConvention or
+ theJavaConvention are safe.
+
+Bugfixes:
+
+* Exceptions in signal handlers print a stack trace to stderr (this can
+ be redirected elsewhere with Python's logging framework). Partially
+ addresses fd.o #9980.
+
+* The reserved local interface and object path are properly checked for.
+
+* When you return a tuple that is not a Struct from a method with no
+ out_signature, it's interpreted as multiple return values, not a
+ single Struct (closes fd.o #10174).
+
+* If send_with_reply() returns TRUE but with pending call NULL, dbus-python
+ no longer crashes. This can happen when unexpectedly disconnected.
+
+* Arguments are not examined for functions declared METH_NOARGS (this is
+ unnecessary and can cause a crash).
+
+Other notable changes:
+
+* dbus-python uses the standard Python logging framework throughout.
+ The first time a WARNING or ERROR is generated, it will configure the
+ logging framework to output to stderr, unless you have already
+ configured logging in your application.
+
+* The tutorial now advocates the use of add_signal_receiver if all you
+ want to do is listen for signals: this avoids undesired activation,
+ e.g. of Listen or Rhythmbox (!). Addresses fd.o #10743, fd.o #10568.
+
+D-Bus Python Bindings 0.80.2 (13 February 2007)
+===============================================
+- Fix numerous memory and reference leaks
+- Only use -Werror if the user specifically asks for it
+- Audit tp_dealloc callbacks to make sure they correctly preserve the
+ exception state
+- Relicense files solely owned by Collabora Ltd. more permissively (LGPL/AFL
+ rather than GPL/AFL) - this includes the tutorial and all the C code
+
+D-Bus Python Bindings 0.80.1 (24 January 2007)
+==============================================
+- The "oops" release
+- Install dbus/_version.py, so dbus.__version__ exists again
+
+D-Bus Python Bindings 0.80.0 (24 January 2007)
+==============================================
+- The "everything changes" release
+- Rewrite dbus_bindings (Pyrex) as _dbus_bindings (C) - API changes!
+- Define what's public API
+- Move low-level but still public API to dbus.lowlevel
+- Remove Variant class, add variant_level property on all D-Bus types
+- Make signal matching keep working as expected when name ownership changes
+- Use unambiguous D-Bus types when transferring from D-Bus to Python
+- Follow well-defined rules when transferring from Python to D-Bus
+- Add utf8_strings and byte_arrays options in various places, so a user
+ can tweak the calling conventions to be more efficient
+- Raise RuntimeError if user tries to use a connection with no main loop
+ to do something that won't work without one
+- Make asynchronous method calls actually asynchronous when made before
+ introspection results come back
+- Redo main loop machinery so we can add pure-Python main loops later without
+ API breakage
+- Allow construction of a dbus.service.Object if you don't have a BusName
+ (or even a Bus)
+- Port introspection XML parser from libxml2 (external package) to expat
+ (included with Python)
+- Port build system from distutils to autoconf/automake/libtool
+- Install a header file for third-party main loop integration
+- Make compatible with Python 2.5, including on 64-bit platforms
+- Add docstrings throughout
+- Add more tests and examples
+- Add interoperability tests (which interoperate with Java)
+- Add copyright notices!
+
+D-Bus Python Bindings 0.71 (24 July 2006)
+==============================================================
+- Binary modules are now installed in the correct directory
+- Distutils exports the dbus and dbus-glib cflags
+
+D-Bus Python Bindings 0.70 (17 July 2006)
+==============================================================
+- First release of bindings split
+- Move to a distutils build enviornment
+- It is possible to now specify sender_keyword="foo", path_keyword="bar" when
+ adding a signal listener
diff --git a/README b/README
new file mode 100644
index 0000000..ddbcca7
--- /dev/null
+++ b/README
@@ -0,0 +1,35 @@
+=======================================
+dbus-python_: Python bindings for D-Bus
+=======================================
+
+.. _dbus-python: http://www.freedesktop.org/wiki/Software/DBusBindings#python
+
+dbus-python is the original Python binding for ``dbus``, the reference
+implementation of the D-Bus protocol.
+
+Online documentation can be found at
+<http://dbus.freedesktop.org/doc/dbus-python/>.
+
+Problems and alternatives
+=========================
+
+dbus-python might not be the best D-Bus binding for you to use. dbus-python
+does not follow the principle of "In the face of ambiguity, refuse the
+temptation to guess", and can't be changed to not do so without seriously
+breaking compatibility.
+
+In addition, it uses libdbus (which has known problems with multi-threaded
+use) and attempts to be main-loop-agnostic (which means you have to select
+a suitable main loop for your application).
+
+Alternative ways to get your Python code onto D-Bus include:
+
+* GDBus, part of the GIO module of `GLib`_, via GObject-Introspection and
+ `PyGI`_ (uses the GLib main loop and object model)
+
+* QtDBus, part of `Qt`_, via `PyQt`_ (uses the Qt main loop and object model)
+
+.. _GLib: http://developer.gnome.org/glib/
+.. _PyGI: https://live.gnome.org/PyGObject
+.. _Qt: https://www.qt.io
+.. _PyQT: http://www.riverbankcomputing.co.uk/software/pyqt/intro
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..56b7d0a
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+test -n "$srcdir" || srcdir=$(dirname "$0")
+test -n "$srcdir" || srcdir=.
+
+olddir=$(pwd)
+
+cd $srcdir
+
+(test -f configure.ac) || {
+ echo "*** ERROR: Directory '$srcdir' does not look like the top-level project directory ***"
+ exit 1
+}
+
+# shellcheck disable=SC2016
+PKG_NAME=$(autoconf --trace 'AC_INIT:$1' configure.ac)
+
+if [ "$#" = 0 -a "x$NOCONFIGURE" = "x" ]; then
+ echo "*** WARNING: I am going to run 'configure' with no arguments." >&2
+ echo "*** If you wish to pass any to it, please specify them on the" >&2
+ echo "*** '$0' command line." >&2
+ echo "" >&2
+fi
+
+aclocal --install || exit 1
+autoreconf --verbose --force --install || exit 1
+
+cd "$olddir"
+if [ "$NOCONFIGURE" = "" ]; then
+ $srcdir/configure "$@" || exit 1
+
+ if [ "$1" = "--help" ]; then exit 0 else
+ echo "Now type 'make' to compile $PKG_NAME" || exit 1
+ fi
+else
+ echo "Skipping configure process."
+fi
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..9bab4ab
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,167 @@
+-*- mode: m4 -*-
+AC_PREREQ(2.59c)
+
+dnl The dbus-python version number
+m4_define(dbus_python_major_version, 1)
+m4_define(dbus_python_minor_version, 2)
+dnl Micro version is odd for non-releases
+m4_define(dbus_python_micro_version, 9)
+
+AC_INIT([dbus-python],
+ dbus_python_major_version.dbus_python_minor_version.dbus_python_micro_version,
+ [http://bugs.freedesktop.org/enter_bug.cgi?product=dbus&component=python])
+AC_CONFIG_MACRO_DIR([m4])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
+AC_CONFIG_AUX_DIR([build-aux])
+
+m4_pattern_forbid([^AX_],
+ [Unexpanded AX_ macro found. Please install GNU autoconf-archive])
+
+# By default, rebuild autotools files on demand; only use ./missing if the
+# user says --disable-maintainer-mode (in particular this is useful when
+# running under pip, which does not preserve timestamps)
+AM_MAINTAINER_MODE([enable])
+
+AX_IS_RELEASE([micro-version])
+AC_DEFINE(DBUS_PYTHON_MAJOR_VERSION, dbus_python_major_version, [dbus-python major version])
+AC_SUBST(DBUS_PYTHON_MAJOR_VERSION, dbus_python_major_version)
+AC_DEFINE(DBUS_PYTHON_MINOR_VERSION, dbus_python_minor_version, [dbus-python minor version])
+AC_SUBST(DBUS_PYTHON_MINOR_VERSION, dbus_python_minor_version)
+AC_DEFINE(DBUS_PYTHON_MICRO_VERSION, dbus_python_micro_version, [dbus-python micro version])
+AC_SUBST(DBUS_PYTHON_MICRO_VERSION, dbus_python_micro_version)
+
+AC_CONFIG_SRCDIR([dbus_bindings/module.c])
+AC_CONFIG_HEADERS(config.h)
+AC_USE_SYSTEM_EXTENSIONS
+
+AM_INIT_AUTOMAKE([1.13 -Wno-portability subdir-objects tar-ustar])
+
+AC_CANONICAL_BUILD
+AC_CANONICAL_HOST
+
+# mingw32, mingw-w64 are native Windows; for our purposes, Cygwin isn't
+AC_MSG_CHECKING([for native Windows host])
+AS_CASE(["$host"],
+ [*-*-mingw*],
+ [windows=yes],
+ [*],
+ [windows=no])
+AC_MSG_RESULT([$windows])
+AM_CONDITIONAL([WINDOWS], [test "x$windows" = xyes])
+
+AC_DISABLE_STATIC
+
+dnl XXXX hack to kill off all the libtool tags ...
+dnl it isn't like we are using C++ or Fortran.
+dnl (copied from libglade/configure.in)
+m4_define([_LT_AC_TAGCONFIG],[])
+
+AC_PROG_LIBTOOL
+AM_PROG_CC_C_O
+AC_C_INLINE
+AC_PROG_MKDIR_P
+
+AC_PROG_AWK
+AC_REQUIRE_AUX_FILE([tap-driver.sh])
+
+AC_ARG_VAR([PYTHON_CPPFLAGS],
+ [compiler flags to find Python headers [default: auto-detect] [typical value: -I/opt/mypython/include]])
+AC_ARG_VAR([PYTHON_LIBS],
+ [libraries to link into Python extensions [default: auto-detect] [typical value: -L/opt/mypython/lib -lpython2.7]])
+AC_ARG_VAR([PYTHON_EXTRA_LIBS],
+ [libraries to link when embedding a Python interpreter [default: auto-detect]])
+AC_ARG_VAR([PYTHON_EXTRA_LDFLAGS],
+ [compiler flags to link when embedding a Python interpreter [default: auto-detect]])
+
+AC_ARG_VAR([PYTHON_INCLUDES], [deprecated form of PYTHON_CPPFLAGS])
+AS_IF([test -n "$PYTHON_INCLUDES"],
+ [PYTHON_CPPFLAGS="$PYTHON_CPPFLAGS $PYTHON_INCLUDES"])
+
+AX_PYTHON_DEVEL([>= '2.7'])
+AM_PATH_PYTHON
+
+# This command exits 0 (success) if sys.version >= "3", or 1 (failure)
+# if sys.version < "3" (i.e. starts with "2").
+AM_CONDITIONAL([HAVE_PYTHON_3],
+ [$PYTHON -c "import sys; sys.exit(sys.version < '3')"])
+
+PLATFORM=`$PYTHON -c "from __future__ import print_function; from distutils import util; print(util.get_platform())"`
+AC_SUBST(PLATFORM)
+
+AC_ARG_ENABLE([installed-tests],
+ [AC_HELP_STRING([--enable-installed tests],
+ [install test programs and helpers for as-installed testing])],
+ [],
+ [enable_installed_tests=no])
+AM_CONDITIONAL([ENABLE_INSTALLED_TESTS], [test "_$enable_installed_tests" = _yes])
+
+dnl Building documentation
+
+AX_GENERATE_CHANGELOG
+
+AC_ARG_ENABLE([documentation],
+ [AC_HELP_STRING([--enable-documentation],
+ [Enable documentation building (requires sphinx and sphinx_rtd_theme)])],
+ [:],
+ [enable_documentation=auto])
+AX_PYTHON_MODULE([sphinx])
+AS_IF([test "x$HAVE_PYMOD_SPHINX" = xno],
+ [
+ AS_IF([test "$enable_documentation" = yes],
+ [AC_MSG_ERROR([cannot build documentation without sphinx Python module])],
+ [enable_documentation=no])
+ ])
+AX_PYTHON_MODULE([sphinx_rtd_theme])
+AS_IF([test "x$HAVE_PYMOD_SPHINX_RTD_THEME" = xno],
+ [
+ AS_IF([test "$enable_documentation" = yes],
+ [AC_MSG_ERROR([cannot build documentation without sphinx_rtd_theme Python module])],
+ [enable_documentation=no])
+ ])
+
+AC_ARG_VAR([DBUS_RUN_SESSION],
+ [The dbus-run-session tool from dbus 1.8 or later])
+AC_PATH_PROG([DBUS_RUN_SESSION], [dbus-run-session], [dbus-run-session])
+
+AM_CONDITIONAL([ENABLE_DOCUMENTATION], [test "$enable_documentation" != no])
+
+PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.8])
+PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.40])
+
+AX_COMPILER_FLAGS([WARN_CFLAGS],
+ [WARN_LDFLAGS], [],
+ dnl unfortunately the Python headers fail various checks included in
+ dnl AX_COMPILER_FLAGS, and are not const-correct for strings
+ [ \
+ -Wdeprecated-declarations \
+ -Wno-duplicated-branches \
+ -Wno-redundant-decls \
+ -Wno-switch-default \
+ -Wno-write-strings \
+ ])
+dnl AX_COMPILER_FLAGS doesn't order the compiler flags correctly to be able
+dnl to disable flags that it would normally enable
+WARN_CFLAGS="$(echo "${WARN_CFLAGS}" | ${SED} \
+ -e s/-Wduplicated-branches// \
+ -e s/-Wredundant-decls// \
+ -e s/-Wswitch-default// \
+ -e s/-Wwrite-strings// \
+ )"
+
+AC_ARG_ENABLE([coding-style-checks],
+ [AC_HELP_STRING([--enable-coding-style-checks],
+ [check coding style using grep])],
+ [ENABLE_CODING_STYLE_CHECKS=$enableval],
+ [ENABLE_CODING_STYLE_CHECKS=$ax_is_release])
+
+# Make dbus-gmain submodule part of dbus-python's namespace
+AH_BOTTOM([
+#define DBUS_GMAIN_FUNCTION_NAME(name) _dbus_py_glib_ ## name
+])
+
+AC_CONFIG_FILES([
+ Makefile
+ dbus-gmain/Makefile
+ dbus-python.pc
+])
+AC_OUTPUT
diff --git a/dbus-gmain/.gitignore b/dbus-gmain/.gitignore
new file mode 100644
index 0000000..ba527ff
--- /dev/null
+++ b/dbus-gmain/.gitignore
@@ -0,0 +1,19 @@
+*.a
+*.gcda
+*.gcno
+*.lineno
+*.lo
+*.o
+*~
+.deps/
+.libs/
+/Makefile
+/Makefile.in
+/libdbus-gmain.la
+/test-suite.log
+/tests/*.log
+/tests/*.trs
+/tests/libtest.la
+/tests/test-30574
+/tests/test-thread-client
+/tests/test-thread-server
diff --git a/dbus-gmain/AUTHORS b/dbus-gmain/AUTHORS
new file mode 100644
index 0000000..83c7766
--- /dev/null
+++ b/dbus-gmain/AUTHORS
@@ -0,0 +1,15 @@
+Alexander Larsson
+Anders Carlsson
+Carlos Garnacho Parro
+Christian Dywan
+Colin Walters
+Havoc Pennington
+James Willcox
+Kristian Hogsberg
+Marc-Andre Lureau
+Mikael Hallendal
+Mike Gorse
+Richard Hult
+Ross Burton
+Steve Frécinaux
+Tobias Mueller
diff --git a/CONTRIBUTING.md b/dbus-gmain/CONTRIBUTING.md
index 5bbcee1..5bbcee1 100644
--- a/CONTRIBUTING.md
+++ b/dbus-gmain/CONTRIBUTING.md
diff --git a/dbus-gmain/COPYING b/dbus-gmain/COPYING
new file mode 100644
index 0000000..d89d965
--- /dev/null
+++ b/dbus-gmain/COPYING
@@ -0,0 +1,551 @@
+The D-Bus GLib main loop bindings are licensed to you under your choice
+of the Academic Free License version 2.1, or the GNU General Public
+License version 2. Both licenses are included here.
+
+In SPDX terms, this is:
+
+SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+
+------------------------------------------------------------------------
+
+The Academic Free License
+v. 2.1
+
+This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following notice immediately following the copyright notice for the Original Work:
+
+Licensed under the Academic Free License version 2.1
+
+1) Grant of Copyright License. Licensor hereby grants You a
+world-wide, royalty-free, non-exclusive, perpetual, sublicenseable
+license to do the following:
+
+a) to reproduce the Original Work in copies;
+
+b) to prepare derivative works ("Derivative Works") based upon the Original Work;
+
+c) to distribute copies of the Original Work and Derivative Works to the public;
+
+d) to perform the Original Work publicly; and
+
+e) to display the Original Work publicly.
+
+2) Grant of Patent License. Licensor hereby grants You a world-wide,
+royalty-free, non-exclusive, perpetual, sublicenseable license, under
+patent claims owned or controlled by the Licensor that are embodied in
+the Original Work as furnished by the Licensor, to make, use, sell and
+offer for sale the Original Work and Derivative Works.
+
+3) Grant of Source Code License. The term "Source Code" means the
+preferred form of the Original Work for making modifications to it and
+all available documentation describing how to modify the Original
+Work. Licensor hereby agrees to provide a machine-readable copy of the
+Source Code of the Original Work along with each copy of the Original
+Work that Licensor distributes. Licensor reserves the right to satisfy
+this obligation by placing a machine-readable copy of the Source Code
+in an information repository reasonably calculated to permit
+inexpensive and convenient access by You for as long as Licensor
+continues to distribute the Original Work, and by publishing the
+address of that information repository in a notice immediately
+following the copyright notice that applies to the Original Work.
+
+4) Exclusions From License Grant. Neither the names of Licensor, nor
+the names of any contributors to the Original Work, nor any of their
+trademarks or service marks, may be used to endorse or promote
+products derived from this Original Work without express prior written
+permission of the Licensor. Nothing in this License shall be deemed to
+grant any rights to trademarks, copyrights, patents, trade secrets or
+any other intellectual property of Licensor except as expressly stated
+herein. No patent license is granted to make, use, sell or offer to
+sell embodiments of any patent claims other than the licensed claims
+defined in Section 2. No right is granted to the trademarks of
+Licensor even if such marks are included in the Original Work. Nothing
+in this License shall be interpreted to prohibit Licensor from
+licensing under different terms from this License any Original Work
+that Licensor otherwise would have a right to license.
+
+5) This section intentionally omitted.
+
+6) Attribution Rights. You must retain, in the Source Code of any
+Derivative Works that You create, all copyright, patent or trademark
+notices from the Source Code of the Original Work, as well as any
+notices of licensing and any descriptive text identified therein as an
+"Attribution Notice." You must cause the Source Code for any
+Derivative Works that You create to carry a prominent Attribution
+Notice reasonably calculated to inform recipients that You have
+modified the Original Work.
+
+7) Warranty of Provenance and Disclaimer of Warranty. Licensor
+warrants that the copyright in and to the Original Work and the patent
+rights granted herein by Licensor are owned by the Licensor or are
+sublicensed to You under the terms of this License with the permission
+of the contributor(s) of those copyrights and patent rights. Except as
+expressly stated in the immediately proceeding sentence, the Original
+Work is provided under this License on an "AS IS" BASIS and WITHOUT
+WARRANTY, either express or implied, including, without limitation,
+the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL
+WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential
+part of this License. No license to Original Work is granted hereunder
+except under this disclaimer.
+
+8) Limitation of Liability. Under no circumstances and under no legal
+theory, whether in tort (including negligence), contract, or
+otherwise, shall the Licensor be liable to any person for any direct,
+indirect, special, incidental, or consequential damages of any
+character arising as a result of this License or the use of the
+Original Work including, without limitation, damages for loss of
+goodwill, work stoppage, computer failure or malfunction, or any and
+all other commercial damages or losses. This limitation of liability
+shall not apply to liability for death or personal injury resulting
+from Licensor's negligence to the extent applicable law prohibits such
+limitation. Some jurisdictions do not allow the exclusion or
+limitation of incidental or consequential damages, so this exclusion
+and limitation may not apply to You.
+
+9) Acceptance and Termination. If You distribute copies of the
+Original Work or a Derivative Work, You must make a reasonable effort
+under the circumstances to obtain the express assent of recipients to
+the terms of this License. Nothing else but this License (or another
+written agreement between Licensor and You) grants You permission to
+create Derivative Works based upon the Original Work or to exercise
+any of the rights granted in Section 1 herein, and any attempt to do
+so except under the terms of this License (or another written
+agreement between Licensor and You) is expressly prohibited by
+U.S. copyright law, the equivalent laws of other countries, and by
+international treaty. Therefore, by exercising any of the rights
+granted to You in Section 1 herein, You indicate Your acceptance of
+this License and all of its terms and conditions.
+
+10) Termination for Patent Action. This License shall terminate
+automatically and You may no longer exercise any of the rights granted
+to You by this License as of the date You commence an action,
+including a cross-claim or counterclaim, against Licensor or any
+licensee alleging that the Original Work infringes a patent. This
+termination provision shall not apply for an action alleging patent
+infringement by combinations of the Original Work with other software
+or hardware.
+
+11) Jurisdiction, Venue and Governing Law. Any action or suit relating
+to this License may be brought only in the courts of a jurisdiction
+wherein the Licensor resides or in which Licensor conducts its primary
+business, and under the laws of that jurisdiction excluding its
+conflict-of-law provisions. The application of the United Nations
+Convention on Contracts for the International Sale of Goods is
+expressly excluded. Any use of the Original Work outside the scope of
+this License or after its termination shall be subject to the
+requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101
+et seq., the equivalent laws of other countries, and international
+treaty. This section shall survive the termination of this License.
+
+12) Attorneys Fees. In any action to enforce the terms of this License
+or seeking damages relating thereto, the prevailing party shall be
+entitled to recover its costs and expenses, including, without
+limitation, reasonable attorneys' fees and costs incurred in
+connection with such action, including any appeal of such action. This
+section shall survive the termination of this License.
+
+13) Miscellaneous. This License represents the complete agreement
+concerning the subject matter hereof. If any provision of this License
+is held to be unenforceable, such provision shall be reformed only to
+the extent necessary to make it enforceable.
+
+14) Definition of "You" in This License. "You" throughout this
+License, whether in upper or lower case, means an individual or a
+legal entity exercising rights under, and complying with all of the
+terms of, this License. For legal entities, "You" includes any entity
+that controls, is controlled by, or is under common control with
+you. For purposes of this definition, "control" means (i) the power,
+direct or indirect, to cause the direction or management of such
+entity, whether by contract or otherwise, or (ii) ownership of fifty
+percent (50%) or more of the outstanding shares, or (iii) beneficial
+ownership of such entity.
+
+15) Right to Use. You may use the Original Work in all ways not
+otherwise restricted or conditioned by this License or by law, and
+Licensor promises not to interfere with or be responsible for such
+uses by You.
+
+This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights
+reserved. Permission is hereby granted to copy and distribute this
+license without modification. This license may not be modified without
+the express written permission of its copyright owner.
+
+
+--
+END OF ACADEMIC FREE LICENSE. The following is intended to describe the essential
+differences between the Academic Free License (AFL) version 1.0 and other
+open source licenses:
+
+The Academic Free License is similar to the BSD, MIT, UoI/NCSA and Apache
+licenses in many respects but it is intended to solve a few problems with
+those licenses.
+
+* The AFL is written so as to make it clear what software is being
+licensed (by the inclusion of a statement following the copyright notice
+in the software). This way, the license functions better than a template
+license. The BSD, MIT and UoI/NCSA licenses apply to unidentified software.
+
+* The AFL contains a complete copyright grant to the software. The BSD
+and Apache licenses are vague and incomplete in that respect.
+
+* The AFL contains a complete patent grant to the software. The BSD, MIT,
+UoI/NCSA and Apache licenses rely on an implied patent license and contain
+no explicit patent grant.
+
+* The AFL makes it clear that no trademark rights are granted to the
+licensor's trademarks. The Apache license contains such a provision, but the
+BSD, MIT and UoI/NCSA licenses do not.
+
+* The AFL includes the warranty by the licensor that it either owns the
+copyright or that it is distributing the software under a license. None of
+the other licenses contain that warranty. All other warranties are disclaimed,
+as is the case for the other licenses.
+
+* The AFL is itself copyrighted (with the right granted to copy and distribute
+without modification). This ensures that the owner of the copyright to the
+license will control changes. The Apache license contains a copyright notice,
+but the BSD, MIT and UoI/NCSA licenses do not.
+
+------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/dbus-gmain/Makefile.am b/dbus-gmain/Makefile.am
new file mode 100644
index 0000000..63a1a2a
--- /dev/null
+++ b/dbus-gmain/Makefile.am
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
+
+EXTRA_DIST = \
+ AUTHORS \
+ CONTRIBUTING.md \
+ COPYING \
+ README.md \
+ $(NULL)
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ $(DBUS_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(NULL)
+
+noinst_LTLIBRARIES = \
+ libdbus-gmain.la \
+ tests/libtest.la \
+ $(NULL)
+
+libdbus_gmain_la_SOURCES = \
+ dbus-gmain.c \
+ dbus-gmain.h \
+ $(NULL)
+
+libdbus_gmain_la_LIBADD = $(DBUS_LIBS) $(GLIB_LIBS)
+libdbus_gmain_la_LDFLAGS = -no-undefined
+
+tests_libtest_la_SOURCES = \
+ tests/util.c \
+ tests/util.h \
+ $(NULL)
+
+tests_libtest_la_LIBADD = $(DBUS_LIBS) $(GLIB_LIBS)
+tests_libtest_la_LDFLAGS = -no-undefined
+
+TESTS = \
+ tests/test-30574 \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ tests/test-30574 \
+ tests/test-thread-server \
+ tests/test-thread-client \
+ $(NULL)
+
+tests_test_thread_server_SOURCES = \
+ tests/test-thread-server.c \
+ tests/test-thread.h \
+ $(NULL)
+tests_test_thread_server_LDADD = \
+ libdbus-gmain.la \
+ tests/libtest.la \
+ $(GLIB_THREADS_LIBS) \
+ $(GLIB_LIBS) \
+ $(DBUS_LIBS) \
+ $(NULL)
+
+tests_test_thread_client_SOURCES = \
+ tests/test-thread-client.c \
+ tests/test-thread.h \
+ $(NULL)
+tests_test_thread_client_LDADD = \
+ libdbus-gmain.la \
+ tests/libtest.la \
+ $(GLIB_THREADS_LIBS) \
+ $(GLIB_LIBS) \
+ $(DBUS_LIBS) \
+ $(NULL)
+
+tests_test_30574_SOURCES = \
+ tests/30574.c \
+ $(NULL)
+tests_test_30574_LDADD = \
+ libdbus-gmain.la \
+ tests/libtest.la \
+ $(GLIB_LIBS) \
+ $(DBUS_LIBS) \
+ $(NULL)
+
+LOG_COMPILER = $(DBUS_RUN_SESSION) --
diff --git a/README.md b/dbus-gmain/README.md
index 8f40161..8f40161 100644
--- a/README.md
+++ b/dbus-gmain/README.md
diff --git a/dbus-gmain.c b/dbus-gmain/dbus-gmain.c
index 06a480c..06a480c 100644
--- a/dbus-gmain.c
+++ b/dbus-gmain/dbus-gmain.c
diff --git a/dbus-gmain.h b/dbus-gmain/dbus-gmain.h
index b8df034..b8df034 100644
--- a/dbus-gmain.h
+++ b/dbus-gmain/dbus-gmain.h
diff --git a/tests/30574.c b/dbus-gmain/tests/30574.c
index bcc1276..bcc1276 100644
--- a/tests/30574.c
+++ b/dbus-gmain/tests/30574.c
diff --git a/tests/test-thread-client.c b/dbus-gmain/tests/test-thread-client.c
index a115d41..a115d41 100644
--- a/tests/test-thread-client.c
+++ b/dbus-gmain/tests/test-thread-client.c
diff --git a/tests/test-thread-server.c b/dbus-gmain/tests/test-thread-server.c
index 54f43b6..54f43b6 100644
--- a/tests/test-thread-server.c
+++ b/dbus-gmain/tests/test-thread-server.c
diff --git a/tests/test-thread.h b/dbus-gmain/tests/test-thread.h
index 8c78fba..8c78fba 100644
--- a/tests/test-thread.h
+++ b/dbus-gmain/tests/test-thread.h
diff --git a/tests/util.c b/dbus-gmain/tests/util.c
index 07da5d9..07da5d9 100644
--- a/tests/util.c
+++ b/dbus-gmain/tests/util.c
diff --git a/tests/util.h b/dbus-gmain/tests/util.h
index 0d8bfa3..0d8bfa3 100644
--- a/tests/util.h
+++ b/dbus-gmain/tests/util.h
diff --git a/dbus-python.pc.in b/dbus-python.pc.in
new file mode 100644
index 0000000..477288c
--- /dev/null
+++ b/dbus-python.pc.in
@@ -0,0 +1,12 @@
+# This file is currently Python-version-independent.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+datarootdir=@datarootdir@
+datadir=@datadir@
+
+Name: dbus-python
+Description: Python bindings for D-Bus
+Requires: dbus-1 >= 1.0
+Version: @VERSION@
+Cflags: -I${includedir}
diff --git a/dbus/__init__.py b/dbus/__init__.py
new file mode 100644
index 0000000..a2aeac9
--- /dev/null
+++ b/dbus/__init__.py
@@ -0,0 +1,96 @@
+"""\
+Implements the public API for a D-Bus client. See the dbus.service module
+to export objects or claim well-known names.
+"""
+
+# Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2003 David Zeuthen
+# Copyright (C) 2004 Rob Taylor
+# Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = [
+ # from _dbus
+ 'Bus', 'SystemBus', 'SessionBus', 'StarterBus',
+
+ # from proxies
+ 'Interface',
+
+ # from _dbus_bindings
+ 'get_default_main_loop', 'set_default_main_loop',
+
+ 'validate_interface_name', 'validate_member_name',
+ 'validate_bus_name', 'validate_object_path',
+ 'validate_error_name',
+
+ 'BUS_DAEMON_NAME', 'BUS_DAEMON_PATH', 'BUS_DAEMON_IFACE',
+ 'LOCAL_PATH', 'LOCAL_IFACE', 'PEER_IFACE',
+ 'INTROSPECTABLE_IFACE', 'PROPERTIES_IFACE',
+
+ 'ObjectPath', 'ByteArray', 'Signature', 'Byte', 'Boolean',
+ 'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64',
+ 'Double', 'String', 'Array', 'Struct', 'Dictionary',
+
+ # from exceptions
+ 'DBusException',
+ 'MissingErrorHandlerException', 'MissingReplyHandlerException',
+ 'ValidationException', 'IntrospectionParserException',
+ 'UnknownMethodException', 'NameExistsException',
+
+ # submodules
+ 'service', 'mainloop', 'lowlevel'
+ ]
+
+from dbus._compat import is_py2
+if is_py2:
+ __all__.append('UTF8String')
+
+__docformat__ = 'restructuredtext'
+
+# OLPC Sugar compatibility
+import dbus.exceptions as exceptions
+import dbus.types as types
+
+from _dbus_bindings import __version__
+version = tuple(map(int, __version__.split('.')))
+
+from _dbus_bindings import (
+ get_default_main_loop, set_default_main_loop, validate_bus_name,
+ validate_error_name, validate_interface_name, validate_member_name,
+ validate_object_path)
+from _dbus_bindings import (
+ BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, INTROSPECTABLE_IFACE,
+ LOCAL_IFACE, LOCAL_PATH, PEER_IFACE, PROPERTIES_IFACE)
+
+from dbus.exceptions import (
+ DBusException, IntrospectionParserException, MissingErrorHandlerException,
+ MissingReplyHandlerException, NameExistsException, UnknownMethodException,
+ ValidationException)
+from _dbus_bindings import (
+ Array, Boolean, Byte, ByteArray, Dictionary, Double, Int16, Int32, Int64,
+ ObjectPath, Signature, String, Struct, UInt16, UInt32, UInt64)
+
+if is_py2:
+ from _dbus_bindings import UTF8String
+
+from dbus._dbus import Bus, SystemBus, SessionBus, StarterBus
+from dbus.proxies import Interface
diff --git a/dbus/_compat.py b/dbus/_compat.py
new file mode 100644
index 0000000..62bbe28
--- /dev/null
+++ b/dbus/_compat.py
@@ -0,0 +1,6 @@
+# Python 2 / Python 3 compatibility helpers.
+
+import sys
+
+is_py3 = (sys.version_info.major == 3)
+is_py2 = not is_py3
diff --git a/dbus/_dbus.py b/dbus/_dbus.py
new file mode 100644
index 0000000..5a497f4
--- /dev/null
+++ b/dbus/_dbus.py
@@ -0,0 +1,230 @@
+"""Implementation for dbus.Bus. Not to be imported directly."""
+
+# Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2003 David Zeuthen
+# Copyright (C) 2004 Rob Taylor
+# Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import generators
+
+__all__ = ('Bus', 'SystemBus', 'SessionBus', 'StarterBus')
+__docformat__ = 'reStructuredText'
+
+from dbus.exceptions import DBusException
+from _dbus_bindings import (
+ BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_SESSION,
+ BUS_STARTER, BUS_SYSTEM, DBUS_START_REPLY_ALREADY_RUNNING,
+ DBUS_START_REPLY_SUCCESS, validate_bus_name,
+ validate_interface_name, validate_member_name, validate_object_path)
+from dbus.bus import BusConnection
+from dbus.lowlevel import SignalMessage
+from dbus._compat import is_py2
+
+if is_py2:
+ from _dbus_bindings import UTF8String
+
+
+class Bus(BusConnection):
+ """A connection to one of three possible standard buses, the SESSION,
+ SYSTEM, or STARTER bus. This class manages shared connections to those
+ buses.
+
+ If you're trying to subclass `Bus`, you may be better off subclassing
+ `BusConnection`, which doesn't have all this magic.
+ """
+
+ _shared_instances = {}
+
+ def __new__(cls, bus_type=BusConnection.TYPE_SESSION, private=False,
+ mainloop=None):
+ """Constructor, returning an existing instance where appropriate.
+
+ The returned instance is actually always an instance of `SessionBus`,
+ `SystemBus` or `StarterBus`.
+
+ :Parameters:
+ `bus_type` : cls.TYPE_SESSION, cls.TYPE_SYSTEM or cls.TYPE_STARTER
+ Connect to the appropriate bus
+ `private` : bool
+ If true, never return an existing shared instance, but instead
+ return a private connection.
+
+ :Deprecated: since 0.82.3. Use dbus.bus.BusConnection for
+ private connections.
+
+ `mainloop` : dbus.mainloop.NativeMainLoop
+ The main loop to use. The default is to use the default
+ main loop if one has been set up, or raise an exception
+ if none has been.
+ :Changed: in dbus-python 0.80:
+ converted from a wrapper around a Connection to a Connection
+ subclass.
+ """
+ if (not private and bus_type in cls._shared_instances):
+ return cls._shared_instances[bus_type]
+
+ # this is a bit odd, but we create instances of the subtypes
+ # so we can return the shared instances if someone tries to
+ # construct one of them (otherwise we'd eg try and return an
+ # instance of Bus from __new__ in SessionBus). why are there
+ # three ways to construct this class? we just don't know.
+ if bus_type == BUS_SESSION:
+ subclass = SessionBus
+ elif bus_type == BUS_SYSTEM:
+ subclass = SystemBus
+ elif bus_type == BUS_STARTER:
+ subclass = StarterBus
+ else:
+ raise ValueError('invalid bus_type %s' % bus_type)
+
+ bus = BusConnection.__new__(subclass, bus_type, mainloop=mainloop)
+
+ bus._bus_type = bus_type
+
+ if not private:
+ cls._shared_instances[bus_type] = bus
+
+ return bus
+
+ def close(self):
+ t = self._bus_type
+ if self.__class__._shared_instances.get(t) is self:
+ del self.__class__._shared_instances[t]
+ super(Bus, self).close()
+
+ def get_connection(self):
+ """Return self, for backwards compatibility with earlier dbus-python
+ versions where Bus was not a subclass of Connection.
+
+ :Deprecated: since 0.80.0
+ """
+ return self
+ _connection = property(get_connection, None, None,
+ """self._connection == self, for backwards
+ compatibility with earlier dbus-python versions
+ where Bus was not a subclass of Connection.""")
+
+ def get_session(private=False):
+ """Static method that returns a connection to the session bus.
+
+ :Parameters:
+ `private` : bool
+ If true, do not return a shared connection.
+ """
+ return SessionBus(private=private)
+
+ get_session = staticmethod(get_session)
+
+ def get_system(private=False):
+ """Static method that returns a connection to the system bus.
+
+ :Parameters:
+ `private` : bool
+ If true, do not return a shared connection.
+ """
+ return SystemBus(private=private)
+
+ get_system = staticmethod(get_system)
+
+
+ def get_starter(private=False):
+ """Static method that returns a connection to the starter bus.
+
+ :Parameters:
+ `private` : bool
+ If true, do not return a shared connection.
+ """
+ return StarterBus(private=private)
+
+ get_starter = staticmethod(get_starter)
+
+ def __repr__(self):
+ if self._bus_type == BUS_SESSION:
+ name = 'session'
+ elif self._bus_type == BUS_SYSTEM:
+ name = 'system'
+ elif self._bus_type == BUS_STARTER:
+ name = 'starter'
+ else:
+ name = 'unknown bus type'
+
+ return '<%s.%s (%s) at %#x>' % (self.__class__.__module__,
+ self.__class__.__name__,
+ name, id(self))
+ __str__ = __repr__
+
+
+# FIXME: Drop the subclasses here? I can't think why we'd ever want
+# polymorphism
+class SystemBus(Bus):
+ """The system-wide message bus."""
+ def __new__(cls, private=False, mainloop=None):
+ """Return a connection to the system bus.
+
+ :Parameters:
+ `private` : bool
+ If true, never return an existing shared instance, but instead
+ return a private connection.
+ `mainloop` : dbus.mainloop.NativeMainLoop
+ The main loop to use. The default is to use the default
+ main loop if one has been set up, or raise an exception
+ if none has been.
+ """
+ return Bus.__new__(cls, Bus.TYPE_SYSTEM, mainloop=mainloop,
+ private=private)
+
+class SessionBus(Bus):
+ """The session (current login) message bus."""
+ def __new__(cls, private=False, mainloop=None):
+ """Return a connection to the session bus.
+
+ :Parameters:
+ `private` : bool
+ If true, never return an existing shared instance, but instead
+ return a private connection.
+ `mainloop` : dbus.mainloop.NativeMainLoop
+ The main loop to use. The default is to use the default
+ main loop if one has been set up, or raise an exception
+ if none has been.
+ """
+ return Bus.__new__(cls, Bus.TYPE_SESSION, private=private,
+ mainloop=mainloop)
+
+class StarterBus(Bus):
+ """The bus that activated this process (only valid if
+ this process was launched by DBus activation).
+ """
+ def __new__(cls, private=False, mainloop=None):
+ """Return a connection to the bus that activated this process.
+
+ :Parameters:
+ `private` : bool
+ If true, never return an existing shared instance, but instead
+ return a private connection.
+ `mainloop` : dbus.mainloop.NativeMainLoop
+ The main loop to use. The default is to use the default
+ main loop if one has been set up, or raise an exception
+ if none has been.
+ """
+ return Bus.__new__(cls, Bus.TYPE_STARTER, private=private,
+ mainloop=mainloop)
diff --git a/dbus/_expat_introspect_parser.py b/dbus/_expat_introspect_parser.py
new file mode 100644
index 0000000..1cf8a6c
--- /dev/null
+++ b/dbus/_expat_introspect_parser.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2003 David Zeuthen
+# Copyright (C) 2004 Rob Taylor
+# Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from xml.parsers.expat import ParserCreate
+from dbus.exceptions import IntrospectionParserException
+
+class _Parser(object):
+ __slots__ = ('map', 'in_iface', 'in_method', 'sig')
+ def __init__(self):
+ self.map = {}
+ self.in_iface = ''
+ self.in_method = ''
+ self.sig = ''
+
+ def parse(self, data):
+ parser = ParserCreate('UTF-8', ' ')
+ parser.buffer_text = True
+ parser.StartElementHandler = self.StartElementHandler
+ parser.EndElementHandler = self.EndElementHandler
+ parser.Parse(data)
+ return self.map
+
+ def StartElementHandler(self, name, attributes):
+ if not self.in_iface:
+ if (not self.in_method and name == 'interface'):
+ self.in_iface = attributes['name']
+ else:
+ if (not self.in_method and name == 'method'):
+ self.in_method = attributes['name']
+ elif (self.in_method and name == 'arg'):
+ if attributes.get('direction', 'in') == 'in':
+ self.sig += attributes['type']
+
+ def EndElementHandler(self, name):
+ if self.in_iface:
+ if (not self.in_method and name == 'interface'):
+ self.in_iface = ''
+ elif (self.in_method and name == 'method'):
+ self.map[self.in_iface + '.' + self.in_method] = self.sig
+ self.in_method = ''
+ self.sig = ''
+
+def process_introspection_data(data):
+ """Return a dict mapping ``interface.method`` strings to the
+ concatenation of all their 'in' parameters, and mapping
+ ``interface.signal`` strings to the concatenation of all their
+ parameters.
+
+ Example output::
+
+ {
+ 'com.example.SignalEmitter.OneString': 's',
+ 'com.example.MethodImplementor.OneInt32Argument': 'i',
+ }
+
+ :Parameters:
+ `data` : str
+ The introspection XML. Must be an 8-bit string of UTF-8.
+ """
+ try:
+ return _Parser().parse(data)
+ except Exception as e:
+ raise IntrospectionParserException('%s: %s' % (e.__class__, e))
diff --git a/dbus/bus.py b/dbus/bus.py
new file mode 100644
index 0000000..109f4c6
--- /dev/null
+++ b/dbus/bus.py
@@ -0,0 +1,445 @@
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ('BusConnection',)
+__docformat__ = 'reStructuredText'
+
+import logging
+import weakref
+
+from _dbus_bindings import (
+ BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_SESSION,
+ BUS_STARTER, BUS_SYSTEM, DBUS_START_REPLY_ALREADY_RUNNING,
+ DBUS_START_REPLY_SUCCESS, NAME_FLAG_ALLOW_REPLACEMENT,
+ NAME_FLAG_DO_NOT_QUEUE, NAME_FLAG_REPLACE_EXISTING,
+ RELEASE_NAME_REPLY_NON_EXISTENT, RELEASE_NAME_REPLY_NOT_OWNER,
+ RELEASE_NAME_REPLY_RELEASED, REQUEST_NAME_REPLY_ALREADY_OWNER,
+ REQUEST_NAME_REPLY_EXISTS, REQUEST_NAME_REPLY_IN_QUEUE,
+ REQUEST_NAME_REPLY_PRIMARY_OWNER, validate_bus_name, validate_error_name,
+ validate_interface_name, validate_member_name, validate_object_path)
+from dbus.connection import Connection
+from dbus.exceptions import DBusException
+from dbus.lowlevel import HANDLER_RESULT_NOT_YET_HANDLED
+from dbus._compat import is_py2
+
+
+_NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s',"
+ "interface='%s',member='NameOwnerChanged',"
+ "path='%s',arg0='%%s'"
+ % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE,
+ BUS_DAEMON_PATH))
+"""(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
+messages"""
+
+_NAME_HAS_NO_OWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner'
+
+_logger = logging.getLogger('dbus.bus')
+
+
+class NameOwnerWatch(object):
+ __slots__ = ('_match', '_pending_call')
+
+ def __init__(self, bus_conn, bus_name, callback):
+ validate_bus_name(bus_name)
+
+ def signal_cb(owned, old_owner, new_owner):
+ callback(new_owner)
+
+ def error_cb(e):
+ if e.get_dbus_name() == _NAME_HAS_NO_OWNER:
+ callback('')
+ else:
+ logging.basicConfig()
+ _logger.debug('GetNameOwner(%s) failed:', bus_name,
+ exc_info=(e.__class__, e, None))
+
+ self._match = bus_conn.add_signal_receiver(signal_cb,
+ 'NameOwnerChanged',
+ BUS_DAEMON_IFACE,
+ BUS_DAEMON_NAME,
+ BUS_DAEMON_PATH,
+ arg0=bus_name)
+ keywords = {}
+ if is_py2:
+ keywords['utf8_strings'] = True
+ self._pending_call = bus_conn.call_async(BUS_DAEMON_NAME,
+ BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE,
+ 'GetNameOwner',
+ 's', (bus_name,),
+ callback, error_cb,
+ **keywords)
+
+ def cancel(self):
+ if self._match is not None:
+ self._match.remove()
+ if self._pending_call is not None:
+ self._pending_call.cancel()
+ self._match = None
+ self._pending_call = None
+
+
+class BusConnection(Connection):
+ """A connection to a D-Bus daemon that implements the
+ ``org.freedesktop.DBus`` pseudo-service.
+
+ :Since: 0.81.0
+ """
+
+ TYPE_SESSION = BUS_SESSION
+ """Represents a session bus (same as the global dbus.BUS_SESSION)"""
+
+ TYPE_SYSTEM = BUS_SYSTEM
+ """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
+
+ TYPE_STARTER = BUS_STARTER
+ """Represents the bus that started this service by activation (same as
+ the global dbus.BUS_STARTER)"""
+
+ START_REPLY_SUCCESS = DBUS_START_REPLY_SUCCESS
+ START_REPLY_ALREADY_RUNNING = DBUS_START_REPLY_ALREADY_RUNNING
+
+ def __new__(cls, address_or_type=TYPE_SESSION, mainloop=None):
+ bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
+
+ # _bus_names is used by dbus.service.BusName!
+ bus._bus_names = weakref.WeakValueDictionary()
+
+ bus._signal_sender_matches = {}
+ """Map from SignalMatch to NameOwnerWatch."""
+
+ return bus
+
+ def add_signal_receiver(self, handler_function, signal_name=None,
+ dbus_interface=None, bus_name=None,
+ path=None, **keywords):
+ named_service = keywords.pop('named_service', None)
+ if named_service is not None:
+ if bus_name is not None:
+ raise TypeError('bus_name and named_service cannot both be '
+ 'specified')
+ bus_name = named_service
+ from warnings import warn
+ warn('Passing the named_service parameter to add_signal_receiver '
+ 'by name is deprecated: please use positional parameters',
+ DeprecationWarning, stacklevel=2)
+
+ match = super(BusConnection, self).add_signal_receiver(
+ handler_function, signal_name, dbus_interface, bus_name,
+ path, **keywords)
+
+ if (bus_name is not None and bus_name != BUS_DAEMON_NAME):
+ if bus_name[:1] == ':':
+ def callback(new_owner):
+ if new_owner == '':
+ match.remove()
+ else:
+ callback = match.set_sender_name_owner
+ watch = self.watch_name_owner(bus_name, callback)
+ self._signal_sender_matches[match] = watch
+
+ self.add_match_string(str(match))
+
+ return match
+
+ def _clean_up_signal_match(self, match):
+ # The signals lock is no longer held here (it was in <= 0.81.0)
+ self.remove_match_string_non_blocking(str(match))
+ watch = self._signal_sender_matches.pop(match, None)
+ if watch is not None:
+ watch.cancel()
+
+ def activate_name_owner(self, bus_name):
+ if (bus_name is not None and bus_name[:1] != ':'
+ and bus_name != BUS_DAEMON_NAME):
+ try:
+ return self.get_name_owner(bus_name)
+ except DBusException as e:
+ if e.get_dbus_name() != _NAME_HAS_NO_OWNER:
+ raise
+ # else it doesn't exist: try to start it
+ self.start_service_by_name(bus_name)
+ return self.get_name_owner(bus_name)
+ else:
+ # already unique
+ return bus_name
+
+ def get_object(self, bus_name, object_path, introspect=True,
+ follow_name_owner_changes=False, **kwargs):
+ """Return a local proxy for the given remote object.
+
+ Method calls on the proxy are translated into method calls on the
+ remote object.
+
+ :Parameters:
+ `bus_name` : str
+ A bus name (either the unique name or a well-known name)
+ of the application owning the object. The keyword argument
+ named_service is a deprecated alias for this.
+ `object_path` : str
+ The object path of the desired object
+ `introspect` : bool
+ If true (default), attempt to introspect the remote
+ object to find out supported methods and their signatures
+ `follow_name_owner_changes` : bool
+ If the object path is a well-known name and this parameter
+ is false (default), resolve the well-known name to the unique
+ name of its current owner and bind to that instead; if the
+ ownership of the well-known name changes in future,
+ keep communicating with the original owner.
+ This is necessary if the D-Bus API used is stateful.
+
+ If the object path is a well-known name and this parameter
+ is true, whenever the well-known name changes ownership in
+ future, bind to the new owner, if any.
+
+ If the given object path is a unique name, this parameter
+ has no effect.
+
+ :Returns: a `dbus.proxies.ProxyObject`
+ :Raises `DBusException`: if resolving the well-known name to a
+ unique name fails
+ """
+ if follow_name_owner_changes:
+ self._require_main_loop() # we don't get the signals otherwise
+
+ named_service = kwargs.pop('named_service', None)
+ if named_service is not None:
+ if bus_name is not None:
+ raise TypeError('bus_name and named_service cannot both '
+ 'be specified')
+ from warnings import warn
+ warn('Passing the named_service parameter to get_object by name '
+ 'is deprecated: please use positional parameters',
+ DeprecationWarning, stacklevel=2)
+ bus_name = named_service
+ if kwargs:
+ raise TypeError('get_object does not take these keyword '
+ 'arguments: %s' % ', '.join(kwargs.keys()))
+
+ return self.ProxyObjectClass(self, bus_name, object_path,
+ introspect=introspect,
+ follow_name_owner_changes=follow_name_owner_changes)
+
+ def get_unix_user(self, bus_name):
+ """Get the numeric uid of the process owning the given bus name.
+
+ :Parameters:
+ `bus_name` : str
+ A bus name, either unique or well-known
+ :Returns: a `dbus.UInt32`
+ :Since: 0.80.0
+ """
+ validate_bus_name(bus_name)
+ return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'GetConnectionUnixUser',
+ 's', (bus_name,))
+
+ def start_service_by_name(self, bus_name, flags=0):
+ """Start a service which will implement the given bus name on this Bus.
+
+ :Parameters:
+ `bus_name` : str
+ The well-known bus name to be activated.
+ `flags` : dbus.UInt32
+ Flags to pass to StartServiceByName (currently none are
+ defined)
+
+ :Returns: A tuple of 2 elements. The first is always True, the
+ second is either START_REPLY_SUCCESS or
+ START_REPLY_ALREADY_RUNNING.
+
+ :Raises `DBusException`: if the service could not be started.
+ :Since: 0.80.0
+ """
+ validate_bus_name(bus_name)
+ return (True, self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE,
+ 'StartServiceByName',
+ 'su', (bus_name, flags)))
+
+ # XXX: it might be nice to signal IN_QUEUE, EXISTS by exception,
+ # but this would not be backwards-compatible
+ def request_name(self, name, flags=0):
+ """Request a bus name.
+
+ :Parameters:
+ `name` : str
+ The well-known name to be requested
+ `flags` : dbus.UInt32
+ A bitwise-OR of 0 or more of the flags
+ `NAME_FLAG_ALLOW_REPLACEMENT`,
+ `NAME_FLAG_REPLACE_EXISTING`
+ and `NAME_FLAG_DO_NOT_QUEUE`
+ :Returns: `REQUEST_NAME_REPLY_PRIMARY_OWNER`,
+ `REQUEST_NAME_REPLY_IN_QUEUE`,
+ `REQUEST_NAME_REPLY_EXISTS` or
+ `REQUEST_NAME_REPLY_ALREADY_OWNER`
+ :Raises `DBusException`: if the bus daemon cannot be contacted or
+ returns an error.
+ """
+ validate_bus_name(name, allow_unique=False)
+ return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'RequestName',
+ 'su', (name, flags))
+
+ def release_name(self, name):
+ """Release a bus name.
+
+ :Parameters:
+ `name` : str
+ The well-known name to be released
+ :Returns: `RELEASE_NAME_REPLY_RELEASED`,
+ `RELEASE_NAME_REPLY_NON_EXISTENT`
+ or `RELEASE_NAME_REPLY_NOT_OWNER`
+ :Raises `DBusException`: if the bus daemon cannot be contacted or
+ returns an error.
+ """
+ validate_bus_name(name, allow_unique=False)
+ return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'ReleaseName',
+ 's', (name,))
+
+ def list_names(self):
+ """Return a list of all currently-owned names on the bus.
+
+ :Returns: a dbus.Array of dbus.UTF8String
+ :Since: 0.81.0
+ """
+ keywords = {}
+ if is_py2:
+ keywords['utf8_strings'] = True
+ return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'ListNames',
+ '', (), **keywords)
+
+ def list_activatable_names(self):
+ """Return a list of all names that can be activated on the bus.
+
+ :Returns: a dbus.Array of dbus.UTF8String
+ :Since: 0.81.0
+ """
+ keywords = {}
+ if is_py2:
+ keywords['utf8_strings'] = True
+ return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'ListActivatableNames',
+ '', (), **keywords)
+
+ def get_name_owner(self, bus_name):
+ """Return the unique connection name of the primary owner of the
+ given name.
+
+ :Raises `DBusException`: if the `bus_name` has no owner
+ :Since: 0.81.0
+ """
+ keywords = {}
+ if is_py2:
+ keywords['utf8_strings'] = True
+ validate_bus_name(bus_name, allow_unique=False)
+ return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'GetNameOwner',
+ 's', (bus_name,), **keywords)
+
+ def watch_name_owner(self, bus_name, callback):
+ """Watch the unique connection name of the primary owner of the
+ given name.
+
+ `callback` will be called with one argument, which is either the
+ unique connection name, or the empty string (meaning the name is
+ not owned).
+
+ :Since: 0.81.0
+ """
+ return NameOwnerWatch(self, bus_name, callback)
+
+ def name_has_owner(self, bus_name):
+ """Return True iff the given bus name has an owner on this bus.
+
+ :Parameters:
+ `bus_name` : str
+ The bus name to look up
+ :Returns: a `bool`
+ """
+ return bool(self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'NameHasOwner',
+ 's', (bus_name,)))
+
+ def add_match_string(self, rule):
+ """Arrange for this application to receive messages on the bus that
+ match the given rule. This version will block.
+
+ :Parameters:
+ `rule` : str
+ The match rule
+ :Raises `DBusException`: on error.
+ :Since: 0.80.0
+ """
+ self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,))
+
+ # FIXME: add an async success/error handler capability?
+ # (and the same for remove_...)
+ def add_match_string_non_blocking(self, rule):
+ """Arrange for this application to receive messages on the bus that
+ match the given rule. This version will not block, but any errors
+ will be ignored.
+
+
+ :Parameters:
+ `rule` : str
+ The match rule
+ :Raises `DBusException`: on error.
+ :Since: 0.80.0
+ """
+ self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,),
+ None, None)
+
+ def remove_match_string(self, rule):
+ """Arrange for this application to receive messages on the bus that
+ match the given rule. This version will block.
+
+ :Parameters:
+ `rule` : str
+ The match rule
+ :Raises `DBusException`: on error.
+ :Since: 0.80.0
+ """
+ self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,))
+
+ def remove_match_string_non_blocking(self, rule):
+ """Arrange for this application to receive messages on the bus that
+ match the given rule. This version will not block, but any errors
+ will be ignored.
+
+
+ :Parameters:
+ `rule` : str
+ The match rule
+ :Raises `DBusException`: on error.
+ :Since: 0.80.0
+ """
+ self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
+ BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,),
+ None, None)
diff --git a/dbus/connection.py b/dbus/connection.py
new file mode 100644
index 0000000..e2361ec
--- /dev/null
+++ b/dbus/connection.py
@@ -0,0 +1,667 @@
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ('Connection', 'SignalMatch')
+__docformat__ = 'reStructuredText'
+
+import logging
+import threading
+import weakref
+
+from _dbus_bindings import (
+ Connection as _Connection, LOCAL_IFACE, LOCAL_PATH, validate_bus_name,
+ validate_interface_name, validate_member_name, validate_object_path)
+from dbus.exceptions import DBusException
+from dbus.lowlevel import (
+ ErrorMessage, HANDLER_RESULT_NOT_YET_HANDLED, MethodCallMessage,
+ MethodReturnMessage, SignalMessage)
+from dbus.proxies import ProxyObject
+from dbus._compat import is_py2, is_py3
+
+if is_py3:
+ from _dbus_bindings import String
+else:
+ from _dbus_bindings import UTF8String
+
+
+_logger = logging.getLogger('dbus.connection')
+
+
+def _noop(*args, **kwargs):
+ pass
+
+
+class SignalMatch(object):
+ _slots = ['_sender_name_owner', '_member', '_interface', '_sender',
+ '_path', '_handler', '_args_match', '_rule',
+ '_byte_arrays', '_conn_weakref',
+ '_destination_keyword', '_interface_keyword',
+ '_message_keyword', '_member_keyword',
+ '_sender_keyword', '_path_keyword', '_int_args_match']
+ if is_py2:
+ _slots.append('_utf8_strings')
+
+ __slots__ = tuple(_slots)
+
+ def __init__(self, conn, sender, object_path, dbus_interface,
+ member, handler, byte_arrays=False,
+ sender_keyword=None, path_keyword=None,
+ interface_keyword=None, member_keyword=None,
+ message_keyword=None, destination_keyword=None,
+ **kwargs):
+ if member is not None:
+ validate_member_name(member)
+ if dbus_interface is not None:
+ validate_interface_name(dbus_interface)
+ if sender is not None:
+ validate_bus_name(sender)
+ if object_path is not None:
+ validate_object_path(object_path)
+
+ self._rule = None
+ self._conn_weakref = weakref.ref(conn)
+ self._sender = sender
+ self._interface = dbus_interface
+ self._member = member
+ self._path = object_path
+ self._handler = handler
+
+ # if the connection is actually a bus, it's responsible for changing
+ # this later
+ self._sender_name_owner = sender
+
+ if is_py2:
+ self._utf8_strings = kwargs.pop('utf8_strings', False)
+ elif 'utf8_strings' in kwargs:
+ raise TypeError("unexpected keyword argument 'utf8_strings'")
+
+ self._byte_arrays = byte_arrays
+ self._sender_keyword = sender_keyword
+ self._path_keyword = path_keyword
+ self._member_keyword = member_keyword
+ self._interface_keyword = interface_keyword
+ self._message_keyword = message_keyword
+ self._destination_keyword = destination_keyword
+
+ self._args_match = kwargs
+ if not kwargs:
+ self._int_args_match = None
+ else:
+ self._int_args_match = {}
+ for kwarg in kwargs:
+ if not kwarg.startswith('arg'):
+ raise TypeError('SignalMatch: unknown keyword argument %s'
+ % kwarg)
+ try:
+ index = int(kwarg[3:])
+ except ValueError:
+ raise TypeError('SignalMatch: unknown keyword argument %s'
+ % kwarg)
+ if index < 0 or index > 63:
+ raise TypeError('SignalMatch: arg match index must be in '
+ 'range(64), not %d' % index)
+ self._int_args_match[index] = kwargs[kwarg]
+
+ def __hash__(self):
+ """SignalMatch objects are compared by identity."""
+ return hash(id(self))
+
+ def __eq__(self, other):
+ """SignalMatch objects are compared by identity."""
+ return self is other
+
+ def __ne__(self, other):
+ """SignalMatch objects are compared by identity."""
+ return self is not other
+
+ sender = property(lambda self: self._sender)
+
+ def __str__(self):
+ if self._rule is None:
+ rule = ["type='signal'"]
+ if self._sender is not None:
+ rule.append("sender='%s'" % self._sender)
+ if self._path is not None:
+ rule.append("path='%s'" % self._path)
+ if self._interface is not None:
+ rule.append("interface='%s'" % self._interface)
+ if self._member is not None:
+ rule.append("member='%s'" % self._member)
+ if self._int_args_match is not None:
+ for index, value in self._int_args_match.items():
+ rule.append("arg%d='%s'" % (index, value))
+
+ self._rule = ','.join(rule)
+
+ return self._rule
+
+ def __repr__(self):
+ return ('<%s at %x "%s" on conn %r>'
+ % (self.__class__, id(self), self._rule, self._conn_weakref()))
+
+ def set_sender_name_owner(self, new_name):
+ self._sender_name_owner = new_name
+
+ def matches_removal_spec(self, sender, object_path,
+ dbus_interface, member, handler, **kwargs):
+ if handler not in (None, self._handler):
+ return False
+ if sender != self._sender:
+ return False
+ if object_path != self._path:
+ return False
+ if dbus_interface != self._interface:
+ return False
+ if member != self._member:
+ return False
+ if kwargs != self._args_match:
+ return False
+ return True
+
+ def maybe_handle_message(self, message):
+ args = None
+
+ # these haven't been checked yet by the match tree
+ if self._sender_name_owner not in (None, message.get_sender()):
+ return False
+ if self._int_args_match is not None:
+ # extracting args with utf8_strings and byte_arrays is less work
+ kwargs = dict(byte_arrays=True)
+ arg_type = (String if is_py3 else UTF8String)
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ args = message.get_args_list(**kwargs)
+ for index, value in self._int_args_match.items():
+ if (index >= len(args)
+ or not isinstance(args[index], arg_type)
+ or args[index] != value):
+ return False
+
+ # these have likely already been checked by the match tree
+ if self._member not in (None, message.get_member()):
+ return False
+ if self._interface not in (None, message.get_interface()):
+ return False
+ if self._path not in (None, message.get_path()):
+ return False
+
+ try:
+ # minor optimization: if we already extracted the args with the
+ # right calling convention to do the args match, don't bother
+ # doing so again
+ utf8_strings = (is_py2 and self._utf8_strings)
+ if args is None or not utf8_strings or not self._byte_arrays:
+ kwargs = dict(byte_arrays=self._byte_arrays)
+ if is_py2:
+ kwargs['utf8_strings'] = self._utf8_strings
+ args = message.get_args_list(**kwargs)
+ kwargs = {}
+ if self._sender_keyword is not None:
+ kwargs[self._sender_keyword] = message.get_sender()
+ if self._destination_keyword is not None:
+ kwargs[self._destination_keyword] = message.get_destination()
+ if self._path_keyword is not None:
+ kwargs[self._path_keyword] = message.get_path()
+ if self._member_keyword is not None:
+ kwargs[self._member_keyword] = message.get_member()
+ if self._interface_keyword is not None:
+ kwargs[self._interface_keyword] = message.get_interface()
+ if self._message_keyword is not None:
+ kwargs[self._message_keyword] = message
+ self._handler(*args, **kwargs)
+ except:
+ # basicConfig is a no-op if logging is already configured
+ logging.basicConfig()
+ _logger.error('Exception in handler for D-Bus signal:', exc_info=1)
+
+ return True
+
+ def remove(self):
+ conn = self._conn_weakref()
+ # do nothing if the connection has already vanished
+ if conn is not None:
+ conn.remove_signal_receiver(self, self._member,
+ self._interface, self._sender,
+ self._path,
+ **self._args_match)
+
+
+class Connection(_Connection):
+ """A connection to another application. In this base class there is
+ assumed to be no bus daemon.
+
+ :Since: 0.81.0
+ """
+
+ ProxyObjectClass = ProxyObject
+
+ def __init__(self, *args, **kwargs):
+ super(Connection, self).__init__(*args, **kwargs)
+
+ # this if-block is needed because shared bus connections can be
+ # __init__'ed more than once
+ if not hasattr(self, '_dbus_Connection_initialized'):
+ self._dbus_Connection_initialized = 1
+
+ self.__call_on_disconnection = []
+
+ self._signal_recipients_by_object_path = {}
+ """Map from object path to dict mapping dbus_interface to dict
+ mapping member to list of SignalMatch objects."""
+
+ self._signals_lock = threading.Lock()
+ """Lock used to protect signal data structures"""
+
+ self.add_message_filter(self.__class__._signal_func)
+
+ def activate_name_owner(self, bus_name):
+ """Return the unique name for the given bus name, activating it
+ if necessary and possible.
+
+ If the name is already unique or this connection is not to a
+ bus daemon, just return it.
+
+ :Returns: a bus name. If the given `bus_name` exists, the returned
+ name identifies its current owner; otherwise the returned name
+ does not exist.
+ :Raises DBusException: if the implementation has failed
+ to activate the given bus name.
+ :Since: 0.81.0
+ """
+ return bus_name
+
+ def get_object(self, bus_name=None, object_path=None, introspect=True,
+ **kwargs):
+ """Return a local proxy for the given remote object.
+
+ Method calls on the proxy are translated into method calls on the
+ remote object.
+
+ :Parameters:
+ `bus_name` : str
+ A bus name (either the unique name or a well-known name)
+ of the application owning the object. The keyword argument
+ named_service is a deprecated alias for this.
+ `object_path` : str
+ The object path of the desired object
+ `introspect` : bool
+ If true (default), attempt to introspect the remote
+ object to find out supported methods and their signatures
+
+ :Returns: a `dbus.proxies.ProxyObject`
+ """
+ named_service = kwargs.pop('named_service', None)
+ if named_service is not None:
+ if bus_name is not None:
+ raise TypeError('bus_name and named_service cannot both '
+ 'be specified')
+ from warnings import warn
+ warn('Passing the named_service parameter to get_object by name '
+ 'is deprecated: please use positional parameters',
+ DeprecationWarning, stacklevel=2)
+ bus_name = named_service
+ if kwargs:
+ raise TypeError('get_object does not take these keyword '
+ 'arguments: %s' % ', '.join(kwargs.keys()))
+
+ return self.ProxyObjectClass(self, bus_name, object_path,
+ introspect=introspect)
+
+ def add_signal_receiver(self, handler_function,
+ signal_name=None,
+ dbus_interface=None,
+ bus_name=None,
+ path=None,
+ **keywords):
+ """Arrange for the given function to be called when a signal matching
+ the parameters is received.
+
+ :Parameters:
+ `handler_function` : callable
+ The function to be called. Its positional arguments will
+ be the arguments of the signal. By default it will receive
+ no keyword arguments, but see the description of
+ the optional keyword arguments below.
+ `signal_name` : str
+ The signal name; None (the default) matches all names
+ `dbus_interface` : str
+ The D-Bus interface name with which to qualify the signal;
+ None (the default) matches all interface names
+ `bus_name` : str
+ A bus name for the sender, which will be resolved to a
+ unique name if it is not already; None (the default) matches
+ any sender.
+ `path` : str
+ The object path of the object which must have emitted the
+ signal; None (the default) matches any object path
+ :Keywords:
+ `utf8_strings` : bool
+ If True, the handler function will receive any string
+ arguments as dbus.UTF8String objects (a subclass of str
+ guaranteed to be UTF-8). If False (default) it will receive
+ any string arguments as dbus.String objects (a subclass of
+ unicode).
+ `byte_arrays` : bool
+ If True, the handler function will receive any byte-array
+ arguments as dbus.ByteArray objects (a subclass of str).
+ If False (default) it will receive any byte-array
+ arguments as a dbus.Array of dbus.Byte (subclasses of:
+ a list of ints).
+ `sender_keyword` : str
+ If not None (the default), the handler function will receive
+ the unique name of the sending endpoint as a keyword
+ argument with this name.
+ `destination_keyword` : str
+ If not None (the default), the handler function will receive
+ the bus name of the destination (or None if the signal is a
+ broadcast, as is usual) as a keyword argument with this name.
+ `interface_keyword` : str
+ If not None (the default), the handler function will receive
+ the signal interface as a keyword argument with this name.
+ `member_keyword` : str
+ If not None (the default), the handler function will receive
+ the signal name as a keyword argument with this name.
+ `path_keyword` : str
+ If not None (the default), the handler function will receive
+ the object-path of the sending object as a keyword argument
+ with this name.
+ `message_keyword` : str
+ If not None (the default), the handler function will receive
+ the `dbus.lowlevel.SignalMessage` as a keyword argument with
+ this name.
+ `arg...` : unicode or UTF-8 str
+ If there are additional keyword parameters of the form
+ ``arg``\ *n*, match only signals where the *n*\ th argument
+ is the value given for that keyword parameter. As of this
+ time only string arguments can be matched (in particular,
+ object paths and signatures can't).
+ `named_service` : str
+ A deprecated alias for `bus_name`.
+ """
+ self._require_main_loop()
+
+ named_service = keywords.pop('named_service', None)
+ if named_service is not None:
+ if bus_name is not None:
+ raise TypeError('bus_name and named_service cannot both be '
+ 'specified')
+ bus_name = named_service
+ from warnings import warn
+ warn('Passing the named_service parameter to add_signal_receiver '
+ 'by name is deprecated: please use positional parameters',
+ DeprecationWarning, stacklevel=2)
+
+ match = SignalMatch(self, bus_name, path, dbus_interface,
+ signal_name, handler_function, **keywords)
+
+ self._signals_lock.acquire()
+ try:
+ by_interface = self._signal_recipients_by_object_path.setdefault(
+ path, {})
+ by_member = by_interface.setdefault(dbus_interface, {})
+ matches = by_member.setdefault(signal_name, [])
+
+ matches.append(match)
+ finally:
+ self._signals_lock.release()
+
+ return match
+
+ def _iter_easy_matches(self, path, dbus_interface, member):
+ if path is not None:
+ path_keys = (None, path)
+ else:
+ path_keys = (None,)
+ if dbus_interface is not None:
+ interface_keys = (None, dbus_interface)
+ else:
+ interface_keys = (None,)
+ if member is not None:
+ member_keys = (None, member)
+ else:
+ member_keys = (None,)
+
+ for path in path_keys:
+ by_interface = self._signal_recipients_by_object_path.get(path)
+ if by_interface is None:
+ continue
+ for dbus_interface in interface_keys:
+ by_member = by_interface.get(dbus_interface, None)
+ if by_member is None:
+ continue
+ for member in member_keys:
+ matches = by_member.get(member, None)
+ if matches is None:
+ continue
+ for m in matches:
+ yield m
+
+ def remove_signal_receiver(self, handler_or_match,
+ signal_name=None,
+ dbus_interface=None,
+ bus_name=None,
+ path=None,
+ **keywords):
+ named_service = keywords.pop('named_service', None)
+ if named_service is not None:
+ if bus_name is not None:
+ raise TypeError('bus_name and named_service cannot both be '
+ 'specified')
+ bus_name = named_service
+ from warnings import warn
+ warn('Passing the named_service parameter to '
+ 'remove_signal_receiver by name is deprecated: please use '
+ 'positional parameters',
+ DeprecationWarning, stacklevel=2)
+
+ new = []
+ deletions = []
+ self._signals_lock.acquire()
+ try:
+ by_interface = self._signal_recipients_by_object_path.get(path,
+ None)
+ if by_interface is None:
+ return
+ by_member = by_interface.get(dbus_interface, None)
+ if by_member is None:
+ return
+ matches = by_member.get(signal_name, None)
+ if matches is None:
+ return
+
+ for match in matches:
+ if (handler_or_match is match
+ or match.matches_removal_spec(bus_name,
+ path,
+ dbus_interface,
+ signal_name,
+ handler_or_match,
+ **keywords)):
+ deletions.append(match)
+ else:
+ new.append(match)
+
+ if new:
+ by_member[signal_name] = new
+ else:
+ del by_member[signal_name]
+ if not by_member:
+ del by_interface[dbus_interface]
+ if not by_interface:
+ del self._signal_recipients_by_object_path[path]
+ finally:
+ self._signals_lock.release()
+
+ for match in deletions:
+ self._clean_up_signal_match(match)
+
+ def _clean_up_signal_match(self, match):
+ # Now called without the signals lock held (it was held in <= 0.81.0)
+ pass
+
+ def _signal_func(self, message):
+ """D-Bus filter function. Handle signals by dispatching to Python
+ callbacks kept in the match-rule tree.
+ """
+
+ if not isinstance(message, SignalMessage):
+ return HANDLER_RESULT_NOT_YET_HANDLED
+
+ dbus_interface = message.get_interface()
+ path = message.get_path()
+ signal_name = message.get_member()
+
+ for match in self._iter_easy_matches(path, dbus_interface,
+ signal_name):
+ match.maybe_handle_message(message)
+
+ if (dbus_interface == LOCAL_IFACE and
+ path == LOCAL_PATH and
+ signal_name == 'Disconnected'):
+ for cb in self.__call_on_disconnection:
+ try:
+ cb(self)
+ except Exception:
+ # basicConfig is a no-op if logging is already configured
+ logging.basicConfig()
+ _logger.error('Exception in handler for Disconnected '
+ 'signal:', exc_info=1)
+
+ return HANDLER_RESULT_NOT_YET_HANDLED
+
+ def call_async(self, bus_name, object_path, dbus_interface, method,
+ signature, args, reply_handler, error_handler,
+ timeout=-1.0, byte_arrays=False,
+ require_main_loop=True, **kwargs):
+ """Call the given method, asynchronously.
+
+ If the reply_handler is None, successful replies will be ignored.
+ If the error_handler is None, failures will be ignored. If both
+ are None, the implementation may request that no reply is sent.
+
+ :Returns: The dbus.lowlevel.PendingCall.
+ :Since: 0.81.0
+ """
+ if object_path == LOCAL_PATH:
+ raise DBusException('Methods may not be called on the reserved '
+ 'path %s' % LOCAL_PATH)
+ if dbus_interface == LOCAL_IFACE:
+ raise DBusException('Methods may not be called on the reserved '
+ 'interface %s' % LOCAL_IFACE)
+ # no need to validate other args - MethodCallMessage ctor will do
+
+ get_args_opts = dict(byte_arrays=byte_arrays)
+ if is_py2:
+ get_args_opts['utf8_strings'] = kwargs.get('utf8_strings', False)
+ elif 'utf8_strings' in kwargs:
+ raise TypeError("unexpected keyword argument 'utf8_strings'")
+
+ message = MethodCallMessage(destination=bus_name,
+ path=object_path,
+ interface=dbus_interface,
+ method=method)
+ # Add the arguments to the function
+ try:
+ message.append(signature=signature, *args)
+ except Exception as e:
+ logging.basicConfig()
+ _logger.error('Unable to set arguments %r according to '
+ 'signature %r: %s: %s',
+ args, signature, e.__class__, e)
+ raise
+
+ if reply_handler is None and error_handler is None:
+ # we don't care what happens, so just send it
+ self.send_message(message)
+ return
+
+ if reply_handler is None:
+ reply_handler = _noop
+ if error_handler is None:
+ error_handler = _noop
+
+ def msg_reply_handler(message):
+ if isinstance(message, MethodReturnMessage):
+ reply_handler(*message.get_args_list(**get_args_opts))
+ elif isinstance(message, ErrorMessage):
+ error_handler(DBusException(name=message.get_error_name(),
+ *message.get_args_list()))
+ else:
+ error_handler(TypeError('Unexpected type for reply '
+ 'message: %r' % message))
+ return self.send_message_with_reply(message, msg_reply_handler,
+ timeout,
+ require_main_loop=require_main_loop)
+
+ def call_blocking(self, bus_name, object_path, dbus_interface, method,
+ signature, args, timeout=-1.0,
+ byte_arrays=False, **kwargs):
+ """Call the given method, synchronously.
+ :Since: 0.81.0
+ """
+ if object_path == LOCAL_PATH:
+ raise DBusException('Methods may not be called on the reserved '
+ 'path %s' % LOCAL_PATH)
+ if dbus_interface == LOCAL_IFACE:
+ raise DBusException('Methods may not be called on the reserved '
+ 'interface %s' % LOCAL_IFACE)
+ # no need to validate other args - MethodCallMessage ctor will do
+
+ get_args_opts = dict(byte_arrays=byte_arrays)
+ if is_py2:
+ get_args_opts['utf8_strings'] = kwargs.get('utf8_strings', False)
+ elif 'utf8_strings' in kwargs:
+ raise TypeError("unexpected keyword argument 'utf8_strings'")
+
+ message = MethodCallMessage(destination=bus_name,
+ path=object_path,
+ interface=dbus_interface,
+ method=method)
+ # Add the arguments to the function
+ try:
+ message.append(signature=signature, *args)
+ except Exception as e:
+ logging.basicConfig()
+ _logger.error('Unable to set arguments %r according to '
+ 'signature %r: %s: %s',
+ args, signature, e.__class__, e)
+ raise
+
+ # make a blocking call
+ reply_message = self.send_message_with_reply_and_block(
+ message, timeout)
+ args_list = reply_message.get_args_list(**get_args_opts)
+ if len(args_list) == 0:
+ return None
+ elif len(args_list) == 1:
+ return args_list[0]
+ else:
+ return tuple(args_list)
+
+ def call_on_disconnection(self, callable):
+ """Arrange for `callable` to be called with one argument (this
+ Connection object) when the Connection becomes
+ disconnected.
+
+ :Since: 0.83.0
+ """
+ self.__call_on_disconnection.append(callable)
diff --git a/dbus/decorators.py b/dbus/decorators.py
new file mode 100644
index 0000000..71c8be0
--- /dev/null
+++ b/dbus/decorators.py
@@ -0,0 +1,354 @@
+"""Service-side D-Bus decorators."""
+
+# Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2003 David Zeuthen
+# Copyright (C) 2004 Rob Taylor
+# Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ('method', 'signal')
+__docformat__ = 'restructuredtext'
+
+import inspect
+
+from dbus import validate_interface_name, Signature, validate_member_name
+from dbus.lowlevel import SignalMessage
+from dbus.exceptions import DBusException
+from dbus._compat import is_py2
+
+
+def method(dbus_interface, in_signature=None, out_signature=None,
+ async_callbacks=None,
+ sender_keyword=None, path_keyword=None, destination_keyword=None,
+ message_keyword=None, connection_keyword=None,
+ byte_arrays=False,
+ rel_path_keyword=None, **kwargs):
+ """Factory for decorators used to mark methods of a `dbus.service.Object`
+ to be exported on the D-Bus.
+
+ The decorated method will be exported over D-Bus as the method of the
+ same name on the given D-Bus interface.
+
+ :Parameters:
+ `dbus_interface` : str
+ Name of a D-Bus interface
+ `in_signature` : str or None
+ If not None, the signature of the method parameters in the usual
+ D-Bus notation
+ `out_signature` : str or None
+ If not None, the signature of the return value in the usual
+ D-Bus notation
+ `async_callbacks` : tuple containing (str,str), or None
+ If None (default) the decorated method is expected to return
+ values matching the `out_signature` as usual, or raise
+ an exception on error. If not None, the following applies:
+
+ `async_callbacks` contains the names of two keyword arguments to
+ the decorated function, which will be used to provide a success
+ callback and an error callback (in that order).
+
+ When the decorated method is called via the D-Bus, its normal
+ return value will be ignored; instead, a pair of callbacks are
+ passed as keyword arguments, and the decorated method is
+ expected to arrange for one of them to be called.
+
+ On success the success callback must be called, passing the
+ results of this method as positional parameters in the format
+ given by the `out_signature`.
+
+ On error the decorated method may either raise an exception
+ before it returns, or arrange for the error callback to be
+ called with an Exception instance as parameter.
+
+ `sender_keyword` : str or None
+ If not None, contains the name of a keyword argument to the
+ decorated function, conventionally ``'sender'``. When the
+ method is called, the sender's unique name will be passed as
+ this keyword argument.
+
+ `path_keyword` : str or None
+ If not None (the default), the decorated method will receive
+ the destination object path as a keyword argument with this
+ name. Normally you already know the object path, but in the
+ case of "fallback paths" you'll usually want to use the object
+ path in the method's implementation.
+
+ For fallback objects, `rel_path_keyword` (new in 0.82.2) is
+ likely to be more useful.
+
+ :Since: 0.80.0?
+
+ `rel_path_keyword` : str or None
+ If not None (the default), the decorated method will receive
+ the destination object path, relative to the path at which the
+ object was exported, as a keyword argument with this
+ name. For non-fallback objects the relative path will always be
+ '/'.
+
+ :Since: 0.82.2
+
+ `destination_keyword` : str or None
+ If not None (the default), the decorated method will receive
+ the destination bus name as a keyword argument with this name.
+ Included for completeness - you shouldn't need this.
+
+ :Since: 0.80.0?
+
+ `message_keyword` : str or None
+ If not None (the default), the decorated method will receive
+ the `dbus.lowlevel.MethodCallMessage` as a keyword argument
+ with this name.
+
+ :Since: 0.80.0?
+
+ `connection_keyword` : str or None
+ If not None (the default), the decorated method will receive
+ the `dbus.connection.Connection` as a keyword argument
+ with this name. This is generally only useful for objects
+ that are available on more than one connection.
+
+ :Since: 0.82.0
+
+ `utf8_strings` : bool
+ If False (default), D-Bus strings are passed to the decorated
+ method as objects of class dbus.String, a unicode subclass.
+
+ If True, D-Bus strings are passed to the decorated method
+ as objects of class dbus.UTF8String, a str subclass guaranteed
+ to be encoded in UTF-8.
+
+ This option does not affect object-paths and signatures, which
+ are always 8-bit strings (str subclass) encoded in ASCII.
+
+ :Since: 0.80.0
+
+ `byte_arrays` : bool
+ If False (default), a byte array will be passed to the decorated
+ method as an `Array` (a list subclass) of `Byte` objects.
+
+ If True, a byte array will be passed to the decorated method as
+ a `ByteArray`, a str subclass. This is usually what you want,
+ but is switched off by default to keep dbus-python's API
+ consistent.
+
+ :Since: 0.80.0
+ """
+ validate_interface_name(dbus_interface)
+
+ def decorator(func):
+ if hasattr(inspect, 'Signature'):
+ args = []
+
+ for arg in inspect.signature(func).parameters.values():
+ if arg.kind in (inspect.Parameter.POSITIONAL_ONLY,
+ inspect.Parameter.POSITIONAL_OR_KEYWORD):
+ args.append(arg.name)
+ else:
+ args = inspect.getargspec(func)[0]
+
+ args.pop(0)
+
+ if async_callbacks:
+ if type(async_callbacks) != tuple:
+ raise TypeError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
+ if len(async_callbacks) != 2:
+ raise ValueError('async_callbacks must be a tuple of (keyword for return callback, keyword for error callback)')
+ args.remove(async_callbacks[0])
+ args.remove(async_callbacks[1])
+
+ if sender_keyword:
+ args.remove(sender_keyword)
+ if rel_path_keyword:
+ args.remove(rel_path_keyword)
+ if path_keyword:
+ args.remove(path_keyword)
+ if destination_keyword:
+ args.remove(destination_keyword)
+ if message_keyword:
+ args.remove(message_keyword)
+ if connection_keyword:
+ args.remove(connection_keyword)
+
+ if in_signature:
+ in_sig = tuple(Signature(in_signature))
+
+ if len(in_sig) > len(args):
+ raise ValueError('input signature is longer than the number of arguments taken')
+ elif len(in_sig) < len(args):
+ raise ValueError('input signature is shorter than the number of arguments taken')
+
+ func._dbus_is_method = True
+ func._dbus_async_callbacks = async_callbacks
+ func._dbus_interface = dbus_interface
+ func._dbus_in_signature = in_signature
+ func._dbus_out_signature = out_signature
+ func._dbus_sender_keyword = sender_keyword
+ func._dbus_path_keyword = path_keyword
+ func._dbus_rel_path_keyword = rel_path_keyword
+ func._dbus_destination_keyword = destination_keyword
+ func._dbus_message_keyword = message_keyword
+ func._dbus_connection_keyword = connection_keyword
+ func._dbus_args = args
+ func._dbus_get_args_options = dict(byte_arrays=byte_arrays)
+ if is_py2:
+ func._dbus_get_args_options['utf8_strings'] = kwargs.get(
+ 'utf8_strings', False)
+ elif 'utf8_strings' in kwargs:
+ raise TypeError("unexpected keyword argument 'utf8_strings'")
+ return func
+
+ return decorator
+
+
+def signal(dbus_interface, signature=None, path_keyword=None,
+ rel_path_keyword=None):
+ """Factory for decorators used to mark methods of a `dbus.service.Object`
+ to emit signals on the D-Bus.
+
+ Whenever the decorated method is called in Python, after the method
+ body is executed, a signal with the same name as the decorated method,
+ with the given D-Bus interface, will be emitted from this object.
+
+ :Parameters:
+ `dbus_interface` : str
+ The D-Bus interface whose signal is emitted
+ `signature` : str
+ The signature of the signal in the usual D-Bus notation
+
+ `path_keyword` : str or None
+ A keyword argument to the decorated method. If not None,
+ that argument will not be emitted as an argument of
+ the signal, and when the signal is emitted, it will appear
+ to come from the object path given by the keyword argument.
+
+ Note that when calling the decorated method, you must always
+ pass in the object path as a keyword argument, not as a
+ positional argument.
+
+ This keyword argument cannot be used on objects where
+ the class attribute ``SUPPORTS_MULTIPLE_OBJECT_PATHS`` is true.
+
+ :Deprecated: since 0.82.0. Use `rel_path_keyword` instead.
+
+ `rel_path_keyword` : str or None
+ A keyword argument to the decorated method. If not None,
+ that argument will not be emitted as an argument of
+ the signal.
+
+ When the signal is emitted, if the named keyword argument is given,
+ the signal will appear to come from the object path obtained by
+ appending the keyword argument to the object's object path.
+ This is useful to implement "fallback objects" (objects which
+ own an entire subtree of the object-path tree).
+
+ If the object is available at more than one object-path on the
+ same or different connections, the signal will be emitted at
+ an appropriate object-path on each connection - for instance,
+ if the object is exported at /abc on connection 1 and at
+ /def and /x/y/z on connection 2, and the keyword argument is
+ /foo, then signals will be emitted from /abc/foo and /def/foo
+ on connection 1, and /x/y/z/foo on connection 2.
+
+ :Since: 0.82.0
+ """
+ validate_interface_name(dbus_interface)
+
+ if path_keyword is not None:
+ from warnings import warn
+ warn(DeprecationWarning('dbus.service.signal::path_keyword has been '
+ 'deprecated since dbus-python 0.82.0, and '
+ 'will not work on objects that support '
+ 'multiple object paths'),
+ DeprecationWarning, stacklevel=2)
+ if rel_path_keyword is not None:
+ raise TypeError('dbus.service.signal::path_keyword and '
+ 'rel_path_keyword cannot both be used')
+
+ def decorator(func):
+ member_name = func.__name__
+ validate_member_name(member_name)
+
+ def emit_signal(self, *args, **keywords):
+ abs_path = None
+ if path_keyword is not None:
+ if self.SUPPORTS_MULTIPLE_OBJECT_PATHS:
+ raise TypeError('path_keyword cannot be used on the '
+ 'signals of an object that supports '
+ 'multiple object paths')
+ abs_path = keywords.pop(path_keyword, None)
+ if (abs_path != self.__dbus_object_path__ and
+ not self.__dbus_object_path__.startswith(abs_path + '/')):
+ raise ValueError('Path %r is not below %r', abs_path,
+ self.__dbus_object_path__)
+
+ rel_path = None
+ if rel_path_keyword is not None:
+ rel_path = keywords.pop(rel_path_keyword, None)
+
+ func(self, *args, **keywords)
+
+ for location in self.locations:
+ if abs_path is None:
+ # non-deprecated case
+ if rel_path is None or rel_path in ('/', ''):
+ object_path = location[1]
+ else:
+ # will be validated by SignalMessage ctor in a moment
+ object_path = location[1] + rel_path
+ else:
+ object_path = abs_path
+
+ message = SignalMessage(object_path,
+ dbus_interface,
+ member_name)
+ message.append(signature=signature, *args)
+
+ location[0].send_message(message)
+ # end emit_signal
+
+ args = inspect.getargspec(func)[0]
+ args.pop(0)
+
+ for keyword in rel_path_keyword, path_keyword:
+ if keyword is not None:
+ try:
+ args.remove(keyword)
+ except ValueError:
+ raise ValueError('function has no argument "%s"' % keyword)
+
+ if signature:
+ sig = tuple(Signature(signature))
+
+ if len(sig) > len(args):
+ raise ValueError('signal signature is longer than the number of arguments provided')
+ elif len(sig) < len(args):
+ raise ValueError('signal signature is shorter than the number of arguments provided')
+
+ emit_signal.__name__ = func.__name__
+ emit_signal.__doc__ = func.__doc__
+ emit_signal._dbus_is_signal = True
+ emit_signal._dbus_interface = dbus_interface
+ emit_signal._dbus_signature = signature
+ emit_signal._dbus_args = args
+ return emit_signal
+
+ return decorator
diff --git a/dbus/exceptions.py b/dbus/exceptions.py
new file mode 100644
index 0000000..0930425
--- /dev/null
+++ b/dbus/exceptions.py
@@ -0,0 +1,134 @@
+"""D-Bus exceptions."""
+
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ('DBusException', 'MissingErrorHandlerException',
+ 'MissingReplyHandlerException', 'ValidationException',
+ 'IntrospectionParserException', 'UnknownMethodException',
+ 'NameExistsException')
+
+from dbus._compat import is_py3
+
+
+class DBusException(Exception):
+
+ include_traceback = False
+ """If True, tracebacks will be included in the exception message sent to
+ D-Bus clients.
+
+ Exceptions that are not DBusException subclasses always behave
+ as though this is True. Set this to True on DBusException subclasses
+ that represent a programming error, and leave it False on subclasses that
+ represent an expected failure condition (e.g. a network server not
+ responding)."""
+
+ def __init__(self, *args, **kwargs):
+ name = kwargs.pop('name', None)
+ if name is not None or getattr(self, '_dbus_error_name', None) is None:
+ self._dbus_error_name = name
+ if kwargs:
+ raise TypeError('DBusException does not take keyword arguments: %s'
+ % ', '.join(kwargs.keys()))
+ Exception.__init__(self, *args)
+
+ def __unicode__(self):
+ """Return a unicode error"""
+ # We can't just use Exception.__unicode__ because it chains up weirdly.
+ # https://code.launchpad.net/~mvo/ubuntu/quantal/dbus-python/lp846044/+merge/129214
+ if len(self.args) > 1:
+ s = unicode(self.args)
+ else:
+ s = ''.join(self.args)
+
+ if self._dbus_error_name is not None:
+ return '%s: %s' % (self._dbus_error_name, s)
+ else:
+ return s
+
+ def __str__(self):
+ """Return a str error"""
+ s = Exception.__str__(self)
+ if self._dbus_error_name is not None:
+ return '%s: %s' % (self._dbus_error_name, s)
+ else:
+ return s
+
+ def get_dbus_message(self):
+ if len(self.args) > 1:
+ if is_py3:
+ s = str(self.args)
+ else:
+ s = unicode(self.args)
+ else:
+ s = ''.join(self.args)
+
+ if isinstance(s, bytes):
+ return s.decode('utf-8', 'replace')
+
+ return s
+
+ def get_dbus_name(self):
+ return self._dbus_error_name
+
+class MissingErrorHandlerException(DBusException):
+
+ include_traceback = True
+
+ def __init__(self):
+ DBusException.__init__(self, "error_handler not defined: if you define a reply_handler you must also define an error_handler")
+
+class MissingReplyHandlerException(DBusException):
+
+ include_traceback = True
+
+ def __init__(self):
+ DBusException.__init__(self, "reply_handler not defined: if you define an error_handler you must also define a reply_handler")
+
+class ValidationException(DBusException):
+
+ include_traceback = True
+
+ def __init__(self, msg=''):
+ DBusException.__init__(self, "Error validating string: %s"%msg)
+
+class IntrospectionParserException(DBusException):
+
+ include_traceback = True
+
+ def __init__(self, msg=''):
+ DBusException.__init__(self, "Error parsing introspect data: %s"%msg)
+
+class UnknownMethodException(DBusException):
+
+ include_traceback = True
+ _dbus_error_name = 'org.freedesktop.DBus.Error.UnknownMethod'
+
+ def __init__(self, method):
+ DBusException.__init__(self, "Unknown method: %s"%method)
+
+class NameExistsException(DBusException):
+
+ include_traceback = True
+
+ def __init__(self, name):
+ DBusException.__init__(self, "Bus name already exists: %s"%name)
diff --git a/dbus/gi_service.py b/dbus/gi_service.py
new file mode 100644
index 0000000..e091771
--- /dev/null
+++ b/dbus/gi_service.py
@@ -0,0 +1,85 @@
+"""Support code for implementing D-Bus services via PyGI."""
+
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ['ExportedGObject']
+
+from gi.repository import GObject
+import dbus.service
+
+# The odd syntax used here is required so that the code is compatible with
+# both Python 2 and Python 3. It essentially creates a new class called
+# ExportedGObject with a metaclass of ExportGObjectType and an __init__()
+# function.
+#
+# Because GObject and `dbus.service.Object` both have custom metaclasses, the
+# naive approach using simple multiple inheritance won't work. This class has
+# `ExportedGObjectType` as its metaclass, which is sufficient to make it work
+# correctly.
+
+class ExportedGObjectType(GObject.GObject.__class__, dbus.service.InterfaceType):
+ """A metaclass which inherits from both GObjectMeta and
+ `dbus.service.InterfaceType`. Used as the metaclass for `ExportedGObject`.
+ """
+ def __init__(cls, name, bases, dct):
+ GObject.GObject.__class__.__init__(cls, name, bases, dct)
+ dbus.service.InterfaceType.__init__(cls, name, bases, dct)
+
+
+def ExportedGObject__init__(self, conn=None, object_path=None, **kwargs):
+ """Initialize an exported GObject.
+
+ :Parameters:
+ `conn` : dbus.connection.Connection
+ The D-Bus connection or bus
+ `object_path` : str
+ The object path at which to register this object.
+ :Keywords:
+ `bus_name` : dbus.service.BusName
+ A bus name to be held on behalf of this object, or None.
+ `gobject_properties` : dict
+ GObject properties to be set on the constructed object.
+
+ Any unrecognised keyword arguments will also be interpreted
+ as GObject properties.
+ """
+ bus_name = kwargs.pop('bus_name', None)
+ gobject_properties = kwargs.pop('gobject_properties', None)
+
+ if gobject_properties is not None:
+ kwargs.update(gobject_properties)
+ GObject.GObject.__init__(self, **kwargs)
+ dbus.service.Object.__init__(self, conn=conn,
+ object_path=object_path,
+ bus_name=bus_name)
+
+ExportedGObject__doc__ = '''
+A GObject which is exported on D-Bus.
+'''
+
+ExportedGObject = ExportedGObjectType(
+ 'ExportedGObject',
+ (GObject.GObject, dbus.service.Object),
+ {'__init__': ExportedGObject__init__,
+ '__doc__': ExportedGObject__doc__,
+ })
diff --git a/dbus/glib.py b/dbus/glib.py
new file mode 100644
index 0000000..bb6db74
--- /dev/null
+++ b/dbus/glib.py
@@ -0,0 +1,51 @@
+# Copyright (C) 2004 Anders Carlsson
+# Copyright (C) 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+"""Deprecated module which sets the default GLib main context as the mainloop
+implementation within D-Bus, as a side-effect of being imported!
+
+This API is highly non-obvious, so instead of importing this module,
+new programs which don't need pre-0.80 compatibility should use this
+equivalent code::
+
+ from dbus.mainloop.glib import DBusGMainLoop
+ DBusGMainLoop(set_as_default=True)
+"""
+__docformat__ = 'restructuredtext'
+
+from dbus.mainloop.glib import DBusGMainLoop, threads_init
+from warnings import warn as _warn
+
+init_threads = threads_init
+
+DBusGMainLoop(set_as_default=True)
+
+_warn(DeprecationWarning("""\
+Importing dbus.glib to use the GLib main loop with dbus-python is deprecated.
+Instead, use this sequence:
+
+ from dbus.mainloop.glib import DBusGMainLoop
+
+ DBusGMainLoop(set_as_default=True)
+"""), DeprecationWarning, stacklevel=2)
diff --git a/dbus/gobject_service.py b/dbus/gobject_service.py
new file mode 100644
index 0000000..ef16009
--- /dev/null
+++ b/dbus/gobject_service.py
@@ -0,0 +1,86 @@
+"""Support code for implementing D-Bus services via GObjects."""
+
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+from warnings import warn as _warn
+_warn(DeprecationWarning("""\
+dbus.gobject_service is deprecated, and is not available under Python 3.
+
+Porting from gobject (PyGObject 2) to gi.repository.GObject (PyGObject 3),
+and using dbus.gi_service instead of dbus.gobject_service, is recommended.
+"""), DeprecationWarning, stacklevel=2)
+
+if 'gi' in sys.modules:
+ # this worked in dbus-python 1.0, so preserve the functionality
+ from gi.repository import GObject as gobject
+else:
+ # this worked in dbus-python < 1.0
+ import gobject
+
+import dbus.service
+
+class ExportedGObjectType(gobject.GObject.__class__, dbus.service.InterfaceType):
+ """A metaclass which inherits from both GObjectMeta and
+ `dbus.service.InterfaceType`. Used as the metaclass for `ExportedGObject`.
+ """
+ def __init__(cls, name, bases, dct):
+ gobject.GObject.__class__.__init__(cls, name, bases, dct)
+ dbus.service.InterfaceType.__init__(cls, name, bases, dct)
+
+class ExportedGObject(gobject.GObject, dbus.service.Object):
+ """A GObject which is exported on the D-Bus.
+
+ Because GObject and `dbus.service.Object` both have custom metaclasses,
+ the naive approach using simple multiple inheritance won't work. This
+ class has `ExportedGObjectType` as its metaclass, which is sufficient
+ to make it work correctly.
+ """
+ __metaclass__ = ExportedGObjectType
+
+ def __init__(self, conn=None, object_path=None, **kwargs):
+ """Initialize an exported GObject.
+
+ :Parameters:
+ `conn` : dbus.connection.Connection
+ The D-Bus connection or bus
+ `object_path` : str
+ The object path at which to register this object.
+ :Keywords:
+ `bus_name` : dbus.service.BusName
+ A bus name to be held on behalf of this object, or None.
+ `gobject_properties` : dict
+ GObject properties to be set on the constructed object.
+
+ Any unrecognised keyword arguments will also be interpreted
+ as GObject properties.
+ """
+ bus_name = kwargs.pop('bus_name', None)
+ gobject_properties = kwargs.pop('gobject_properties', None)
+
+ if gobject_properties is not None:
+ kwargs.update(gobject_properties)
+ gobject.GObject.__init__(self, **kwargs)
+ dbus.service.Object.__init__(self, conn=conn,
+ object_path=object_path,
+ bus_name=bus_name)
diff --git a/dbus/lowlevel.py b/dbus/lowlevel.py
new file mode 100644
index 0000000..398a10f
--- /dev/null
+++ b/dbus/lowlevel.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+"""Low-level interface to D-Bus."""
+
+__all__ = ('PendingCall', 'Message', 'MethodCallMessage',
+ 'MethodReturnMessage', 'ErrorMessage', 'SignalMessage',
+ 'HANDLER_RESULT_HANDLED', 'HANDLER_RESULT_NOT_YET_HANDLED',
+ 'MESSAGE_TYPE_INVALID', 'MESSAGE_TYPE_METHOD_CALL',
+ 'MESSAGE_TYPE_METHOD_RETURN', 'MESSAGE_TYPE_ERROR',
+ 'MESSAGE_TYPE_SIGNAL')
+
+from _dbus_bindings import (
+ ErrorMessage, HANDLER_RESULT_HANDLED, HANDLER_RESULT_NOT_YET_HANDLED,
+ MESSAGE_TYPE_ERROR, MESSAGE_TYPE_INVALID, MESSAGE_TYPE_METHOD_CALL,
+ MESSAGE_TYPE_METHOD_RETURN, MESSAGE_TYPE_SIGNAL, Message,
+ MethodCallMessage, MethodReturnMessage, PendingCall, SignalMessage)
diff --git a/dbus/mainloop/__init__.py b/dbus/mainloop/__init__.py
new file mode 100644
index 0000000..dfaeefb
--- /dev/null
+++ b/dbus/mainloop/__init__.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+"""Base definitions, etc. for main loop integration.
+
+"""
+
+import _dbus_bindings
+
+NativeMainLoop = _dbus_bindings.NativeMainLoop
+
+NULL_MAIN_LOOP = _dbus_bindings.NULL_MAIN_LOOP
+"""A null mainloop which doesn't actually do anything.
+
+For advanced users who want to dispatch events by hand. This is almost
+certainly a bad idea - if in doubt, use the GLib main loop found in
+`dbus.mainloop.glib`.
+"""
+
+WATCH_READABLE = _dbus_bindings.WATCH_READABLE
+"""Represents a file descriptor becoming readable.
+Used to implement file descriptor watches."""
+
+WATCH_WRITABLE = _dbus_bindings.WATCH_WRITABLE
+"""Represents a file descriptor becoming readable.
+Used to implement file descriptor watches."""
+
+WATCH_HANGUP = _dbus_bindings.WATCH_HANGUP
+"""Represents a file descriptor reaching end-of-file.
+Used to implement file descriptor watches."""
+
+WATCH_ERROR = _dbus_bindings.WATCH_ERROR
+"""Represents an error condition on a file descriptor.
+Used to implement file descriptor watches."""
+
+__all__ = (
+ # Imported into this module
+ 'NativeMainLoop', 'WATCH_READABLE', 'WATCH_WRITABLE',
+ 'WATCH_HANGUP', 'WATCH_ERROR', 'NULL_MAIN_LOOP',
+
+ # Submodules
+ 'glib'
+ )
diff --git a/dbus/mainloop/glib.py b/dbus/mainloop/glib.py
new file mode 100644
index 0000000..39fbb1b
--- /dev/null
+++ b/dbus/mainloop/glib.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2004 Anders Carlsson
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+"""GLib main loop integration using libdbus-glib."""
+
+__all__ = ('DBusGMainLoop', 'threads_init')
+
+from _dbus_glib_bindings import DBusGMainLoop, gthreads_init
+
+_dbus_gthreads_initialized = False
+def threads_init():
+ """Initialize threads in dbus-glib, if this has not already been done.
+
+ This must be called before creating a second thread in a program that
+ uses this module.
+ """
+ global _dbus_gthreads_initialized
+ if not _dbus_gthreads_initialized:
+ gthreads_init()
+ _dbus_gthreads_initialized = True
diff --git a/dbus/proxies.py b/dbus/proxies.py
new file mode 100644
index 0000000..c7cd802
--- /dev/null
+++ b/dbus/proxies.py
@@ -0,0 +1,567 @@
+# Copyright (C) 2003-2007 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2003 David Zeuthen
+# Copyright (C) 2004 Rob Taylor
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import logging
+
+try:
+ from threading import RLock
+except ImportError:
+ from dummy_threading import RLock
+
+import _dbus_bindings
+from dbus._expat_introspect_parser import process_introspection_data
+from dbus.exceptions import (
+ DBusException, IntrospectionParserException, MissingErrorHandlerException,
+ MissingReplyHandlerException)
+
+__docformat__ = 'restructuredtext'
+
+
+_logger = logging.getLogger('dbus.proxies')
+
+from _dbus_bindings import (
+ BUS_DAEMON_IFACE, BUS_DAEMON_NAME, BUS_DAEMON_PATH, INTROSPECTABLE_IFACE,
+ LOCAL_PATH)
+from dbus._compat import is_py2
+
+
+class _DeferredMethod:
+ """A proxy method which will only get called once we have its
+ introspection reply.
+ """
+ def __init__(self, proxy_method, append, block):
+ self._proxy_method = proxy_method
+ # the test suite relies on the existence of this property
+ self._method_name = proxy_method._method_name
+ self._append = append
+ self._block = block
+
+ def __call__(self, *args, **keywords):
+ if ('reply_handler' in keywords or
+ keywords.get('ignore_reply', False)):
+ # defer the async call til introspection finishes
+ self._append(self._proxy_method, args, keywords)
+ return None
+ else:
+ # we're being synchronous, so block
+ self._block()
+ return self._proxy_method(*args, **keywords)
+
+ def call_async(self, *args, **keywords):
+ self._append(self._proxy_method, args, keywords)
+
+
+class _ProxyMethod:
+ """A proxy method.
+
+ Typically a member of a ProxyObject. Calls to the
+ method produce messages that travel over the Bus and are routed
+ to a specific named Service.
+ """
+ def __init__(self, proxy, connection, bus_name, object_path, method_name,
+ iface):
+ if object_path == LOCAL_PATH:
+ raise DBusException('Methods may not be called on the reserved '
+ 'path %s' % LOCAL_PATH)
+
+ # trust that the proxy, and the properties it had, are OK
+ self._proxy = proxy
+ self._connection = connection
+ self._named_service = bus_name
+ self._object_path = object_path
+ # fail early if the method name is bad
+ _dbus_bindings.validate_member_name(method_name)
+ # the test suite relies on the existence of this property
+ self._method_name = method_name
+ # fail early if the interface name is bad
+ if iface is not None:
+ _dbus_bindings.validate_interface_name(iface)
+ self._dbus_interface = iface
+
+ def __call__(self, *args, **keywords):
+ reply_handler = keywords.pop('reply_handler', None)
+ error_handler = keywords.pop('error_handler', None)
+ ignore_reply = keywords.pop('ignore_reply', False)
+ signature = keywords.pop('signature', None)
+
+ if reply_handler is not None or error_handler is not None:
+ if reply_handler is None:
+ raise MissingReplyHandlerException()
+ elif error_handler is None:
+ raise MissingErrorHandlerException()
+ elif ignore_reply:
+ raise TypeError('ignore_reply and reply_handler cannot be '
+ 'used together')
+
+ dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
+
+ if signature is None:
+ if dbus_interface is None:
+ key = self._method_name
+ else:
+ key = dbus_interface + '.' + self._method_name
+
+ signature = self._proxy._introspect_method_map.get(key, None)
+
+ if ignore_reply or reply_handler is not None:
+ self._connection.call_async(self._named_service,
+ self._object_path,
+ dbus_interface,
+ self._method_name,
+ signature,
+ args,
+ reply_handler,
+ error_handler,
+ **keywords)
+ else:
+ return self._connection.call_blocking(self._named_service,
+ self._object_path,
+ dbus_interface,
+ self._method_name,
+ signature,
+ args,
+ **keywords)
+
+ def call_async(self, *args, **keywords):
+ reply_handler = keywords.pop('reply_handler', None)
+ error_handler = keywords.pop('error_handler', None)
+ signature = keywords.pop('signature', None)
+
+ dbus_interface = keywords.pop('dbus_interface', self._dbus_interface)
+
+ if signature is None:
+ if dbus_interface:
+ key = dbus_interface + '.' + self._method_name
+ else:
+ key = self._method_name
+ signature = self._proxy._introspect_method_map.get(key, None)
+
+ self._connection.call_async(self._named_service,
+ self._object_path,
+ dbus_interface,
+ self._method_name,
+ signature,
+ args,
+ reply_handler,
+ error_handler,
+ **keywords)
+
+
+class ProxyObject(object):
+ """A proxy to the remote Object.
+
+ A ProxyObject is provided by the Bus. ProxyObjects
+ have member functions, and can be called like normal Python objects.
+ """
+ ProxyMethodClass = _ProxyMethod
+ DeferredMethodClass = _DeferredMethod
+
+ INTROSPECT_STATE_DONT_INTROSPECT = 0
+ INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1
+ INTROSPECT_STATE_INTROSPECT_DONE = 2
+
+ def __init__(self, conn=None, bus_name=None, object_path=None,
+ introspect=True, follow_name_owner_changes=False, **kwargs):
+ """Initialize the proxy object.
+
+ :Parameters:
+ `conn` : `dbus.connection.Connection`
+ The bus or connection on which to find this object.
+ The keyword argument `bus` is a deprecated alias for this.
+ `bus_name` : str
+ A bus name for the application owning the object, to be used
+ as the destination for method calls and the sender for
+ signal matches. The keyword argument ``named_service`` is a
+ deprecated alias for this.
+ `object_path` : str
+ The object path at which the application exports the object
+ `introspect` : bool
+ If true (default), attempt to introspect the remote
+ object to find out supported methods and their signatures
+ `follow_name_owner_changes` : bool
+ If true (default is false) and the `bus_name` is a
+ well-known name, follow ownership changes for that name
+ """
+ bus = kwargs.pop('bus', None)
+ if bus is not None:
+ if conn is not None:
+ raise TypeError('conn and bus cannot both be specified')
+ conn = bus
+ from warnings import warn
+ warn('Passing the bus parameter to ProxyObject by name is '
+ 'deprecated: please use positional parameters',
+ DeprecationWarning, stacklevel=2)
+ named_service = kwargs.pop('named_service', None)
+ if named_service is not None:
+ if bus_name is not None:
+ raise TypeError('bus_name and named_service cannot both be '
+ 'specified')
+ bus_name = named_service
+ from warnings import warn
+ warn('Passing the named_service parameter to ProxyObject by name '
+ 'is deprecated: please use positional parameters',
+ DeprecationWarning, stacklevel=2)
+ if kwargs:
+ raise TypeError('ProxyObject.__init__ does not take these '
+ 'keyword arguments: %s'
+ % ', '.join(kwargs.keys()))
+
+ if follow_name_owner_changes:
+ # we don't get the signals unless the Bus has a main loop
+ # XXX: using Bus internals
+ conn._require_main_loop()
+
+ self._bus = conn
+
+ if bus_name is not None:
+ _dbus_bindings.validate_bus_name(bus_name)
+ # the attribute is still called _named_service for the moment,
+ # for the benefit of telepathy-python
+ self._named_service = self._requested_bus_name = bus_name
+
+ _dbus_bindings.validate_object_path(object_path)
+ self.__dbus_object_path__ = object_path
+
+ if not follow_name_owner_changes:
+ self._named_service = conn.activate_name_owner(bus_name)
+
+ #PendingCall object for Introspect call
+ self._pending_introspect = None
+ #queue of async calls waiting on the Introspect to return
+ self._pending_introspect_queue = []
+ #dictionary mapping method names to their input signatures
+ self._introspect_method_map = {}
+
+ # must be a recursive lock because block() is called while locked,
+ # and calls the callback which re-takes the lock
+ self._introspect_lock = RLock()
+
+ if not introspect or self.__dbus_object_path__ == LOCAL_PATH:
+ self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
+ else:
+ self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
+
+ self._pending_introspect = self._Introspect()
+
+ bus_name = property(lambda self: self._named_service, None, None,
+ """The bus name to which this proxy is bound. (Read-only,
+ may change.)
+
+ If the proxy was instantiated using a unique name, this property
+ is that unique name.
+
+ If the proxy was instantiated with a well-known name and with
+ ``follow_name_owner_changes`` set false (the default), this
+ property is the unique name of the connection that owned that
+ well-known name when the proxy was instantiated, which might
+ not actually own the requested well-known name any more.
+
+ If the proxy was instantiated with a well-known name and with
+ ``follow_name_owner_changes`` set true, this property is that
+ well-known name.
+ """)
+
+ requested_bus_name = property(lambda self: self._requested_bus_name,
+ None, None,
+ """The bus name which was requested when this proxy was
+ instantiated.
+ """)
+
+ object_path = property(lambda self: self.__dbus_object_path__,
+ None, None,
+ """The object-path of this proxy.""")
+
+ # XXX: We don't currently support this because it's the signal receiver
+ # that's responsible for tracking name owner changes, but it
+ # seems a natural thing to add in future.
+ #unique_bus_name = property(lambda self: something, None, None,
+ # """The unique name of the connection to which this proxy is
+ # currently bound. (Read-only, may change.)
+ # """)
+
+ def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
+ """Arrange for the given function to be called when the given signal
+ is received.
+
+ :Parameters:
+ `signal_name` : str
+ The name of the signal
+ `handler_function` : callable
+ A function to be called when the signal is emitted by
+ the remote object. Its positional arguments will be the
+ arguments of the signal; optionally, it may be given
+ keyword arguments as described below.
+ `dbus_interface` : str
+ Optional interface with which to qualify the signal name.
+ If None (the default) the handler will be called whenever a
+ signal of the given member name is received, whatever
+ its interface.
+ :Keywords:
+ `utf8_strings` : bool
+ If True, the handler function will receive any string
+ arguments as dbus.UTF8String objects (a subclass of str
+ guaranteed to be UTF-8). If False (default) it will receive
+ any string arguments as dbus.String objects (a subclass of
+ unicode).
+ `byte_arrays` : bool
+ If True, the handler function will receive any byte-array
+ arguments as dbus.ByteArray objects (a subclass of str).
+ If False (default) it will receive any byte-array
+ arguments as a dbus.Array of dbus.Byte (subclasses of:
+ a list of ints).
+ `sender_keyword` : str
+ If not None (the default), the handler function will receive
+ the unique name of the sending endpoint as a keyword
+ argument with this name
+ `destination_keyword` : str
+ If not None (the default), the handler function will receive
+ the bus name of the destination (or None if the signal is a
+ broadcast, as is usual) as a keyword argument with this name.
+ `interface_keyword` : str
+ If not None (the default), the handler function will receive
+ the signal interface as a keyword argument with this name.
+ `member_keyword` : str
+ If not None (the default), the handler function will receive
+ the signal name as a keyword argument with this name.
+ `path_keyword` : str
+ If not None (the default), the handler function will receive
+ the object-path of the sending object as a keyword argument
+ with this name
+ `message_keyword` : str
+ If not None (the default), the handler function will receive
+ the `dbus.lowlevel.SignalMessage` as a keyword argument with
+ this name.
+ `arg...` : unicode or UTF-8 str
+ If there are additional keyword parameters of the form
+ ``arg``\ *n*, match only signals where the *n*\ th argument
+ is the value given for that keyword parameter. As of this time
+ only string arguments can be matched (in particular,
+ object paths and signatures can't).
+ """
+ return \
+ self._bus.add_signal_receiver(handler_function,
+ signal_name=signal_name,
+ dbus_interface=dbus_interface,
+ bus_name=self._named_service,
+ path=self.__dbus_object_path__,
+ **keywords)
+
+ def _Introspect(self):
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ return self._bus.call_async(self._named_service,
+ self.__dbus_object_path__,
+ INTROSPECTABLE_IFACE, 'Introspect', '', (),
+ self._introspect_reply_handler,
+ self._introspect_error_handler,
+ require_main_loop=False, **kwargs)
+
+ def _introspect_execute_queue(self):
+ # FIXME: potential to flood the bus
+ # We should make sure mainloops all have idle handlers
+ # and do one message per idle
+ for (proxy_method, args, keywords) in self._pending_introspect_queue:
+ proxy_method(*args, **keywords)
+ self._pending_introspect_queue = []
+
+ def _introspect_reply_handler(self, data):
+ self._introspect_lock.acquire()
+ try:
+ try:
+ self._introspect_method_map = process_introspection_data(data)
+ except IntrospectionParserException as e:
+ self._introspect_error_handler(e)
+ return
+
+ self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_DONE
+ self._pending_introspect = None
+ self._introspect_execute_queue()
+ finally:
+ self._introspect_lock.release()
+
+ def _introspect_error_handler(self, error):
+ logging.basicConfig()
+ _logger.error("Introspect error on %s:%s: %s.%s: %s",
+ self._named_service, self.__dbus_object_path__,
+ error.__class__.__module__, error.__class__.__name__,
+ error)
+ self._introspect_lock.acquire()
+ try:
+ _logger.debug('Executing introspect queue due to error')
+ self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT
+ self._pending_introspect = None
+ self._introspect_execute_queue()
+ finally:
+ self._introspect_lock.release()
+
+ def _introspect_block(self):
+ self._introspect_lock.acquire()
+ try:
+ if self._pending_introspect is not None:
+ self._pending_introspect.block()
+ # else someone still has a _DeferredMethod from before we
+ # finished introspection: no need to do anything special any more
+ finally:
+ self._introspect_lock.release()
+
+ def _introspect_add_to_queue(self, callback, args, kwargs):
+ self._introspect_lock.acquire()
+ try:
+ if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
+ self._pending_introspect_queue.append((callback, args, kwargs))
+ else:
+ # someone still has a _DeferredMethod from before we
+ # finished introspection
+ callback(*args, **kwargs)
+ finally:
+ self._introspect_lock.release()
+
+ def __getattr__(self, member):
+ if member.startswith('__') and member.endswith('__'):
+ raise AttributeError(member)
+ else:
+ return self.get_dbus_method(member)
+
+ def get_dbus_method(self, member, dbus_interface=None):
+ """Return a proxy method representing the given D-Bus method. The
+ returned proxy method can be called in the usual way. For instance, ::
+
+ proxy.get_dbus_method("Foo", dbus_interface='com.example.Bar')(123)
+
+ is equivalent to::
+
+ proxy.Foo(123, dbus_interface='com.example.Bar')
+
+ or even::
+
+ getattr(proxy, "Foo")(123, dbus_interface='com.example.Bar')
+
+ However, using `get_dbus_method` is the only way to call D-Bus
+ methods with certain awkward names - if the author of a service
+ implements a method called ``connect_to_signal`` or even
+ ``__getattr__``, you'll need to use `get_dbus_method` to call them.
+
+ For services which follow the D-Bus convention of CamelCaseMethodNames
+ this won't be a problem.
+ """
+
+ ret = self.ProxyMethodClass(self, self._bus,
+ self._named_service,
+ self.__dbus_object_path__, member,
+ dbus_interface)
+
+ # this can be done without taking the lock - the worst that can
+ # happen is that we accidentally return a _DeferredMethod just after
+ # finishing introspection, in which case _introspect_add_to_queue and
+ # _introspect_block will do the right thing anyway
+ if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS:
+ ret = self.DeferredMethodClass(ret, self._introspect_add_to_queue,
+ self._introspect_block)
+
+ return ret
+
+ def __repr__(self):
+ return '<ProxyObject wrapping %s %s %s at %#x>'%(
+ self._bus, self._named_service, self.__dbus_object_path__, id(self))
+ __str__ = __repr__
+
+
+class Interface(object):
+ """An interface into a remote object.
+
+ An Interface can be used to wrap ProxyObjects
+ so that calls can be routed to their correct
+ D-Bus interface.
+ """
+
+ def __init__(self, object, dbus_interface):
+ """Construct a proxy for the given interface on the given object.
+
+ :Parameters:
+ `object` : `dbus.proxies.ProxyObject` or `dbus.Interface`
+ The remote object or another of its interfaces
+ `dbus_interface` : str
+ An interface the `object` implements
+ """
+ if isinstance(object, Interface):
+ self._obj = object.proxy_object
+ else:
+ self._obj = object
+ self._dbus_interface = dbus_interface
+
+ object_path = property (lambda self: self._obj.object_path, None, None,
+ "The D-Bus object path of the underlying object")
+ __dbus_object_path__ = object_path
+ bus_name = property (lambda self: self._obj.bus_name, None, None,
+ "The bus name to which the underlying proxy object "
+ "is bound")
+ requested_bus_name = property (lambda self: self._obj.requested_bus_name,
+ None, None,
+ "The bus name which was requested when the "
+ "underlying object was created")
+ proxy_object = property (lambda self: self._obj, None, None,
+ """The underlying proxy object""")
+ dbus_interface = property (lambda self: self._dbus_interface, None, None,
+ """The D-Bus interface represented""")
+
+ def connect_to_signal(self, signal_name, handler_function,
+ dbus_interface=None, **keywords):
+ """Arrange for a function to be called when the given signal is
+ emitted.
+
+ The parameters and keyword arguments are the same as for
+ `dbus.proxies.ProxyObject.connect_to_signal`, except that if
+ `dbus_interface` is None (the default), the D-Bus interface that
+ was passed to the `Interface` constructor is used.
+ """
+ if not dbus_interface:
+ dbus_interface = self._dbus_interface
+
+ return self._obj.connect_to_signal(signal_name, handler_function,
+ dbus_interface, **keywords)
+
+ def __getattr__(self, member):
+ if member.startswith('__') and member.endswith('__'):
+ raise AttributeError(member)
+ else:
+ return self._obj.get_dbus_method(member, self._dbus_interface)
+
+ def get_dbus_method(self, member, dbus_interface=None):
+ """Return a proxy method representing the given D-Bus method.
+
+ This is the same as `dbus.proxies.ProxyObject.get_dbus_method`
+ except that if `dbus_interface` is None (the default),
+ the D-Bus interface that was passed to the `Interface` constructor
+ is used.
+ """
+ if dbus_interface is None:
+ dbus_interface = self._dbus_interface
+ return self._obj.get_dbus_method(member, dbus_interface)
+
+ def __repr__(self):
+ return '<Interface %r implementing %r at %#x>'%(
+ self._obj, self._dbus_interface, id(self))
+ __str__ = __repr__
diff --git a/dbus/server.py b/dbus/server.py
new file mode 100644
index 0000000..1988101
--- /dev/null
+++ b/dbus/server.py
@@ -0,0 +1,117 @@
+# Copyright (C) 2008 Openismus GmbH <http://openismus.com/>
+# Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ('Server', )
+__docformat__ = 'reStructuredText'
+
+from _dbus_bindings import _Server
+from dbus.connection import Connection
+
+class Server(_Server):
+ """An opaque object representing a server that listens for connections from
+ other applications.
+
+ This class is not useful to instantiate directly: you must subclass it and
+ either extend the method connection_added, or append to the
+ list on_connection_added.
+
+ :Since: 0.83
+ """
+
+ def __new__(cls, address, connection_class=Connection,
+ mainloop=None, auth_mechanisms=None):
+ """Construct a new Server.
+
+ :Parameters:
+ `address` : str
+ Listen on this address.
+ `connection_class` : type
+ When new connections come in, instantiate this subclass
+ of dbus.connection.Connection to represent them.
+ The default is Connection.
+ `mainloop` : dbus.mainloop.NativeMainLoop or None
+ The main loop with which to associate the new connections.
+ `auth_mechanisms` : sequence of str
+ Authentication mechanisms to allow. The default is to allow
+ any authentication mechanism supported by ``libdbus``.
+ """
+ return super(Server, cls).__new__(cls, address, connection_class,
+ mainloop, auth_mechanisms)
+
+ def __init__(self, *args, **kwargs):
+
+ self.__connections = {}
+
+ self.on_connection_added = []
+ """A list of callbacks to invoke when a connection is added.
+ They receive two arguments: this Server and the new Connection."""
+
+ self.on_connection_removed = []
+ """A list of callbacks to invoke when a connection becomes
+ disconnected. They receive two arguments: this Server and the removed
+ Connection."""
+
+ # This method name is hard-coded in _dbus_bindings._Server.
+ # This is not public API.
+ def _on_new_connection(self, conn):
+ conn.call_on_disconnection(self.connection_removed)
+ self.connection_added(conn)
+
+ def connection_added(self, conn):
+ """Respond to the creation of a new Connection.
+
+ This base-class implementation just invokes the callbacks in
+ the on_connection_added attribute.
+
+ :Parameters:
+ `conn` : dbus.connection.Connection
+ A D-Bus connection which has just been added.
+
+ The type of this parameter is whatever was passed
+ to the Server constructor as the ``connection_class``.
+ """
+ if self.on_connection_added:
+ for cb in self.on_connection_added:
+ cb(conn)
+
+ def connection_removed(self, conn):
+ """Respond to the disconnection of a Connection.
+
+ This base-class implementation just invokes the callbacks in
+ the on_connection_removed attribute.
+
+ :Parameters:
+ `conn` : dbus.connection.Connection
+ A D-Bus connection which has just become disconnected.
+
+ The type of this parameter is whatever was passed
+ to the Server constructor as the ``connection_class``.
+ """
+ if self.on_connection_removed:
+ for cb in self.on_connection_removed:
+ cb(conn)
+
+ address = property(_Server.get_address)
+ id = property(_Server.get_id)
+ is_connected = property(_Server.get_is_connected)
+
diff --git a/dbus/service.py b/dbus/service.py
new file mode 100644
index 0000000..6093ac5
--- /dev/null
+++ b/dbus/service.py
@@ -0,0 +1,835 @@
+# Copyright (C) 2003-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2003 David Zeuthen
+# Copyright (C) 2004 Rob Taylor
+# Copyright (C) 2005-2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+__all__ = ('BusName', 'Object', 'FallbackObject', 'method', 'signal')
+__docformat__ = 'restructuredtext'
+
+import sys
+import logging
+import threading
+import traceback
+from collections import Sequence
+
+import _dbus_bindings
+from dbus import (
+ INTROSPECTABLE_IFACE, ObjectPath, SessionBus, Signature, Struct,
+ validate_bus_name, validate_object_path)
+from dbus.decorators import method, signal
+from dbus.exceptions import (
+ DBusException, NameExistsException, UnknownMethodException)
+from dbus.lowlevel import ErrorMessage, MethodReturnMessage, MethodCallMessage
+from dbus.proxies import LOCAL_PATH
+from dbus._compat import is_py2
+
+
+_logger = logging.getLogger('dbus.service')
+
+
+class _VariantSignature(object):
+ """A fake method signature which, when iterated, yields an endless stream
+ of 'v' characters representing variants (handy with zip()).
+
+ It has no string representation.
+ """
+ def __iter__(self):
+ """Return self."""
+ return self
+
+ def __next__(self):
+ """Return 'v' whenever called."""
+ return 'v'
+
+ if is_py2:
+ next = __next__
+
+
+class BusName(object):
+ """A base class for exporting your own Named Services across the Bus.
+
+ When instantiated, objects of this class attempt to claim the given
+ well-known name on the given bus for the current process. The name is
+ released when the BusName object becomes unreferenced.
+
+ If a well-known name is requested multiple times, multiple references
+ to the same BusName object will be returned.
+
+ :Caveats:
+
+ - Assumes that named services are only ever requested using this class -
+ if you request names from the bus directly, confusion may occur.
+ - Does not handle queueing.
+ """
+ def __new__(cls, name, bus=None, allow_replacement=False , replace_existing=False, do_not_queue=False):
+ """Constructor, which may either return an existing cached object
+ or a new object.
+
+ :Parameters:
+ `name` : str
+ The well-known name to be advertised
+ `bus` : dbus.Bus
+ A Bus on which this service will be advertised.
+
+ Omitting this parameter or setting it to None has been
+ deprecated since version 0.82.1. For backwards compatibility,
+ if this is done, the global shared connection to the session
+ bus will be used.
+
+ `allow_replacement` : bool
+ If True, other processes trying to claim the same well-known
+ name will take precedence over this one.
+ `replace_existing` : bool
+ If True, this process can take over the well-known name
+ from other processes already holding it.
+ `do_not_queue` : bool
+ If True, this service will not be placed in the queue of
+ services waiting for the requested name if another service
+ already holds it.
+ """
+ validate_bus_name(name, allow_well_known=True, allow_unique=False)
+
+ # if necessary, get default bus (deprecated)
+ if bus is None:
+ import warnings
+ warnings.warn('Omitting the "bus" parameter to '
+ 'dbus.service.BusName.__init__ is deprecated',
+ DeprecationWarning, stacklevel=2)
+ bus = SessionBus()
+
+ # see if this name is already defined, return it if so
+ # FIXME: accessing internals of Bus
+ if name in bus._bus_names:
+ return bus._bus_names[name]
+
+ # otherwise register the name
+ name_flags = (
+ (allow_replacement and _dbus_bindings.NAME_FLAG_ALLOW_REPLACEMENT or 0) |
+ (replace_existing and _dbus_bindings.NAME_FLAG_REPLACE_EXISTING or 0) |
+ (do_not_queue and _dbus_bindings.NAME_FLAG_DO_NOT_QUEUE or 0))
+
+ retval = bus.request_name(name, name_flags)
+
+ # TODO: more intelligent tracking of bus name states?
+ if retval == _dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
+ pass
+ elif retval == _dbus_bindings.REQUEST_NAME_REPLY_IN_QUEUE:
+ # queueing can happen by default, maybe we should
+ # track this better or let the user know if they're
+ # queued or not?
+ pass
+ elif retval == _dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
+ raise NameExistsException(name)
+ elif retval == _dbus_bindings.REQUEST_NAME_REPLY_ALREADY_OWNER:
+ # if this is a shared bus which is being used by someone
+ # else in this process, this can happen legitimately
+ pass
+ else:
+ raise RuntimeError('requesting bus name %s returned unexpected value %s' % (name, retval))
+
+ # and create the object
+ bus_name = object.__new__(cls)
+ bus_name._bus = bus
+ bus_name._name = name
+
+ # cache instance (weak ref only)
+ # FIXME: accessing Bus internals again
+ bus._bus_names[name] = bus_name
+
+ return bus_name
+
+ # do nothing because this is called whether or not the bus name
+ # object was retrieved from the cache or created new
+ def __init__(self, *args, **keywords):
+ pass
+
+ # we can delete the low-level name here because these objects
+ # are guaranteed to exist only once for each bus name
+ def __del__(self):
+ self._bus.release_name(self._name)
+ pass
+
+ def get_bus(self):
+ """Get the Bus this Service is on"""
+ return self._bus
+
+ def get_name(self):
+ """Get the name of this service"""
+ return self._name
+
+ def __repr__(self):
+ return '<dbus.service.BusName %s on %r at %#x>' % (self._name, self._bus, id(self))
+ __str__ = __repr__
+
+
+def _method_lookup(self, method_name, dbus_interface):
+ """Walks the Python MRO of the given class to find the method to invoke.
+
+ Returns two methods, the one to call, and the one it inherits from which
+ defines its D-Bus interface name, signature, and attributes.
+ """
+ parent_method = None
+ candidate_class = None
+ successful = False
+
+ # split up the cases when we do and don't have an interface because the
+ # latter is much simpler
+ if dbus_interface:
+ # search through the class hierarchy in python MRO order
+ for cls in self.__class__.__mro__:
+ # if we haven't got a candidate class yet, and we find a class with a
+ # suitably named member, save this as a candidate class
+ if (not candidate_class and method_name in cls.__dict__):
+ if ("_dbus_is_method" in cls.__dict__[method_name].__dict__
+ and "_dbus_interface" in cls.__dict__[method_name].__dict__):
+ # however if it is annotated for a different interface
+ # than we are looking for, it cannot be a candidate
+ if cls.__dict__[method_name]._dbus_interface == dbus_interface:
+ candidate_class = cls
+ parent_method = cls.__dict__[method_name]
+ successful = True
+ break
+ else:
+ pass
+ else:
+ candidate_class = cls
+
+ # if we have a candidate class, carry on checking this and all
+ # superclasses for a method annoated as a dbus method
+ # on the correct interface
+ if (candidate_class and method_name in cls.__dict__
+ and "_dbus_is_method" in cls.__dict__[method_name].__dict__
+ and "_dbus_interface" in cls.__dict__[method_name].__dict__
+ and cls.__dict__[method_name]._dbus_interface == dbus_interface):
+ # the candidate class has a dbus method on the correct interface,
+ # or overrides a method that is, success!
+ parent_method = cls.__dict__[method_name]
+ successful = True
+ break
+
+ else:
+ # simpler version of above
+ for cls in self.__class__.__mro__:
+ if (not candidate_class and method_name in cls.__dict__):
+ candidate_class = cls
+
+ if (candidate_class and method_name in cls.__dict__
+ and "_dbus_is_method" in cls.__dict__[method_name].__dict__):
+ parent_method = cls.__dict__[method_name]
+ successful = True
+ break
+
+ if successful:
+ return (candidate_class.__dict__[method_name], parent_method)
+ else:
+ if dbus_interface:
+ raise UnknownMethodException('%s is not a valid method of interface %s' % (method_name, dbus_interface))
+ else:
+ raise UnknownMethodException('%s is not a valid method' % method_name)
+
+
+def _method_reply_return(connection, message, method_name, signature, *retval):
+ reply = MethodReturnMessage(message)
+ try:
+ reply.append(signature=signature, *retval)
+ except Exception as e:
+ logging.basicConfig()
+ if signature is None:
+ try:
+ signature = reply.guess_signature(retval) + ' (guessed)'
+ except Exception as e:
+ _logger.error('Unable to guess signature for arguments %r: '
+ '%s: %s', retval, e.__class__, e)
+ raise
+ _logger.error('Unable to append %r to message with signature %s: '
+ '%s: %s', retval, signature, e.__class__, e)
+ raise
+
+ connection.send_message(reply)
+
+
+def _method_reply_error(connection, message, exception):
+ name = getattr(exception, '_dbus_error_name', None)
+
+ if name is not None:
+ pass
+ elif getattr(exception, '__module__', '') in ('', '__main__'):
+ name = 'org.freedesktop.DBus.Python.%s' % exception.__class__.__name__
+ else:
+ name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
+
+ et, ev, etb = sys.exc_info()
+ if isinstance(exception, DBusException) and not exception.include_traceback:
+ # We don't actually want the traceback anyway
+ contents = exception.get_dbus_message()
+ elif ev is exception:
+ # The exception was actually thrown, so we can get a traceback
+ contents = ''.join(traceback.format_exception(et, ev, etb))
+ else:
+ # We don't have any traceback for it, e.g.
+ # async_err_cb(MyException('Failed to badger the mushroom'))
+ # see also https://bugs.freedesktop.org/show_bug.cgi?id=12403
+ contents = ''.join(traceback.format_exception_only(exception.__class__,
+ exception))
+ reply = ErrorMessage(message, name, contents)
+
+ connection.send_message(reply)
+
+
+class InterfaceType(type):
+ def __init__(cls, name, bases, dct):
+ # these attributes are shared between all instances of the Interface
+ # object, so this has to be a dictionary that maps class names to
+ # the per-class introspection/interface data
+ class_table = getattr(cls, '_dbus_class_table', {})
+ cls._dbus_class_table = class_table
+ interface_table = class_table[cls.__module__ + '.' + name] = {}
+
+ # merge all the name -> method tables for all the interfaces
+ # implemented by our base classes into our own
+ for b in bases:
+ base_name = b.__module__ + '.' + b.__name__
+ if getattr(b, '_dbus_class_table', False):
+ for (interface, method_table) in class_table[base_name].items():
+ our_method_table = interface_table.setdefault(interface, {})
+ our_method_table.update(method_table)
+
+ # add in all the name -> method entries for our own methods/signals
+ for func in dct.values():
+ if getattr(func, '_dbus_interface', False):
+ method_table = interface_table.setdefault(func._dbus_interface, {})
+ method_table[func.__name__] = func
+
+ super(InterfaceType, cls).__init__(name, bases, dct)
+
+ # methods are different to signals, so we have two functions... :)
+ def _reflect_on_method(cls, func):
+ args = func._dbus_args
+
+ if func._dbus_in_signature:
+ # convert signature into a tuple so length refers to number of
+ # types, not number of characters. the length is checked by
+ # the decorator to make sure it matches the length of args.
+ in_sig = tuple(Signature(func._dbus_in_signature))
+ else:
+ # magic iterator which returns as many v's as we need
+ in_sig = _VariantSignature()
+
+ if func._dbus_out_signature:
+ out_sig = Signature(func._dbus_out_signature)
+ else:
+ # its tempting to default to Signature('v'), but
+ # for methods that return nothing, providing incorrect
+ # introspection data is worse than providing none at all
+ out_sig = []
+
+ reflection_data = ' <method name="%s">\n' % (func.__name__)
+ for pair in zip(in_sig, args):
+ reflection_data += ' <arg direction="in" type="%s" name="%s" />\n' % pair
+ for type in out_sig:
+ reflection_data += ' <arg direction="out" type="%s" />\n' % type
+ reflection_data += ' </method>\n'
+
+ return reflection_data
+
+ def _reflect_on_signal(cls, func):
+ args = func._dbus_args
+
+ if func._dbus_signature:
+ # convert signature into a tuple so length refers to number of
+ # types, not number of characters
+ sig = tuple(Signature(func._dbus_signature))
+ else:
+ # magic iterator which returns as many v's as we need
+ sig = _VariantSignature()
+
+ reflection_data = ' <signal name="%s">\n' % (func.__name__)
+ for pair in zip(sig, args):
+ reflection_data = reflection_data + ' <arg type="%s" name="%s" />\n' % pair
+ reflection_data = reflection_data + ' </signal>\n'
+
+ return reflection_data
+
+
+# Define Interface as an instance of the metaclass InterfaceType, in a way
+# that is compatible across both Python 2 and Python 3.
+Interface = InterfaceType('Interface', (object,), {})
+
+
+#: A unique object used as the value of Object._object_path and
+#: Object._connection if it's actually in more than one place
+_MANY = object()
+
+class Object(Interface):
+ r"""A base class for exporting your own Objects across the Bus.
+
+ Just inherit from Object and mark exported methods with the
+ @\ `dbus.service.method` or @\ `dbus.service.signal` decorator.
+
+ Example::
+
+ class Example(dbus.service.object):
+ def __init__(self, object_path):
+ dbus.service.Object.__init__(self, dbus.SessionBus(), path)
+ self._last_input = None
+
+ @dbus.service.method(interface='com.example.Sample',
+ in_signature='v', out_signature='s')
+ def StringifyVariant(self, var):
+ self.LastInputChanged(var) # emits the signal
+ return str(var)
+
+ @dbus.service.signal(interface='com.example.Sample',
+ signature='v')
+ def LastInputChanged(self, var):
+ # run just before the signal is actually emitted
+ # just put "pass" if nothing should happen
+ self._last_input = var
+
+ @dbus.service.method(interface='com.example.Sample',
+ in_signature='', out_signature='v')
+ def GetLastInput(self):
+ return self._last_input
+ """
+
+ #: If True, this object can be made available at more than one object path.
+ #: If True but `SUPPORTS_MULTIPLE_CONNECTIONS` is False, the object may
+ #: handle more than one object path, but they must all be on the same
+ #: connection.
+ SUPPORTS_MULTIPLE_OBJECT_PATHS = False
+
+ #: If True, this object can be made available on more than one connection.
+ #: If True but `SUPPORTS_MULTIPLE_OBJECT_PATHS` is False, the object must
+ #: have the same object path on all its connections.
+ SUPPORTS_MULTIPLE_CONNECTIONS = False
+
+ def __init__(self, conn=None, object_path=None, bus_name=None):
+ """Constructor. Either conn or bus_name is required; object_path
+ is also required.
+
+ :Parameters:
+ `conn` : dbus.connection.Connection or None
+ The connection on which to export this object.
+
+ If None, use the Bus associated with the given ``bus_name``.
+ If there is no ``bus_name`` either, the object is not
+ initially available on any Connection.
+
+ For backwards compatibility, if an instance of
+ dbus.service.BusName is passed as the first parameter,
+ this is equivalent to passing its associated Bus as
+ ``conn``, and passing the BusName itself as ``bus_name``.
+
+ `object_path` : str or None
+ A D-Bus object path at which to make this Object available
+ immediately. If this is not None, a `conn` or `bus_name` must
+ also be provided.
+
+ `bus_name` : dbus.service.BusName or None
+ Represents a well-known name claimed by this process. A
+ reference to the BusName object will be held by this
+ Object, preventing the name from being released during this
+ Object's lifetime (unless it's released manually).
+ """
+ if object_path is not None:
+ validate_object_path(object_path)
+
+ if isinstance(conn, BusName):
+ # someone's using the old API; don't gratuitously break them
+ bus_name = conn
+ conn = bus_name.get_bus()
+ elif conn is None:
+ if bus_name is not None:
+ # someone's using the old API but naming arguments, probably
+ conn = bus_name.get_bus()
+
+ #: Either an object path, None or _MANY
+ self._object_path = None
+ #: Either a dbus.connection.Connection, None or _MANY
+ self._connection = None
+ #: A list of tuples (Connection, object path, False) where the False
+ #: is for future expansion (to support fallback paths)
+ self._locations = []
+ #: Lock protecting `_locations`, `_connection` and `_object_path`
+ self._locations_lock = threading.Lock()
+
+ #: True if this is a fallback object handling a whole subtree.
+ self._fallback = False
+
+ self._name = bus_name
+
+ if conn is None and object_path is not None:
+ raise TypeError('If object_path is given, either conn or bus_name '
+ 'is required')
+ if conn is not None and object_path is not None:
+ self.add_to_connection(conn, object_path)
+
+ @property
+ def __dbus_object_path__(self):
+ """The object-path at which this object is available.
+ Access raises AttributeError if there is no object path, or more than
+ one object path.
+
+ Changed in 0.82.0: AttributeError can be raised.
+ """
+ if self._object_path is _MANY:
+ raise AttributeError('Object %r has more than one object path: '
+ 'use Object.locations instead' % self)
+ elif self._object_path is None:
+ raise AttributeError('Object %r has no object path yet' % self)
+ else:
+ return self._object_path
+
+ @property
+ def connection(self):
+ """The Connection on which this object is available.
+ Access raises AttributeError if there is no Connection, or more than
+ one Connection.
+
+ Changed in 0.82.0: AttributeError can be raised.
+ """
+ if self._connection is _MANY:
+ raise AttributeError('Object %r is on more than one Connection: '
+ 'use Object.locations instead' % self)
+ elif self._connection is None:
+ raise AttributeError('Object %r has no Connection yet' % self)
+ else:
+ return self._connection
+
+ @property
+ def locations(self):
+ """An iterable over tuples representing locations at which this
+ object is available.
+
+ Each tuple has at least two items, but may have more in future
+ versions of dbus-python, so do not rely on their exact length.
+ The first two items are the dbus.connection.Connection and the object
+ path.
+
+ :Since: 0.82.0
+ """
+ return iter(self._locations)
+
+ def add_to_connection(self, connection, path):
+ """Make this object accessible via the given D-Bus connection and
+ object path.
+
+ :Parameters:
+ `connection` : dbus.connection.Connection
+ Export the object on this connection. If the class attribute
+ SUPPORTS_MULTIPLE_CONNECTIONS is False (default), this object
+ can only be made available on one connection; if the class
+ attribute is set True by a subclass, the object can be made
+ available on more than one connection.
+
+ `path` : dbus.ObjectPath or other str
+ Place the object at this object path. If the class attribute
+ SUPPORTS_MULTIPLE_OBJECT_PATHS is False (default), this object
+ can only be made available at one object path; if the class
+ attribute is set True by a subclass, the object can be made
+ available with more than one object path.
+
+ :Raises ValueError: if the object's class attributes do not allow the
+ object to be exported in the desired way.
+ :Since: 0.82.0
+ """
+ if path == LOCAL_PATH:
+ raise ValueError('Objects may not be exported on the reserved '
+ 'path %s' % LOCAL_PATH)
+
+ self._locations_lock.acquire()
+ try:
+ if (self._connection is not None and
+ self._connection is not connection and
+ not self.SUPPORTS_MULTIPLE_CONNECTIONS):
+ raise ValueError('%r is already exported on '
+ 'connection %r' % (self, self._connection))
+
+ if (self._object_path is not None and
+ not self.SUPPORTS_MULTIPLE_OBJECT_PATHS and
+ self._object_path != path):
+ raise ValueError('%r is already exported at object '
+ 'path %s' % (self, self._object_path))
+
+ connection._register_object_path(path, self._message_cb,
+ self._unregister_cb,
+ self._fallback)
+
+ if self._connection is None:
+ self._connection = connection
+ elif self._connection is not connection:
+ self._connection = _MANY
+
+ if self._object_path is None:
+ self._object_path = path
+ elif self._object_path != path:
+ self._object_path = _MANY
+
+ self._locations.append((connection, path, self._fallback))
+ finally:
+ self._locations_lock.release()
+
+ def remove_from_connection(self, connection=None, path=None):
+ """Make this object inaccessible via the given D-Bus connection
+ and object path. If no connection or path is specified,
+ the object ceases to be accessible via any connection or path.
+
+ :Parameters:
+ `connection` : dbus.connection.Connection or None
+ Only remove the object from this Connection. If None,
+ remove from all Connections on which it's exported.
+ `path` : dbus.ObjectPath or other str, or None
+ Only remove the object from this object path. If None,
+ remove from all object paths.
+ :Raises LookupError:
+ if the object was not exported on the requested connection
+ or path, or (if both are None) was not exported at all.
+ :Since: 0.81.1
+ """
+ self._locations_lock.acquire()
+ try:
+ if self._object_path is None or self._connection is None:
+ raise LookupError('%r is not exported' % self)
+
+ if connection is not None or path is not None:
+ dropped = []
+ for location in self._locations:
+ if ((connection is None or location[0] is connection) and
+ (path is None or location[1] == path)):
+ dropped.append(location)
+ else:
+ dropped = self._locations
+ self._locations = []
+
+ if not dropped:
+ raise LookupError('%r is not exported at a location matching '
+ '(%r,%r)' % (self, connection, path))
+
+ for location in dropped:
+ try:
+ location[0]._unregister_object_path(location[1])
+ except LookupError:
+ pass
+ if self._locations:
+ try:
+ self._locations.remove(location)
+ except ValueError:
+ pass
+ finally:
+ self._locations_lock.release()
+
+ def _unregister_cb(self, connection):
+ # there's not really enough information to do anything useful here
+ _logger.info('Unregistering exported object %r from some path '
+ 'on %r', self, connection)
+
+ def _message_cb(self, connection, message):
+ if not isinstance(message, MethodCallMessage):
+ return
+
+ try:
+ # lookup candidate method and parent method
+ method_name = message.get_member()
+ interface_name = message.get_interface()
+ (candidate_method, parent_method) = _method_lookup(self, method_name, interface_name)
+
+ # set up method call parameters
+ args = message.get_args_list(**parent_method._dbus_get_args_options)
+ keywords = {}
+
+ if parent_method._dbus_out_signature is not None:
+ signature = Signature(parent_method._dbus_out_signature)
+ else:
+ signature = None
+
+ # set up async callback functions
+ if parent_method._dbus_async_callbacks:
+ (return_callback, error_callback) = parent_method._dbus_async_callbacks
+ keywords[return_callback] = lambda *retval: _method_reply_return(connection, message, method_name, signature, *retval)
+ keywords[error_callback] = lambda exception: _method_reply_error(connection, message, exception)
+
+ # include the sender etc. if desired
+ if parent_method._dbus_sender_keyword:
+ keywords[parent_method._dbus_sender_keyword] = message.get_sender()
+ if parent_method._dbus_path_keyword:
+ keywords[parent_method._dbus_path_keyword] = message.get_path()
+ if parent_method._dbus_rel_path_keyword:
+ path = message.get_path()
+ rel_path = path
+ for exp in self._locations:
+ # pathological case: if we're exported in two places,
+ # one of which is a subtree of the other, then pick the
+ # subtree by preference (i.e. minimize the length of
+ # rel_path)
+ if exp[0] is connection:
+ if path == exp[1]:
+ rel_path = '/'
+ break
+ if exp[1] == '/':
+ # we already have rel_path == path at the beginning
+ continue
+ if path.startswith(exp[1] + '/'):
+ # yes we're in this exported subtree
+ suffix = path[len(exp[1]):]
+ if len(suffix) < len(rel_path):
+ rel_path = suffix
+ rel_path = ObjectPath(rel_path)
+ keywords[parent_method._dbus_rel_path_keyword] = rel_path
+
+ if parent_method._dbus_destination_keyword:
+ keywords[parent_method._dbus_destination_keyword] = message.get_destination()
+ if parent_method._dbus_message_keyword:
+ keywords[parent_method._dbus_message_keyword] = message
+ if parent_method._dbus_connection_keyword:
+ keywords[parent_method._dbus_connection_keyword] = connection
+
+ # call method
+ retval = candidate_method(self, *args, **keywords)
+
+ # we're done - the method has got callback functions to reply with
+ if parent_method._dbus_async_callbacks:
+ return
+
+ # otherwise we send the return values in a reply. if we have a
+ # signature, use it to turn the return value into a tuple as
+ # appropriate
+ if signature is not None:
+ signature_tuple = tuple(signature)
+ # if we have zero or one return values we want make a tuple
+ # for the _method_reply_return function, otherwise we need
+ # to check we're passing it a sequence
+ if len(signature_tuple) == 0:
+ if retval == None:
+ retval = ()
+ else:
+ raise TypeError('%s has an empty output signature but did not return None' %
+ method_name)
+ elif len(signature_tuple) == 1:
+ retval = (retval,)
+ else:
+ if isinstance(retval, Sequence):
+ # multi-value signature, multi-value return... proceed
+ # unchanged
+ pass
+ else:
+ raise TypeError('%s has multiple output values in signature %s but did not return a sequence' %
+ (method_name, signature))
+
+ # no signature, so just turn the return into a tuple and send it as normal
+ else:
+ if retval is None:
+ retval = ()
+ elif (isinstance(retval, tuple)
+ and not isinstance(retval, Struct)):
+ # If the return is a tuple that is not a Struct, we use it
+ # as-is on the assumption that there are multiple return
+ # values - this is the usual Python idiom. (fd.o #10174)
+ pass
+ else:
+ retval = (retval,)
+
+ _method_reply_return(connection, message, method_name, signature, *retval)
+ except Exception as exception:
+ # send error reply
+ _method_reply_error(connection, message, exception)
+
+ @method(INTROSPECTABLE_IFACE, in_signature='', out_signature='s',
+ path_keyword='object_path', connection_keyword='connection')
+ def Introspect(self, object_path, connection):
+ """Return a string of XML encoding this object's supported interfaces,
+ methods and signals.
+ """
+ reflection_data = _dbus_bindings.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ reflection_data += '<node name="%s">\n' % object_path
+
+ interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
+ for (name, funcs) in interfaces.items():
+ reflection_data += ' <interface name="%s">\n' % (name)
+
+ for func in funcs.values():
+ if getattr(func, '_dbus_is_method', False):
+ reflection_data += self.__class__._reflect_on_method(func)
+ elif getattr(func, '_dbus_is_signal', False):
+ reflection_data += self.__class__._reflect_on_signal(func)
+
+ reflection_data += ' </interface>\n'
+
+ for name in connection.list_exported_child_objects(object_path):
+ reflection_data += ' <node name="%s"/>\n' % name
+
+ reflection_data += '</node>\n'
+
+ return reflection_data
+
+ def __repr__(self):
+ where = ''
+ if (self._object_path is not _MANY
+ and self._object_path is not None):
+ where = ' at %s' % self._object_path
+ return '<%s.%s%s at %#x>' % (self.__class__.__module__,
+ self.__class__.__name__, where,
+ id(self))
+ __str__ = __repr__
+
+class FallbackObject(Object):
+ """An object that implements an entire subtree of the object-path
+ tree.
+
+ :Since: 0.82.0
+ """
+
+ SUPPORTS_MULTIPLE_OBJECT_PATHS = True
+
+ def __init__(self, conn=None, object_path=None):
+ """Constructor.
+
+ Note that the superclass' ``bus_name`` __init__ argument is not
+ supported here.
+
+ :Parameters:
+ `conn` : dbus.connection.Connection or None
+ The connection on which to export this object. If this is not
+ None, an `object_path` must also be provided.
+
+ If None, the object is not initially available on any
+ Connection.
+
+ `object_path` : str or None
+ A D-Bus object path at which to make this Object available
+ immediately. If this is not None, a `conn` must also be
+ provided.
+
+ This object will implements all object-paths in the subtree
+ starting at this object-path, except where a more specific
+ object has been added.
+ """
+ super(FallbackObject, self).__init__()
+ self._fallback = True
+
+ if conn is None:
+ if object_path is not None:
+ raise TypeError('If object_path is given, conn is required')
+ elif object_path is None:
+ raise TypeError('If conn is given, object_path is required')
+ else:
+ self.add_to_connection(conn, object_path)
diff --git a/dbus/types.py b/dbus/types.py
new file mode 100644
index 0000000..c33acff
--- /dev/null
+++ b/dbus/types.py
@@ -0,0 +1,14 @@
+__all__ = ['ObjectPath', 'ByteArray', 'Signature', 'Byte', 'Boolean',
+ 'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64',
+ 'Double', 'String', 'Array', 'Struct', 'Dictionary',
+ 'UnixFd']
+
+from _dbus_bindings import (
+ Array, Boolean, Byte, ByteArray, Dictionary, Double, Int16, Int32, Int64,
+ ObjectPath, Signature, String, Struct, UInt16, UInt32, UInt64,
+ UnixFd)
+
+from dbus._compat import is_py2
+if is_py2:
+ from _dbus_bindings import UTF8String
+ __all__.append('UTF8String')
diff --git a/dbus_bindings/abstract.c b/dbus_bindings/abstract.c
new file mode 100644
index 0000000..7bdf368
--- /dev/null
+++ b/dbus_bindings/abstract.c
@@ -0,0 +1,841 @@
+/* Subclasses of built-in Python types supporting extra D-Bus functionality.
+ *
+ * Copyright (C) 2006-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "types-internal.h"
+
+/* Dict indexed by object IDs, whose values are nonzero variant levels
+ * for immutable variable-sized D-Bus data types (_LongBase, _StrBase, Struct).
+ *
+ * This is a strange way to store them, but adding a __dict__ to the offending
+ * objects seems even more error-prone, given that their sizes are variable!
+ */
+PyObject *_dbus_py_variant_levels = NULL;
+
+long
+dbus_py_variant_level_get(PyObject *obj)
+{
+ PyObject *vl_obj;
+ PyObject *key = PyLong_FromVoidPtr(obj);
+ long variant_level;
+
+ if (!key) {
+ return -1;
+ }
+
+ vl_obj = PyDict_GetItem(_dbus_py_variant_levels, key);
+ Py_CLEAR(key);
+
+ if (!vl_obj) {
+ /* PyDict_GetItem() does not set an exception when the key is missing.
+ * In our case, it just means that there was no entry in the variant
+ * dictionary for this object. Semantically, this is equivalent to a
+ * variant level of 0.
+ */
+ return 0;
+ }
+ variant_level = NATIVEINT_ASLONG(vl_obj);
+ if (variant_level == -1 && PyErr_Occurred()) {
+ /* variant_level < 0 can never be inserted into the dictionary; see
+ * dbus_py_variant_level_set() below. The semantics of setting
+ * variant_level < 0 is to delete it from the dictionary.
+ */
+ return -1;
+ }
+ assert(variant_level >= 0);
+ return variant_level;
+}
+
+dbus_bool_t
+dbus_py_variant_level_set(PyObject *obj, long variant_level)
+{
+ /* key is the object's ID (= pointer) to avoid referencing it */
+ PyObject *key = PyLong_FromVoidPtr(obj);
+
+ if (!key) {
+ return FALSE;
+ }
+
+ if (variant_level <= 0) {
+ if (PyDict_GetItem (_dbus_py_variant_levels, key)) {
+ if (PyDict_DelItem (_dbus_py_variant_levels, key) < 0) {
+ Py_CLEAR(key);
+ return FALSE;
+ }
+ }
+ }
+ else {
+ PyObject *vl_obj = NATIVEINT_FROMLONG(variant_level);
+ if (!vl_obj) {
+ Py_CLEAR(key);
+ return FALSE;
+ }
+ if (PyDict_SetItem(_dbus_py_variant_levels, key, vl_obj) < 0) {
+ Py_CLEAR(vl_obj);
+ Py_CLEAR(key);
+ return FALSE;
+ }
+ Py_CLEAR(vl_obj);
+ }
+ Py_CLEAR(key);
+ return TRUE;
+}
+
+PyObject *
+dbus_py_variant_level_getattro(PyObject *obj, PyObject *name)
+{
+ PyObject *key, *value;
+
+#ifdef PY3
+ if (PyUnicode_CompareWithASCIIString(name, "variant_level"))
+ return PyObject_GenericGetAttr(obj, name);
+#else
+ if (PyBytes_Check(name)) {
+ Py_INCREF(name);
+ }
+ else if (PyUnicode_Check(name)) {
+ name = PyUnicode_AsEncodedString(name, NULL, NULL);
+ if (!name) {
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "attribute name must be string");
+ return NULL;
+ }
+
+ if (strcmp(PyBytes_AS_STRING(name), "variant_level")) {
+ value = PyObject_GenericGetAttr(obj, name);
+ Py_CLEAR(name);
+ return value;
+ }
+
+ Py_CLEAR(name);
+#endif /* PY3 */
+
+ key = PyLong_FromVoidPtr(obj);
+
+ if (!key) {
+ return NULL;
+ }
+
+ value = PyDict_GetItem(_dbus_py_variant_levels, key);
+ Py_CLEAR(key);
+
+ if (!value)
+ return NATIVEINT_FROMLONG(0);
+ Py_INCREF(value);
+ return value;
+}
+
+/* To be invoked by destructors. Clear the variant level without touching the
+ * exception state */
+void
+dbus_py_variant_level_clear(PyObject *self)
+{
+ PyObject *et, *ev, *etb;
+
+ /* avoid clobbering any pending exception */
+ PyErr_Fetch(&et, &ev, &etb);
+ if (!dbus_py_variant_level_set(self, 0)) {
+ /* should never happen */
+ PyErr_WriteUnraisable(self);
+ }
+ PyErr_Restore(et, ev, etb);
+}
+
+#ifndef PY3
+/* Support code for int subclasses. ================================== */
+
+PyDoc_STRVAR(DBusPythonInt_tp_doc,\
+"Base class for int subclasses with a ``variant_level`` attribute.\n"
+"Do not rely on the existence of this class outside dbus-python.\n"
+);
+
+static PyMemberDef DBusPythonInt_tp_members[] = {
+ {"variant_level", T_LONG, offsetof(DBusPyIntBase, variant_level),
+ READONLY,
+ "The number of nested variants wrapping the real data. "
+ "0 if not in a variant."},
+ {NULL},
+};
+
+static PyObject *
+DBusPythonInt_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self;
+ long variantness = 0;
+ static char *argnames[] = {"variant_level", NULL};
+
+ if (PyTuple_Size(args) > 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "__new__ takes at most one positional parameter");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|l:__new__", argnames,
+ &variantness)) return NULL;
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+
+ self = (PyInt_Type.tp_new)(cls, args, NULL);
+ if (self) {
+ ((DBusPyIntBase *)self)->variant_level = variantness;
+ }
+ return self;
+}
+
+static PyObject *
+DBusPythonInt_tp_repr(PyObject *self)
+{
+ PyObject *parent_repr = (PyInt_Type.tp_repr)(self);
+ long variant_level = ((DBusPyIntBase *)self)->variant_level;
+ PyObject *my_repr;
+
+ if (!parent_repr) return NULL;
+ if (variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V)", Py_TYPE(self)->tp_name,
+ REPRV(parent_repr));
+ }
+ /* whether my_repr is NULL or not: */
+ Py_CLEAR(parent_repr);
+ return my_repr;
+}
+
+PyTypeObject DBusPyIntBase_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "_dbus_bindings._IntBase",
+ sizeof(DBusPyIntBase),
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ DBusPythonInt_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ DBusPythonInt_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ DBusPythonInt_tp_members, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&PyInt_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ PyType_GenericAlloc, /* tp_alloc */
+ DBusPythonInt_tp_new, /* tp_new */
+ PyObject_Del, /* tp_free */
+};
+#endif /* !PY3 */
+
+/* Support code for float subclasses. ================================ */
+
+/* There's only one subclass at the moment (Double) but these are factored
+out to make room for Float later. (Float is implemented and #if'd out) */
+
+PyDoc_STRVAR(DBusPythonFloat_tp_doc,\
+"Base class for float subclasses with a ``variant_level`` attribute.\n"
+"Do not rely on the existence of this class outside dbus-python.\n"
+);
+
+static PyMemberDef DBusPythonFloat_tp_members[] = {
+ {"variant_level", T_LONG, offsetof(DBusPyFloatBase, variant_level),
+ READONLY,
+ "The number of nested variants wrapping the real data. "
+ "0 if not in a variant."},
+ {NULL},
+};
+
+static PyObject *
+DBusPythonFloat_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self;
+ long variantness = 0;
+ static char *argnames[] = {"variant_level", NULL};
+
+ if (PyTuple_Size(args) > 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "__new__ takes at most one positional parameter");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|l:__new__", argnames,
+ &variantness)) return NULL;
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+
+ self = (PyFloat_Type.tp_new)(cls, args, NULL);
+ if (self) {
+ ((DBusPyFloatBase *)self)->variant_level = variantness;
+ }
+ return self;
+}
+
+static PyObject *
+DBusPythonFloat_tp_repr(PyObject *self)
+{
+ PyObject *parent_repr = (PyFloat_Type.tp_repr)(self);
+ long variant_level = ((DBusPyFloatBase *)self)->variant_level;
+ PyObject *my_repr;
+
+ if (!parent_repr) return NULL;
+ if (variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V)", Py_TYPE(self)->tp_name,
+ REPRV(parent_repr));
+ }
+ /* whether my_repr is NULL or not: */
+ Py_CLEAR(parent_repr);
+ return my_repr;
+}
+
+PyTypeObject DBusPyFloatBase_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "_dbus_bindings._FloatBase",
+ sizeof(DBusPyFloatBase),
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ DBusPythonFloat_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ DBusPythonFloat_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ DBusPythonFloat_tp_members, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&PyFloat_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ DBusPythonFloat_tp_new, /* tp_new */
+};
+
+#ifdef PY3
+/* Support code for bytes subclasses ================================== */
+
+PyDoc_STRVAR(DBusPythonBytes_tp_doc,\
+"Base class for bytes subclasses with a ``variant_level`` attribute.\n"
+"Do not rely on the existence of this class outside dbus-python.\n"
+);
+
+static PyObject *
+DBusPythonBytes_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self;
+ long variantness = 0;
+ static char *argnames[] = {"variant_level", NULL};
+
+ if (PyTuple_Size(args) > 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "__new__ takes at most one positional parameter");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|l:__new__", argnames,
+ &variantness))
+ return NULL;
+
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+
+ self = (PyBytes_Type.tp_new)(cls, args, NULL);
+ if (self) {
+ if (!dbus_py_variant_level_set(self, variantness)) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ }
+ return self;
+}
+
+static PyObject *
+DBusPythonBytes_tp_repr(PyObject *self)
+{
+ PyObject *parent_repr = (PyBytes_Type.tp_repr)(self);
+ PyObject *vl_obj;
+ PyObject *my_repr;
+ long variant_level;
+
+ if (!parent_repr) return NULL;
+ vl_obj = PyObject_GetAttr(self, dbus_py_variant_level_const);
+ if (!vl_obj) {
+ Py_CLEAR(parent_repr);
+ return NULL;
+ }
+ variant_level = NATIVEINT_ASLONG(vl_obj);
+ Py_CLEAR(vl_obj);
+ if (variant_level == -1 && PyErr_Occurred()) {
+ Py_CLEAR(parent_repr);
+ return NULL;
+ }
+ if (variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V)", Py_TYPE(self)->tp_name,
+ REPRV(parent_repr));
+ }
+ /* whether my_repr is NULL or not: */
+ Py_CLEAR(parent_repr);
+ return my_repr;
+}
+
+static void
+DBusPyBytesBase_tp_dealloc(PyObject *self)
+{
+ dbus_py_variant_level_clear(self);
+ (PyBytes_Type.tp_dealloc)(self);
+}
+
+PyTypeObject DBusPyBytesBase_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "_dbus_bindings._BytesBase",
+ 0,
+ 0,
+ DBusPyBytesBase_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ DBusPythonBytes_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ dbus_py_variant_level_getattro, /* tp_getattro */
+ dbus_py_immutable_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ DBusPythonBytes_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&PyBytes_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ DBusPythonBytes_tp_new, /* tp_new */
+};
+#endif /* PY3 */
+
+/* Support code for str subclasses ================================== */
+
+PyDoc_STRVAR(DBusPythonString_tp_doc,\
+"Base class for str subclasses with a ``variant_level`` attribute.\n"
+"Do not rely on the existence of this class outside dbus-python.\n"
+);
+
+static PyObject *
+DBusPythonString_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self;
+ long variantness = 0;
+ static char *argnames[] = {"variant_level", NULL};
+
+ if (PyTuple_Size(args) > 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "__new__ takes at most one positional parameter");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|l:__new__", argnames,
+ &variantness)) return NULL;
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+
+ self = (NATIVESTR_TYPE.tp_new)(cls, args, NULL);
+ if (self) {
+ if (!dbus_py_variant_level_set(self, variantness)) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ }
+ return self;
+}
+
+static PyObject *
+DBusPythonString_tp_repr(PyObject *self)
+{
+ PyObject *parent_repr = (NATIVESTR_TYPE.tp_repr)(self);
+ PyObject *vl_obj;
+ PyObject *my_repr;
+ long variant_level;
+
+ if (!parent_repr) return NULL;
+ vl_obj = PyObject_GetAttr(self, dbus_py_variant_level_const);
+ if (!vl_obj) {
+ Py_CLEAR(parent_repr);
+ return NULL;
+ }
+ variant_level = NATIVEINT_ASLONG(vl_obj);
+ Py_CLEAR(vl_obj);
+ if (variant_level == -1 && PyErr_Occurred()) {
+ Py_CLEAR(parent_repr);
+ return NULL;
+ }
+
+ if (variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V)", Py_TYPE(self)->tp_name,
+ REPRV(parent_repr));
+ }
+ /* whether my_repr is NULL or not: */
+ Py_CLEAR(parent_repr);
+ return my_repr;
+}
+
+static void
+DBusPyStrBase_tp_dealloc(PyObject *self)
+{
+ dbus_py_variant_level_clear(self);
+ (NATIVESTR_TYPE.tp_dealloc)(self);
+}
+
+PyTypeObject DBusPyStrBase_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "_dbus_bindings._StrBase",
+ 0,
+ 0,
+ DBusPyStrBase_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ DBusPythonString_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ dbus_py_variant_level_getattro, /* tp_getattro */
+ dbus_py_immutable_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ DBusPythonString_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&NATIVESTR_TYPE), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ DBusPythonString_tp_new, /* tp_new */
+};
+
+/* Support code for long subclasses ================================= */
+
+PyDoc_STRVAR(DBusPythonLong_tp_doc,\
+"Base class for ``long`` subclasses with a ``variant_level`` attribute.\n"
+"Do not rely on the existence of this class outside dbus-python.\n"
+);
+
+static PyObject *
+DBusPythonLong_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self;
+ long variantness = 0;
+ static char *argnames[] = {"variant_level", NULL};
+
+ if (PyTuple_Size(args) > 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "__new__ takes at most one positional parameter");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|l:__new__", argnames,
+ &variantness)) return NULL;
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+
+ self = (PyLong_Type.tp_new)(cls, args, NULL);
+ if (self) {
+ if (!dbus_py_variant_level_set(self, variantness)) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ }
+ return self;
+}
+
+static PyObject *
+DBusPythonLong_tp_repr(PyObject *self)
+{
+ PyObject *parent_repr = (PyLong_Type.tp_repr)(self);
+ PyObject *vl_obj;
+ PyObject *my_repr;
+ long variant_level;
+
+ if (!parent_repr) return NULL;
+ vl_obj = PyObject_GetAttr(self, dbus_py_variant_level_const);
+ if (!vl_obj) {
+ Py_CLEAR(parent_repr);
+ return NULL;
+ }
+ variant_level = NATIVEINT_ASLONG(vl_obj);
+ Py_CLEAR(vl_obj);
+ if (variant_level < 0 && PyErr_Occurred()) {
+ Py_CLEAR(parent_repr);
+ return NULL;
+ }
+
+ if (variant_level) {
+ my_repr = PyUnicode_FromFormat("%s(%V, variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V)", Py_TYPE(self)->tp_name,
+ REPRV(parent_repr));
+ }
+ /* whether my_repr is NULL or not: */
+ Py_CLEAR(parent_repr);
+ return my_repr;
+}
+
+static void
+DBusPyLongBase_tp_dealloc(PyObject *self)
+{
+ dbus_py_variant_level_clear(self);
+ (PyLong_Type.tp_dealloc)(self);
+}
+
+PyTypeObject DBusPyLongBase_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "_dbus_bindings._LongBase",
+ 0,
+ 0,
+ DBusPyLongBase_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ DBusPythonLong_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ dbus_py_variant_level_getattro, /* tp_getattro */
+ dbus_py_immutable_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ DBusPythonLong_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&PyLong_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ DBusPythonLong_tp_new, /* tp_new */
+};
+
+PyObject *dbus_py_variant_level_const = NULL;
+PyObject *dbus_py_signature_const = NULL;
+PyObject *dbus_py__dbus_object_path__const = NULL;
+
+#ifdef PY3
+#define INTERN (PyUnicode_InternFromString)
+#else
+/* Neither Python 2.6 nor 2.7 define the expected PyBytes_InternFromString
+ * alias in bytesobject.h.
+ */
+#define INTERN (PyString_InternFromString)
+#endif
+
+dbus_bool_t
+dbus_py_init_abstract(void)
+{
+ _dbus_py_variant_levels = PyDict_New();
+ if (!_dbus_py_variant_levels) return 0;
+
+ dbus_py__dbus_object_path__const = INTERN("__dbus_object_path__");
+ if (!dbus_py__dbus_object_path__const) return 0;
+
+ dbus_py_variant_level_const = INTERN("variant_level");
+ if (!dbus_py_variant_level_const) return 0;
+
+ dbus_py_signature_const = INTERN("signature");
+ if (!dbus_py_signature_const) return 0;
+
+#ifdef PY3
+ DBusPyBytesBase_Type.tp_base = &PyBytes_Type;
+ if (PyType_Ready(&DBusPyBytesBase_Type) < 0) return 0;
+ DBusPyBytesBase_Type.tp_print = NULL;
+#else
+ DBusPyIntBase_Type.tp_base = &PyInt_Type;
+ if (PyType_Ready(&DBusPyIntBase_Type) < 0) return 0;
+ /* disable the tp_print copied from PyInt_Type, so tp_repr gets called as
+ desired */
+ DBusPyIntBase_Type.tp_print = NULL;
+#endif
+
+ DBusPyFloatBase_Type.tp_base = &PyFloat_Type;
+ if (PyType_Ready(&DBusPyFloatBase_Type) < 0) return 0;
+ DBusPyFloatBase_Type.tp_print = NULL;
+
+ DBusPyLongBase_Type.tp_base = &PyLong_Type;
+ if (PyType_Ready(&DBusPyLongBase_Type) < 0) return 0;
+ DBusPyLongBase_Type.tp_print = NULL;
+
+ DBusPyStrBase_Type.tp_base = &NATIVESTR_TYPE;
+ if (PyType_Ready(&DBusPyStrBase_Type) < 0) return 0;
+ DBusPyStrBase_Type.tp_print = NULL;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_abstract_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+#ifdef PY3
+ Py_INCREF(&DBusPyBytesBase_Type);
+ if (PyModule_AddObject(this_module, "_BytesBase",
+ (PyObject *)&DBusPyBytesBase_Type) < 0) return 0;
+#else
+ Py_INCREF(&DBusPyIntBase_Type);
+ if (PyModule_AddObject(this_module, "_IntBase",
+ (PyObject *)&DBusPyIntBase_Type) < 0) return 0;
+#endif
+ Py_INCREF(&DBusPyLongBase_Type);
+ Py_INCREF(&DBusPyStrBase_Type);
+ Py_INCREF(&DBusPyFloatBase_Type);
+ if (PyModule_AddObject(this_module, "_LongBase",
+ (PyObject *)&DBusPyLongBase_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "_StrBase",
+ (PyObject *)&DBusPyStrBase_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "_FloatBase",
+ (PyObject *)&DBusPyFloatBase_Type) < 0) return 0;
+
+ return 1;
+}
diff --git a/dbus_bindings/bus.c b/dbus_bindings/bus.c
new file mode 100644
index 0000000..ef257f3
--- /dev/null
+++ b/dbus_bindings/bus.c
@@ -0,0 +1,199 @@
+/* Implementation of Bus, a subtype of Connection.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include "conn-internal.h"
+
+PyObject *
+DBusPyConnection_NewForBus(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *first = NULL, *mainloop = NULL;
+ DBusConnection *conn;
+ DBusError error;
+ Connection *self;
+ static char *argnames[] = {"address_or_type", "mainloop", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OO", argnames,
+ &first, &mainloop)) {
+ return NULL;
+ }
+
+ dbus_error_init(&error);
+
+ if (first &&
+#ifdef PY3
+ PyUnicode_Check(first)
+#else
+ PyBytes_Check(first)
+#endif
+ )
+ {
+ dbus_bool_t ret;
+
+ /* It's a custom address. First connect to it, then register. */
+
+ self = (Connection *)(DBusPyConnection_Type.tp_new)(cls, args, kwargs);
+ if (!self) return NULL;
+ TRACE(self);
+
+ Py_BEGIN_ALLOW_THREADS
+ ret = dbus_bus_register(self->conn, &error);
+ Py_END_ALLOW_THREADS
+ if (!ret) {
+ DBusPyException_ConsumeError(&error);
+ Py_CLEAR(self);
+ return NULL;
+ }
+
+ return (PyObject *)self;
+ }
+ else if (!first || INTORLONG_CHECK(first))
+ {
+ long type;
+ PyObject *libdbusconn;
+ PyObject *new_args;
+ PyObject *new_kwargs;
+
+ /* If the first argument isn't a string, it must be an integer
+ representing one of the well-known bus types. The default is
+ DBUS_BUS_SESSION. */
+
+ if (first) {
+ /* on Python 2 this accepts either int or long */
+ type = PyLong_AsLong(first);
+ if (type == -1 && PyErr_Occurred())
+ return NULL;
+
+ if (type != DBUS_BUS_SESSION && type != DBUS_BUS_SYSTEM
+ && type != DBUS_BUS_STARTER) {
+ PyErr_Format(PyExc_ValueError, "Unknown bus type %ld", type);
+ return NULL;
+ }
+ }
+ else {
+ type = DBUS_BUS_SESSION;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ conn = dbus_bus_get_private(type, &error);
+ Py_END_ALLOW_THREADS
+
+ if (!conn) {
+ DBusPyException_ConsumeError(&error);
+ return NULL;
+ }
+
+ libdbusconn = DBusPyLibDBusConnection_New (conn);
+ dbus_connection_unref (conn);
+
+ if (!libdbusconn)
+ return NULL;
+
+ new_args = PyTuple_Pack(2, libdbusconn, mainloop ? mainloop : Py_None);
+ Py_CLEAR(libdbusconn);
+
+ if (!new_args) {
+ return NULL;
+ }
+
+ new_kwargs = PyDict_New();
+
+ if (!new_kwargs) {
+ Py_CLEAR(new_args);
+ return NULL;
+ }
+
+ self = (Connection *)(DBusPyConnection_Type.tp_new)(cls, new_args,
+ new_kwargs);
+ Py_CLEAR(new_args);
+ Py_CLEAR(new_kwargs);
+
+ return (PyObject *)self; /* whether NULL or not */
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "A string address or an integer "
+ "bus type is required");
+ return NULL;
+ }
+}
+
+PyObject *
+DBusPyConnection_GetUniqueName(Connection *self, PyObject *args UNUSED)
+{
+ const char *name;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ Py_BEGIN_ALLOW_THREADS
+ name = dbus_bus_get_unique_name(self->conn);
+ Py_END_ALLOW_THREADS
+ if (!name) {
+ return DBusPyException_SetString("This connection has no unique name "
+ "yet");
+ }
+ return NATIVESTR_FROMSTR(name);
+}
+
+PyObject *
+DBusPyConnection_SetUniqueName(Connection *self, PyObject *args)
+{
+ const char *old_name, *new_name;
+
+ if (!PyArg_ParseTuple(args, "s:set_unique_name", &new_name)) {
+ return NULL;
+ }
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+
+ /* libdbus will assert if we try to set a unique name when there's
+ * already one, so we need to make sure that can't happen.
+ * (Thanks, libdbus.)
+ *
+ * The things that can set the unique name are:
+ * - this function - but we don't release the GIL, so only one instance of
+ * this function can run
+ * - dbus_bus_get - but this is only called in a __new__ or __new__-like
+ * function, so the new connection isn't available to other code yet
+ * and this function can't be called on it
+ * - dbus_bus_register - same as dbus_bus_get
+ *
+ * Code outside dbus-python shouldn't be setting the unique name, because
+ * we're using a private connection; we have to trust the authors
+ * of mainloop bindings not to do silly things like that.
+ */
+ old_name = dbus_bus_get_unique_name(self->conn);
+ if (old_name != NULL) {
+ PyErr_Format(PyExc_ValueError, "This connection already has a "
+ "unique name: '%s'", old_name);
+ return NULL;
+ }
+ dbus_bus_set_unique_name(self->conn, new_name);
+
+ Py_RETURN_NONE;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/bytes.c b/dbus_bindings/bytes.c
new file mode 100644
index 0000000..873a2fa
--- /dev/null
+++ b/dbus_bindings/bytes.c
@@ -0,0 +1,304 @@
+/* D-Bus Byte and ByteArray types.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "types-internal.h"
+
+#ifdef PY3
+#define DBUS_PY_BYTE_BASE (DBusPyLongBase_Type)
+#else
+#define DBUS_PY_BYTE_BASE (DBusPyIntBase_Type)
+#endif
+
+PyDoc_STRVAR(Byte_tp_doc,
+"dbus.Byte(integer or bytes of length 1[, variant_level])\n"
+"\n"
+"An unsigned byte: a subtype of int, with range restricted to [0, 255].\n"
+"\n"
+"A Byte `b` may be converted to a ``str`` of length 1 via\n"
+"``str(b) == chr(b)`` (Python 2) or to a ``bytes`` of length 1\n"
+"via ``bytes([b])`` (Python 3).\n"
+"\n"
+"Most of the time you don't want to use this class - it mainly exists\n"
+"for symmetry with the other D-Bus types. See `dbus.ByteArray` for a\n"
+"better way to handle arrays of Byte.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a byte, this is represented in Python by a\n"
+" Byte with variant_level==2.\n"
+);
+
+static PyObject *
+Byte_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *obj;
+ PyObject *tuple;
+ long variantness = 0;
+ static char *argnames[] = {"variant_level", NULL};
+
+ if (PyTuple_Size(args) > 1) {
+ PyErr_SetString(PyExc_TypeError, "Byte constructor takes no more "
+ "than one positional argument");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|l:__new__", argnames,
+ &variantness)) return NULL;
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+
+ /* obj is a borrowed reference. It gets turned into an owned reference on
+ * the good-path of the if-statements below.
+ */
+ obj = PyTuple_GetItem(args, 0);
+
+ if (PyBytes_Check(obj)) {
+ /* string of length 1, we hope */
+ if (PyBytes_GET_SIZE(obj) != 1) {
+ goto bad_arg;
+ }
+ obj = NATIVEINT_FROMLONG((unsigned char)(PyBytes_AS_STRING(obj)[0]));
+ if (!obj)
+ goto bad_arg;
+ }
+ else if (INTORLONG_CHECK(obj)) {
+ /* on Python 2 this accepts either int or long */
+ long i = PyLong_AsLong(obj);
+ long my_variant_level;
+
+ if (i == -1 && PyErr_Occurred())
+ goto bad_arg;
+
+#ifdef PY3
+ my_variant_level = dbus_py_variant_level_get(obj);
+ if (my_variant_level < 0)
+ return NULL;
+#else
+ my_variant_level = ((DBusPyIntBase *)obj)->variant_level;
+#endif
+ if (Py_TYPE(obj) == cls && my_variant_level == variantness) {
+ Py_INCREF(obj);
+ return obj;
+ }
+ if (i < 0 || i > 255) goto bad_range;
+ /* else make it a new reference */
+ Py_INCREF(obj);
+ }
+ else {
+ goto bad_arg;
+ }
+
+ /* The tuple steals the reference to obj. */
+ tuple = Py_BuildValue("(N)", obj);
+ if (!tuple) return NULL;
+
+ obj = DBUS_PY_BYTE_BASE.tp_new(cls, tuple, kwargs);
+ Py_CLEAR(tuple);
+ return obj;
+
+bad_arg:
+ PyErr_SetString(PyExc_TypeError, "Expected a bytes or str of length 1, "
+ "or an int in the range 0-255");
+ return NULL;
+bad_range:
+ PyErr_SetString(PyExc_ValueError, "Integer outside range 0-255");
+ return NULL;
+}
+
+static PyObject *
+Byte_tp_str(PyObject *self)
+{
+ long i = NATIVEINT_ASLONG(self);
+ unsigned char str[2] = { 0, 0 };
+
+ if (i == -1 && PyErr_Occurred())
+ return NULL;
+ if (i < 0 || i > 255) {
+ PyErr_SetString(PyExc_RuntimeError, "Integer outside range 0-255");
+ return NULL;
+ }
+
+ str[0] = (unsigned char)i;
+ return PyUnicode_FromStringAndSize((char *)str, 1);
+}
+
+PyTypeObject DBusPyByte_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Byte",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ Byte_tp_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Byte_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBUS_PY_BYTE_BASE), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Byte_new, /* tp_new */
+};
+
+#ifdef PY3
+#define DBUS_PY_BYTEARRAY_BASE (DBusPyBytesBase_Type)
+#else
+#define DBUS_PY_BYTEARRAY_BASE (DBusPyStrBase_Type)
+#endif
+
+PyDoc_STRVAR(ByteArray_tp_doc,
+"ByteArray(str)\n"
+"\n"
+"ByteArray is a subtype of :py:class:`bytes` (an alias for\n"
+":py:class:`str` in Python 2 but a distinct type in Python 3)\n"
+"which can be used when you want an\n"
+"efficient immutable representation of a D-Bus byte array (signature ``ay``).\n"
+"\n"
+"By default, when byte arrays are converted from D-Bus to Python, they\n"
+"come out as a `dbus.Array` of `dbus.Byte`. This is just for symmetry with\n"
+"the other D-Bus types - in practice, what you usually want is the byte\n"
+"array represented as a string, using this class. To get this, pass the\n"
+"``byte_arrays=True`` keyword argument to any of these methods:\n"
+"\n"
+"* any D-Bus method proxy, or ``connect_to_signal``, on the objects returned\n"
+" by `Bus.get_object`\n"
+"* any D-Bus method on a `dbus.Interface`\n"
+"* `dbus.Interface.connect_to_signal`\n"
+"* `Bus.add_signal_receiver`\n"
+"\n"
+"Import via::\n"
+"\n"
+" from dbus import ByteArray\n"
+"\n"
+"Constructor::\n"
+"\n"
+" ByteArray(str)\n"
+);
+
+PyTypeObject DBusPyByteArray_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.ByteArray",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ ByteArray_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBUS_PY_BYTEARRAY_BASE), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+dbus_bool_t
+dbus_py_init_byte_types(void)
+{
+ DBusPyByte_Type.tp_base = &DBUS_PY_BYTE_BASE;
+ if (PyType_Ready(&DBusPyByte_Type) < 0) return 0;
+ DBusPyByte_Type.tp_print = NULL;
+
+ DBusPyByteArray_Type.tp_base = &DBUS_PY_BYTEARRAY_BASE;
+ if (PyType_Ready(&DBusPyByteArray_Type) < 0) return 0;
+ DBusPyByteArray_Type.tp_print = NULL;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_byte_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF(&DBusPyByte_Type);
+ if (PyModule_AddObject(this_module, "Byte",
+ (PyObject *)&DBusPyByte_Type) < 0) return 0;
+ Py_INCREF(&DBusPyByteArray_Type);
+ if (PyModule_AddObject(this_module, "ByteArray",
+ (PyObject *)&DBusPyByteArray_Type) < 0) return 0;
+
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/compat-internal.h b/dbus_bindings/compat-internal.h
new file mode 100644
index 0000000..9b9dce4
--- /dev/null
+++ b/dbus_bindings/compat-internal.h
@@ -0,0 +1,32 @@
+/* Old D-Bus compatibility: implementation internals
+ *
+ * Copyright © 2006-2011 Collabora Ltd.
+ * Copyright © 2011 Nokia Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DBUS_BINDINGS_COMPAT_INTERNAL_H
+#define DBUS_BINDINGS_COMPAT_INTERNAL_H
+
+#include "dbus_bindings-internal.h"
+
+#endif
diff --git a/dbus_bindings/conn-internal.h b/dbus_bindings/conn-internal.h
new file mode 100644
index 0000000..f4c7a80
--- /dev/null
+++ b/dbus_bindings/conn-internal.h
@@ -0,0 +1,67 @@
+/* _dbus_bindings internal API. For use within _dbus_bindings only.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DBUS_BINDINGS_CONN_H
+#define DBUS_BINDINGS_CONN_H
+
+#include "dbus_bindings-internal.h"
+
+typedef struct {
+ PyObject_HEAD
+ DBusConnection *conn;
+ /* A list of filter callbacks. */
+ PyObject *filters;
+ /* A dict mapping object paths to one of:
+ * - tuples (unregister_callback or None, message_callback)
+ * - None (meaning unregistration from libdbus is in progress and nobody
+ * should touch this entry til we're finished)
+ */
+ PyObject *object_paths;
+
+ /* Weak-references list to make Connections weakly referenceable */
+ PyObject *weaklist;
+
+ dbus_bool_t has_mainloop;
+} Connection;
+
+typedef struct {
+ PyObject_HEAD
+ DBusConnection *conn;
+} DBusPyLibDBusConnection;
+
+extern struct PyMethodDef DBusPyConnection_tp_methods[];
+extern DBusHandlerResult DBusPyConnection_HandleMessage(Connection *,
+ PyObject *,
+ PyObject *);
+extern PyObject *DBusPyConnection_ExistingFromDBusConnection(DBusConnection *);
+extern PyObject *DBusPyConnection_GetObjectPathHandlers(PyObject *self,
+ PyObject *path);
+
+extern PyObject *DBusPyConnection_NewForBus(PyTypeObject *cls, PyObject *args,
+ PyObject *kwargs);
+extern PyObject *DBusPyConnection_SetUniqueName(Connection *, PyObject *);
+extern PyObject *DBusPyConnection_GetUniqueName(Connection *, PyObject *);
+
+#endif
diff --git a/dbus_bindings/conn-methods.c b/dbus_bindings/conn-methods.c
new file mode 100644
index 0000000..424fdc4
--- /dev/null
+++ b/dbus_bindings/conn-methods.c
@@ -0,0 +1,1068 @@
+/* Implementation of normal Python-accessible methods on the _dbus_bindings
+ * Connection type; separated out to keep the file size manageable.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include "conn-internal.h"
+
+static void
+_object_path_unregister(DBusConnection *conn, void *user_data)
+{
+ PyGILState_STATE gil = PyGILState_Ensure();
+ PyObject *tuple = NULL;
+ Connection *conn_obj = NULL;
+ PyObject *callable;
+
+ conn_obj = (Connection *)DBusPyConnection_ExistingFromDBusConnection(conn);
+ if (!conn_obj) goto out;
+ TRACE(conn_obj);
+
+ DBG("Connection at %p unregistering object path %s",
+ conn_obj, PyBytes_AS_STRING((PyObject *)user_data));
+ tuple = DBusPyConnection_GetObjectPathHandlers(
+ (PyObject *)conn_obj, (PyObject *)user_data);
+ if (!tuple) goto out;
+ if (tuple == Py_None) goto out;
+
+ DBG("%s", "... yes we have handlers for that object path");
+
+ /* 0'th item is the unregisterer (if that's a word) */
+ callable = PyTuple_GetItem(tuple, 0);
+ if (callable && callable != Py_None) {
+ DBG("%s", "... and we even have an unregisterer");
+ /* any return from the unregisterer is ignored */
+ Py_XDECREF(PyObject_CallFunctionObjArgs(callable, conn_obj, NULL));
+ }
+out:
+ Py_CLEAR(conn_obj);
+ Py_CLEAR(tuple);
+ /* the user_data (a Python str) is no longer ref'd by the DBusConnection */
+ Py_CLEAR(user_data);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ PyGILState_Release(gil);
+}
+
+static DBusHandlerResult
+_object_path_message(DBusConnection *conn, DBusMessage *message,
+ void *user_data)
+{
+ DBusHandlerResult ret;
+ PyGILState_STATE gil = PyGILState_Ensure();
+ Connection *conn_obj = NULL;
+ PyObject *tuple = NULL;
+ PyObject *msg_obj;
+ PyObject *callable; /* borrowed */
+
+ dbus_message_ref(message);
+ msg_obj = DBusPyMessage_ConsumeDBusMessage(message);
+ if (!msg_obj) {
+ ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto out;
+ }
+
+ conn_obj = (Connection *)DBusPyConnection_ExistingFromDBusConnection(conn);
+ if (!conn_obj) {
+ ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto out;
+ }
+ TRACE(conn_obj);
+
+ DBG("Connection at %p messaging object path %s",
+ conn_obj, PyBytes_AS_STRING((PyObject *)user_data));
+ DBG_DUMP_MESSAGE(message);
+ tuple = DBusPyConnection_GetObjectPathHandlers(
+ (PyObject *)conn_obj, (PyObject *)user_data);
+ if (!tuple || tuple == Py_None) {
+ ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto out;
+ }
+
+ DBG("%s", "... yes we have handlers for that object path");
+
+ /* 1st item (0-based) is the message callback */
+ callable = PyTuple_GetItem(tuple, 1);
+ if (!callable) {
+ DBG("%s", "... error getting message handler from tuple");
+ ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ else if (callable == Py_None) {
+ /* there was actually no handler after all */
+ DBG("%s", "... but those handlers don't do messages");
+ ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ else {
+ DBG("%s", "... and we have a message handler for that object path");
+ ret = DBusPyConnection_HandleMessage(conn_obj, msg_obj, callable);
+ }
+
+out:
+ Py_CLEAR(msg_obj);
+ Py_CLEAR(conn_obj);
+ Py_CLEAR(tuple);
+ if (PyErr_Occurred()) {
+ PyErr_Print();
+ }
+ PyGILState_Release(gil);
+ return ret;
+}
+
+static const DBusObjectPathVTable _object_path_vtable = {
+ _object_path_unregister,
+ _object_path_message,
+};
+
+static DBusHandlerResult
+_filter_message(DBusConnection *conn, DBusMessage *message, void *user_data)
+{
+ DBusHandlerResult ret;
+ PyGILState_STATE gil = PyGILState_Ensure();
+ Connection *conn_obj = NULL;
+ PyObject *callable = NULL;
+ PyObject *msg_obj;
+#ifndef DBUS_PYTHON_DISABLE_CHECKS
+ Py_ssize_t i, size;
+#endif
+
+ dbus_message_ref(message);
+ msg_obj = DBusPyMessage_ConsumeDBusMessage(message);
+ if (!msg_obj) {
+ DBG("%s", "OOM while trying to construct Message");
+ ret = DBUS_HANDLER_RESULT_NEED_MEMORY;
+ goto out;
+ }
+
+ conn_obj = (Connection *)DBusPyConnection_ExistingFromDBusConnection(conn);
+ if (!conn_obj) {
+ DBG("%s", "failed to traverse DBusConnection -> Connection weakref");
+ ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto out;
+ }
+ TRACE(conn_obj);
+
+ /* The user_data is a pointer to a Python object. To avoid
+ * cross-library reference cycles, the DBusConnection isn't allowed
+ * to reference it. However, as long as the Connection is still
+ * alive, its ->filters list owns a reference to the same Python
+ * object, so the object should also still be alive.
+ *
+ * To ensure that this works, be careful whenever manipulating the
+ * filters list! (always put things in the list *before* giving
+ * them to libdbus, etc.)
+ */
+#ifdef DBUS_PYTHON_DISABLE_CHECKS
+ callable = (PyObject *)user_data;
+#else
+ size = PyList_GET_SIZE(conn_obj->filters);
+ for (i = 0; i < size; i++) {
+ callable = PyList_GET_ITEM(conn_obj->filters, i);
+ if (callable == user_data) {
+ Py_INCREF(callable);
+ break;
+ }
+ else {
+ callable = NULL;
+ }
+ }
+
+ if (!callable) {
+ DBG("... filter %p has vanished from ->filters, so not calling it",
+ user_data);
+ ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto out;
+ }
+#endif
+
+ ret = DBusPyConnection_HandleMessage(conn_obj, msg_obj, callable);
+out:
+ Py_CLEAR(msg_obj);
+ Py_CLEAR(conn_obj);
+ Py_CLEAR(callable);
+ PyGILState_Release(gil);
+ return ret;
+}
+
+PyDoc_STRVAR(Connection__require_main_loop__doc__,
+"_require_main_loop()\n\n"
+"Raise an exception if this Connection is not bound to any main loop -\n"
+"in this state, asynchronous calls, receiving signals and exporting objects\n"
+"will not work.\n"
+"\n"
+"`dbus.mainloop.NULL_MAIN_LOOP` is treated like a valid main loop - if you're\n"
+"using that, you presumably know what you're doing.\n");
+static PyObject *
+Connection__require_main_loop (Connection *self, PyObject *args UNUSED)
+{
+ if (!self->has_mainloop) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "To make asynchronous calls, receive signals or "
+ "export objects, D-Bus connections must be attached "
+ "to a main loop by passing mainloop=... to the "
+ "constructor or calling "
+ "dbus.set_default_main_loop(...)");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Connection_close__doc__,
+"close()\n\n"
+"Close the connection.");
+static PyObject *
+Connection_close (Connection *self, PyObject *args UNUSED)
+{
+ TRACE(self);
+ /* Because the user explicitly asked to close the connection, we'll even
+ let them close shared connections. */
+ if (self->conn) {
+ Py_BEGIN_ALLOW_THREADS
+ dbus_connection_close(self->conn);
+ Py_END_ALLOW_THREADS
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Connection_get_is_connected__doc__,
+"get_is_connected() -> bool\n\n"
+"Return true if this Connection is connected.\n");
+static PyObject *
+Connection_get_is_connected (Connection *self, PyObject *args UNUSED)
+{
+ dbus_bool_t ret;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ Py_BEGIN_ALLOW_THREADS
+ ret = dbus_connection_get_is_connected(self->conn);
+ Py_END_ALLOW_THREADS
+ return PyBool_FromLong(ret);
+}
+
+PyDoc_STRVAR(Connection_get_is_authenticated__doc__,
+"get_is_authenticated() -> bool\n\n"
+"Return true if this Connection was ever authenticated.\n");
+static PyObject *
+Connection_get_is_authenticated (Connection *self, PyObject *args UNUSED)
+{
+ dbus_bool_t ret;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ Py_BEGIN_ALLOW_THREADS
+ ret = dbus_connection_get_is_authenticated(self->conn);
+ Py_END_ALLOW_THREADS
+ return PyBool_FromLong(ret);
+}
+
+PyDoc_STRVAR(Connection_set_exit_on_disconnect__doc__,
+"set_exit_on_disconnect(bool)\n\n"
+"Set whether the C function ``_exit`` will be called when this Connection\n"
+"becomes disconnected. This will cause the program to exit without calling\n"
+"any cleanup code or exit handlers.\n"
+"\n"
+"The default is for this feature to be disabled for Connections and enabled\n"
+"for Buses.\n");
+static PyObject *
+Connection_set_exit_on_disconnect (Connection *self, PyObject *args)
+{
+ int exit_on_disconnect;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!PyArg_ParseTuple(args, "i:set_exit_on_disconnect",
+ &exit_on_disconnect)) {
+ return NULL;
+ }
+ Py_BEGIN_ALLOW_THREADS
+ dbus_connection_set_exit_on_disconnect(self->conn,
+ exit_on_disconnect ? 1 : 0);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Connection_send_message__doc__,
+"send_message(msg) -> long\n\n"
+"Queue the given message for sending, and return the message serial number.\n"
+"\n"
+":Parameters:\n"
+" `msg` : dbus.lowlevel.Message\n"
+" The message to be sent.\n"
+);
+static PyObject *
+Connection_send_message(Connection *self, PyObject *args)
+{
+ dbus_bool_t ok;
+ PyObject *obj;
+ DBusMessage *msg;
+ dbus_uint32_t serial;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!PyArg_ParseTuple(args, "O", &obj)) return NULL;
+
+ msg = DBusPyMessage_BorrowDBusMessage(obj);
+ if (!msg) return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_send(self->conn, msg, &serial);
+ Py_END_ALLOW_THREADS
+
+ if (!ok) {
+ return PyErr_NoMemory();
+ }
+
+ return PyLong_FromUnsignedLong(serial);
+}
+
+PyDoc_STRVAR(Connection_set_allow_anonymous__doc__,
+"set_allow_anonymous(bool)\n\n"
+"Allows anonymous clients. Call this on the server side of a connection in a on_connection_added callback"
+);
+static PyObject *
+Connection_set_allow_anonymous(Connection *self, PyObject *args)
+{
+ dbus_bool_t t;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!PyArg_ParseTuple(args, "i", &t)) {
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ dbus_connection_set_allow_anonymous(self->conn, t ? 1 : 0);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+/* The timeout is in seconds here, since that's conventional in Python. */
+PyDoc_STRVAR(Connection_send_message_with_reply__doc__,
+"send_message_with_reply(msg, reply_handler, timeout_s=-1, "
+"require_main_loop=False) -> dbus.lowlevel.PendingCall\n\n"
+"Queue the message for sending; expect a reply via the returned PendingCall,\n"
+"which can also be used to cancel the pending call.\n"
+"\n"
+":Parameters:\n"
+" `msg` : dbus.lowlevel.Message\n"
+" The message to be sent\n"
+" `reply_handler` : callable\n"
+" Asynchronous reply handler: will be called with one positional\n"
+" parameter, a Message instance representing the reply.\n"
+" `timeout_s` : float\n"
+" If the reply takes more than this many seconds, a timeout error\n"
+" will be created locally and raised instead. If this timeout is\n"
+" negative (default), a sane default (supplied by libdbus) is used.\n"
+" `require_main_loop` : bool\n"
+" If True, raise RuntimeError if this Connection does not have a main\n"
+" loop configured. If False (default) and there is no main loop, you are\n"
+" responsible for calling block() on the PendingCall.\n"
+"\n"
+);
+static PyObject *
+Connection_send_message_with_reply(Connection *self, PyObject *args, PyObject *kw)
+{
+ dbus_bool_t ok;
+ double timeout_s = -1.0;
+ int timeout_ms;
+ PyObject *obj, *callable;
+ DBusMessage *msg;
+ DBusPendingCall *pending;
+ int require_main_loop = 0;
+ static char *argnames[] = {"msg", "reply_handler", "timeout_s",
+ "require_main_loop", NULL};
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!PyArg_ParseTupleAndKeywords(args, kw,
+ "OO|di:send_message_with_reply",
+ argnames,
+ &obj, &callable, &timeout_s,
+ &require_main_loop)) {
+ return NULL;
+ }
+ if (require_main_loop && !Connection__require_main_loop(self, NULL)) {
+ return NULL;
+ }
+
+ msg = DBusPyMessage_BorrowDBusMessage(obj);
+ if (!msg) return NULL;
+
+ if (timeout_s < 0) {
+ timeout_ms = -1;
+ }
+ else {
+ if (timeout_s > ((double)INT_MAX) / 1000.0) {
+ PyErr_SetString(PyExc_ValueError, "Timeout too long");
+ return NULL;
+ }
+ timeout_ms = (int)(timeout_s * 1000.0);
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_send_with_reply(self->conn, msg, &pending,
+ timeout_ms);
+ Py_END_ALLOW_THREADS
+
+ if (!ok) {
+ return PyErr_NoMemory();
+ }
+
+ if (!pending) {
+ /* connection is disconnected (doesn't return FALSE!) */
+ return DBusPyException_SetString ("Connection is disconnected - "
+ "unable to make method call");
+ }
+
+ return DBusPyPendingCall_ConsumeDBusPendingCall(pending, callable);
+}
+
+/* Again, the timeout is in seconds, since that's conventional in Python. */
+PyDoc_STRVAR(Connection_send_message_with_reply_and_block__doc__,
+"send_message_with_reply_and_block(msg, timeout_s=-1)"
+" -> dbus.lowlevel.Message\n\n"
+"Send the message and block while waiting for a reply.\n"
+"\n"
+"This does not re-enter the main loop, so it can lead to a deadlock, if\n"
+"the called method tries to make a synchronous call to a method in this\n"
+"application. As such, it's probably a bad idea.\n"
+"\n"
+":Parameters:\n"
+" `msg` : dbus.lowlevel.Message\n"
+" The message to be sent\n"
+" `timeout_s` : float\n"
+" If the reply takes more than this many seconds, a timeout error\n"
+" will be created locally and raised instead. If this timeout is\n"
+" negative (default), a sane default (supplied by libdbus) is used.\n"
+":Returns:\n"
+" A `dbus.lowlevel.Message` instance (probably a `dbus.lowlevel.MethodReturnMessage`) on success\n"
+":Raises dbus.DBusException:\n"
+" On error (including if the reply arrives but is an\n"
+" error message)\n"
+"\n"
+);
+static PyObject *
+Connection_send_message_with_reply_and_block(Connection *self, PyObject *args)
+{
+ double timeout_s = -1.0;
+ int timeout_ms;
+ PyObject *obj;
+ DBusMessage *msg, *reply;
+ DBusError error;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!PyArg_ParseTuple(args, "O|d:send_message_with_reply_and_block", &obj,
+ &timeout_s)) {
+ return NULL;
+ }
+
+ msg = DBusPyMessage_BorrowDBusMessage(obj);
+ if (!msg) return NULL;
+
+ if (timeout_s < 0) {
+ timeout_ms = -1;
+ }
+ else {
+ if (timeout_s > ((double)INT_MAX) / 1000.0) {
+ PyErr_SetString(PyExc_ValueError, "Timeout too long");
+ return NULL;
+ }
+ timeout_ms = (int)(timeout_s * 1000.0);
+ }
+
+ dbus_error_init(&error);
+ Py_BEGIN_ALLOW_THREADS
+ reply = dbus_connection_send_with_reply_and_block(self->conn, msg,
+ timeout_ms, &error);
+ Py_END_ALLOW_THREADS
+
+ /* FIXME: if we instead used send_with_reply and blocked on the resulting
+ * PendingCall, then we could get all args from the error, not just
+ * the first */
+ if (!reply) {
+ return DBusPyException_ConsumeError(&error);
+ }
+ return DBusPyMessage_ConsumeDBusMessage(reply);
+}
+
+PyDoc_STRVAR(Connection_flush__doc__,
+"flush()\n\n"
+"Block until the outgoing message queue is empty.\n");
+static PyObject *
+Connection_flush (Connection *self, PyObject *args UNUSED)
+{
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ Py_BEGIN_ALLOW_THREADS
+ dbus_connection_flush (self->conn);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+/* Unsupported:
+ * dbus_connection_preallocate_send
+ * dbus_connection_free_preallocated_send
+ * dbus_connection_send_preallocated
+ * dbus_connection_borrow_message
+ * dbus_connection_return_message
+ * dbus_connection_steal_borrowed_message
+ * dbus_connection_pop_message
+ */
+
+/* Non-main-loop handling not yet implemented: */
+ /* dbus_connection_read_write_dispatch */
+ /* dbus_connection_read_write */
+
+/* Main loop handling not yet implemented: */
+ /* dbus_connection_get_dispatch_status */
+ /* dbus_connection_dispatch */
+ /* dbus_connection_set_watch_functions */
+ /* dbus_connection_set_timeout_functions */
+ /* dbus_connection_set_wakeup_main_function */
+ /* dbus_connection_set_dispatch_status_function */
+
+/* Normally in Python this would be called fileno(), but I don't want to
+ * encourage people to select() on it */
+PyDoc_STRVAR(Connection_get_unix_fd__doc__,
+"get_unix_fd() -> int or None\n\n"
+"Get the connection's UNIX file descriptor, if any.\n\n"
+"This can be used for SELinux access control checks with ``getpeercon()``\n"
+"for example. **Do not** read or write to the file descriptor, or try to\n"
+"``select()`` on it.\n");
+static PyObject *
+Connection_get_unix_fd (Connection *self, PyObject *unused UNUSED)
+{
+ int fd;
+ dbus_bool_t ok;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_get_unix_fd (self->conn, &fd);
+ Py_END_ALLOW_THREADS
+ if (!ok) Py_RETURN_NONE;
+ return NATIVEINT_FROMLONG(fd);
+}
+
+PyDoc_STRVAR(Connection_get_peer_unix_user__doc__,
+"get_peer_unix_user() -> long or None\n\n"
+"Get the UNIX user ID at the other end of the connection, if it has been\n"
+"authenticated. Return None if this is a non-UNIX platform or the\n"
+"connection has not been authenticated.\n");
+static PyObject *
+Connection_get_peer_unix_user (Connection *self, PyObject *unused UNUSED)
+{
+ unsigned long uid;
+ dbus_bool_t ok;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_get_unix_user (self->conn, &uid);
+ Py_END_ALLOW_THREADS
+ if (!ok) Py_RETURN_NONE;
+ return PyLong_FromUnsignedLong (uid);
+}
+
+PyDoc_STRVAR(Connection_get_peer_unix_process_id__doc__,
+"get_peer_unix_process_id() -> long or None\n\n"
+"Get the UNIX process ID at the other end of the connection, if it has been\n"
+"authenticated. Return None if this is a non-UNIX platform or the\n"
+"connection has not been authenticated.\n");
+static PyObject *
+Connection_get_peer_unix_process_id (Connection *self, PyObject *unused UNUSED)
+{
+ unsigned long pid;
+ dbus_bool_t ok;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_get_unix_process_id (self->conn, &pid);
+ Py_END_ALLOW_THREADS
+ if (!ok) Py_RETURN_NONE;
+ return PyLong_FromUnsignedLong (pid);
+}
+
+/* TODO: wrap dbus_connection_set_unix_user_function Pythonically */
+
+PyDoc_STRVAR(Connection_add_message_filter__doc__,
+"add_message_filter(callable)\n\n"
+"Add the given message filter to the internal list.\n\n"
+"Filters are handlers that are run on all incoming messages, prior to the\n"
+"objects registered to handle object paths.\n"
+"\n"
+"Filters are run in the order that they were added. The same handler can\n"
+"be added as a filter more than once, in which case it will be run more\n"
+"than once. Filters added during a filter callback won't be run on the\n"
+"message being processed.\n"
+);
+static PyObject *
+Connection_add_message_filter(Connection *self, PyObject *callable)
+{
+ dbus_bool_t ok;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ /* The callable must be referenced by ->filters *before* it is
+ * given to libdbus, which does not own a reference to it.
+ */
+ if (PyList_Append(self->filters, callable) < 0) {
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_add_filter(self->conn, _filter_message, callable,
+ NULL);
+ Py_END_ALLOW_THREADS
+
+ if (!ok) {
+ Py_XDECREF(PyObject_CallMethod(self->filters, "remove", "(O)",
+ callable));
+ PyErr_NoMemory();
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Connection_remove_message_filter__doc__,
+"remove_message_filter(callable)\n\n"
+"Remove the given message filter (see `add_message_filter` for details).\n"
+"\n"
+":Raises LookupError:\n"
+" The given callable is not among the registered filters\n");
+static PyObject *
+Connection_remove_message_filter(Connection *self, PyObject *callable)
+{
+ PyObject *obj;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ /* It's safe to do this before removing it from libdbus, because
+ * the presence of callable in our arguments means we have a ref
+ * to it. */
+ obj = PyObject_CallMethod(self->filters, "remove", "(O)", callable);
+ if (!obj) return NULL;
+ Py_CLEAR(obj);
+
+ Py_BEGIN_ALLOW_THREADS
+ dbus_connection_remove_filter(self->conn, _filter_message, callable);
+ Py_END_ALLOW_THREADS
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Connection__register_object_path__doc__,
+"register_object_path(path, on_message, on_unregister=None, fallback=False)\n"
+"\n"
+"Register a callback to be called when messages arrive at the given\n"
+"object-path. Used to export objects' methods on the bus in a low-level\n"
+"way. For the high-level interface to this functionality (usually\n"
+"recommended) see the `dbus.service.Object` base class.\n"
+"\n"
+":Parameters:\n"
+" `path` : str\n"
+" Object path to be acted on\n"
+" `on_message` : callable\n"
+" Called when a message arrives at the given object-path, with\n"
+" two positional parameters: the first is this Connection,\n"
+" the second is the incoming `dbus.lowlevel.Message`.\n"
+" `on_unregister` : callable or None\n"
+" If not None, called when the callback is unregistered.\n"
+" `fallback` : bool\n"
+" If True (the default is False), when a message arrives for a\n"
+" 'subdirectory' of the given path and there is no more specific\n"
+" handler, use this handler. Normally this handler is only run if\n"
+" the paths match exactly.\n"
+);
+static PyObject *
+Connection__register_object_path(Connection *self, PyObject *args,
+ PyObject *kwargs)
+{
+ dbus_bool_t ok;
+ int fallback = 0;
+ char *path_bytes;
+ PyObject *callbacks, *path, *tuple, *on_message, *on_unregister = Py_None;
+ static char *argnames[] = {"path", "on_message", "on_unregister",
+ "fallback", NULL};
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!Connection__require_main_loop(self, NULL)) {
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "OO|Oi:_register_object_path",
+ argnames,
+ &path,
+ &on_message, &on_unregister,
+ &fallback)) return NULL;
+
+ /* Take a reference to path, which we give away to libdbus in a moment.
+
+ Also, path needs to be a string (not a subclass which could do something
+ mad) to preserve the desirable property that the DBusConnection can
+ never strongly reference the Connection, even indirectly.
+ */
+ if (PyBytes_CheckExact(path)) {
+ Py_INCREF(path);
+ }
+ else if (PyUnicode_Check(path)) {
+ path = PyUnicode_AsUTF8String(path);
+ if (!path) return NULL;
+ }
+ else if (PyBytes_Check(path)) {
+ path = PyBytes_FromString(PyBytes_AS_STRING(path));
+ if (!path) return NULL;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "path must be a str, bytes, or unicode object");
+ return NULL;
+ }
+
+ path_bytes = PyBytes_AS_STRING(path);
+ if (!dbus_py_validate_object_path(path_bytes)) {
+ Py_CLEAR(path);
+ return NULL;
+ }
+
+ tuple = Py_BuildValue("(OO)", on_unregister, on_message);
+ if (!tuple) {
+ Py_CLEAR(path);
+ return NULL;
+ }
+
+ /* Guard against registering a handler that already exists. */
+ callbacks = PyDict_GetItem(self->object_paths, path);
+ if (callbacks && callbacks != Py_None) {
+ PyErr_Format(PyExc_KeyError, "Can't register the object-path "
+ "handler for '%s': there is already a handler",
+ path_bytes);
+ Py_CLEAR(tuple);
+ Py_CLEAR(path);
+ return NULL;
+ }
+
+ /* Pre-allocate a slot in the dictionary, so we know we'll be able
+ * to replace it with the callbacks without OOM.
+ * This ensures we can keep libdbus' opinion of whether those
+ * paths are handled in sync with our own. */
+ if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) {
+ Py_CLEAR(tuple);
+ Py_CLEAR(path);
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ if (fallback) {
+ ok = dbus_connection_register_fallback(self->conn,
+ path_bytes,
+ &_object_path_vtable,
+ path);
+ }
+ else {
+ ok = dbus_connection_register_object_path(self->conn,
+ path_bytes,
+ &_object_path_vtable,
+ path);
+ }
+ Py_END_ALLOW_THREADS
+
+ if (ok) {
+ if (PyDict_SetItem(self->object_paths, path, tuple) < 0) {
+ /* That shouldn't have happened, we already allocated enough
+ memory for it. Oh well, try to undo the registration to keep
+ things in sync. If this fails too, we've leaked a bit of
+ memory in libdbus, but tbh we should never get here anyway. */
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_unregister_object_path(self->conn,
+ path_bytes);
+ Py_END_ALLOW_THREADS
+ return NULL;
+ }
+ /* don't DECREF path: libdbus owns a ref now */
+ Py_CLEAR(tuple);
+ Py_RETURN_NONE;
+ }
+ else {
+ /* Oops, OOM. Tidy up, if we can, ignoring any error. */
+ PyDict_DelItem(self->object_paths, path);
+ PyErr_Clear();
+ Py_CLEAR(tuple);
+ Py_CLEAR(path);
+ PyErr_NoMemory();
+ return NULL;
+ }
+}
+
+PyDoc_STRVAR(Connection__unregister_object_path__doc__,
+"unregister_object_path(path)\n\n"
+"Remove a previously registered handler for the given object path.\n"
+"\n"
+":Parameters:\n"
+" `path` : str\n"
+" The object path whose handler is to be removed\n"
+":Raises KeyError: if there is no handler registered for exactly that\n"
+" object path.\n"
+);
+static PyObject *
+Connection__unregister_object_path(Connection *self, PyObject *args,
+ PyObject *kwargs)
+{
+ dbus_bool_t ok;
+ char *path_bytes;
+ PyObject *path;
+ PyObject *callbacks;
+ static char *argnames[] = {"path", NULL};
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "O:_unregister_object_path",
+ argnames, &path)) return NULL;
+
+ /* Take a ref to the path. Same comments as for _register_object_path. */
+ if (PyBytes_CheckExact(path)) {
+ Py_INCREF(path);
+ }
+ else if (PyUnicode_Check(path)) {
+ path = PyUnicode_AsUTF8String(path);
+ if (!path) return NULL;
+ }
+ else if (PyBytes_Check(path)) {
+ path = PyBytes_FromString(PyBytes_AS_STRING(path));
+ if (!path) return NULL;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "path must be a str, bytes, or unicode object");
+ return NULL;
+ }
+
+ path_bytes = PyBytes_AS_STRING(path);
+
+ /* Guard against unregistering a handler that doesn't, in fact, exist,
+ or whose unregistration is already in progress. */
+ callbacks = PyDict_GetItem(self->object_paths, path);
+ if (!callbacks || callbacks == Py_None) {
+ PyErr_Format(PyExc_KeyError, "Can't unregister the object-path "
+ "handler for '%s': there is no such handler",
+ path_bytes);
+ Py_CLEAR(path);
+ return NULL;
+ }
+
+ /* Hang on to a reference to the callbacks for the moment. */
+ Py_INCREF(callbacks);
+
+ /* Get rid of the object-path while we still have the GIL, to
+ guard against unregistering twice from different threads (which
+ causes undefined behaviour in libdbus).
+
+ Because deletion would make it possible for the re-insertion below
+ to fail, we instead set the handler to None as a placeholder.
+ */
+ if (PyDict_SetItem(self->object_paths, path, Py_None) < 0) {
+ /* If that failed, there's no need to be paranoid as below - the
+ callbacks are still set, so we failed, but at least everything
+ is in sync. */
+ Py_CLEAR(callbacks);
+ Py_CLEAR(path);
+ return NULL;
+ }
+
+ /* BEGIN PARANOIA
+ This is something of a critical section - the dict of object-paths
+ and libdbus' internal structures are out of sync for a bit. We have
+ to be able to cope with that.
+
+ It's really annoying that dbus_connection_unregister_object_path
+ can fail, *and* has undefined behaviour if the object path has
+ already been unregistered. Either/or would be fine.
+ */
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_unregister_object_path(self->conn, path_bytes);
+ Py_END_ALLOW_THREADS
+
+ if (ok) {
+ Py_CLEAR(callbacks);
+ PyDict_DelItem(self->object_paths, path);
+ /* END PARANOIA on successful code path */
+ /* The above can't fail unless by some strange trickery the key is no
+ longer present. Ignore any errors. */
+ Py_CLEAR(path);
+ PyErr_Clear();
+ Py_RETURN_NONE;
+ }
+ else {
+ /* Oops, OOM. Put the callbacks back in the dict so
+ * we'll have another go if/when the user frees some memory
+ * and tries calling this method again. */
+ PyDict_SetItem(self->object_paths, path, callbacks);
+ /* END PARANOIA on failing code path */
+ /* If the SetItem failed, there's nothing we can do about it - but
+ since we know it's an existing entry, it shouldn't be able to fail
+ anyway. */
+ Py_CLEAR(path);
+ Py_CLEAR(callbacks);
+ return PyErr_NoMemory();
+ }
+}
+
+PyDoc_STRVAR(Connection_list_exported_child_objects__doc__,
+"list_exported_child_objects(path: str) -> list of str\n\n"
+"Return a list of the names of objects exported on this Connection as\n"
+"direct children of the given object path.\n"
+"\n"
+"Each name returned may be converted to a valid object path using\n"
+"``dbus.ObjectPath('%s%s%s' % (path, (path != '/' and '/' or ''), name))``.\n"
+"For the purposes of this function, every parent or ancestor of an exported\n"
+"object is considered to be an exported object, even if it's only an object\n"
+"synthesized by the library to support introspection.\n");
+static PyObject *
+Connection_list_exported_child_objects (Connection *self, PyObject *args,
+ PyObject *kwargs)
+{
+ const char *path;
+ char **kids, **kid_ptr;
+ dbus_bool_t ok;
+ PyObject *ret;
+ static char *argnames[] = {"path", NULL};
+
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->conn);
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", argnames, &path)) {
+ return NULL;
+ }
+
+ if (!dbus_py_validate_object_path(path)) {
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_list_registered(self->conn, path, &kids);
+ Py_END_ALLOW_THREADS
+
+ if (!ok) {
+ return PyErr_NoMemory();
+ }
+
+ ret = PyList_New(0);
+ if (!ret) {
+ return NULL;
+ }
+ for (kid_ptr = kids; *kid_ptr; kid_ptr++) {
+ PyObject *tmp = NATIVESTR_FROMSTR(*kid_ptr);
+
+ if (!tmp) {
+ Py_CLEAR(ret);
+ return NULL;
+ }
+ if (PyList_Append(ret, tmp) < 0) {
+ Py_CLEAR(tmp);
+ Py_CLEAR(ret);
+ return NULL;
+ }
+ Py_CLEAR(tmp);
+ }
+
+ dbus_free_string_array(kids);
+
+ return ret;
+}
+
+ /* dbus_connection_get_object_path_data - not useful to Python,
+ * the object path data is just a PyBytes containing the path */
+ /* dbus_connection_list_registered could be useful, though */
+
+/* dbus_connection_set_change_sigpipe - sets global state */
+
+/* Maxima. Does Python code ever need to manipulate these?
+ * OTOH they're easy to wrap */
+ /* dbus_connection_set_max_message_size */
+ /* dbus_connection_get_max_message_size */
+ /* dbus_connection_set_max_received_size */
+ /* dbus_connection_get_max_received_size */
+
+/* dbus_connection_get_outgoing_size - almost certainly unneeded */
+
+PyDoc_STRVAR(new_for_bus__doc__,
+"Connection._new_for_bus([address: str or int]) -> Connection\n"
+"\n"
+"If the address is an int it must be one of the constants BUS_SESSION,\n"
+"BUS_SYSTEM, BUS_STARTER; if a string, it must be a D-Bus address.\n"
+"The default is BUS_SESSION.\n"
+);
+
+PyDoc_STRVAR(get_unique_name__doc__,
+"get_unique_name() -> str\n\n"
+"Return this application's unique name on this bus.\n"
+"\n"
+":Raises DBusException: if the connection has no unique name yet\n"
+" (for Bus objects this can't happen, for peer-to-peer connections\n"
+" this means you haven't called `set_unique_name`)\n");
+
+PyDoc_STRVAR(set_unique_name__doc__,
+"set_unique_name(str)\n\n"
+"Set this application's unique name on this bus. Raise ValueError if it has\n"
+"already been set.\n");
+
+struct PyMethodDef DBusPyConnection_tp_methods[] = {
+#define ENTRY(name, flags) {#name, (PyCFunction)Connection_##name, flags, Connection_##name##__doc__}
+ ENTRY(_require_main_loop, METH_NOARGS),
+ ENTRY(close, METH_NOARGS),
+ ENTRY(flush, METH_NOARGS),
+ ENTRY(get_is_connected, METH_NOARGS),
+ ENTRY(get_is_authenticated, METH_NOARGS),
+ ENTRY(set_exit_on_disconnect, METH_VARARGS),
+ ENTRY(get_unix_fd, METH_NOARGS),
+ ENTRY(get_peer_unix_user, METH_NOARGS),
+ ENTRY(get_peer_unix_process_id, METH_NOARGS),
+ ENTRY(add_message_filter, METH_O),
+ ENTRY(_register_object_path, METH_VARARGS|METH_KEYWORDS),
+ ENTRY(remove_message_filter, METH_O),
+ ENTRY(send_message, METH_VARARGS),
+ ENTRY(send_message_with_reply, METH_VARARGS|METH_KEYWORDS),
+ ENTRY(send_message_with_reply_and_block, METH_VARARGS),
+ ENTRY(_unregister_object_path, METH_VARARGS|METH_KEYWORDS),
+ ENTRY(list_exported_child_objects, METH_VARARGS|METH_KEYWORDS),
+ {"_new_for_bus", (PyCFunction)DBusPyConnection_NewForBus,
+ METH_CLASS|METH_VARARGS|METH_KEYWORDS,
+ new_for_bus__doc__},
+ {"get_unique_name", (PyCFunction)DBusPyConnection_GetUniqueName,
+ METH_NOARGS,
+ get_unique_name__doc__},
+ {"set_unique_name", (PyCFunction)DBusPyConnection_SetUniqueName,
+ METH_VARARGS,
+ set_unique_name__doc__},
+ ENTRY(set_allow_anonymous, METH_VARARGS),
+ {NULL},
+#undef ENTRY
+};
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/conn.c b/dbus_bindings/conn.c
new file mode 100644
index 0000000..739f305
--- /dev/null
+++ b/dbus_bindings/conn.c
@@ -0,0 +1,500 @@
+/* Implementation of the _dbus_bindings Connection type, a Python wrapper
+ * for DBusConnection. See also conn-methods.c.
+ *
+ * Copyright (C) 2006-2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include "conn-internal.h"
+
+/* Connection definition ============================================ */
+
+PyDoc_STRVAR(Connection_tp_doc,
+"_dbus_bindings.Connection(address, mainloop=None)\n"
+"\n"
+"A D-Bus connection.\n"
+);
+
+/* D-Bus Connection user data slot, containing an owned reference to either
+ * the Connection, or a weakref to the Connection.
+ */
+static dbus_int32_t _connection_python_slot;
+
+/* C API for main-loop hooks ======================================== */
+
+/* Return a borrowed reference to the DBusConnection which underlies this
+ * Connection. */
+DBusConnection *
+DBusPyConnection_BorrowDBusConnection(PyObject *self)
+{
+ DBusConnection *dbc;
+
+ TRACE(self);
+ if (!DBusPyConnection_Check(self)) {
+ PyErr_SetString(PyExc_TypeError, "A dbus.Connection is required");
+ return NULL;
+ }
+ dbc = ((Connection *)self)->conn;
+ if (!dbc) {
+ PyErr_SetString(PyExc_RuntimeError, "Connection is in an invalid "
+ "state: no DBusConnection");
+ return NULL;
+ }
+ return dbc;
+}
+
+/* Internal C API =================================================== */
+
+/* Pass a message through a handler. */
+DBusHandlerResult
+DBusPyConnection_HandleMessage(Connection *conn,
+ PyObject *msg,
+ PyObject *callable)
+{
+ PyObject *obj;
+
+ TRACE(conn);
+ obj = PyObject_CallFunctionObjArgs(callable, conn, msg,
+ NULL);
+ if (obj == Py_None) {
+ DBG("%p: OK, handler %p returned None", conn, callable);
+ Py_CLEAR(obj);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (obj == Py_NotImplemented) {
+ DBG("%p: handler %p returned NotImplemented, continuing",
+ conn, callable);
+ Py_CLEAR(obj);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ else if (!obj) {
+ if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
+ DBG_EXC("%p: handler %p caused OOM", conn, callable);
+ PyErr_Clear();
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+ DBG_EXC("%p: handler %p raised exception", conn, callable);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ else {
+ long i = PyLong_AsLong(obj);
+ DBG("%p: handler %p returned %ld", conn, callable, i);
+ Py_CLEAR(obj);
+ if (i == -1 && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError, "Return from D-Bus message "
+ "handler callback should be None, "
+ "NotImplemented or integer");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ else if (i == DBUS_HANDLER_RESULT_HANDLED ||
+ i == DBUS_HANDLER_RESULT_NOT_YET_HANDLED ||
+ i == DBUS_HANDLER_RESULT_NEED_MEMORY) {
+ return i;
+ }
+ else {
+ PyErr_Format(PyExc_ValueError, "Integer return from "
+ "D-Bus message handler callback should "
+ "be a DBUS_HANDLER_RESULT_... constant, "
+ "not %d", (int)i);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ }
+}
+
+/* On KeyError or if unregistration is in progress, return None. */
+PyObject *
+DBusPyConnection_GetObjectPathHandlers(PyObject *self, PyObject *path)
+{
+ PyObject *callbacks;
+
+ TRACE(self);
+ callbacks = PyDict_GetItem(((Connection *)self)->object_paths, path);
+ if (!callbacks) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ PyErr_Clear();
+ Py_RETURN_NONE;
+ }
+ }
+ Py_INCREF(callbacks);
+ return callbacks;
+}
+
+/* Return a new reference to a Python Connection or subclass corresponding
+ * to the DBusConnection conn. For use in callbacks.
+ *
+ * Raises AssertionError if the DBusConnection does not have a Connection.
+ */
+PyObject *
+DBusPyConnection_ExistingFromDBusConnection(DBusConnection *conn)
+{
+ PyObject *self, *ref;
+
+ Py_BEGIN_ALLOW_THREADS
+ ref = (PyObject *)dbus_connection_get_data(conn,
+ _connection_python_slot);
+ Py_END_ALLOW_THREADS
+ if (ref) {
+ DBG("(DBusConnection *)%p has weak reference at %p", conn, ref);
+ self = PyWeakref_GetObject(ref); /* still a borrowed ref */
+ if (self && self != Py_None && DBusPyConnection_Check(self)) {
+ DBG("(DBusConnection *)%p has weak reference at %p pointing to %p",
+ conn, ref, self);
+ TRACE(self);
+ Py_INCREF(self);
+ TRACE(self);
+ return self;
+ }
+ }
+
+ PyErr_SetString(PyExc_AssertionError,
+ "D-Bus connection does not have a Connection "
+ "instance associated with it");
+ return NULL;
+}
+
+/* Return a new reference to a Python Connection or subclass (given by cls)
+ * corresponding to the DBusConnection conn, which must have been newly
+ * created. For use by the Connection and Bus constructors.
+ *
+ * Raises AssertionError if the DBusConnection already has a Connection.
+ */
+static PyObject *
+DBusPyConnection_NewConsumingDBusConnection(PyTypeObject *cls,
+ DBusConnection *conn,
+ PyObject *mainloop)
+{
+ Connection *self = NULL;
+ PyObject *ref;
+ dbus_bool_t ok;
+
+ DBG("%s(cls=%p, conn=%p, mainloop=%p)", __func__, cls, conn, mainloop);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(conn);
+
+ Py_BEGIN_ALLOW_THREADS
+ ref = (PyObject *)dbus_connection_get_data(conn,
+ _connection_python_slot);
+ Py_END_ALLOW_THREADS
+ if (ref) {
+ self = (Connection *)PyWeakref_GetObject(ref);
+ ref = NULL;
+ if (self && (PyObject *)self != Py_None) {
+ self = NULL;
+ PyErr_SetString(PyExc_AssertionError,
+ "Newly created D-Bus connection already has a "
+ "Connection instance associated with it");
+ DBG("%s() fail - assertion failed, DBusPyConn has a DBusConn already", __func__);
+ DBG_WHEREAMI;
+ return NULL;
+ }
+ }
+ ref = NULL;
+
+ /* Change mainloop from a borrowed reference to an owned reference */
+ if (!mainloop || mainloop == Py_None) {
+ mainloop = dbus_py_get_default_main_loop();
+ if (!mainloop)
+ goto err;
+ }
+ else {
+ Py_INCREF(mainloop);
+ }
+
+ DBG("Constructing Connection from DBusConnection at %p", conn);
+
+ self = (Connection *)(cls->tp_alloc(cls, 0));
+ if (!self) goto err;
+ TRACE(self);
+
+ DBG_WHEREAMI;
+
+ self->has_mainloop = (mainloop != Py_None);
+ self->conn = NULL;
+ self->filters = PyList_New(0);
+ self->weaklist = NULL;
+ if (!self->filters) goto err;
+ self->object_paths = PyDict_New();
+ if (!self->object_paths) goto err;
+
+ ref = PyWeakref_NewRef((PyObject *)self, NULL);
+ if (!ref) goto err;
+ DBG("Created weak ref %p to (Connection *)%p for (DBusConnection *)%p",
+ ref, self, conn);
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_connection_set_data(conn, _connection_python_slot,
+ (void *)ref,
+ (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
+ Py_END_ALLOW_THREADS
+
+ if (ok) {
+ DBG("Attached weak ref %p ((Connection *)%p) to (DBusConnection *)%p",
+ ref, self, conn);
+ ref = NULL; /* don't DECREF it - the DBusConnection owns it now */
+ }
+ else {
+ DBG("Failed to attached weak ref %p ((Connection *)%p) to "
+ "(DBusConnection *)%p - will dispose of it", ref, self, conn);
+ PyErr_NoMemory();
+ goto err;
+ }
+
+ DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(conn, err);
+ self->conn = conn;
+ /* the DBusPyConnection will close it now */
+ conn = NULL;
+
+ if (self->has_mainloop
+ && !dbus_py_set_up_connection((PyObject *)self, mainloop)) {
+ goto err;
+ }
+
+ Py_CLEAR(mainloop);
+
+ DBG("%s() -> %p", __func__, self);
+ TRACE(self);
+ return (PyObject *)self;
+
+err:
+ DBG("Failed to construct Connection from DBusConnection at %p", conn);
+ Py_CLEAR(mainloop);
+ Py_CLEAR(self);
+ Py_CLEAR(ref);
+ if (conn) {
+ Py_BEGIN_ALLOW_THREADS
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+ Py_END_ALLOW_THREADS
+ }
+ DBG("%s() fail", __func__);
+ DBG_WHEREAMI;
+ return NULL;
+}
+
+/* Connection type-methods ========================================== */
+
+/* Constructor */
+static PyObject *
+Connection_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ DBusConnection *conn;
+ PyObject *address_or_conn;
+ DBusError error;
+ PyObject *self, *mainloop = NULL;
+ static char *argnames[] = {"address", "mainloop", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", argnames,
+ &address_or_conn, &mainloop)) {
+ return NULL;
+ }
+
+ if (DBusPyLibDBusConnection_CheckExact(address_or_conn)) {
+ DBusPyLibDBusConnection *wrapper =
+ (DBusPyLibDBusConnection *) address_or_conn;
+
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(wrapper->conn);
+
+ conn = dbus_connection_ref (wrapper->conn);
+ }
+ else if (PyBytes_Check(address_or_conn)) {
+ const char *address = PyBytes_AS_STRING(address_or_conn);
+
+ dbus_error_init(&error);
+
+ /* We always open a private connection (at the libdbus level). Sharing
+ * is done in Python, to keep things simple. */
+ Py_BEGIN_ALLOW_THREADS
+ conn = dbus_connection_open_private(address, &error);
+ Py_END_ALLOW_THREADS
+
+ if (!conn) {
+ DBusPyException_ConsumeError(&error);
+ return NULL;
+ }
+ }
+ else if (PyUnicode_Check(address_or_conn)) {
+ PyObject *address_as_bytes = PyUnicode_AsUTF8String(address_or_conn);
+ const char *address;
+
+ if (!address_as_bytes)
+ return NULL;
+ address = PyBytes_AS_STRING(address_as_bytes);
+
+ dbus_error_init(&error);
+
+ /* We always open a private connection (at the libdbus level). Sharing
+ * is done in Python, to keep things simple. */
+ Py_BEGIN_ALLOW_THREADS
+ conn = dbus_connection_open_private(address, &error);
+ Py_END_ALLOW_THREADS
+
+ Py_CLEAR(address_as_bytes);
+ if (!conn) {
+ DBusPyException_ConsumeError(&error);
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "connection or str expected");
+ return NULL;
+ }
+
+ self = DBusPyConnection_NewConsumingDBusConnection(cls, conn, mainloop);
+ TRACE(self);
+
+ return self;
+}
+
+/* Post-construction: nothing to do (but don't chain up to object.__init__,
+ * which takes no arguments and does nothing) */
+static int
+Connection_tp_init(PyObject *self UNUSED, PyObject *args UNUSED,
+ PyObject *kwargs UNUSED)
+{
+ return 0;
+}
+
+/* Destructor */
+static void Connection_tp_dealloc(Connection *self)
+{
+ DBusConnection *conn = self->conn;
+ PyObject *et, *ev, *etb;
+ PyObject *filters = self->filters;
+ PyObject *object_paths = self->object_paths;
+
+ /* avoid clobbering any pending exception */
+ PyErr_Fetch(&et, &ev, &etb);
+
+ if (self->weaklist) {
+ PyObject_ClearWeakRefs((PyObject *)self);
+ }
+
+ TRACE(self);
+ DBG("Deallocating Connection at %p (DBusConnection at %p)", self, conn);
+ DBG_WHEREAMI;
+
+ DBG("Connection at %p: deleting callbacks", self);
+ self->filters = NULL;
+ Py_CLEAR(filters);
+ self->object_paths = NULL;
+ Py_CLEAR(object_paths);
+
+ if (conn) {
+ /* Might trigger callbacks if we're unlucky... */
+ DBG("Connection at %p has a conn, closing it...", self);
+ Py_BEGIN_ALLOW_THREADS
+ dbus_connection_close(conn);
+ Py_END_ALLOW_THREADS
+ }
+
+ /* make sure to do this last to preserve the invariant that
+ * self->conn is always non-NULL for any referenced Connection
+ * (until the filters and object paths were freed, we might have been
+ * in a reference cycle!)
+ */
+ DBG("Connection at %p: nulling self->conn", self);
+ self->conn = NULL;
+
+ if (conn) {
+ DBG("Connection at %p: unreffing conn", self);
+ dbus_connection_unref(conn);
+ }
+
+ DBG("Connection at %p: freeing self", self);
+ PyErr_Restore(et, ev, etb);
+ (Py_TYPE(self)->tp_free)((PyObject *)self);
+}
+
+/* Connection type object =========================================== */
+
+PyTypeObject DBusPyConnection_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_dbus_bindings.Connection", /*tp_name*/
+ sizeof(Connection), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)Connection_tp_dealloc,
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+#ifdef PY3
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+#else
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
+#endif
+ Connection_tp_doc, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ offsetof(Connection, weaklist), /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ DBusPyConnection_tp_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ Connection_tp_init, /*tp_init*/
+ 0, /*tp_alloc*/
+ Connection_tp_new, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+dbus_bool_t
+dbus_py_init_conn_types(void)
+{
+ /* Get a slot to store our weakref on DBus Connections */
+ _connection_python_slot = -1;
+ if (!dbus_connection_allocate_data_slot(&_connection_python_slot))
+ return FALSE;
+ if (PyType_Ready(&DBusPyConnection_Type) < 0)
+ return FALSE;
+ return TRUE;
+}
+
+dbus_bool_t
+dbus_py_insert_conn_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF (&DBusPyConnection_Type);
+ if (PyModule_AddObject(this_module, "Connection",
+ (PyObject *)&DBusPyConnection_Type) < 0) return FALSE;
+ return TRUE;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/containers.c b/dbus_bindings/containers.c
new file mode 100644
index 0000000..26f983b
--- /dev/null
+++ b/dbus_bindings/containers.c
@@ -0,0 +1,811 @@
+/* D-Bus container types: Array, Dict and Struct.
+ *
+ * Copyright (C) 2006-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "types-internal.h"
+
+/* Array ============================================================ */
+
+PyDoc_STRVAR(Array_tp_doc,
+"dbus.Array([iterable][, signature][, variant_level])\n"
+"\n"
+"An array of similar items, implemented as a subtype of list.\n"
+"\n"
+"As currently implemented, an Array behaves just like a list, but\n"
+"with the addition of a ``signature`` property set by the constructor;\n"
+"conversion of its items to D-Bus types is only done when it's sent in\n"
+"a Message. This might change in future so validation is done earlier.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+"``signature`` is the D-Bus signature string for a single element of the\n"
+"array, or None. If not None it must represent a single complete type, the\n"
+"type of a single array item; the signature of the whole Array may be\n"
+"obtained by prepending ``a`` to the given signature.\n"
+"\n"
+"If None (the default), when the Array is sent over\n"
+"D-Bus, the item signature will be guessed from the first element.\n"
+);
+
+static struct PyMemberDef Array_tp_members[] = {
+ {"signature", T_OBJECT, offsetof(DBusPyArray, signature), READONLY,
+ "The D-Bus signature of each element of this Array (a Signature "
+ "instance)"},
+ {"variant_level", T_LONG, offsetof(DBusPyArray, variant_level),
+ READONLY,
+ "Indicates how many nested Variant containers this object\n"
+ "is contained in: if a message's wire format has a variant containing a\n"
+ "variant containing an array, this is represented in Python by an\n"
+ "Array with variant_level==2.\n"
+ },
+ {NULL},
+};
+
+static void
+Array_tp_dealloc (DBusPyArray *self)
+{
+ Py_CLEAR(self->signature);
+ (PyList_Type.tp_dealloc)((PyObject *)self);
+}
+
+static PyObject *
+Array_tp_repr(DBusPyArray *self)
+{
+ PyObject *parent_repr = (PyList_Type.tp_repr)((PyObject *)self);
+ PyObject *sig_repr = PyObject_Repr(self->signature);
+ PyObject *my_repr = NULL;
+ long variant_level = self->variant_level;
+
+ if (!parent_repr) goto finally;
+ if (!sig_repr) goto finally;
+ if (variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, signature=%V, "
+ "variant_level=%ld)",
+ Py_TYPE(&self->super)->tp_name,
+ REPRV(parent_repr),
+ REPRV(sig_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V, signature=%V)",
+ Py_TYPE(&self->super)->tp_name,
+ REPRV(parent_repr),
+ REPRV(sig_repr));
+ }
+finally:
+ Py_CLEAR(parent_repr);
+ Py_CLEAR(sig_repr);
+ return my_repr;
+}
+
+static PyObject *
+Array_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *variant_level = NULL;
+ DBusPyArray *self = (DBusPyArray *)(PyList_Type.tp_new)(cls, args, kwargs);
+
+ /* variant_level is immutable, so handle it in __new__ rather than
+ __init__ */
+ if (!self) return NULL;
+ Py_INCREF(Py_None);
+ self->signature = Py_None;
+ self->variant_level = 0;
+ if (kwargs) {
+ variant_level = PyDict_GetItem(kwargs, dbus_py_variant_level_const);
+ }
+ if (variant_level) {
+ long new_variant_level = PyLong_AsLong(variant_level);
+ if (new_variant_level == -1 && PyErr_Occurred()) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ self->variant_level = new_variant_level;
+ }
+ return (PyObject *)self;
+}
+
+static int
+Array_tp_init (DBusPyArray *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *obj = dbus_py_empty_tuple;
+ PyObject *signature = NULL;
+ PyObject *tuple;
+ PyObject *variant_level;
+ /* variant_level is accepted but ignored - it's immutable, so
+ * __new__ handles it */
+ static char *argnames[] = {"iterable", "signature", "variant_level", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO:__init__", argnames,
+ &obj, &signature, &variant_level)) {
+ return -1;
+ }
+
+ /* convert signature from a borrowed ref of unknown type to an owned ref
+ of type Signature (or None) */
+ if (!signature) signature = Py_None;
+ if (signature == Py_None
+ || PyObject_IsInstance(signature, (PyObject *)&DBusPySignature_Type)) {
+ Py_INCREF(signature);
+ }
+ else {
+ signature = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
+ "(O)", signature);
+ if (!signature) return -1;
+ }
+
+ if (signature != Py_None) {
+ const char *c_str;
+ PyObject *signature_as_bytes;
+
+ if (
+#ifdef PY3
+ !PyUnicode_Check(signature)
+#else
+ !PyBytes_Check(signature)
+#endif
+ )
+ {
+ PyErr_SetString(PyExc_TypeError, "str expected");
+ Py_CLEAR(signature);
+ return -1;
+ }
+#ifdef PY3
+ if (!(signature_as_bytes = PyUnicode_AsUTF8String(signature))) {
+ Py_CLEAR(signature);
+ return -1;
+ }
+#else
+ signature_as_bytes = signature;
+ Py_INCREF(signature_as_bytes);
+#endif
+
+ c_str = PyBytes_AS_STRING(signature_as_bytes);
+
+ if (!dbus_signature_validate_single(c_str, NULL)) {
+ Py_CLEAR(signature);
+ Py_CLEAR(signature_as_bytes);
+ PyErr_SetString(PyExc_ValueError,
+ "There must be exactly one complete type in "
+ "an Array's signature parameter");
+ return -1;
+ }
+ Py_CLEAR(signature_as_bytes);
+ }
+
+ tuple = Py_BuildValue("(O)", obj);
+ if (!tuple) {
+ Py_CLEAR(signature);
+ return -1;
+ }
+ if ((PyList_Type.tp_init)((PyObject *)self, tuple, NULL) < 0) {
+ Py_CLEAR(tuple);
+ Py_CLEAR(signature);
+ return -1;
+ }
+ Py_CLEAR(tuple);
+
+ Py_CLEAR(self->signature);
+ self->signature = signature;
+ return 0;
+}
+
+PyTypeObject DBusPyArray_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Array",
+ sizeof(DBusPyArray),
+ 0,
+ (destructor)Array_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)Array_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Array_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ Array_tp_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Array_tp_init, /* tp_init */
+ 0, /* tp_alloc */
+ Array_tp_new, /* tp_new */
+};
+
+/* Dict ============================================================= */
+
+PyDoc_STRVAR(Dict_tp_doc,
+"dbus.Dictionary(mapping_or_iterable=(), signature=None, variant_level=0)\n"
+"\n"
+"An mapping whose keys are similar and whose values are similar,\n"
+"implemented as a subtype of dict.\n"
+"\n"
+"As currently implemented, a Dictionary behaves just like a dict, but\n"
+"with the addition of a ``signature`` property set by the constructor;\n"
+"conversion of its items to D-Bus types is only done when it's sent in\n"
+"a Message. This may change in future so validation is done earlier.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+"``signature`` is either a string or None. If a string, it must consist\n"
+"of exactly two complete type signatures, representing the 'key' type\n"
+"(which must be a primitive type, i.e. one of \"bdginoqstuxy\")\n"
+"and the 'value' type. The signature of the whole Dictionary will be\n"
+"``a{xx}`` where ``xx`` is replaced by the given signature.\n"
+"\n"
+"If it is None (the default), when the Dictionary is sent over\n"
+"D-Bus, the key and value signatures will be guessed from an arbitrary\n"
+"element of the Dictionary.\n"
+);
+
+static struct PyMemberDef Dict_tp_members[] = {
+ {"signature", T_OBJECT, offsetof(DBusPyDict, signature), READONLY,
+ "The D-Bus signature of each key in this Dictionary, followed by "
+ "that of each value in this Dictionary, as a Signature instance."},
+ {"variant_level", T_LONG, offsetof(DBusPyDict, variant_level),
+ READONLY,
+ "Indicates how many nested Variant containers this object\n"
+ "is contained in: if a message's wire format has a variant containing a\n"
+ "variant containing a dictionary, this is represented in Python by a\n"
+ "Dictionary with variant_level==2.\n"
+ },
+ {NULL},
+};
+
+static void
+Dict_tp_dealloc (DBusPyDict *self)
+{
+ Py_CLEAR(self->signature);
+ (PyDict_Type.tp_dealloc)((PyObject *)self);
+}
+
+static PyObject *
+Dict_tp_repr(DBusPyDict *self)
+{
+ PyObject *parent_repr = (PyDict_Type.tp_repr)((PyObject *)self);
+ PyObject *sig_repr = PyObject_Repr(self->signature);
+ PyObject *my_repr = NULL;
+ long variant_level = self->variant_level;
+
+ if (!parent_repr) goto finally;
+ if (!sig_repr) goto finally;
+ if (variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, signature=%V, "
+ "variant_level=%ld)",
+ Py_TYPE(&self->super)->tp_name,
+ REPRV(parent_repr),
+ REPRV(sig_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V, signature=%V)",
+ Py_TYPE(&self->super)->tp_name,
+ REPRV(parent_repr),
+ REPRV(sig_repr));
+ }
+finally:
+ Py_CLEAR(parent_repr);
+ Py_CLEAR(sig_repr);
+ return my_repr;
+}
+
+static PyObject *
+Dict_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ DBusPyDict *self = (DBusPyDict *)(PyDict_Type.tp_new)(cls, args, kwargs);
+ PyObject *variant_level = NULL;
+
+ /* variant_level is immutable, so handle it in __new__ rather than
+ __init__ */
+ if (!self) return NULL;
+ Py_INCREF(Py_None);
+ self->signature = Py_None;
+ self->variant_level = 0;
+ if (kwargs) {
+ variant_level = PyDict_GetItem(kwargs, dbus_py_variant_level_const);
+ }
+ if (variant_level) {
+ long new_variant_level = PyLong_AsLong(variant_level);
+
+ if (new_variant_level == -1 && PyErr_Occurred()) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ self->variant_level = new_variant_level;
+ }
+ return (PyObject *)self;
+}
+
+static int
+Dict_tp_init(DBusPyDict *self, PyObject *args, PyObject *kwargs)
+{
+ PyObject *obj = dbus_py_empty_tuple;
+ PyObject *signature = NULL;
+ PyObject *tuple;
+ PyObject *variant_level; /* ignored here - __new__ uses it */
+ static char *argnames[] = {"mapping_or_iterable", "signature",
+ "variant_level", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO:__init__", argnames,
+ &obj, &signature, &variant_level)) {
+ return -1;
+ }
+
+ /* convert signature from a borrowed ref of unknown type to an owned ref
+ of type Signature (or None) */
+ if (!signature) signature = Py_None;
+ if (signature == Py_None
+ || PyObject_IsInstance(signature, (PyObject *)&DBusPySignature_Type)) {
+ Py_INCREF(signature);
+ }
+ else {
+ signature = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
+ "(O)", signature);
+ if (!signature) return -1;
+ }
+
+ if (signature != Py_None) {
+ const char *c_str;
+ PyObject *signature_as_bytes;
+
+ if (!NATIVESTR_CHECK(signature)) {
+ PyErr_SetString(PyExc_TypeError, "str expected");
+ Py_CLEAR(signature);
+ return -1;
+ }
+#ifdef PY3
+ if (!(signature_as_bytes = PyUnicode_AsUTF8String(signature))) {
+ Py_CLEAR(signature);
+ return -1;
+ }
+#else
+ signature_as_bytes = signature;
+ Py_INCREF(signature_as_bytes);
+#endif
+
+ c_str = PyBytes_AS_STRING(signature_as_bytes);
+ switch (c_str[0]) {
+ case DBUS_TYPE_BYTE:
+ case DBUS_TYPE_BOOLEAN:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_UINT16:
+ case DBUS_TYPE_INT32:
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ case DBUS_TYPE_DOUBLE:
+#ifdef WITH_DBUS_FLOAT32
+ case DBUS_TYPE_FLOAT:
+#endif
+#ifdef DBUS_TYPE_UNIX_FD
+ case DBUS_TYPE_UNIX_FD:
+#endif
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ case DBUS_TYPE_SIGNATURE:
+ break;
+ default:
+ Py_CLEAR(signature);
+ Py_CLEAR(signature_as_bytes);
+ PyErr_SetString(PyExc_ValueError,
+ "The key type in a Dictionary's signature "
+ "must be a primitive type");
+ return -1;
+ }
+
+ if (!dbus_signature_validate_single(c_str + 1, NULL)) {
+ Py_CLEAR(signature);
+ Py_CLEAR(signature_as_bytes);
+ PyErr_SetString(PyExc_ValueError,
+ "There must be exactly two complete types in "
+ "a Dictionary's signature parameter");
+ return -1;
+ }
+ Py_CLEAR(signature_as_bytes);
+ }
+
+ tuple = Py_BuildValue("(O)", obj);
+ if (!tuple) {
+ Py_CLEAR(signature);
+ return -1;
+ }
+
+ if ((PyDict_Type.tp_init((PyObject *)self, tuple, NULL)) < 0) {
+ Py_CLEAR(tuple);
+ Py_CLEAR(signature);
+ return -1;
+ }
+ Py_CLEAR(tuple);
+
+ Py_CLEAR(self->signature);
+ self->signature = signature;
+ return 0;
+}
+
+PyTypeObject DBusPyDict_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Dictionary",
+ sizeof(DBusPyDict),
+ 0,
+ (destructor)Dict_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)Dict_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Dict_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ Dict_tp_members, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)Dict_tp_init, /* tp_init */
+ 0, /* tp_alloc */
+ Dict_tp_new, /* tp_new */
+};
+
+/* Struct =========================================================== */
+
+static PyObject *struct_signatures;
+
+PyDoc_STRVAR(Struct_tp_doc,
+"dbus.Struct(iterable, signature=None, variant_level=0)\n"
+"\n"
+"An structure containing items of possibly distinct types.\n"
+"\n"
+"D-Bus structs may not be empty, so the iterable argument is required and\n"
+"may not be an empty iterable.\n"
+"\n"
+"``signature`` is either None, or a string representing the contents of the\n"
+"struct as one or more complete type signatures. The overall signature of\n"
+"the struct will be the given signature enclosed in parentheses, ``()``.\n"
+"\n"
+"If the signature is None (default) it will be guessed\n"
+"from the types of the items during construction.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a struct, this is represented in Python by a\n"
+" Struct with variant_level==2.\n"
+);
+
+static PyObject *
+Struct_tp_repr(PyObject *self)
+{
+ PyObject *parent_repr = (PyTuple_Type.tp_repr)((PyObject *)self);
+ PyObject *sig;
+ PyObject *sig_repr = NULL;
+ PyObject *key;
+ long variant_level;
+ PyObject *my_repr = NULL;
+
+ if (!parent_repr) goto finally;
+ key = PyLong_FromVoidPtr(self);
+ if (!key) goto finally;
+ sig = PyDict_GetItem(struct_signatures, key);
+ Py_CLEAR(key);
+ if (!sig) sig = Py_None;
+ sig_repr = PyObject_Repr(sig);
+ if (!sig_repr) goto finally;
+
+ variant_level = dbus_py_variant_level_get(self);
+ if (variant_level < 0)
+ goto finally;
+
+ if (variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, signature=%V, "
+ "variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ REPRV(sig_repr),
+ variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V, signature=%V)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ REPRV(sig_repr));
+ }
+
+finally:
+ Py_CLEAR(parent_repr);
+ Py_CLEAR(sig_repr);
+ return my_repr;
+}
+
+static PyObject *
+Struct_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *signature = NULL;
+ long variantness = 0;
+ PyObject *self, *key;
+ static char *argnames[] = {"signature", "variant_level", NULL};
+
+ if (PyTuple_Size(args) != 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "__new__ takes exactly one positional parameter");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|Ol:__new__", argnames,
+ &signature, &variantness)) {
+ return NULL;
+ }
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+
+ self = (PyTuple_Type.tp_new)(cls, args, NULL);
+ if (!self)
+ return NULL;
+ if (PyTuple_Size(self) < 1) {
+ PyErr_SetString(PyExc_ValueError, "D-Bus structs may not be empty");
+ Py_CLEAR(self);
+ return NULL;
+ }
+
+ if (!dbus_py_variant_level_set(self, variantness)) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+
+ /* convert signature from a borrowed ref of unknown type to an owned ref
+ of type Signature (or None) */
+ if (!signature) signature = Py_None;
+ if (signature == Py_None
+ || PyObject_IsInstance(signature, (PyObject *)&DBusPySignature_Type)) {
+ Py_INCREF(signature);
+ }
+ else {
+ signature = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
+ "(O)", signature);
+ if (!signature) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ }
+
+ key = PyLong_FromVoidPtr(self);
+ if (!key) {
+ Py_CLEAR(self);
+ Py_CLEAR(signature);
+ return NULL;
+ }
+ if (PyDict_SetItem(struct_signatures, key, signature) < 0) {
+ Py_CLEAR(key);
+ Py_CLEAR(self);
+ Py_CLEAR(signature);
+ return NULL;
+ }
+
+ Py_CLEAR(key);
+ Py_CLEAR(signature);
+ return self;
+}
+
+static void
+Struct_tp_dealloc(PyObject *self)
+{
+ PyObject *et, *ev, *etb, *key;
+
+ dbus_py_variant_level_clear(self);
+ PyErr_Fetch(&et, &ev, &etb);
+
+ key = PyLong_FromVoidPtr(self);
+ if (key) {
+ if (PyDict_GetItem(struct_signatures, key)) {
+ if (PyDict_DelItem(struct_signatures, key) < 0) {
+ /* should never happen */
+ PyErr_WriteUnraisable(self);
+ }
+ }
+ Py_CLEAR(key);
+ }
+ else {
+ /* not enough memory to free all the memory... leak the signature,
+ * there's not much else we could do here */
+ PyErr_WriteUnraisable(self);
+ }
+
+ PyErr_Restore(et, ev, etb);
+ (PyTuple_Type.tp_dealloc)(self);
+}
+
+static PyObject *
+Struct_tp_getattro(PyObject *obj, PyObject *name)
+{
+ PyObject *key, *value;
+
+#ifdef PY3
+ if (PyUnicode_CompareWithASCIIString(name, "signature"))
+ return dbus_py_variant_level_getattro(obj, name);
+#else
+ if (PyBytes_Check(name)) {
+ Py_INCREF(name);
+ }
+ else if (PyUnicode_Check(name)) {
+ name = PyUnicode_AsEncodedString(name, NULL, NULL);
+ if (!name) {
+ return NULL;
+ }
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError, "attribute name must be string");
+ return NULL;
+ }
+
+ if (strcmp(PyBytes_AS_STRING(name), "signature")) {
+ value = dbus_py_variant_level_getattro(obj, name);
+ Py_CLEAR(name);
+ return value;
+ }
+ Py_CLEAR(name);
+#endif /* PY3 */
+
+ key = PyLong_FromVoidPtr(obj);
+
+ if (!key) {
+ return NULL;
+ }
+
+ value = PyDict_GetItem(struct_signatures, key);
+ Py_CLEAR(key);
+
+ if (!value)
+ value = Py_None;
+ Py_INCREF(value);
+ return value;
+}
+
+PyTypeObject DBusPyStruct_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Struct",
+ 0,
+ 0,
+ Struct_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)Struct_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ Struct_tp_getattro, /* tp_getattro */
+ dbus_py_immutable_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Struct_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Struct_tp_new, /* tp_new */
+};
+
+dbus_bool_t
+dbus_py_init_container_types(void)
+{
+ struct_signatures = PyDict_New();
+ if (!struct_signatures) return 0;
+
+ DBusPyArray_Type.tp_base = &PyList_Type;
+ if (PyType_Ready(&DBusPyArray_Type) < 0) return 0;
+ DBusPyArray_Type.tp_print = NULL;
+
+ DBusPyDict_Type.tp_base = &PyDict_Type;
+ if (PyType_Ready(&DBusPyDict_Type) < 0) return 0;
+ DBusPyDict_Type.tp_print = NULL;
+
+ DBusPyStruct_Type.tp_base = &PyTuple_Type;
+ if (PyType_Ready(&DBusPyStruct_Type) < 0) return 0;
+ DBusPyStruct_Type.tp_print = NULL;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_container_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF(&DBusPyArray_Type);
+ if (PyModule_AddObject(this_module, "Array",
+ (PyObject *)&DBusPyArray_Type) < 0) return 0;
+
+ Py_INCREF(&DBusPyDict_Type);
+ if (PyModule_AddObject(this_module, "Dictionary",
+ (PyObject *)&DBusPyDict_Type) < 0) return 0;
+
+ Py_INCREF(&DBusPyStruct_Type);
+ if (PyModule_AddObject(this_module, "Struct",
+ (PyObject *)&DBusPyStruct_Type) < 0) return 0;
+
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
+
diff --git a/dbus_bindings/dbus_bindings-internal.h b/dbus_bindings/dbus_bindings-internal.h
new file mode 100644
index 0000000..b3006b5
--- /dev/null
+++ b/dbus_bindings/dbus_bindings-internal.h
@@ -0,0 +1,296 @@
+/* _dbus_bindings internal API. For use within _dbus_bindings only.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DBUS_BINDINGS_INTERNAL_H
+#define DBUS_BINDINGS_INTERNAL_H
+
+#define PY_SSIZE_T_CLEAN 1
+#define PY_SIZE_T_CLEAN 1
+
+#include <Python.h>
+
+#define INSIDE_DBUS_PYTHON_BINDINGS
+#include "dbus/dbus-python.h"
+
+#if defined(__GNUC__)
+# if __GNUC__ >= 3
+# define UNUSED __attribute__((__unused__))
+# define NORETURN __attribute__((__noreturn__))
+# else
+# define UNUSED /*nothing*/
+# define NORETURN /*nothing*/
+# endif
+#else
+# define UNUSED /*nothing*/
+# define NORETURN /*nothing*/
+#endif
+
+/* no need for extern "C", this is only for internal use */
+
+/* on/off switch for debugging support (see below) */
+#undef USING_DBG
+#if 0 && !defined(DBG_IS_TOO_VERBOSE)
+# define USING_DBG 1
+#endif
+
+#define DEFINE_CHECK(type) \
+static inline int type##_Check (PyObject *o) \
+{ \
+ return (PyObject_TypeCheck (o, &type##_Type)); \
+} \
+static inline int type##_CheckExact (PyObject *o) \
+{ \
+ return (Py_TYPE(o) == &type##_Type); \
+}
+
+/* This is a clever little trick to make writing the various object reprs
+ * easier. It relies on Python's %V format option which consumes two
+ * arguments. The first is a unicode object which may be NULL, and the second
+ * is a char* which will be used if the first parameter is NULL.
+ *
+ * The issue is that we don't know whether the `parent_repr` at the call site
+ * is a unicode or a bytes (a.k.a. 8-bit string). Under Python 3, it will
+ * always be a unicode. Under Python 2 it will *probably* be a bytes/str, but
+ * could potentially be a unicode. So, we check the type, and if it's a
+ * unicode, we pass that as the first argument, leaving NULL as the second
+ * argument (since it will never be checked). However, if the object is not a
+ * unicode, it better be a bytes. In that case, we'll pass NULL as the first
+ * argument so that the second one gets used, and we'll dig the char* out of
+ * the bytes object for that purpose.
+ *
+ * You might think that this would crash if obj is neither a bytes/str or
+ * unicode, and you'd be right *except* that Python doesn't allow any other
+ * types to be returned in the reprs. Also, since obj will always be the repr
+ * of a built-in type, it will never be anything other than a bytes or a
+ * unicode in any version of Python. So in practice, this is safe.
+ */
+#define REPRV(obj) \
+ (PyUnicode_Check(obj) ? (obj) : NULL), \
+ (PyUnicode_Check(obj) ? NULL : PyBytes_AS_STRING(obj))
+
+#ifdef PY3
+#define NATIVEINT_TYPE (PyLong_Type)
+#define NATIVEINT_FROMLONG(x) (PyLong_FromLong(x))
+#define NATIVEINT_ASLONG(x) (PyLong_AsLong(x))
+#define INTORLONG_CHECK(obj) (PyLong_Check(obj))
+#define NATIVESTR_TYPE (PyUnicode_Type)
+#define NATIVESTR_CHECK(obj) (PyUnicode_Check(obj))
+#define NATIVESTR_FROMSTR(obj) (PyUnicode_FromString(obj))
+#else
+#define NATIVEINT_TYPE (PyInt_Type)
+#define NATIVEINT_FROMLONG(x) (PyInt_FromLong(x))
+#define NATIVEINT_ASLONG(x) (PyInt_AsLong(x))
+#define INTORLONG_CHECK(obj) (PyLong_Check(obj) || PyInt_Check(obj))
+#define NATIVESTR_TYPE (PyBytes_Type)
+#define NATIVESTR_CHECK(obj) (PyBytes_Check(obj))
+#define NATIVESTR_FROMSTR(obj) (PyBytes_FromString(obj))
+#endif
+
+#ifdef PY3
+PyMODINIT_FUNC PyInit__dbus_bindings(void);
+#else
+PyMODINIT_FUNC init_dbus_bindings(void);
+#endif
+
+/* conn.c */
+extern PyTypeObject DBusPyConnection_Type;
+DEFINE_CHECK(DBusPyConnection)
+extern dbus_bool_t dbus_py_init_conn_types(void);
+extern dbus_bool_t dbus_py_insert_conn_types(PyObject *this_module);
+
+/* libdbusconn.c */
+extern PyTypeObject DBusPyLibDBusConnection_Type;
+DEFINE_CHECK(DBusPyLibDBusConnection)
+PyObject *DBusPyLibDBusConnection_New(DBusConnection *conn);
+extern dbus_bool_t dbus_py_init_libdbus_conn_types(void);
+extern dbus_bool_t dbus_py_insert_libdbus_conn_types(PyObject *this_module);
+
+/* bus.c */
+extern dbus_bool_t dbus_py_init_bus_types(void);
+extern dbus_bool_t dbus_py_insert_bus_types(PyObject *this_module);
+
+/* exceptions.c */
+extern PyObject *DBusPyException_SetString(const char *msg);
+extern PyObject *DBusPyException_ConsumeError(DBusError *error);
+extern dbus_bool_t dbus_py_init_exception_types(void);
+extern dbus_bool_t dbus_py_insert_exception_types(PyObject *this_module);
+
+/* types */
+extern PyTypeObject DBusPyBoolean_Type;
+DEFINE_CHECK(DBusPyBoolean)
+extern PyTypeObject DBusPyObjectPath_Type, DBusPySignature_Type;
+DEFINE_CHECK(DBusPyObjectPath)
+DEFINE_CHECK(DBusPySignature)
+extern PyTypeObject DBusPyArray_Type, DBusPyDict_Type, DBusPyStruct_Type;
+DEFINE_CHECK(DBusPyArray)
+DEFINE_CHECK(DBusPyDict)
+DEFINE_CHECK(DBusPyStruct)
+extern PyTypeObject DBusPyByte_Type, DBusPyByteArray_Type;
+DEFINE_CHECK(DBusPyByteArray)
+DEFINE_CHECK(DBusPyByte)
+extern PyTypeObject DBusPyString_Type;
+DEFINE_CHECK(DBusPyString)
+#ifndef PY3
+extern PyTypeObject DBusPyUTF8String_Type;
+DEFINE_CHECK(DBusPyUTF8String)
+#endif
+extern PyTypeObject DBusPyDouble_Type;
+DEFINE_CHECK(DBusPyDouble)
+extern PyTypeObject DBusPyInt16_Type, DBusPyUInt16_Type;
+DEFINE_CHECK(DBusPyInt16)
+DEFINE_CHECK(DBusPyUInt16)
+extern PyTypeObject DBusPyInt32_Type, DBusPyUInt32_Type;
+DEFINE_CHECK(DBusPyInt32)
+DEFINE_CHECK(DBusPyUInt32)
+extern PyTypeObject DBusPyUnixFd_Type;
+DEFINE_CHECK(DBusPyUnixFd)
+extern PyTypeObject DBusPyInt64_Type, DBusPyUInt64_Type;
+DEFINE_CHECK(DBusPyInt64)
+DEFINE_CHECK(DBusPyUInt64)
+extern dbus_bool_t dbus_py_init_abstract(void);
+extern dbus_bool_t dbus_py_init_signature(void);
+extern dbus_bool_t dbus_py_init_int_types(void);
+extern dbus_bool_t dbus_py_init_unixfd_type(void);
+extern dbus_bool_t dbus_py_init_string_types(void);
+extern dbus_bool_t dbus_py_init_float_types(void);
+extern dbus_bool_t dbus_py_init_container_types(void);
+extern dbus_bool_t dbus_py_init_byte_types(void);
+extern dbus_bool_t dbus_py_insert_abstract_types(PyObject *this_module);
+extern dbus_bool_t dbus_py_insert_signature(PyObject *this_module);
+extern dbus_bool_t dbus_py_insert_int_types(PyObject *this_module);
+extern dbus_bool_t dbus_py_insert_unixfd_type(PyObject *this_module);
+extern dbus_bool_t dbus_py_insert_string_types(PyObject *this_module);
+extern dbus_bool_t dbus_py_insert_float_types(PyObject *this_module);
+extern dbus_bool_t dbus_py_insert_container_types(PyObject *this_module);
+extern dbus_bool_t dbus_py_insert_byte_types(PyObject *this_module);
+
+int dbus_py_unix_fd_get_fd(PyObject *self);
+
+/* generic */
+extern void dbus_py_take_gil_and_xdecref(PyObject *);
+extern int dbus_py_immutable_setattro(PyObject *, PyObject *, PyObject *);
+extern PyObject *dbus_py_empty_tuple;
+extern dbus_bool_t dbus_py_init_generic(void);
+
+/* message.c */
+extern DBusMessage *DBusPyMessage_BorrowDBusMessage(PyObject *msg);
+extern PyObject *DBusPyMessage_ConsumeDBusMessage(DBusMessage *);
+extern dbus_bool_t dbus_py_init_message_types(void);
+extern dbus_bool_t dbus_py_insert_message_types(PyObject *this_module);
+
+/* pending-call.c */
+extern PyObject *DBusPyPendingCall_ConsumeDBusPendingCall(DBusPendingCall *,
+ PyObject *);
+extern dbus_bool_t dbus_py_init_pending_call(void);
+extern dbus_bool_t dbus_py_insert_pending_call(PyObject *this_module);
+
+/* mainloop.c */
+extern dbus_bool_t dbus_py_set_up_connection(PyObject *conn,
+ PyObject *mainloop);
+extern dbus_bool_t dbus_py_set_up_server(PyObject *server,
+ PyObject *mainloop);
+extern PyObject *dbus_py_get_default_main_loop(void);
+extern dbus_bool_t dbus_py_check_mainloop_sanity(PyObject *);
+extern dbus_bool_t dbus_py_init_mainloop(void);
+extern dbus_bool_t dbus_py_insert_mainloop_types(PyObject *);
+
+/* server.c */
+extern PyTypeObject DBusPyServer_Type;
+DEFINE_CHECK(DBusPyServer)
+extern dbus_bool_t dbus_py_init_server_types(void);
+extern dbus_bool_t dbus_py_insert_server_types(PyObject *this_module);
+extern DBusServer *DBusPyServer_BorrowDBusServer(PyObject *self);
+
+/* validation.c */
+dbus_bool_t dbus_py_validate_bus_name(const char *name,
+ dbus_bool_t may_be_unique,
+ dbus_bool_t may_be_not_unique);
+dbus_bool_t dbus_py_validate_member_name(const char *name);
+dbus_bool_t dbus_py_validate_interface_name(const char *name);
+dbus_bool_t dbus_py_validate_object_path(const char *path);
+#define dbus_py_validate_error_name dbus_py_validate_interface_name
+
+/* debugging support */
+void _dbus_py_assertion_failed(const char *) NORETURN;
+#define DBUS_PY_RAISE_VIA_NULL_IF_FAIL(assertion) \
+ do { if (!(assertion)) { \
+ _dbus_py_assertion_failed(#assertion); \
+ return NULL; \
+ } \
+ } while (0)
+
+#define DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(assertion, label) \
+ do { if (!(assertion)) { \
+ _dbus_py_assertion_failed(#assertion); \
+ goto label; \
+ } \
+ } while (0)
+
+#define DBUS_PY_RAISE_VIA_RETURN_IF_FAIL(assertion, value) \
+ do { if (!(assertion)) { \
+ _dbus_py_assertion_failed(#assertion); \
+ return value; \
+ } \
+ } while (0)
+
+/* verbose debugging support */
+#ifdef USING_DBG
+
+# include <sys/types.h>
+# include <unistd.h>
+
+void _dbus_py_dbg_exc(void);
+void _dbus_py_whereami(void);
+void _dbus_py_dbg_dump_message(DBusMessage *);
+
+# define TRACE(self) do { \
+ fprintf(stderr, "TRACE: <%s at %p> in %s, " \
+ "%d refs\n", \
+ self ? Py_TYPE(self)->tp_name : NULL, \
+ self, __func__, \
+ self ? (int)Py_REFCNT(self) : 0); \
+ } while (0)
+# define DBG(format, ...) fprintf(stderr, "DEBUG: " format "\n",\
+ __VA_ARGS__)
+# define DBG_EXC(format, ...) do {DBG(format, __VA_ARGS__); \
+ _dbus_py_dbg_exc();} while (0)
+# define DBG_DUMP_MESSAGE(x) _dbus_py_dbg_dump_message(x)
+# define DBG_WHEREAMI _dbus_py_whereami()
+
+#else /* !defined(USING_DBG) */
+
+# define TRACE(self) do {} while (0)
+# define DBG(format, ...) do {} while (0)
+# define DBG_EXC(format, ...) do {} while (0)
+# define DBG_DUMP_MESSAGE(x) do {} while (0)
+# define DBG_WHEREAMI do {} while (0)
+
+#endif /* !defined(USING_DBG) */
+
+/* General-purpose Python glue */
+
+#define DEFERRED_ADDRESS(ADDR) 0
+
+#endif
diff --git a/dbus_bindings/debug.c b/dbus_bindings/debug.c
new file mode 100644
index 0000000..4620c86
--- /dev/null
+++ b/dbus_bindings/debug.c
@@ -0,0 +1,96 @@
+/* Debug code for _dbus_bindings.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <stdlib.h>
+
+void
+_dbus_py_assertion_failed(const char *assertion)
+{
+ PyErr_SetString(PyExc_AssertionError, assertion);
+#if 1 || defined(USING_DBG) || defined(FATAL_ASSERTIONS)
+ /* print the Python stack, and dump core so we can see the C stack too */
+ PyErr_Print();
+ abort();
+#endif
+}
+
+#ifdef USING_DBG
+void
+_dbus_py_whereami(void)
+{
+ PyObject *c, *v, *t;
+ /* This is a little mad. We want to get the traceback without
+ clearing the error indicator, if any. */
+ PyErr_Fetch(&c, &v, &t); /* 3 new refs */
+ Py_XINCREF(c); Py_XINCREF(v); Py_XINCREF(t); /* now we own 6 refs */
+ PyErr_Restore(c, v, t); /* steals 3 refs */
+
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_AssertionError,
+ "No error, but plz provide traceback kthx");
+ }
+ PyErr_Print();
+
+ PyErr_Restore(c, v, t); /* steals another 3 refs */
+}
+
+void
+_dbus_py_dbg_exc(void)
+{
+ PyObject *c, *v, *t;
+ /* This is a little mad. We want to get the traceback without
+ clearing the error indicator. */
+ PyErr_Fetch(&c, &v, &t); /* 3 new refs */
+ Py_XINCREF(c); Py_XINCREF(v); Py_XINCREF(t); /* now we own 6 refs */
+ PyErr_Restore(c, v, t); /* steals 3 refs */
+ PyErr_Print();
+ PyErr_Restore(c, v, t); /* steals another 3 refs */
+}
+
+void
+_dbus_py_dbg_dump_message(DBusMessage *message)
+{
+ const char *s;
+ fprintf(stderr, "DBusMessage at %p\n", message);
+
+ s = dbus_message_get_destination(message);
+ if (!s) s = "(null)";
+ fprintf(stderr, "\tdestination %s\n", s);
+
+ s = dbus_message_get_interface(message);
+ if (!s) s = "(null)";
+ fprintf(stderr, "\tinterface %s\n", s);
+
+ s = dbus_message_get_member(message);
+ if (!s) s = "(null)";
+ fprintf(stderr, "\tmember %s\n", s);
+
+ s = dbus_message_get_path(message);
+ if (!s) s = "(null)";
+ fprintf(stderr, "\tpath %s\n", s);
+}
+#endif
diff --git a/dbus_bindings/exceptions.c b/dbus_bindings/exceptions.c
new file mode 100644
index 0000000..6721f5c
--- /dev/null
+++ b/dbus_bindings/exceptions.c
@@ -0,0 +1,104 @@
+/* D-Bus exception base classes.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+static PyObject *imported_dbus_exception = NULL;
+
+static dbus_bool_t
+import_exception(void)
+{
+ PyObject *name;
+ PyObject *exceptions;
+
+ if (imported_dbus_exception != NULL) {
+ return TRUE;
+ }
+
+ name = NATIVESTR_FROMSTR("dbus.exceptions");
+ if (name == NULL) {
+ return FALSE;
+ }
+ exceptions = PyImport_Import(name);
+ Py_CLEAR(name);
+ if (exceptions == NULL) {
+ return FALSE;
+ }
+ imported_dbus_exception = PyObject_GetAttrString(exceptions,
+ "DBusException");
+ Py_CLEAR(exceptions);
+
+ return (imported_dbus_exception != NULL);
+}
+
+PyObject *
+DBusPyException_SetString(const char *msg)
+{
+ if (imported_dbus_exception != NULL || import_exception()) {
+ PyErr_SetString(imported_dbus_exception, msg);
+ }
+ return NULL;
+}
+
+PyObject *
+DBusPyException_ConsumeError(DBusError *error)
+{
+ PyObject *exc_value = NULL;
+
+ if (imported_dbus_exception == NULL && !import_exception()) {
+ goto finally;
+ }
+
+ exc_value = PyObject_CallFunction(imported_dbus_exception,
+ "s",
+ error->message ? error->message
+ : "");
+
+ if (!exc_value) {
+ goto finally;
+ }
+
+ if (error->name) {
+ PyObject *name = NATIVESTR_FROMSTR(error->name);
+ int ret;
+
+ if (!name)
+ goto finally;
+ ret = PyObject_SetAttrString(exc_value, "_dbus_error_name", name);
+ Py_CLEAR(name);
+ if (ret < 0) {
+ goto finally;
+ }
+ }
+
+ PyErr_SetObject(imported_dbus_exception, exc_value);
+
+finally:
+ Py_CLEAR(exc_value);
+ dbus_error_free(error);
+ return NULL;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/float.c b/dbus_bindings/float.c
new file mode 100644
index 0000000..9d05c19
--- /dev/null
+++ b/dbus_bindings/float.c
@@ -0,0 +1,156 @@
+/* Simple D-Bus types: Double and (with appropriate #defines) Float
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "types-internal.h"
+
+PyDoc_STRVAR(Double_tp_doc,
+"A double-precision floating point number (a subtype of float).");
+
+#ifdef WITH_DBUS_FLOAT32
+PyDoc_STRVAR(Float_tp_doc,
+"A single-precision floating point number (a subtype of float).");
+#endif
+
+PyTypeObject DBusPyDouble_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Double",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Double_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPythonFloatType), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+#ifdef WITH_DBUS_FLOAT32
+
+PyTypeObject DBusPyFloat_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Float",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Float_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPythonFloatType), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+#endif /* defined(WITH_DBUS_FLOAT32) */
+
+dbus_bool_t
+dbus_py_init_float_types(void)
+{
+ DBusPyDouble_Type.tp_base = &DBusPyFloatBase_Type;
+ if (PyType_Ready(&DBusPyDouble_Type) < 0) return 0;
+ DBusPyDouble_Type.tp_print = NULL;
+
+#ifdef WITH_DBUS_FLOAT32
+ DBusPyFloat_Type.tp_base = &DBusPyFloatBase_Type;
+ if (PyType_Ready(&DBusPyFloat_Type) < 0) return 0;
+ DBusPyFloat_Type.tp_print = NULL;
+#endif
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_float_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF(&DBusPyDouble_Type);
+ if (PyModule_AddObject(this_module, "Double",
+ (PyObject *)&DBusPyDouble_Type) < 0) return 0;
+#ifdef WITH_DBUS_FLOAT32
+ Py_INCREF(&DBusPyFloat_Type);
+ if (PyModule_AddObject(this_module, "Float",
+ (PyObject *)&DBusPyFloat_Type) < 0) return 0;
+#endif
+
+ return 1;
+}
diff --git a/dbus_bindings/generic.c b/dbus_bindings/generic.c
new file mode 100644
index 0000000..ed12025
--- /dev/null
+++ b/dbus_bindings/generic.c
@@ -0,0 +1,61 @@
+/* General Python glue code, used in _dbus_bindings but not actually anything
+ * to do with D-Bus.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+/* The empty tuple, held globally since dbus-python turns out to use it quite
+ * a lot
+ */
+PyObject *dbus_py_empty_tuple = NULL;
+
+int
+dbus_py_immutable_setattro(PyObject *obj UNUSED,
+ PyObject *name UNUSED,
+ PyObject *value UNUSED)
+{
+ PyErr_SetString(PyExc_AttributeError, "Object is immutable");
+ return -1;
+}
+
+/* Take the global interpreter lock and decrement the reference count.
+ * Suitable for calling from a C callback. */
+void
+dbus_py_take_gil_and_xdecref(PyObject *obj)
+{
+ PyGILState_STATE gil = PyGILState_Ensure();
+ Py_CLEAR(obj);
+ PyGILState_Release(gil);
+}
+
+dbus_bool_t
+dbus_py_init_generic(void)
+{
+ dbus_py_empty_tuple = PyTuple_New(0);
+ if (!dbus_py_empty_tuple) return 0;
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/int.c b/dbus_bindings/int.c
new file mode 100644
index 0000000..109d4c7
--- /dev/null
+++ b/dbus_bindings/int.c
@@ -0,0 +1,784 @@
+/* Simple D-Bus types: integers of various sizes, and ObjectPath.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include "types-internal.h"
+
+#ifdef PY3
+#define INTBASE (DBusPyLongBase_Type)
+#define LONG_TYPE_NAME "int"
+#else
+#define INTBASE (DBusPyIntBase_Type)
+#define LONG_TYPE_NAME "long"
+#endif
+
+/* Specific types =================================================== */
+
+/* Boolean, a subclass of DBusPythonInt ============================= */
+
+PyDoc_STRVAR(Boolean_tp_doc,
+"dbus.Boolean(value: bool[, variant_level: int])\n"
+"\n"
+"A boolean, represented as a subtype of ``int`` (not ``bool``, because ``bool``\n"
+"cannot be subclassed).\n"
+"\n"
+"``value`` is converted to 0 or 1 as if by ``int(bool(value))``.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a boolean, this is represented in Python by a\n"
+" Boolean with variant_level==2.\n"
+);
+
+static PyObject *
+Boolean_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *tuple, *self, *value = Py_None;
+ long variantness = 0;
+ static char *argnames[] = {"_", "variant_level", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Ol:__new__", argnames,
+ &value, &variantness)) return NULL;
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+ tuple = Py_BuildValue("(i)", PyObject_IsTrue(value) ? 1 : 0);
+ if (!tuple) return NULL;
+ self = (INTBASE.tp_new)(cls, tuple, kwargs);
+ Py_CLEAR(tuple);
+ return self;
+}
+
+static PyObject *
+Boolean_tp_repr(PyObject *self)
+{
+ int is_true = PyObject_IsTrue(self);
+#ifdef PY3
+ long variant_level = dbus_py_variant_level_get(self);
+ if (variant_level < 0)
+ return NULL;
+#else
+ long variant_level = ((DBusPyIntBase *)self)->variant_level;
+#endif
+
+ if (is_true == -1)
+ return NULL;
+
+ if (variant_level > 0) {
+ return PyUnicode_FromFormat("%s(%s, variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ is_true ? "True" : "False",
+ variant_level);
+ }
+ return PyUnicode_FromFormat("%s(%s)",
+ Py_TYPE(self)->tp_name,
+ is_true ? "True" : "False");
+}
+
+PyTypeObject DBusPyBoolean_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Boolean",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ Boolean_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Boolean_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&INTBASE), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Boolean_tp_new, /* tp_new */
+};
+
+/* Int16 ============================================================ */
+
+PyDoc_STRVAR(Int16_tp_doc,
+"dbus.Int16(value: int[, variant_level: int])\n"
+"\n"
+"A signed 16-bit integer between -0x8000 and +0x7FFF, represented as\n"
+"a subtype of `int`.\n"
+"\n"
+"value must be within the allowed range, or OverflowError will be\n"
+"raised.\n"
+"\n"
+" variant_level must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing an int16, this is represented in Python by an\n"
+" Int16 with variant_level==2.\n"
+);
+
+dbus_int16_t
+dbus_py_int16_range_check(PyObject *obj)
+{
+ long i = PyLong_AsLong(obj);
+ if (i == -1 && PyErr_Occurred())
+ return -1;
+
+ if (i < -0x8000 || i > 0x7fff) {
+ PyErr_Format(PyExc_OverflowError, "Value %d out of range for Int16",
+ (int)i);
+ return -1;
+ }
+ return (dbus_int16_t)i;
+}
+
+static PyObject *
+Int16_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self = (INTBASE.tp_new)(cls, args, kwargs);
+ if (self && dbus_py_int16_range_check(self) == -1 && PyErr_Occurred()) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ return self;
+}
+
+PyTypeObject DBusPyInt16_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Int16",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Int16_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&INTBASE), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Int16_tp_new, /* tp_new */
+};
+
+/* UInt16 =========================================================== */
+
+PyDoc_STRVAR(UInt16_tp_doc,
+"dbus.UInt16(value: int[, variant_level: int])\n"
+"\n"
+"An unsigned 16-bit integer between 0 and 0xFFFF, represented as\n"
+"a subtype of ``int``.\n"
+"\n"
+"``value`` must be within the allowed range, or `OverflowError` will be\n"
+"raised.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a uint16, this is represented in Python by a\n"
+" UInt16 with variant_level==2.\n"
+);
+
+dbus_uint16_t
+dbus_py_uint16_range_check(PyObject *obj)
+{
+ long i = PyLong_AsLong(obj);
+ if (i == -1 && PyErr_Occurred())
+ return (dbus_uint16_t)(-1);
+
+ if (i < 0 || i > 0xffff) {
+ PyErr_Format(PyExc_OverflowError, "Value %d out of range for UInt16",
+ (int)i);
+ return (dbus_uint16_t)(-1);
+ }
+ return (dbus_uint16_t)i;
+}
+
+static PyObject *
+UInt16_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self = (INTBASE.tp_new)(cls, args, kwargs);
+ if (self && dbus_py_uint16_range_check(self) == (dbus_uint16_t)(-1)
+ && PyErr_Occurred())
+ {
+ Py_CLEAR (self);
+ return NULL;
+ }
+ return self;
+}
+
+PyTypeObject DBusPyUInt16_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.UInt16",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ UInt16_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&INTBASE), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ UInt16_tp_new, /* tp_new */
+};
+
+/* Int32 ============================================================ */
+
+PyDoc_STRVAR(Int32_tp_doc,
+"dbus.Int32(value: int[, variant_level: int])\n"
+"\n"
+"A signed 32-bit integer between -0x8000 0000 and +0x7FFF FFFF, represented as\n"
+"a subtype of ``int``.\n"
+"\n"
+"``value`` must be within the allowed range, or `OverflowError` will be\n"
+"raised.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing an int32, this is represented in Python by an\n"
+" Int32 with variant_level==2.\n"
+);
+
+dbus_int32_t
+dbus_py_int32_range_check(PyObject *obj)
+{
+ long i = PyLong_AsLong(obj);
+ if (i == -1 && PyErr_Occurred())
+ return -1;
+
+ if (i < INT32_MIN || i > INT32_MAX) {
+ PyErr_Format(PyExc_OverflowError, "Value %d out of range for Int32",
+ (int)i);
+ return -1;
+ }
+ return (dbus_int32_t)i;
+}
+
+static PyObject *
+Int32_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self = (INTBASE.tp_new)(cls, args, kwargs);
+ if (self && dbus_py_int32_range_check(self) == -1 && PyErr_Occurred()) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ return self;
+}
+
+PyTypeObject DBusPyInt32_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Int32",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Int32_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&INTBASE), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Int32_tp_new, /* tp_new */
+};
+
+/* UInt32 =========================================================== */
+
+PyDoc_STRVAR(UInt32_tp_doc,
+"dbus.UInt32(value: " LONG_TYPE_NAME "[, variant_level: int])\n"
+"\n"
+"An unsigned 32-bit integer between 0 and 0xFFFF FFFF, represented as a\n"
+"subtype of ``long`` in Python 2 or ``int`` in Python 3.\n"
+"\n"
+"``value`` must be within the allowed range, or `OverflowError` will be\n"
+"raised.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a uint32, this is represented in Python by a\n"
+" UInt32 with variant_level==2.\n"
+);
+
+dbus_uint32_t
+dbus_py_uint32_range_check(PyObject *obj)
+{
+ unsigned long i;
+ PyObject *long_obj = PyNumber_Long(obj);
+
+ if (!long_obj) return (dbus_uint32_t)(-1);
+ i = PyLong_AsUnsignedLong(long_obj);
+ if (i == (unsigned long)(-1) && PyErr_Occurred()) {
+ Py_CLEAR(long_obj);
+ return (dbus_uint32_t)(-1);
+ }
+ if (i > UINT32_MAX) {
+ PyErr_Format(PyExc_OverflowError, "Value %d out of range for UInt32",
+ (int)i);
+ Py_CLEAR(long_obj);
+ return (dbus_uint32_t)(-1);
+ }
+ Py_CLEAR(long_obj);
+ return i;
+}
+
+static PyObject *
+UInt32_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self = (DBusPyLongBase_Type.tp_new)(cls, args, kwargs);
+ if (self && dbus_py_uint32_range_check(self) == (dbus_uint32_t)(-1)
+ && PyErr_Occurred()) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ return self;
+}
+
+PyTypeObject DBusPyUInt32_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.UInt32",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ UInt32_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPyLongBase_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ UInt32_tp_new, /* tp_new */
+};
+
+/* Int64 =========================================================== */
+
+PyDoc_STRVAR(Int64_tp_doc,
+"dbus.Int64(value: " LONG_TYPE_NAME "[, variant_level: int])\n"
+"\n"
+"A signed 64-bit integer between -0x8000 0000 0000 0000 and\n"
+"+0x7FFF FFFF FFFF FFFF, represented as a\n"
+"subtype of ``long`` in Python 2 or ``int`` in Python 3.\n"
+"\n"
+"Note that this may be changed in future to be a subtype of `int` on\n"
+"64-bit platforms; applications should not rely on either behaviour.\n"
+"\n"
+"``value`` must be within the allowed range, or `OverflowError` will be\n"
+"raised.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing an int64, this is represented in Python by an\n"
+" Int64 with variant_level==2.\n"
+);
+
+#ifdef DBUS_PYTHON_64_BIT_WORKS
+dbus_int64_t
+dbus_py_int64_range_check(PyObject *obj)
+{
+ PY_LONG_LONG i;
+ PyObject *long_obj = PyNumber_Long(obj);
+
+ if (!long_obj) return -1;
+ i = PyLong_AsLongLong(long_obj);
+ if (i == -1 && PyErr_Occurred()) {
+ Py_CLEAR(long_obj);
+ return -1;
+ }
+ if (i < INT64_MIN || i > INT64_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "Value out of range for Int64");
+ Py_CLEAR(long_obj);
+ return -1;
+ }
+ Py_CLEAR(long_obj);
+ return i;
+}
+#endif
+
+static PyObject *
+Int64_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+#ifdef DBUS_PYTHON_64_BIT_WORKS
+ PyObject *self = (DBusPyLongBase_Type.tp_new)(cls, args, kwargs);
+ if (self && dbus_py_int64_range_check(self) == -1 && PyErr_Occurred()) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ return self;
+#else
+ PyErr_SetString(PyExc_NotImplementedError,
+ "64-bit types are not available on this platform");
+ return NULL;
+#endif
+}
+
+PyTypeObject DBusPyInt64_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Int64",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Int64_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPyLongBase_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Int64_tp_new, /* tp_new */
+};
+
+/* UInt64 =========================================================== */
+
+PyDoc_STRVAR(UInt64_tp_doc,
+"dbus.UInt64(value: " LONG_TYPE_NAME "[, variant_level: int])\n"
+"\n"
+"An unsigned 64-bit integer between 0 and 0xFFFF FFFF FFFF FFFF,\n"
+"subtype of ``long`` in Python 2 or ``int`` in Python 3.\n"
+"\n"
+"``value`` must be within the allowed range, or `OverflowError` will be\n"
+"raised.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a uint64, this is represented in Python by a\n"
+" UInt64 with variant_level==2.\n"
+);
+
+dbus_uint64_t
+dbus_py_uint64_range_check(PyObject *obj)
+{
+ unsigned PY_LONG_LONG i;
+ PyObject *long_obj = PyNumber_Long(obj);
+
+ if (!long_obj) return (dbus_uint64_t)(-1);
+ i = PyLong_AsUnsignedLongLong(long_obj);
+ if (i == (unsigned PY_LONG_LONG)(-1) && PyErr_Occurred()) {
+ Py_CLEAR(long_obj);
+ return (dbus_uint64_t)(-1);
+ }
+ if (i > UINT64_MAX) {
+ PyErr_SetString(PyExc_OverflowError, "Value out of range for UInt64");
+ Py_CLEAR(long_obj);
+ return (dbus_uint64_t)(-1);
+ }
+ Py_CLEAR(long_obj);
+ return i;
+}
+
+static PyObject *
+UInt64_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+#ifdef DBUS_PYTHON_64_BIT_WORKS
+ PyObject *self = (DBusPyLongBase_Type.tp_new)(cls, args, kwargs);
+ if (self && dbus_py_uint64_range_check(self) == (dbus_uint64_t)(-1)
+ && PyErr_Occurred()) {
+ Py_CLEAR(self);
+ return NULL;
+ }
+ return self;
+#else
+ PyErr_SetString(PyExc_NotImplementedError,
+ "64-bit integer types are not supported on this platform");
+ return NULL;
+#endif
+}
+
+PyTypeObject DBusPyUInt64_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.UInt64",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ UInt64_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPyLongBase_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ UInt64_tp_new, /* tp_new */
+};
+
+dbus_bool_t
+dbus_py_init_int_types(void)
+{
+ DBusPyInt16_Type.tp_base = &INTBASE;
+ if (PyType_Ready(&DBusPyInt16_Type) < 0) return 0;
+ /* disable the tp_print copied from PyInt_Type, so tp_repr gets called as
+ desired */
+ DBusPyInt16_Type.tp_print = NULL;
+
+ DBusPyUInt16_Type.tp_base = &INTBASE;
+ if (PyType_Ready(&DBusPyUInt16_Type) < 0) return 0;
+ DBusPyUInt16_Type.tp_print = NULL;
+
+ DBusPyInt32_Type.tp_base = &INTBASE;
+ if (PyType_Ready(&DBusPyInt32_Type) < 0) return 0;
+ DBusPyInt32_Type.tp_print = NULL;
+
+ DBusPyUInt32_Type.tp_base = &DBusPyLongBase_Type;
+ if (PyType_Ready(&DBusPyUInt32_Type) < 0) return 0;
+ DBusPyUInt32_Type.tp_print = NULL;
+
+#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
+ DBusPyInt64_Type.tp_base = &DBusPyLongBase_Type;
+ if (PyType_Ready(&DBusPyInt64_Type) < 0) return 0;
+ DBusPyInt64_Type.tp_print = NULL;
+
+ DBusPyUInt64_Type.tp_base = &DBusPyLongBase_Type;
+ if (PyType_Ready(&DBusPyUInt64_Type) < 0) return 0;
+ DBusPyUInt64_Type.tp_print = NULL;
+#endif
+
+ DBusPyBoolean_Type.tp_base = &INTBASE;
+ if (PyType_Ready(&DBusPyBoolean_Type) < 0) return 0;
+ DBusPyBoolean_Type.tp_print = NULL;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_int_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF(&DBusPyInt16_Type);
+ Py_INCREF(&DBusPyUInt16_Type);
+ Py_INCREF(&DBusPyInt32_Type);
+ Py_INCREF(&DBusPyUInt32_Type);
+ Py_INCREF(&DBusPyInt64_Type);
+ Py_INCREF(&DBusPyUInt64_Type);
+ Py_INCREF(&DBusPyBoolean_Type);
+ if (PyModule_AddObject(this_module, "Int16",
+ (PyObject *)&DBusPyInt16_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "UInt16",
+ (PyObject *)&DBusPyUInt16_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "Int32",
+ (PyObject *)&DBusPyInt32_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "UInt32",
+ (PyObject *)&DBusPyUInt32_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "Int64",
+ (PyObject *)&DBusPyInt64_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "UInt64",
+ (PyObject *)&DBusPyUInt64_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "Boolean",
+ (PyObject *)&DBusPyBoolean_Type) < 0) return 0;
+
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/libdbusconn.c b/dbus_bindings/libdbusconn.c
new file mode 100644
index 0000000..e9401fe
--- /dev/null
+++ b/dbus_bindings/libdbusconn.c
@@ -0,0 +1,126 @@
+/* An extremely thin wrapper around a libdbus Connection, for use by
+ * Server.
+ *
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+#include "conn-internal.h"
+
+PyDoc_STRVAR(DBusPyLibDBusConnection_tp_doc,
+"A reference to a ``DBusConnection`` from ``libdbus``, which might not\n"
+"have been attached to a `dbus.connection.Connection` yet.\n"
+"\n"
+"Cannot be instantiated from Python. The only use of this object is to\n"
+"pass it to the ``dbus.connection.Connection`` constructor instead of an\n"
+"address.\n"
+);
+
+/** Create a DBusPyLibDBusConnection from a DBusConnection.
+ */
+PyObject *
+DBusPyLibDBusConnection_New(DBusConnection *conn)
+{
+ DBusPyLibDBusConnection *self = NULL;
+
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(conn);
+
+ self = (DBusPyLibDBusConnection *)(DBusPyLibDBusConnection_Type.tp_alloc(
+ &DBusPyLibDBusConnection_Type, 0));
+
+ if (!self)
+ return NULL;
+
+ self->conn = dbus_connection_ref (conn);
+
+ return (PyObject *)self;
+}
+
+/* Destructor */
+static void
+DBusPyLibDBusConnection_tp_dealloc(Connection *self)
+{
+ DBusConnection *conn = self->conn;
+ PyObject *et, *ev, *etb;
+
+ /* avoid clobbering any pending exception */
+ PyErr_Fetch(&et, &ev, &etb);
+
+ self->conn = NULL;
+
+ if (conn) {
+ dbus_connection_unref(conn);
+ }
+
+ PyErr_Restore(et, ev, etb);
+ (Py_TYPE(self)->tp_free)((PyObject *) self);
+}
+
+PyTypeObject DBusPyLibDBusConnection_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_dbus_bindings._LibDBusConnection",
+ sizeof(DBusPyLibDBusConnection),
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)DBusPyLibDBusConnection_tp_dealloc,
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT,
+ DBusPyLibDBusConnection_tp_doc,
+};
+
+dbus_bool_t
+dbus_py_init_libdbus_conn_types(void)
+{
+ if (PyType_Ready(&DBusPyLibDBusConnection_Type) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+dbus_bool_t
+dbus_py_insert_libdbus_conn_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF (&DBusPyLibDBusConnection_Type);
+
+ if (PyModule_AddObject(this_module, "_LibDBusConnection",
+ (PyObject *)&DBusPyLibDBusConnection_Type) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/mainloop.c b/dbus_bindings/mainloop.c
new file mode 100644
index 0000000..59d1e99
--- /dev/null
+++ b/dbus_bindings/mainloop.c
@@ -0,0 +1,207 @@
+/* Implementation of main-loop integration for dbus-python.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008 Huang Peng <phuang@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+/* Native mainloop wrapper ========================================= */
+
+PyDoc_STRVAR(NativeMainLoop_tp_doc,
+"Object representing D-Bus main loop integration done in native code.\n"
+"Cannot be instantiated directly.\n"
+);
+
+static PyTypeObject NativeMainLoop_Type;
+
+DEFINE_CHECK(NativeMainLoop)
+
+typedef struct {
+ PyObject_HEAD
+ /* Called with the GIL held, should set a Python exception on error */
+ dbus_bool_t (*set_up_connection_cb)(DBusConnection *, void *);
+ dbus_bool_t (*set_up_server_cb)(DBusServer *, void *);
+ /* Called in a destructor. Must not touch the exception state (use
+ * PyErr_Fetch and PyErr_Restore if necessary). */
+ void (*free_cb)(void *);
+ void *data;
+} NativeMainLoop;
+
+static void NativeMainLoop_tp_dealloc(NativeMainLoop *self)
+{
+ if (self->data && self->free_cb) {
+ (self->free_cb)(self->data);
+ }
+ PyObject_Del((PyObject *)self);
+}
+
+static PyTypeObject NativeMainLoop_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.mainloop.NativeMainLoop",
+ sizeof(NativeMainLoop),
+ 0,
+ (destructor)NativeMainLoop_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ NativeMainLoop_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ /* deliberately not callable! */
+ 0, /* tp_new */
+};
+
+/* Internal C API for Connection, Bus, Server ======================= */
+
+dbus_bool_t
+dbus_py_check_mainloop_sanity(PyObject *mainloop)
+{
+ if (NativeMainLoop_Check(mainloop)) {
+ return TRUE;
+ }
+ PyErr_SetString(PyExc_TypeError,
+ "A dbus.mainloop.NativeMainLoop instance is required");
+ return FALSE;
+}
+
+dbus_bool_t
+dbus_py_set_up_connection(PyObject *conn, PyObject *mainloop)
+{
+ if (NativeMainLoop_Check(mainloop)) {
+ /* Native mainloops are allowed to do arbitrary strange things */
+ NativeMainLoop *nml = (NativeMainLoop *)mainloop;
+ DBusConnection *dbc = DBusPyConnection_BorrowDBusConnection(conn);
+
+ if (!dbc) {
+ return FALSE;
+ }
+ return (nml->set_up_connection_cb)(dbc, nml->data);
+ }
+ PyErr_SetString(PyExc_TypeError,
+ "A dbus.mainloop.NativeMainLoop instance is required");
+ return FALSE;
+}
+
+dbus_bool_t
+dbus_py_set_up_server(PyObject *server, PyObject *mainloop)
+{
+ if (NativeMainLoop_Check(mainloop)) {
+ /* Native mainloops are allowed to do arbitrary strange things */
+ NativeMainLoop *nml = (NativeMainLoop *)mainloop;
+ DBusServer *dbs = DBusPyServer_BorrowDBusServer(server);
+
+ if (!dbs) {
+ return FALSE;
+ }
+ return (nml->set_up_server_cb)(dbs, nml->data);
+ }
+ PyErr_SetString(PyExc_TypeError,
+ "A dbus.mainloop.NativeMainLoop instance is required");
+ return FALSE;
+}
+
+/* C API ============================================================ */
+
+PyObject *
+DBusPyNativeMainLoop_New4(dbus_bool_t (*conn_cb)(DBusConnection *, void *),
+ dbus_bool_t (*server_cb)(DBusServer *, void *),
+ void (*free_cb)(void *),
+ void *data)
+{
+ NativeMainLoop *self = PyObject_New(NativeMainLoop, &NativeMainLoop_Type);
+ if (self) {
+ self->data = data;
+ self->free_cb = free_cb;
+ self->set_up_connection_cb = conn_cb;
+ self->set_up_server_cb = server_cb;
+ }
+ return (PyObject *)self;
+}
+
+/* Null mainloop implementation ===================================== */
+
+static dbus_bool_t
+noop_main_loop_cb(void *conn_or_server UNUSED, void *data UNUSED)
+{
+ return TRUE;
+}
+
+#define noop_conn_cb ((dbus_bool_t (*)(DBusConnection *, void *))(noop_main_loop_cb))
+#define noop_server_cb ((dbus_bool_t (*)(DBusServer *, void *))(noop_main_loop_cb))
+
+/* Initialization =================================================== */
+
+dbus_bool_t
+dbus_py_init_mainloop(void)
+{
+ if (PyType_Ready (&NativeMainLoop_Type) < 0) return 0;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_mainloop_types(PyObject *this_module)
+{
+ PyObject *null_main_loop = DBusPyNativeMainLoop_New4(noop_conn_cb,
+ noop_server_cb,
+ NULL,
+ NULL);
+ if (!null_main_loop) return 0;
+
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF (&NativeMainLoop_Type);
+ if (PyModule_AddObject (this_module, "NativeMainLoop",
+ (PyObject *)&NativeMainLoop_Type) < 0) return 0;
+ if (PyModule_AddObject (this_module, "NULL_MAIN_LOOP",
+ null_main_loop) < 0) return 0;
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/message-append.c b/dbus_bindings/message-append.c
new file mode 100644
index 0000000..0480ceb
--- /dev/null
+++ b/dbus_bindings/message-append.c
@@ -0,0 +1,1294 @@
+/* D-Bus Message serialization. This contains all the logic to map from
+ * Python objects to D-Bus types.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <assert.h>
+
+#define DBG_IS_TOO_VERBOSE
+#include "compat-internal.h"
+#include "types-internal.h"
+#include "message-internal.h"
+
+/* Return the number of variants wrapping the given object. Return 0
+ * if the object is not a D-Bus type.
+ */
+static long
+get_variant_level(PyObject *obj)
+{
+ if (DBusPyString_Check(obj)) {
+ return ((DBusPyString *)obj)->variant_level;
+ }
+#ifndef PY3
+ else if (DBusPyIntBase_Check(obj)) {
+ return ((DBusPyIntBase *)obj)->variant_level;
+ }
+#endif
+ else if (DBusPyFloatBase_Check(obj)) {
+ return ((DBusPyFloatBase *)obj)->variant_level;
+ }
+ else if (DBusPyArray_Check(obj)) {
+ return ((DBusPyArray *)obj)->variant_level;
+ }
+ else if (DBusPyDict_Check(obj)) {
+ return ((DBusPyDict *)obj)->variant_level;
+ }
+ else if (DBusPyLongBase_Check(obj) ||
+#ifdef PY3
+ DBusPyBytesBase_Check(obj) ||
+#endif
+ DBusPyStrBase_Check(obj) ||
+ DBusPyStruct_Check(obj)) {
+ return dbus_py_variant_level_get(obj);
+ }
+ else {
+ return 0;
+ }
+}
+
+char dbus_py_Message_append__doc__[] = (
+"message.append(*args, **kwargs)\n"
+"\n"
+"Set the message's arguments from the positional parameter, according to\n"
+"the signature given by the ``signature`` keyword parameter.\n"
+"\n"
+"The following type conversions are supported:\n\n"
+"=============================== ===========================\n"
+"D-Bus (in signature) Python\n"
+"=============================== ===========================\n"
+"boolean (b) any object (via bool())\n"
+"byte (y) string of length 1\n"
+" any integer\n"
+"any integer type any integer\n"
+"double (d) any float\n"
+"object path anything with a __dbus_object_path__ attribute\n"
+"string, signature, object path str (must be UTF-8) or unicode\n"
+"dict (a{...}) any mapping\n"
+"array (a...) any iterable over appropriate objects\n"
+"struct ((...)) any iterable over appropriate objects\n"
+"variant any object above (guess type as below)\n"
+"=============================== ===========================\n"
+"\n"
+"Here 'any integer' means anything on which int() or long()\n"
+"(as appropriate) will work, except for basestring subclasses.\n"
+"'Any float' means anything on which float() will work, except\n"
+"for basestring subclasses.\n"
+"\n"
+"If there is no signature, guess from the arguments using\n"
+"the static method `Message.guess_signature`.\n"
+);
+
+char dbus_py_Message_guess_signature__doc__[] = (
+"guess_signature(*args) -> Signature [static method]\n\n"
+"Guess a D-Bus signature which should be used to encode the given\n"
+"Python objects.\n"
+"\n"
+"The signature is constructed as follows:\n\n"
+"+-------------------------------+---------------------------+\n"
+"|Python |D-Bus |\n"
+"+===============================+===========================+\n"
+"|D-Bus type, variant_level > 0 |variant (v) |\n"
+"+-------------------------------+---------------------------+\n"
+"|D-Bus type, variant_level == 0 |the corresponding type |\n"
+"+-------------------------------+---------------------------+\n"
+"|anything with a |object path |\n"
+"|__dbus_object_path__ attribute | |\n"
+"+-------------------------------+---------------------------+\n"
+"|bool |boolean (y) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other int subclass |int32 (i) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other long subclass |int64 (x) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other float subclass |double (d) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other str subclass |string (s) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other unicode subclass |string (s) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other tuple subclass |struct ((...)) |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other list subclass |array (a...), guess |\n"
+"| |contents' type according to|\n"
+"| |type of first item |\n"
+"+-------------------------------+---------------------------+\n"
+"|any other dict subclass |dict (a{...}), guess key, |\n"
+"| |value type according to |\n"
+"| |types for an arbitrary item|\n"
+"+-------------------------------+---------------------------+\n"
+"|anything else |raise TypeError |\n"
+"+-------------------------------+---------------------------+\n"
+);
+
+/* return a new reference, possibly to None */
+static PyObject *
+get_object_path(PyObject *obj)
+{
+ PyObject *magic_attr = PyObject_GetAttr(obj, dbus_py__dbus_object_path__const);
+
+ if (magic_attr) {
+ if (PyUnicode_Check(magic_attr) || PyBytes_Check(magic_attr)) {
+ return magic_attr;
+ }
+ else {
+ Py_CLEAR(magic_attr);
+ PyErr_SetString(PyExc_TypeError, "__dbus_object_path__ must be "
+ "a string");
+ return NULL;
+ }
+ }
+ else {
+ /* Ignore exceptions, except for SystemExit and KeyboardInterrupt */
+ if (PyErr_ExceptionMatches(PyExc_SystemExit) ||
+ PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
+ return NULL;
+ PyErr_Clear();
+ Py_RETURN_NONE;
+ }
+}
+
+/* Return a new reference. If the object is a variant and variant_level_ptr
+ * is not NULL, put the variant level in the variable pointed to, and
+ * return the contained type instead of "v". */
+static PyObject *
+_signature_string_from_pyobject(PyObject *obj, long *variant_level_ptr)
+{
+ PyObject *magic_attr;
+ long variant_level = get_variant_level(obj);
+
+ if (variant_level < 0)
+ return NULL;
+
+ if (variant_level_ptr) {
+ *variant_level_ptr = variant_level;
+ }
+ else if (variant_level > 0) {
+ return NATIVESTR_FROMSTR(DBUS_TYPE_VARIANT_AS_STRING);
+ }
+
+ if (obj == Py_True || obj == Py_False) {
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BOOLEAN_AS_STRING);
+ }
+
+ magic_attr = get_object_path(obj);
+ if (!magic_attr)
+ return NULL;
+ if (magic_attr != Py_None) {
+ Py_CLEAR(magic_attr);
+ return NATIVESTR_FROMSTR(DBUS_TYPE_OBJECT_PATH_AS_STRING);
+ }
+ Py_CLEAR(magic_attr);
+
+ /* Ordering is important: some of these are subclasses of each other. */
+#ifdef PY3
+ if (PyLong_Check(obj)) {
+ if (DBusPyUInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT64_AS_STRING);
+ else if (DBusPyInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT64_AS_STRING);
+ else if (DBusPyUInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT32_AS_STRING);
+ else if (DBusPyInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ else if (DBusPyUInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT16_AS_STRING);
+ else if (DBusPyInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT16_AS_STRING);
+ else if (DBusPyByte_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BYTE_AS_STRING);
+ else if (DBusPyBoolean_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BOOLEAN_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ }
+#else /* !PY3 */
+ if (PyInt_Check(obj)) {
+ if (DBusPyInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT16_AS_STRING);
+ else if (DBusPyInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ else if (DBusPyByte_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BYTE_AS_STRING);
+ else if (DBusPyUInt16_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT16_AS_STRING);
+ else if (DBusPyBoolean_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_BOOLEAN_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT32_AS_STRING);
+ }
+ else if (PyLong_Check(obj)) {
+ if (DBusPyInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT64_AS_STRING);
+ else if (DBusPyUInt32_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT32_AS_STRING);
+ else if (DBusPyUInt64_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UINT64_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_INT64_AS_STRING);
+ }
+#endif /* PY3 */
+ else if (PyUnicode_Check(obj)) {
+ /* Object paths and signatures are unicode subtypes in Python 3
+ * (the first two cases will never be true in Python 2) */
+ if (DBusPyObjectPath_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_OBJECT_PATH_AS_STRING);
+ else if (DBusPySignature_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_SIGNATURE_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_STRING_AS_STRING);
+ }
+#if defined(DBUS_TYPE_UNIX_FD)
+ else if (DBusPyUnixFd_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_UNIX_FD_AS_STRING);
+#endif
+ else if (PyFloat_Check(obj)) {
+#ifdef WITH_DBUS_FLOAT32
+ if (DBusPyDouble_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_DOUBLE_AS_STRING);
+ else if (DBusPyFloat_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_FLOAT_AS_STRING);
+ else
+#endif
+ return NATIVESTR_FROMSTR(DBUS_TYPE_DOUBLE_AS_STRING);
+ }
+ else if (PyBytes_Check(obj)) {
+ /* Object paths and signatures are bytes subtypes in Python 2
+ * (the first two cases will never be true in Python 3) */
+ if (DBusPyObjectPath_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_OBJECT_PATH_AS_STRING);
+ else if (DBusPySignature_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_SIGNATURE_AS_STRING);
+ else if (DBusPyByteArray_Check(obj))
+ return NATIVESTR_FROMSTR(DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_TYPE_BYTE_AS_STRING);
+ else
+ return NATIVESTR_FROMSTR(DBUS_TYPE_STRING_AS_STRING);
+ }
+ else if (PyTuple_Check(obj)) {
+ Py_ssize_t len = PyTuple_GET_SIZE(obj);
+ PyObject *list = PyList_New(len + 2); /* new ref */
+ PyObject *item; /* temporary new ref */
+ PyObject *empty_str; /* temporary new ref */
+ PyObject *ret;
+ Py_ssize_t i;
+
+ if (!list) return NULL;
+ if (len == 0) {
+ PyErr_SetString(PyExc_ValueError, "D-Bus structs cannot be empty");
+ Py_CLEAR(list);
+ return NULL;
+ }
+ /* Set the first and last elements of list to be the parentheses */
+ item = NATIVESTR_FROMSTR(DBUS_STRUCT_BEGIN_CHAR_AS_STRING);
+ if (PyList_SetItem(list, 0, item) < 0) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = NATIVESTR_FROMSTR(DBUS_STRUCT_END_CHAR_AS_STRING);
+ if (PyList_SetItem(list, len + 1, item) < 0) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ if (!item || !PyList_GET_ITEM(list, 0)) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = NULL;
+
+ for (i = 0; i < len; i++) {
+ item = PyTuple_GetItem(obj, i);
+ if (!item) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = _signature_string_from_pyobject(item, NULL);
+ if (!item) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ if (PyList_SetItem(list, i + 1, item) < 0) {
+ Py_CLEAR(list);
+ return NULL;
+ }
+ item = NULL;
+ }
+ empty_str = NATIVESTR_FROMSTR("");
+ if (!empty_str) {
+ /* really shouldn't happen */
+ Py_CLEAR(list);
+ return NULL;
+ }
+ ret = PyObject_CallMethod(empty_str, "join", "(O)", list); /* new ref */
+ /* whether ret is NULL or not, */
+ Py_CLEAR(empty_str);
+ Py_CLEAR(list);
+ return ret;
+ }
+ else if (PyList_Check(obj)) {
+ PyObject *tmp;
+ PyObject *ret = NATIVESTR_FROMSTR(DBUS_TYPE_ARRAY_AS_STRING);
+ if (!ret) return NULL;
+#ifdef PY3
+ if (DBusPyArray_Check(obj) &&
+ PyUnicode_Check(((DBusPyArray *)obj)->signature))
+ {
+ PyObject *concat = PyUnicode_Concat(
+ ret, ((DBusPyArray *)obj)->signature);
+ Py_CLEAR(ret);
+ return concat;
+ }
+#else
+ if (DBusPyArray_Check(obj) &&
+ PyBytes_Check(((DBusPyArray *)obj)->signature))
+ {
+ PyBytes_Concat(&ret, ((DBusPyArray *)obj)->signature);
+ return ret;
+ }
+#endif
+ if (PyList_GET_SIZE(obj) == 0) {
+ /* No items, so fail. Or should we guess "av"? */
+ PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
+ "from an empty list");
+ return NULL;
+ }
+ tmp = PyList_GetItem(obj, 0);
+ tmp = _signature_string_from_pyobject(tmp, NULL);
+ if (!tmp) return NULL;
+#ifdef PY3
+ {
+ PyObject *concat = PyUnicode_Concat(ret, tmp);
+ Py_CLEAR(ret);
+ Py_CLEAR(tmp);
+ return concat;
+ }
+#else
+ PyBytes_ConcatAndDel(&ret, tmp);
+ return ret;
+#endif
+ }
+ else if (PyDict_Check(obj)) {
+ PyObject *key, *value, *keysig, *valuesig;
+ Py_ssize_t pos = 0;
+ PyObject *ret = NULL;
+
+#ifdef PY3
+ if (DBusPyDict_Check(obj) &&
+ PyUnicode_Check(((DBusPyDict *)obj)->signature))
+ {
+ return PyUnicode_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%U"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ ((DBusPyDict *)obj)->signature);
+ }
+#else
+ if (DBusPyDict_Check(obj) &&
+ PyBytes_Check(((DBusPyDict *)obj)->signature))
+ {
+ const char *sig = PyBytes_AS_STRING(((DBusPyDict *)obj)->signature);
+
+ return PyBytes_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%s"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ sig);
+ }
+#endif
+ if (!PyDict_Next(obj, &pos, &key, &value)) {
+ /* No items, so fail. Or should we guess "a{vv}"? */
+ PyErr_SetString(PyExc_ValueError, "Unable to guess signature "
+ "from an empty dict");
+ return NULL;
+ }
+ keysig = _signature_string_from_pyobject(key, NULL);
+ valuesig = _signature_string_from_pyobject(value, NULL);
+ if (keysig && valuesig) {
+#ifdef PY3
+ ret = PyUnicode_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%U%U"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ keysig, valuesig);
+#else
+ ret = PyBytes_FromFormat((DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ "%s%s"
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING),
+ PyBytes_AS_STRING(keysig),
+ PyBytes_AS_STRING(valuesig));
+#endif
+ }
+ Py_CLEAR(keysig);
+ Py_CLEAR(valuesig);
+ return ret;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError, "Don't know which D-Bus type "
+ "to use to encode type \"%s\"",
+ Py_TYPE(obj)->tp_name);
+ return NULL;
+ }
+}
+
+PyObject *
+dbus_py_Message_guess_signature(PyObject *unused UNUSED, PyObject *args)
+{
+ PyObject *tmp, *ret = NULL;
+
+ if (!args) {
+ if (!PyErr_Occurred()) {
+ PyErr_BadInternalCall();
+ }
+ return NULL;
+ }
+
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: called Message_guess_signature", (long)getpid());
+ PyObject_Print(args, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+
+ if (!PyTuple_Check(args)) {
+ DBG("%s", "Message_guess_signature: args not a tuple");
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ /* if there were no args, easy */
+ if (PyTuple_GET_SIZE(args) == 0) {
+ DBG("%s", "Message_guess_signature: no args, so return Signature('')");
+ return PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s)", "");
+ }
+
+ /* if there were args, the signature we want is, by construction,
+ * exactly the signature we get for the tuple args, except that we don't
+ * want the parentheses. */
+ tmp = _signature_string_from_pyobject(args, NULL);
+ if (!tmp) {
+ DBG("%s", "Message_guess_signature: failed");
+ return NULL;
+ }
+ if (PyUnicode_Check(tmp)) {
+ PyObject *as_bytes = PyUnicode_AsUTF8String(tmp);
+ Py_CLEAR(tmp);
+ if (!as_bytes)
+ return NULL;
+ if (PyBytes_GET_SIZE(as_bytes) < 2) {
+ PyErr_SetString(PyExc_RuntimeError, "Internal error: "
+ "_signature_string_from_pyobject returned "
+ "a bad result");
+ Py_CLEAR(as_bytes);
+ return NULL;
+ }
+ tmp = as_bytes;
+ }
+ if (!PyBytes_Check(tmp) || PyBytes_GET_SIZE(tmp) < 2) {
+ PyErr_SetString(PyExc_RuntimeError, "Internal error: "
+ "_signature_string_from_pyobject returned "
+ "a bad result");
+ Py_CLEAR(tmp);
+ return NULL;
+ }
+ ret = PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s#)",
+ PyBytes_AS_STRING(tmp) + 1,
+ PyBytes_GET_SIZE(tmp) - 2);
+ Py_CLEAR(tmp);
+ return ret;
+}
+
+static int _message_iter_append_pyobject(DBusMessageIter *appender,
+ DBusSignatureIter *sig_iter,
+ PyObject *obj,
+ dbus_bool_t *more);
+static int _message_iter_append_variant(DBusMessageIter *appender,
+ PyObject *obj);
+
+static int
+_message_iter_append_string(DBusMessageIter *appender,
+ int sig_type, PyObject *obj,
+ dbus_bool_t allow_object_path_attr)
+{
+ char *s;
+ PyObject *utf8;
+
+ if (sig_type == DBUS_TYPE_OBJECT_PATH && allow_object_path_attr) {
+ PyObject *object_path = get_object_path (obj);
+
+ if (object_path == Py_None) {
+ Py_CLEAR(object_path);
+ }
+ else if (!object_path) {
+ return -1;
+ }
+ else {
+ int ret = _message_iter_append_string(appender, sig_type,
+ object_path, FALSE);
+ Py_CLEAR(object_path);
+ return ret;
+ }
+ }
+
+ if (PyBytes_Check(obj)) {
+ utf8 = obj;
+ Py_INCREF(obj);
+ }
+ else if (PyUnicode_Check(obj)) {
+ utf8 = PyUnicode_AsUTF8String(obj);
+ if (!utf8) return -1;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "Expected a string or unicode object");
+ return -1;
+ }
+
+ /* Raise TypeError if the string has embedded NULs */
+ if (PyBytes_AsStringAndSize(utf8, &s, NULL) < 0)
+ return -1;
+
+ /* Validate UTF-8, strictly */
+ if (!dbus_validate_utf8(s, NULL)) {
+ PyErr_SetString(PyExc_UnicodeError, "String parameters "
+ "to be sent over D-Bus must be valid UTF-8 "
+ "with no noncharacter code points");
+ return -1;
+ }
+
+ DBG("Performing actual append: string (from unicode) %s", s);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &s)) {
+ Py_CLEAR(utf8);
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ Py_CLEAR(utf8);
+ return 0;
+}
+
+static int
+_message_iter_append_byte(DBusMessageIter *appender, PyObject *obj)
+{
+ unsigned char y;
+
+ if (PyBytes_Check(obj)) {
+ if (PyBytes_GET_SIZE(obj) != 1) {
+ PyErr_Format(PyExc_ValueError,
+ "Expected a length-1 bytes but found %d bytes",
+ (int)PyBytes_GET_SIZE(obj));
+ return -1;
+ }
+ y = *(unsigned char *)PyBytes_AS_STRING(obj);
+ }
+ else {
+ /* on Python 2 this accepts either int or long */
+ long i = PyLong_AsLong(obj);
+
+ if (i == -1 && PyErr_Occurred()) return -1;
+ if (i < 0 || i > 0xff) {
+ PyErr_Format(PyExc_ValueError,
+ "%d outside range for a byte value",
+ (int)i);
+ return -1;
+ }
+ y = i;
+ }
+ DBG("Performing actual append: byte \\x%02x", (unsigned)y);
+ if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_BYTE, &y)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+
+static dbus_bool_t
+dbuspy_message_iter_close_container(DBusMessageIter *iter,
+ DBusMessageIter *sub,
+ dbus_bool_t is_ok)
+{
+ if (!is_ok) {
+ dbus_message_iter_abandon_container(iter, sub);
+ return TRUE;
+ }
+ return dbus_message_iter_close_container(iter, sub);
+}
+
+#if defined(DBUS_TYPE_UNIX_FD)
+static int
+_message_iter_append_unixfd(DBusMessageIter *appender, PyObject *obj)
+{
+ int fd;
+ long original_fd;
+
+ if (INTORLONG_CHECK(obj))
+ {
+ /* on Python 2 this accepts either int or long */
+ original_fd = PyLong_AsLong(obj);
+ if (original_fd == -1 && PyErr_Occurred())
+ return -1;
+ if (original_fd < INT_MIN || original_fd > INT_MAX) {
+ PyErr_Format(PyExc_ValueError, "out of int range: %ld",
+ original_fd);
+ return -1;
+ }
+ fd = (int)original_fd;
+ }
+ else if (PyObject_IsInstance(obj, (PyObject*) &DBusPyUnixFd_Type)) {
+ fd = dbus_py_unix_fd_get_fd(obj);
+ }
+ else {
+ return -1;
+ }
+
+ DBG("Performing actual append: fd %d", fd);
+ if (!dbus_message_iter_append_basic(appender, DBUS_TYPE_UNIX_FD, &fd)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+static int
+_message_iter_append_dictentry(DBusMessageIter *appender,
+ DBusSignatureIter *sig_iter,
+ PyObject *dict, PyObject *key)
+{
+ DBusSignatureIter sub_sig_iter;
+ DBusMessageIter sub;
+ int ret = -1;
+ PyObject *value = PyObject_GetItem(dict, key);
+ dbus_bool_t more;
+
+ if (!value) return -1;
+
+#ifdef USING_DBG
+ fprintf(stderr, "Append dictentry: ");
+ PyObject_Print(key, stderr, 0);
+ fprintf(stderr, " => ");
+ PyObject_Print(value, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+
+ DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
+ dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
+#ifdef USING_DBG
+ {
+ char *s;
+ s = dbus_signature_iter_get_signature(sig_iter);
+ DBG("Signature of parent iterator %p is %s", sig_iter, s);
+ dbus_free(s);
+ s = dbus_signature_iter_get_signature(&sub_sig_iter);
+ DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
+ dbus_free(s);
+ }
+#endif
+
+ DBG("%s", "Opening DICT_ENTRY container");
+ if (!dbus_message_iter_open_container(appender, DBUS_TYPE_DICT_ENTRY,
+ NULL, &sub)) {
+ PyErr_NoMemory();
+ goto out;
+ }
+ ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, key, &more);
+ if (ret == 0) {
+ ret = _message_iter_append_pyobject(&sub, &sub_sig_iter, value, &more);
+ }
+ DBG("%s", "Closing DICT_ENTRY container");
+ if (!dbuspy_message_iter_close_container(appender, &sub, (ret == 0))) {
+ PyErr_NoMemory();
+ ret = -1;
+ }
+out:
+ Py_CLEAR(value);
+ return ret;
+}
+
+static int
+_message_iter_append_multi(DBusMessageIter *appender,
+ const DBusSignatureIter *sig_iter,
+ int mode, PyObject *obj)
+{
+ DBusMessageIter sub_appender;
+ DBusSignatureIter sub_sig_iter;
+ PyObject *contents;
+ int ret;
+ PyObject *iterator = PyObject_GetIter(obj);
+ char *sig = NULL;
+ int container = mode;
+ dbus_bool_t is_byte_array = DBusPyByteArray_Check(obj);
+ int inner_type;
+ dbus_bool_t more;
+
+ assert(mode == DBUS_TYPE_DICT_ENTRY || mode == DBUS_TYPE_ARRAY ||
+ mode == DBUS_TYPE_STRUCT);
+
+#ifdef USING_DBG
+ fprintf(stderr, "Appending multiple: ");
+ PyObject_Print(obj, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+
+ if (!iterator) return -1;
+ if (mode == DBUS_TYPE_DICT_ENTRY) container = DBUS_TYPE_ARRAY;
+
+ DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
+ dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
+#ifdef USING_DBG
+ {
+ char *s;
+ s = dbus_signature_iter_get_signature(sig_iter);
+ DBG("Signature of parent iterator %p is %s", sig_iter, s);
+ dbus_free(s);
+ s = dbus_signature_iter_get_signature(&sub_sig_iter);
+ DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
+ dbus_free(s);
+ }
+#endif
+ inner_type = dbus_signature_iter_get_current_type(&sub_sig_iter);
+
+ if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
+ sig = dbus_signature_iter_get_signature(&sub_sig_iter);
+ if (!sig) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ }
+ /* else leave sig set to NULL. */
+
+ DBG("Opening '%c' container", container);
+ if (!dbus_message_iter_open_container(appender, container,
+ sig, &sub_appender)) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ ret = 0;
+ more = TRUE;
+ while ((contents = PyIter_Next(iterator))) {
+
+ if (mode == DBUS_TYPE_ARRAY || mode == DBUS_TYPE_DICT_ENTRY) {
+ DBG("Recursing signature iterator %p -> %p", sig_iter, &sub_sig_iter);
+ dbus_signature_iter_recurse(sig_iter, &sub_sig_iter);
+#ifdef USING_DBG
+ {
+ char *s;
+ s = dbus_signature_iter_get_signature(sig_iter);
+ DBG("Signature of parent iterator %p is %s", sig_iter, s);
+ dbus_free(s);
+ s = dbus_signature_iter_get_signature(&sub_sig_iter);
+ DBG("Signature of sub-iterator %p is %s", &sub_sig_iter, s);
+ dbus_free(s);
+ }
+#endif
+ }
+ else /* struct */ {
+ if (!more) {
+ PyErr_Format(PyExc_TypeError, "Fewer items found in struct's "
+ "D-Bus signature than in Python arguments ");
+ ret = -1;
+ break;
+ }
+ }
+
+ if (mode == DBUS_TYPE_DICT_ENTRY) {
+ ret = _message_iter_append_dictentry(&sub_appender, &sub_sig_iter,
+ obj, contents);
+ }
+ else if (mode == DBUS_TYPE_ARRAY && is_byte_array
+ && inner_type == DBUS_TYPE_VARIANT) {
+ /* Subscripting a ByteArray gives a str of length 1, but if the
+ * container is a ByteArray and the parameter is an array of
+ * variants, we want to produce an array of variants containing
+ * bytes, not strings.
+ */
+ PyObject *args = Py_BuildValue("(O)", contents);
+ PyObject *byte;
+
+ if (!args)
+ break;
+ byte = PyObject_Call((PyObject *)&DBusPyByte_Type, args, NULL);
+ Py_CLEAR(args);
+ if (!byte)
+ break;
+ ret = _message_iter_append_variant(&sub_appender, byte);
+ Py_CLEAR(byte);
+ }
+ else {
+ /* advances sub_sig_iter and sets more on success - for array
+ * this doesn't matter, for struct it's essential */
+ ret = _message_iter_append_pyobject(&sub_appender, &sub_sig_iter,
+ contents, &more);
+ }
+
+ Py_CLEAR(contents);
+ if (ret < 0) {
+ break;
+ }
+ }
+
+ if (PyErr_Occurred()) {
+ ret = -1;
+ }
+ else if (mode == DBUS_TYPE_STRUCT && more) {
+ PyErr_Format(PyExc_TypeError, "More items found in struct's D-Bus "
+ "signature than in Python arguments ");
+ ret = -1;
+ }
+
+ /* This must be run as cleanup, even on failure. */
+ DBG("Closing '%c' container", container);
+ if (!dbuspy_message_iter_close_container(appender, &sub_appender, (ret == 0))) {
+ PyErr_NoMemory();
+ ret = -1;
+ }
+
+out:
+ Py_CLEAR(iterator);
+ dbus_free(sig);
+ return ret;
+}
+
+static int
+_message_iter_append_string_as_byte_array(DBusMessageIter *appender,
+ PyObject *obj)
+{
+ /* a bit of a faster path for byte arrays that are strings */
+ Py_ssize_t len = PyBytes_GET_SIZE(obj);
+ const char *s;
+ DBusMessageIter sub;
+ int ret;
+
+ s = PyBytes_AS_STRING(obj);
+ DBG("%s", "Opening ARRAY container");
+ if (!dbus_message_iter_open_container(appender, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &sub)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ DBG("Appending fixed array of %d bytes", (int)len);
+ if (dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &s, len)) {
+ ret = 0;
+ }
+ else {
+ PyErr_NoMemory();
+ ret = -1;
+ }
+ DBG("%s", "Closing ARRAY container");
+ if (!dbus_message_iter_close_container(appender, &sub)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return ret;
+}
+
+/* Encode some Python object into a D-Bus variant slot. */
+static int
+_message_iter_append_variant(DBusMessageIter *appender, PyObject *obj)
+{
+ DBusSignatureIter obj_sig_iter;
+ const char *obj_sig_str;
+ PyObject *obj_sig;
+ int ret;
+ long variant_level;
+ dbus_bool_t dummy;
+ DBusMessageIter *variant_iters = NULL;
+
+ /* Separate the object into the contained object, and the number of
+ * variants it's wrapped in. */
+ obj_sig = _signature_string_from_pyobject(obj, &variant_level);
+ if (!obj_sig) return -1;
+
+ if (PyUnicode_Check(obj_sig)) {
+ PyObject *obj_sig_as_bytes = PyUnicode_AsUTF8String(obj_sig);
+ Py_CLEAR(obj_sig);
+ if (!obj_sig_as_bytes)
+ return -1;
+ obj_sig = obj_sig_as_bytes;
+ }
+ obj_sig_str = PyBytes_AsString(obj_sig);
+ if (!obj_sig_str) {
+ Py_CLEAR(obj_sig);
+ return -1;
+ }
+
+ if (variant_level < 1) {
+ variant_level = 1;
+ }
+
+ dbus_signature_iter_init(&obj_sig_iter, obj_sig_str);
+
+ {
+ long i;
+
+ variant_iters = calloc (variant_level, sizeof (DBusMessageIter));
+
+ if (!variant_iters) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+
+ for (i = 0; i < variant_level; i++) {
+ DBusMessageIter *child = &variant_iters[i];
+ /* The first is a special case: its parent is the iter passed in
+ * to this function, instead of being the previous one in the
+ * stack
+ */
+ DBusMessageIter *parent = (i == 0
+ ? appender
+ : &(variant_iters[i-1]));
+ /* The last is also a special case: it contains the actual
+ * object, rather than another variant
+ */
+ const char *sig_str = (i == variant_level-1
+ ? obj_sig_str
+ : DBUS_TYPE_VARIANT_AS_STRING);
+
+ DBG("Opening VARIANT container %p inside %p containing '%s'",
+ child, parent, sig_str);
+ if (!dbus_message_iter_open_container(parent, DBUS_TYPE_VARIANT,
+ sig_str, child)) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* Put the object itself into the innermost variant */
+ ret = _message_iter_append_pyobject(&variant_iters[variant_level-1],
+ &obj_sig_iter, obj, &dummy);
+
+ /* here we rely on i (and variant_level) being a signed long */
+ for (i = variant_level - 1; i >= 0; i--) {
+ DBusMessageIter *child = &variant_iters[i];
+ /* The first is a special case: its parent is the iter passed in
+ * to this function, instead of being the previous one in the
+ * stack
+ */
+ DBusMessageIter *parent = (i == 0 ? appender
+ : &(variant_iters[i-1]));
+
+ DBG("Closing VARIANT container %p inside %p", child, parent);
+ if (!dbus_message_iter_close_container(parent, child)) {
+ PyErr_NoMemory();
+ ret = -1;
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (variant_iters != NULL)
+ free (variant_iters);
+
+ Py_CLEAR(obj_sig);
+ return ret;
+}
+
+/* On success, *more is set to whether there's more in the signature. */
+static int
+_message_iter_append_pyobject(DBusMessageIter *appender,
+ DBusSignatureIter *sig_iter,
+ PyObject *obj,
+ dbus_bool_t *more)
+{
+ int sig_type = dbus_signature_iter_get_current_type(sig_iter);
+ DBusBasicValue u;
+ int ret = -1;
+
+#ifdef USING_DBG
+ fprintf(stderr, "Appending object at %p: ", obj);
+ PyObject_Print(obj, stderr, 0);
+ fprintf(stderr, " into appender at %p, dbus wants type %c\n",
+ appender, sig_type);
+#endif
+
+ switch (sig_type) {
+ /* The numeric types are relatively simple to deal with, so are
+ * inlined here. */
+
+ case DBUS_TYPE_BOOLEAN:
+ if (PyObject_IsTrue(obj)) {
+ u.bool_val = 1;
+ }
+ else {
+ u.bool_val = 0;
+ }
+ DBG("Performing actual append: bool(%ld)", (long)u.bool_val);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.bool_val)) {
+ PyErr_NoMemory();
+ ret = -1;
+ break;
+ }
+ ret = 0;
+ break;
+
+ case DBUS_TYPE_DOUBLE:
+ u.dbl = PyFloat_AsDouble(obj);
+ if (PyErr_Occurred()) {
+ ret = -1;
+ break;
+ }
+ DBG("Performing actual append: double(%f)", u.dbl);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.dbl)) {
+ PyErr_NoMemory();
+ ret = -1;
+ break;
+ }
+ ret = 0;
+ break;
+
+#ifdef WITH_DBUS_FLOAT32
+ case DBUS_TYPE_FLOAT:
+ u.dbl = PyFloat_AsDouble(obj);
+ if (PyErr_Occurred()) {
+ ret = -1;
+ break;
+ }
+ /* FIXME: DBusBasicValue will need to grow a float member if
+ * float32 becomes supported */
+ u.f = (float)u.dbl;
+ DBG("Performing actual append: float(%f)", u.f);
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.f)) {
+ PyErr_NoMemory();
+ ret = -1;
+ break;
+ }
+ ret = 0;
+ break;
+#endif
+
+ /* The integer types are all basically the same - we delegate to
+ intNN_range_check() */
+#define PROCESS_INTEGER(size, member) \
+ u.member = dbus_py_##size##_range_check(obj);\
+ if (u.member == (dbus_##size##_t)(-1) && PyErr_Occurred()) {\
+ ret = -1; \
+ break; \
+ }\
+ DBG("Performing actual append: " #size "(%lld)", (long long)u.member); \
+ if (!dbus_message_iter_append_basic(appender, sig_type, &u.member)) {\
+ PyErr_NoMemory();\
+ ret = -1;\
+ break;\
+ } \
+ ret = 0;
+
+ case DBUS_TYPE_INT16:
+ PROCESS_INTEGER(int16, i16)
+ break;
+ case DBUS_TYPE_UINT16:
+ PROCESS_INTEGER(uint16, u16)
+ break;
+ case DBUS_TYPE_INT32:
+ PROCESS_INTEGER(int32, i32)
+ break;
+ case DBUS_TYPE_UINT32:
+ PROCESS_INTEGER(uint32, u32)
+ break;
+#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
+ case DBUS_TYPE_INT64:
+ PROCESS_INTEGER(int64, i64)
+ break;
+ case DBUS_TYPE_UINT64:
+ PROCESS_INTEGER(uint64, u64)
+ break;
+#else
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ PyErr_SetString(PyExc_NotImplementedError, "64-bit integer "
+ "types are not supported on this platform");
+ ret = -1;
+ break;
+#endif
+#undef PROCESS_INTEGER
+
+ /* Now the more complicated cases, which are delegated to helper
+ * functions (although in practice, the compiler will hopefully
+ * inline them anyway). */
+
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_SIGNATURE:
+ case DBUS_TYPE_OBJECT_PATH:
+ ret = _message_iter_append_string(appender, sig_type, obj, TRUE);
+ break;
+
+ case DBUS_TYPE_BYTE:
+ ret = _message_iter_append_byte(appender, obj);
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ /* 3 cases - it might actually be a dict, or it might be a byte array
+ * being copied from a string (for which we have a faster path),
+ * or it might be a generic array. */
+
+ sig_type = dbus_signature_iter_get_element_type(sig_iter);
+ if (sig_type == DBUS_TYPE_DICT_ENTRY)
+ ret = _message_iter_append_multi(appender, sig_iter,
+ DBUS_TYPE_DICT_ENTRY, obj);
+ else if (sig_type == DBUS_TYPE_BYTE && PyBytes_Check(obj))
+ ret = _message_iter_append_string_as_byte_array(appender, obj);
+ else
+ ret = _message_iter_append_multi(appender, sig_iter,
+ DBUS_TYPE_ARRAY, obj);
+ DBG("_message_iter_append_multi(): %d", ret);
+ break;
+
+ case DBUS_TYPE_STRUCT:
+ ret = _message_iter_append_multi(appender, sig_iter, sig_type, obj);
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ ret = _message_iter_append_variant(appender, obj);
+ break;
+
+ case DBUS_TYPE_INVALID:
+ PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
+ "signature than in Python arguments");
+ ret = -1;
+ break;
+
+#if defined(DBUS_TYPE_UNIX_FD)
+ case DBUS_TYPE_UNIX_FD:
+ ret = _message_iter_append_unixfd(appender, obj);
+ break;
+#endif
+
+ default:
+ PyErr_Format(PyExc_TypeError, "Unknown type '\\x%x' in D-Bus "
+ "signature", sig_type);
+ ret = -1;
+ break;
+ }
+ if (ret < 0) return -1;
+
+ DBG("Advancing signature iter at %p", sig_iter);
+ *more = dbus_signature_iter_next(sig_iter);
+#ifdef USING_DBG
+ DBG("- result: %ld, type %02x '%c'", (long)(*more),
+ (int)dbus_signature_iter_get_current_type(sig_iter),
+ (int)dbus_signature_iter_get_current_type(sig_iter));
+#endif
+ return 0;
+}
+
+
+PyObject *
+dbus_py_Message_append(Message *self, PyObject *args, PyObject *kwargs)
+{
+ const char *signature = NULL;
+ PyObject *signature_obj = NULL;
+ DBusSignatureIter sig_iter;
+ DBusMessageIter appender;
+ static char *argnames[] = {"signature", NULL};
+ dbus_bool_t more;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: called Message_append(*", (long)getpid());
+ PyObject_Print(args, stderr, 0);
+ if (kwargs) {
+ fprintf(stderr, ", **");
+ PyObject_Print(kwargs, stderr, 0);
+ }
+ fprintf(stderr, ")\n");
+#endif
+
+ /* only use kwargs for this step: deliberately ignore args for now */
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs, "|z:append",
+ argnames, &signature)) return NULL;
+
+ if (!signature) {
+ DBG("%s", "No signature for message, guessing...");
+ signature_obj = dbus_py_Message_guess_signature(NULL, args);
+ if (!signature_obj) return NULL;
+ if (PyUnicode_Check(signature_obj)) {
+ PyObject *signature_as_bytes;
+ signature_as_bytes = PyUnicode_AsUTF8String(signature_obj);
+ Py_CLEAR(signature_obj);
+ if (!signature_as_bytes)
+ return NULL;
+ signature_obj = signature_as_bytes;
+ }
+ else {
+ assert(PyBytes_Check(signature_obj));
+ }
+ signature = PyBytes_AS_STRING(signature_obj);
+ }
+ /* from here onwards, you have to do a goto rather than returning NULL
+ to make sure signature_obj gets freed */
+
+ /* iterate over args and the signature, together */
+ if (!dbus_signature_validate(signature, NULL)) {
+ PyErr_SetString(PyExc_ValueError, "Corrupt type signature");
+ goto err;
+ }
+ dbus_message_iter_init_append(self->msg, &appender);
+
+ if (signature[0] != '\0') {
+ int i = 0;
+
+ more = TRUE;
+ dbus_signature_iter_init(&sig_iter, signature);
+ while (more) {
+ if (i >= PyTuple_GET_SIZE(args)) {
+ PyErr_SetString(PyExc_TypeError, "More items found in D-Bus "
+ "signature than in Python arguments");
+ goto hosed;
+ }
+ if (_message_iter_append_pyobject(&appender, &sig_iter,
+ PyTuple_GET_ITEM(args, i),
+ &more) < 0) {
+ goto hosed;
+ }
+ i++;
+ }
+ if (i < PyTuple_GET_SIZE(args)) {
+ PyErr_SetString(PyExc_TypeError, "Fewer items found in D-Bus "
+ "signature than in Python arguments");
+ goto hosed;
+ }
+ }
+
+ /* success! */
+ Py_CLEAR(signature_obj);
+ Py_RETURN_NONE;
+
+hosed:
+ /* "If appending any of the arguments fails due to lack of memory,
+ * generally the message is hosed and you have to start over" -libdbus docs
+ * Enforce this by throwing away the message structure.
+ */
+ dbus_message_unref(self->msg);
+ self->msg = NULL;
+err:
+ Py_CLEAR(signature_obj);
+ return NULL;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/message-get-args.c b/dbus_bindings/message-get-args.c
new file mode 100644
index 0000000..65596d9
--- /dev/null
+++ b/dbus_bindings/message-get-args.c
@@ -0,0 +1,558 @@
+/* D-Bus Message unserialization. This contains all the logic to map from
+ * D-Bus types to Python objects.
+ *
+ * Copyright (C) 2006-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#define DBG_IS_TOO_VERBOSE
+#include "compat-internal.h"
+#include "types-internal.h"
+#include "message-internal.h"
+
+char dbus_py_Message_get_args_list__doc__[] = (
+"get_args_list(**kwargs) -> list\n\n"
+"Return the message's arguments. Keyword arguments control the translation\n"
+"of D-Bus types to Python:\n"
+"\n"
+":Keywords:\n"
+" `byte_arrays` : bool\n"
+" If true, convert arrays of byte (signature 'ay') into dbus.ByteArray,\n"
+" a str subclass. In practice, this is usually what you want, but\n"
+" it's off by default for consistency.\n"
+"\n"
+" If false (default), convert them into a dbus.Array of Bytes.\n"
+#ifndef PY3
+" `utf8_strings` : bool\n"
+" If true, return D-Bus strings as Python `bytes` objects (in UTF-8).\n"
+" If false (default), return D-Bus strings as Python `unicode` objects.\n"
+#endif
+"\n"
+"Most of the type mappings should be fairly obvious:\n"
+"\n"
+"=============== ===================================================\n"
+"D-Bus Python\n"
+"=============== ===================================================\n"
+"byte (y) dbus.Byte (int subclass)\n"
+"bool (b) dbus.Boolean (int subclass)\n"
+"Signature (g) dbus.Signature (str subclass)\n"
+"intNN, uintNN dbus.IntNN, dbus.UIntNN (int or long subclasses)\n"
+"double (d) dbus.Double\n"
+"string (s) dbus.String (unicode subclass)\n"
+" (or dbus.UTF8String, bytes subclass, if utf8_strings set)\n"
+"Object path (o) dbus.ObjectPath (str subclass)\n"
+"dict (a{...}) dbus.Dictionary\n"
+"array (a...) dbus.Array (list subclass) containing appropriate types\n"
+"byte array (ay) dbus.ByteArray (str subclass) if byte_arrays set; or\n"
+" list of Byte\n"
+"struct ((...)) dbus.Struct (tuple subclass) of appropriate types\n"
+"variant (v) contained type, but with variant_level > 0\n"
+"=============== ===================================================\n"
+);
+
+typedef struct {
+ int byte_arrays;
+#ifndef PY3
+ int utf8_strings;
+#endif
+} Message_get_args_options;
+
+static PyObject *_message_iter_get_pyobject(DBusMessageIter *iter,
+ Message_get_args_options *opts,
+ long extra_variants);
+
+/* Append all the items iterated over to the given Python list object.
+ * Return 0 on success/-1 with exception on failure. */
+static int
+_message_iter_append_all_to_list(DBusMessageIter *iter, PyObject *list,
+ Message_get_args_options *opts)
+{
+ int ret, type;
+ while ((type = dbus_message_iter_get_arg_type(iter))
+ != DBUS_TYPE_INVALID) {
+ PyObject *item;
+ DBG("type == %d '%c'", type, type);
+
+ item = _message_iter_get_pyobject(iter, opts, 0);
+ if (!item) return -1;
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: appending to list: %p == ", (long)getpid(), item);
+ PyObject_Print(item, stderr, 0);
+ fprintf(stderr, " of type %p\n", Py_TYPE(item));
+#endif
+ ret = PyList_Append(list, item);
+ Py_CLEAR(item);
+ if (ret < 0) return -1;
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: list now contains: ", (long)getpid());
+ PyObject_Print(list, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+ dbus_message_iter_next(iter);
+ }
+ return 0;
+}
+
+static inline PyObject *
+_message_iter_get_dict(DBusMessageIter *iter,
+ Message_get_args_options *opts,
+ PyObject *kwargs)
+{
+ DBusMessageIter entries;
+ char *sig_str = dbus_message_iter_get_signature(iter);
+ PyObject *sig;
+ PyObject *ret;
+ int status;
+
+ if (!sig_str) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ sig = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
+ "(s#)", sig_str+2,
+ (Py_ssize_t)strlen(sig_str)-3);
+ dbus_free(sig_str);
+ if (!sig) {
+ return NULL;
+ }
+ status = PyDict_SetItem(kwargs, dbus_py_signature_const, sig);
+ Py_CLEAR(sig);
+ if (status < 0) {
+ return NULL;
+ }
+
+ ret = PyObject_Call((PyObject *)&DBusPyDict_Type, dbus_py_empty_tuple, kwargs);
+ if (!ret) {
+ return NULL;
+ }
+
+ dbus_message_iter_recurse(iter, &entries);
+ while (dbus_message_iter_get_arg_type(&entries) == DBUS_TYPE_DICT_ENTRY) {
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ DBusMessageIter kv;
+
+ DBG("%s", "dict entry...");
+
+ dbus_message_iter_recurse(&entries, &kv);
+
+ key = _message_iter_get_pyobject(&kv, opts, 0);
+ if (!key) {
+ Py_CLEAR(ret);
+ return NULL;
+ }
+ dbus_message_iter_next(&kv);
+
+ value = _message_iter_get_pyobject(&kv, opts, 0);
+ if (!value) {
+ Py_CLEAR(key);
+ Py_CLEAR(ret);
+ return NULL;
+ }
+
+ status = PyDict_SetItem(ret, key, value);
+ Py_CLEAR(key);
+ Py_CLEAR(value);
+
+ if (status < 0) {
+ Py_CLEAR(ret);
+ return NULL;
+ }
+ dbus_message_iter_next(&entries);
+ }
+
+ return ret;
+}
+
+/* Returns a new reference. */
+static PyObject *
+_message_iter_get_pyobject(DBusMessageIter *iter,
+ Message_get_args_options *opts,
+ long variant_level)
+{
+ DBusBasicValue u;
+ int type = dbus_message_iter_get_arg_type(iter);
+ PyObject *args = NULL;
+ PyObject *kwargs = NULL;
+ PyObject *ret = NULL;
+
+ /* If the variant-level is >0, prepare a dict for the kwargs.
+ * For variant wrappers optimize slightly by skipping this.
+ */
+ if (variant_level > 0 && type != DBUS_TYPE_VARIANT) {
+ PyObject *variant_level_int;
+
+ variant_level_int = NATIVEINT_FROMLONG(variant_level);
+ if (!variant_level_int) {
+ return NULL;
+ }
+ kwargs = PyDict_New();
+ if (!kwargs) {
+ Py_CLEAR(variant_level_int);
+ return NULL;
+ }
+ if (PyDict_SetItem(kwargs, dbus_py_variant_level_const,
+ variant_level_int) < 0) {
+ Py_CLEAR(variant_level_int);
+ Py_CLEAR(kwargs);
+ return NULL;
+ }
+ Py_CLEAR(variant_level_int);
+ }
+ /* From here down you need to break from the switch to exit, so the
+ * dict is freed if necessary
+ */
+
+ switch (type) {
+ PyObject *unicode;
+
+ case DBUS_TYPE_STRING:
+ DBG("%s", "found a string");
+ dbus_message_iter_get_basic(iter, &u.str);
+#ifndef PY3
+ if (opts->utf8_strings) {
+ args = Py_BuildValue("(s)", u.str);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyUTF8String_Type,
+ args, kwargs);
+ }
+ else {
+#endif
+ unicode = PyUnicode_DecodeUTF8(u.str, strlen(u.str), NULL);
+ if (!unicode) {
+ break;
+ }
+ args = Py_BuildValue("(N)", unicode);
+ if (!args) {
+ break;
+ }
+ ret = PyObject_Call((PyObject *)&DBusPyString_Type,
+ args, kwargs);
+#ifndef PY3
+ }
+#endif
+ break;
+
+ case DBUS_TYPE_SIGNATURE:
+ DBG("%s", "found a signature");
+ dbus_message_iter_get_basic(iter, &u.str);
+ args = Py_BuildValue("(s)", u.str);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPySignature_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_OBJECT_PATH:
+ DBG("%s", "found an object path");
+ dbus_message_iter_get_basic(iter, &u.str);
+ args = Py_BuildValue("(s)", u.str);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyObjectPath_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_DOUBLE:
+ DBG("%s", "found a double");
+ dbus_message_iter_get_basic(iter, &u.dbl);
+ args = Py_BuildValue("(f)", u.dbl);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyDouble_Type, args, kwargs);
+ break;
+
+#ifdef WITH_DBUS_FLOAT32
+ case DBUS_TYPE_FLOAT:
+ DBG("%s", "found a float");
+ /* FIXME: DBusBasicValue will need to grow a float member if
+ * float32 becomes supported */
+ dbus_message_iter_get_basic(iter, &u.f);
+ args = Py_BuildValue("(f)", (double)u.f);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyFloat_Type, args, kwargs);
+ break;
+#endif
+
+ case DBUS_TYPE_INT16:
+ DBG("%s", "found an int16");
+ dbus_message_iter_get_basic(iter, &u.i16);
+ args = Py_BuildValue("(i)", (int)u.i16);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyInt16_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_UINT16:
+ DBG("%s", "found a uint16");
+ dbus_message_iter_get_basic(iter, &u.u16);
+ args = Py_BuildValue("(i)", (int)u.u16);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyUInt16_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_INT32:
+ DBG("%s", "found an int32");
+ dbus_message_iter_get_basic(iter, &u.i32);
+ args = Py_BuildValue("(l)", (long)u.i32);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyInt32_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_UINT32:
+ DBG("%s", "found a uint32");
+ dbus_message_iter_get_basic(iter, &u.u32);
+ args = Py_BuildValue("(k)", (unsigned long)u.u32);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyUInt32_Type, args, kwargs);
+ break;
+
+#ifdef DBUS_TYPE_UNIX_FD
+ case DBUS_TYPE_UNIX_FD:
+ DBG("%s", "found an unix fd");
+ dbus_message_iter_get_basic(iter, &u.fd);
+ args = Py_BuildValue("(i)", u.fd);
+ if (args) {
+ ret = PyObject_Call((PyObject *)&DBusPyUnixFd_Type, args,
+ kwargs);
+ }
+ if (u.fd >= 0) {
+ close(u.fd);
+ }
+ break;
+#endif
+
+#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
+ case DBUS_TYPE_INT64:
+ DBG("%s", "found an int64");
+ dbus_message_iter_get_basic(iter, &u.i64);
+ args = Py_BuildValue("(L)", (PY_LONG_LONG)u.i64);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyInt64_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_UINT64:
+ DBG("%s", "found a uint64");
+ dbus_message_iter_get_basic(iter, &u.u64);
+ args = Py_BuildValue("(K)", (unsigned PY_LONG_LONG)u.u64);
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyUInt64_Type, args, kwargs);
+ break;
+#else
+ case DBUS_TYPE_INT64:
+ case DBUS_TYPE_UINT64:
+ PyErr_SetString(PyExc_NotImplementedError,
+ "64-bit integer types are not supported on "
+ "this platform");
+ break;
+#endif
+
+ case DBUS_TYPE_BYTE:
+ DBG("%s", "found a byte");
+ dbus_message_iter_get_basic(iter, &u.byt);
+ args = Py_BuildValue("(l)", (long)u.byt);
+ if (!args)
+ break;
+ ret = PyObject_Call((PyObject *)&DBusPyByte_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_BOOLEAN:
+ DBG("%s", "found a bool");
+ dbus_message_iter_get_basic(iter, &u.bool_val);
+ args = Py_BuildValue("(l)", (long)u.bool_val);
+ if (!args)
+ break;
+ ret = PyObject_Call((PyObject *)&DBusPyBoolean_Type, args, kwargs);
+ break;
+
+ case DBUS_TYPE_ARRAY:
+ DBG("%s", "found an array...");
+ /* Dicts are arrays of DBUS_TYPE_DICT_ENTRY on the wire.
+ Also, we special-case arrays of DBUS_TYPE_BYTE sometimes. */
+ type = dbus_message_iter_get_element_type(iter);
+ if (type == DBUS_TYPE_DICT_ENTRY) {
+ DBG("%s", "no, actually it's a dict...");
+ if (!kwargs) {
+ kwargs = PyDict_New();
+ if (!kwargs) break;
+ }
+ ret = _message_iter_get_dict(iter, opts, kwargs);
+ }
+ else if (opts->byte_arrays && type == DBUS_TYPE_BYTE) {
+ DBusMessageIter sub;
+ int n;
+
+ DBG("%s", "actually, a byte array...");
+ dbus_message_iter_recurse(iter, &sub);
+ dbus_message_iter_get_fixed_array(&sub,
+ (const unsigned char **)&u.str,
+ &n);
+ if (n == 0 && u.str == NULL) {
+ /* fd.o #21831: s# turns (NULL, 0) into None, but
+ * dbus_message_iter_get_fixed_array produces (NULL, 0)
+ * for an empty byte-blob... */
+ u.str = "";
+ }
+#ifdef PY3
+ args = Py_BuildValue("(y#)", u.str, (Py_ssize_t)n);
+#else
+ args = Py_BuildValue("(s#)", u.str, (Py_ssize_t)n);
+#endif
+ if (!args) break;
+ ret = PyObject_Call((PyObject *)&DBusPyByteArray_Type,
+ args, kwargs);
+ }
+ else {
+ DBusMessageIter sub;
+ char *sig;
+ PyObject *sig_obj;
+ int status;
+
+ DBG("%s", "a normal array...");
+ if (!kwargs) {
+ kwargs = PyDict_New();
+ if (!kwargs) break;
+ }
+ dbus_message_iter_recurse(iter, &sub);
+ sig = dbus_message_iter_get_signature(&sub);
+ if (!sig) break;
+ sig_obj = PyObject_CallFunction((PyObject *)&DBusPySignature_Type,
+ "(s)", sig);
+ dbus_free(sig);
+ if (!sig_obj) break;
+ status = PyDict_SetItem(kwargs, dbus_py_signature_const, sig_obj);
+ Py_CLEAR(sig_obj);
+ if (status < 0) break;
+ ret = PyObject_Call((PyObject *)&DBusPyArray_Type,
+ dbus_py_empty_tuple, kwargs);
+ if (!ret) break;
+ if (_message_iter_append_all_to_list(&sub, ret, opts) < 0) {
+ Py_CLEAR(ret);
+ }
+ }
+ break;
+
+ case DBUS_TYPE_STRUCT:
+ {
+ DBusMessageIter sub;
+ PyObject *list = PyList_New(0);
+ PyObject *tuple;
+
+ DBG("%s", "found a struct...");
+ if (!list) break;
+ dbus_message_iter_recurse(iter, &sub);
+ if (_message_iter_append_all_to_list(&sub, list, opts) < 0) {
+ Py_CLEAR(list);
+ break;
+ }
+ tuple = Py_BuildValue("(O)", list);
+ if (tuple) {
+ ret = PyObject_Call((PyObject *)&DBusPyStruct_Type, tuple, kwargs);
+ }
+ else {
+ ret = NULL;
+ }
+ /* whether successful or not, we take the same action: */
+ Py_CLEAR(list);
+ Py_CLEAR(tuple);
+ }
+ break;
+
+ case DBUS_TYPE_VARIANT:
+ {
+ DBusMessageIter sub;
+
+ DBG("%s", "found a variant...");
+ dbus_message_iter_recurse(iter, &sub);
+ ret = _message_iter_get_pyobject(&sub, opts, variant_level+1);
+ }
+ break;
+
+ default:
+ PyErr_Format(PyExc_TypeError, "Unknown type '\\%x' in D-Bus "
+ "message", type);
+ }
+
+ Py_CLEAR(args);
+ Py_CLEAR(kwargs);
+ return ret;
+}
+
+PyObject *
+dbus_py_Message_get_args_list(Message *self, PyObject *args, PyObject *kwargs)
+{
+#ifdef PY3
+ Message_get_args_options opts = { 0 };
+ static char *argnames[] = { "byte_arrays", NULL };
+#else
+ Message_get_args_options opts = { 0, 0 };
+ static char *argnames[] = { "byte_arrays", "utf8_strings", NULL };
+#endif
+ PyObject *list;
+ DBusMessageIter iter;
+
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: called Message_get_args_list(self, *",
+ (long)getpid());
+ PyObject_Print(args, stderr, 0);
+ if (kwargs) {
+ fprintf(stderr, ", **");
+ PyObject_Print(kwargs, stderr, 0);
+ }
+ fprintf(stderr, ")\n");
+#endif
+
+ if (PyTuple_Size(args) != 0) {
+ PyErr_SetString(PyExc_TypeError, "get_args_list takes no positional "
+ "arguments");
+ return NULL;
+ }
+#ifdef PY3
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:get_args_list",
+ argnames,
+ &(opts.byte_arrays))) return NULL;
+#else
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii:get_args_list",
+ argnames,
+ &(opts.byte_arrays),
+ &(opts.utf8_strings))) return NULL;
+#endif
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+
+ list = PyList_New(0);
+ if (!list) return NULL;
+
+ /* Iterate over args, if any, appending to list */
+ if (dbus_message_iter_init(self->msg, &iter)) {
+ if (_message_iter_append_all_to_list(&iter, list, &opts) < 0) {
+ Py_CLEAR(list);
+ DBG_EXC("%s", "Message_get_args: appending all to list failed:");
+ return NULL;
+ }
+ }
+
+#ifdef USING_DBG
+ fprintf(stderr, "DBG/%ld: message has args list ", (long)getpid());
+ PyObject_Print(list, stderr, 0);
+ fprintf(stderr, "\n");
+#endif
+
+ return list;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/message-internal.h b/dbus_bindings/message-internal.h
new file mode 100644
index 0000000..dfa78e0
--- /dev/null
+++ b/dbus_bindings/message-internal.h
@@ -0,0 +1,49 @@
+/* D-Bus message: implementation internals
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DBUS_BINDINGS_MESSAGE_INTERNAL_H
+#define DBUS_BINDINGS_MESSAGE_INTERNAL_H
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+
+typedef struct {
+ PyObject_HEAD
+ DBusMessage *msg;
+} Message;
+
+extern char dbus_py_Message_append__doc__[];
+extern PyObject *dbus_py_Message_append(Message *, PyObject *, PyObject *);
+extern char dbus_py_Message_guess_signature__doc__[];
+extern PyObject *dbus_py_Message_guess_signature(PyObject *, PyObject *);
+extern char dbus_py_Message_get_args_list__doc__[];
+extern PyObject *dbus_py_Message_get_args_list(Message *,
+ PyObject *,
+ PyObject *);
+
+extern PyObject *DBusPy_RaiseUnusableMessage(void);
+
+#endif
diff --git a/dbus_bindings/message.c b/dbus_bindings/message.c
new file mode 100644
index 0000000..43b8df6
--- /dev/null
+++ b/dbus_bindings/message.c
@@ -0,0 +1,1112 @@
+/* Implementation of D-Bus Message and subclasses (but see message-get-args.h
+ * and message-append.h for unserialization and serialization code).
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+#include "message-internal.h"
+
+static PyTypeObject MessageType, SignalMessageType, ErrorMessageType;
+static PyTypeObject MethodReturnMessageType, MethodCallMessageType;
+
+static inline int Message_Check(PyObject *o)
+{
+ return (Py_TYPE(o) == &MessageType)
+ || PyObject_IsInstance(o, (PyObject *)&MessageType);
+}
+
+PyObject *
+DBusPy_RaiseUnusableMessage(void)
+{
+ DBusPyException_SetString("Message object is uninitialized, or has become "
+ "unusable due to error while appending "
+ "arguments");
+ return NULL;
+}
+
+PyDoc_STRVAR(Message_tp_doc,
+"A message to be sent or received over a D-Bus Connection.\n");
+
+static void Message_tp_dealloc(Message *self)
+{
+ if (self->msg) {
+ dbus_message_unref(self->msg);
+ }
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyObject *
+Message_tp_new(PyTypeObject *type,
+ PyObject *args UNUSED,
+ PyObject *kwargs UNUSED)
+{
+ Message *self;
+
+ self = (Message *)type->tp_alloc(type, 0);
+ if (!self) return NULL;
+ self->msg = NULL;
+ return (PyObject *)self;
+}
+
+static PyObject *
+MethodCallMessage_tp_repr(PyObject *self)
+{
+ DBusMessage *msg = ((Message *)self)->msg;
+ const char *destination = dbus_message_get_destination(msg);
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ if (!path)
+ path = "n/a";
+ if (!interface)
+ interface = "n/a";
+ if (!member)
+ member = "n/a";
+ if (!destination)
+ destination = "n/a";
+
+ return PyUnicode_FromFormat(
+ "<%s path: %s, iface: %s, member: %s dest: %s>",
+ Py_TYPE(self)->tp_name,
+ path, interface, member, destination);
+}
+
+PyDoc_STRVAR(MethodCallMessage_tp_doc,
+"dbus.lowlevel.MethodCallMessage(destination: str or None, path: str, "
+"interface: str or None, method: str)\n"
+"\n"
+"A method-call message.\n"
+"\n"
+"``destination`` is the destination bus name, or None to send the\n"
+"message directly to the peer (usually the bus daemon).\n"
+"\n"
+"``path`` is the object-path of the object whose method is to be called.\n"
+"\n"
+"``interface`` is the interface qualifying the method name, or None to omit\n"
+"the interface from the message header.\n"
+"\n"
+"``method`` is the method name (member name).\n"
+);
+
+static int
+MethodCallMessage_tp_init(Message *self, PyObject *args, PyObject *kwargs)
+{
+ const char *destination, *path, *interface, *method;
+ static char *kwlist[] = {"destination", "path", "interface", "method", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "zszs:__init__", kwlist,
+ &destination, &path, &interface,
+ &method)) {
+ return -1;
+ }
+ if (destination && !dbus_py_validate_bus_name(destination, 1, 1)) return -1;
+ if (!dbus_py_validate_object_path(path)) return -1;
+ if (interface && !dbus_py_validate_interface_name(interface)) return -1;
+ if (!dbus_py_validate_member_name(method)) return -1;
+ if (self->msg) {
+ dbus_message_unref(self->msg);
+ self->msg = NULL;
+ }
+ self->msg = dbus_message_new_method_call(destination, path, interface,
+ method);
+ if (!self->msg) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+
+PyDoc_STRVAR(MethodReturnMessage_tp_doc,
+"dbus.lowlevel.MethodReturnMessage(method_call: MethodCallMessage)\n"
+"\n"
+"A method-return message.");
+
+static int
+MethodReturnMessage_tp_init(Message *self, PyObject *args, PyObject *kwargs)
+{
+ Message *other;
+ static char *kwlist[] = {"method_call", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!:__init__", kwlist,
+ &MessageType, &other)) {
+ return -1;
+ }
+ if (self->msg) {
+ dbus_message_unref(self->msg);
+ self->msg = NULL;
+ }
+ self->msg = dbus_message_new_method_return(other->msg);
+ if (!self->msg) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+
+PyDoc_STRVAR(SignalMessage_tp_doc,
+"dbus.lowlevel.SignalMessage(path: str, interface: str, method: str)\n"
+"\n"
+"A signal message.\n");
+static int
+SignalMessage_tp_init(Message *self, PyObject *args, PyObject *kwargs)
+{
+ const char *path, *interface, *name;
+ static char *kwlist[] = {"path", "interface", "name", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss:__init__", kwlist,
+ &path, &interface, &name)) {
+ return -1;
+ }
+ if (!dbus_py_validate_object_path(path)) return -1;
+ if (!dbus_py_validate_interface_name(interface)) return -1;
+ if (!dbus_py_validate_member_name(name)) return -1;
+ if (self->msg) {
+ dbus_message_unref(self->msg);
+ self->msg = NULL;
+ }
+ self->msg = dbus_message_new_signal(path, interface, name);
+ if (!self->msg) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject *
+SignalMessage_tp_repr(PyObject *self)
+{
+ DBusMessage *msg = ((Message *)self)->msg;
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+ const char *destination = dbus_message_get_destination(msg);
+
+ if (!path)
+ path = "n/a";
+ if (!interface)
+ interface = "n/a";
+ if (!member)
+ member = "n/a";
+ if (!destination)
+ destination = "(broadcast)";
+
+ return PyUnicode_FromFormat("<%s path: %s, iface: %s, member: %s, dest: %s>",
+ Py_TYPE(self)->tp_name,
+ path, interface, member, destination);
+}
+
+PyDoc_STRVAR(ErrorMessage_tp_doc,
+"dbus.lowlevel.ErrorMessage(reply_to: Message, error_name: str, "
+"error_message: str or None)\n"
+"\n"
+"An error message.\n");
+static int
+ErrorMessage_tp_init(Message *self, PyObject *args, PyObject *kwargs)
+{
+ Message *reply_to;
+ const char *error_name, *error_message;
+ static char *kwlist[] = {"reply_to", "error_name", "error_message", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!sz:__init__", kwlist,
+ &MessageType, &reply_to, &error_name,
+ &error_message)) {
+ return -1;
+ }
+ if (!dbus_py_validate_error_name(error_name)) return -1;
+ if (self->msg) {
+ dbus_message_unref(self->msg);
+ self->msg = NULL;
+ }
+ self->msg = dbus_message_new_error(reply_to->msg, error_name, error_message);
+ if (!self->msg) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ return 0;
+}
+
+DBusMessage *
+DBusPyMessage_BorrowDBusMessage(PyObject *msg)
+{
+ if (!Message_Check(msg)) {
+ PyErr_SetString(PyExc_TypeError,
+ "A dbus.lowlevel.Message instance is required");
+ return NULL;
+ }
+ if (!((Message *)msg)->msg) {
+ DBusPy_RaiseUnusableMessage();
+ return NULL;
+ }
+ return ((Message *)msg)->msg;
+}
+
+PyObject *
+DBusPyMessage_ConsumeDBusMessage(DBusMessage *msg)
+{
+ PyTypeObject *type;
+ Message *self;
+
+ switch (dbus_message_get_type(msg)) {
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ type = &MethodCallMessageType;
+ break;
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ type = &MethodReturnMessageType;
+ break;
+ case DBUS_MESSAGE_TYPE_ERROR:
+ type = &ErrorMessageType;
+ break;
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ type = &SignalMessageType;
+ break;
+ default:
+ type = &MessageType;
+ }
+
+ self = (Message *)(type->tp_new) (type, dbus_py_empty_tuple, NULL);
+ if (!self) {
+ dbus_message_unref(msg);
+ return NULL;
+ }
+ self->msg = msg;
+ return (PyObject *)self;
+}
+
+PyDoc_STRVAR(Message_copy__doc__,
+"message.copy() -> Message (or subclass)\n"
+"Deep-copy the message, resetting the serial number to zero.\n");
+static PyObject *
+Message_copy(Message *self, PyObject *args UNUSED)
+{
+ DBusMessage *msg;
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ msg = dbus_message_copy(self->msg);
+ if (!msg) return PyErr_NoMemory();
+ return DBusPyMessage_ConsumeDBusMessage(msg);
+}
+
+PyDoc_STRVAR(Message_get_auto_start__doc__,
+"message.get_auto_start() -> bool\n"
+"Return true if this message will cause an owner for the destination name\n"
+"to be auto-started.\n");
+static PyObject *
+Message_get_auto_start(Message *self, PyObject *unused UNUSED)
+{
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_get_auto_start(self->msg));
+}
+
+PyDoc_STRVAR(Message_set_auto_start__doc__,
+"message.set_auto_start(bool) -> None\n"
+"Set whether this message will cause an owner for the destination name\n"
+"to be auto-started.\n");
+static PyObject *
+Message_set_auto_start(Message *self, PyObject *args)
+{
+ int value;
+ if (!PyArg_ParseTuple(args, "i", &value)) return NULL;
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ dbus_message_set_auto_start(self->msg, value ? TRUE : FALSE);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(Message_get_no_reply__doc__,
+"message.get_no_reply() -> bool\n"
+"Return true if this message need not be replied to.\n");
+static PyObject *
+Message_get_no_reply(Message *self, PyObject *unused UNUSED)
+{
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_get_no_reply(self->msg));
+}
+
+PyDoc_STRVAR(Message_set_no_reply__doc__,
+"message.set_no_reply(bool) -> None\n"
+"Set whether no reply to this message is required.\n");
+static PyObject *
+Message_set_no_reply(Message *self, PyObject *args)
+{
+ int value;
+ if (!PyArg_ParseTuple(args, "i", &value)) return NULL;
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ dbus_message_set_no_reply(self->msg, value ? TRUE : FALSE);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Message_get_reply_serial__doc__,
+"message.get_reply_serial() -> long\n"
+"Returns the serial that the message is a reply to or 0 if none.\n");
+static PyObject *
+Message_get_reply_serial(Message *self, PyObject *unused UNUSED)
+{
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyLong_FromUnsignedLong(dbus_message_get_reply_serial(self->msg));
+}
+
+PyDoc_STRVAR(Message_set_reply_serial__doc__,
+"message.set_reply_serial(bool) -> None\n"
+"Set the serial that this message is a reply to.\n");
+static PyObject *
+Message_set_reply_serial(Message *self, PyObject *args)
+{
+ dbus_uint32_t value;
+
+ if (!PyArg_ParseTuple(args, "k", &value)) return NULL;
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ if (!dbus_message_set_reply_serial(self->msg, value)) {
+ return PyErr_NoMemory();
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+PyDoc_STRVAR(Message_get_type__doc__,
+"message.get_type() -> int\n\n"
+"Returns the type of the message.\n");
+static PyObject *
+Message_get_type(Message *self, PyObject *unused UNUSED)
+{
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return NATIVEINT_FROMLONG(dbus_message_get_type(self->msg));
+}
+
+PyDoc_STRVAR(Message_get_serial__doc__,
+"message.get_serial() -> long\n"
+"Returns the serial of a message or 0 if none has been specified.\n"
+"\n"
+"The message's serial number is provided by the application sending the\n"
+"message and is used to identify replies to this message. All messages\n"
+"received on a connection will have a serial, but messages you haven't\n"
+"sent yet may return 0.\n");
+static PyObject *
+Message_get_serial(Message *self, PyObject *unused UNUSED)
+{
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyLong_FromUnsignedLong(dbus_message_get_serial(self->msg));
+}
+
+PyDoc_STRVAR(Message_is_method_call__doc__,
+"is_method_call(interface: str, member: str) -> bool");
+static PyObject *
+Message_is_method_call(Message *self, PyObject *args)
+{
+ const char *interface, *method;
+
+ if (!PyArg_ParseTuple(args, "ss:is_method_call", &interface, &method)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_is_method_call(self->msg, interface,
+ method));
+}
+
+PyDoc_STRVAR(Message_is_error__doc__,
+"is_error(error: str) -> bool");
+static PyObject *
+Message_is_error(Message *self, PyObject *args)
+{
+ const char *error_name;
+
+ if (!PyArg_ParseTuple(args, "s:is_error", &error_name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_is_error(self->msg, error_name));
+}
+
+PyDoc_STRVAR(Message_is_signal__doc__,
+"is_signal(interface: str, member: str) -> bool");
+static PyObject *
+Message_is_signal(Message *self, PyObject *args)
+{
+ const char *interface, *signal_name;
+
+ if (!PyArg_ParseTuple(args, "ss:is_signal", &interface, &signal_name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_is_signal(self->msg, interface,
+ signal_name));
+}
+
+PyDoc_STRVAR(Message_get_member__doc__,
+"get_member() -> str or None");
+static PyObject *
+Message_get_member(Message *self, PyObject *unused UNUSED)
+{
+ const char *c_str;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ c_str = dbus_message_get_member(self->msg);
+ if (!c_str) {
+ Py_RETURN_NONE;
+ }
+ return NATIVESTR_FROMSTR(c_str);
+}
+
+PyDoc_STRVAR(Message_has_member__doc__,
+"has_member(name: str or None) -> bool");
+static PyObject *
+Message_has_member(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:has_member", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_has_member(self->msg, name));
+}
+
+PyDoc_STRVAR(Message_set_member__doc__,
+"set_member(unique_name: str or None)");
+static PyObject *
+Message_set_member(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:set_member", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ if (!dbus_py_validate_member_name(name)) return NULL;
+ if (!dbus_message_set_member(self->msg, name)) return PyErr_NoMemory();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Message_get_path__doc__,
+"get_path() -> ObjectPath or None\n\n"
+"Return the message's destination object path (if it's a method call) or\n"
+"source object path (if it's a method reply or a signal) or None (if it\n"
+"has no path).\n");
+static PyObject *
+Message_get_path(Message *self, PyObject *unused UNUSED)
+{
+ const char *c_str;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ c_str = dbus_message_get_path(self->msg);
+ if (!c_str) {
+ Py_RETURN_NONE;
+ }
+ return PyObject_CallFunction((PyObject *)&DBusPyObjectPath_Type, "(s)", c_str);
+}
+
+PyDoc_STRVAR(Message_get_path_decomposed__doc__,
+"get_path_decomposed() -> list of str, or None\n\n"
+"Return a list of path components (e.g. /foo/bar -> ['foo','bar'], / -> [])\n"
+"or None if the message has no associated path.\n");
+static PyObject *
+Message_get_path_decomposed(Message *self, PyObject *unused UNUSED)
+{
+ char **paths, **ptr;
+ PyObject *ret = PyList_New(0);
+
+ if (!ret) return NULL;
+ if (!self->msg) {
+ Py_CLEAR(ret);
+ return DBusPy_RaiseUnusableMessage();
+ }
+ if (!dbus_message_get_path_decomposed(self->msg, &paths)) {
+ Py_CLEAR(ret);
+ return PyErr_NoMemory();
+ }
+ if (!paths) {
+ Py_CLEAR(ret);
+ Py_RETURN_NONE;
+ }
+ for (ptr = paths; *ptr; ptr++) {
+ PyObject *str = NATIVESTR_FROMSTR(*ptr);
+
+ if (!str) {
+ Py_CLEAR(ret);
+ break;
+ }
+ if (PyList_Append(ret, str) < 0) {
+ Py_CLEAR(ret);
+ break;
+ }
+ Py_CLEAR(str);
+ str = NULL;
+ }
+ dbus_free_string_array(paths);
+ return ret;
+}
+
+PyDoc_STRVAR(Message_has_path__doc__,
+"has_path(name: str or None) -> bool");
+static PyObject *
+Message_has_path(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:has_path", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_has_path(self->msg, name));
+}
+
+PyDoc_STRVAR(Message_set_path__doc__,
+"set_path(name: str or None)");
+static PyObject *
+Message_set_path(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:set_path", &name)) return NULL;
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ if (!dbus_message_has_path(self->msg, name)) return PyErr_NoMemory();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Message_get_signature__doc__,
+"get_signature() -> Signature or None");
+static PyObject *
+Message_get_signature(Message *self, PyObject *unused UNUSED)
+{
+ const char *c_str;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ c_str = dbus_message_get_signature(self->msg);
+ if (!c_str) {
+ return PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s)", "");
+ }
+ return PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "(s)", c_str);
+}
+
+PyDoc_STRVAR(Message_has_signature__doc__,
+"has_signature(signature: str) -> bool");
+static PyObject *
+Message_has_signature(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "s:has_signature", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_has_signature(self->msg, name));
+}
+
+PyDoc_STRVAR(Message_get_sender__doc__,
+"get_sender() -> str or None\n\n"
+"Return the message's sender unique name, or None if none.\n");
+static PyObject *
+Message_get_sender(Message *self, PyObject *unused UNUSED)
+{
+ const char *c_str;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ c_str = dbus_message_get_sender(self->msg);
+ if (!c_str) {
+ Py_RETURN_NONE;
+ }
+ return NATIVESTR_FROMSTR(c_str);
+}
+
+PyDoc_STRVAR(Message_has_sender__doc__,
+"has_sender(unique_name: str) -> bool");
+static PyObject *
+Message_has_sender(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "s:has_sender", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_has_sender(self->msg, name));
+}
+
+PyDoc_STRVAR(Message_set_sender__doc__,
+"set_sender(unique_name: str or None)");
+static PyObject *
+Message_set_sender(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:set_sender", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ if (!dbus_py_validate_bus_name(name, 1, 1)) return NULL;
+ if (!dbus_message_set_sender(self->msg, name)) return PyErr_NoMemory();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Message_get_destination__doc__,
+"get_destination() -> str or None\n\n"
+"Return the message's destination bus name, or None if none.\n");
+static PyObject *
+Message_get_destination(Message *self, PyObject *unused UNUSED)
+{
+ const char *c_str;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ c_str = dbus_message_get_destination(self->msg);
+ if (!c_str) {
+ Py_RETURN_NONE;
+ }
+ return NATIVESTR_FROMSTR(c_str);
+}
+
+PyDoc_STRVAR(Message_has_destination__doc__,
+"has_destination(bus_name: str) -> bool");
+static PyObject *
+Message_has_destination(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "s:has_destination", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_has_destination(self->msg, name));
+}
+
+PyDoc_STRVAR(Message_set_destination__doc__,
+"set_destination(bus_name: str or None)");
+static PyObject *
+Message_set_destination(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:set_destination", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ if (!dbus_py_validate_bus_name(name, 1, 1)) return NULL;
+ if (!dbus_message_set_destination(self->msg, name)) return PyErr_NoMemory();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Message_get_interface__doc__,
+"get_interface() -> str or None");
+static PyObject *
+Message_get_interface(Message *self, PyObject *unused UNUSED)
+{
+ const char *c_str;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ c_str = dbus_message_get_interface(self->msg);
+ if (!c_str) {
+ Py_RETURN_NONE;
+ }
+ return NATIVESTR_FROMSTR(c_str);
+}
+
+PyDoc_STRVAR(Message_has_interface__doc__,
+"has_interface(interface: str or None) -> bool");
+static PyObject *
+Message_has_interface(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:has_interface", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ return PyBool_FromLong(dbus_message_has_interface(self->msg, name));
+}
+
+PyDoc_STRVAR(Message_set_interface__doc__,
+"set_interface(name: str or None)");
+static PyObject *
+Message_set_interface(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:set_interface", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ if (!dbus_py_validate_interface_name(name)) return NULL;
+ if (!dbus_message_set_interface(self->msg, name)) return PyErr_NoMemory();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Message_get_error_name__doc__,
+"get_error_name() -> str or None");
+static PyObject *
+Message_get_error_name(Message *self, PyObject *unused UNUSED)
+{
+ const char *c_str;
+
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ c_str = dbus_message_get_error_name(self->msg);
+ if (!c_str) {
+ Py_RETURN_NONE;
+ }
+ return NATIVESTR_FROMSTR(c_str);
+}
+
+PyDoc_STRVAR(Message_set_error_name__doc__,
+"set_error_name(name: str or None)");
+static PyObject *
+Message_set_error_name(Message *self, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "z:set_error_name", &name)) {
+ return NULL;
+ }
+ if (!self->msg) return DBusPy_RaiseUnusableMessage();
+ if (!dbus_py_validate_error_name(name)) return NULL;
+ if (!dbus_message_set_error_name(self->msg, name)) return PyErr_NoMemory();
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef Message_tp_methods[] = {
+ {"copy", (PyCFunction)Message_copy,
+ METH_NOARGS, Message_copy__doc__},
+ {"is_method_call", (PyCFunction)Message_is_method_call,
+ METH_VARARGS, Message_is_method_call__doc__},
+ {"is_signal", (PyCFunction)Message_is_signal,
+ METH_VARARGS, Message_is_signal__doc__},
+ {"is_error", (PyCFunction)Message_is_error,
+ METH_VARARGS, Message_is_error__doc__},
+
+ {"get_args_list", (PyCFunction)dbus_py_Message_get_args_list,
+ METH_VARARGS|METH_KEYWORDS, dbus_py_Message_get_args_list__doc__},
+ {"guess_signature", (PyCFunction)dbus_py_Message_guess_signature,
+ METH_VARARGS|METH_STATIC, dbus_py_Message_guess_signature__doc__},
+ {"append", (PyCFunction)dbus_py_Message_append,
+ METH_VARARGS|METH_KEYWORDS, dbus_py_Message_append__doc__},
+
+ {"get_auto_start", (PyCFunction)Message_get_auto_start,
+ METH_NOARGS, Message_get_auto_start__doc__},
+ {"set_auto_start", (PyCFunction)Message_set_auto_start,
+ METH_VARARGS, Message_set_auto_start__doc__},
+ {"get_destination", (PyCFunction)Message_get_destination,
+ METH_NOARGS, Message_get_destination__doc__},
+ {"set_destination", (PyCFunction)Message_set_destination,
+ METH_VARARGS, Message_set_destination__doc__},
+ {"has_destination", (PyCFunction)Message_has_destination,
+ METH_VARARGS, Message_has_destination__doc__},
+ {"get_error_name", (PyCFunction)Message_get_error_name,
+ METH_NOARGS, Message_get_error_name__doc__},
+ {"set_error_name", (PyCFunction)Message_set_error_name,
+ METH_VARARGS, Message_set_error_name__doc__},
+ {"get_interface", (PyCFunction)Message_get_interface,
+ METH_NOARGS, Message_get_interface__doc__},
+ {"set_interface", (PyCFunction)Message_set_interface,
+ METH_VARARGS, Message_set_interface__doc__},
+ {"has_interface", (PyCFunction)Message_has_interface,
+ METH_VARARGS, Message_has_interface__doc__},
+ {"get_member", (PyCFunction)Message_get_member,
+ METH_NOARGS, Message_get_member__doc__},
+ {"set_member", (PyCFunction)Message_set_member,
+ METH_VARARGS, Message_set_member__doc__},
+ {"has_member", (PyCFunction)Message_has_member,
+ METH_VARARGS, Message_has_member__doc__},
+ {"get_path", (PyCFunction)Message_get_path,
+ METH_NOARGS, Message_get_path__doc__},
+ {"get_path_decomposed", (PyCFunction)Message_get_path_decomposed,
+ METH_NOARGS, Message_get_path_decomposed__doc__},
+ {"set_path", (PyCFunction)Message_set_path,
+ METH_VARARGS, Message_set_path__doc__},
+ {"has_path", (PyCFunction)Message_has_path,
+ METH_VARARGS, Message_has_path__doc__},
+ {"get_no_reply", (PyCFunction)Message_get_no_reply,
+ METH_NOARGS, Message_get_no_reply__doc__},
+ {"set_no_reply", (PyCFunction)Message_set_no_reply,
+ METH_VARARGS, Message_set_no_reply__doc__},
+ {"get_reply_serial", (PyCFunction)Message_get_reply_serial,
+ METH_NOARGS, Message_get_reply_serial__doc__},
+ {"set_reply_serial", (PyCFunction)Message_set_reply_serial,
+ METH_VARARGS, Message_set_reply_serial__doc__},
+ {"get_sender", (PyCFunction)Message_get_sender,
+ METH_NOARGS, Message_get_sender__doc__},
+ {"set_sender", (PyCFunction)Message_set_sender,
+ METH_VARARGS, Message_set_sender__doc__},
+ {"has_sender", (PyCFunction)Message_has_sender,
+ METH_VARARGS, Message_has_sender__doc__},
+ {"get_serial", (PyCFunction)Message_get_serial,
+ METH_NOARGS, Message_get_serial__doc__},
+ {"get_signature", (PyCFunction)Message_get_signature,
+ METH_NOARGS, Message_get_signature__doc__},
+ {"has_signature", (PyCFunction)Message_has_signature,
+ METH_VARARGS, Message_has_signature__doc__},
+ {"get_type", (PyCFunction)Message_get_type,
+ METH_NOARGS, Message_get_type__doc__},
+ {NULL, NULL, 0, NULL}
+};
+
+static PyTypeObject MessageType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "dbus.lowlevel.Message", /*tp_name*/
+ sizeof(Message), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ (destructor)Message_tp_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Message_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ Message_tp_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Message_tp_new, /* tp_new */
+};
+
+static PyTypeObject MethodCallMessageType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "dbus.lowlevel.MethodCallMessage", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ MethodCallMessage_tp_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ MethodCallMessage_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&MessageType), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)MethodCallMessage_tp_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+static PyTypeObject MethodReturnMessageType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "dbus.lowlevel.MethodReturnMessage", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ MethodReturnMessage_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&MessageType), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)MethodReturnMessage_tp_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+static PyTypeObject SignalMessageType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "dbus.lowlevel.SignalMessage", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ SignalMessage_tp_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ SignalMessage_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&MessageType), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)SignalMessage_tp_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+static PyTypeObject ErrorMessageType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "dbus.lowlevel.ErrorMessage", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ 0, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash */
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ ErrorMessage_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&MessageType), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)ErrorMessage_tp_init, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
+
+dbus_bool_t
+dbus_py_init_message_types(void)
+{
+ if (PyType_Ready(&MessageType) < 0) return 0;
+
+ MethodCallMessageType.tp_base = &MessageType;
+ if (PyType_Ready(&MethodCallMessageType) < 0) return 0;
+
+ MethodReturnMessageType.tp_base = &MessageType;
+ if (PyType_Ready(&MethodReturnMessageType) < 0) return 0;
+
+ SignalMessageType.tp_base = &MessageType;
+ if (PyType_Ready(&SignalMessageType) < 0) return 0;
+
+ ErrorMessageType.tp_base = &MessageType;
+ if (PyType_Ready(&ErrorMessageType) < 0) return 0;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_message_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF (&MessageType);
+ Py_INCREF (&MethodCallMessageType);
+ Py_INCREF (&MethodReturnMessageType);
+ Py_INCREF (&ErrorMessageType);
+ Py_INCREF (&SignalMessageType);
+
+ if (PyModule_AddObject(this_module, "Message",
+ (PyObject *)&MessageType) < 0) return 0;
+
+ if (PyModule_AddObject(this_module, "MethodCallMessage",
+ (PyObject *)&MethodCallMessageType) < 0) return 0;
+
+ if (PyModule_AddObject(this_module, "MethodReturnMessage",
+ (PyObject *)&MethodReturnMessageType) < 0) return 0;
+
+ if (PyModule_AddObject(this_module, "ErrorMessage",
+ (PyObject *)&ErrorMessageType) < 0) return 0;
+
+ if (PyModule_AddObject(this_module, "SignalMessage",
+ (PyObject *)&SignalMessageType) < 0) return 0;
+
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/module.c b/dbus_bindings/module.c
new file mode 100644
index 0000000..5b8851c
--- /dev/null
+++ b/dbus_bindings/module.c
@@ -0,0 +1,432 @@
+/* Main module source for the _dbus_bindings extension.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+#include <structmember.h>
+
+PyDoc_STRVAR(module_doc,
+"Low-level Python bindings for libdbus. Don't use this module directly -\n"
+"the public API is provided by the `dbus`, `dbus.service`, `dbus.mainloop`\n"
+"and `dbus.mainloop.glib` modules, with a lower-level API provided by the\n"
+"`dbus.lowlevel` module.\n"
+);
+
+/* Global functions - validation wrappers ===========================*/
+
+PyDoc_STRVAR(validate_bus_name__doc__,
+"validate_bus_name(name, allow_unique=True, allow_well_known=True)\n"
+"\n"
+"Raise ValueError if the argument is not a valid bus name.\n"
+"\n"
+"By default both unique and well-known names are accepted.\n"
+"\n"
+":Parameters:\n"
+" `name` : str\n"
+" The name to be validated\n"
+" `allow_unique` : bool\n"
+" If False, unique names of the form :1.123 will be rejected\n"
+" `allow_well_known` : bool\n"
+" If False, well-known names of the form com.example.Foo\n"
+" will be rejected\n"
+":Since: 0.80\n"
+);
+
+static PyObject *
+validate_bus_name(PyObject *unused UNUSED, PyObject *args, PyObject *kwargs)
+{
+ const char *name;
+ int allow_unique = 1;
+ int allow_well_known = 1;
+ static char *argnames[] = { "name", "allow_unique", "allow_well_known",
+ NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs,
+ "s|ii:validate_bus_name", argnames,
+ &name, &allow_unique,
+ &allow_well_known)) {
+ return NULL;
+ }
+ if (!dbus_py_validate_bus_name(name, !!allow_unique, !!allow_well_known)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(validate_member_name__doc__,
+"validate_member_name(name)\n"
+"\n"
+"Raise ValueError if the argument is not a valid member (signal or method) "
+"name.\n"
+"\n"
+":Since: 0.80\n"
+);
+
+static PyObject *
+validate_member_name(PyObject *unused UNUSED, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "s:validate_member_name", &name)) {
+ return NULL;
+ }
+ if (!dbus_py_validate_member_name(name)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(validate_interface_name__doc__,
+"validate_interface_name(name)\n\n"
+"Raise ValueError if the given string is not a valid interface name.\n"
+"\n"
+":Since: 0.80\n"
+);
+
+PyDoc_STRVAR(validate_error_name__doc__,
+"validate_error_name(name)\n\n"
+"Raise ValueError if the given string is not a valid error name.\n"
+"\n"
+":Since: 0.80\n"
+);
+
+static PyObject *
+validate_interface_name(PyObject *unused UNUSED, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "s:validate_interface_name", &name)) {
+ return NULL;
+ }
+ if (!dbus_py_validate_interface_name(name)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(validate_object_path__doc__,
+"validate_object_path(name)\n\n"
+"Raise ValueError if the given string is not a valid object path.\n"
+"\n"
+":Since: 0.80\n"
+);
+
+static PyObject *
+validate_object_path(PyObject *unused UNUSED, PyObject *args)
+{
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "s:validate_object_path", &name)) {
+ return NULL;
+ }
+ if (!dbus_py_validate_object_path(name)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+/* Global functions - main loop =====================================*/
+
+/* The main loop if none is passed to the constructor */
+static PyObject *default_main_loop = NULL;
+
+/* Return a new reference to the default main loop */
+PyObject *
+dbus_py_get_default_main_loop(void)
+{
+ if (!default_main_loop) {
+ Py_RETURN_NONE;
+ }
+ Py_INCREF(default_main_loop);
+ return default_main_loop;
+}
+
+PyDoc_STRVAR(get_default_main_loop__doc__,
+"get_default_main_loop() -> object\n\n"
+"Return the global default dbus-python main loop wrapper, which is used\n"
+"when no main loop wrapper is passed to the Connection constructor.\n"
+"\n"
+"If None, there is no default and you should always pass the mainloop\n"
+"parameter to the constructor - if you don't, then asynchronous calls,\n"
+"connecting to signals and exporting objects will raise an exception.\n"
+"There is no default until set_default_main_loop is called.\n");
+static PyObject *
+get_default_main_loop(PyObject *always_null UNUSED,
+ PyObject *no_args UNUSED)
+{
+ return dbus_py_get_default_main_loop();
+}
+
+PyDoc_STRVAR(set_default_main_loop__doc__,
+"set_default_main_loop(object)\n\n"
+"Change the global default dbus-python main loop wrapper, which is used\n"
+"when no main loop wrapper is passed to the Connection constructor.\n"
+"\n"
+"If None, return to the initial situation: there is no default, and you\n"
+"must always pass the mainloop parameter to the constructor.\n"
+"\n"
+"Two types of main loop wrapper are planned in dbus-python.\n"
+"Native main-loop wrappers are instances of `dbus.mainloop.NativeMainLoop`\n"
+"supplied by extension modules like `dbus.mainloop.glib`: they have no\n"
+"Python API, but connect themselves to ``libdbus`` using native code.\n"
+
+"Python main-loop wrappers are not yet implemented. They will be objects\n"
+"supporting the interface defined by `dbus.mainloop.MainLoop`, with an\n"
+"API entirely based on Python methods.\n"
+"\n"
+);
+static PyObject *
+set_default_main_loop(PyObject *always_null UNUSED,
+ PyObject *args)
+{
+ PyObject *new_loop, *old_loop;
+
+ if (!PyArg_ParseTuple(args, "O", &new_loop)) {
+ return NULL;
+ }
+ if (!dbus_py_check_mainloop_sanity(new_loop)) {
+ return NULL;
+ }
+ old_loop = default_main_loop;
+ Py_INCREF(new_loop);
+ default_main_loop = new_loop;
+ Py_CLEAR(old_loop);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef module_functions[] = {
+#define ENTRY(name,flags) {#name, (PyCFunction)name, flags, name##__doc__}
+ ENTRY(validate_interface_name, METH_VARARGS),
+ ENTRY(validate_member_name, METH_VARARGS),
+ ENTRY(validate_bus_name, METH_VARARGS|METH_KEYWORDS),
+ ENTRY(validate_object_path, METH_VARARGS),
+ ENTRY(set_default_main_loop, METH_VARARGS),
+ ENTRY(get_default_main_loop, METH_NOARGS),
+ /* validate_error_name is just implemented as validate_interface_name */
+ {"validate_error_name", validate_interface_name,
+ METH_VARARGS, validate_error_name__doc__},
+#undef ENTRY
+ {NULL, NULL, 0, NULL}
+};
+
+PyMODINIT_FUNC
+#ifdef PY3
+PyInit__dbus_bindings(void)
+#else
+init_dbus_bindings(void)
+#endif
+{
+ PyObject *this_module = NULL, *c_api;
+ static const int API_count = DBUS_BINDINGS_API_COUNT;
+ static _dbus_py_func_ptr dbus_bindings_API[DBUS_BINDINGS_API_COUNT];
+
+#ifdef PY3
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_dbus_bindings", /* m_name */
+ module_doc, /* m_doc */
+ -1, /* m_size */
+ module_functions, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+ };
+#endif
+
+ dbus_bindings_API[0] = (_dbus_py_func_ptr)&API_count;
+ dbus_bindings_API[1] = (_dbus_py_func_ptr)DBusPyConnection_BorrowDBusConnection;
+ dbus_bindings_API[2] = (_dbus_py_func_ptr)DBusPyNativeMainLoop_New4;
+
+ default_main_loop = NULL;
+
+ if (!dbus_py_init_generic()) goto init_error;
+ if (!dbus_py_init_abstract()) goto init_error;
+ if (!dbus_py_init_signature()) goto init_error;
+ if (!dbus_py_init_int_types()) goto init_error;
+ if (!dbus_py_init_unixfd_type()) goto init_error;
+ if (!dbus_py_init_string_types()) goto init_error;
+ if (!dbus_py_init_float_types()) goto init_error;
+ if (!dbus_py_init_container_types()) goto init_error;
+ if (!dbus_py_init_byte_types()) goto init_error;
+ if (!dbus_py_init_message_types()) goto init_error;
+ if (!dbus_py_init_pending_call()) goto init_error;
+ if (!dbus_py_init_mainloop()) goto init_error;
+ if (!dbus_py_init_libdbus_conn_types()) goto init_error;
+ if (!dbus_py_init_conn_types()) goto init_error;
+ if (!dbus_py_init_server_types()) goto init_error;
+
+#ifdef PY3
+ this_module = PyModule_Create(&moduledef);
+#else
+ this_module = Py_InitModule3("_dbus_bindings",
+ module_functions, module_doc);
+#endif
+ if (!this_module) goto init_error;
+
+ if (!dbus_py_insert_abstract_types(this_module)) goto init_error;
+ if (!dbus_py_insert_signature(this_module)) goto init_error;
+ if (!dbus_py_insert_int_types(this_module)) goto init_error;
+ if (!dbus_py_insert_unixfd_type(this_module)) goto init_error;
+ if (!dbus_py_insert_string_types(this_module)) goto init_error;
+ if (!dbus_py_insert_float_types(this_module)) goto init_error;
+ if (!dbus_py_insert_container_types(this_module)) goto init_error;
+ if (!dbus_py_insert_byte_types(this_module)) goto init_error;
+ if (!dbus_py_insert_message_types(this_module)) goto init_error;
+ if (!dbus_py_insert_pending_call(this_module)) goto init_error;
+ if (!dbus_py_insert_mainloop_types(this_module)) goto init_error;
+ if (!dbus_py_insert_libdbus_conn_types(this_module)) goto init_error;
+ if (!dbus_py_insert_conn_types(this_module)) goto init_error;
+ if (!dbus_py_insert_server_types(this_module)) goto init_error;
+
+ if (PyModule_AddStringConstant(this_module, "BUS_DAEMON_NAME",
+ DBUS_SERVICE_DBUS) < 0) goto init_error;
+ if (PyModule_AddStringConstant(this_module, "BUS_DAEMON_PATH",
+ DBUS_PATH_DBUS) < 0) goto init_error;
+ if (PyModule_AddStringConstant(this_module, "BUS_DAEMON_IFACE",
+ DBUS_INTERFACE_DBUS) < 0) goto init_error;
+ if (PyModule_AddStringConstant(this_module, "LOCAL_PATH",
+ DBUS_PATH_LOCAL) < 0) goto init_error;
+ if (PyModule_AddStringConstant(this_module, "LOCAL_IFACE",
+ DBUS_INTERFACE_LOCAL) < 0) goto init_error;
+ if (PyModule_AddStringConstant(this_module, "INTROSPECTABLE_IFACE",
+ DBUS_INTERFACE_INTROSPECTABLE) < 0)
+ goto init_error;
+ if (PyModule_AddStringConstant(this_module, "PEER_IFACE",
+ DBUS_INTERFACE_PEER) < 0) goto init_error;
+ if (PyModule_AddStringConstant(this_module, "PROPERTIES_IFACE",
+ DBUS_INTERFACE_PROPERTIES) < 0)
+ goto init_error;
+ if (PyModule_AddStringConstant(this_module,
+ "DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER",
+ DBUS_INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER) < 0)
+ goto init_error;
+ if (PyModule_AddStringConstant(this_module,
+ "DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER",
+ DBUS_INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER) < 0)
+ goto init_error;
+ if (PyModule_AddStringConstant(this_module,
+ "DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE",
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE) < 0)
+ goto init_error;
+
+#define ADD_CONST_VAL(x, v) \
+ if (PyModule_AddIntConstant(this_module, x, v) < 0) goto init_error;
+#define ADD_CONST_PREFIXED(x) ADD_CONST_VAL(#x, DBUS_##x)
+#define ADD_CONST(x) ADD_CONST_VAL(#x, x)
+
+ ADD_CONST(DBUS_START_REPLY_SUCCESS)
+ ADD_CONST(DBUS_START_REPLY_ALREADY_RUNNING)
+
+ ADD_CONST_PREFIXED(RELEASE_NAME_REPLY_RELEASED)
+ ADD_CONST_PREFIXED(RELEASE_NAME_REPLY_NON_EXISTENT)
+ ADD_CONST_PREFIXED(RELEASE_NAME_REPLY_NOT_OWNER)
+
+ ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_IN_QUEUE)
+ ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_EXISTS)
+ ADD_CONST_PREFIXED(REQUEST_NAME_REPLY_ALREADY_OWNER)
+
+ ADD_CONST_PREFIXED(NAME_FLAG_ALLOW_REPLACEMENT)
+ ADD_CONST_PREFIXED(NAME_FLAG_REPLACE_EXISTING)
+ ADD_CONST_PREFIXED(NAME_FLAG_DO_NOT_QUEUE)
+
+ ADD_CONST_PREFIXED(BUS_SESSION)
+ ADD_CONST_PREFIXED(BUS_SYSTEM)
+ ADD_CONST_PREFIXED(BUS_STARTER)
+
+ ADD_CONST_PREFIXED(MESSAGE_TYPE_INVALID)
+ ADD_CONST_PREFIXED(MESSAGE_TYPE_METHOD_CALL)
+ ADD_CONST_PREFIXED(MESSAGE_TYPE_METHOD_RETURN)
+ ADD_CONST_PREFIXED(MESSAGE_TYPE_ERROR)
+ ADD_CONST_PREFIXED(MESSAGE_TYPE_SIGNAL)
+
+ ADD_CONST_PREFIXED(TYPE_INVALID)
+ ADD_CONST_PREFIXED(TYPE_BYTE)
+ ADD_CONST_PREFIXED(TYPE_BOOLEAN)
+ ADD_CONST_PREFIXED(TYPE_INT16)
+ ADD_CONST_PREFIXED(TYPE_UINT16)
+ ADD_CONST_PREFIXED(TYPE_INT32)
+#ifdef DBUS_TYPE_UNIX_FD
+ ADD_CONST_PREFIXED(TYPE_UNIX_FD)
+#endif
+ ADD_CONST_PREFIXED(TYPE_UINT32)
+ ADD_CONST_PREFIXED(TYPE_INT64)
+ ADD_CONST_PREFIXED(TYPE_UINT64)
+ ADD_CONST_PREFIXED(TYPE_DOUBLE)
+ ADD_CONST_PREFIXED(TYPE_STRING)
+ ADD_CONST_PREFIXED(TYPE_OBJECT_PATH)
+ ADD_CONST_PREFIXED(TYPE_SIGNATURE)
+ ADD_CONST_PREFIXED(TYPE_ARRAY)
+ ADD_CONST_PREFIXED(TYPE_STRUCT)
+ ADD_CONST_VAL("STRUCT_BEGIN", DBUS_STRUCT_BEGIN_CHAR)
+ ADD_CONST_VAL("STRUCT_END", DBUS_STRUCT_END_CHAR)
+ ADD_CONST_PREFIXED(TYPE_VARIANT)
+ ADD_CONST_PREFIXED(TYPE_DICT_ENTRY)
+ ADD_CONST_VAL("DICT_ENTRY_BEGIN", DBUS_DICT_ENTRY_BEGIN_CHAR)
+ ADD_CONST_VAL("DICT_ENTRY_END", DBUS_DICT_ENTRY_END_CHAR)
+
+ ADD_CONST_PREFIXED(HANDLER_RESULT_HANDLED)
+ ADD_CONST_PREFIXED(HANDLER_RESULT_NOT_YET_HANDLED)
+ ADD_CONST_PREFIXED(HANDLER_RESULT_NEED_MEMORY)
+
+ ADD_CONST_PREFIXED(WATCH_READABLE)
+ ADD_CONST_PREFIXED(WATCH_WRITABLE)
+ ADD_CONST_PREFIXED(WATCH_HANGUP)
+ ADD_CONST_PREFIXED(WATCH_ERROR)
+
+ if (PyModule_AddStringConstant(this_module, "__docformat__",
+ "restructuredtext") < 0) goto init_error;
+
+ if (PyModule_AddStringConstant(this_module, "__version__",
+ PACKAGE_VERSION) < 0) goto init_error;
+
+ if (PyModule_AddIntConstant(this_module, "_python_version",
+ PY_VERSION_HEX) < 0) goto init_error;
+
+#ifdef PY3
+ c_api = PyCapsule_New((void *)dbus_bindings_API,
+ PYDBUS_CAPSULE_NAME, NULL);
+#else
+ c_api = PyCObject_FromVoidPtr ((void *)dbus_bindings_API, NULL);
+#endif
+ if (!c_api) {
+ goto init_error;
+ }
+ PyModule_AddObject(this_module, "_C_API", c_api);
+
+#ifdef PY3
+ return this_module;
+ init_error:
+ Py_CLEAR(this_module);
+ return NULL;
+#else
+ init_error:
+ return;
+#endif
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/pending-call.c b/dbus_bindings/pending-call.c
new file mode 100644
index 0000000..469c997
--- /dev/null
+++ b/dbus_bindings/pending-call.c
@@ -0,0 +1,294 @@
+/* Implementation of PendingCall helper type for D-Bus bindings.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+PyDoc_STRVAR(PendingCall_tp_doc,
+"Object representing a pending D-Bus call, returned by\n"
+"Connection.send_message_with_reply(). Cannot be instantiated directly.\n"
+);
+
+static PyTypeObject PendingCallType;
+
+static inline int PendingCall_Check (PyObject *o)
+{
+ return (Py_TYPE(o) == &PendingCallType)
+ || PyObject_IsInstance(o, (PyObject *)&PendingCallType);
+}
+
+typedef struct {
+ PyObject_HEAD
+ DBusPendingCall *pc;
+} PendingCall;
+
+PyDoc_STRVAR(PendingCall_cancel__doc__,
+"cancel()\n\n"
+"Cancel this pending call. Its reply will be ignored and the associated\n"
+"reply handler will never be called.\n");
+static PyObject *
+PendingCall_cancel(PendingCall *self, PyObject *unused UNUSED)
+{
+ Py_BEGIN_ALLOW_THREADS
+ dbus_pending_call_cancel(self->pc);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(PendingCall_block__doc__,
+"block()\n\n"
+"Block until this pending call has completed and the associated\n"
+"reply handler has been called.\n"
+"\n"
+"This can lead to a deadlock, if the called method tries to make a\n"
+"synchronous call to a method in this application.\n");
+static PyObject *
+PendingCall_block(PendingCall *self, PyObject *unused UNUSED)
+{
+ Py_BEGIN_ALLOW_THREADS
+ dbus_pending_call_block(self->pc);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+static void
+_pending_call_notify_function(DBusPendingCall *pc,
+ PyObject *list)
+{
+ PyGILState_STATE gil = PyGILState_Ensure();
+ /* BEGIN CRITICAL SECTION
+ * While holding the GIL, make sure the callback only gets called once
+ * by deleting it from the 1-item list that's held by libdbus.
+ */
+ PyObject *handler = PyList_GetItem(list, 0);
+ DBusMessage *msg;
+
+ if (!handler) {
+ PyErr_Print();
+ goto release;
+ }
+ if (handler == Py_None) {
+ /* We've already called (and thrown away) the callback */
+ goto release;
+ }
+ Py_INCREF(handler); /* previously borrowed from the list, now owned */
+ Py_INCREF(Py_None); /* take a ref so SetItem can steal it */
+ PyList_SetItem(list, 0, Py_None);
+ /* END CRITICAL SECTION */
+
+ msg = dbus_pending_call_steal_reply(pc);
+
+ if (!msg) {
+ /* omg, what happened here? the notify should only get called
+ * when we have a reply */
+ PyErr_Warn(PyExc_UserWarning, "D-Bus notify function was called "
+ "for an incomplete pending call (shouldn't happen)");
+ } else {
+ PyObject *msg_obj = DBusPyMessage_ConsumeDBusMessage(msg);
+
+ if (msg_obj) {
+ PyObject *ret = PyObject_CallFunctionObjArgs(handler, msg_obj, NULL);
+
+ if (!ret) {
+ PyErr_Print();
+ }
+ Py_CLEAR(ret);
+ Py_CLEAR(msg_obj);
+ }
+ /* else OOM has happened - not a lot we can do about that,
+ * except possibly making it fatal (FIXME?) */
+ }
+
+release:
+ Py_CLEAR(handler);
+ PyGILState_Release(gil);
+}
+
+PyDoc_STRVAR(PendingCall_get_completed__doc__,
+"get_completed() -> bool\n\n"
+"Return true if this pending call has completed.\n\n"
+"If so, its associated reply handler has been called and it is no\n"
+"longer meaningful to cancel it.\n");
+static PyObject *
+PendingCall_get_completed(PendingCall *self, PyObject *unused UNUSED)
+{
+ dbus_bool_t ret;
+
+ Py_BEGIN_ALLOW_THREADS
+ ret = dbus_pending_call_get_completed(self->pc);
+ Py_END_ALLOW_THREADS
+ return PyBool_FromLong(ret);
+}
+
+/* Steals the reference to the pending call. */
+PyObject *
+DBusPyPendingCall_ConsumeDBusPendingCall(DBusPendingCall *pc,
+ PyObject *callable)
+{
+ dbus_bool_t ret;
+ PyObject *list = PyList_New(1);
+ PendingCall *self = PyObject_New(PendingCall, &PendingCallType);
+
+ if (!list || !self) {
+ Py_CLEAR(list);
+ Py_CLEAR(self);
+ Py_BEGIN_ALLOW_THREADS
+ dbus_pending_call_cancel(pc);
+ dbus_pending_call_unref(pc);
+ Py_END_ALLOW_THREADS
+ return NULL;
+ }
+
+ /* INCREF because SET_ITEM steals a ref */
+ Py_INCREF(callable);
+ PyList_SET_ITEM(list, 0, callable);
+
+ /* INCREF so we can give a ref to set_notify and still have one */
+ Py_INCREF(list);
+
+ Py_BEGIN_ALLOW_THREADS
+ ret = dbus_pending_call_set_notify(pc,
+ (DBusPendingCallNotifyFunction)_pending_call_notify_function,
+ (void *)list, (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
+ Py_END_ALLOW_THREADS
+
+ if (!ret) {
+ PyErr_NoMemory();
+ /* DECREF twice - one for the INCREF and one for the allocation */
+ Py_DECREF(list);
+ Py_CLEAR(list);
+ Py_CLEAR(self);
+ Py_BEGIN_ALLOW_THREADS
+ dbus_pending_call_cancel(pc);
+ dbus_pending_call_unref(pc);
+ Py_END_ALLOW_THREADS
+ return NULL;
+ }
+
+ /* As Alexander Larsson pointed out on dbus@lists.fd.o on 2006-11-30,
+ * the API has a race condition if set_notify runs in one thread and a
+ * mail loop runs in another - if the reply gets in before set_notify
+ * runs, the notify isn't called and there is no indication of error.
+ *
+ * The workaround is to check for completion immediately, but this also
+ * has a race which might lead to getting the notify called twice if
+ * we're unlucky. So I use the list to arrange for the notify to be
+ * deleted before it's called for the second time. The GIL protects
+ * the critical section in which I delete the callback from the list.
+ */
+ if (dbus_pending_call_get_completed(pc)) {
+ /* the first race condition happened, so call the callable here.
+ * FIXME: we ought to arrange for the callable to run from the
+ * mainloop thread, like it would if the race hadn't happened...
+ * this needs a better mainloop abstraction, though.
+ */
+ _pending_call_notify_function(pc, list);
+ }
+
+ Py_CLEAR(list);
+ self->pc = pc;
+ return (PyObject *)self;
+}
+
+static void
+PendingCall_tp_dealloc (PendingCall *self)
+{
+ if (self->pc) {
+ Py_BEGIN_ALLOW_THREADS
+ dbus_pending_call_unref(self->pc);
+ Py_END_ALLOW_THREADS
+ }
+ PyObject_Del (self);
+}
+
+static PyMethodDef PendingCall_tp_methods[] = {
+ {"block", (PyCFunction)PendingCall_block, METH_NOARGS,
+ PendingCall_block__doc__},
+ {"cancel", (PyCFunction)PendingCall_cancel, METH_NOARGS,
+ PendingCall_cancel__doc__},
+ {"get_completed", (PyCFunction)PendingCall_get_completed, METH_NOARGS,
+ PendingCall_get_completed__doc__},
+ {NULL, NULL, 0, NULL}
+};
+
+static PyTypeObject PendingCallType = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.lowlevel.PendingCall",
+ sizeof(PendingCall),
+ 0,
+ (destructor)PendingCall_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ PendingCall_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ PendingCall_tp_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ /* deliberately not callable! Use PendingCall_ConsumeDBusPendingCall */
+ 0, /* tp_new */
+};
+
+dbus_bool_t
+dbus_py_init_pending_call (void)
+{
+ if (PyType_Ready (&PendingCallType) < 0) return 0;
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_pending_call (PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF (&PendingCallType);
+ if (PyModule_AddObject (this_module, "PendingCall",
+ (PyObject *)&PendingCallType) < 0) return 0;
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/server.c b/dbus_bindings/server.c
new file mode 100644
index 0000000..3ce67c2
--- /dev/null
+++ b/dbus_bindings/server.c
@@ -0,0 +1,618 @@
+/* Implementation of the _dbus_bindings Server type, a Python wrapper
+ * for DBusServer.
+ *
+ * Copyright (C) 2008 Openismus GmbH <http://openismus.com/>
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+/* Server definition ================================================ */
+
+typedef struct {
+ PyObject_HEAD
+ DBusServer *server;
+
+ /* The Connection subtype for which this Server is a factory */
+ PyObject *conn_class;
+
+ /* Weak-references list to make server weakly referenceable */
+ PyObject *weaklist;
+
+ PyObject *mainloop;
+} Server;
+
+PyDoc_STRVAR(Server_tp_doc,
+"A D-Bus server.\n"
+"\n"
+"::\n"
+"\n"
+" Server(address, connection_subtype, mainloop=None, auth_mechanisms=None)\n"
+" -> Server\n"
+);
+
+/* D-Bus Server user data slot, containing an owned reference to either
+ * the Server, or a weakref to the Server.
+ */
+static dbus_int32_t _server_python_slot;
+
+/* C API for main-loop hooks ======================================== */
+
+/* Return a borrowed reference to the DBusServer which underlies this
+ * Server. */
+DBusServer *
+DBusPyServer_BorrowDBusServer(PyObject *self)
+{
+ DBusServer *dbs;
+
+ TRACE(self);
+ if (!DBusPyServer_Check(self)) {
+ PyErr_SetString(PyExc_TypeError, "A dbus.server.Server is required");
+ return NULL;
+ }
+ dbs = ((Server *)self)->server;
+ if (!dbs) {
+ PyErr_SetString(PyExc_RuntimeError, "Server is in an invalid "
+ "state: no DBusServer");
+ return NULL;
+ }
+ return dbs;
+}
+
+/* Internal C API =================================================== */
+
+static dbus_bool_t
+DBusPyServer_set_auth_mechanisms(Server *self,
+ PyObject *auth_mechanisms)
+{
+ PyObject *fast_seq = NULL, *references = NULL;
+ Py_ssize_t length;
+ Py_ssize_t i;
+ /* a mutable array of constant strings */
+ const char **list = NULL;
+ dbus_bool_t ret = FALSE;
+
+ fast_seq = PySequence_Fast(auth_mechanisms,
+ "Expecting sequence for auth_mechanisms parameter");
+
+ if (!fast_seq)
+ return FALSE;
+
+ length = PySequence_Fast_GET_SIZE(fast_seq);
+
+ list = calloc (length + 1, sizeof (char *));
+
+ if (!list) {
+ PyErr_NoMemory();
+ goto finally;
+ }
+
+ if (!(references = PyTuple_New(length)))
+ goto finally;
+
+ for (i = 0; i < length; ++i) {
+ PyObject *am, *am_as_bytes;
+
+ am = PySequence_Fast_GET_ITEM(auth_mechanisms, i);
+ if (!am)
+ goto finally;
+
+ if (PyUnicode_Check(am)) {
+ am_as_bytes = PyUnicode_AsUTF8String(am);
+ if (!am_as_bytes)
+ goto finally;
+ }
+ else {
+ am_as_bytes = am;
+ Py_INCREF(am_as_bytes);
+ }
+ list[i] = PyBytes_AsString(am_as_bytes);
+ if (!list[i])
+ goto finally;
+
+ PyTuple_SET_ITEM(references, i, am_as_bytes);
+ }
+
+ list[length] = NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_set_auth_mechanisms(self->server, list);
+ Py_END_ALLOW_THREADS
+
+ ret = TRUE;
+
+finally:
+ if (list)
+ free (list);
+ Py_CLEAR(fast_seq);
+ Py_CLEAR(references);
+ return ret;
+}
+
+/* Return a new reference to a Python Server or subclass corresponding
+ * to the DBusServer server. For use in callbacks.
+ *
+ * Raises AssertionError if the DBusServer does not have a Server.
+ */
+static PyObject *
+DBusPyServer_ExistingFromDBusServer(DBusServer *server)
+{
+ PyObject *self, *ref;
+
+ Py_BEGIN_ALLOW_THREADS
+ ref = (PyObject *)dbus_server_get_data(server,
+ _server_python_slot);
+ Py_END_ALLOW_THREADS
+ if (ref) {
+ DBG("(DBusServer *)%p has weak reference at %p", server, ref);
+ self = PyWeakref_GetObject(ref); /* still a borrowed ref */
+ if (self && self != Py_None && DBusPyServer_Check(self)) {
+ DBG("(DBusServer *)%p has weak reference at %p pointing to %p",
+ server, ref, self);
+ TRACE(self);
+ Py_INCREF(self);
+ TRACE(self);
+ return self;
+ }
+ }
+
+ PyErr_SetString(PyExc_AssertionError,
+ "D-Bus server does not have a Server "
+ "instance associated with it");
+ return NULL;
+}
+
+static void
+DBusPyServer_new_connection_cb(DBusServer *server,
+ DBusConnection *conn,
+ void *data UNUSED)
+{
+ PyGILState_STATE gil = PyGILState_Ensure();
+ PyObject *self = NULL;
+ PyObject *method = NULL;
+
+ self = DBusPyServer_ExistingFromDBusServer(server);
+ if (!self) goto out;
+ TRACE(self);
+
+ method = PyObject_GetAttrString(self, "_on_new_connection");
+ TRACE(method);
+
+ if (method) {
+ PyObject *conn_class = ((Server *)self)->conn_class;
+ PyObject *wrapper = DBusPyLibDBusConnection_New(conn);
+ PyObject *conn_obj;
+ PyObject *result;
+
+ if (!wrapper)
+ goto out;
+
+ conn_obj = PyObject_CallFunctionObjArgs((PyObject *)conn_class,
+ wrapper, ((Server*) self)->mainloop, NULL);
+ Py_CLEAR(wrapper);
+
+ if (!conn_obj)
+ goto out;
+
+ result = PyObject_CallFunctionObjArgs(method, conn_obj, NULL);
+ Py_CLEAR (conn_obj);
+
+ /* discard result if not NULL, and fall through regardless */
+ Py_CLEAR(result);
+ }
+
+out:
+ Py_CLEAR(method);
+ Py_CLEAR(self);
+
+ if (PyErr_Occurred())
+ PyErr_Print();
+
+ PyGILState_Release(gil);
+}
+
+/* Return a new reference to a Python Server or subclass (given by cls)
+ * corresponding to the DBusServer server, which must have been newly
+ * created. For use by the Server constructor.
+ *
+ * Raises AssertionError if the DBusServer already has a Server.
+ *
+ * One reference to server is stolen - either the returned DBusPyServer
+ * claims it, or it's unreffed.
+ */
+static PyObject *
+DBusPyServer_NewConsumingDBusServer(PyTypeObject *cls,
+ DBusServer *server,
+ PyObject *conn_class,
+ PyObject *mainloop,
+ PyObject *auth_mechanisms)
+{
+ Server *self = NULL;
+ PyObject *ref;
+ dbus_bool_t ok;
+
+ DBG("%s(cls=%p, server=%p, mainloop=%p, auth_mechanisms=%p)",
+ __func__, cls, server, mainloop, auth_mechanisms);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(server);
+
+ Py_BEGIN_ALLOW_THREADS
+ ref = (PyObject *)dbus_server_get_data(server,
+ _server_python_slot);
+ Py_END_ALLOW_THREADS
+ if (ref) {
+ self = (Server *)PyWeakref_GetObject(ref);
+ ref = NULL;
+ if (self && (PyObject *)self != Py_None) {
+ self = NULL;
+ PyErr_SetString(PyExc_AssertionError,
+ "Newly created D-Bus server already has a "
+ "Server instance associated with it");
+ DBG("%s() fail - assertion failed, DBusPyServer has a DBusServer already", __func__);
+ DBG_WHEREAMI;
+ return NULL;
+ }
+ }
+ ref = NULL;
+
+ /* Change mainloop from a borrowed reference to an owned reference */
+ if (!mainloop || mainloop == Py_None) {
+ mainloop = dbus_py_get_default_main_loop();
+
+ if (!mainloop || mainloop == Py_None) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "To run a D-Bus server, you need to either "
+ "pass mainloop=... to the constructor or call "
+ "dbus.set_default_main_loop(...)");
+ goto err;
+ }
+ }
+ else {
+ Py_INCREF(mainloop);
+ }
+
+ DBG("Constructing Server from DBusServer at %p", server);
+
+ self = (Server *)(cls->tp_alloc(cls, 0));
+ if (!self) goto err;
+ TRACE(self);
+
+ DBG_WHEREAMI;
+
+ self->server = NULL;
+
+ Py_INCREF(conn_class);
+ self->conn_class = conn_class;
+
+ self->mainloop = mainloop;
+ mainloop = NULL; /* don't DECREF it - the DBusServer owns it now */
+
+ ref = PyWeakref_NewRef((PyObject *)self, NULL);
+ if (!ref) goto err;
+ DBG("Created weak ref %p to (Server *)%p for (DBusServer *)%p",
+ ref, self, server);
+
+ Py_BEGIN_ALLOW_THREADS
+ ok = dbus_server_set_data(server, _server_python_slot,
+ (void *)ref,
+ (DBusFreeFunction)dbus_py_take_gil_and_xdecref);
+ Py_END_ALLOW_THREADS
+
+ if (ok) {
+ DBG("Attached weak ref %p ((Server *)%p) to (DBusServer *)%p",
+ ref, self, server);
+
+ ref = NULL; /* don't DECREF it - the DBusServer owns it now */
+ }
+ else {
+ DBG("Failed to attached weak ref %p ((Server *)%p) to "
+ "(DBusServer *)%p - will dispose of it", ref, self, server);
+ PyErr_NoMemory();
+ goto err;
+ }
+
+ DBUS_PY_RAISE_VIA_GOTO_IF_FAIL(server, err);
+ self->server = server;
+ /* the DBusPyServer will close it now */
+ server = NULL;
+
+ if (self->mainloop != Py_None &&
+ !dbus_py_set_up_server((PyObject *)self, self->mainloop))
+ goto err;
+
+ if (auth_mechanisms && auth_mechanisms != Py_None &&
+ !DBusPyServer_set_auth_mechanisms(self, auth_mechanisms))
+ goto err;
+
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_set_new_connection_function(self->server,
+ DBusPyServer_new_connection_cb,
+ NULL, NULL);
+ Py_END_ALLOW_THREADS
+
+ DBG("%s() -> %p", __func__, self);
+ TRACE(self);
+ return (PyObject *)self;
+
+err:
+ DBG("Failed to construct Server from DBusServer at %p", server);
+ Py_CLEAR(mainloop);
+ Py_CLEAR(self);
+ Py_CLEAR(ref);
+
+ if (server) {
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_disconnect(server);
+ dbus_server_unref(server);
+ Py_END_ALLOW_THREADS
+ }
+
+ DBG("%s() fail", __func__);
+ DBG_WHEREAMI;
+ return NULL;
+}
+
+/* Server type-methods ============================================== */
+
+static PyObject *
+Server_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ DBusServer *server;
+ const char *address;
+ DBusError error;
+ PyObject *self, *conn_class, *mainloop = NULL, *auth_mechanisms = NULL;
+ static char *argnames[] = { "address", "connection_class", "mainloop",
+ "auth_mechanisms", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|OO", argnames,
+ &address, &conn_class, &mainloop, &auth_mechanisms)) {
+ return NULL;
+ }
+
+ if (!PyType_Check(conn_class) ||
+ !PyType_IsSubtype((PyTypeObject *) conn_class, &DBusPyConnection_Type)) {
+ /* strictly speaking, it can be any subtype of
+ * _dbus_bindings._Connection - but nobody else should be subtyping
+ * that, so let's keep this slightly inaccurate message */
+ PyErr_SetString(PyExc_TypeError, "connection_class must be "
+ "dbus.connection.Connection or a subtype");
+ return NULL;
+ }
+
+ dbus_error_init(&error);
+
+ Py_BEGIN_ALLOW_THREADS
+ server = dbus_server_listen(address, &error);
+ Py_END_ALLOW_THREADS
+
+ if (!server) {
+ DBusPyException_ConsumeError(&error);
+ return NULL;
+ }
+
+ self = DBusPyServer_NewConsumingDBusServer(cls, server, conn_class,
+ mainloop, auth_mechanisms);
+
+ if (!self) {
+ return NULL;
+ }
+
+ ((Server *)self)->weaklist = NULL;
+ TRACE(self);
+
+ return self;
+}
+
+/* Destructor */
+static void Server_tp_dealloc(Server *self)
+{
+ DBusServer *server = self->server;
+ PyObject *et, *ev, *etb;
+
+ /* avoid clobbering any pending exception */
+ PyErr_Fetch(&et, &ev, &etb);
+
+ if (self->weaklist) {
+ PyObject_ClearWeakRefs((PyObject *)self);
+ }
+
+ TRACE(self);
+ DBG("Deallocating Server at %p (DBusServer at %p)", self, server);
+ DBG_WHEREAMI;
+
+ if (server) {
+ DBG("Server at %p has a server, disconnecting it...", self);
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_disconnect(server);
+ Py_END_ALLOW_THREADS
+ }
+
+ Py_CLEAR(self->mainloop);
+
+ /* make sure to do this last to preserve the invariant that
+ * self->server is always non-NULL for any referenced Server.
+ */
+ DBG("Server at %p: nulling self->server", self);
+ self->server = NULL;
+
+ if (server) {
+ DBG("Server at %p: unreffing server", self);
+ dbus_server_unref(server);
+ }
+
+ DBG("Server at %p: freeing self", self);
+ PyErr_Restore(et, ev, etb);
+ (Py_TYPE(self)->tp_free)((PyObject *)self);
+}
+
+PyDoc_STRVAR(Server_disconnect__doc__,
+"disconnect()\n\n"
+"Releases the server's address and stops listening for new clients.\n\n"
+"If called more than once, only the first call has an effect.");
+static PyObject *
+Server_disconnect (Server *self, PyObject *args UNUSED)
+{
+ TRACE(self);
+ if (self->server) {
+ Py_BEGIN_ALLOW_THREADS
+ dbus_server_disconnect(self->server);
+ Py_END_ALLOW_THREADS
+ }
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(Server_get_address__doc__,
+"get_address() -> str\n\n"
+"Returns the address of the server.");
+static PyObject *
+Server_get_address(Server *self, PyObject *args UNUSED)
+{
+ const char *address;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+ Py_BEGIN_ALLOW_THREADS
+ address = dbus_server_get_address(self->server);
+ Py_END_ALLOW_THREADS
+
+ return NATIVESTR_FROMSTR(address);
+}
+
+PyDoc_STRVAR(Server_get_id__doc__,
+"get_id() -> str\n\n"
+"Returns the unique ID of the server.");
+static PyObject *
+Server_get_id(Server *self, PyObject *args UNUSED)
+{
+ const char *id;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+ Py_BEGIN_ALLOW_THREADS
+ id = dbus_server_get_id(self->server);
+ Py_END_ALLOW_THREADS
+
+ return NATIVESTR_FROMSTR(id);
+}
+
+PyDoc_STRVAR(Server_get_is_connected__doc__,
+"get_is_connected() -> bool\n\n"
+"Return true if this Server is still listening for new connections.\n");
+static PyObject *
+Server_get_is_connected (Server *self, PyObject *args UNUSED)
+{
+ dbus_bool_t ret;
+
+ TRACE(self);
+ DBUS_PY_RAISE_VIA_NULL_IF_FAIL(self->server);
+ Py_BEGIN_ALLOW_THREADS
+ ret = dbus_server_get_is_connected(self->server);
+ Py_END_ALLOW_THREADS
+ return PyBool_FromLong(ret);
+}
+
+/* Server type object =============================================== */
+
+struct PyMethodDef DBusPyServer_tp_methods[] = {
+#define ENTRY(name, flags) {#name, (PyCFunction)Server_##name, flags, Server_##name##__doc__}
+ ENTRY(disconnect, METH_NOARGS),
+ ENTRY(get_address, METH_NOARGS),
+ ENTRY(get_id, METH_NOARGS),
+ ENTRY(get_is_connected, METH_NOARGS),
+ {NULL},
+#undef ENTRY
+};
+
+PyTypeObject DBusPyServer_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "_dbus_bindings._Server",/*tp_name*/
+ sizeof(Server), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)Server_tp_dealloc,
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+#ifdef PY3
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+#else
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS | Py_TPFLAGS_BASETYPE,
+#endif
+ Server_tp_doc, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ offsetof(Server, weaklist), /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ DBusPyServer_tp_methods,/*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ Server_tp_new, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+dbus_bool_t
+dbus_py_init_server_types(void)
+{
+ /* Get a slot to store our weakref on DBus Server */
+ _server_python_slot = -1;
+ if (!dbus_server_allocate_data_slot(&_server_python_slot))
+ return FALSE;
+
+ if (PyType_Ready(&DBusPyServer_Type) < 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+dbus_bool_t
+dbus_py_insert_server_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF (&DBusPyServer_Type);
+ if (PyModule_AddObject(this_module, "_Server",
+ (PyObject *)&DBusPyServer_Type) < 0) return FALSE;
+
+ return TRUE;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/signature.c b/dbus_bindings/signature.c
new file mode 100644
index 0000000..c1a32e7
--- /dev/null
+++ b/dbus_bindings/signature.c
@@ -0,0 +1,255 @@
+/* Implementation of Signature type for D-Bus bindings.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "types-internal.h"
+
+PyDoc_STRVAR(Signature_tp_doc,
+"Signature(value: str or unicode[, variant_level: int])\n"
+"\n"
+"A string subclass whose values are restricted to valid D-Bus\n"
+"signatures. When iterated over, instead of individual characters it\n"
+"produces Signature instances representing single complete types.\n"
+"\n"
+"``value`` must be a valid D-Bus signature (zero or more single complete\n"
+"types).\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a signature, this is represented in Python by a\n"
+" Signature with variant_level==2.\n"
+);
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *bytes;
+ DBusSignatureIter iter;
+} SignatureIter;
+
+static void
+SignatureIter_tp_dealloc (SignatureIter *self)
+{
+ Py_CLEAR(self->bytes);
+ PyObject_Del(self);
+}
+
+static PyObject *
+SignatureIter_tp_iternext (SignatureIter *self)
+{
+ char *sig;
+ PyObject *obj;
+
+ /* Stop immediately if finished or not correctly initialized */
+ if (!self->bytes) return NULL;
+
+ sig = dbus_signature_iter_get_signature(&(self->iter));
+ if (!sig) return PyErr_NoMemory();
+ obj = PyObject_CallFunction((PyObject *)&DBusPySignature_Type, "s", sig);
+ dbus_free(sig);
+ if (!obj) return NULL;
+
+ if (!dbus_signature_iter_next(&(self->iter))) {
+ /* mark object as having been finished with */
+ Py_CLEAR(self->bytes);
+ }
+
+ return obj;
+}
+
+static PyObject *
+SignatureIter_tp_iter(PyObject *self)
+{
+ Py_INCREF(self);
+ return self;
+}
+
+static PyTypeObject SignatureIterType = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "_dbus_bindings._SignatureIter",
+ sizeof(SignatureIter),
+ 0,
+ (destructor)SignatureIter_tp_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ 0, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ SignatureIter_tp_iter, /* tp_iter */
+ (iternextfunc)SignatureIter_tp_iternext, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ /* deliberately not callable! Use iter(Signature) instead */
+ 0, /* tp_new */
+ 0, /* tp_free */
+};
+
+static PyObject *
+Signature_tp_iter(PyObject *self)
+{
+ SignatureIter *iter = PyObject_New(SignatureIter, &SignatureIterType);
+ PyObject *self_as_bytes;
+
+ if (!iter) return NULL;
+
+#ifdef PY3
+ self_as_bytes = PyUnicode_AsUTF8String(self);
+ if (!self_as_bytes) {
+ Py_CLEAR(iter);
+ return NULL;
+ }
+#else
+ self_as_bytes = self;
+ Py_INCREF(self_as_bytes);
+#endif
+
+ if (PyBytes_GET_SIZE(self_as_bytes) > 0) {
+ iter->bytes = self_as_bytes;
+ dbus_signature_iter_init(&(iter->iter),
+ PyBytes_AS_STRING(self_as_bytes));
+ }
+ else {
+ /* this is a null string, make a null iterator */
+ iter->bytes = NULL;
+ Py_CLEAR(self_as_bytes);
+ }
+ return (PyObject *)iter;
+}
+
+static PyObject *
+Signature_tp_new (PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ const char *str = NULL;
+ PyObject *ignored;
+ static char *argnames[] = {"object_path", "variant_level", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|O:__new__", argnames,
+ &str, &ignored)) return NULL;
+ if (!dbus_signature_validate(str, NULL)) {
+ PyErr_SetString(PyExc_ValueError, "Corrupt type signature");
+ return NULL;
+ }
+ return (DBusPyStrBase_Type.tp_new)(cls, args, kwargs);
+}
+
+PyTypeObject DBusPySignature_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.Signature",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ Signature_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ Signature_tp_iter, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPyStrBase_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ Signature_tp_new, /* tp_new */
+ 0, /* tp_free */
+};
+
+dbus_bool_t
+dbus_py_init_signature(void)
+{
+ if (PyType_Ready(&SignatureIterType) < 0) return 0;
+
+ DBusPySignature_Type.tp_base = &DBusPyStrBase_Type;
+ if (PyType_Ready(&DBusPySignature_Type) < 0) return 0;
+ DBusPySignature_Type.tp_print = NULL;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_signature(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF(&DBusPySignature_Type);
+ if (PyModule_AddObject(this_module, "Signature",
+ (PyObject *)&DBusPySignature_Type) < 0) return 0;
+ Py_INCREF(&SignatureIterType);
+ if (PyModule_AddObject(this_module, "_SignatureIter",
+ (PyObject *)&SignatureIterType) < 0) return 0;
+
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/string.c b/dbus_bindings/string.c
new file mode 100644
index 0000000..ce8f03d
--- /dev/null
+++ b/dbus_bindings/string.c
@@ -0,0 +1,375 @@
+/* Simple D-Bus types: ObjectPath and other string types.
+ *
+ * Copyright (C) 2006-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include "types-internal.h"
+#include <structmember.h>
+
+#ifndef PY3
+/* UTF-8 string representation ====================================== */
+
+PyDoc_STRVAR(UTF8String_tp_doc,
+"dbus.UTF8String(value: bytes or unicode[, variant_level: int=0])\n"
+"\n"
+"A string represented using UTF-8 - a subtype of `bytes`.\n"
+"This type is only available in Python 2.\n"
+"\n"
+"All strings on D-Bus are required to be valid Unicode; in the \"wire\n"
+"protocol\" they're transported as UTF-8.\n"
+"\n"
+"By default, when byte arrays are converted from D-Bus to Python, they\n"
+"come out as a `dbus.String`, which is a subtype of `unicode`.\n"
+"If you prefer to get UTF-8 strings (as instances of this class) or you\n"
+"want to avoid the conversion overhead of going from UTF-8 to Python's\n"
+"internal Unicode representation, you can pass the ``utf8_strings=True``\n"
+"keyword argument to any of these methods:\n"
+"\n"
+"* any D-Bus method proxy, or ``connect_to_signal``, on the objects returned\n"
+" by `Bus.get_object`\n"
+"* any D-Bus method on a `dbus.Interface`\n"
+"* `dbus.Interface.connect_to_signal`\n"
+"* `Bus.add_signal_receiver`\n"
+"\n"
+"If value is a bytes object it must be valid UTF-8.\n"
+"\n"
+"variant_level must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing a string, this is represented in Python by a\n"
+" String or UTF8String with variant_level==2.\n"
+"\n"
+":Since: 0.80 (in older versions, use dbus.String)\n"
+);
+
+static PyObject *
+UTF8String_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ const char *str = NULL;
+ long variantness = 0;
+ static char *argnames[] = {"value", "variant_level", NULL};
+ PyObject *unicode;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|l:__new__", argnames,
+ &str, &variantness)) return NULL;
+ unicode = PyUnicode_DecodeUTF8(str, strlen(str), NULL);
+ if (!unicode) return NULL;
+ Py_CLEAR(unicode);
+ return (DBusPyStrBase_Type.tp_new)(cls, args, kwargs);
+}
+
+PyTypeObject DBusPyUTF8String_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.UTF8String",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ UTF8String_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPyStrBase_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ UTF8String_tp_new, /* tp_new */
+};
+#endif /* !PY3 */
+
+/* Object path ====================================================== */
+
+PyDoc_STRVAR(ObjectPath_tp_doc,
+"dbus.ObjectPath(path: str[, variant_level: int=0])\n"
+"A D-Bus object path, such as ``/com/example/MyApp/Documents/abc``.\n"
+"\n"
+"ObjectPath is a subtype of :py:class:`str`, and object-paths behave like strings.\n"
+"\n"
+"path must be an ASCII string following the syntax of object paths.\n"
+"variant_level must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing an object path, this is represented in Python by an\n"
+" ObjectPath with variant_level==2.\n"
+);
+
+static PyObject *
+ObjectPath_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ const char *str = NULL;
+ long variantness = 0;
+ static char *argnames[] = {"object_path", "variant_level", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|l:__new__", argnames,
+ &str, &variantness)) return NULL;
+ if (!dbus_py_validate_object_path(str)) {
+ return NULL;
+ }
+ return (DBusPyStrBase_Type.tp_new)(cls, args, kwargs);
+}
+
+PyTypeObject DBusPyObjectPath_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.ObjectPath",
+ 0,
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ ObjectPath_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&DBusPyStrBase_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ ObjectPath_tp_new, /* tp_new */
+};
+
+/* Unicode string representation ==================================== */
+
+PyDoc_STRVAR(String_tp_doc,
+"dbus.String(value: str or unicode[, variant_level: int])\n"
+"\n"
+"A string represented using Unicode - a subtype of ``unicode`` (Python 2)\n"
+"or ``str`` (Python 3).\n"
+"\n"
+"All strings on D-Bus are required to be valid Unicode; in the \"wire\n"
+"protocol\" they're transported as UTF-8.\n"
+"\n"
+"By default, when strings are converted from D-Bus to Python, they\n"
+"come out as this class. In Python 2, if you prefer to get UTF-8 strings\n"
+"(as instances\n"
+"of a subtype of `str`) or you want to avoid the conversion overhead of\n"
+"going from UTF-8 to Python's internal Unicode representation, see the\n"
+"documentation for `dbus.UTF8String`.\n"
+"\n"
+"variant_level must be non-negative; the default is 0.\n"
+);
+
+static PyMemberDef String_tp_members[] = {
+ {"variant_level", T_LONG, offsetof(DBusPyString, variant_level),
+ READONLY,
+ "Indicates how many nested Variant containers this object\n"
+ "is contained in: if a message's wire format has a variant containing a\n"
+ "variant containing an array, this is represented in Python by a\n"
+ "String or UTF8String with variant_level==2.\n"
+ },
+ {NULL},
+};
+
+static PyObject *
+String_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs)
+{
+ PyObject *self;
+ long variantness = 0;
+ static char *argnames[] = {"variant_level", NULL};
+
+ if (PyTuple_Size(args) > 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "__new__ takes at most one positional parameter");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(dbus_py_empty_tuple, kwargs,
+ "|l:__new__", argnames,
+ &variantness)) return NULL;
+ if (variantness < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "variant_level must be non-negative");
+ return NULL;
+ }
+ self = (PyUnicode_Type.tp_new)(cls, args, NULL);
+ if (self) {
+ ((DBusPyString *)self)->variant_level = variantness;
+ }
+ return self;
+}
+
+static PyObject *
+String_tp_repr(PyObject *self)
+{
+ PyObject *parent_repr = (PyUnicode_Type.tp_repr)(self);
+ PyObject *my_repr;
+
+ if (!parent_repr) {
+ return NULL;
+ }
+ if (((DBusPyString *)self)->variant_level > 0) {
+ my_repr = PyUnicode_FromFormat("%s(%V, variant_level=%ld)",
+ Py_TYPE(self)->tp_name,
+ REPRV(parent_repr),
+ ((DBusPyString *)self)->variant_level);
+ }
+ else {
+ my_repr = PyUnicode_FromFormat("%s(%V)", Py_TYPE(self)->tp_name,
+ REPRV(parent_repr));
+ }
+ /* whether my_repr is NULL or not: */
+ Py_CLEAR(parent_repr);
+ return my_repr;
+}
+
+PyTypeObject DBusPyString_Type = {
+ PyVarObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type), 0)
+ "dbus.String",
+ sizeof(DBusPyString),
+ 0,
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ String_tp_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ dbus_py_immutable_setattro, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+ String_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ String_tp_members, /* tp_members */
+ 0, /* tp_getset */
+ DEFERRED_ADDRESS(&PyUnicode_Type), /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ String_tp_new, /* tp_new */
+};
+
+dbus_bool_t
+dbus_py_init_string_types(void)
+{
+ /* don't need to do strange contortions for unicode, since it's not a
+ * "variable-size" object (it has a pointer to its data instead)
+ */
+ if (PyUnicode_Type.tp_itemsize != 0) {
+ fprintf(stderr, "dbus-python is not compatible with this version of "
+ "Python (unicode objects are assumed to be fixed-size)");
+ return 0;
+ }
+ DBusPyString_Type.tp_base = &PyUnicode_Type;
+ if (PyType_Ready(&DBusPyString_Type) < 0) return 0;
+ DBusPyString_Type.tp_print = NULL;
+
+#ifndef PY3
+ DBusPyUTF8String_Type.tp_base = &DBusPyStrBase_Type;
+ if (PyType_Ready(&DBusPyUTF8String_Type) < 0) return 0;
+ DBusPyUTF8String_Type.tp_print = NULL;
+#endif
+
+ DBusPyObjectPath_Type.tp_base = &DBusPyStrBase_Type;
+ if (PyType_Ready(&DBusPyObjectPath_Type) < 0) return 0;
+ DBusPyObjectPath_Type.tp_print = NULL;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_string_types(PyObject *this_module)
+{
+ /* PyModule_AddObject steals a ref */
+ Py_INCREF(&DBusPyObjectPath_Type);
+ Py_INCREF(&DBusPyString_Type);
+ if (PyModule_AddObject(this_module, "ObjectPath",
+ (PyObject *)&DBusPyObjectPath_Type) < 0) return 0;
+ if (PyModule_AddObject(this_module, "String",
+ (PyObject *)&DBusPyString_Type) < 0) return 0;
+
+#ifndef PY3
+ Py_INCREF(&DBusPyUTF8String_Type);
+ if (PyModule_AddObject(this_module, "UTF8String",
+ (PyObject *)&DBusPyUTF8String_Type) < 0) return 0;
+#endif
+
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/types-internal.h b/dbus_bindings/types-internal.h
new file mode 100644
index 0000000..041c33b
--- /dev/null
+++ b/dbus_bindings/types-internal.h
@@ -0,0 +1,112 @@
+/* D-Bus types: implementation internals
+ *
+ * Copyright (C) 2006-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include <Python.h>
+
+/* In Python2 >= 2.6 this aliases PyString to PyBytes. There is no PyString
+ * in Python 3, so this allows the C extension to be compilable in both Python
+ * versions.
+ */
+#include <bytesobject.h>
+
+/* In Python 2.x, we need this to define the type of PyLongObject */
+#ifndef PY3
+#include <longintrepr.h>
+#endif
+
+#ifndef DBUS_BINDINGS_TYPES_INTERNAL_H
+#define DBUS_BINDINGS_TYPES_INTERNAL_H
+
+#ifndef PY3
+extern PyTypeObject DBusPyIntBase_Type;
+DEFINE_CHECK(DBusPyIntBase)
+
+typedef struct {
+ PyIntObject base;
+ long variant_level;
+} DBusPyIntBase;
+#endif
+
+extern PyTypeObject DBusPyLongBase_Type;
+DEFINE_CHECK(DBusPyLongBase)
+
+extern PyTypeObject DBusPyFloatBase_Type;
+DEFINE_CHECK(DBusPyFloatBase)
+
+typedef struct {
+ PyFloatObject base;
+ long variant_level;
+} DBusPyFloatBase;
+
+typedef struct {
+ PyUnicodeObject unicode;
+ long variant_level;
+} DBusPyString;
+
+extern PyTypeObject DBusPyStrBase_Type;
+DEFINE_CHECK(DBusPyStrBase)
+
+#ifdef PY3
+extern PyTypeObject DBusPyBytesBase_Type;
+DEFINE_CHECK(DBusPyBytesBase)
+#endif
+
+dbus_int16_t dbus_py_int16_range_check(PyObject *);
+dbus_uint16_t dbus_py_uint16_range_check(PyObject *);
+dbus_int32_t dbus_py_int32_range_check(PyObject *);
+dbus_uint32_t dbus_py_uint32_range_check(PyObject *);
+
+#if defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG)
+# define DBUS_PYTHON_64_BIT_WORKS 1
+dbus_int64_t dbus_py_int64_range_check(PyObject *);
+dbus_uint64_t dbus_py_uint64_range_check(PyObject *);
+#else
+# undef DBUS_PYTHON_64_BIT_WORKS
+#endif /* defined(DBUS_HAVE_INT64) && defined(HAVE_LONG_LONG) */
+
+extern PyObject *dbus_py_variant_level_const;
+extern PyObject *dbus_py_signature_const;
+extern PyObject *dbus_py__dbus_object_path__const;
+
+typedef struct {
+ PyListObject super;
+ PyObject *signature;
+ long variant_level;
+} DBusPyArray;
+
+typedef struct {
+ PyDictObject super;
+ PyObject *signature;
+ long variant_level;
+} DBusPyDict;
+
+PyObject *dbus_py_variant_level_getattro(PyObject *obj, PyObject *name);
+dbus_bool_t dbus_py_variant_level_set(PyObject *obj, long variant_level);
+void dbus_py_variant_level_clear(PyObject *obj);
+long dbus_py_variant_level_get(PyObject *obj);
+
+#endif
diff --git a/dbus_bindings/unixfd.c b/dbus_bindings/unixfd.c
new file mode 100644
index 0000000..ecb32d2
--- /dev/null
+++ b/dbus_bindings/unixfd.c
@@ -0,0 +1,252 @@
+/* Simple D-Bus types: Unix FD type.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2010 Signove <http://www.signove.com>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+#include "types-internal.h"
+
+PyDoc_STRVAR(UnixFd_tp_doc,
+"dbus.UnixFd(value: int or file object[, variant_level: int])\n"
+"\n"
+"A Unix Fd.\n"
+"\n"
+"``value`` must be the integer value of a file descriptor, or an object that\n"
+"implements the fileno() method. Otherwise, `ValueError` will be\n"
+"raised.\n"
+"\n"
+"UnixFd keeps a dup() (duplicate) of the supplied file descriptor. The\n"
+"caller remains responsible for closing the original fd.\n"
+"\n"
+":py:attr:`variant_level` must be non-negative; the default is 0.\n"
+"\n"
+".. py:attribute:: variant_level\n"
+"\n"
+" Indicates how many nested Variant containers this object\n"
+" is contained in: if a message's wire format has a variant containing a\n"
+" variant containing an Unix Fd, this is represented in Python by an\n"
+" Unix Fd with variant_level==2.\n"
+);
+
+typedef struct {
+ PyObject_HEAD
+ int fd;
+} UnixFdObject;
+
+/* Return values:
+ * -2 - the long value overflows an int
+ * -1 - Python failed producing a long (or in Python 2 an int)
+ * 0 - success
+ * 1 - arg is not a long (or in Python 2 an int)
+ *
+ * Or to summarize:
+ * status < 0 - an error occurred, and a Python exception is set.
+ * status == 0 - all is okay, output argument *fd is set.
+ * status > 0 - try something else
+ */
+static int
+make_fd(PyObject *arg, int *fd)
+{
+ long fd_arg;
+
+ if (INTORLONG_CHECK(arg))
+ {
+ /* on Python 2 this accepts either int or long */
+ fd_arg = PyLong_AsLong(arg);
+ if (fd_arg == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ }
+ else {
+ return 1;
+ }
+ /* Check for int overflow. */
+ if (fd_arg < INT_MIN || fd_arg > INT_MAX) {
+ PyErr_Format(PyExc_ValueError, "int is outside fd range");
+ return -2;
+ }
+ *fd = (int)fd_arg;
+ return 0;
+}
+
+static PyObject *
+UnixFd_tp_new(PyTypeObject *cls, PyObject *args, PyObject *kwargs UNUSED)
+{
+ UnixFdObject *self = NULL;
+ PyObject *arg;
+ int status, fd, fd_original = -1;
+
+ if (!PyArg_ParseTuple(args, "O", &arg, NULL)) {
+ return NULL;
+ }
+
+ status = make_fd(arg, &fd_original);
+ if (status < 0)
+ return NULL;
+
+ if (status > 0) {
+ if (PyObject_HasAttrString(arg, "fileno")) {
+ PyObject *fd_number = PyObject_CallMethod(arg, "fileno", NULL);
+ if (!fd_number)
+ return NULL;
+ status = make_fd(fd_number, &fd_original);
+ Py_CLEAR(fd_number);
+ if (status < 0)
+ return NULL;
+ if (status > 0) {
+ PyErr_Format(PyExc_ValueError, "Argument's fileno() method "
+ "returned a non-int value");
+ return NULL;
+ }
+ /* fd_original is all good. */
+ }
+ else {
+ PyErr_Format(PyExc_ValueError, "Argument is not int and does not "
+ "implement fileno() method");
+ return NULL;
+ }
+ }
+ assert(fd_original >= 0);
+ fd = dup(fd_original);
+ if (fd < 0) {
+ PyErr_Format(PyExc_ValueError, "Invalid file descriptor");
+ return NULL;
+ }
+
+ self = (UnixFdObject *) cls->tp_alloc(cls, 0);
+ if (!self)
+ return NULL;
+
+ self->fd = fd;
+ return (PyObject *)self;
+}
+
+static void
+UnixFd_dealloc(UnixFdObject *self)
+{
+ if (self->fd >= 0) {
+ close(self->fd);
+ self->fd = -1;
+ }
+}
+
+PyDoc_STRVAR(UnixFd_take__doc__,
+"take() -> int\n"
+"\n"
+"This method returns the file descriptor owned by UnixFd object.\n"
+"Note that, once this method is called, closing the file descriptor is\n"
+"the caller's responsibility.\n"
+"\n"
+"This method may be called at most once; UnixFd 'forgets' the file\n"
+"descriptor after it is taken.\n"
+"\n"
+":Raises ValueError: if this method has already been called\n"
+);
+static PyObject *
+UnixFd_take(UnixFdObject *self)
+{
+ PyObject *fdnumber;
+
+ if (self->fd < 0) {
+ PyErr_SetString(PyExc_ValueError, "File descriptor already taken");
+ return NULL;
+ }
+
+ fdnumber = Py_BuildValue("i", self->fd);
+ self->fd = -1;
+
+ return fdnumber;
+}
+
+int
+dbus_py_unix_fd_get_fd(PyObject *self)
+{
+ return ((UnixFdObject *) self)->fd;
+}
+
+static PyMethodDef UnixFd_methods[] = {
+ {"take", (PyCFunction) UnixFd_take, METH_NOARGS, UnixFd_take__doc__ },
+ {NULL}
+};
+
+PyTypeObject DBusPyUnixFd_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "dbus.UnixFd",
+ sizeof(UnixFdObject),
+ 0,
+ (destructor) UnixFd_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ UnixFd_tp_doc, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ UnixFd_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ UnixFd_tp_new, /* tp_new */
+};
+
+dbus_bool_t
+dbus_py_init_unixfd_type(void)
+{
+ if (PyType_Ready(&DBusPyUnixFd_Type) < 0) return 0;
+
+ return 1;
+}
+
+dbus_bool_t
+dbus_py_insert_unixfd_type(PyObject *this_module)
+{
+ Py_INCREF(&DBusPyUnixFd_Type);
+ if (PyModule_AddObject(this_module, "UnixFd",
+ (PyObject *)&DBusPyUnixFd_Type) < 0) return 0;
+ return 1;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_bindings/validation.c b/dbus_bindings/validation.c
new file mode 100644
index 0000000..abec0a9
--- /dev/null
+++ b/dbus_bindings/validation.c
@@ -0,0 +1,245 @@
+/* Implementation of various validation functions for use in dbus-python.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dbus_bindings-internal.h"
+
+dbus_bool_t
+dbus_py_validate_bus_name(const char *name,
+ dbus_bool_t may_be_unique,
+ dbus_bool_t may_be_not_unique)
+{
+ dbus_bool_t dot = FALSE;
+ dbus_bool_t unique;
+ char last;
+ const char *ptr;
+
+ if (name[0] == '\0') {
+ PyErr_SetString(PyExc_ValueError, "Invalid bus name: "
+ "may not be empty");
+ return FALSE;
+ }
+ unique = (name[0] == ':');
+ if (unique && !may_be_unique) {
+ PyErr_Format(PyExc_ValueError, "Invalid well-known bus name '%s':"
+ "only unique names may start with ':'", name);
+ return FALSE;
+ }
+ if (!unique && !may_be_not_unique) {
+ PyErr_Format(PyExc_ValueError, "Invalid unique bus name '%s': "
+ "unique names must start with ':'", name);
+ return FALSE;
+ }
+ if (strlen(name) > 255) {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': "
+ "too long (> 255 characters)", name);
+ return FALSE;
+ }
+ last = '\0';
+ for (ptr = name + (unique ? 1 : 0); *ptr; ptr++) {
+ if (*ptr == '.') {
+ dot = TRUE;
+ if (last == '.') {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': "
+ "contains substring '..'", name);
+ return FALSE;
+ }
+ else if (last == '\0') {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': "
+ "must not start with '.'", name);
+ return FALSE;
+ }
+ }
+ else if (*ptr >= '0' && *ptr <= '9') {
+ if (!unique) {
+ if (last == '.') {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': "
+ "a digit may not follow '.' except in a "
+ "unique name starting with ':'", name);
+ return FALSE;
+ }
+ else if (last == '\0') {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': "
+ "must not start with a digit", name);
+ return FALSE;
+ }
+ }
+ }
+ else if ((*ptr < 'a' || *ptr > 'z') &&
+ (*ptr < 'A' || *ptr > 'Z') && *ptr != '_' && *ptr != '-') {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': "
+ "contains invalid character '%c'", name, *ptr);
+ return FALSE;
+ }
+ last = *ptr;
+ }
+ if (last == '.') {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': must "
+ "not end with '.'", name);
+ return FALSE;
+ }
+ if (!dot) {
+ PyErr_Format(PyExc_ValueError, "Invalid bus name '%s': must "
+ "contain '.'", name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+dbus_bool_t
+dbus_py_validate_member_name(const char *name)
+{
+ const char *ptr;
+
+ if (name[0] == '\0') {
+ PyErr_SetString(PyExc_ValueError, "Invalid member name: may not "
+ "be empty");
+ return FALSE;
+ }
+ if (strlen(name) > 255) {
+ PyErr_Format(PyExc_ValueError, "Invalid member name '%s': "
+ "too long (> 255 characters)", name);
+ return FALSE;
+ }
+ for (ptr = name; *ptr; ptr++) {
+ if (*ptr >= '0' && *ptr <= '9') {
+ if (ptr == name) {
+ PyErr_Format(PyExc_ValueError, "Invalid member name '%s': "
+ "must not start with a digit", name);
+ return FALSE;
+ }
+ }
+ else if ((*ptr < 'a' || *ptr > 'z') &&
+ (*ptr < 'A' || *ptr > 'Z') && *ptr != '_') {
+ PyErr_Format(PyExc_ValueError, "Invalid member name '%s': "
+ "contains invalid character '%c'", name, *ptr);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+dbus_bool_t
+dbus_py_validate_interface_name(const char *name)
+{
+ dbus_bool_t dot = FALSE;
+ char last;
+ const char *ptr;
+
+ if (name[0] == '\0') {
+ PyErr_SetString(PyExc_ValueError, "Invalid interface or error name: "
+ "may not be empty");
+ return FALSE;
+ }
+ if (strlen(name) > 255) {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or error name '%s': "
+ "too long (> 255 characters)", name);
+ return FALSE;
+ }
+ last = '\0';
+ for (ptr = name; *ptr; ptr++) {
+ if (*ptr == '.') {
+ dot = TRUE;
+ if (last == '.') {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or "
+ "error name '%s': contains substring '..'", name);
+ return FALSE;
+ }
+ else if (last == '\0') {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or error "
+ "name '%s': must not start with '.'", name);
+ return FALSE;
+ }
+ }
+ else if (*ptr >= '0' && *ptr <= '9') {
+ if (last == '.') {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or error "
+ "name '%s': a digit may not follow '.'", name);
+ return FALSE;
+ }
+ else if (last == '\0') {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or error "
+ "name '%s': must not start with a digit", name);
+ return FALSE;
+ }
+ }
+ else if ((*ptr < 'a' || *ptr > 'z') &&
+ (*ptr < 'A' || *ptr > 'Z') && *ptr != '_') {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or error "
+ "name '%s': contains invalid character '%c'",
+ name, *ptr);
+ return FALSE;
+ }
+ last = *ptr;
+ }
+ if (last == '.') {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or error name "
+ "'%s': must not end with '.'", name);
+ return FALSE;
+ }
+ if (!dot) {
+ PyErr_Format(PyExc_ValueError, "Invalid interface or error name "
+ "'%s': must contain '.'", name);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+dbus_bool_t
+dbus_py_validate_object_path(const char *path)
+{
+ const char *ptr;
+
+ if (path[0] != '/') {
+ PyErr_Format(PyExc_ValueError, "Invalid object path '%s': does not "
+ "start with '/'", path);
+ return FALSE;
+ }
+ if (path[1] == '\0') return TRUE;
+ for (ptr = path + 1; *ptr; ptr++) {
+ if (*ptr == '/') {
+ if (ptr[-1] == '/') {
+ PyErr_Format(PyExc_ValueError, "Invalid object path '%s': "
+ "contains substring '//'", path);
+ return FALSE;
+ }
+ }
+ else if ((*ptr < 'a' || *ptr > 'z') &&
+ (*ptr < 'A' || *ptr > 'Z') &&
+ (*ptr < '0' || *ptr > '9') && *ptr != '_') {
+ PyErr_Format(PyExc_ValueError, "Invalid object path '%s': "
+ "contains invalid character '%c'", path, *ptr);
+ return FALSE;
+ }
+ }
+ if (ptr[-1] == '/') {
+ PyErr_Format(PyExc_ValueError, "Invalid object path '%s': ends "
+ "with '/' and is not just '/'", path);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/dbus_glib_bindings/module.c b/dbus_glib_bindings/module.c
new file mode 100644
index 0000000..ac05bca
--- /dev/null
+++ b/dbus_glib_bindings/module.c
@@ -0,0 +1,215 @@
+/* Glue code to attach the GObject main loop to D-Bus from within Python.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <Python.h>
+#include <dbus/dbus-python.h>
+#include <dbus-gmain/dbus-gmain.h>
+
+#ifdef PY3
+PyMODINIT_FUNC PyInit__dbus_glib_bindings(void);
+#else
+PyMODINIT_FUNC init_dbus_glib_bindings(void);
+#endif
+
+#if defined(__GNUC__)
+# if __GNUC__ >= 3
+# define UNUSED __attribute__((__unused__))
+# else
+# define UNUSED /*nothing*/
+# endif
+#else
+# define UNUSED /*nothing*/
+#endif
+
+static dbus_bool_t
+dbus_py_glib_set_up_conn(DBusConnection *conn, void *data)
+{
+ GMainContext *ctx = (GMainContext *)data;
+ Py_BEGIN_ALLOW_THREADS
+ _dbus_py_glib_set_up_connection(conn, ctx);
+ Py_END_ALLOW_THREADS
+ return 1;
+}
+
+static dbus_bool_t
+dbus_py_glib_set_up_srv(DBusServer *srv, void *data)
+{
+ GMainContext *ctx = (GMainContext *)data;
+ Py_BEGIN_ALLOW_THREADS
+ _dbus_py_glib_set_up_server(srv, ctx);
+ Py_END_ALLOW_THREADS
+ return 1;
+}
+
+static void
+dbus_py_glib_unref_mainctx(void *data)
+{
+ if (data)
+ g_main_context_unref((GMainContext *)data);
+}
+
+/* Generate a dbus-python NativeMainLoop wrapper from a GLib main loop */
+static PyObject *
+dbus_glib_native_mainloop(GMainContext *ctx)
+{
+ PyObject *loop = DBusPyNativeMainLoop_New4(dbus_py_glib_set_up_conn,
+ dbus_py_glib_set_up_srv,
+ dbus_py_glib_unref_mainctx,
+ ctx ? g_main_context_ref(ctx)
+ : NULL);
+ if (!loop && ctx) {
+ g_main_context_unref(ctx);
+ }
+ return loop;
+}
+
+PyDoc_STRVAR(module_doc, "");
+
+PyDoc_STRVAR(DBusGMainLoop__doc__,
+"DBusGMainLoop([set_as_default=False]) -> NativeMainLoop\n"
+"\n"
+"Return a NativeMainLoop object which can be used to\n"
+"represent the default GLib main context in dbus-python.\n"
+"\n"
+"If the keyword argument set_as_default is given and is true, set the new\n"
+"main loop as the default for all new Connection or Bus instances.\n"
+"\n"
+"Non-default main contexts are not currently supported.\n");
+static PyObject *
+DBusGMainLoop (PyObject *always_null UNUSED, PyObject *args, PyObject *kwargs)
+{
+ PyObject *mainloop, *function, *result;
+ int set_as_default = 0;
+ static char *argnames[] = {"set_as_default", NULL};
+
+ if (PyTuple_Size(args) != 0) {
+ PyErr_SetString(PyExc_TypeError, "DBusGMainLoop() takes no "
+ "positional arguments");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", argnames,
+ &set_as_default)) {
+ return NULL;
+ }
+
+ mainloop = dbus_glib_native_mainloop(NULL);
+ if (mainloop && set_as_default) {
+ if (!_dbus_bindings_module) {
+ PyErr_SetString(PyExc_ImportError, "_dbus_bindings not imported");
+ Py_CLEAR(mainloop);
+ return NULL;
+ }
+ function = PyObject_GetAttrString(_dbus_bindings_module,
+ "set_default_main_loop");
+ if (!function) {
+ Py_CLEAR(mainloop);
+ return NULL;
+ }
+ result = PyObject_CallFunctionObjArgs(function, mainloop, NULL);
+ Py_CLEAR(function);
+ if (!result) {
+ Py_CLEAR(mainloop);
+ return NULL;
+ }
+ Py_CLEAR(result);
+ }
+ return mainloop;
+}
+
+PyDoc_STRVAR(setup_with_g_main__doc__,
+"setup_with_g_main(conn: dbus.Connection)\n"
+"\n"
+"Deprecated.\n");
+static PyObject *
+setup_with_g_main (PyObject *always_null UNUSED, PyObject *args)
+{
+ DBusConnection *dbc;
+ PyObject *conn;
+ if (!PyArg_ParseTuple(args, "O:setup_with_g_main", &conn)) return NULL;
+
+ dbc = DBusPyConnection_BorrowDBusConnection (conn);
+ if (!dbc) return NULL;
+ dbus_py_glib_set_up_conn(dbc, NULL);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(gthreads_init__doc__,
+"gthreads_init()");
+static PyObject *
+gthreads_init (PyObject *always_null UNUSED, PyObject *no_args UNUSED)
+{
+ dbus_threads_init_default();
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef module_functions[] = {
+ {"setup_with_g_main", setup_with_g_main, METH_VARARGS,
+ setup_with_g_main__doc__},
+ {"gthreads_init", gthreads_init, METH_NOARGS, gthreads_init__doc__},
+ {"DBusGMainLoop", (PyCFunction)DBusGMainLoop,
+ METH_VARARGS|METH_KEYWORDS, DBusGMainLoop__doc__},
+ {NULL, NULL, 0, NULL}
+};
+
+#ifdef PY3
+PyMODINIT_FUNC
+PyInit__dbus_glib_bindings(void)
+{
+ PyObject *this_module;
+
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "_dbus_glib_bindings", /* m_name */
+ module_doc, /* m_doc */
+ -1, /* m_size */
+ module_functions, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+ };
+
+ if (import_dbus_bindings("_dbus_glib_bindings") < 0)
+ return NULL;
+
+ if (!(this_module = PyModule_Create(&moduledef))) {
+ return NULL;
+ }
+ return this_module;
+}
+#else
+PyMODINIT_FUNC
+init_dbus_glib_bindings(void)
+{
+ PyObject *this_module;
+
+ if (import_dbus_bindings("_dbus_glib_bindings") < 0) return;
+ this_module = Py_InitModule3 ("_dbus_glib_bindings", module_functions,
+ module_doc);
+ if (!this_module) return;
+}
+#endif
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/doc/API_CHANGES.txt b/doc/API_CHANGES.txt
new file mode 100644
index 0000000..ace4f5b
--- /dev/null
+++ b/doc/API_CHANGES.txt
@@ -0,0 +1,124 @@
+===============================
+API changes in dbus-python 0.80
+===============================
+
+:Author: Simon McVittie
+:Contact: simon.mcvittie@collabora.co.uk
+:Organization: `Collabora Ltd`_
+:Date: 2006-11-23
+
+.. _Collabora Ltd: http://www.collabora.co.uk/
+
+Type changes
+============
+
+* The Byte constructor accepts either single-byte strings, or integers in
+ the range 0 to 255.
+
+* There is no Variant type any more. Instead, the ``variant_level``
+ attribute on D-Bus types gives the number of variant wrappers in
+ which it is contained; this is to remove ambiguity. For instance, calling
+ this method::
+
+ @dbus.service.method('com.example', in_signature='v', out_signature='')
+ def Print(self, variant):
+ print repr(variant)
+
+ yields the following results::
+
+ # on the wire: Variant containing Int32
+ Int32(0, variant_level=1)
+ # on the wire: Variant containing Variant containing Int32
+ Int32(0, variant_level=2)
+
+ Once an object of a D-Bus type has been constructed, its
+ ``variant_level`` cannot be altered.
+
+* The D-Bus integer types (dbus.Int32, etc.) are properly range-checked.
+
+* The Array constructor takes arguments (iterable[, signature])
+ rather than (iterable[, type][, signature]); ditto for Dict.
+
+Calling conventions
+===================
+
+* In method parameters, method returns from proxy methods, etc.,
+ integers arrive as instances of dbus.Int32 etc., bytes arrive as
+ Byte, and so on, rather than everything being converted to an
+ appropriate built-in Python type. This means you can tell exactly
+ what arguments went over the bus, and their types.
+
+* Proxy methods with multiple return values return a tuple rather than
+ a list.
+
+* Calling a proxy method with reply ignored, or with async
+ handlers, returns None
+
+``dbus_bindings``
+=================
+
+* ConnectionError no longer exists (it was never raised)
+
+* ``dbus_bindings`` is now called ``_dbus_bindings``, and is considerably
+ different internally:
+
+ * connections are private at the libdbus level: shared connections
+ are only shared among Python code
+
+ * The MessageIter stuff is now done in C: there's a much simpler
+ Python API, ``Message.append(...)`` where positional arguments are
+ the things to be appended, and the keyword argument ``signature``
+ controls how objects are interpreted
+
+ * The signature-guessing algorithm used if there is no proper
+ signature is exposed as a static method,
+ ``Message.guess_signature(*args)``
+
+ * Bus is a subclass of Connection rather than being a wrapper object
+ which has-a Connection
+
+ * The timeouts in _send_with_reply and in _send_with_reply_and_block
+ are in (possibly fractional) seconds, as is conventional in Python
+
+ * The specialized Message subclasses have names ending with Message
+
+* There is a small amount of compatibility glue in a new
+ ``dbus_bindings`` module (also ``dbus.dbus_bindings``)
+ which should enable most current code to work - this is deprecated,
+ and will disappear in a future version of dbus-python
+
+Main loops
+==========
+
+Main loop handling is different - instead of the
+``use_default_mainloop`` keyword argument to Bus and subclasses, there's now
+``mainloop`` which takes an instance of dbus.mainloop.NativeMainLoop.
+
+Alternatively, you can set a default main loop by calling
+``dbus.set_default_main_loop()`` and passing it a NativeMainLoop, or
+by passing ``set_as_default=True`` to the factory function
+from which you obtained the native main loop.
+
+The plan is that in a future version of dbus-python there will be an
+abstract base class dbus.mainloop.MainLoop (or something); when it's added,
+instances of its subclasses will be accepted wherever a NativeMainLoop
+instance is now. This will let you wrap main loops using a Python API.
+This will be used to implement SimpleMainLoop (a pure-Python main loop
+which can only do D-Bus) and a Twisted main-loop wrapper.
+
+The only working mainloop implementation is (still) GLib; you can get
+a NativeMainLoop instance by::
+
+ from dbus.mainloop.glib import DBusGMainLoop
+ my_native_main_loop = DBusGMainLoop(set_as_default=True)
+
+The above is how the highly magical ``dbus.glib`` module is now implemented.
+At some point ``dbus.glib`` will be deprecated, since it's non-obvious,
+and pychecker will usually complain if you use it correctly!
+
+At the moment the GLib main loop always uses the default main context;
+python-gobject will probably need to add some extra API before we can
+allow other main-contexts to be used.
+
+..
+ vim:set sw=2 sts=2 et ft=rst tw=72:
diff --git a/doc/HACKING.txt b/doc/HACKING.txt
new file mode 100644
index 0000000..4d9e887
--- /dev/null
+++ b/doc/HACKING.txt
@@ -0,0 +1,74 @@
+===============
+Developer notes
+===============
+
+:Author: Simon McVittie, `Collabora`_
+:Date: 2007-01-24
+
+.. _Collabora: http://www.collabora.co.uk/
+
+Upstream development
+====================
+
+dbus-python is developed at freedesktop.org using ``git``.
+See UsingGit_ for some details.
+
+.. _UsingGit: http://www.freedesktop.org/wiki/UsingGit
+
+Anonymous access
+ ``git clone https://gitlab.freedesktop.org/dbus/dbus-python.git``
+Committer access
+ ``git clone git@gitlab.freedesktop.org:dbus/dbus-python.git``
+
+dbus-gmain is maintained via `git subtree`. To update, assuming you have
+a checkout of the dbus-gmain branch of the dbus-glib repository in
+../dbus-gmain::
+
+ git subtree pull -P dbus-gmain ../dbus-gmain HEAD
+
+Modules
+=======
+
+``dbus``, ``dbus.service`` and ``dbus.mainloop`` are core public API.
+
+``dbus.lowlevel`` provides a lower-level public API for advanced use.
+
+``dbus.mainloop.glib`` is the public API for the GLib main loop integration.
+
+``dbus.types`` and ``dbus.exceptions`` are mainly for backwards
+compatibility - use ``dbus`` instead in new code. Ditto ``dbus.glib``.
+
+``dbus._dbus``, ``dbus.introspect_parser``, ``dbus.proxies`` are internal
+implementation details.
+
+``_dbus_bindings`` is the real implementation of the Python/libdbus
+integration, while ``_dbus_bindings`` is the real implementation of
+Python/libdbus-glib integration. Neither is public API, although some
+of the classes and functions are exposed as public API in other modules.
+
+Threading/locking model
+=======================
+
+All Python functions must be called with the GIL (obviously).
+
+Before calling into any D-Bus function that can block, release the GIL;
+as well as the usual "be nice to other threads", D-Bus does its own
+locking and we don't want to deadlock with it. Most Connection methods
+can block.
+
+Other notes
+===========
+
+Code needs to be Python 2.5 compatible - use Py_ssize_t where appropriate.
+See http://www.python.org/dev/peps/pep-0353/ for details.
+
+Indentation and other holy wars
+===============================
+
+Python code is meant to follow PEP8, and has 4-space indentation, no hard tabs.
+
+C code is meant to follow what PEP7 refers to as "Python 3000" style - 4-space
+indentation, no hard tabs, otherwise consistent with historical Python 2.x
+code.
+
+Docstrings etc. are reStructuredText.
diff --git a/doc/PY3PORT.txt b/doc/PY3PORT.txt
new file mode 100644
index 0000000..e159849
--- /dev/null
+++ b/doc/PY3PORT.txt
@@ -0,0 +1,222 @@
+===================
+Porting to Python 3
+===================
+
+This is an experimental port to Python 3.x where x >= 2. There are lots of
+great sources for porting C extensions to Python 3, including:
+
+ * http://python3porting.com/toc.html
+ * http://docs.python.org/howto/cporting.html
+ * http://docs.python.org/py3k/c-api/index.html
+
+I also consulted an early take on this port by John Palmieri and David Malcolm
+in the context of Fedora:
+
+ * https://bugs.freedesktop.org/show_bug.cgi?id=26420
+
+although I have made some different choices. The patches in that tracker
+issue also don't cover porting the Python bits (e.g. the test suite), nor the
+pygtk -> pygi porting, both which I've also attempted to do in this branch.
+
+This document outlines my notes and strategies for doing this port. Please
+feel free to contact me with any bugs, issues, disagreements, suggestions,
+kudos, and curses.
+
+Barry Warsaw
+barry@python.org
+2011-11-11
+
+
+User visible changes
+====================
+
+You've got some dbus-python code that works great in Python 2. This branch
+should generally allow your existing Python 2 code to continue to work
+unchanged. There are a few changes you'll notice in Python 2 though:
+
+ - The minimum supported Python 2 version is 2.7.
+ - All object reprs are unicodes. This change was made because it greatly
+ simplifies the implementation and cross-compatibility with Python 3.
+ - Some exception strings have changed.
+ - `MethodCallMessage` and `SignalMessage` objects have better reprs now.
+
+What do you need to do to port that to Python 3? Here are the user visible
+changes you should be aware of, relative to Python 2. Python 3.2 is the
+minimal required version:
+
+ - `ByteArray` objects must be initialized with bytes objects, not unicodes.
+ Use `b''` literals in the constructor. This also works in Python 2, where
+ bytes objects are aliases for 8-bit strings.
+ - `Byte` objects must be initialized with either a length-1 bytes object
+ (again, use `b''` literals to be compatible with either Python 2 or 3)
+ or an integer.
+ - byte signatures (i.e. `y` type codes) must be passed either a length-1
+ bytes object or an integer. unicodes (str in Python 3) are not allowed.
+ - `ByteArray` is now a subclass of `bytes`, where in Python 2 it is a
+ subclass of `str`.
+ - `dbus.UTF8String` is gone, use `dbus.String`. Also `utf8_string` arguments
+ are no longer allowed.
+ - All longs are now ints, since Python 3 has only a single int type. This
+ also means that the class hierarchy for the dbus numeric types has changed
+ (all derive from int in Python 3).
+
+
+Bytes vs. Strings
+=================
+
+All strings in dbus are defined as UTF-8:
+
+http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures
+
+However, the dbus C API accepts `char*` which must be UTF-8 strings NUL
+terminated and no other NUL bytes.
+
+This page describes the mapping between Python types and dbus types:
+
+ http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html#basic-types
+
+Notice that it maps dbus `string` (`'s'`) to `dbus.String` (unicode) or
+`dbus.UTF8String` (str). Also notice that there is no direct dbus equivalent
+of Python's bytes type (although dbus does have byte arrays), so I am mapping
+dbus strings to unicodes in all cases, and getting rid of `dbus.UTF8String` in
+Python 3. I've also added a `dbus._BytesBase` type which is unused in Python
+2, but which forms the base class for `dbus.ByteArray` in Python 3. This is
+an implementation detail and not part of the public API.
+
+In Python 3, object paths (`'o'` or `dbus.ObjectPath`), signatures (`'g'` or
+`dbus.Signature`), bus names, interfaces, and methods are all strings. A
+previous aborted effort was made to use bytes for these, which at first blush
+may makes some sense, but on deeper consideration does not. This approach
+also tended to impose too many changes on user code, and caused lots of
+difficult to track down problems.
+
+In Python 3, all such objects are subclasses of `str` (i.e. `unicode`).
+
+(As an example, dbus-python's callback dispatching pretty much assumes all
+these things are strings. When they are bytes, the fact that `'foo' != b'foo'`
+causes dispatch matching to fail in difficult to debug ways. Even bus names
+are not immune, since they do things like `bus_name[:1] == ':'` which fails in
+multiple ways when `bus_name` is a bytes. For sanity purposes, these are all
+unicode strings now, and we just eat the complexity at the C level.)
+
+I am using `#include <bytesobject.h>`, which exposes the PyBytes API to Python
+2.6 and 2.7, and I have converted all internal PyString calls to PyBytes
+calls. Where this is inappropriate, we'll use PyUnicode calls explicitly.
+E.g. all repr() implementations now return unicodes. Most of these changes
+shouldn't be noticed, even in existing Python 2 code.
+
+Generally, I've left the descriptions and docstrings saying "str" instead of
+"unicode" since there's no distinction in Python 3.
+
+APIs which previously returned PyStrings will usually return PyUnicodes, not
+PyBytes.
+
+
+Ints vs. Longs
+==============
+
+Python 3 only has PyLong types; PyInts are gone. For that reason, I've
+switched all PyInt calls to use PyLong in both Python 2 and Python 3. Python
+3.0 had a nice `<intobject.h>` header that aliased PyInt to PyLong, but that's
+gone as of Python 3.1, and the minimal required Python 3 version is 3.2.
+
+In the above page mapping basic types, you'll notice that the Python int type
+is mapped to 32-bit signed integers ('i') and the Python long type is mapped
+to 64-bit signed integers ('x'). Python 3 doesn't have this distinction, so
+ints map to 'i' even though ints can be larger in Python 3. Use the
+dbus-specific integer types if you must have more exact mappings.
+
+APIs which accepted ints in Python 2 will still do so, but they'll also now
+accept longs. These APIs obviously only accept longs in Python 3.
+
+Long literals in Python code are an interesting thing to have to port. Don't
+use them if you want your code to work in both Python versions.
+
+`dbus._IntBase` is removed in Python 3, you only have `dbus._LongBase`, which
+inherits from a Python 3 int (i.e. a PyLong). Again, this is an
+implementation detail that users should never care about.
+
+
+Macros
+======
+
+In types-internal.h, I define `PY3K` when `PY_MAJOR_VERSION` >= 3, so you'll
+see ifdefs on the former symbol within the C code.
+
+Python 3 really could use a PY_REFCNT() wrapper for ob_refcnt access.
+
+
+PyCapsule vs. PyCObject
+=======================
+
+`_dbus_bindings._C_API` is an attribute exposed to Python in the module. In
+Python 2, this is a PyCObject, but these do not exist in Python >= 3.2, so it
+is replaced with a PyCapsules for Python 3. However, since PyCapsules were
+only introduced in Python 2.7, and I want to support Python 2.6, PyCObjects
+are still used when this module is compiled for Python 2.
+
+
+Python level compatibility
+==========================
+
+`from dbus import _is_py3` gives you a flag to check if you must do something
+different in Python 3. In general I use this flag to support both versions in
+one set of sources, which seems better than trying to use 2to3. It's not part
+of the dbus-python public API, so you must not use it in third-party projects.
+
+
+Miscellaneous
+=============
+
+The PyDoc_STRVAR() documentation is probably out of date. Once the API
+choices have been green-lighted upstream, I'll make a pass through the code to
+update them. It might be tricky based on any differences between Python 2 and
+Python 3.
+
+There were a few places where I noticed what might be considered bugs,
+unchecked exception conditions, or possible reference count leaks. In these
+cases, I've just fixed what I can and hopefully haven't made the situation
+worse.
+
+`dbus_py_variant_level_get()` did not check possible error conditions, nor did
+their callers. When `dbus_py_variant_level_get()` encounters an error, it now
+returns -1, and callers check this.
+
+As much as possible, I've refrained from general code cleanups (e.g. 80
+columns), unless it just bugged me too much or I touched the code for reasons
+related to the port. I've also tried to stick to existing C code style,
+e.g. through the use of pervasive `Py_CLEAR()` calls, comparison against NULL
+usually with `!foo`, and such. As Bart Simpson might write on his classroom
+blackboard::
+
+ This is not a rewrite
+ This is not a rewrite
+ This is not a rewrite
+ This is not a rewrite
+ ...
+
+and so on. Well, mostly ;).
+
+I think I fixed a reference leak in `DBusPyServer_set_auth_mechanisms()`.
+`PySequence_Fast()` returns a new reference, which wasn't getting decref'd in
+any return path.
+
+ - Instantiation of metaclasses uses different, incompatible syntax in Python
+ 2 and 3. You have to use direct calling of the metaclass to work across
+ versions, i.e. `Interface = InterfaceType('Interface', (object,), {})`
+ - `iteritems()` and friends are gone. I dropped the "iter" prefixes.
+ - `xrange() is gone. I changed them to use `range()`.
+ - `isSequenceType()` is gone in Python 3, so I use a different idiom there.
+ - `__next__()` vs. `next()`
+ - `PyUnicode_FromFormat()` `%V` flag is a clever hack!
+ - `PyArg_Parse()`: No 'y' code in Python 2; in Python 3, no equivalent of 'z'
+ for bytes objects.
+
+
+Open issues
+===========
+
+Here are a few things that still need to be done, or for which there may be
+open questions::
+
+ - Update all C extension docstrings for accuracy.
diff --git a/doc/_static/.gitignore b/doc/_static/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/_static/.gitignore
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..f094a26
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/stable/config
+
+# -- Path setup --------------------------------------------------------------
+
+import os
+import sys
+
+sys.path.insert(0,
+ os.path.join(
+ os.environ.get('abs_top_builddir', os.path.abspath('..')),
+ '.libs',
+ ),
+)
+sys.path.insert(0, os.environ.get('abs_top_srcdir', os.path.abspath('..')))
+
+import _dbus_bindings
+
+# -- Project information -----------------------------------------------------
+
+project = u'dbus-python'
+copyright = u'2003-2018, D-Bus contributors'
+author = u'D-Bus contributors'
+
+# The short X.Y version
+version = _dbus_bindings.__version__
+# The full version, including alpha/beta/rc tags
+release = version
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.coverage',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = ['.rst', '.txt']
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path .
+exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself. Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'dbus-python'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_documents = [
+ (master_doc, 'dbus-python.tex', u'dbus-python Documentation',
+ u'D-Bus contributors', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (master_doc, 'dbus-python', u'dbus-python Documentation',
+ [author], 3)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (master_doc, 'dbus-python', u'dbus-python Documentation',
+ author, 'dbus-python',
+ 'Python bindings for the reference implementation of D-Bus.',
+ 'Miscellaneous'),
+]
+
+
+# -- Extension configuration -------------------------------------------------
diff --git a/doc/dbus.bus.rst b/doc/dbus.bus.rst
new file mode 100644
index 0000000..1f78f3e
--- /dev/null
+++ b/doc/dbus.bus.rst
@@ -0,0 +1,7 @@
+dbus.bus module
+===============
+
+.. automodule:: dbus.bus
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.connection.rst b/doc/dbus.connection.rst
new file mode 100644
index 0000000..7b78212
--- /dev/null
+++ b/doc/dbus.connection.rst
@@ -0,0 +1,7 @@
+dbus.connection module
+======================
+
+.. automodule:: dbus.connection
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.decorators.rst b/doc/dbus.decorators.rst
new file mode 100644
index 0000000..3f6625c
--- /dev/null
+++ b/doc/dbus.decorators.rst
@@ -0,0 +1,7 @@
+dbus.decorators module
+----------------------
+
+.. automodule:: dbus.decorators
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.exceptions.rst b/doc/dbus.exceptions.rst
new file mode 100644
index 0000000..fea743c
--- /dev/null
+++ b/doc/dbus.exceptions.rst
@@ -0,0 +1,7 @@
+dbus.exceptions module
+----------------------
+
+.. automodule:: dbus.exceptions
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.gi_service.rst b/doc/dbus.gi_service.rst
new file mode 100644
index 0000000..945d5a0
--- /dev/null
+++ b/doc/dbus.gi_service.rst
@@ -0,0 +1,7 @@
+dbus.gi\_service module
+-----------------------
+
+.. automodule:: dbus.gi_service
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.glib.rst b/doc/dbus.glib.rst
new file mode 100644
index 0000000..20958be
--- /dev/null
+++ b/doc/dbus.glib.rst
@@ -0,0 +1,7 @@
+dbus.glib module
+----------------
+
+.. automodule:: dbus.glib
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.gobject_service.rst b/doc/dbus.gobject_service.rst
new file mode 100644
index 0000000..b1ff1be
--- /dev/null
+++ b/doc/dbus.gobject_service.rst
@@ -0,0 +1,36 @@
+.. This is not done via automodule because it cannot be imported in
+.. Python 3.
+
+dbus.gobject\_service module
+----------------------------
+
+.. py:module:: gobject_service
+
+This module is only available when using Python 2, and is deprecated.
+
+.. py:class:: ExportedGObjectType(cls, name, bases, dct)
+
+ A metaclass which inherits from both GObjectMeta and
+ `dbus.service.InterfaceType`. Used as the metaclass for
+ `ExportedGObject`.
+
+.. py:class:: ExportedGObject(self, conn=None, object_path=None, **kwargs)
+
+ A GObject which is exported on the D-Bus.
+
+ Because GObject and `dbus.service.Object` both have custom metaclasses,
+ the naive approach using simple multiple inheritance won't work. This
+ class has `ExportedGObjectType` as its metaclass, which is sufficient
+ to make it work correctly.
+
+ :param dbus.connection.Connection conn:
+ The D-Bus connection or bus
+ :param str object_path:
+ The object path at which to register this object.
+ :keyword dbus.service.BusName bus_name:
+ A bus name to be held on behalf of this object, or None.
+ :keyword dict gobject_properties:
+ GObject properties to be set on the constructed object.
+
+ Any unrecognised keyword arguments will also be interpreted
+ as GObject properties.
diff --git a/doc/dbus.lowlevel.rst b/doc/dbus.lowlevel.rst
new file mode 100644
index 0000000..74e5f47
--- /dev/null
+++ b/doc/dbus.lowlevel.rst
@@ -0,0 +1,7 @@
+dbus.lowlevel module
+--------------------
+
+.. automodule:: dbus.lowlevel
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.mainloop.rst b/doc/dbus.mainloop.rst
new file mode 100644
index 0000000..14bbf14
--- /dev/null
+++ b/doc/dbus.mainloop.rst
@@ -0,0 +1,18 @@
+dbus.mainloop package
+=====================
+
+Module contents
+---------------
+
+.. automodule:: dbus.mainloop
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+dbus.mainloop.glib module
+-------------------------
+
+.. automodule:: dbus.mainloop.glib
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.proxies.rst b/doc/dbus.proxies.rst
new file mode 100644
index 0000000..8e4e8e3
--- /dev/null
+++ b/doc/dbus.proxies.rst
@@ -0,0 +1,7 @@
+dbus.proxies module
+-------------------
+
+.. automodule:: dbus.proxies
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.rst b/doc/dbus.rst
new file mode 100644
index 0000000..2b2ab96
--- /dev/null
+++ b/doc/dbus.rst
@@ -0,0 +1,35 @@
+dbus package API reference
+==========================
+
+Submodules
+----------
+
+.. toctree::
+
+ dbus.bus
+ dbus.connection
+ dbus.decorators
+ dbus.exceptions
+ dbus.gi_service
+ dbus.lowlevel
+ dbus.mainloop
+ dbus.proxies
+ dbus.server
+ dbus.service
+ dbus.types
+
+Deprecated submodules
+---------------------
+
+.. toctree::
+
+ dbus.glib
+ dbus.gobject_service
+
+Module contents
+---------------
+
+.. automodule:: dbus
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.server.rst b/doc/dbus.server.rst
new file mode 100644
index 0000000..c0d94c6
--- /dev/null
+++ b/doc/dbus.server.rst
@@ -0,0 +1,7 @@
+dbus.server module
+------------------
+
+.. automodule:: dbus.server
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.service.rst b/doc/dbus.service.rst
new file mode 100644
index 0000000..61958d6
--- /dev/null
+++ b/doc/dbus.service.rst
@@ -0,0 +1,7 @@
+dbus.service module
+-------------------
+
+.. automodule:: dbus.service
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/dbus.types.rst b/doc/dbus.types.rst
new file mode 100644
index 0000000..c02c788
--- /dev/null
+++ b/doc/dbus.types.rst
@@ -0,0 +1,7 @@
+dbus.types module
+-----------------
+
+.. automodule:: dbus.types
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..ca754b5
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,53 @@
+=======================================
+dbus-python_: Python bindings for D-Bus
+=======================================
+
+.. _dbus-python: http://www.freedesktop.org/wiki/Software/DBusBindings#python
+
+dbus-python is a Python binding for ``dbus``, the reference implementation
+of the D-Bus protocol.
+
+Problems and alternatives
+=========================
+
+dbus-python might not be the best D-Bus binding for you to use. dbus-python
+does not follow the principle of "In the face of ambiguity, refuse the
+temptation to guess", and can't be changed to not do so without seriously
+breaking compatibility.
+
+In addition, it uses libdbus (which has known problems with multi-threaded
+use) and attempts to be main-loop-agnostic (which means you have to select
+a suitable main loop for your application).
+
+Alternative ways to get your Python code onto D-Bus include:
+
+* GDBus, part of the GIO module of `GLib`_, via GObject-Introspection and
+ `PyGI`_ (uses the GLib main loop and object model)
+
+* QtDBus, part of `Qt`_, via `PyQt`_ (uses the Qt main loop and object model)
+
+.. _GLib: http://developer.gnome.org/glib/
+.. _PyGI: https://live.gnome.org/PyGObject
+.. _Qt: https://qt.nokia.com/
+.. _PyQT: http://www.riverbankcomputing.co.uk/software/pyqt/intro
+
+Documentation
+=============
+
+.. toctree::
+ :maxdepth: 2
+ :caption: Contents:
+
+ tutorial
+ dbus
+ PY3PORT
+ news
+ HACKING
+ API_CHANGES
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/doc/news.rst b/doc/news.rst
new file mode 100644
index 0000000..249c431
--- /dev/null
+++ b/doc/news.rst
@@ -0,0 +1,5 @@
+===============
+Release history
+===============
+
+.. include:: ../NEWS
diff --git a/doc/redirects b/doc/redirects
new file mode 100644
index 0000000..659373d
--- /dev/null
+++ b/doc/redirects
@@ -0,0 +1,138 @@
+doc/API_CHANGES.html ../API_CHANGES.html
+doc/API_CHANGES.txt "See ../API_CHANGES.html"
+doc/HACKING.html ../HACKING.html
+doc/HACKING.txt "See ../HACKING.html"
+doc/PY3PORT.html ../PY3PORT.html
+doc/PY3PORT.txt "See ../PY3PORT.html"
+doc/tutorial.html ../tutorial.html
+doc/tutorial.txt "See ../tutorial.html"
+api/change-index.html ../news.html
+api/class-tree.html ../index.html
+api/dbus.Array-class.html ../dbus.html
+api/dbus.Boolean-class.html ../dbus.html
+api/dbus.bus.BusConnection-class.html ../dbus.bus.html
+api/dbus.bus-module.html ../dbus.bus.html
+api/dbus.bus.NameOwnerWatch-class.html ../dbus.bus.html
+api/dbus.bus-pysrc.html ../dbus.bus.html
+api/dbus.ByteArray-class.html ../dbus.html
+api/dbus.Byte-class.html ../dbus.html
+api/dbus._compat-module.html ../dbus.html
+api/dbus._compat-pysrc.html ../dbus.html
+api/dbus.connection.Connection-class.html ../dbus.connection.html
+api/dbus.connection-module.html ../dbus.connection.html
+api/dbus.connection-pysrc.html ../dbus.connection.html
+api/dbus.connection.SignalMatch-class.html ../dbus.connection.html
+api/dbus.dbus_bindings.ConnectionError-class.html ../dbus.html
+api/dbus.dbus_bindings-module.html ../dbus.html
+api/dbus.dbus_bindings-pysrc.html ../dbus.html
+api/dbus._dbus.Bus-class.html ../dbus.html
+api/dbus._dbus._DBusBindingsEmulation-class.html ../dbus.html
+api/dbus._dbus-module.html ../dbus.html
+api/dbus._dbus-pysrc.html ../dbus.html
+api/dbus._dbus.SessionBus-class.html ../dbus.html
+api/dbus._dbus.StarterBus-class.html ../dbus.html
+api/dbus._dbus.SystemBus-class.html ../dbus.html
+api/dbus.decorators-module.html ../dbus.decorators.html
+api/dbus.decorators-pysrc.html ../dbus.decorators.html
+api/dbus.Dictionary-class.html ../dbus.html
+api/dbus.Double-class.html ../dbus.html
+api/dbus.exceptions.DBusException-class.html ../dbus.html
+api/dbus.exceptions.IntrospectionParserException-class.html ../dbus.html
+api/dbus.exceptions.MissingErrorHandlerException-class.html ../dbus.html
+api/dbus.exceptions.MissingReplyHandlerException-class.html ../dbus.html
+api/dbus.exceptions-module.html ../dbus.html
+api/dbus.exceptions.NameExistsException-class.html ../dbus.html
+api/dbus.exceptions-pysrc.html ../dbus.html
+api/dbus.exceptions.UnknownMethodException-class.html ../dbus.html
+api/dbus.exceptions.ValidationException-class.html ../dbus.html
+api/dbus._expat_introspect_parser-module.html ../dbus.html
+api/dbus._expat_introspect_parser._Parser-class.html ../dbus.html
+api/dbus._expat_introspect_parser-pysrc.html ../dbus.html
+api/dbus.gi_service.ExportedGObject-class.html ../dbus.gi_service.html
+api/dbus.gi_service.ExportedGObjectType-class.html ../dbus.gi_service.html
+api/dbus.gi_service-module.html ../dbus.gi_service.html
+api/dbus.gi_service-pysrc.html ../dbus.gi_service.html
+api/dbus.glib-module.html ../dbus.glib.html
+api/dbus.glib-pysrc.html ../dbus.glib.html
+api/dbus.gobject_service.ExportedGObject-class.html ../dbus.gobject_service.html
+api/dbus.gobject_service.ExportedGObjectType-class.html ../dbus.gobject_service.html
+api/dbus.gobject_service-module.html ../dbus.gobject_service.html
+api/dbus.gobject_service-pysrc.html ../dbus.gobject_service.html
+api/dbus.Int16-class.html ../dbus.html
+api/dbus.Int32-class.html ../dbus.html
+api/dbus.Int64-class.html ../dbus.html
+api/dbus.lowlevel.ErrorMessage-class.html ../dbus.lowlevel.html
+api/dbus.lowlevel.Message-class.html ../dbus.lowlevel.html
+api/dbus.lowlevel.MethodCallMessage-class.html ../dbus.lowlevel.html
+api/dbus.lowlevel.MethodReturnMessage-class.html ../dbus.lowlevel.html
+api/dbus.lowlevel-module.html ../dbus.lowlevel.html
+api/dbus.lowlevel.PendingCall-class.html ../dbus.lowlevel.html
+api/dbus.lowlevel-pysrc.html ../dbus.lowlevel.html
+api/dbus.lowlevel.SignalMessage-class.html ../dbus.lowlevel.html
+api/dbus.mainloop.glib-module.html ../dbus.mainloop.html
+api/dbus.mainloop.glib-pysrc.html ../dbus.mainloop.html
+api/dbus.mainloop-module.html ../dbus.mainloop.html
+api/dbus.mainloop.NativeMainLoop-class.html ../dbus.mainloop.html
+api/dbus.mainloop-pysrc.html ../dbus.mainloop.html
+api/dbus-module.html ../dbus.html
+api/dbus.ObjectPath-class.html ../dbus.html
+api/dbus.proxies._DeferredMethod-class.html ../dbus.proxies.html
+api/dbus.proxies.Interface-class.html ../dbus.proxies.html
+api/dbus.proxies-module.html ../dbus.proxies.html
+api/dbus.proxies._ProxyMethod-class.html ../dbus.proxies.html
+api/dbus.proxies.ProxyObject-class.html ../dbus.proxies.html
+api/dbus.proxies-pysrc.html ../dbus.proxies.html
+api/dbus-pysrc.html ../dbus.html
+api/dbus.server-module.html ../dbus.server.html
+api/dbus.server-pysrc.html ../dbus.server.html
+api/dbus.server.Server-class.html ../dbus.server.html
+api/dbus.service.BusName-class.html ../dbus.service.html
+api/dbus.service.FallbackObject-class.html ../dbus.service.html
+api/dbus.service.Interface-class.html ../dbus.service.html
+api/dbus.service.InterfaceType-class.html ../dbus.service.html
+api/dbus.service-module.html ../dbus.service.html
+api/dbus.service.Object-class.html ../dbus.service.html
+api/dbus.service-pysrc.html ../dbus.service.html
+api/dbus.service._VariantSignature-class.html ../dbus.service.html
+api/dbus.Signature-class.html ../dbus.html
+api/dbus.String-class.html ../dbus.html
+api/dbus.Struct-class.html ../dbus.html
+api/dbus.types-module.html ../dbus.html
+api/dbus.types-pysrc.html ../dbus.html
+api/dbus.types.UnixFd-class.html ../dbus.html
+api/dbus.UInt16-class.html ../dbus.html
+api/dbus.UInt32-class.html ../dbus.html
+api/dbus.UInt64-class.html ../dbus.html
+api/dbus.UTF8String-class.html ../dbus.html
+api/dbus._version-module.html ../dbus.html
+api/dbus._version-pysrc.html ../dbus.html
+api/deprecated-index.html ../dbus.html
+api/frames.html ../dbus.html
+api/help.html ../dbus.html
+api/identifier-dbus.html ../dbus.html
+api/index.html ../dbus.html
+api/module-tree.html ../dbus.html
+api/redirect.html ../dbus.html
+api/since-index.html ../news.html
+api/toc-dbus.bus-module.html ../dbus.bus.html
+api/toc-dbus._compat-module.html ../dbus.html
+api/toc-dbus.connection-module.html ../dbus.connection.html
+api/toc-dbus.dbus_bindings-module.html ../dbus.html
+api/toc-dbus._dbus-module.html ../dbus.html
+api/toc-dbus.decorators-module.html ../dbus.decorators.html
+api/toc-dbus.exceptions-module.html ../dbus.exceptions.html
+api/toc-dbus._expat_introspect_parser-module.html ../dbus.html
+api/toc-dbus.gi_service-module.html ../dbus.gi_service.html
+api/toc-dbus.glib-module.html ../dbus.glib.html
+api/toc-dbus.gobject_service-module.html ../dbus.gobject_service.html
+api/toc-dbus.lowlevel-module.html ../dbus.lowlevel.html
+api/toc-dbus.mainloop.glib-module.html ../dbus.mainloop.html
+api/toc-dbus.mainloop-module.html ../dbus.mainloop.html
+api/toc-dbus-module.html ../dbus.html
+api/toc-dbus.proxies-module.html ../dbus.proxies.html
+api/toc-dbus.server-module.html ../dbus.server.html
+api/toc-dbus.service-module.html ../dbus.service.html
+api/toc-dbus.types-module.html ../dbus.types.html
+api/toc-dbus._version-module.html ../dbus.html
+api/toc-everything.html ../dbus.html
+api/toc.html ../dbus.html
diff --git a/doc/redirects.py b/doc/redirects.py
new file mode 100644
index 0000000..5cee516
--- /dev/null
+++ b/doc/redirects.py
@@ -0,0 +1,43 @@
+#!/usr/bin/python
+
+import os
+
+SRCDIR = os.environ.get('DBUS_TOP_SRCDIR', '.')
+
+if __name__ == '__main__':
+ with open(os.path.join(SRCDIR, 'doc', 'redirects'), 'r') as reader:
+ for line in reader:
+ line = line.strip()
+
+ if not line:
+ continue
+
+ if line.startswith('#'):
+ continue
+
+ page, dest = line.split(None, 1)
+
+ try:
+ os.makedirs(os.path.join('doc', '_build', os.path.dirname(page)))
+ except OSError:
+ pass
+
+ assert not os.path.exists(os.path.join('doc', '_build', page))
+
+ if dest.startswith('"'):
+ assert page.endswith('.txt')
+ text = dest.strip('"')
+
+ with open(os.path.join('doc', '_build', page), 'w') as writer:
+ writer.write(text)
+ writer.write('\n')
+ else:
+ assert page.endswith('.html')
+
+ with open(os.path.join('doc', '_build', page), 'w') as writer:
+ writer.write(
+ '<meta http-equiv="refresh" content="0; url={}" />\n'.format(
+ dest))
+ writer.write(
+ 'See <a href="{}">{}</a>\n'.format(
+ dest, dest))
diff --git a/doc/tutorial.txt b/doc/tutorial.txt
new file mode 100644
index 0000000..cc7a99e
--- /dev/null
+++ b/doc/tutorial.txt
@@ -0,0 +1,716 @@
+====================
+dbus-python tutorial
+====================
+
+:Author: Simon McVittie, `Collabora Ltd.`_
+:Date: 2006-06-14
+
+.. _`Collabora Ltd.`: http://www.collabora.co.uk/
+
+This tutorial requires Python 2.4 or up, and ``dbus-python`` 0.80rc4 or up.
+
+.. contents::
+
+.. --------------------------------------------------------------------
+
+.. _Bus object:
+.. _Bus objects:
+
+Connecting to the Bus
+=====================
+
+Applications that use D-Bus typically connect to a *bus daemon*, which
+forwards messages between the applications. To use D-Bus, you need to create a
+``Bus`` object representing the connection to the bus daemon.
+
+There are generally two bus daemons you may be interested in. Each user
+login session should have a *session bus*, which is local to that
+session. It's used to communicate between desktop applications. Connect
+to the session bus by creating a ``SessionBus`` object::
+
+ import dbus
+
+ session_bus = dbus.SessionBus()
+
+The *system bus* is global and usually started during boot; it's used to
+communicate with system services like udev_, NetworkManager_, and the
+`Hardware Abstraction Layer daemon (hald)`_. To connect to the system
+bus, create a ``SystemBus`` object::
+
+ import dbus
+
+ system_bus = dbus.SystemBus()
+
+Of course, you can connect to both in the same application.
+
+For special purposes, you might use a non-default Bus, or a connection
+which isn't a Bus at all, using some new API added in dbus-python 0.81.0.
+This is not described here, and will at some stage be the subject of a separate
+tutorial.
+
+.. _udev:
+ http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
+.. _NetworkManager:
+ http://www.gnome.org/projects/NetworkManager/
+.. _Hardware Abstraction Layer daemon (hald):
+ http://www.freedesktop.org/wiki/Software/hal
+
+.. --------------------------------------------------------------------
+
+Making method calls
+===================
+
+D-Bus applications can export objects for other applications' use. To
+start working with an object in another application, you need to know:
+
+* The *bus name*. This identifies which application you want to
+ communicate with. You'll usually identify applications by a
+ *well-known name*, which is a dot-separated string starting with a
+ reversed domain name, such as ``org.freedesktop.NetworkManager``
+ or ``com.example.WordProcessor``.
+
+* The *object path*. Applications can export many objects - for
+ instance, example.com's word processor might provide an object
+ representing the word processor application itself and an object for
+ each document window opened, or it might also provide an object for
+ each paragraph within a document.
+
+ To identify which one you want to interact with, you use an object path,
+ a slash-separated string resembling a filename. For instance, example.com's
+ word processor might provide an object at ``/`` representing the word
+ processor itself, and objects at ``/documents/123`` and
+ ``/documents/345`` representing opened document windows.
+
+As you'd expect, one of the main things you can do with remote objects
+is to call their methods. As in Python, methods may have parameters,
+and they may return one or more values.
+
+.. _proxy object:
+
+Proxy objects
+-------------
+
+To interact with a remote object, you use a *proxy object*. This is a
+Python object which acts as a proxy or "stand-in" for the remote object -
+when you call a method on a proxy object, this causes dbus-python to make
+a method call on the remote object, passing back any return values from
+the remote object's method as the return values of the proxy method call.
+
+To obtain a proxy object, call the ``get_object`` method on the ``Bus``.
+For example, NetworkManager_ has the well-known name
+``org.freedesktop.NetworkManager`` and exports an object whose object
+path is ``/org/freedesktop/NetworkManager``, plus an object per network
+interface at object paths like
+``/org/freedesktop/NetworkManager/Devices/eth0``. You can get a proxy
+for the object representing eth0 like this::
+
+ import dbus
+ bus = dbus.SystemBus()
+ proxy = bus.get_object('org.freedesktop.NetworkManager',
+ '/org/freedesktop/NetworkManager/Devices/eth0')
+ # proxy is a dbus.proxies.ProxyObject
+
+Interfaces and methods
+----------------------
+
+D-Bus uses *interfaces* to provide a namespacing mechanism for methods.
+An interface is a group of related methods and signals (more on signals
+later), identified by a name which is a series of dot-separated components
+starting with a reversed domain name. For instance, each NetworkManager_
+object representing a network interface implements the interface
+``org.freedesktop.NetworkManager.Devices``, which has methods like
+``getProperties``.
+
+To call a method, call the method of the same name on the proxy object,
+passing in the interface name via the ``dbus_interface`` keyword argument::
+
+ import dbus
+ bus = dbus.SystemBus()
+ eth0 = bus.get_object('org.freedesktop.NetworkManager',
+ '/org/freedesktop/NetworkManager/Devices/eth0')
+ props = eth0.getProperties(dbus_interface='org.freedesktop.NetworkManager.Devices')
+ # props is a tuple of properties, the first of which is the object path
+
+.. _dbus.Interface:
+
+As a short cut, if you're going to be calling many methods with the same
+interface, you can construct a ``dbus.Interface`` object and call
+methods on that, without needing to specify the interface again::
+
+ import dbus
+ bus = dbus.SystemBus()
+ eth0 = bus.get_object('org.freedesktop.NetworkManager',
+ '/org/freedesktop/NetworkManager/Devices/eth0')
+ eth0_dev_iface = dbus.Interface(eth0,
+ dbus_interface='org.freedesktop.NetworkManager.Devices')
+ props = eth0_dev_iface.getProperties()
+ # props is the same as before
+
+See also
+~~~~~~~~
+
+See the example in ``examples/example-client.py``. Before running it,
+you'll need to run ``examples/example-service.py`` in the background or
+in another shell.
+
+Data types
+----------
+
+Unlike Python, D-Bus is statically typed - each method has a certain
+*signature* representing the types of its arguments, and will not accept
+arguments of other types.
+
+D-Bus has an introspection mechanism, which ``dbus-python`` tries to use
+to discover the correct argument types. If this succeeds, Python types
+are converted into the right D-Bus data types automatically, if possible;
+``TypeError`` is raised if the type is inappropriate.
+
+If the introspection mechanism fails (or the argument's type is
+variant - see below), you have to provide arguments of
+the correct type. ``dbus-python`` provides Python types corresponding to
+the D-Bus data types, and a few native Python types are also converted to
+D-Bus data types automatically. If you use a type which isn't among these,
+a ``TypeError`` will be raised telling you that ``dbus-python`` was
+unable to guess the D-Bus signature.
+
+Basic types
+~~~~~~~~~~~
+
+The following basic data types are supported.
+
++-----------------------+---------------------+-----------------------+
+|Python type |converted to |notes |
+| |D-Bus type | |
++=======================+=====================+=======================+
+|D-Bus `proxy object`_ |object path |`(+)`_ |
++-----------------------+(signature 'o') | |
+|`dbus.Interface`_ | | |
++-----------------------+ | |
+|`dbus.service.Object`_ | | |
++-----------------------+---------------------+-----------------------+
+|``dbus.Boolean`` |Boolean |a subclass of ``int`` |
+| |(signature 'b') | |
++-----------------------+---------------------+ |
+|``dbus.Byte`` |byte (signature 'y') | |
++-----------------------+---------------------+ |
+|``dbus.Int16`` |16-bit signed | |
+| |integer ('n') | |
++-----------------------+---------------------+ |
+|``dbus.UInt16`` |16-bit unsigned | |
+| |integer ('q') | |
++-----------------------+---------------------+ |
+|``dbus.Int32`` |32-bit signed | |
+| |integer ('i') | |
++-----------------------+---------------------+-----------------------+
+|``dbus.UInt32`` |32-bit unsigned |a subclass of ``long`` |
+| |integer ('u') |(Python 2) |
++-----------------------+---------------------+ |
+|``dbus.Int64`` |64-bit signed |a subclass of ``int`` |
+| |integer ('x') |(Python 3) |
++-----------------------+---------------------+ |
+|``dbus.UInt64`` |64-bit unsigned | |
+| |integer ('t') | |
++-----------------------+---------------------+-----------------------+
+|``dbus.Double`` |double-precision |a subclass of ``float``|
+| |floating point ('d') | |
++-----------------------+---------------------+-----------------------+
+|``dbus.ObjectPath`` |object path ('o') |a subclass of ``str`` |
++-----------------------+---------------------+ |
+|``dbus.Signature`` |signature ('g') | |
++-----------------------+---------------------+-----------------------+
+|``dbus.String`` |string ('s') |a subclass of |
+| | |``unicode`` (Python 2) |
+| | | |
+| | |a subclass of ``str`` |
+| | |(Python 3) |
++-----------------------+---------------------+-----------------------+
+|``dbus.UTF8String`` |string ('s') |a subclass of ``str``, |
+| | |only in Python 2 |
++-----------------------+---------------------+-----------------------+
+|``bool`` |Boolean ('b') | |
++-----------------------+---------------------+-----------------------+
+|``int`` or subclass |32-bit signed | |
+| |integer ('i') | |
++-----------------------+---------------------+-----------------------+
+|``long`` or subclass |64-bit signed | Python 2 only |
+| |integer ('i') | |
++-----------------------+---------------------+-----------------------+
+|``float`` or subclass |double-precision | |
+| |floating point ('d') | |
++-----------------------+---------------------+-----------------------+
+|``bytes`` or subclass |string ('s') | must be valid UTF-8 |
++-----------------------+---------------------+-----------------------+
+|Python 2 ``unicode`` |string ('s') | |
++-----------------------+ | |
+|Python 3 ``str`` | | |
++-----------------------+---------------------+-----------------------+
+
+.. _(+):
+
+(+): D-Bus proxy objects, exported D-Bus service objects and anything
+else with the special attribute ``__dbus_object_path__``, which
+must be a string, are converted to their object-path. This might be
+useful if you're writing an object-oriented API using dbus-python.
+
+Basic type conversions
+~~~~~~~~~~~~~~~~~~~~~~
+
+If introspection succeeded, ``dbus-python`` will also accept:
+
+* for Boolean parameters, any object (converted as if via ``int(bool(...))``)
+* for byte parameters, a single-character string (converted as if via ``ord()``)
+* for byte and integer parameters, any integer (must be in the correct range)
+* for object-path and signature parameters, any ``str`` or ``unicode``
+ subclass (the value must follow the appropriate syntax)
+
+Container types
+~~~~~~~~~~~~~~~
+
+D-Bus supports four container types: array (a variable-length sequence of the
+same type), struct (a fixed-length sequence whose members may have
+different types), dictionary (a mapping from values of the same basic type to
+values of the same type), and variant (a container which may hold any
+D-Bus type, including another variant).
+
+Arrays are represented by Python lists, or by ``dbus.Array``, a subclass
+of ``list``. When sending an array, if an introspected signature is
+available, that will be used; otherwise, if the ``signature`` keyword
+parameter was passed to the ``Array`` constructor, that will be used to
+determine the contents' signature; otherwise, ``dbus-python`` will guess
+from the array's first item.
+
+The signature of an array is 'ax' where 'x' represents the signature of
+one item. For instance, you could also have 'as' (array of strings) or
+'a(ii)' (array of structs each containing two 32-bit integers).
+
+There's also a type ``dbus.ByteArray`` which is a subclass of ``bytes``,
+used as a more efficient representation of a D-Bus array of bytes
+(signature 'ay').
+
+Structs are represented by Python tuples, or by ``dbus.Struct``, a
+subclass of ``tuple``. When sending a struct, if an introspected signature is
+available, that will be used; otherwise, if the ``signature`` keyword
+parameter was passed to the ``Array`` constructor, that will be used to
+determine the contents' signature; otherwise, ``dbus-python`` will guess
+from the array's first item.
+
+The signature of a struct consists of the signatures of the contents,
+in parentheses - for instance '(is)' is the signature of a struct
+containing a 32-bit integer and a string.
+
+Dictionaries are represented by Python dictionaries, or by
+``dbus.Dictionary``, a subclass of ``dict``. When sending a dictionary,
+if an introspected signature is available, that will be used; otherwise,
+if the ``signature`` keyword parameter was passed to the ``Dictionary``
+constructor, that will be used to determine the contents' key and value
+signatures; otherwise, ``dbus-python`` will guess from an arbitrary item
+of the ``dict``.
+
+The signature of a dictionary is 'a{xy}' where 'x' represents the
+signature of the keys (which may not be a container type) and 'y'
+represents the signature of the values. For instance,
+'a{s(ii)}' is a dictionary where the keys are strings and the values are
+structs containing two 32-bit integers.
+
+Variants are represented by setting the ``variant_level`` keyword
+argument in the constructor of any D-Bus data type to a value greater
+than 0 (``variant_level`` 1 means a variant containing some other data type,
+``variant_level`` 2 means a variant containing a variant containing some
+other data type, and so on). If a non-variant is passed as an argument
+but introspection indicates that a variant is expected, it'll
+automatically be wrapped in a variant.
+
+The signature of a variant is 'v'.
+
+.. _byte_arrays and utf8_strings:
+
+Return values, and the ``byte_arrays`` and ``utf8_strings`` options
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If a D-Bus method returns no value, the Python proxy method will return
+``None``.
+
+If a D-Bus method returns one value, the Python proxy method will return
+that value as one of the ``dbus.`` types - by default, strings are
+returned as ``dbus.String`` (a subclass of Unicode) and byte arrays are
+returned as a ``dbus.Array`` of ``dbus.Byte``.
+
+If a D-Bus method returns multiple values, the Python proxy method
+will return a tuple containing those values.
+
+If you want strings returned as ``dbus.UTF8String`` (a subclass of
+``bytes``) pass the keyword parameter ``utf8_strings=True`` to the proxy
+method. This mode is only available in Python 2.
+
+If you want byte arrays returned as ``dbus.ByteArray`` (also a
+subclass of ``bytes`` - in practice, this is often what you want) pass
+the keyword parameter ``byte_arrays=True`` to the proxy method.
+
+.. --------------------------------------------------------------------
+
+Making asynchronous method calls
+================================
+
+Asynchronous (non-blocking) method calls allow multiple method calls to
+be in progress simultaneously, and allow your application to do other
+work while it's waiting for the results. To make asynchronous calls,
+you first need an event loop or "main loop".
+
+Setting up an event loop
+------------------------
+
+Currently, the only main loop supported by ``dbus-python`` is GLib.
+
+``dbus-python`` has a global default main loop, which is the easiest way
+to use this functionality. To arrange for the GLib main loop to be the
+default, use::
+
+ from dbus.mainloop.glib import DBusGMainLoop
+
+ DBusGMainLoop(set_as_default=True)
+
+You must do this before `connecting to the bus`_.
+
+Actually starting the main loop is as usual for ``pygi``::
+
+ from gi.repository import GLib
+
+ loop = GLib.MainLoop()
+ loop.run()
+
+While ``loop.run()`` is executing, GLib will run your callbacks when
+appropriate. To stop, call ``loop.quit()``.
+
+You can also set a main loop on a per-connection basis, by passing a
+main loop to the Bus constructor::
+
+ import dbus
+ from dbus.mainloop.glib import DBusGMainLoop
+
+ dbus_loop = DBusGMainLoop()
+
+ bus = dbus.SessionBus(mainloop=dbus_loop)
+
+This isn't very useful until we support more than one main loop, though.
+
+Backwards compatibility: ``dbus.glib``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In versions of ``dbus-python`` prior to 0.80, the way to set GLib as the
+default main loop was::
+
+ import dbus.glib
+
+Executing that import statement would automatically load the GLib main
+loop and make this the default. This is now deprecated, since it's
+highly non-obvious, but may be useful if you want to write or understand
+backwards-compatible code.
+
+The Qt main loop
+~~~~~~~~~~~~~~~~
+
+PyQt v4.2 and later includes support for integrating dbus-python with
+the Qt event loop. To connect D-Bus to this main loop, call
+``dbus.mainloop.qt.DBusQtMainLoop`` instead of
+``dbus.mainloop.glib.DBusGMainLoop``. Otherwise the Qt loop is used in
+exactly the same way as the GLib loop.
+
+Making asynchronous calls
+-------------------------
+
+To make a call asynchronous, pass two callables as keyword arguments
+``reply_handler`` and ``error_handler`` to the proxy method. The proxy
+method will immediately return `None`. At some later time, when the event
+loop is running, one of these will happen: either
+
+* the ``reply_handler`` will be called with the method's return values
+ as arguments; or
+
+* the ``error_handler`` will be called with one argument, an instance of
+ ``DBusException`` representing a remote exception.
+
+See also
+~~~~~~~~
+
+``examples/example-async-client.py`` makes asynchronous method calls to
+the service provided by ``examples/example-service.py`` which return
+either a value or an exception. As for ``examples/example-client.py``,
+you need to run ``examples/example-service.py`` in the background or
+in another shell first.
+
+.. --------------------------------------------------------------------
+
+Receiving signals
+=================
+
+To receive signals, the Bus needs to be connected to an event loop - see
+section `Setting up an event loop`_. Signals will only be received while
+the event loop is running.
+
+Signal matching
+---------------
+
+To respond to signals, you can use the ``add_signal_receiver`` method on
+`Bus objects`_. This arranges for a callback to be called when a
+matching signal is received, and has the following arguments:
+
+* a callable (the ``handler_function``) which will be called by the event loop
+ when the signal is received - its parameters will be the arguments of
+ the signal
+
+* the signal name, ``signal_name``: here None (the default) matches all names
+
+* the D-Bus interface, ``dbus_interface``: again None is the default,
+ and matches all interfaces
+
+* a sender bus name (well-known or unique), ``bus_name``: None is again
+ the default, and matches all senders. Well-known names match signals
+ from whatever application is currently the primary owner of that
+ well-known name.
+
+* a sender object path, ``path``: once again None is the default and
+ matches all object paths
+
+``add_signal_receiver`` also has keyword arguments ``utf8_strings`` and
+``byte_arrays`` which influence the types used when calling the
+handler function, in the same way as the `byte_arrays and utf8_strings`_
+options on proxy methods.
+
+``add_signal_receiver`` returns a ``SignalMatch`` object. Its only
+useful public API at the moment is a ``remove`` method with no
+arguments, which removes the signal match from the connection.
+
+Getting more information from a signal
+--------------------------------------
+
+You can also arrange for more information to be passed to the handler
+function. If you pass the keyword arguments ``sender_keyword``,
+``destination_keyword``, ``interface_keyword``, ``member_keyword`` or
+``path_keyword`` to the ``connect_to_signal`` method, the appropriate
+part of the signal message will be passed to the handler function as a
+keyword argument: for instance if you use ::
+
+ def handler(sender=None):
+ print "got signal from %r" % sender
+
+ iface.connect_to_signal("Hello", handler, sender_keyword='sender')
+
+and a signal ``Hello`` with no arguments is received from
+``com.example.Foo``, the ``handler`` function will be called with
+``sender='com.example.Foo'``.
+
+String argument matching
+------------------------
+
+If there are keyword parameters for the form ``arg``\ *n* where n is a
+small non-negative number, their values must be Unicode strings (Python
+2 ``unicode`` or Python 3 ``str``) or UTF-8 bytestrings. The handler
+will only be called if that argument of the signal (numbered from zero)
+is a D-Bus string (in particular, not an object-path or a signature)
+with that value.
+
+.. *this comment is to stop the above breaking vim syntax highlighting*
+
+Receiving signals from a proxy object
+-------------------------------------
+
+`Proxy objects`_ have a special method ``connect_to_signal`` which
+arranges for a callback to be called when a signal is received
+from the corresponding remote object. The parameters are:
+
+* the name of the signal
+
+* a callable (the handler function) which will be called by the event loop
+ when the signal is received - its parameters will be the arguments of
+ the signal
+
+* the handler function, a callable: the same as for ``add_signal_receiver``
+
+* the keyword argument ``dbus_interface`` qualifies the name with its
+ interface
+
+`dbus.Interface` objects have a similar ``connect_to_signal`` method,
+but in this case you don't need the ``dbus_interface`` keyword argument
+since the interface to use is already known.
+
+The same extra keyword arguments as for ``add_signal_receiver`` are also
+available, and just like ``add_signal_receiver``, it returns a
+SignalMatch.
+
+You shouldn't use proxy objects just to listen to signals, since they
+might activate the relevant service when created, but if you already have a
+proxy object in order to call methods, it's often convenient to use it to add
+signal matches too.
+
+See also
+--------
+
+``examples/signal-recipient.py`` receives signals - it demonstrates
+general signal matching as well as ``connect_to_signal``. Before running it,
+you'll need to run ``examples/signal-emitter.py`` in the background or
+in another shell.
+
+.. _BusName:
+
+.. --------------------------------------------------------------------
+
+Claiming a bus name
+===================
+
+FIXME describe `BusName`_ - perhaps fix its API first?
+
+The unique-instance idiom
+-------------------------
+
+FIXME provide exemplary code, put it in examples
+
+.. _exported object:
+.. _exported objects:
+
+.. --------------------------------------------------------------------
+
+Exporting objects
+=================
+
+Objects made available to other applications over D-Bus are said to be
+*exported*. All subclasses of ``dbus.service.Object`` are automatically
+exported.
+
+To export objects, the Bus needs to be connected to an event loop - see
+section `Setting up an event loop`_. Exported methods will only be called,
+and queued signals will only be sent, while the event loop is running.
+
+.. _dbus.service.Object:
+
+Inheriting from ``dbus.service.Object``
+---------------------------------------
+
+To export an object onto the Bus, just subclass
+``dbus.service.Object``. Object expects either a `BusName`_ or a `Bus
+object`_, and an object-path, to be passed to its constructor: arrange
+for this information to be available. For example::
+
+ class Example(dbus.service.Object):
+ def __init__(self, object_path):
+ dbus.service.Object.__init__(self, dbus.SessionBus(), path)
+
+This object will automatically support introspection, but won't do
+anything particularly interesting. To fix that, you'll need to export some
+methods and signals too.
+
+FIXME also mention dbus.gobject.ExportedGObject once I've written it
+
+Exporting methods with ``dbus.service.method``
+----------------------------------------------
+
+To export a method, use the decorator ``dbus.service.method``. For
+example::
+
+ class Example(dbus.service.Object):
+ def __init__(self, object_path):
+ dbus.service.Object.__init__(self, dbus.SessionBus(), path)
+
+ @dbus.service.method(dbus_interface='com.example.Sample',
+ in_signature='v', out_signature='s')
+ def StringifyVariant(self, variant):
+ return str(variant)
+
+The ``in_signature`` and ``out_signature`` are D-Bus signature strings
+as described in `Data Types`_.
+
+As well as the keywords shown, you can pass ``utf8_strings`` and
+``byte_arrays`` keyword arguments, which influence the types which will
+be passed to the decorated method when it's called via D-Bus, in the
+same way that the `byte_arrays and utf8_strings`_ options affect the
+return value of a proxy method.
+
+You can find a simple example in ``examples/example-service.py``, which
+we used earlier to demonstrate ``examples/example-client.py``.
+
+Finding out the caller's bus name
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``method`` decorator accepts a ``sender_keyword`` keyword argument.
+If you set that to a string, the unique bus name of the sender will be
+passed to the decorated method as a keyword argument of that name::
+
+ class Example(dbus.service.Object):
+ def __init__(self, object_path):
+ dbus.service.Object.__init__(self, dbus.SessionBus(), path)
+
+ @dbus.service.method(dbus_interface='com.example.Sample',
+ in_signature='', out_signature='s',
+ sender_keyword='sender')
+ def SayHello(self, sender=None):
+ return 'Hello, %s!' % sender
+ # -> something like 'Hello, :1.1!'
+
+Asynchronous method implementations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+FIXME and also add an example, perhaps examples/example-async-service.py
+
+Emitting signals with ``dbus.service.signal``
+---------------------------------------------
+
+To export a signal, use the decorator ``dbus.service.signal``; to emit
+that signal, call the decorated method. The decorated method can also
+contain code which will be run when called, as usual. For example::
+
+ class Example(dbus.service.Object):
+ def __init__(self, object_path):
+ dbus.service.Object.__init__(self, dbus.SessionBus(), path)
+
+ @dbus.service.signal(dbus_interface='com.example.Sample',
+ signature='us')
+ def NumberOfBottlesChanged(self, number, contents):
+ print "%d bottles of %s on the wall" % (number, contents)
+
+ e = Example('/bottle-counter')
+ e.NumberOfBottlesChanged(100, 'beer')
+ # -> emits com.example.Sample.NumberOfBottlesChanged(100, 'beer')
+ # and prints "100 bottles of beer on the wall"
+
+The signal will be queued for sending when the decorated method returns -
+you can prevent the signal from being sent by raising an exception
+from the decorated method (for instance, if the parameters are
+inappropriate). The signal will only actually be sent when the event loop
+next runs.
+
+Example
+~~~~~~~
+
+``examples/example-signal-emitter.py`` emits some signals on demand when
+one of its methods is called. (In reality, you'd emit a signal when some
+sort of internal state changed, which may or may not be triggered by a
+D-Bus method call.)
+
+.. --------------------------------------------------------------------
+
+License for this document
+=========================
+
+Copyright 2006-2007 `Collabora Ltd.`_
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy,
+modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+..
+ vim:set ft=rst sw=4 sts=4 et tw=72:
diff --git a/examples/example-async-client.py b/examples/example-async-client.py
new file mode 100755
index 0000000..a24191d
--- /dev/null
+++ b/examples/example-async-client.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python example-service.py &
+python example-async-client.py
+python example-client.py --exit-service
+"""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+import traceback
+
+from gi.repository import GLib
+
+import dbus
+import dbus.mainloop.glib
+
+# Callbacks for asynchronous calls
+
+def handle_hello_reply(r):
+ global hello_replied
+ hello_replied = True
+
+ print str(r)
+
+ if hello_replied and raise_replied:
+ loop.quit()
+
+def handle_hello_error(e):
+ global failed
+ global hello_replied
+ hello_replied = True
+ failed = True
+
+ print "HelloWorld raised an exception! That's not meant to happen..."
+ print "\t", str(e)
+
+ if hello_replied and raise_replied:
+ loop.quit()
+
+def handle_raise_reply():
+ global failed
+ global raise_replied
+ raise_replied = True
+ failed = True
+
+ print "RaiseException returned normally! That's not meant to happen..."
+
+ if hello_replied and raise_replied:
+ loop.quit()
+
+def handle_raise_error(e):
+ global raise_replied
+ raise_replied = True
+
+ print "RaiseException raised an exception as expected:"
+ print "\t", str(e)
+
+ if hello_replied and raise_replied:
+ loop.quit()
+
+def make_calls():
+ # To make an async call, use the reply_handler and error_handler kwargs
+ remote_object.HelloWorld("Hello from example-async-client.py!",
+ dbus_interface='com.example.SampleInterface',
+ reply_handler=handle_hello_reply,
+ error_handler=handle_hello_error)
+
+ # Interface objects also support async calls
+ iface = dbus.Interface(remote_object, 'com.example.SampleInterface')
+
+ iface.RaiseException(reply_handler=handle_raise_reply,
+ error_handler=handle_raise_error)
+
+ return False
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SessionBus()
+ try:
+ remote_object = bus.get_object("com.example.SampleService","/SomeObject")
+ except dbus.DBusException:
+ traceback.print_exc()
+ print usage
+ sys.exit(1)
+
+ # Make the method call after a short delay
+ GLib.timeout_add(1000, make_calls)
+
+ failed = False
+ hello_replied = False
+ raise_replied = False
+
+ loop = GLib.MainLoop()
+ loop.run()
+ if failed:
+ raise SystemExit("Example async client failed!")
diff --git a/examples/example-client.py b/examples/example-client.py
new file mode 100755
index 0000000..262f892
--- /dev/null
+++ b/examples/example-client.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python example-service.py &
+python example-client.py
+python example-client.py --exit-service
+"""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+from traceback import print_exc
+
+import dbus
+
+def main():
+ bus = dbus.SessionBus()
+
+ try:
+ remote_object = bus.get_object("com.example.SampleService",
+ "/SomeObject")
+
+ # you can either specify the dbus_interface in each call...
+ hello_reply_list = remote_object.HelloWorld("Hello from example-client.py!",
+ dbus_interface = "com.example.SampleInterface")
+ except dbus.DBusException:
+ print_exc()
+ print usage
+ sys.exit(1)
+
+ print (hello_reply_list)
+
+ # ... or create an Interface wrapper for the remote object
+ iface = dbus.Interface(remote_object, "com.example.SampleInterface")
+
+ hello_reply_tuple = iface.GetTuple()
+
+ print hello_reply_tuple
+
+ hello_reply_dict = iface.GetDict()
+
+ print hello_reply_dict
+
+ # D-Bus exceptions are mapped to Python exceptions
+ try:
+ iface.RaiseException()
+ except dbus.DBusException as e:
+ print str(e)
+
+ # introspection is automatically supported
+ print remote_object.Introspect(dbus_interface="org.freedesktop.DBus.Introspectable")
+
+ if sys.argv[1:] == ['--exit-service']:
+ iface.Exit()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/example-service.py b/examples/example-service.py
new file mode 100755
index 0000000..af22577
--- /dev/null
+++ b/examples/example-service.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python example-service.py &
+python example-client.py
+python example-async-client.py
+python example-client.py --exit-service
+"""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from gi.repository import GLib
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class DemoException(dbus.DBusException):
+ _dbus_error_name = 'com.example.DemoException'
+
+class SomeObject(dbus.service.Object):
+
+ @dbus.service.method("com.example.SampleInterface",
+ in_signature='s', out_signature='as')
+ def HelloWorld(self, hello_message):
+ print (str(hello_message))
+ return ["Hello", " from example-service.py", "with unique name",
+ session_bus.get_unique_name()]
+
+ @dbus.service.method("com.example.SampleInterface",
+ in_signature='', out_signature='')
+ def RaiseException(self):
+ raise DemoException('The RaiseException method does what you might '
+ 'expect')
+
+ @dbus.service.method("com.example.SampleInterface",
+ in_signature='', out_signature='(ss)')
+ def GetTuple(self):
+ return ("Hello Tuple", " from example-service.py")
+
+ @dbus.service.method("com.example.SampleInterface",
+ in_signature='', out_signature='a{ss}')
+ def GetDict(self):
+ return {"first": "Hello Dict", "second": " from example-service.py"}
+
+ @dbus.service.method("com.example.SampleInterface",
+ in_signature='', out_signature='')
+ def Exit(self):
+ mainloop.quit()
+
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ session_bus = dbus.SessionBus()
+ name = dbus.service.BusName("com.example.SampleService", session_bus)
+ object = SomeObject(session_bus, '/SomeObject')
+
+ mainloop = GLib.MainLoop()
+ print "Running example service."
+ print usage
+ mainloop.run()
diff --git a/examples/example-signal-emitter.py b/examples/example-signal-emitter.py
new file mode 100755
index 0000000..6cf1387
--- /dev/null
+++ b/examples/example-signal-emitter.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python example-signal-emitter.py &
+python example-signal-recipient.py
+python example-signal-recipient.py --exit-service
+"""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from gi.repository import GLib
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+
+class TestObject(dbus.service.Object):
+ def __init__(self, conn, object_path='/com/example/TestService/object'):
+ dbus.service.Object.__init__(self, conn, object_path)
+
+ @dbus.service.signal('com.example.TestService')
+ def HelloSignal(self, message):
+ # The signal is emitted when this method exits
+ # You can have code here if you wish
+ pass
+
+ @dbus.service.method('com.example.TestService')
+ def emitHelloSignal(self):
+ #you emit signals by calling the signal's skeleton method
+ self.HelloSignal('Hello')
+ return 'Signal emitted'
+
+ @dbus.service.method("com.example.TestService",
+ in_signature='', out_signature='')
+ def Exit(self):
+ loop.quit()
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ session_bus = dbus.SessionBus()
+ name = dbus.service.BusName('com.example.TestService', session_bus)
+ object = TestObject(session_bus)
+
+ loop = GLib.MainLoop()
+ print "Running example signal emitter service."
+ print usage
+ loop.run()
diff --git a/examples/example-signal-recipient.py b/examples/example-signal-recipient.py
new file mode 100755
index 0000000..818b0fa
--- /dev/null
+++ b/examples/example-signal-recipient.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python example-signal-emitter.py &
+python example-signal-recipient.py
+python example-signal-recipient.py --exit-service
+"""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+import traceback
+
+from gi.repository import GLib
+
+import dbus
+import dbus.mainloop.glib
+
+def handle_reply(msg):
+ print msg
+
+def handle_error(e):
+ print str(e)
+
+def emit_signal():
+ #call the emitHelloSignal method
+ object.emitHelloSignal(dbus_interface="com.example.TestService")
+ #reply_handler = handle_reply, error_handler = handle_error)
+ # exit after waiting a short time for the signal
+ GLib.timeout_add(2000, loop.quit)
+
+ if sys.argv[1:] == ['--exit-service']:
+ object.Exit(dbus_interface='com.example.TestService')
+
+ return False
+
+def hello_signal_handler(hello_string):
+ print ("Received signal (by connecting using remote object) and it says: "
+ + hello_string)
+
+def catchall_signal_handler(*args, **kwargs):
+ print ("Caught signal (in catchall handler) "
+ + kwargs['dbus_interface'] + "." + kwargs['member'])
+ for arg in args:
+ print " " + str(arg)
+
+def catchall_hello_signals_handler(hello_string):
+ print "Received a hello signal and it says " + hello_string
+
+def catchall_testservice_interface_handler(hello_string, dbus_message):
+ print "com.example.TestService interface says " + hello_string + " when it sent signal " + dbus_message.get_member()
+
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SessionBus()
+ try:
+ object = bus.get_object("com.example.TestService","/com/example/TestService/object")
+
+ object.connect_to_signal("HelloSignal", hello_signal_handler, dbus_interface="com.example.TestService", arg0="Hello")
+ except dbus.DBusException:
+ traceback.print_exc()
+ print usage
+ sys.exit(1)
+
+ #lets make a catchall
+ bus.add_signal_receiver(catchall_signal_handler, interface_keyword='dbus_interface', member_keyword='member')
+
+ bus.add_signal_receiver(catchall_hello_signals_handler, dbus_interface = "com.example.TestService", signal_name = "HelloSignal")
+
+ bus.add_signal_receiver(catchall_testservice_interface_handler, dbus_interface = "com.example.TestService", message_keyword='dbus_message')
+
+ # Tell the remote object to emit the signal after a short delay
+ GLib.timeout_add(2000, emit_signal)
+
+ loop = GLib.MainLoop()
+ loop.run()
diff --git a/examples/gconf-proxy-client.py b/examples/gconf-proxy-client.py
new file mode 100755
index 0000000..e57d7cd
--- /dev/null
+++ b/examples/gconf-proxy-client.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+# Client for gconf-proxy-service2.py.
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import dbus
+
+gconf_key = "/desktop/gnome/file_views/icon_theme"
+
+bus = dbus.SessionBus()
+gconf_key_object = dbus.Interface(bus.get_object("com.example.GConfProxy", "/org/gnome/GConf" + gconf_key), "org.gnome.GConf")
+
+value = gconf_key_object.getString()
+
+print ("Value of GConf key %s is %s" % (gconf_key, value))
diff --git a/examples/gconf-proxy-service2.py b/examples/gconf-proxy-service2.py
new file mode 100755
index 0000000..b9c78ef
--- /dev/null
+++ b/examples/gconf-proxy-service2.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+
+# Example of implementing an entire subtree of objects using
+# a FallbackObject.
+#
+# This is not a particularly realistic example of real-world code any more,
+# because GConf now uses D-Bus internally itself, and is deprecated;
+# but it's a valid example of a FallbackObject.
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import dbus
+import dbus.mainloop.glib
+import dbus.service
+
+from gi.repository import GLib
+import gconf
+
+dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+# there is a real service called "org.gnome.GConf"; don't collide with it.
+name = dbus.service.BusName("com.example.GConfProxy", dbus.SessionBus())
+
+class GConfObject(dbus.service.FallbackObject):
+ def __init__(self):
+ dbus.service.FallbackObject.__init__(self, dbus.SessionBus(), '/org/gnome/GConf')
+ self.client = gconf.client_get_default()
+
+ @dbus.service.method("org.gnome.GConf", in_signature='', out_signature='s', rel_path_keyword='object_path')
+ def getString(self, object_path):
+ return self.client.get_string(object_path)
+
+ @dbus.service.method("org.gnome.GConf", in_signature='s', out_signature='', rel_path_keyword='object_path')
+ def setString(self, value, object_path):
+ self.client.set_string(object_path, value)
+
+ @dbus.service.method("org.gnome.GConf", in_signature='', out_signature='i', rel_path_keyword='object_path')
+ def getInt(self, object_path):
+ return self.client.get_int(object_path)
+
+ @dbus.service.method("org.gnome.GConf", in_signature='i', out_signature='', rel_path_keyword='object_path')
+ def setInt(self, value, object_path):
+ self.client.set_int(object_path, value)
+
+gconf_service = GConfObject()
+
+print ("GConf Proxy service started.")
+print ("Run 'gconf-proxy-client.py' to fetch a GConf key through the proxy...")
+
+mainloop = GLib.MainLoop()
+mainloop.run()
diff --git a/examples/list-system-services.py b/examples/list-system-services.py
new file mode 100755
index 0000000..a8a1829
--- /dev/null
+++ b/examples/list-system-services.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+"""Usage: python list-system-services.py [--session|--system]
+List services on the system bus (default) or the session bus."""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+
+import dbus
+
+def main(argv):
+ factory = dbus.SystemBus
+
+ if len(argv) > 2:
+ sys.exit(__doc__)
+ elif len(argv) == 2:
+ if argv[1] == '--session':
+ factory = dbus.SessionBus
+ elif argv[1] != '--system':
+ sys.exit(__doc__)
+
+ # Get a connection to the system or session bus as appropriate
+ # We're only using blocking calls, so don't actually need a main loop here
+ bus = factory()
+
+ # This could be done by calling bus.list_names(), but here's
+ # more or less what that means:
+
+ # Get a reference to the desktop bus' standard object, denoted
+ # by the path /org/freedesktop/DBus.
+ dbus_object = bus.get_object('org.freedesktop.DBus',
+ '/org/freedesktop/DBus')
+
+ # The object /org/freedesktop/DBus
+ # implements the 'org.freedesktop.DBus' interface
+ dbus_iface = dbus.Interface(dbus_object, 'org.freedesktop.DBus')
+
+ # One of the member functions in the org.freedesktop.DBus interface
+ # is ListNames(), which provides a list of all the other services
+ # registered on this bus. Call it, and print the list.
+ services = dbus_iface.ListNames()
+ services.sort()
+ for service in services:
+ print service
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/examples/unix-fd-client.py b/examples/unix-fd-client.py
new file mode 100755
index 0000000..ce1011d
--- /dev/null
+++ b/examples/unix-fd-client.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+
+import time
+
+usage = """Usage:
+python unix-fd-service.py <file name> &
+python unix-fd-client.py
+"""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+# Copyright (C) 2010 Signove <http://www.signove.com>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+from traceback import print_exc
+import os
+
+import dbus
+
+def main():
+ bus = dbus.SessionBus()
+
+ try:
+ remote_object = bus.get_object("com.example.SampleService",
+ "/SomeObject")
+
+ except dbus.DBusException:
+ print_exc()
+ print usage
+ sys.exit(1)
+
+ iface = dbus.Interface(remote_object, "com.example.SampleInterface")
+
+ # UnixFd is an opaque object that takes care of received fd
+ fd_object = iface.GetFd()
+ print fd_object
+
+ # Once we take the fd number, we are in charge of closing it!
+ fd = fd_object.take()
+ print fd
+
+ # We want to encapsulate the integer fd into a Python file or socket object
+ f = os.fdopen(fd, "r")
+
+ # If it were an UNIX socket we would do
+ # sk = socket.fromfd(fd, socket.AF_UNIX, socket.SOCK_STREAM)
+ # os.close(fd)
+ #
+ # fromfd() dup()s the descriptor so we need to close the original,
+ # otherwise it 'leaks' (stays open until program exits).
+
+ f.seek(0)
+ print f.read()
+
+if __name__ == '__main__':
+ main()
diff --git a/examples/unix-fd-service.py b/examples/unix-fd-service.py
new file mode 100755
index 0000000..8188647
--- /dev/null
+++ b/examples/unix-fd-service.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+
+usage = """Usage:
+python unix-fd-service.py <file name> &
+python unix-fd-client.py
+"""
+
+# Copyright (C) 2004-2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+# Copyright (C) 2010 Signove <http://www.signove.com>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from gi.repository import GLib
+
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import sys
+import random
+
+class SomeObject(dbus.service.Object):
+
+ counter = 0
+
+ @dbus.service.method("com.example.SampleInterface",
+ in_signature='', out_signature='h')
+ def GetFd(self):
+ self.counter = (self.counter + 1) % 3
+
+ if self.counter == 0:
+ print "sending UnixFd(filelike)"
+ return dbus.types.UnixFd(f)
+ elif self.counter == 1:
+ print "sending int"
+ return f.fileno()
+ else:
+ print "sending UnixFd(int)"
+ return dbus.types.UnixFd(f.fileno())
+
+if len(sys.argv) < 2:
+ print usage
+ sys.exit(1)
+
+f = file(sys.argv[1], "r")
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ session_bus = dbus.SessionBus()
+ name = dbus.service.BusName("com.example.SampleService", session_bus)
+ object = SomeObject(session_bus, '/SomeObject')
+
+ mainloop = GLib.MainLoop()
+ print "Running fd service."
+ print usage
+ mainloop.run()
diff --git a/include/dbus/dbus-python.h b/include/dbus/dbus-python.h
new file mode 100644
index 0000000..d247081
--- /dev/null
+++ b/include/dbus/dbus-python.h
@@ -0,0 +1,119 @@
+/* C API for _dbus_bindings, used by _dbus_glib_bindings and any third-party
+ * main loop integration which might happen in future.
+ *
+ * This file is currently Python-version-independent - please keep it that way.
+ *
+ * Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef DBUS_PYTHON_H
+#define DBUS_PYTHON_H
+
+#include <Python.h>
+#include <dbus/dbus.h>
+
+#if PY_MAJOR_VERSION >= 3
+#define PY3
+#define PYDBUS_CAPSULE_NAME "_dbus_bindings._C_API"
+#endif
+
+DBUS_BEGIN_DECLS
+
+typedef void (*_dbus_py_func_ptr)(void);
+
+typedef dbus_bool_t (*_dbus_py_conn_setup_func)(DBusConnection *, void *);
+typedef dbus_bool_t (*_dbus_py_srv_setup_func)(DBusServer *, void *);
+typedef void (*_dbus_py_free_func)(void *);
+
+#define DBUS_BINDINGS_API_COUNT 3
+
+#ifdef INSIDE_DBUS_PYTHON_BINDINGS
+
+extern DBusConnection *DBusPyConnection_BorrowDBusConnection(PyObject *);
+extern PyObject *DBusPyNativeMainLoop_New4(_dbus_py_conn_setup_func,
+ _dbus_py_srv_setup_func,
+ _dbus_py_free_func,
+ void *);
+
+#else
+
+static PyObject *_dbus_bindings_module = NULL;
+static _dbus_py_func_ptr *dbus_bindings_API;
+
+#define DBusPyConnection_BorrowDBusConnection \
+ (*(DBusConnection *(*)(PyObject *))dbus_bindings_API[1])
+#define DBusPyNativeMainLoop_New4 \
+ ((PyObject *(*)(_dbus_py_conn_setup_func, _dbus_py_srv_setup_func, \
+ _dbus_py_free_func, void *))dbus_bindings_API[2])
+
+static int
+import_dbus_bindings(const char *this_module_name)
+{
+ PyObject *c_api;
+ int count;
+
+ _dbus_bindings_module = PyImport_ImportModule("_dbus_bindings");
+ if (!_dbus_bindings_module) {
+ return -1;
+ }
+ c_api = PyObject_GetAttrString(_dbus_bindings_module, "_C_API");
+ if (c_api == NULL) return -1;
+#ifdef PY3
+ dbus_bindings_API = NULL;
+ if (PyCapsule_IsValid(c_api, PYDBUS_CAPSULE_NAME)) {
+ dbus_bindings_API = (_dbus_py_func_ptr *)PyCapsule_GetPointer(
+ c_api, PYDBUS_CAPSULE_NAME);
+ }
+ Py_CLEAR(c_api);
+ if (!dbus_bindings_API) {
+ PyErr_SetString(PyExc_RuntimeError, "C API is not a PyCapsule");
+ return -1;
+ }
+#else
+ if (PyCObject_Check(c_api)) {
+ dbus_bindings_API = (_dbus_py_func_ptr *)PyCObject_AsVoidPtr(c_api);
+ }
+ else {
+ Py_DECREF(c_api);
+ PyErr_SetString(PyExc_RuntimeError, "C API is not a PyCObject");
+ return -1;
+ }
+ Py_DECREF (c_api);
+#endif
+ count = *(int *)dbus_bindings_API[0];
+ if (count < DBUS_BINDINGS_API_COUNT) {
+ PyErr_Format(PyExc_RuntimeError,
+ "_dbus_bindings has API version %d but %s needs "
+ "_dbus_bindings API version at least %d",
+ count, this_module_name,
+ DBUS_BINDINGS_API_COUNT);
+ return -1;
+ }
+ return 0;
+}
+
+#endif
+
+DBUS_END_DECLS
+
+#endif
diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4
new file mode 100644
index 0000000..44dbd83
--- /dev/null
+++ b/m4/ax_python_devel.m4
@@ -0,0 +1,327 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_python_devel.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PYTHON_DEVEL([version])
+#
+# DESCRIPTION
+#
+# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it
+# in your configure.ac.
+#
+# This macro checks for Python and tries to get the include path to
+# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LIBS) output
+# variables. It also exports $(PYTHON_EXTRA_LIBS) and
+# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code.
+#
+# You can search for some particular version of Python by passing a
+# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please
+# note that you *have* to pass also an operator along with the version to
+# match, and pay special attention to the single quotes surrounding the
+# version number. Don't use "PYTHON_VERSION" for this: that environment
+# variable is declared as precious and thus reserved for the end-user.
+#
+# This macro should work for all versions of Python >= 2.1.0. As an end
+# user, you can disable the check for the python version by setting the
+# PYTHON_NOVERSIONCHECK environment variable to something else than the
+# empty string.
+#
+# If you need to use this macro for an older Python version, please
+# contact the authors. We're always open for feedback.
+#
+# LICENSE
+#
+# Copyright (c) 2009 Sebastian Huber <sebastian-huber@web.de>
+# Copyright (c) 2009 Alan W. Irwin
+# Copyright (c) 2009 Rafael Laboissiere <rafael@laboissiere.net>
+# Copyright (c) 2009 Andrew Collier
+# Copyright (c) 2009 Matteo Settenvini <matteo@member.fsf.org>
+# Copyright (c) 2009 Horst Knorr <hk_classes@knoda.org>
+# Copyright (c) 2013 Daniel Mullner <muellner@math.stanford.edu>
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# This program 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 General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 21
+
+AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
+AC_DEFUN([AX_PYTHON_DEVEL],[
+ #
+ # Allow the use of a (user set) custom python version
+ #
+ AC_ARG_VAR([PYTHON_VERSION],[The installed Python
+ version to use, for example '2.3'. This string
+ will be appended to the Python interpreter
+ canonical name.])
+
+ AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
+ if test -z "$PYTHON"; then
+ AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for a version of Python >= 2.1.0
+ #
+ AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
+ ac_supports_python_ver=`$PYTHON -c "import sys; \
+ ver = sys.version.split ()[[0]]; \
+ print (ver >= '2.1.0')"`
+ if test "$ac_supports_python_ver" != "True"; then
+ if test -z "$PYTHON_NOVERSIONCHECK"; then
+ AC_MSG_RESULT([no])
+ AC_MSG_FAILURE([
+This version of the AC@&t@_PYTHON_DEVEL macro
+doesn't work properly with versions of Python before
+2.1.0. You may need to re-run configure, setting the
+variables PYTHON_CPPFLAGS, PYTHON_LIBS, PYTHON_SITE_PKG,
+PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
+Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
+to something else than an empty string.
+])
+ else
+ AC_MSG_RESULT([skip at user request])
+ fi
+ else
+ AC_MSG_RESULT([yes])
+ fi
+
+ #
+ # if the macro parameter ``version'' is set, honour it
+ #
+ if test -n "$1"; then
+ AC_MSG_CHECKING([for a version of Python $1])
+ ac_supports_python_ver=`$PYTHON -c "import sys; \
+ ver = sys.version.split ()[[0]]; \
+ print (ver $1)"`
+ if test "$ac_supports_python_ver" = "True"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([this package requires Python $1.
+If you have it installed, but it isn't the default Python
+interpreter in your system path, please pass the PYTHON_VERSION
+variable to configure. See ``configure --help'' for reference.
+])
+ PYTHON_VERSION=""
+ fi
+ fi
+
+ #
+ # Check if you have distutils, else fail
+ #
+ AC_MSG_CHECKING([for the distutils Python package])
+ ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+ if test $? -eq 0; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot import Python module "distutils".
+Please check your Python installation. The error was:
+$ac_distutils_result])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for Python include path
+ #
+ AC_MSG_CHECKING([for Python include path])
+ if test -z "$PYTHON_CPPFLAGS"; then
+ python_path=`$PYTHON -c "import distutils.sysconfig; \
+ print (distutils.sysconfig.get_python_inc ());"`
+ plat_python_path=`$PYTHON -c "import distutils.sysconfig; \
+ print (distutils.sysconfig.get_python_inc (plat_specific=1));"`
+ if test -n "${python_path}"; then
+ if test "${plat_python_path}" != "${python_path}"; then
+ python_path="-I$python_path -I$plat_python_path"
+ else
+ python_path="-I$python_path"
+ fi
+ fi
+ PYTHON_CPPFLAGS=$python_path
+ fi
+ AC_MSG_RESULT([$PYTHON_CPPFLAGS])
+ AC_SUBST([PYTHON_CPPFLAGS])
+
+ #
+ # Check for Python library path
+ #
+ AC_MSG_CHECKING([for Python library path])
+ if test -z "$PYTHON_LIBS"; then
+ # (makes two attempts to ensure we've got a version number
+ # from the interpreter)
+ ac_python_version=`cat<<EOD | $PYTHON -
+
+# join all versioning strings, on some systems
+# major/minor numbers could be in different list elements
+from distutils.sysconfig import *
+e = get_config_var('VERSION')
+if e is not None:
+ print(e)
+EOD`
+
+ if test -z "$ac_python_version"; then
+ if test -n "$PYTHON_VERSION"; then
+ ac_python_version=$PYTHON_VERSION
+ else
+ ac_python_version=`$PYTHON -c "import sys; \
+ print (sys.version[[:3]])"`
+ fi
+ fi
+
+ # Make the versioning information available to the compiler
+ AC_DEFINE_UNQUOTED([HAVE_PYTHON], ["$ac_python_version"],
+ [If available, contains the Python version number currently in use.])
+
+ # First, the library directory:
+ ac_python_libdir=`cat<<EOD | $PYTHON -
+
+# There should be only one
+import distutils.sysconfig
+e = distutils.sysconfig.get_config_var('LIBDIR')
+if e is not None:
+ print (e)
+EOD`
+
+ # Now, for the library:
+ ac_python_library=`cat<<EOD | $PYTHON -
+
+import distutils.sysconfig
+c = distutils.sysconfig.get_config_vars()
+if 'LDVERSION' in c:
+ print ('python'+c[['LDVERSION']])
+else:
+ print ('python'+c[['VERSION']])
+EOD`
+
+ # This small piece shamelessly adapted from PostgreSQL python macro;
+ # credits goes to momjian, I think. I'd like to put the right name
+ # in the credits, if someone can point me in the right direction... ?
+ #
+ if test -n "$ac_python_libdir" -a -n "$ac_python_library"
+ then
+ # use the official shared library
+ ac_python_library=`echo "$ac_python_library" | sed "s/^lib//"`
+ PYTHON_LIBS="-L$ac_python_libdir -l$ac_python_library"
+ else
+ # old way: use libpython from python_configdir
+ ac_python_libdir=`$PYTHON -c \
+ "from distutils.sysconfig import get_python_lib as f; \
+ import os; \
+ print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
+ PYTHON_LIBS="-L$ac_python_libdir -lpython$ac_python_version"
+ fi
+
+ if test -z "PYTHON_LIBS"; then
+ AC_MSG_ERROR([
+ Cannot determine location of your Python DSO. Please check it was installed with
+ dynamic libraries enabled, or try setting PYTHON_LIBS by hand.
+ ])
+ fi
+ fi
+ AC_MSG_RESULT([$PYTHON_LIBS])
+ AC_SUBST([PYTHON_LIBS])
+
+ #
+ # Check for site packages
+ #
+ AC_MSG_CHECKING([for Python site-packages path])
+ if test -z "$PYTHON_SITE_PKG"; then
+ PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+ print (distutils.sysconfig.get_python_lib(0,0));"`
+ fi
+ AC_MSG_RESULT([$PYTHON_SITE_PKG])
+ AC_SUBST([PYTHON_SITE_PKG])
+
+ #
+ # libraries which must be linked in when embedding
+ #
+ AC_MSG_CHECKING(python extra libraries)
+ if test -z "$PYTHON_EXTRA_LIBS"; then
+ PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print (conf('LIBS') + ' ' + conf('SYSLIBS'))"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
+ AC_SUBST(PYTHON_EXTRA_LIBS)
+
+ #
+ # linking flags needed when embedding
+ #
+ AC_MSG_CHECKING(python extra linking flags)
+ if test -z "$PYTHON_EXTRA_LDFLAGS"; then
+ PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print (conf('LINKFORSHARED'))"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
+ AC_SUBST(PYTHON_EXTRA_LDFLAGS)
+
+ #
+ # final check to see if everything compiles alright
+ #
+ AC_MSG_CHECKING([consistency of all components of python development environment])
+ # save current global flags
+ ac_save_LIBS="$LIBS"
+ ac_save_LDFLAGS="$LDFLAGS"
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ LIBS="$ac_save_LIBS $PYTHON_LIBS $PYTHON_EXTRA_LIBS $PYTHON_EXTRA_LIBS"
+ LDFLAGS="$ac_save_LDFLAGS $PYTHON_EXTRA_LDFLAGS"
+ CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
+ AC_LANG_PUSH([C])
+ AC_LINK_IFELSE([
+ AC_LANG_PROGRAM([[#include <Python.h>]],
+ [[Py_Initialize();]])
+ ],[pythonexists=yes],[pythonexists=no])
+ AC_LANG_POP([C])
+ # turn back to default flags
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBS="$ac_save_LIBS"
+ LDFLAGS="$ac_save_LDFLAGS"
+
+ AC_MSG_RESULT([$pythonexists])
+
+ if test ! "x$pythonexists" = "xyes"; then
+ AC_MSG_FAILURE([
+ Could not link test program to Python. Maybe the main Python library has been
+ installed in some non-standard library path. If so, pass it to configure,
+ via the LIBS environment variable.
+ Example: ./configure LIBS="-L/usr/non-standard-path/python/lib"
+ ============================================================================
+ ERROR!
+ You probably have to install the development version of the Python package
+ for your distribution. The exact name of this package varies among them.
+ ============================================================================
+ ])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # all done!
+ #
+])
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..fefe403
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+# Copyright © 2016 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from distutils.dir_util import copy_tree, mkpath
+from distutils.file_util import copy_file
+from setuptools.dist import Distribution
+from setuptools import setup, Extension
+import os
+import subprocess
+import sys
+
+if os.path.exists('.version'):
+ version = open('.version').read().strip()
+else:
+ version = subprocess.check_output(['autoconf', '--trace', 'AC_INIT:$2',
+ 'configure.ac']).decode('utf-8').strip()
+
+class Build(Distribution().get_command_class('build')):
+ """Dummy version of distutils build which runs an Autotools build system
+ instead.
+ """
+
+ def run(self):
+ srcdir = os.getcwd()
+ builddir = os.path.join(srcdir, self.build_temp)
+ configure = os.path.join(srcdir, 'configure')
+ mkpath(builddir)
+
+ if not os.path.exists(configure):
+ configure = os.path.join(srcdir, 'autogen.sh')
+
+ subprocess.check_call([
+ configure,
+ '--disable-maintainer-mode',
+ 'PYTHON=' + sys.executable,
+ # Put the documentation, etc. out of the way: we only want
+ # the Python code and extensions
+ '--prefix=' + os.path.join(builddir, 'prefix'),
+ ],
+ cwd=builddir)
+ make_args = [
+ 'pythondir=' + os.path.join(srcdir, self.build_lib),
+ 'pyexecdir=' + os.path.join(srcdir, self.build_lib),
+ ]
+ subprocess.check_call(['make', '-C', builddir] + make_args)
+ subprocess.check_call(['make', '-C', builddir, 'install'] + make_args)
+
+class BuildExt(Distribution().get_command_class('build_ext')):
+ def run(self):
+ pass
+
+class BuildPy(Distribution().get_command_class('build_py')):
+ def run(self):
+ pass
+
+dbus_bindings = Extension('_dbus_bindings',
+ sources=['dbus_bindings/module.c'])
+dbus_glib_bindings = Extension('_dbus_glib_bindings',
+ sources=['dbus_glib_bindings/module.c'])
+
+setup(
+ name='dbus-python',
+ version=version,
+ description='Python bindings for libdbus',
+ long_description=open('README').read(),
+ maintainer='The D-Bus maintainers',
+ maintainer_email='dbus@lists.freedesktop.org',
+ download_url='http://dbus.freedesktop.org/releases/dbus-python/',
+ url='http://www.freedesktop.org/wiki/Software/DBusBindings/#python',
+ packages=['dbus'],
+ ext_modules=[dbus_bindings, dbus_glib_bindings],
+ license='Expat (MIT/X11)',
+ classifiers=[
+ 'Development Status :: 7 - Inactive',
+ 'License :: OSI Approved :: MIT License',
+ 'Programming Language :: C',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: Implementation :: CPython',
+ 'Topic :: Software Development :: Object Brokering',
+ ],
+ cmdclass={
+ 'build': Build,
+ 'build_py': BuildPy,
+ 'build_ext': BuildExt,
+ },
+ tests_require=['tap.py'],
+)
diff --git a/test/TestSuitePythonService.service.in b/test/TestSuitePythonService.service.in
new file mode 100644
index 0000000..b9a96df
--- /dev/null
+++ b/test/TestSuitePythonService.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.DBus.TestSuitePythonService
+Exec=/bin/bash -c "@PYTHON@ @G_TEST_SRCDIR@/test/test-service.py"
diff --git a/test/cross-test-client.py b/test/cross-test-client.py
new file mode 100755
index 0000000..cc96023
--- /dev/null
+++ b/test/cross-test-client.py
@@ -0,0 +1,421 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import print_function, unicode_literals
+import logging
+
+try:
+ from gi.repository import GObject as gobject
+except ImportError:
+ raise SystemExit(77)
+
+from dbus import (
+ Array, Boolean, Byte, ByteArray, Double, Int16, Int32, Int64,
+ Interface, SessionBus, String, UInt16, UInt32, UInt64)
+from dbus._compat import is_py2, is_py3
+import dbus.glib
+
+if is_py2:
+ from dbus import UTF8String
+
+from crosstest import (
+ CROSS_TEST_BUS_NAME, CROSS_TEST_PATH, INTERFACE_CALLBACK_TESTS,
+ INTERFACE_SIGNAL_TESTS, INTERFACE_SINGLE_TESTS, INTERFACE_TESTS,
+ SignalTestsImpl)
+
+if is_py3:
+ def make_long(n):
+ return n
+else:
+ def make_long(n):
+ return long(n)
+
+
+logging.basicConfig()
+logging.getLogger().setLevel(1)
+logger = logging.getLogger('cross-test-client')
+
+
+class Client(SignalTestsImpl):
+ fail_id = 0
+ expected = set()
+
+ def quit(self):
+ for x in self.expected:
+ self.fail_id += 1
+ print("%s fail %d" % (x, self.fail_id))
+ s = "report %d: reply to %s didn't arrive" % (self.fail_id, x)
+ print(s)
+ logger.error(s)
+ logger.info("asking server to Exit")
+ Interface(self.obj, INTERFACE_TESTS).Exit(reply_handler=self.quit_reply_handler, error_handler=self.quit_error_handler)
+
+ def quit_reply_handler(self):
+ logger.info("server says it will exit")
+ loop.quit()
+
+ def quit_error_handler(self, e):
+ logger.error("error telling server to quit: %s %s",
+ e.__class__, e)
+ loop.quit()
+
+ @dbus.service.method(INTERFACE_CALLBACK_TESTS, 'qd')
+ def Response(self, input1, input2):
+ logger.info("signal/callback: Response received (%r,%r)",
+ input1, input2)
+ self.expected.discard('%s.Trigger' % INTERFACE_SIGNAL_TESTS)
+ if (input1, input2) != (42, 23):
+ self.fail_id += 1
+ print("%s.Trigger fail %d" %
+ (INTERFACE_SIGNAL_TESTS, self.fail_id))
+ s = ("report %d: expected (42,23), got %r"
+ % (self.fail_id, (input1, input2)))
+ logger.error(s)
+ print(s)
+ else:
+ print("%s.Trigger pass" % INTERFACE_SIGNAL_TESTS)
+ self.quit()
+
+ def assert_method_matches(self, interface, check_fn, check_arg, member,
+ *args):
+ if_obj = Interface(self.obj, interface)
+ method = getattr(if_obj, member)
+ try:
+ real_ret = method(*args)
+ except Exception as e:
+ self.fail_id += 1
+ print("%s.%s fail %d" % (interface, member, self.fail_id))
+ s = ("report %d: %s.%s%r: raised %r \"%s\""
+ % (self.fail_id, interface, member, args, e, e))
+ print(s)
+ logger.error(s)
+ __import__('traceback').print_exc()
+ return
+ try:
+ check_fn(real_ret, check_arg)
+ except Exception as e:
+ self.fail_id += 1
+ print("%s.%s fail %d" % (interface, member, self.fail_id))
+ s = ("report %d: %s.%s%r: %s"
+ % (self.fail_id, interface, member, args, e))
+ print(s)
+ logger.error(s)
+ return
+ print("%s.%s pass" % (interface, member))
+
+ def assert_method_eq(self, interface, ret, member, *args):
+ def equals(real_ret, exp):
+ if real_ret != exp:
+ raise AssertionError('expected %r of class %s, got %r of class %s' % (exp, exp.__class__, real_ret, real_ret.__class__))
+ if real_ret != exp:
+ raise AssertionError('expected %r, got %r' % (exp, real_ret))
+ if not isinstance(exp, (tuple, type(None))):
+ if real_ret.variant_level != getattr(exp, 'variant_level', 0):
+ raise AssertionError('expected variant_level=%d, got %r with level %d'
+ % (getattr(exp, 'variant_level', 0), real_ret,
+ real_ret.variant_level))
+ if isinstance(exp, list) or isinstance(exp, tuple):
+ for i in range(len(exp)):
+ try:
+ equals(real_ret[i], exp[i])
+ except AssertionError as e:
+ if not isinstance(e.args, tuple):
+ e.args = (e.args,)
+ e.args = e.args + ('(at position %d in sequence)' % i,)
+ raise e
+ elif isinstance(exp, dict):
+ for k in exp:
+ try:
+ equals(real_ret[k], exp[k])
+ except AssertionError as e:
+ if not isinstance(e.args, tuple):
+ e.args = (e.args,)
+ e.args = e.args + ('(at key %r in dict)' % k,)
+ raise e
+ self.assert_method_matches(interface, equals, ret, member, *args)
+
+ def assert_InvertMapping_eq(self, interface, expected, member, mapping):
+ def check(real_ret, exp):
+ for key in exp:
+ if key not in real_ret:
+ raise AssertionError('missing key %r' % key)
+ for key in real_ret:
+ if key not in exp:
+ raise AssertionError('unexpected key %r' % key)
+ got = list(real_ret[key])
+ wanted = list(exp[key])
+ got.sort()
+ wanted.sort()
+ if got != wanted:
+ raise AssertionError('expected %r => %r, got %r'
+ % (key, wanted, got))
+ self.assert_method_matches(interface, check, expected, member, mapping)
+
+ def triggered_cb(self, param, sender_path):
+ logger.info("method/signal: Triggered(%r) by %r",
+ param, sender_path)
+ self.expected.discard('%s.Trigger' % INTERFACE_TESTS)
+ if sender_path != '/Where/Ever':
+ self.fail_id += 1
+ print("%s.Trigger fail %d" % (INTERFACE_TESTS, self.fail_id))
+ s = ("report %d: expected signal from /Where/Ever, got %r"
+ % (self.fail_id, sender_path))
+ print(s)
+ logger.error(s)
+ elif param != 42:
+ self.fail_id += 1
+ print("%s.Trigger fail %d" % (INTERFACE_TESTS, self.fail_id))
+ s = ("report %d: expected signal param 42, got %r"
+ % (self.fail_id, param))
+ print(s)
+ logger.error(s)
+ else:
+ print("%s.Trigger pass" % INTERFACE_TESTS)
+
+ def trigger_returned_cb(self):
+ logger.info('method/signal: Trigger() returned')
+ # Callback tests
+ logger.info("signal/callback: Emitting signal to trigger callback")
+ self.expected.add('%s.Trigger' % INTERFACE_SIGNAL_TESTS)
+ self.Trigger(UInt16(42), 23.0)
+ logger.info("signal/callback: Emitting signal returned")
+
+ def run_client(self):
+ bus = SessionBus()
+ obj = bus.get_object(CROSS_TEST_BUS_NAME, CROSS_TEST_PATH)
+ self.obj = obj
+
+ self.run_synchronous_tests(obj)
+
+ # Signal tests
+ logger.info("Binding signal handler for Triggered")
+ # FIXME: doesn't seem to work when going via the Interface method
+ # FIXME: should be possible to ask the proxy object for its
+ # bus name
+ bus.add_signal_receiver(self.triggered_cb, 'Triggered',
+ INTERFACE_SIGNAL_TESTS,
+ CROSS_TEST_BUS_NAME,
+ path_keyword='sender_path')
+ logger.info("method/signal: Triggering signal")
+ self.expected.add('%s.Trigger' % INTERFACE_TESTS)
+ Interface(obj, INTERFACE_TESTS).Trigger(
+ '/Where/Ever', dbus.UInt64(42),
+ reply_handler=self.trigger_returned_cb,
+ error_handler=self.trigger_error_handler)
+
+ def trigger_error_handler(self, e):
+ logger.error("method/signal: %s %s", e.__class__, e)
+ Interface(self.obj, INTERFACE_TESTS).Exit()
+ self.quit()
+
+ def run_synchronous_tests(self, obj):
+ # We can't test that coercion works correctly unless the server has
+ # sent us introspection data. Java doesn't :-/
+ have_signatures = True
+
+ # "Single tests"
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_SINGLE_TESTS, 6, 'Sum', [1, 2, 3])
+ self.assert_method_eq(INTERFACE_SINGLE_TESTS, 6, 'Sum',
+ [b'\x01', b'\x02', b'\x03'])
+ self.assert_method_eq(INTERFACE_SINGLE_TESTS, 6, 'Sum', [Byte(1), Byte(2), Byte(3)])
+ self.assert_method_eq(INTERFACE_SINGLE_TESTS, 6, 'Sum', ByteArray(b'\x01\x02\x03'))
+
+ # Main tests
+ self.assert_method_eq(INTERFACE_TESTS, String('foo', variant_level=1), 'Identity', String('foo'))
+ if is_py2:
+ self.assert_method_eq(INTERFACE_TESTS, String('foo', variant_level=1), 'Identity', UTF8String('foo'))
+ self.assert_method_eq(INTERFACE_TESTS, Byte(42, variant_level=1), 'Identity', Byte(42))
+ self.assert_method_eq(INTERFACE_TESTS, Byte(42, variant_level=23), 'Identity', Byte(42, variant_level=23))
+ self.assert_method_eq(INTERFACE_TESTS, Double(42.5, variant_level=1), 'Identity', 42.5)
+ self.assert_method_eq(INTERFACE_TESTS, Double(-42.5, variant_level=1), 'Identity', -42.5)
+
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, String('foo', variant_level=1), 'Identity', 'foo')
+ self.assert_method_eq(INTERFACE_TESTS, Byte(42, variant_level=1), 'Identity', Byte(42))
+ self.assert_method_eq(INTERFACE_TESTS, Double(42.5, variant_level=1), 'Identity', Double(42.5))
+ self.assert_method_eq(INTERFACE_TESTS, Double(-42.5, variant_level=1), 'Identity', -42.5)
+
+ for i in (0, 42, 255):
+ self.assert_method_eq(INTERFACE_TESTS, Byte(i), 'IdentityByte', Byte(i))
+ for i in (True, False):
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityBool', i)
+
+ for i in (-0x8000, 0, 42, 0x7fff):
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityInt16', Int16(i))
+ for i in (0, 42, 0xffff):
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityUInt16', UInt16(i))
+ for i in (-0x7fffffff-1, 0, 42, 0x7fffffff):
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityInt32', Int32(i))
+ for i in (0, 42, 0xffffffff):
+ i = make_long(i)
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityUInt32', UInt32(i))
+ MANY = 1
+ for n in (0x8000, 0x10000, 0x10000, 0x10000):
+ MANY *= make_long(n)
+ for i in (-MANY, 0, 42, MANY-1):
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityInt64', Int64(i))
+ for i in (0, 42, 2*MANY - 1):
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityUInt64', UInt64(i))
+
+ self.assert_method_eq(INTERFACE_TESTS, 42.3, 'IdentityDouble', 42.3)
+ for i in ('', 'foo'):
+ self.assert_method_eq(INTERFACE_TESTS, i, 'IdentityString', i)
+ for i in ('\xa9', b'\xc2\xa9'):
+ self.assert_method_eq(INTERFACE_TESTS, '\xa9', 'IdentityString', i)
+
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, Byte(0x42),
+ 'IdentityByte', b'\x42')
+ self.assert_method_eq(INTERFACE_TESTS, True, 'IdentityBool', 42)
+ self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityInt16', 42)
+ self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityUInt16', 42)
+ self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityInt32', 42)
+ self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityUInt32', 42)
+ self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityInt64', 42)
+ self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityUInt64', 42)
+ self.assert_method_eq(INTERFACE_TESTS, 42.0, 'IdentityDouble', 42)
+
+ self.assert_method_eq(INTERFACE_TESTS, [Byte(b'\x01', variant_level=1),
+ Byte(b'\x02', variant_level=1),
+ Byte(b'\x03', variant_level=1)],
+ 'IdentityArray',
+ Array([Byte(b'\x01'),
+ Byte(b'\x02'),
+ Byte(b'\x03')],
+ signature='v'))
+
+ self.assert_method_eq(INTERFACE_TESTS, [Int32(1, variant_level=1),
+ Int32(2, variant_level=1),
+ Int32(3, variant_level=1)],
+ 'IdentityArray',
+ Array([Int32(1),
+ Int32(2),
+ Int32(3)],
+ signature='v'))
+ self.assert_method_eq(INTERFACE_TESTS, [String('a', variant_level=1),
+ String('b', variant_level=1),
+ String('c', variant_level=1)],
+ 'IdentityArray',
+ Array([String('a'),
+ String('b'),
+ String('c')],
+ signature='v'))
+
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, [Byte(b'\x01', variant_level=1),
+ Byte(b'\x02', variant_level=1),
+ Byte(b'\x03', variant_level=1)],
+ 'IdentityArray',
+ ByteArray(b'\x01\x02\x03'))
+ self.assert_method_eq(INTERFACE_TESTS, [Int32(1, variant_level=1),
+ Int32(2, variant_level=1),
+ Int32(3, variant_level=1)],
+ 'IdentityArray',
+ [Int32(1),
+ Int32(2),
+ Int32(3)])
+ self.assert_method_eq(INTERFACE_TESTS, [String('a', variant_level=1),
+ String('b', variant_level=1),
+ String('c', variant_level=1)],
+ 'IdentityArray',
+ ['a','b','c'])
+
+ self.assert_method_eq(INTERFACE_TESTS,
+ [Byte(1), Byte(2), Byte(3)],
+ 'IdentityByteArray',
+ ByteArray(b'\x01\x02\x03'))
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3],
+ 'IdentityByteArray',
+ [b'\x01', b'\x02', b'\x03'])
+ self.assert_method_eq(INTERFACE_TESTS, [False,True], 'IdentityBoolArray', [False,True])
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, [False,True,True], 'IdentityBoolArray', [0,1,2])
+
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityInt16Array', [Int16(1),Int16(2),Int16(3)])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityUInt16Array', [UInt16(1),UInt16(2),UInt16(3)])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityInt32Array', [Int32(1),Int32(2),Int32(3)])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityUInt32Array', [UInt32(1),UInt32(2),UInt32(3)])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityInt64Array', [Int64(1),Int64(2),Int64(3)])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityUInt64Array', [UInt64(1),UInt64(2),UInt64(3)])
+
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityInt16Array', [1,2,3])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityUInt16Array', [1,2,3])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityInt32Array', [1,2,3])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityUInt32Array', [1,2,3])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityInt64Array', [1,2,3])
+ self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityUInt64Array', [1,2,3])
+
+ self.assert_method_eq(INTERFACE_TESTS, [1.0,2.5,3.1], 'IdentityDoubleArray', [1.0,2.5,3.1])
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, [1.0,2.5,3.1], 'IdentityDoubleArray', [1,2.5,3.1])
+ self.assert_method_eq(INTERFACE_TESTS, ['a','b','c'], 'IdentityStringArray', ['a','b','c'])
+ self.assert_method_eq(INTERFACE_TESTS, 6, 'Sum', [Int32(1),Int32(2),Int32(3)])
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, 6, 'Sum', [1,2,3])
+
+ self.assert_InvertMapping_eq(INTERFACE_TESTS, {'fps': ['unreal', 'quake'], 'rts': ['warcraft']}, 'InvertMapping', {'unreal': 'fps', 'quake': 'fps', 'warcraft': 'rts'})
+
+ self.assert_method_eq(INTERFACE_TESTS, ('a', 1, 2), 'DeStruct', ('a', UInt32(1), Int16(2)))
+ self.assert_method_eq(INTERFACE_TESTS, Array([String('x', variant_level=1)]),
+ 'Primitize', [String('x', variant_level=1)])
+ self.assert_method_eq(INTERFACE_TESTS, Array([String('x', variant_level=1)]),
+ 'Primitize', [String('x', variant_level=23)])
+ self.assert_method_eq(INTERFACE_TESTS,
+ Array([String('x', variant_level=1),
+ Byte(1, variant_level=1),
+ Byte(2, variant_level=1)]),
+ 'Primitize',
+ Array([String('x'), Byte(1), Byte(2)],
+ signature='v'))
+ self.assert_method_eq(INTERFACE_TESTS,
+ Array([String('x', variant_level=1),
+ Byte(1, variant_level=1),
+ Byte(2, variant_level=1)]),
+ 'Primitize',
+ Array([String('x'), Array([Byte(1), Byte(2)])],
+ signature='v'))
+ self.assert_method_eq(INTERFACE_TESTS, Boolean(False), 'Invert', True)
+ self.assert_method_eq(INTERFACE_TESTS, Boolean(True), 'Invert', False)
+ if have_signatures:
+ self.assert_method_eq(INTERFACE_TESTS, Boolean(False), 'Invert', 42)
+ self.assert_method_eq(INTERFACE_TESTS, Boolean(True), 'Invert', 0)
+
+
+if __name__ == '__main__':
+ # FIXME: should be possible to export objects without a bus name
+ if 0:
+ client = Client(dbus.SessionBus(), '/Client')
+ else:
+ # the Java cross test's interpretation is that the client should be
+ # at /Test too
+ client = Client(dbus.SessionBus(), '/Test')
+ gobject.idle_add(client.run_client)
+
+ loop = gobject.MainLoop()
+ logger.info("running...")
+ loop.run()
+ logger.info("main loop exited.")
diff --git a/test/cross-test-server.py b/test/cross-test-server.py
new file mode 100755
index 0000000..30edb1c
--- /dev/null
+++ b/test/cross-test-server.py
@@ -0,0 +1,343 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import print_function
+import logging
+
+try:
+ from gi.repository import GObject as gobject
+except ImportError:
+ raise SystemExit(77)
+
+import dbus.glib
+from dbus import SessionBus
+from dbus.service import BusName
+from dbus._compat import is_py2
+
+from crosstest import (
+ CROSS_TEST_BUS_NAME, CROSS_TEST_PATH, INTERFACE_CALLBACK_TESTS,
+ INTERFACE_SIGNAL_TESTS, INTERFACE_SINGLE_TESTS, INTERFACE_TESTS,
+ SignalTestsImpl)
+
+
+logging.basicConfig()
+logging.getLogger().setLevel(1)
+logger = logging.getLogger('cross-test-server')
+
+
+class VerboseSet(set):
+ def add(self, thing):
+ print('%s ok' % thing)
+ set.add(self, thing)
+
+
+objects = {}
+
+
+tested_things = VerboseSet()
+testable_things = [
+ INTERFACE_SINGLE_TESTS + '.Sum',
+ INTERFACE_TESTS + '.Identity',
+ INTERFACE_TESTS + '.IdentityByte',
+ INTERFACE_TESTS + '.IdentityBool',
+ INTERFACE_TESTS + '.IdentityInt16',
+ INTERFACE_TESTS + '.IdentityUInt16',
+ INTERFACE_TESTS + '.IdentityInt32',
+ INTERFACE_TESTS + '.IdentityUInt32',
+ INTERFACE_TESTS + '.IdentityInt64',
+ INTERFACE_TESTS + '.IdentityUInt64',
+ INTERFACE_TESTS + '.IdentityDouble',
+ INTERFACE_TESTS + '.IdentityString',
+ INTERFACE_TESTS + '.IdentityArray',
+ INTERFACE_TESTS + '.IdentityByteArray',
+ INTERFACE_TESTS + '.IdentityBoolArray',
+ INTERFACE_TESTS + '.IdentityInt16Array',
+ INTERFACE_TESTS + '.IdentityUInt16Array',
+ INTERFACE_TESTS + '.IdentityInt32Array',
+ INTERFACE_TESTS + '.IdentityUInt32Array',
+ INTERFACE_TESTS + '.IdentityInt64Array',
+ INTERFACE_TESTS + '.IdentityUInt64Array',
+ INTERFACE_TESTS + '.IdentityDoubleArray',
+ INTERFACE_TESTS + '.IdentityStringArray',
+ INTERFACE_TESTS + '.Sum',
+ INTERFACE_TESTS + '.InvertMapping',
+ INTERFACE_TESTS + '.DeStruct',
+ INTERFACE_TESTS + '.Primitize',
+ INTERFACE_TESTS + '.Trigger',
+ INTERFACE_TESTS + '.Exit',
+ INTERFACE_TESTS + '.Invert',
+ INTERFACE_SIGNAL_TESTS + '.Trigger',
+]
+
+
+class SingleTestsImpl(dbus.service.Object):
+
+ @dbus.service.method(INTERFACE_SINGLE_TESTS, 'ay', 'u')
+ def Sum(self, input):
+ tested_things.add(INTERFACE_SINGLE_TESTS + '.Sum')
+ u = sum(input)
+ logger.info('Sum of %r is %r', input, u)
+ return u
+
+
+class TestsImpl(dbus.service.Object):
+
+ def __init__(self, bus_name, service_path, exit_fn):
+ self._exit_fn = exit_fn
+ dbus.service.Object.__init__(self, bus_name, service_path)
+
+ @dbus.service.method(INTERFACE_TESTS, 'v', 'v')
+ def Identity(self, input):
+ tested_things.add(INTERFACE_TESTS + '.Identity')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'y', 'y')
+ def IdentityByte(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityByte')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'b', 'b')
+ def IdentityBool(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityBool')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'n', 'n')
+ def IdentityInt16(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityInt16')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'q', 'q')
+ def IdentityUInt16(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityUInt16')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'i', 'i')
+ def IdentityInt32(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityInt32')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'u', 'u')
+ def IdentityUInt32(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityUInt32')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'x', 'x')
+ def IdentityInt64(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityInt64')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 't', 't')
+ def IdentityUInt64(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityUInt64')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'd', 'd')
+ def IdentityDouble(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityDouble')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 's', 's')
+ def IdentityString(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityString')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'av', 'av')
+ def IdentityArray(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityArray')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'ay', 'ay')
+ def IdentityByteArray(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityByteArray')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'ab', 'ab')
+ def IdentityBoolArray(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityBoolArray')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'an', 'an')
+ def IdentityInt16Array(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityInt16Array')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'aq', 'aq')
+ def IdentityUInt16Array(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityUInt16Array')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'ai', 'ai')
+ def IdentityInt32Array(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityInt32Array')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'au', 'au')
+ def IdentityUInt32Array(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityUInt32Array')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'ax', 'ax')
+ def IdentityInt64Array(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityInt64Array')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'at', 'at')
+ def IdentityUInt64Array(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityUInt64Array')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'ad', 'ad')
+ def IdentityDoubleArray(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityDoubleArray')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'as', 'as')
+ def IdentityStringArray(self, input):
+ tested_things.add(INTERFACE_TESTS + '.IdentityStringArray')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'ai', 'x')
+ def Sum(self, input):
+ tested_things.add(INTERFACE_TESTS + '.Sum')
+ x = sum(input)
+ logger.info('Sum of %r is %r', input, x)
+ return x
+
+
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+
+ @dbus.service.method(INTERFACE_TESTS, 'a{ss}', 'a{sas}', **kwargs)
+ def InvertMapping(self, input):
+ tested_things.add(INTERFACE_TESTS + '.InvertMapping')
+ output = dbus.Dictionary({})
+ for k, v in input.items():
+ output.setdefault(v, []).append(k)
+ return output
+
+ @dbus.service.method(INTERFACE_TESTS, '(sun)', 'sun')
+ def DeStruct(self, input):
+ tested_things.add(INTERFACE_TESTS + '.DeStruct')
+ return input
+
+ @dbus.service.method(INTERFACE_TESTS, 'v', 'av')
+ def Primitize(self, input):
+ tested_things.add(INTERFACE_TESTS + '.Primitize')
+ return list(self.primitivize_helper(input))
+
+ def primitivize_helper(self, input):
+ if (isinstance(input, tuple) or isinstance(input, dbus.Struct)
+ or isinstance(input, list) or isinstance(input, dbus.Array)):
+ for x in input:
+ for y in self.primitivize_helper(x):
+ yield y
+ elif isinstance(input, dbus.ByteArray):
+ for x in input:
+ yield dbus.Byte(ord(x))
+ elif isinstance(input, dict) or isinstance(input, dbus.Dictionary):
+ for x in input:
+ for y in self.primitivize_helper(x):
+ yield y
+ for y in self.primitivize_helper(input[x]):
+ yield y
+ elif input.variant_level > 0:
+ yield input.__class__(input)
+ else:
+ yield input
+
+ @dbus.service.method(INTERFACE_TESTS, 'b', 'b')
+ def Invert(self, input):
+ tested_things.add(INTERFACE_TESTS + '.Invert')
+ return not input
+
+ @dbus.service.method(INTERFACE_TESTS, 'st', '',
+ connection_keyword='conn',
+ async_callbacks=('reply_cb', 'error_cb'),
+ **kwargs)
+ def Trigger(self, object, parameter, conn=None, reply_cb=None,
+ error_cb=None):
+ assert isinstance(object, str)
+ logger.info('method/signal: client wants me to emit Triggered(%r) from %r', parameter, object)
+ tested_things.add(INTERFACE_TESTS + '.Trigger')
+ gobject.idle_add(lambda: self.emit_Triggered_from(conn, object,
+ parameter,
+ reply_cb))
+
+ def emit_Triggered_from(self, conn, object, parameter, reply_cb):
+ assert isinstance(object, str)
+ logger.info('method/signal: Emitting Triggered(%r) from %r', parameter, object)
+ obj = objects.get(object, None)
+ if obj is None:
+ obj = SignalTestsImpl(conn, object)
+ objects[object] = obj
+ obj.Triggered(parameter)
+ logger.info('method/signal: Emitted Triggered')
+ reply_cb()
+ logger.info('method/signal: Sent reply for Tests.Trigger()')
+
+ @dbus.service.method(INTERFACE_TESTS, '', '')
+ def Exit(self):
+ logger.info('client wants me to Exit')
+ tested_things.add(INTERFACE_TESTS + '.Exit')
+ for x in testable_things:
+ if x not in tested_things:
+ print('%s untested' % x)
+ logger.info('will quit when idle')
+ gobject.idle_add(self._exit_fn)
+
+
+class Server(SingleTestsImpl, TestsImpl, SignalTestsImpl):
+
+ def triggered_by_client(self, parameter1, parameter2, sender, sender_path):
+ # Called when the client emits TestSignals.Trigger from any object.
+ logger.info('signal/callback: Triggered by client (%s:%s): (%r,%r)', sender, sender_path, parameter1, parameter2)
+ tested_things.add(INTERFACE_SIGNAL_TESTS + '.Trigger')
+ dbus.Interface(dbus.SessionBus().get_object(sender, sender_path),
+ INTERFACE_CALLBACK_TESTS).Response(parameter1, parameter2)
+ logger.info('signal/callback: Sent Response')
+
+
+
+if __name__ == '__main__':
+ bus = SessionBus()
+ bus_name = BusName(CROSS_TEST_BUS_NAME, bus=bus)
+ loop = gobject.MainLoop()
+ obj = Server(bus_name, CROSS_TEST_PATH, loop.quit)
+ objects[CROSS_TEST_PATH] = obj
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ bus.add_signal_receiver(obj.triggered_by_client,
+ signal_name='Trigger',
+ dbus_interface=INTERFACE_SIGNAL_TESTS,
+ named_service=None,
+ path=None,
+ sender_keyword='sender',
+ path_keyword='sender_path',
+ **kwargs)
+
+ logger.info("running...")
+ loop.run()
+ logger.info("main loop exited.")
diff --git a/test/crosstest.py b/test/crosstest.py
new file mode 100644
index 0000000..0020baf
--- /dev/null
+++ b/test/crosstest.py
@@ -0,0 +1,44 @@
+# Shared code for the cross-test.
+
+# Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import dbus.service
+
+INTERFACE_SINGLE_TESTS = 'org.freedesktop.DBus.Binding.SingleTests'
+INTERFACE_TESTS = 'org.freedesktop.DBus.Binding.Tests'
+INTERFACE_SIGNAL_TESTS = 'org.freedesktop.DBus.Binding.TestSignals'
+INTERFACE_CALLBACK_TESTS = 'org.freedesktop.DBus.Binding.TestCallbacks'
+
+CROSS_TEST_PATH = '/Test'
+CROSS_TEST_BUS_NAME = 'org.freedesktop.DBus.Binding.TestServer'
+
+
+# Exported by both the client and the server
+class SignalTestsImpl(dbus.service.Object):
+ @dbus.service.signal(INTERFACE_SIGNAL_TESTS, 't')
+ def Triggered(self, parameter):
+ pass
+
+ @dbus.service.signal(INTERFACE_SIGNAL_TESTS, 'qd')
+ def Trigger(self, parameter1, parameter2):
+ pass
diff --git a/test/dbus_py_test.c b/test/dbus_py_test.c
new file mode 100644
index 0000000..bd2399d
--- /dev/null
+++ b/test/dbus_py_test.c
@@ -0,0 +1,152 @@
+/* Test fixtures for dbus-python, based on _dbus_glib_bindings.
+ *
+ * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <Python.h>
+#include <dbus/dbus-python.h>
+
+#ifdef PY3
+PyMODINIT_FUNC PyInit_dbus_py_test(void);
+#else
+PyMODINIT_FUNC initdbus_py_test(void);
+#endif
+
+#if defined(__GNUC__)
+# if __GNUC__ >= 3
+# define UNUSED __attribute__((__unused__))
+# else
+# define UNUSED /*nothing*/
+# endif
+#else
+# define UNUSED /*nothing*/
+#endif
+
+static dbus_bool_t
+dbus_py_test_set_up_conn(DBusConnection *conn UNUSED, void *data UNUSED)
+{
+ PyErr_SetString(PyExc_ValueError, "Dummy error from UnusableMainLoop");
+ return 0;
+}
+
+static dbus_bool_t
+dbus_py_test_set_up_srv(DBusServer *srv UNUSED, void *data UNUSED)
+{
+ PyErr_SetString(PyExc_ValueError, "Dummy error from UnusableMainLoop");
+ return 0;
+}
+
+static void
+dbus_py_test_free(void *data UNUSED)
+{
+}
+
+static PyObject *
+dbus_test_native_mainloop(void)
+{
+ PyObject *loop = DBusPyNativeMainLoop_New4(dbus_py_test_set_up_conn,
+ dbus_py_test_set_up_srv,
+ dbus_py_test_free,
+ NULL);
+ return loop;
+}
+
+static PyObject *
+UnusableMainLoop (PyObject *always_null UNUSED, PyObject *args, PyObject *kwargs)
+{
+ PyObject *mainloop, *function, *result;
+ int set_as_default = 0;
+ static char *argnames[] = {"set_as_default", NULL};
+
+ if (PyTuple_Size(args) != 0) {
+ PyErr_SetString(PyExc_TypeError, "UnusableMainLoop() takes no "
+ "positional arguments");
+ return NULL;
+ }
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", argnames,
+ &set_as_default)) {
+ return NULL;
+ }
+
+ mainloop = dbus_test_native_mainloop();
+ if (mainloop && set_as_default) {
+ if (!_dbus_bindings_module) {
+ PyErr_SetString(PyExc_ImportError, "_dbus_bindings not imported");
+ Py_CLEAR(mainloop);
+ return NULL;
+ }
+ function = PyObject_GetAttrString(_dbus_bindings_module,
+ "set_default_main_loop");
+ if (!function) {
+ Py_CLEAR(mainloop);
+ return NULL;
+ }
+ result = PyObject_CallFunctionObjArgs(function, mainloop, NULL);
+ Py_CLEAR(function);
+ if (!result) {
+ Py_CLEAR(mainloop);
+ return NULL;
+ }
+ }
+ return mainloop;
+}
+
+static PyMethodDef module_functions[] = {
+ {"UnusableMainLoop", (PyCFunction)UnusableMainLoop,
+ METH_VARARGS|METH_KEYWORDS, "Return a main loop that fails to attach"},
+ {NULL, NULL, 0, NULL}
+};
+
+#ifdef PY3
+PyMODINIT_FUNC
+PyInit_dbus_py_test(void)
+{
+ static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "dbus_py_test", /* m_name */
+ NULL, /* m_doc */
+ -1, /* m_size */
+ module_functions, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+ };
+ if (import_dbus_bindings("dbus_py_test") < 0)
+ return NULL;
+
+ return PyModule_Create(&moduledef);
+}
+#else
+PyMODINIT_FUNC
+initdbus_py_test(void)
+{
+ PyObject *this_module;
+
+ if (import_dbus_bindings("dbus_py_test") < 0) return;
+ this_module = Py_InitModule3 ("dbus_py_test", module_functions, "");
+ if (!this_module) return;
+}
+#endif
+
+/* vim:set ft=c cino< sw=4 sts=4 et: */
diff --git a/test/import-repeatedly.c b/test/import-repeatedly.c
new file mode 100644
index 0000000..2df82e9
--- /dev/null
+++ b/test/import-repeatedly.c
@@ -0,0 +1,25 @@
+/* Regression test for https://bugs.freedesktop.org/show_bug.cgi?id=23831 */
+
+#include <stdio.h>
+
+#include <Python.h>
+
+int main(void)
+{
+ int i;
+
+ puts("1..1");
+
+ for (i = 0; i < 100; ++i) {
+ Py_Initialize();
+ if (PyRun_SimpleString("import dbus\n") != 0) {
+ puts("not ok 1 - there was an exception");
+ return 1;
+ }
+ Py_Finalize();
+ }
+
+ puts("ok 1 - was able to import dbus 100 times");
+
+ return 0;
+}
diff --git a/test/run-test.sh b/test/run-test.sh
new file mode 100755
index 0000000..da49918
--- /dev/null
+++ b/test/run-test.sh
@@ -0,0 +1,143 @@
+#! /bin/bash
+
+# Copyright (C) 2006 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2006-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+set -e
+
+failed=
+test_num=0
+
+echo "# DBUS_TOP_SRCDIR=$DBUS_TOP_SRCDIR"
+echo "# DBUS_TOP_BUILDDIR=$DBUS_TOP_BUILDDIR"
+echo "# PYTHONPATH=$PYTHONPATH"
+echo "# PYTHON=${PYTHON:=python}"
+
+if ! [ -d "$DBUS_TEST_TMPDIR" ]; then
+ DBUS_TEST_TMPDIR="$(mktemp -d)"
+ if ! [ -d "$DBUS_TEST_TMPDIR" ]; then
+ echo "Bail out! Failed to create temporary directory (install mktemp?)"
+ exit 1
+ fi
+fi
+
+if ! "$PYTHON" -c 'from gi.repository import GLib'; then
+ echo "1..0 # SKIP could not import python-gi"
+ exit 0
+fi
+
+ok () {
+ test_num=$(( $test_num + 1 ))
+ echo "ok $test_num - $*"
+}
+
+not_ok () {
+ test_num=$(( $test_num + 1 ))
+ echo "not ok $test_num - $*"
+}
+
+skip () {
+ test_num=$(( $test_num + 1 ))
+ echo "ok $test_num # SKIP - $*"
+}
+
+dbus-monitor > "$DBUS_TEST_TMPDIR"/monitor.log &
+
+#echo "running the examples"
+
+#$PYTHON "$DBUS_TOP_SRCDIR"/examples/example-service.py &
+#$PYTHON "$DBUS_TOP_SRCDIR"/examples/example-signal-emitter.py &
+#$PYTHON "$DBUS_TOP_SRCDIR"/examples/list-system-services.py --session ||
+# die "list-system-services.py --session failed!"
+#$PYTHON "$DBUS_TOP_SRCDIR"/examples/example-async-client.py ||
+# die "example-async-client failed!"
+#$PYTHON "$DBUS_TOP_SRCDIR"/examples/example-client.py --exit-service ||
+# die "example-client failed!"
+#$PYTHON "$DBUS_TOP_SRCDIR"/examples/example-signal-recipient.py --exit-service ||
+# die "example-signal-recipient failed!"
+
+echo "# running cross-test (for better diagnostics use mjj29's dbus-test)"
+
+$PYTHON "$DBUS_TOP_SRCDIR"/test/cross-test-server.py > "$DBUS_TEST_TMPDIR"/cross-server.log &
+cross_test_server_pid="$!"
+
+$PYTHON "$DBUS_TOP_SRCDIR"/test/wait-for-name.py org.freedesktop.DBus.Binding.TestServer >&2
+
+e=0
+$PYTHON "$DBUS_TOP_SRCDIR"/test/cross-test-client.py > "$DBUS_TEST_TMPDIR"/cross-client.log || e=$?
+echo "# test-client exit status: $e"
+
+if test "$e" = 77; then
+ skip "cross-test-client exited $e, marking as skipped"
+elif grep . "$DBUS_TEST_TMPDIR"/cross-client.log >/dev/null; then
+ ok "cross-test-client produced some output"
+else
+ not_ok "cross-test-client produced no output"
+fi
+
+if test "$e" = 77; then
+ skip "test-client exited $e, marking as skipped"
+elif grep . "$DBUS_TEST_TMPDIR"/cross-server.log >/dev/null; then
+ ok "cross-test-server produced some output"
+else
+ not_ok "cross-test-server produced no output"
+fi
+
+if grep fail "$DBUS_TEST_TMPDIR"/cross-client.log >&2; then
+ not_ok "cross-client reported failures"
+else
+ ok "cross-test client reported no failures"
+fi
+
+if grep untested "$DBUS_TEST_TMPDIR"/cross-server.log; then
+ not_ok "cross-server reported untested functions"
+else
+ ok "cross-test server reported no untested functions"
+fi
+
+echo "# waiting for cross-test server to exit"
+if wait "$cross_test_server_pid"; then
+ ok "cross-test server: exit status 0"
+else
+ not_ok "cross-test server: exit status $?"
+fi
+
+echo "# ==== client log ===="
+cat "$DBUS_TEST_TMPDIR"/cross-client.log | sed -e 's/^/# /'
+echo "# ==== end ===="
+
+echo "# ==== server log ===="
+cat "$DBUS_TEST_TMPDIR"/cross-server.log | sed -e 's/^/# /'
+echo "# ==== end ===="
+
+rm -f "$DBUS_TEST_TMPDIR"/test-service.log
+rm -f "$DBUS_TEST_TMPDIR"/cross-client.log
+rm -f "$DBUS_TEST_TMPDIR"/cross-server.log
+rm -f "$DBUS_TEST_TMPDIR"/monitor.log
+
+echo "1..$test_num"
+
+if test -n "$failed"; then
+ exit 1
+fi
+exit 0
diff --git a/test/test-client.py b/test/test-client.py
new file mode 100755
index 0000000..5a177b7
--- /dev/null
+++ b/test/test-client.py
@@ -0,0 +1,639 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2004 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import print_function
+import os
+import unittest
+import time
+import logging
+import weakref
+
+try:
+ from tap.runner import TAPTestRunner
+except ImportError:
+ print('1..0 # SKIP cannot import TAPTestRunner')
+ raise SystemExit(0)
+
+import dbus
+import _dbus_bindings
+import dbus.glib
+import dbus.service
+
+from dbus._compat import is_py2, is_py3
+
+try:
+ from gi.repository import GObject as gobject
+except ImportError:
+ print('1..0 # SKIP cannot import GObject')
+ raise SystemExit(0)
+
+logging.basicConfig()
+
+if 'DBUS_TEST_UNINSTALLED' in os.environ:
+ builddir = os.path.normpath(os.environ["DBUS_TOP_BUILDDIR"])
+ pydir = os.path.normpath(os.environ["DBUS_TOP_SRCDIR"])
+ pkg = dbus.__file__
+
+ if not pkg.startswith(pydir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % pkg)
+
+ if not _dbus_bindings.__file__.startswith(builddir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % _dbus_bindings.__file__)
+
+test_types_vals = [1, 12323231, 3.14159265, 99999999.99,
+ "dude", "123", "What is all the fuss about?", "gob@gob.com",
+ '\\u310c\\u310e\\u3114', '\\u0413\\u0414\\u0415',
+ '\\u2200software \\u2203crack', '\\xf4\\xe5\\xe8',
+ [1,2,3], ["how", "are", "you"], [1.23,2.3], [1], ["Hello"],
+ (1,2,3), (1,), (1,"2",3), ("2", "what"), ("you", 1.2),
+ {1:"a", 2:"b"}, {"a":1, "b":2}, #{"a":(1,"B")},
+ {1:1.1, 2:2.2}, [[1,2,3],[2,3,4]], [["a","b"],["c","d"]],
+ True, False,
+ dbus.Int16(-10), dbus.UInt16(10), 'SENTINEL',
+ #([1,2,3],"c", 1.2, ["a","b","c"], {"a": (1,"v"), "b": (2,"d")})
+ ]
+
+NAME = "org.freedesktop.DBus.TestSuitePythonService"
+IFACE = "org.freedesktop.DBus.TestSuiteInterface"
+OBJECT = "/org/freedesktop/DBus/TestSuitePythonObject"
+
+class TestDBusBindings(unittest.TestCase):
+ def setUp(self):
+ self.bus = dbus.SessionBus()
+ self.remote_object = self.bus.get_object(NAME, OBJECT)
+ self.remote_object_follow = self.bus.get_object(NAME, OBJECT,
+ follow_name_owner_changes=True)
+ self.iface = dbus.Interface(self.remote_object, IFACE)
+
+ def testGObject(self):
+ print("Testing ExportedGObject... ", end='')
+ remote_gobject = self.bus.get_object(NAME, OBJECT + '/GObject')
+ iface = dbus.Interface(remote_gobject, IFACE)
+ print("introspection, ", end='')
+ remote_gobject.Introspect(dbus_interface=dbus.INTROSPECTABLE_IFACE)
+ print("method call, ", end='')
+ self.assertEqual(iface.Echo('123'), '123')
+ print("... OK")
+
+ def testWeakRefs(self):
+ # regression test for Sugar crash caused by smcv getting weak refs
+ # wrong - pre-bugfix, this would segfault
+ bus = dbus.SessionBus(private=True)
+ ref = weakref.ref(bus)
+ self.assertTrue(ref() is bus)
+ del bus
+ self.assertTrue(ref() is None)
+
+ def testInterfaceKeyword(self):
+ #test dbus_interface parameter
+ print(self.remote_object.Echo("dbus_interface on Proxy test Passed",
+ dbus_interface = IFACE))
+ print(self.iface.Echo("dbus_interface on Interface test Passed",
+ dbus_interface = IFACE))
+ self.assertTrue(True)
+
+ def testGetDBusMethod(self):
+ self.assertEqual(self.iface.get_dbus_method('AcceptListOfByte')(b'\1\2\3'), [1,2,3])
+ self.assertEqual(self.remote_object.get_dbus_method('AcceptListOfByte', dbus_interface=IFACE)(b'\1\2\3'), [1,2,3])
+
+ def testCallingConventionOptions(self):
+ self.assertEqual(self.iface.AcceptListOfByte(b'\1\2\3'), [1,2,3])
+ self.assertEqual(self.iface.AcceptListOfByte(b'\1\2\3', byte_arrays=True), b'\1\2\3')
+ self.assertEqual(self.iface.AcceptByteArray(b'\1\2\3'), [1,2,3])
+ self.assertEqual(self.iface.AcceptByteArray(b'\1\2\3', byte_arrays=True), b'\1\2\3')
+ if is_py2:
+ self.assertTrue(isinstance(self.iface.AcceptUTF8String('abc'), unicode))
+ self.assertTrue(isinstance(self.iface.AcceptUTF8String('abc', utf8_strings=True), str))
+ unicode_type = (str if is_py3 else unicode)
+ self.assertTrue(isinstance(self.iface.AcceptUnicodeString('abc'),
+ unicode_type))
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ self.assertTrue(isinstance(self.iface.AcceptUnicodeString('abc', **kwargs), str))
+
+ def testIntrospection(self):
+ #test introspection
+ print("\n********* Introspection Test ************")
+ print(self.remote_object.Introspect(
+ dbus_interface="org.freedesktop.DBus.Introspectable"))
+ print("Introspection test passed")
+ self.assertTrue(True)
+
+ def testMultiPathIntrospection(self):
+ # test introspection on an object exported in multiple places
+ # https://bugs.freedesktop.org/show_bug.cgi?id=11794
+ remote_object = self.bus.get_object(NAME, OBJECT + '/Multi1')
+ remote_object.Introspect(dbus_interface="org.freedesktop.DBus.Introspectable")
+ remote_object = self.bus.get_object(NAME, OBJECT + '/Multi2')
+ remote_object.Introspect(dbus_interface="org.freedesktop.DBus.Introspectable")
+ remote_object = self.bus.get_object(NAME, OBJECT + '/Multi2/3')
+ remote_object.Introspect(dbus_interface="org.freedesktop.DBus.Introspectable")
+ self.assertTrue(True)
+
+ def testPythonTypes(self):
+ #test sending python types and getting them back
+ print("\n********* Testing Python Types ***********")
+
+ for send_val in test_types_vals:
+ print("Testing %s"% str(send_val))
+ recv_val = self.iface.Echo(send_val)
+ self.assertEqual(send_val, recv_val)
+ self.assertEqual(recv_val.variant_level, 1)
+
+ def testMethodExtraInfoKeywords(self):
+ print("Testing MethodExtraInfoKeywords...")
+ sender, path, destination, message_cls = self.iface.MethodExtraInfoKeywords()
+ self.assertTrue(sender.startswith(':'))
+ self.assertEqual(path, '/org/freedesktop/DBus/TestSuitePythonObject')
+ # we're using the "early binding" form of get_object (without
+ # follow_name_owner_changes), so the destination we actually sent it
+ # to will be the unique name
+ self.assertTrue(destination.startswith(':'))
+ self.assertEqual(message_cls, 'dbus.lowlevel.MethodCallMessage')
+
+ def testUtf8StringsSync(self):
+ if is_py3:
+ return
+ send_val = 'foo'
+ recv_val = self.iface.Echo(send_val, utf8_strings=True)
+ self.assertTrue(isinstance(recv_val, str))
+ self.assertTrue(isinstance(recv_val, dbus.UTF8String))
+ recv_val = self.iface.Echo(send_val, utf8_strings=False)
+ self.assertTrue(isinstance(recv_val, unicode))
+ self.assertTrue(isinstance(recv_val, dbus.String))
+
+ def testBenchmarkIntrospect(self):
+ print("\n********* Benchmark Introspect ************")
+ a = time.time()
+ print(a)
+ print(self.iface.GetComplexArray())
+ b = time.time()
+ print(b)
+ print("Delta: %f" % (b - a))
+ self.assertTrue(True)
+
+ def testAsyncCalls(self):
+ #test sending python types and getting them back async
+ print("\n********* Testing Async Calls ***********")
+
+ failures = []
+ main_loop = gobject.MainLoop()
+
+ class async_check:
+ def __init__(self, test_controler, expected_result, do_exit, **kwargs):
+ self.expected_result = expected_result
+ self.do_exit = do_exit
+ self.test_controler = test_controler
+ if is_py2:
+ self.utf8 = kwargs['utf8']
+ elif 'utf8' in kwargs:
+ raise TypeError("unexpected keyword argument 'utf8'")
+
+ def callback(self, val):
+ try:
+ if self.do_exit:
+ main_loop.quit()
+
+ self.test_controler.assertEqual(val, self.expected_result)
+ self.test_controler.assertEqual(val.variant_level, 1)
+ if is_py2:
+ if self.utf8 and not isinstance(val, dbus.UTF8String):
+ failures.append('%r should have been utf8 but was not' % val)
+ return
+ elif not self.utf8 and isinstance(val, dbus.UTF8String):
+ failures.append('%r should not have been utf8' % val)
+ return
+ except Exception as e:
+ failures.append("%s:\n%s" % (e.__class__, e))
+
+ def error_handler(self, error):
+ print(error)
+ if self.do_exit:
+ main_loop.quit()
+
+ failures.append('%s: %s' % (error.__class__, error))
+
+ last_type = test_types_vals[-1]
+ for send_val in test_types_vals:
+ print("Testing %s" % str(send_val))
+ kwargs = {}
+ if is_py2:
+ utf8 = (send_val == 'gob@gob.com')
+ kwargs['utf8'] = utf8
+ kwargs['utf8_strings'] = utf8
+ check = async_check(self, send_val, last_type == send_val,
+ **kwargs)
+ recv_val = self.iface.Echo(send_val,
+ reply_handler=check.callback,
+ error_handler=check.error_handler,
+ **kwargs)
+ main_loop.run()
+ if failures:
+ self.assertTrue(False, failures)
+
+ def testStrictMarshalling(self):
+ print("\n********* Testing strict return & signal marshalling ***********")
+
+ # these values are the same as in the server, and the
+ # methods should only succeed when they are called with
+ # the right value number, because they have out_signature
+ # decorations, and return an unmatching type when called
+ # with a different number
+ values = ["", ("",""), ("","",""), [], {}, ["",""], ["","",""]]
+ methods = [
+ (self.iface.ReturnOneString, 'SignalOneString', set([0]), set([0])),
+ (self.iface.ReturnTwoStrings, 'SignalTwoStrings', set([1, 5]), set([1])),
+ (self.iface.ReturnStruct, 'SignalStruct', set([1, 5]), set([1])),
+ # all of our test values are sequences so will marshall correctly
+ # into an array :P
+ (self.iface.ReturnArray, 'SignalArray', set(range(len(values))), set([3, 5, 6])),
+ (self.iface.ReturnDict, 'SignalDict', set([0, 3, 4]), set([4]))
+ ]
+
+ for (method, signal, success_values, return_values) in methods:
+ print("\nTrying correct behaviour of", method._method_name)
+ for value in range(len(values)):
+ try:
+ ret = method(value)
+ except Exception as e:
+ print("%s(%r) raised %s: %s" %
+ (method._method_name, values[value], e.__class__, e))
+
+ # should fail if it tried to marshal the wrong type
+ self.assertTrue(value not in success_values,
+ "%s should succeed when we ask it to "
+ "return %r\n%s\n%s" % (
+ method._method_name, values[value],
+ e.__class__, e))
+ else:
+ print("%s(%r) returned %r" % (
+ method._method_name, values[value], ret))
+
+ # should only succeed if it's the right return type
+ self.assertTrue(value in success_values,
+ "%s should fail when we ask it to "
+ "return %r" % (
+ method._method_name, values[value]))
+
+ # check the value is right too :D
+ returns = map(lambda n: values[n], return_values)
+ self.assertTrue(ret in returns,
+ "%s should return one of %r but it "
+ "returned %r instead" % (
+ method._method_name, returns, ret))
+
+ print("\nTrying correct emission of", signal)
+ for value in range(len(values)):
+ try:
+ self.iface.EmitSignal(signal, value)
+ except Exception as e:
+ print("EmitSignal(%s, %r) raised %s" % (signal, values[value], e.__class__))
+
+ # should fail if it tried to marshal the wrong type
+ self.assertTrue(value not in success_values, "EmitSignal(%s) should succeed when we ask it to return %r\n%s\n%s" % (signal, values[value], e.__class__, e))
+ else:
+ print("EmitSignal(%s, %r) appeared to succeed" % (signal, values[value]))
+
+ # should only succeed if it's the right return type
+ self.assertTrue(value in success_values, "EmitSignal(%s) should fail when we ask it to return %r" % (signal, values[value]))
+
+ # FIXME: wait for the signal here
+
+ print()
+
+ def testInheritance(self):
+ print("\n********* Testing inheritance from dbus.method.Interface ***********")
+ ret = self.iface.CheckInheritance()
+ print("CheckInheritance returned %s" % ret)
+ self.assertTrue(ret, "overriding CheckInheritance from TestInterface failed")
+
+ def testAsyncMethods(self):
+ print("\n********* Testing asynchronous method implementation *******")
+ for async_ in (True, False):
+ for fail in (True, False):
+ try:
+ val = ('a', 1, False, [1,2], {1:2})
+ print("calling AsynchronousMethod with %s %s %s" % (async_, fail, val))
+ ret = self.iface.AsynchronousMethod(async_, fail, val)
+ except Exception as e:
+ self.assertTrue(fail, '%s: %s' % (e.__class__, e))
+ print("Expected failure: %s: %s" % (e.__class__, e))
+ else:
+ self.assertTrue(not fail, 'Expected failure but succeeded?!')
+ self.assertEqual(val, ret)
+ self.assertEqual(1, ret.variant_level)
+
+ def testBusInstanceCaching(self):
+ print("\n********* Testing dbus.Bus instance sharing *********")
+
+ # unfortunately we can't test the system bus here
+ # but the codepaths are the same
+ for (cls, type, func) in ((dbus.SessionBus, dbus.Bus.TYPE_SESSION, dbus.Bus.get_session), (dbus.StarterBus, dbus.Bus.TYPE_STARTER, dbus.Bus.get_starter)):
+ print("\nTesting %s:" % cls.__name__)
+
+ share_cls = cls()
+ share_type = dbus.Bus(bus_type=type)
+ share_func = func()
+
+ private_cls = cls(private=True)
+ private_type = dbus.Bus(bus_type=type, private=True)
+ private_func = func(private=True)
+
+ print(" - checking shared instances are the same...")
+ self.assertTrue(share_cls == share_type, '%s should equal %s' % (share_cls, share_type))
+ self.assertTrue(share_type == share_func, '%s should equal %s' % (share_type, share_func))
+
+ print(" - checking private instances are distinct from the shared instance...")
+ self.assertTrue(share_cls != private_cls, '%s should not equal %s' % (share_cls, private_cls))
+ self.assertTrue(share_type != private_type, '%s should not equal %s' % (share_type, private_type))
+ self.assertTrue(share_func != private_func, '%s should not equal %s' % (share_func, private_func))
+
+ print(" - checking private instances are distinct from each other...")
+ self.assertTrue(private_cls != private_type, '%s should not equal %s' % (private_cls, private_type))
+ self.assertTrue(private_type != private_func, '%s should not equal %s' % (private_type, private_func))
+ self.assertTrue(private_func != private_cls, '%s should not equal %s' % (private_func, private_cls))
+
+ def testSenderName(self):
+ print('\n******** Testing sender name keyword ********')
+ myself = self.iface.WhoAmI()
+ print("I am", myself)
+
+ def testBusGetNameOwner(self):
+ ret = self.bus.get_name_owner(NAME)
+ self.assertTrue(ret.startswith(':'), ret)
+
+ def testBusListNames(self):
+ ret = self.bus.list_names()
+ self.assertTrue(NAME in ret, ret)
+
+ def testBusListActivatableNames(self):
+ ret = self.bus.list_activatable_names()
+ self.assertTrue(NAME in ret, ret)
+
+ def testBusNameHasOwner(self):
+ self.assertTrue(self.bus.name_has_owner(NAME))
+ self.assertTrue(not self.bus.name_has_owner('badger.mushroom.snake'))
+
+ def testBusNameCreation(self):
+ print('\n******** Testing BusName creation ********')
+ test = [('org.freedesktop.DBus.Python.TestName', True),
+ ('org.freedesktop.DBus.Python.TestName', True),
+ ('org.freedesktop.DBus.Python.InvalidName&^*%$', False)]
+ # Do some more intelligent handling/testing of queueing vs success?
+ # ('org.freedesktop.DBus.TestSuitePythonService', False)]
+ # For some reason this actually succeeds
+ # ('org.freedesktop.DBus', False)]
+
+ # make a method call to ensure the test service is active
+ self.iface.Echo("foo")
+
+ names = {}
+ for (name, succeed) in test:
+ try:
+ print("requesting %s" % name)
+ busname = dbus.service.BusName(name, dbus.SessionBus())
+ except Exception as e:
+ print("%s:\n%s" % (e.__class__, e))
+ self.assertTrue(not succeed, 'did not expect registering bus name %s to fail' % name)
+ else:
+ print(busname)
+ self.assertTrue(succeed, 'expected registering bus name %s to fail'% name)
+ if name in names:
+ self.assertTrue(names[name] == busname, 'got a new instance for same name %s' % name)
+ print("instance of %s re-used, good!" % name)
+ else:
+ names[name] = busname
+
+ del busname
+
+ print()
+
+ del names
+
+ bus = dbus.Bus()
+ ret = bus.name_has_owner('org.freedesktop.DBus.Python.TestName')
+ self.assertTrue(not ret, 'deleting reference failed to release BusName org.freedesktop.DBus.Python.TestName')
+
+ def testMultipleReturnWithoutSignature(self):
+ # https://bugs.freedesktop.org/show_bug.cgi?id=10174
+ ret = self.iface.MultipleReturnWithoutSignature()
+ self.assertTrue(not isinstance(ret, dbus.Struct), repr(ret))
+ self.assertEqual(ret, ('abc', 123))
+
+ def testListExportedChildObjects(self):
+ self.assertTrue(self.iface.TestListExportedChildObjects())
+
+ def testRemoveFromConnection(self):
+ # https://bugs.freedesktop.org/show_bug.cgi?id=10457
+ self.assertTrue(not self.iface.HasRemovableObject())
+ self.assertTrue(self.iface.AddRemovableObject())
+ self.assertTrue(self.iface.HasRemovableObject())
+
+ removable = self.bus.get_object(NAME, OBJECT + '/RemovableObject')
+ iface = dbus.Interface(removable, IFACE)
+ self.assertTrue(iface.IsThere())
+ self.assertTrue(iface.RemoveSelf())
+
+ self.assertTrue(not self.iface.HasRemovableObject())
+
+ # and again...
+ self.assertTrue(self.iface.AddRemovableObject())
+ self.assertTrue(self.iface.HasRemovableObject())
+ self.assertTrue(iface.IsThere())
+ self.assertTrue(iface.RemoveSelf())
+ self.assertTrue(not self.iface.HasRemovableObject())
+
+ def testFallbackObjectTrivial(self):
+ obj = self.bus.get_object(NAME, OBJECT + '/Fallback')
+ iface = dbus.Interface(obj, IFACE)
+ path, rel, unique_name = iface.TestPathAndConnKeywords()
+ self.assertEqual(path, OBJECT + '/Fallback')
+ self.assertEqual(rel, '/')
+ self.assertEqual(unique_name, obj.bus_name)
+
+ def testFallbackObjectNested(self):
+ obj = self.bus.get_object(NAME, OBJECT + '/Fallback/Nested')
+ iface = dbus.Interface(obj, IFACE)
+ path, rel, unique_name = iface.TestPathAndConnKeywords()
+ self.assertEqual(path, OBJECT + '/Fallback/Nested')
+ self.assertEqual(rel, '/')
+ self.assertEqual(unique_name, obj.bus_name)
+
+ obj = self.bus.get_object(NAME, OBJECT + '/Fallback/Nested/Badger/Mushroom')
+ iface = dbus.Interface(obj, IFACE)
+ path, rel, unique_name = iface.TestPathAndConnKeywords()
+ self.assertEqual(path, OBJECT + '/Fallback/Nested/Badger/Mushroom')
+ self.assertEqual(rel, '/Badger/Mushroom')
+ self.assertEqual(unique_name, obj.bus_name)
+
+ def testFallbackObject(self):
+ obj = self.bus.get_object(NAME, OBJECT + '/Fallback/Badger/Mushroom')
+ iface = dbus.Interface(obj, IFACE)
+ path, rel, unique_name = iface.TestPathAndConnKeywords()
+ self.assertEqual(path, OBJECT + '/Fallback/Badger/Mushroom')
+ self.assertEqual(rel, '/Badger/Mushroom')
+ self.assertEqual(unique_name, obj.bus_name)
+
+ def testTimeoutSync(self):
+ self.assertTrue(self.iface.BlockFor500ms(timeout=1.0) is None)
+ self.assertRaises(dbus.DBusException,
+ lambda: self.iface.BlockFor500ms(timeout=0.25))
+
+ def testAsyncRaise(self):
+ self.assertRaises(dbus.DBusException, self.iface.AsyncRaise)
+ try:
+ self.iface.AsyncRaise()
+ except dbus.DBusException as e:
+ self.assertTrue(e.get_dbus_name() ==
+ 'org.freedesktop.bugzilla.bug12403',
+ e.get_dbus_name())
+ else:
+ self.assertTrue(False)
+
+ def testClosePrivateBus(self):
+ # fd.o #12096
+ dbus.Bus(private=True).close()
+
+ def testTimeoutAsyncClient(self):
+ loop = gobject.MainLoop()
+ passes = []
+ fails = []
+ def correctly_returned():
+ passes.append('1000')
+ if len(passes) + len(fails) >= 2:
+ loop.quit()
+ def correctly_failed(exc):
+ passes.append('250')
+ if len(passes) + len(fails) >= 2:
+ loop.quit()
+ def incorrectly_returned():
+ fails.append('250')
+ if len(passes) + len(fails) >= 2:
+ loop.quit()
+ def incorrectly_failed(exc):
+ fails.append('1000')
+ if len(passes) + len(fails) >= 2:
+ loop.quit()
+ self.iface.BlockFor500ms(timeout=1.0,
+ reply_handler=correctly_returned,
+ error_handler=incorrectly_failed)
+ self.iface.BlockFor500ms(timeout=0.25,
+ reply_handler=incorrectly_returned,
+ error_handler=correctly_failed)
+ loop.run()
+ self.assertEqual(passes, ['250', '1000'])
+ self.assertEqual(fails, [])
+
+ def testTimeoutAsyncService(self):
+ self.assertTrue(self.iface.AsyncWait500ms(timeout=1.0) is None)
+ self.assertRaises(dbus.DBusException,
+ lambda: self.iface.AsyncWait500ms(timeout=0.25))
+
+ def testExceptions(self):
+ #self.assertRaises(dbus.DBusException,
+ # lambda: self.iface.RaiseValueError)
+ #self.assertRaises(dbus.DBusException,
+ # lambda: self.iface.RaiseDBusExceptionNoTraceback)
+ #self.assertRaises(dbus.DBusException,
+ # lambda: self.iface.RaiseDBusExceptionWithTraceback)
+
+ try:
+ self.iface.RaiseValueError()
+ except Exception as e:
+ self.assertTrue(isinstance(e, dbus.DBusException), e.__class__)
+ self.assertTrue('.ValueError: Traceback ' in str(e),
+ 'Wanted a traceback but got:\n"""%s"""' % str(e))
+ else:
+ raise AssertionError('Wanted an exception')
+
+ try:
+ self.iface.RaiseDBusExceptionNoTraceback()
+ except Exception as e:
+ self.assertTrue(isinstance(e, dbus.DBusException), e.__class__)
+ self.assertEqual(e.get_dbus_name(),
+ 'com.example.Networking.ServerError')
+ self.assertEqual(str(e),
+ 'com.example.Networking.ServerError: '
+ 'Server not responding')
+ else:
+ raise AssertionError('Wanted an exception')
+
+ try:
+ self.iface.RaiseDBusExceptionWithTraceback()
+ except Exception as e:
+ self.assertTrue(isinstance(e, dbus.DBusException), e.__class__)
+ self.assertEqual(e.get_dbus_name(),
+ 'com.example.Misc.RealityFailure')
+ self.assertTrue(str(e).startswith('com.example.Misc.RealityFailure: '
+ 'Traceback '),
+ 'Wanted a traceback but got:\n%s' % str(e))
+ else:
+ raise AssertionError('Wanted an exception')
+
+""" Remove this for now
+class TestDBusPythonToGLibBindings(unittest.TestCase):
+ def setUp(self):
+ self.bus = dbus.SessionBus()
+ self.remote_object = self.bus.get_object("org.freedesktop.DBus.TestSuiteGLibService", "/org/freedesktop/DBus/Tests/MyTestObject")
+ self.iface = dbus.Interface(self.remote_object, "org.freedesktop.DBus.Tests.MyObject")
+
+ def testIntrospection(self):
+ #test introspection
+ print "\n********* Introspection Test ************"
+ print self.remote_object.Introspect(dbus_interface="org.freedesktop.DBus.Introspectable")
+ print "Introspection test passed"
+ self.assertTrue(True)
+
+ def testCalls(self):
+ print "\n********* Call Test ************"
+ result = self.iface.ManyArgs(1000, 'Hello GLib', 2)
+ print result
+ self.assertTrue(result == [2002.0, 'HELLO GLIB'])
+
+ arg0 = {"Dude": 1, "john": "palmieri", "python": 2.4}
+ result = self.iface.ManyStringify(arg0)
+ print result
+
+ print "Call test passed"
+ self.assertTrue(True)
+
+ def testPythonTypes(self):
+ print "\n********* Testing Python Types ***********"
+
+ for send_val in test_types_vals:
+ print "Testing %s"% str(send_val)
+ recv_val = self.iface.EchoVariant(send_val)
+ self.assertEqual(send_val, recv_val.object)
+"""
+if __name__ == '__main__':
+ gobject.threads_init()
+ dbus.glib.init_threads()
+
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner)
diff --git a/test/test-exception-py2.py b/test/test-exception-py2.py
new file mode 100755
index 0000000..3633ea3
--- /dev/null
+++ b/test/test-exception-py2.py
@@ -0,0 +1,70 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import sys
+import unittest
+
+try:
+ from tap.runner import TAPTestRunner
+except ImportError:
+ print('1..0 # SKIP cannot import TAPTestRunner')
+ raise SystemExit(0)
+
+import dbus
+
+# from test-service.py
+class ServiceError(dbus.DBusException):
+ """Exception representing a normal "environmental" error"""
+ include_traceback = False
+ _dbus_error_name = 'com.example.Networking.ServerError'
+
+
+class DBusExceptionTestCase(unittest.TestCase):
+ """Test the DBusException str/unicode behavior with py2"""
+
+ def test_dbus_exception_normal(self):
+ """Test the normal Exception behavior"""
+ e = dbus.exceptions.DBusException("baaa")
+ msg = e.get_dbus_message()
+ self.assertEqual(msg, u"baaa")
+
+ def test_dbus_exception_unicode(self):
+ """Test that DBusExceptions that take a py2 unicode work"""
+ e = dbus.exceptions.DBusException(u"bäää")
+ msg = e.get_dbus_message()
+ self.assertEqual(msg, u"bäää")
+
+ def test_dbus_exception_convert_str(self):
+ """Test that converting a DBusException to str() works as expected"""
+ e = dbus.exceptions.DBusException(u"bxxx")
+ self.assertEqual(str(e), "bxxx")
+
+ def test_dbus_exception_convert_str_fail(self):
+ """Test that a non-ascii Exception fails to convert to str"""
+ if sys.getdefaultencoding() == 'ascii':
+ self.assertRaises(UnicodeEncodeError,
+ lambda: str(dbus.exceptions.DBusException(u"bä")))
+ else:
+ self.skipTest("you're using a weird non-ascii "
+ "sys.getdefaultencoding()")
+
+ def test_dbus_exception_convert_unicode(self):
+ """Test that converting a DBusEception to unicode works"""
+ e = dbus.exceptions.DBusException(u"bäää")
+ self.assertEqual(e.get_dbus_message(), u"bäää")
+ self.assertEqual(e.__unicode__(), u"bäää")
+ self.assertEqual(unicode(e), u"bäää")
+
+ def test_subclass_exception_unicode(self):
+ """Test that DBusExceptions that take a py2 unicode work"""
+ e = ServiceError(u"bäää")
+ msg = e.get_dbus_message()
+ self.assertEqual(msg, u"bäää")
+ self.assertEqual(
+ unicode(e), u"com.example.Networking.ServerError: bäää")
+
+
+if __name__ == "__main__":
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner)
diff --git a/test/test-exception-py3.py b/test/test-exception-py3.py
new file mode 100755
index 0000000..a0c981e
--- /dev/null
+++ b/test/test-exception-py3.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import unittest
+
+try:
+ from tap.runner import TAPTestRunner
+except ImportError:
+ print('1..0 # SKIP cannot import TAPTestRunner')
+ raise SystemExit(0)
+
+import dbus
+
+# from test-service.py
+class ServiceError(dbus.DBusException):
+ """Exception representing a normal "environmental" error"""
+ include_traceback = False
+ _dbus_error_name = 'com.example.Networking.ServerError'
+
+
+class DBusExceptionTestCase(unittest.TestCase):
+
+ def test_dbus_exception(self):
+ e = dbus.exceptions.DBusException("bäää")
+ msg = e.get_dbus_message()
+ self.assertEqual(msg, "bäää")
+ self.assertEqual(str(e), "bäää")
+
+ def test_subclass_exception(self):
+ e = ServiceError("bäää")
+ msg = e.get_dbus_message()
+ self.assertEqual(msg, "bäää")
+ self.assertEqual(str(e), "com.example.Networking.ServerError: bäää")
+
+
+if __name__ == "__main__":
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner)
diff --git a/test/test-p2p.py b/test/test-p2p.py
new file mode 100755
index 0000000..7c7655d
--- /dev/null
+++ b/test/test-p2p.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2004 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+
+import os
+import unittest
+import logging
+
+import dbus
+import dbus.glib
+import dbus.service
+
+from dbus._compat import is_py2
+
+try:
+ from tap.runner import TAPTestRunner
+except ImportError:
+ print('1..0 # SKIP cannot import TAPTestRunner')
+ raise SystemExit(0)
+
+try:
+ from gi.repository import GObject as gobject
+except ImportError:
+ print('1..0 # SKIP cannot import GObject')
+ raise SystemExit(0)
+
+logging.basicConfig()
+logging.getLogger().setLevel(1)
+
+
+NAME = "org.freedesktop.DBus.TestSuitePythonService"
+IFACE = "org.freedesktop.DBus.TestSuiteInterface"
+OBJECT = "/org/freedesktop/DBus/TestSuitePythonObject"
+
+class TestDBusBindings(unittest.TestCase):
+ # This test case relies on the test service already having been activated.
+
+ def get_conn_and_unique(self):
+ # since I haven't implemented servers yet, I'll use the bus daemon
+ # as our peer - note that there's no name tracking because we're not
+ # using dbus.bus.BusConnection!
+ conn = dbus.connection.Connection(
+ os.environ['DBUS_SESSION_BUS_ADDRESS'])
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ unique = conn.call_blocking('org.freedesktop.DBus',
+ '/org/freedesktop/DBus',
+ 'org.freedesktop.DBus', 'Hello',
+ '', (), **kwargs)
+ if is_py2:
+ self.assertTrue(unique.__class__ == dbus.UTF8String, repr(unique))
+ self.assertTrue(unique.startswith(':'), unique)
+ conn.set_unique_name(unique)
+ return conn, unique
+
+ def testCall(self):
+ conn, unique = self.get_conn_and_unique()
+ ret = conn.call_blocking(NAME, OBJECT, IFACE, 'Echo', 'v', ('V',))
+ self.assertEqual(ret, 'V')
+
+ def testCallThroughProxy(self):
+ conn, unique = self.get_conn_and_unique()
+ proxy = conn.get_object(NAME, OBJECT)
+ iface = dbus.Interface(proxy, IFACE)
+ ret = iface.Echo('V')
+ self.assertEqual(ret, 'V')
+
+ def testSetUniqueName(self):
+ conn, unique = self.get_conn_and_unique()
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ ret = conn.call_blocking(NAME, OBJECT, IFACE,
+ 'MethodExtraInfoKeywords', '', (),
+ **kwargs)
+ self.assertEqual(ret, (unique, OBJECT, NAME,
+ 'dbus.lowlevel.MethodCallMessage'))
+
+
+if __name__ == '__main__':
+ gobject.threads_init()
+ dbus.glib.init_threads()
+
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner)
diff --git a/test/test-server.py b/test/test-server.py
new file mode 100755
index 0000000..c90ccf1
--- /dev/null
+++ b/test/test-server.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+
+from dbus.mainloop.glib import DBusGMainLoop
+
+import dbus
+import dbus.connection
+import dbus.service
+import dbus.server
+
+from gi.repository import GLib
+import os, sys
+
+class TestService(dbus.service.Object):
+ NAME = 'org.freedesktop.dbus.TestService'
+ PATH = '/org/freedesktop/dbus/TestService'
+
+ def __init__(self, conn, path=PATH):
+ super(TestService, self).__init__(conn, path)
+
+ @dbus.service.method(dbus_interface=NAME,
+ out_signature='s',
+ in_signature='s')
+ def reverse(self, text):
+ text = list(text)
+ text.reverse()
+
+ return ''.join(text)
+
+pin, pout = os.pipe()
+child = os.fork()
+
+if 0 == child:
+ DBusGMainLoop(set_as_default=True)
+ server = dbus.server.Server('unix:tmpdir=/tmp')
+
+ def new_connection(conn):
+ print "new connection, %r" % conn
+ TestService(conn)
+
+ def connection_gone(conn):
+ print "goodbye, %r" % conn
+
+ # Instantiate a TestService every time a connection is created
+ server.on_connection_added.append(new_connection)
+ server.on_connection_removed.append(connection_gone)
+
+ os.write(pout, server.address)
+ os.close(pout)
+ os.close(pin)
+
+ print 'server running: %s' % server.address
+ GLib.MainLoop().run()
+
+ print 'server quit'
+
+ print 'done?'
+
+else:
+ os.waitpid(child, os.WNOHANG)
+ os.close(pout)
+
+ address = os.read(pin, 128)
+ os.close(pin)
+
+ client = dbus.connection.Connection(address)
+ proxy = client.get_object(TestService.NAME, TestService.PATH)
+ object = dbus.Interface(proxy, TestService.NAME)
+
+ while True:
+ line = sys.stdin.readline()
+ if not line: break
+
+ text = line.strip()
+ print 'reverse(%s): %s' % (text, object.reverse(text))
+
+ client.close()
diff --git a/test/test-service.py b/test/test-service.py
new file mode 100755
index 0000000..4e531cb
--- /dev/null
+++ b/test/test-service.py
@@ -0,0 +1,387 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2004 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import os
+import logging
+from time import sleep
+
+import dbus
+
+if 'DBUS_TEST_UNINSTALLED' in os.environ:
+ import _dbus_bindings
+
+ builddir = os.path.normpath(os.environ["DBUS_TOP_BUILDDIR"])
+ pydir = os.path.normpath(os.environ["DBUS_TOP_SRCDIR"])
+ pkg = dbus.__file__
+
+ if not pkg.startswith(pydir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % pkg)
+
+ if not _dbus_bindings.__file__.startswith(builddir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % _dbus_bindings.__file__)
+
+import dbus.service
+import dbus.glib
+import random
+
+from dbus.gi_service import ExportedGObject
+from gi.repository import GObject
+from dbus._compat import is_py2, is_py3
+
+
+if 'DBUS_TEST_TMPDIR' in os.environ:
+ logging.basicConfig(
+ filename=os.environ['DBUS_TEST_TMPDIR'] + '/test-service.log',
+ filemode='a')
+else:
+ logging.basicConfig()
+
+logging.getLogger().setLevel(1)
+logger = logging.getLogger('test-service')
+
+NAME = "org.freedesktop.DBus.TestSuitePythonService"
+IFACE = "org.freedesktop.DBus.TestSuiteInterface"
+OBJECT = "/org/freedesktop/DBus/TestSuitePythonObject"
+
+class RemovableObject(dbus.service.Object):
+ # Part of test for https://bugs.freedesktop.org/show_bug.cgi?id=10457
+ @dbus.service.method(IFACE, in_signature='', out_signature='b')
+ def IsThere(self):
+ return True
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='b')
+ def RemoveSelf(self):
+ self.remove_from_connection()
+ return True
+
+class TestGObject(ExportedGObject):
+ def __init__(self, bus_name, object_path=OBJECT + '/GObject'):
+ super(TestGObject, self).__init__(bus_name, object_path)
+
+ @dbus.service.method(IFACE)
+ def Echo(self, arg):
+ return arg
+
+class TestInterface(dbus.service.Interface):
+ @dbus.service.method(IFACE, in_signature='', out_signature='b')
+ def CheckInheritance(self):
+ return False
+
+class Fallback(dbus.service.FallbackObject):
+ def __init__(self, conn, object_path=OBJECT + '/Fallback'):
+ super(Fallback, self).__init__(conn, object_path)
+ self.add_to_connection(conn, object_path + '/Nested')
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='oos',
+ path_keyword='path', rel_path_keyword='rel',
+ connection_keyword='conn')
+ def TestPathAndConnKeywords(self, path=None, conn=None, rel=None):
+ return path, rel, conn.get_unique_name()
+
+ @dbus.service.signal(IFACE, signature='s', rel_path_keyword='rel_path')
+ def SignalOneString(self, test, rel_path=None):
+ logger.info('SignalOneString(%r) @ %r', test, rel_path)
+
+ # Deprecated usage
+ @dbus.service.signal(IFACE, signature='ss', path_keyword='path')
+ def SignalTwoStrings(self, test, test2, path=None):
+ logger.info('SignalTwoStrings(%r, %r) @ %r', test, test2, path)
+
+ @dbus.service.method(IFACE, in_signature='su', out_signature='',
+ path_keyword='path')
+ def EmitSignal(self, signal, value, path=None):
+ sig = getattr(self, str(signal), None)
+ assert sig is not None
+
+ assert path.startswith(OBJECT + '/Fallback')
+ rel_path = path[len(OBJECT + '/Fallback'):]
+ if rel_path == '':
+ rel_path = '/'
+
+ if signal == 'SignalOneString':
+ logger.info('Emitting %s from rel %r', signal, rel_path)
+ sig('I am a fallback', rel_path=rel_path)
+ else:
+ val = ('I am', 'a fallback')
+ logger.info('Emitting %s from abs %r', signal, path)
+ sig('I am', 'a deprecated fallback', path=path)
+
+class MultiPathObject(dbus.service.Object):
+ SUPPORTS_MULTIPLE_OBJECT_PATHS = True
+
+class TestObject(dbus.service.Object, TestInterface):
+ def __init__(self, bus_name, object_path=OBJECT):
+ dbus.service.Object.__init__(self, bus_name, object_path)
+ self._removable = RemovableObject()
+ self._multi = MultiPathObject(bus_name, object_path + '/Multi1')
+ self._multi.add_to_connection(bus_name.get_bus(),
+ object_path + '/Multi2')
+ self._multi.add_to_connection(bus_name.get_bus(),
+ object_path + '/Multi2/3')
+
+ """ Echo whatever is sent
+ """
+ @dbus.service.method(IFACE)
+ def Echo(self, arg):
+ return arg
+
+ @dbus.service.method(IFACE, in_signature='s', out_signature='s')
+ def AcceptUnicodeString(self, foo):
+ unicode_type = (str if is_py3 else unicode)
+ assert isinstance(foo, unicode_type), (foo, foo.__class__.__mro__)
+ return foo
+
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ @dbus.service.method(IFACE, in_signature='s', out_signature='s', **kwargs)
+ def AcceptUTF8String(self, foo):
+ assert isinstance(foo, str), (foo, foo.__class__.__mro__)
+ return foo
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='soss',
+ sender_keyword='sender', path_keyword='path',
+ destination_keyword='dest', message_keyword='msg')
+ def MethodExtraInfoKeywords(self, sender=None, path=None, dest=None,
+ msg=None):
+ return (sender, path, dest,
+ msg.__class__.__module__ + '.' + msg.__class__.__name__)
+
+ @dbus.service.method(IFACE, in_signature='ay', out_signature='ay')
+ def AcceptListOfByte(self, foo):
+ assert isinstance(foo, list), (foo, foo.__class__.__mro__)
+ return foo
+
+ @dbus.service.method(IFACE, in_signature='ay', out_signature='ay',
+ byte_arrays=True)
+ def AcceptByteArray(self, foo):
+ assert isinstance(foo, bytes), (foo, foo.__class__.__mro__)
+ return foo
+
+ @dbus.service.method(IFACE)
+ def GetComplexArray(self):
+ ret = []
+ for i in range(0,100):
+ ret.append((random.randint(0,100), random.randint(0,100), str(random.randint(0,100))))
+
+ return dbus.Array(ret, signature="(uus)")
+
+ def returnValue(self, test):
+ if test == 0:
+ return ""
+ elif test == 1:
+ return "",""
+ elif test == 2:
+ return "","",""
+ elif test == 3:
+ return []
+ elif test == 4:
+ return {}
+ elif test == 5:
+ return ["",""]
+ elif test == 6:
+ return ["","",""]
+
+ @dbus.service.method(IFACE, in_signature='u', out_signature='s')
+ def ReturnOneString(self, test):
+ return self.returnValue(test)
+
+ @dbus.service.method(IFACE, in_signature='u', out_signature='ss')
+ def ReturnTwoStrings(self, test):
+ return self.returnValue(test)
+
+ @dbus.service.method(IFACE, in_signature='u', out_signature='(ss)')
+ def ReturnStruct(self, test):
+ logger.info('ReturnStruct(%r) -> %r', test, self.returnValue(test))
+ return self.returnValue(test)
+
+ @dbus.service.method(IFACE, in_signature='u', out_signature='as')
+ def ReturnArray(self, test):
+ return self.returnValue(test)
+
+ @dbus.service.method(IFACE, in_signature='u', out_signature='a{ss}')
+ def ReturnDict(self, test):
+ return self.returnValue(test)
+
+ @dbus.service.signal(IFACE, signature='s')
+ def SignalOneString(self, test):
+ logger.info('SignalOneString(%r)', test)
+
+ @dbus.service.signal(IFACE, signature='ss')
+ def SignalTwoStrings(self, test, test2):
+ logger.info('SignalTwoStrings(%r, %r)', test, test2)
+
+ @dbus.service.signal(IFACE, signature='(ss)')
+ def SignalStruct(self, test):
+ logger.info('SignalStruct(%r)', test)
+
+ @dbus.service.signal(IFACE, signature='as')
+ def SignalArray(self, test):
+ pass
+
+ @dbus.service.signal(IFACE, signature='a{ss}')
+ def SignalDict(self, test):
+ pass
+
+ @dbus.service.method(IFACE, in_signature='su', out_signature='')
+ def EmitSignal(self, signal, value):
+ sig = getattr(self, str(signal), None)
+ assert(sig != None)
+
+ val = self.returnValue(value)
+ # make two string case work by passing arguments in by tuple
+ if (signal == 'SignalTwoStrings' and (value == 1 or value == 5)):
+ val = tuple(val)
+ else:
+ val = tuple([val])
+
+ logger.info('Emitting %s with %r', signal, val)
+ sig(*val)
+
+ def CheckInheritance(self):
+ return True
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='b')
+ def TestListExportedChildObjects(self):
+ objs_root = session_bus.list_exported_child_objects('/')
+ assert objs_root == ['org'], objs_root
+ objs_org = session_bus.list_exported_child_objects('/org')
+ assert objs_org == ['freedesktop'], objs_org
+ return True
+
+ @dbus.service.method(IFACE, in_signature='bbv', out_signature='v',
+ async_callbacks=('return_cb', 'error_cb'))
+ def AsynchronousMethod(self, async_, fail, variant, return_cb, error_cb):
+ try:
+ if async_:
+ GObject.timeout_add(500, self.AsynchronousMethod, False, fail,
+ variant, return_cb, error_cb)
+ return
+ else:
+ if fail:
+ raise RuntimeError
+ else:
+ return_cb(variant)
+
+ return False # do not run again
+ except Exception as e:
+ error_cb(e)
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='s',
+ sender_keyword='sender')
+ def WhoAmI(self, sender):
+ return sender
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='b')
+ def AddRemovableObject(self):
+ # Part of test for https://bugs.freedesktop.org/show_bug.cgi?id=10457
+ # Keep the removable object reffed, since that's the use case for this
+ self._removable.add_to_connection(self._connection,
+ OBJECT + '/RemovableObject')
+ return True
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='b')
+ def HasRemovableObject(self):
+ # Part of test for https://bugs.freedesktop.org/show_bug.cgi?id=10457
+ objs = session_bus.list_exported_child_objects(OBJECT)
+ return ('RemovableObject' in objs)
+
+ @dbus.service.method(IFACE)
+ def MultipleReturnWithoutSignature(self):
+ # https://bugs.freedesktop.org/show_bug.cgi?id=10174
+ return dbus.String('abc'), dbus.Int32(123)
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='')
+ def BlockFor500ms(self):
+ sleep(0.5)
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='',
+ async_callbacks=('return_cb', 'raise_cb'))
+ def AsyncWait500ms(self, return_cb, raise_cb):
+ def return_from_async_wait():
+ return_cb()
+ return False
+ GObject.timeout_add(500, return_from_async_wait)
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='')
+ def RaiseValueError(self):
+ raise ValueError('Wrong!')
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='')
+ def RaiseDBusExceptionNoTraceback(self):
+ class ServerError(dbus.DBusException):
+ """Exception representing a normal "environmental" error"""
+ include_traceback = False
+ _dbus_error_name = 'com.example.Networking.ServerError'
+
+ raise ServerError('Server not responding')
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='')
+ def RaiseDBusExceptionWithTraceback(self):
+ class RealityFailure(dbus.DBusException):
+ """Exception representing a programming error"""
+ include_traceback = True
+ _dbus_error_name = 'com.example.Misc.RealityFailure'
+
+ raise RealityFailure('Botched invariant')
+
+ @dbus.service.method(IFACE, in_signature='', out_signature='',
+ async_callbacks=('return_cb', 'raise_cb'))
+ def AsyncRaise(self, return_cb, raise_cb):
+ class Fdo12403Error(dbus.DBusException):
+ _dbus_error_name = 'org.freedesktop.bugzilla.bug12403'
+
+ raise_cb(Fdo12403Error())
+
+
+def main():
+ global session_bus
+ logger.info('getting session bus')
+ session_bus = dbus.SessionBus()
+ logger.info('getting bus name %s', NAME)
+ global_name = dbus.service.BusName(NAME, bus=session_bus)
+ logger.info('making TestObject')
+ object = TestObject(global_name)
+ logger.info('making TestGObject')
+ g_object = TestGObject(global_name)
+ logger.info('making Fallback')
+ fallback_object = Fallback(session_bus)
+ logger.info('creating mainloop')
+ loop = GObject.MainLoop()
+ logger.info('running')
+ loop.run()
+ logger.info('done')
+
+
+if __name__ == '__main__':
+ session_bus = None
+ try:
+ logger.info('entering main')
+ main()
+ except:
+ logger.exception('test-service main failure')
+ raise
diff --git a/test/test-signals.py b/test/test-signals.py
new file mode 100755
index 0000000..3c618c8
--- /dev/null
+++ b/test/test-signals.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2004 Red Hat Inc. <http://www.redhat.com/>
+# Copyright (C) 2005-2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+import os
+import unittest
+import time
+import logging
+
+import dbus
+import _dbus_bindings
+import dbus.glib
+import dbus.service
+
+try:
+ from tap.runner import TAPTestRunner
+except ImportError:
+ print('1..0 # SKIP cannot import TAPTestRunner')
+ raise SystemExit(0)
+
+try:
+ from gi.repository import GObject as gobject
+except ImportError:
+ print('1..0 # SKIP cannot import GObject')
+ raise SystemExit(0)
+
+logging.basicConfig()
+logging.getLogger().setLevel(1)
+logger = logging.getLogger('test-signals')
+
+if 'DBUS_TEST_UNINSTALLED' in os.environ:
+ builddir = os.path.normpath(os.environ["DBUS_TOP_BUILDDIR"])
+ pydir = os.path.normpath(os.environ["DBUS_TOP_SRCDIR"])
+ pkg = dbus.__file__
+
+ if not pkg.startswith(pydir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % pkg)
+
+ if not _dbus_bindings.__file__.startswith(builddir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % _dbus_bindings.__file__)
+
+NAME = "org.freedesktop.DBus.TestSuitePythonService"
+IFACE = "org.freedesktop.DBus.TestSuiteInterface"
+OBJECT = "/org/freedesktop/DBus/TestSuitePythonObject"
+
+
+class TestSignals(unittest.TestCase):
+ def setUp(self):
+ logger.info('setUp()')
+ self.bus = dbus.SessionBus()
+ self.remote_object = self.bus.get_object(NAME, OBJECT)
+ self.remote_object_fallback_trivial = self.bus.get_object(NAME,
+ OBJECT + '/Fallback')
+ self.remote_object_fallback = self.bus.get_object(NAME,
+ OBJECT + '/Fallback/Badger')
+ self.remote_object_follow = self.bus.get_object(NAME, OBJECT,
+ follow_name_owner_changes=True)
+ self.iface = dbus.Interface(self.remote_object, IFACE)
+ self.iface_follow = dbus.Interface(self.remote_object_follow, IFACE)
+ self.fallback_iface = dbus.Interface(self.remote_object_fallback, IFACE)
+ self.fallback_trivial_iface = dbus.Interface(
+ self.remote_object_fallback_trivial, IFACE)
+ self.in_test = None
+
+ def signal_test_impl(self, iface, name, test_removal=False):
+ self.in_test = name
+ # using append rather than assignment here to avoid scoping issues
+ result = []
+
+ def _timeout_handler():
+ logger.debug('_timeout_handler for %s: current state %s', name, self.in_test)
+ if self.in_test == name:
+ main_loop.quit()
+ def _signal_handler(s, sender, path):
+ logger.debug('_signal_handler for %s: current state %s', name, self.in_test)
+ if self.in_test not in (name, name + '+removed'):
+ return
+ logger.info('Received signal from %s:%s, argument is %r',
+ sender, path, s)
+ result.append('received')
+ main_loop.quit()
+ def _rm_timeout_handler():
+ logger.debug('_timeout_handler for %s: current state %s', name, self.in_test)
+ if self.in_test == name + '+removed':
+ main_loop.quit()
+
+ logger.info('Testing %s', name)
+ match = iface.connect_to_signal('SignalOneString', _signal_handler,
+ sender_keyword='sender',
+ path_keyword='path')
+ logger.info('Waiting for signal...')
+ iface.EmitSignal('SignalOneString', 0)
+ source_id = gobject.timeout_add(1000, _timeout_handler)
+ main_loop.run()
+ if not result:
+ raise AssertionError('Signal did not arrive within 1 second')
+ logger.debug('Removing match')
+ match.remove()
+ gobject.source_remove(source_id)
+ if test_removal:
+ self.in_test = name + '+removed'
+ logger.info('Testing %s', name)
+ result = []
+ iface.EmitSignal('SignalOneString', 0)
+ source_id = gobject.timeout_add(1000, _rm_timeout_handler)
+ main_loop.run()
+ if result:
+ raise AssertionError('Signal should not have arrived, but did')
+ gobject.source_remove(source_id)
+
+ def testFallback(self):
+ self.signal_test_impl(self.fallback_iface, 'Fallback')
+
+ def testFallbackTrivial(self):
+ self.signal_test_impl(self.fallback_trivial_iface, 'FallbackTrivial')
+
+ def testSignal(self):
+ self.signal_test_impl(self.iface, 'Signal')
+
+ def testRemoval(self):
+ self.signal_test_impl(self.iface, 'Removal', True)
+
+ def testSignalAgain(self):
+ self.signal_test_impl(self.iface, 'SignalAgain')
+
+ def testRemovalAgain(self):
+ self.signal_test_impl(self.iface, 'RemovalAgain', True)
+
+ def testSignalF(self):
+ self.signal_test_impl(self.iface_follow, 'Signal')
+
+ def testRemovalF(self):
+ self.signal_test_impl(self.iface_follow, 'Removal', True)
+
+ def testSignalAgainF(self):
+ self.signal_test_impl(self.iface_follow, 'SignalAgain')
+
+ def testRemovalAgainF(self):
+ self.signal_test_impl(self.iface_follow, 'RemovalAgain', True)
+
+if __name__ == '__main__':
+ main_loop = gobject.MainLoop()
+ gobject.threads_init()
+ dbus.glib.init_threads()
+
+ logger.info('Starting unit test')
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner)
diff --git a/test/test-standalone.py b/test/test-standalone.py
new file mode 100755
index 0000000..c68b1db
--- /dev/null
+++ b/test/test-standalone.py
@@ -0,0 +1,548 @@
+#!/usr/bin/env python
+
+"""Tests that don't need an active D-Bus connection to run, but can be
+run in isolation.
+"""
+
+# Copyright (C) 2006 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import unicode_literals
+
+import struct
+import sys
+import os
+import unittest
+
+try:
+ from tap.runner import TAPTestRunner
+except ImportError:
+ print('1..0 # SKIP cannot import TAPTestRunner')
+ raise SystemExit(0)
+
+import _dbus_bindings
+import dbus
+import dbus.lowlevel as lowlevel
+import dbus.types as types
+from dbus._compat import is_py2, is_py3
+
+if is_py3:
+ def make_long(n):
+ return n
+else:
+ def make_long(n):
+ return long(n)
+
+if 'DBUS_TEST_UNINSTALLED' in os.environ:
+ builddir = os.path.normpath(os.environ["DBUS_TOP_BUILDDIR"])
+ pydir = os.path.normpath(os.environ["DBUS_TOP_SRCDIR"])
+ pkg = dbus.__file__
+
+ if not pkg.startswith(pydir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % pkg)
+
+ if not _dbus_bindings.__file__.startswith(builddir):
+ raise Exception("DBus modules (%s) are not being picked up from the "
+ "package" % _dbus_bindings.__file__)
+
+ assert _dbus_bindings.__version__ == os.environ['DBUS_PYTHON_VERSION'], \
+ '_dbus_bindings was compiled as version %s but Automake says '\
+ 'we should be version %s' \
+ % (_dbus_bindings.__version__, os.environ['DBUS_PYTHON_VERSION'])
+
+assert (_dbus_bindings._python_version & 0xffff0000
+ == sys.hexversion & 0xffff0000), \
+ '_dbus_bindings was compiled for Python %x but this is Python %x, '\
+ 'a different major version'\
+ % (_dbus_bindings._python_version, sys.hexversion)
+
+def uni(x):
+ """Return a Unicode string consisting of the single Unicode character
+ with the given codepoint (represented as a surrogate pair if this is
+ a "narrow" Python build). This is a generalization of unichr().
+
+ uni(0x0001f639) == u'\\U0001f639' in versions where that syntax is
+ supported.
+ """
+ if x <= 0xFFFF:
+ if is_py3:
+ return chr(x)
+ else:
+ return unichr(x)
+ else:
+ return struct.pack('>I', x).decode('utf_32_be')
+
+class TestTypes(unittest.TestCase):
+
+ def test_Dictionary(self):
+ self.assertEqual(types.Dictionary({'foo':'bar'}), {'foo':'bar'})
+ self.assertEqual(types.Dictionary({}, variant_level=2), {})
+ self.assertEqual(types.Dictionary({}, variant_level=2).variant_level, 2)
+
+ def test_Array(self):
+ self.assertEqual(types.Array(['foo','bar']), ['foo','bar'])
+ self.assertEqual(types.Array([], variant_level=2), [])
+ self.assertEqual(types.Array([], variant_level=2).variant_level, 2)
+
+ def test_Double(self):
+ self.assertEqual(types.Double(0.0), 0.0)
+ self.assertEqual(types.Double(0.125, variant_level=2), 0.125)
+ self.assertEqual(types.Double(0.125, variant_level=2).variant_level, 2)
+
+ def test_Struct(self):
+ x = types.Struct(('',))
+ self.assertEqual(x.variant_level, 0)
+ self.assertEqual(x, ('',))
+ x = types.Struct('abc', variant_level=42)
+ self.assertEqual(x.variant_level, 42)
+ self.assertEqual(x, ('a','b','c'))
+
+ def test_Byte(self):
+ self.assertEqual(types.Byte(b'x', variant_level=2),
+ types.Byte(ord('x')))
+ self.assertEqual(types.Byte(1), 1)
+ self.assertEqual(types.Byte(make_long(1)), 1)
+ self.assertRaises(Exception, lambda: types.Byte(b'ab'))
+ self.assertRaises(TypeError, types.Byte, '\x12xxxxxxxxxxxxx')
+
+ # Byte from a unicode object: what would that even mean?
+ self.assertRaises(Exception,
+ lambda: types.Byte(b'a'.decode('latin-1')))
+
+ def test_ByteArray(self):
+ self.assertEqual(types.ByteArray(b''), b'')
+
+ def test_object_path_attr(self):
+ class MyObject(object):
+ __dbus_object_path__ = '/foo'
+ from _dbus_bindings import SignalMessage
+ self.assertEqual(SignalMessage.guess_signature(MyObject()), 'o')
+
+ def test_integers(self):
+ subclasses = [int]
+ if is_py2:
+ subclasses.append(long)
+ subclasses = tuple(subclasses)
+ # This is an API guarantee. Note that exactly which of these types
+ # are ints and which of them are longs is *not* guaranteed.
+ for cls in (types.Int16, types.UInt16, types.Int32, types.UInt32,
+ types.Int64, types.UInt64):
+ self.assertTrue(issubclass(cls, subclasses))
+ self.assertTrue(isinstance(cls(0), subclasses))
+ self.assertEqual(cls(0), 0)
+ self.assertEqual(cls(23, variant_level=1), 23)
+ self.assertEqual(cls(23, variant_level=1).variant_level, 1)
+
+ def test_integer_limits_16(self):
+ self.assertEqual(types.Int16(0x7fff), 0x7fff)
+ self.assertEqual(types.Int16(-0x8000), -0x8000)
+ self.assertEqual(types.UInt16(0xffff), 0xffff)
+ self.assertRaises(Exception, types.Int16, 0x8000)
+ self.assertRaises(Exception, types.Int16, -0x8001)
+ self.assertRaises(Exception, types.UInt16, 0x10000)
+
+ def test_integer_limits_32(self):
+ self.assertEqual(types.Int32(0x7fffffff), 0x7fffffff)
+ self.assertEqual(types.Int32(make_long(-0x80000000)),
+ make_long(-0x80000000))
+ self.assertEqual(types.UInt32(make_long(0xffffffff)),
+ make_long(0xffffffff))
+ self.assertRaises(Exception, types.Int32, make_long(0x80000000))
+ self.assertRaises(Exception, types.Int32, make_long(-0x80000001))
+ self.assertRaises(Exception, types.UInt32, make_long(0x100000000))
+
+ def test_integer_limits_64(self):
+ self.assertEqual(types.Int64(make_long(0x7fffffffffffffff)),
+ make_long(0x7fffffffffffffff))
+ self.assertEqual(types.Int64(make_long(-0x8000000000000000)),
+ make_long(-0x8000000000000000))
+ self.assertEqual(types.UInt64(make_long(0xffffffffffffffff)),
+ make_long(0xffffffffffffffff))
+ self.assertRaises(Exception, types.Int16,
+ make_long(0x8000000000000000))
+ self.assertRaises(Exception, types.Int16,
+ make_long(-0x8000000000000001))
+ self.assertRaises(Exception, types.UInt16,
+ make_long(0x10000000000000000))
+
+ def test_Signature(self):
+ self.assertRaises(Exception, types.Signature, 'a')
+ self.assertEqual(types.Signature('ab', variant_level=23), 'ab')
+ self.assertTrue(isinstance(types.Signature('ab'), str))
+ self.assertEqual(tuple(types.Signature('ab(xt)a{sv}')),
+ ('ab', '(xt)', 'a{sv}'))
+ self.assertTrue(isinstance(tuple(types.Signature('ab'))[0],
+ types.Signature))
+
+
+class TestMessageMarshalling(unittest.TestCase):
+
+ def test_path(self):
+ s = lowlevel.SignalMessage('/a/b/c', 'foo.bar', 'baz')
+ self.assertEqual(s.get_path(), types.ObjectPath('/a/b/c'))
+ self.assertEqual(type(s.get_path()), types.ObjectPath)
+ self.assertEqual(s.get_path_decomposed(), ['a', 'b', 'c'])
+ # this is true in both major versions: it's a bytestring in
+ # Python 2 and a Unicode string in Python 3
+ self.assertEqual(type(s.get_path_decomposed()[0]), str)
+ self.assertTrue(s.has_path('/a/b/c'))
+ self.assertFalse(s.has_path('/a/b'))
+ self.assertFalse(s.has_path('/a/b/c/d'))
+
+ s = lowlevel.SignalMessage('/', 'foo.bar', 'baz')
+ self.assertEqual(s.get_path(), types.ObjectPath('/'))
+ self.assertEqual(s.get_path().__class__, types.ObjectPath)
+ self.assertEqual(s.get_path_decomposed(), [])
+ self.assertTrue(s.has_path('/'))
+ self.assertFalse(s.has_path(None))
+
+ def test_sender(self):
+ s = lowlevel.SignalMessage('/a/b/c', 'foo.bar', 'baz')
+ self.assertEqual(s.get_sender(), None)
+ self.assertFalse(s.has_sender(':1.23'))
+ s.set_sender(':1.23')
+ self.assertEqual(s.get_sender(), ':1.23')
+ # bytestring in Python 2, Unicode string in Python 3
+ self.assertEqual(type(s.get_sender()), str)
+ self.assertTrue(s.has_sender(':1.23'))
+
+ def test_destination(self):
+ s = lowlevel.SignalMessage('/a/b/c', 'foo.bar', 'baz')
+ self.assertEqual(s.get_destination(), None)
+ self.assertFalse(s.has_destination(':1.23'))
+ s.set_destination(':1.23')
+ self.assertEqual(s.get_destination(), ':1.23')
+ # bytestring in Python 2, Unicode string in Python 3
+ self.assertEqual(type(s.get_destination()), str)
+ self.assertTrue(s.has_destination(':1.23'))
+
+ def test_interface(self):
+ s = lowlevel.SignalMessage('/a/b/c', 'foo.bar', 'baz')
+ self.assertEqual(s.get_interface(), 'foo.bar')
+ # bytestring in Python 2, Unicode string in Python 3
+ self.assertEqual(type(s.get_interface()), str)
+ self.assertTrue(s.has_interface('foo.bar'))
+
+ def test_member(self):
+ s = lowlevel.SignalMessage('/a/b/c', 'foo.bar', 'baz')
+ self.assertEqual(s.get_member(), 'baz')
+ # bytestring in Python 2, Unicode string in Python 3
+ self.assertEqual(type(s.get_member()), str)
+ self.assertTrue(s.has_member('baz'))
+
+ def test_count(self):
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ try:
+ s.append('a', signature='ss')
+ except TypeError:
+ pass
+ else:
+ raise AssertionError('Appending too few things in a message '
+ 'should fail')
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ try:
+ s.append('a','b','c', signature='ss')
+ except TypeError:
+ pass
+ else:
+ raise AssertionError('Appending too many things in a message '
+ 'should fail')
+
+ def test_append(self):
+ aeq = self.assertEqual
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append([types.Byte(1)], signature='ay')
+ aeq(s.get_signature(), 'ay')
+ aeq(s.get_args_list(), [[types.Byte(1)]])
+
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append([], signature='ay')
+ aeq(s.get_args_list(), [[]])
+
+ def test_append_Byte(self):
+ aeq = self.assertEqual
+ from _dbus_bindings import SignalMessage
+
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(0xFE, signature='y')
+ aeq(s.get_args_list(), [types.Byte(0xFE)])
+
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(b'\xfe', signature='y')
+ aeq(s.get_args_list(), [types.Byte(0xFE)])
+
+ # appending a unicode object (including str in Python 3)
+ # is not allowed
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ self.assertRaises(Exception,
+ lambda: s.append('a'.decode('latin-1'), signature='y'))
+
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ self.assertRaises(Exception,
+ lambda: s.append(b'ab', signature='y'))
+
+ def test_append_ByteArray(self):
+ aeq = self.assertEqual
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(types.ByteArray(b'ab'), signature='ay')
+ aeq(s.get_args_list(), [[types.Byte(b'a'), types.Byte(b'b')]])
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(types.ByteArray(b'ab'), signature='av')
+ aeq(s.get_args_list(), [[types.Byte(b'a'), types.Byte(b'b')]])
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(types.ByteArray(b''), signature='ay')
+ aeq(s.get_args_list(), [[]])
+ aeq(s.get_args_list(byte_arrays=True), [types.ByteArray(b'')])
+
+ def test_append_Variant(self):
+ aeq = self.assertEqual
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(types.Int32(1, variant_level=0),
+ types.String('a', variant_level=42),
+ types.Array([types.Byte(b'a', variant_level=1),
+ types.UInt32(123, variant_level=1)],
+ signature='v'),
+ signature='vvv')
+ aeq(s.get_signature(), 'vvv')
+ args = s.get_args_list()
+ aeq(args[0].__class__, types.Int32)
+ aeq(args[0].variant_level, 1)
+ aeq(args[1].__class__, types.String)
+ aeq(args[1].variant_level, 42)
+ aeq(args[2].__class__, types.Array)
+ aeq(args[2].variant_level, 1)
+ aeq(args[2].signature, 'v')
+
+ def test_guess_signature(self):
+ aeq = self.assertEqual
+ from _dbus_bindings import Message
+ aeq(Message.guess_signature(('a','b')), '(ss)')
+ aeq(Message.guess_signature('a','b'), 'ss')
+ aeq(Message.guess_signature(['a','b']), 'as')
+ aeq(Message.guess_signature(('a',)), '(s)')
+ aeq(Message.guess_signature('abc'), 's')
+ aeq(Message.guess_signature(types.Int32(123)), 'i')
+ aeq(Message.guess_signature(types.ByteArray(b'abc')), 'ay')
+ aeq(Message.guess_signature(('a',)), '(s)')
+ aeq(Message.guess_signature(['a']), 'as')
+ aeq(Message.guess_signature({'a':'b'}), 'a{ss}')
+ aeq(Message.guess_signature(types.ObjectPath('/')), 'o')
+ aeq(Message.guess_signature(types.Signature('x')), 'g')
+
+ def test_guess_signature_python_ints(self):
+ aeq = self.assertEqual
+ from _dbus_bindings import Message
+ aeq(Message.guess_signature(7), 'i')
+ if is_py2:
+ aeq(Message.guess_signature(make_long(7)), 'x')
+
+ def test_guess_signature_dbus_types(self):
+ aeq = self.assertEqual
+ from _dbus_bindings import Message
+ gs = Message.guess_signature
+ aeq(gs(types.Dictionary({'a':'b'})), 'a{ss}')
+ aeq(gs(types.Dictionary({'a':'b'}, signature='sv')), 'a{sv}')
+ aeq(gs(types.Dictionary({}, signature='iu')), 'a{iu}')
+ aeq(gs(types.Array([types.Int32(1)])), 'ai')
+ aeq(gs(types.Array([types.Int32(1)], signature='u')), 'au')
+
+ def test_get_args_options(self):
+ aeq = self.assertEqual
+ s = _dbus_bindings.SignalMessage('/', 'foo.bar', 'baz')
+ s.append(b'b', b'bytes', -1, 1, 'str', 'var', signature='yayiusv')
+ aeq(s.get_args_list(), [
+ ord('b'),
+ [ord('b'),ord('y'),ord('t'),ord('e'), ord('s')],
+ -1, 1, 'str', 'var'
+ ])
+ byte, bytes, int32, uint32, string, variant = s.get_args_list()
+ aeq(byte.__class__, types.Byte)
+ aeq(bytes.__class__, types.Array)
+ aeq(bytes[0].__class__, types.Byte)
+ aeq(int32.__class__, types.Int32)
+ aeq(uint32.__class__, types.UInt32)
+ aeq(string.__class__, types.String)
+ aeq(string.variant_level, 0)
+ aeq(variant.__class__, types.String)
+ aeq(variant.variant_level, 1)
+
+ byte, bytes, int32, uint32, string, variant = s.get_args_list(
+ byte_arrays=True)
+ aeq(byte.__class__, types.Byte)
+ aeq(bytes.__class__, types.ByteArray)
+ aeq(bytes, b'bytes')
+ if is_py3:
+ aeq(bytes[0].__class__, int)
+ else:
+ aeq(bytes[0].__class__, str)
+ aeq(int32.__class__, types.Int32)
+ aeq(uint32.__class__, types.UInt32)
+ aeq(string.__class__, types.String)
+ aeq(variant.__class__, types.String)
+ aeq(variant.variant_level, 1)
+
+ kwargs = {}
+ if is_py2:
+ kwargs['utf8_strings'] = True
+ byte, bytes, int32, uint32, string, variant = s.get_args_list(
+ **kwargs)
+ aeq(byte.__class__, types.Byte)
+ aeq(bytes.__class__, types.Array)
+ aeq(bytes[0].__class__, types.Byte)
+ aeq(int32.__class__, types.Int32)
+ aeq(uint32.__class__, types.UInt32)
+ if is_py2:
+ aeq(string.__class__, types.UTF8String)
+ aeq(string, 'str')
+ if is_py2:
+ aeq(variant.__class__, types.UTF8String)
+ aeq(variant.variant_level, 1)
+ aeq(variant, 'var')
+
+ def test_object_path_attr(self):
+ from _dbus_bindings import SignalMessage
+ class MyObject(object):
+ __dbus_object_path__ = '/foo'
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(MyObject(), signature='o')
+ s.append(MyObject())
+ self.assertEqual(s.get_args_list(), ['/foo', '/foo'])
+
+ def test_struct(self):
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ try:
+ s.append(('a',), signature='(ss)')
+ except TypeError:
+ pass
+ else:
+ raise AssertionError('Appending too few things in a struct '
+ 'should fail')
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ try:
+ s.append(('a','b','c'), signature='(ss)')
+ except TypeError:
+ pass
+ else:
+ raise AssertionError('Appending too many things in a struct '
+ 'should fail')
+
+ def test_utf8(self):
+ from _dbus_bindings import SignalMessage
+
+ for bad in [
+ uni(0xD800),
+ b'\xed\xa0\x80',
+ ]:
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ try:
+ s.append(bad, signature='s')
+ except UnicodeError:
+ pass
+ else:
+ raise AssertionError('Appending %r should fail' % bad)
+ for good in [
+ uni(0xfdcf),
+ uni(0xfdf0),
+ uni(0xfeff),
+ uni(0x0001feff),
+ uni(0x00020000),
+ uni(0x0007feff),
+ uni(0x00080000),
+ uni(0x0010feff),
+ ]:
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(good, signature='s')
+ s.append(good.encode('utf-8'), signature='s')
+ for noncharacter in [
+ uni(0xFDD0),
+ b'\xef\xb7\x90',
+ uni(0xFDD7),
+ b'\xef\xb7\x97',
+ uni(0xFDEF),
+ b'\xef\xb7\xaf',
+ uni(0xFFFE),
+ b'\xef\xbf\xbe',
+ uni(0xFFFF),
+ b'\xef\xbf\xbf',
+ uni(0x0001FFFE),
+ b'\xf0\x9f\xbf\xbe',
+ uni(0x0001FFFF),
+ b'\xf0\x9f\xbf\xbf',
+ uni(0x0007FFFE),
+ b'\xf1\xbf\xbf\xbe',
+ uni(0x0007FFFF),
+ b'\xf1\xbf\xbf\xbf',
+ uni(0x0010FFFE),
+ b'\xf4\x8f\xbf\xbe',
+ uni(0x0010FFFF),
+ b'\xf4\x8f\xbf\xbf',
+ ]:
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ try:
+ s.append(noncharacter, signature='s')
+ except UnicodeError:
+ pass # libdbus < 1.6.10 disallows noncharacters
+ else:
+ pass # libdbus >= 1.6.10 allows noncharacters
+
+class TestMatching(unittest.TestCase):
+ def setUp(self):
+ from _dbus_bindings import SignalMessage
+ from dbus.connection import SignalMatch
+ self._message = SignalMessage('/', 'a.b', 'c')
+ class FakeConn(object): pass
+ def ignore_cb(*args, **kws): pass
+ self._match = SignalMatch(FakeConn(), None, '/', None, None,
+ ignore_cb, arg0='/')
+
+ def test_string_match(self):
+ self._message.append('/', signature='s')
+ self.assertTrue(self._match.maybe_handle_message(self._message))
+
+ def test_object_path_no_match(self):
+ self._message.append('/', signature='o')
+ self.assertFalse(self._match.maybe_handle_message(self._message))
+
+class TestVersion(unittest.TestCase):
+ if sys.version_info[:2] < (2, 7):
+ def assertGreater(self, first, second):
+ self.assertTrue(first > second)
+
+ def assertLess(self, first, second):
+ self.assertTrue(first < second)
+
+ def test_version(self):
+ self.assertGreater(dbus.version, (0, 41))
+ self.assertLess(dbus.version, (23, 0))
+ self.assertGreater(dbus.__version__, '0')
+ self.assertLess(dbus.__version__, '9')
+
+if __name__ == '__main__':
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner, verbosity=2)
diff --git a/test/test-unusable-main-loop.py b/test/test-unusable-main-loop.py
new file mode 100755
index 0000000..6e81315
--- /dev/null
+++ b/test/test-unusable-main-loop.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+from __future__ import print_function
+
+import unittest
+
+try:
+ from tap.runner import TAPTestRunner
+except ImportError:
+ print('1..0 # SKIP cannot import TAPTestRunner')
+ raise SystemExit(0)
+
+import dbus
+
+from dbus_py_test import UnusableMainLoop
+
+class Test(unittest.TestCase):
+ def test_unusable_main_loop(self):
+ UnusableMainLoop(set_as_default=True)
+ self.assertRaises(ValueError, lambda: dbus.SessionBus())
+
+if __name__ == '__main__':
+ runner = TAPTestRunner()
+ runner.set_stream(True)
+ unittest.main(testRunner=runner)
diff --git a/test/tmp-session-bus.conf.in b/test/tmp-session-bus.conf.in
new file mode 100644
index 0000000..f5fd435
--- /dev/null
+++ b/test/tmp-session-bus.conf.in
@@ -0,0 +1,21 @@
+<!-- @configure_input@ -->
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <!-- Our well-known bus type, don't change this -->
+ <type>session</type>
+ <listen>unix:tmpdir=/tmp</listen>
+
+ <servicedir>@G_TEST_BUILDDIR@/test</servicedir>
+
+ <policy context="default">
+ <!-- Allow everything to be sent -->
+ <allow send_destination="*"/>
+ <!-- Allow everything to be received -->
+ <allow eavesdrop="true"/>
+ <!-- Allow anyone to own anything -->
+ <allow own="*"/>
+ </policy>
+
+</busconfig>
+<!-- vim:set ft=xml sts=2 sw=2 et: -->
diff --git a/test/wait-for-name.py b/test/wait-for-name.py
new file mode 100755
index 0000000..344f641
--- /dev/null
+++ b/test/wait-for-name.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+# Copyright © 2016 Simon McVittie
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+import sys
+
+from gi.repository import GLib
+
+import dbus
+import dbus.mainloop.glib
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ timed_out = False
+ name = sys.argv[1]
+ bus = dbus.SessionBus()
+ loop = GLib.MainLoop()
+
+ def time_out(*args):
+ global timed_out
+ timed_out = True
+ loop.quit()
+
+ GLib.timeout_add_seconds(30, time_out)
+
+ def name_cb(owner):
+ if owner:
+ loop.quit()
+
+ bus.watch_name_owner(name, name_cb)
+
+ loop.run()
+
+ if timed_out:
+ raise SystemExit('timed out')
diff --git a/test/with-session-bus.sh b/test/with-session-bus.sh
new file mode 100755
index 0000000..d20701d
--- /dev/null
+++ b/test/with-session-bus.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+# with-session-bus.sh - run a program with a temporary D-Bus session daemon
+#
+# Copyright (C) 2007-2008 Collabora Ltd. <http://www.collabora.co.uk/>
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.
+
+set -e
+
+me=with-session-bus
+
+dbus_daemon_args="--print-address=5 --print-pid=6 --fork"
+sleep=0
+
+usage ()
+{
+ echo "usage: $me [options] -- program [program_options]" >&2
+ echo "Requires write access to the current directory." >&2
+ echo "" >&2
+ echo "If \$WITH_SESSION_BUS_FORK_DBUS_MONITOR is set, fork dbus-monitor" >&2
+ echo "with the arguments in \$WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT." >&2
+ echo "The output of dbus-monitor is saved in $me-<pid>.dbus-monitor-logs" >&2
+ exit 2
+}
+
+while test "z$1" != "z--"; do
+ case "$1" in
+ --sleep=*)
+ sleep="$1"
+ sleep="${sleep#--sleep=}"
+ shift
+ ;;
+ --session)
+ dbus_daemon_args="$dbus_daemon_args --session"
+ shift
+ ;;
+ --config-file=*)
+ # FIXME: assumes config file doesn't contain any special characters
+ dbus_daemon_args="$dbus_daemon_args $1"
+ shift
+ ;;
+ *)
+ usage
+ ;;
+ esac
+done
+shift
+if test "z$1" = "z"; then usage; fi
+
+exec 5> $me-$$.address
+exec 6> $me-$$.pid
+
+cleanup ()
+{
+ pid=`head -n1 $me-$$.pid`
+ if test -n "$pid" ; then
+ if [ -n "$VERBOSE_TESTS" ]; then
+ echo "Killing temporary bus daemon: $pid" >&2
+ fi
+ kill -INT "$pid"
+ fi
+ rm -f $me-$$.address
+ rm -f $me-$$.pid
+}
+
+trap cleanup INT HUP TERM
+dbus-daemon $dbus_daemon_args
+
+if [ -n "$VERBOSE_TESTS" ]; then
+ { echo -n "Temporary bus daemon is "; cat $me-$$.address; } >&2
+ { echo -n "Temporary bus daemon PID is "; head -n1 $me-$$.pid; } >&2
+fi
+
+e=0
+
+# These might be non-null when run from e.g. gnome-terminal 3.8, which uses
+# an activatable service for its windows; we don't want to inherit them either
+unset DBUS_STARTER_ADDRESS
+unset DBUS_STARTER_BUS_TYPE
+
+DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`"
+export DBUS_SESSION_BUS_ADDRESS
+DBUS_SESSION_BUS_PID="`cat $me-$$.pid`"
+export DBUS_SESSION_BUS_PID
+
+if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then
+ echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2
+ dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \
+ > $me-$$.dbus-monitor-logs 2>&1 &
+fi
+
+"$@" || e=$?
+
+if test $sleep != 0; then
+ sleep $sleep
+fi
+
+trap - INT HUP TERM
+cleanup
+
+exit $e
diff --git a/tools/check-c-style.sh b/tools/check-c-style.sh
new file mode 100644
index 0000000..177355e
--- /dev/null
+++ b/tools/check-c-style.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+fail=0
+
+/bin/sh "${top_srcdir}"/tools/check-whitespace.sh "$@" || fail=$?
+
+# at the moment we're not actually checking much C style here... to be added
+
+if test -n "$CHECK_FOR_LONG_LINES"
+then
+ if egrep -n '.{80,}' "$@"
+ then
+ echo "^^^ The above files contain long lines"
+ fail=1
+ fi
+fi
+
+exit $fail
diff --git a/tools/check-coding-style.mk b/tools/check-coding-style.mk
new file mode 100644
index 0000000..7496b35
--- /dev/null
+++ b/tools/check-coding-style.mk
@@ -0,0 +1,26 @@
+check-local::
+ @fail=0; \
+ cd $(srcdir) || exit $$?; \
+ if test -n "$(check_misc_sources)"; then \
+ echo check-coding-style.mk: checking misc sources...; \
+ top_srcdir=$(abs_top_srcdir) \
+ sh $(abs_top_srcdir)/tools/check-whitespace.sh \
+ $(check_misc_sources) || fail=1; \
+ fi; \
+ if test -n "$(check_py_sources)"; then \
+ echo check-coding-style.mk: checking Python sources...; \
+ top_srcdir=$(abs_top_srcdir) \
+ sh $(abs_top_srcdir)/tools/check-py-style.sh \
+ $(check_py_sources) || fail=1; \
+ fi;\
+ if test -n "$(check_c_sources)"; then \
+ echo check-coding-style.mk: checking C sources...; \
+ top_srcdir=$(abs_top_srcdir) \
+ sh $(abs_top_srcdir)/tools/check-c-style.sh \
+ $(check_c_sources) || fail=1; \
+ fi;\
+ if test yes = "@ENABLE_CODING_STYLE_CHECKS@"; then \
+ exit "$$fail";\
+ else \
+ exit 0;\
+ fi
diff --git a/tools/check-py-style.sh b/tools/check-py-style.sh
new file mode 100644
index 0000000..16a2c3a
--- /dev/null
+++ b/tools/check-py-style.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+fail=0
+
+/bin/sh "${top_srcdir}"/tools/check-whitespace.sh "$@" || fail=$?
+
+# at the moment we're not actually checking much Python style here...
+# to be added
+
+if test -n "$CHECK_FOR_LONG_LINES"
+then
+ if egrep -n '.{80,}' "$@"
+ then
+ echo "^^^ The above files contain long lines"
+ fail=1
+ fi
+fi
+
+exit $fail
diff --git a/tools/check-whitespace.sh b/tools/check-whitespace.sh
new file mode 100644
index 0000000..5348331
--- /dev/null
+++ b/tools/check-whitespace.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+fail=0
+
+if grep -n ' $' "$@"
+then
+ echo "^^^ The above files contain unwanted trailing spaces"
+ fail=1
+fi
+
+if grep -n ' ' "$@"
+then
+ echo "^^^ The above files contain tabs"
+ fail=1
+fi
+
+exit $fail
diff --git a/tools/ci-build.sh b/tools/ci-build.sh
new file mode 100755
index 0000000..a643fb9
--- /dev/null
+++ b/tools/ci-build.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+
+# Copyright © 2016 Simon McVittie
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use, copy,
+# modify, merge, publish, distribute, sublicense, and/or sell copies
+# of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+set -e
+set -x
+
+NULL=
+srcdir="$(pwd)"
+builddir="$(mktemp -d -t "builddir.XXXXXX")"
+prefix="$(mktemp -d -t "prefix.XXXXXX")"
+
+if [ -n "$dbus_ci_parallel" ]; then
+ dbus_ci_parallel=2
+fi
+
+if [ -n "$TRAVIS" ] && [ -n "$dbus_ci_system_python" ]; then
+ # Reset to standard paths to use the Ubuntu version of python
+ unset LDFLAGS
+ unset PYTHONPATH
+ unset PYTHON_CFLAGS
+ unset PYTHON_CONFIGURE_OPTS
+ unset VIRTUAL_ENV
+ export PATH=/usr/bin:/bin
+ export PYTHON="$(command -v "$dbus_ci_system_python")"
+
+ case "$dbus_ci_system_python" in
+ (python-dbg|python2.7-dbg)
+ # This is a workaround. Python 2 doesn't have the
+ # LDVERSION sysconfig variable, which would give
+ # AX_PYTHON_DEVEL the information it needs to know
+ # that it should link -lpython2.7_d and not
+ # -lpython2.7.
+ export PYTHON_LIBS="-lpython${TRAVIS_PYTHON_VERSION}_d"
+ ;;
+ esac
+
+elif [ -n "$TRAVIS_PYTHON_VERSION" ]; then
+ # Possibly in a virtualenv
+ dbus_ci_bindir="$(python -c 'import sys; print(sys.prefix)')"/bin
+ # The real underlying paths, even if we have a virtualenv
+ # e.g. /opt/pythonwhatever/bin on travis-ci
+ dbus_ci_real_bindir="$(python -c 'import distutils.sysconfig; print(distutils.sysconfig.get_config_var("BINDIR"))')"
+ dbus_ci_real_libdir="$(python -c 'import distutils.sysconfig; print(distutils.sysconfig.get_config_var("LIBDIR"))')"
+
+ # We want the virtualenv bindir for python itself, then the real bindir
+ # for python[X.Y]-config (which isn't copied into the virtualenv, so we
+ # risk picking up the wrong one from travis-ci's PATH if we don't
+ # do this)
+ export PATH="${dbus_ci_bindir}:${dbus_ci_real_bindir}:${PATH}"
+ # travis-ci's /opt/pythonwhatever/lib isn't on the library search path
+ export LD_LIBRARY_PATH="${dbus_ci_real_libdir}"
+ # travis-ci's Python 2 library is static, so it raises warnings
+ # about tmpnam_r and tempnam
+ case "$TRAVIS_PYTHON_VERSION" in
+ (2*) export LDFLAGS=-Wl,--no-fatal-warnings;;
+ esac
+fi
+
+# dbus-run-session is significantly nicer to debug than with-session-bus.sh,
+# but isn't in the version of dbus in Ubuntu 14.10. Take the version from
+# dbus-1.10.0 and alter it to be standalone.
+if ! command -v dbus-run-session >/dev/null; then
+ drsdir="$(mktemp -d -t "d-r-s.XXXXXX")"
+ curl -o "$drsdir/dbus-run-session.c" \
+ "https://cgit.freedesktop.org/dbus/dbus/plain/tools/dbus-run-session.c?h=dbus-1.10.0"
+ sed -e 's/^ //' > "$drsdir/config.h" <<EOF
+ #include <stdlib.h>
+
+ #define VERSION "1.10.0~local"
+ #define dbus_setenv my_dbus_setenv
+
+ static inline int
+ my_dbus_setenv (const char *name, const char *value)
+ {
+ if (value)
+ return !setenv (name, value, 1);
+ else
+ return !unsetenv (name);
+ }
+EOF
+ cc -I"${drsdir}" -o"${drsdir}/dbus-run-session" \
+ "${drsdir}/dbus-run-session.c" \
+ $(pkg-config --cflags --libs dbus-1) \
+ ${NULL}
+ export PATH="${drsdir}:$PATH"
+
+ # Force the build to be run even though dbus is less than version 1.8.
+ export DBUS_CFLAGS="$(pkg-config --cflags dbus-1)"
+ export DBUS_LIBS="$(pkg-config --libs dbus-1)"
+fi
+
+NOCONFIGURE=1 ./autogen.sh
+
+e=0
+(
+ cd "$builddir" && "${srcdir}/configure" \
+ --enable-installed-tests \
+ --prefix="$prefix" \
+ ${NULL}
+) || e=1
+if [ "x$e" != x0 ] || [ -n "$TRAVIS" ]; then
+ cat "$builddir/config.log"
+fi
+test "x$e" = x0
+
+make="make -j${dbus_ci_parallel} V=1 VERBOSE=1"
+
+$make -C "$builddir"
+$make -C "$builddir" check
+$make -C "$builddir" distcheck
+$make -C "$builddir" install
+( cd "$prefix" && find . -ls )
+
+dbus_ci_pyversion="$(${PYTHON:-python} -c 'import distutils.sysconfig; print(distutils.sysconfig.get_config_var("VERSION"))')"
+export PYTHONPATH="$prefix/lib/python$dbus_ci_pyversion/site-packages:$PYTHONPATH"
+export XDG_DATA_DIRS="$prefix/share:/usr/local/share:/usr/share"
+gnome-desktop-testing-runner dbus-python
+
+# re-run the tests with dbus-python only installed via pip
+if [ -n "$VIRTUAL_ENV" ]; then
+ rm -fr "${prefix}/lib/python$dbus_ci_pyversion/site-packages"
+ pip install -vvv "${builddir}"/dbus-python-*.tar.gz
+ gnome-desktop-testing-runner dbus-python
+fi
diff --git a/tools/travis-ci-setup.sh b/tools/travis-ci-setup.sh
new file mode 100755
index 0000000..a28d050
--- /dev/null
+++ b/tools/travis-ci-setup.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+set -e
+set -u
+NULL=
+
+sudo apt-get -qq -y update
+sudo apt-get -qq -y install \
+ autoconf \
+ automake \
+ ccache \
+ dbus \
+ gnome-desktop-testing \
+ libdbus-1-dev \
+ libtool \
+ ${NULL}
+
+if [ -n "${dbus_ci_system_python-}" ]; then
+ sudo apt-get -qq -y install \
+ ${dbus_ci_system_python} \
+ ${dbus_ci_system_python%-dbg}-docutils \
+ ${dbus_ci_system_python%-dbg}-gi \
+ ${dbus_ci_system_python%-dbg}-pip \
+ ${dbus_ci_system_python%-dbg}-setuptools \
+ ${NULL}
+
+ if [ "${dbus_ci_system_python%-dbg}" != "${dbus_ci_system_python}" ]; then
+ sudo apt-get -qq -y install ${dbus_ci_system_python%-dbg}-gi-dbg
+ fi
+
+ if [ "$dbus_ci_system_python" = python ]; then
+ sudo apt-get -qq -y install python-gobject-2
+ fi
+fi
+
+wget \
+http://deb.debian.org/debian/pool/main/a/autoconf-archive/autoconf-archive_20160916-1_all.deb
+sudo dpkg -i autoconf-archive_*_all.deb
+rm autoconf-archive_*_all.deb
+
+if [ -n "${dbus_ci_system_python-}" ]; then
+ "$dbus_ci_system_python" -m pip install --user \
+ sphinx \
+ sphinx_rtd_theme \
+ tap.py \
+ ${NULL}
+else
+ pip install \
+ sphinx \
+ sphinx_rtd_theme \
+ tap.py \
+ ${NULL}
+fi