alien-everywhere/shimming/alienkeyboardservice/dbussy.py

7022 lines
267 KiB
Python
Raw Normal View History

2023-12-20 10:44:46 +00:00
"""
Pure-Python binding for D-Bus <https://www.freedesktop.org/wiki/Software/dbus/>,
built around libdbus <https://dbus.freedesktop.org/doc/api/html/index.html>.
This Python binding supports hooking into event loops via Pythons standard
asyncio module.
"""
#+
# Copyright 2017-2020 Lawrence D'Oliveiro <ldo@geek-central.gen.nz>.
# Licensed under the GNU Lesser General Public License v2.1 or later.
#-
import os
import builtins
import operator
import array
import enum
import ctypes as ct
from weakref import \
ref as weak_ref, \
WeakValueDictionary
import threading
import io
import atexit
import asyncio
import functools
from xml.etree import \
ElementTree as XMLElementTree
from xml.sax.saxutils import \
quoteattr as quote_xml_attr
dbus = ct.cdll.LoadLibrary("libdbus-1.so.3")
class DBUS :
"useful definitions adapted from the D-Bus includes. You will need to use the" \
" constants, but apart from that, see the more Pythonic wrappers defined outside" \
" this class in preference to accessing low-level structures directly."
# General ctypes gotcha: when passing addresses of ctypes-constructed objects
# to routine calls, do not construct the objects directly in the call. Otherwise
# the refcount goes to 0 before the routine is actually entered, and the object
# can get prematurely disposed. Always store the object reference into a local
# variable, and pass the value of the variable instead.
# from dbus-protocol.h:
# Message byte order
LITTLE_ENDIAN = 'l'
BIG_ENDIAN = 'B'
# Protocol version.
MAJOR_PROTOCOL_VERSION = 1
# Type code that is never equal to a legitimate type code
TYPE_INVALID = 0
# Primitive types
TYPE_BYTE = ord('y') # 8-bit unsigned integer
TYPE_BOOLEAN = ord('b') # boolean
TYPE_INT16 = ord('n') # 16-bit signed integer
TYPE_UINT16 = ord('q') # 16-bit unsigned integer
TYPE_INT32 = ord('i') # 32-bit signed integer
TYPE_UINT32 = ord('u') # 32-bit unsigned integer
TYPE_INT64 = ord('x') # 64-bit signed integer
TYPE_UINT64 = ord('t') # 64-bit unsigned integer
TYPE_DOUBLE = ord('d') # 8-byte double in IEEE 754 format
TYPE_STRING = ord('s') # UTF-8 encoded, nul-terminated Unicode string
TYPE_OBJECT_PATH = ord('o') # D-Bus object path
TYPE_SIGNATURE = ord('g') # D-Bus type signature
TYPE_UNIX_FD = ord('h') # unix file descriptor
basic_to_ctypes = \
{ # ctypes objects suitable for holding values of D-Bus types
TYPE_BYTE : ct.c_ubyte,
TYPE_BOOLEAN : ct.c_ubyte,
TYPE_INT16 : ct.c_short,
TYPE_UINT16 : ct.c_ushort,
TYPE_INT32 : ct.c_int,
TYPE_UINT32 : ct.c_uint,
TYPE_INT64 : ct.c_longlong,
TYPE_UINT64 : ct.c_ulonglong,
TYPE_DOUBLE : ct.c_double,
TYPE_STRING : ct.c_char_p,
TYPE_OBJECT_PATH : ct.c_char_p,
TYPE_SIGNATURE : ct.c_char_p,
TYPE_UNIX_FD : ct.c_int,
}
def int_subtype(i, bits, signed) :
"returns integer i after checking that it fits in the given number of bits."
if not isinstance(i, int) :
raise TypeError("value is not int: %s" % repr(i))
#end if
if signed :
lo = - 1 << bits - 1
hi = (1 << bits - 1) - 1
else :
lo = 0
hi = (1 << bits) - 1
#end if
if i < lo or i > hi :
raise ValueError \
(
"%d not in range of %s %d-bit value" % (i, ("unsigned", "signed")[signed], bits)
)
#end if
return \
i
#end int_subtype
subtype_boolean = lambda i : DBUS.int_subtype(i, 1, False)
subtype_byte = lambda i : DBUS.int_subtype(i, 8, False)
subtype_int16 = lambda i : DBUS.int_subtype(i, 16, True)
subtype_uint16 = lambda i : DBUS.int_subtype(i, 16, False)
subtype_int32 = lambda i : DBUS.int_subtype(i, 32, True)
subtype_uint32 = lambda i : DBUS.int_subtype(i, 32, False)
subtype_int64 = lambda i : DBUS.int_subtype(i, 64, True)
subtype_uint64 = lambda i : DBUS.int_subtype(i, 64, False)
int_convert = \
{ # range checks for the various D-Bus integer types
TYPE_BOOLEAN : subtype_boolean,
TYPE_BYTE : subtype_byte,
TYPE_INT16 : subtype_int16,
TYPE_UINT16 : subtype_uint16,
TYPE_INT32 : subtype_int32,
TYPE_UINT32 : subtype_uint32,
TYPE_INT64 : subtype_int64,
TYPE_UINT64 : subtype_uint64,
}
# subclasses for distinguishing various special kinds of D-Bus values:
class ObjectPath(str) :
"an object path string."
def __repr__(self) :
return \
"%s(%s)" % (self.__class__.__name__, super().__repr__())
#end __repr__
#end ObjectPath
class Signature(str) :
"a type-signature string."
def __repr__(self) :
return \
"%s(%s)" % (self.__class__.__name__, super().__repr__())
#end __repr__
#end Signature
class UnixFD(int) :
"a file-descriptor integer."
def __repr__(self) :
return \
"%s(%s)" % (self.__class__.__name__, super().__repr__())
#end __repr__
#end UnixFD
basic_subclasses = \
{
TYPE_BOOLEAN : bool,
TYPE_OBJECT_PATH : ObjectPath,
TYPE_SIGNATURE : Signature,
TYPE_UNIX_FD : UnixFD,
}
# Compound types
TYPE_ARRAY = ord('a') # D-Bus array type
TYPE_VARIANT = ord('v') # D-Bus variant type
TYPE_STRUCT = ord('r') # a struct; however, type signatures use STRUCT_BEGIN/END_CHAR
TYPE_DICT_ENTRY = ord('e') # a dict entry; however, type signatures use DICT_ENTRY_BEGIN/END_CHAR
NUMBER_OF_TYPES = 16 # does not include TYPE_INVALID or STRUCT/DICT_ENTRY_BEGIN/END_CHAR
# characters other than typecodes that appear in type signatures
STRUCT_BEGIN_CHAR = ord('(') # start of a struct type in a type signature
STRUCT_END_CHAR = ord(')') # end of a struct type in a type signature
DICT_ENTRY_BEGIN_CHAR = ord('{') # start of a dict entry type in a type signature
DICT_ENTRY_END_CHAR = ord('}') # end of a dict entry type in a type signature
MAXIMUM_NAME_LENGTH = 255 # max length in bytes of a bus name, interface or member (object paths are unlimited)
MAXIMUM_SIGNATURE_LENGTH = 255 # fits in a byte
MAXIMUM_MATCH_RULE_LENGTH = 1024
MAXIMUM_MATCH_RULE_ARG_NUMBER = 63
MAXIMUM_ARRAY_LENGTH = 67108864 # 2 * 26
MAXIMUM_ARRAY_LENGTH_BITS = 26 # to store the max array size
MAXIMUM_MESSAGE_LENGTH = MAXIMUM_ARRAY_LENGTH * 2
MAXIMUM_MESSAGE_LENGTH_BITS = 27
MAXIMUM_MESSAGE_UNIX_FDS = MAXIMUM_MESSAGE_LENGTH // 4 # FDs are at least 32 bits
MAXIMUM_MESSAGE_UNIX_FDS_BITS = MAXIMUM_MESSAGE_LENGTH_BITS - 2
MAXIMUM_TYPE_RECURSION_DEPTH = 32
# Types of message
MESSAGE_TYPE_INVALID = 0 # never a valid message type
MESSAGE_TYPE_METHOD_CALL = 1
MESSAGE_TYPE_METHOD_RETURN = 2
MESSAGE_TYPE_ERROR = 3
MESSAGE_TYPE_SIGNAL = 4
NUM_MESSAGE_TYPES = 5
# Header flags
HEADER_FLAG_NO_REPLY_EXPECTED = 0x1
HEADER_FLAG_NO_AUTO_START = 0x2
HEADER_FLAG_ALLOW_INTERACTIVE_AUTHORIZATION = 0x4
# Header fields
HEADER_FIELD_INVALID = 0
HEADER_FIELD_PATH = 1
HEADER_FIELD_INTERFACE = 2
HEADER_FIELD_MEMBER = 3
HEADER_FIELD_ERROR_NAME = 4
HEADER_FIELD_REPLY_SERIAL = 5
HEADER_FIELD_DESTINATION = 6
HEADER_FIELD_SENDER = 7
HEADER_FIELD_SIGNATURE = 8
HEADER_FIELD_UNIX_FDS = 9
HEADER_FIELD_LAST = HEADER_FIELD_UNIX_FDS
HEADER_SIGNATURE = bytes \
((
TYPE_BYTE,
TYPE_BYTE,
TYPE_BYTE,
TYPE_BYTE,
TYPE_UINT32,
TYPE_UINT32,
TYPE_ARRAY,
STRUCT_BEGIN_CHAR,
TYPE_BYTE,
TYPE_VARIANT,
STRUCT_END_CHAR,
))
MINIMUM_HEADER_SIZE = 16 # smallest header size that can occur (missing required fields, though)
# Errors
ERROR_FAILED = "org.freedesktop.DBus.Error.Failed" # generic error
ERROR_NO_MEMORY = "org.freedesktop.DBus.Error.NoMemory"
ERROR_SERVICE_UNKNOWN = "org.freedesktop.DBus.Error.ServiceUnknown"
ERROR_NAME_HAS_NO_OWNER = "org.freedesktop.DBus.Error.NameHasNoOwner"
ERROR_NO_REPLY = "org.freedesktop.DBus.Error.NoReply"
ERROR_IO_ERROR = "org.freedesktop.DBus.Error.IOError"
ERROR_BAD_ADDRESS = "org.freedesktop.DBus.Error.BadAddress"
ERROR_NOT_SUPPORTED = "org.freedesktop.DBus.Error.NotSupported"
ERROR_LIMITS_EXCEEDED = "org.freedesktop.DBus.Error.LimitsExceeded"
ERROR_ACCESS_DENIED = "org.freedesktop.DBus.Error.AccessDenied"
ERROR_AUTH_FAILED = "org.freedesktop.DBus.Error.AuthFailed"
ERROR_NO_SERVER = "org.freedesktop.DBus.Error.NoServer"
ERROR_TIMEOUT = "org.freedesktop.DBus.Error.Timeout"
ERROR_NO_NETWORK = "org.freedesktop.DBus.Error.NoNetwork"
ERROR_ADDRESS_IN_USE = "org.freedesktop.DBus.Error.AddressInUse"
ERROR_DISCONNECTED = "org.freedesktop.DBus.Error.Disconnected"
ERROR_INVALID_ARGS = "org.freedesktop.DBus.Error.InvalidArgs"
ERROR_FILE_NOT_FOUND = "org.freedesktop.DBus.Error.FileNotFound"
ERROR_FILE_EXISTS = "org.freedesktop.DBus.Error.FileExists"
ERROR_UNKNOWN_METHOD = "org.freedesktop.DBus.Error.UnknownMethod"
ERROR_UNKNOWN_OBJECT = "org.freedesktop.DBus.Error.UnknownObject"
ERROR_UNKNOWN_INTERFACE = "org.freedesktop.DBus.Error.UnknownInterface"
ERROR_UNKNOWN_PROPERTY = "org.freedesktop.DBus.Error.UnknownProperty"
ERROR_PROPERTY_READ_ONLY = "org.freedesktop.DBus.Error.PropertyReadOnly"
ERROR_TIMED_OUT = "org.freedesktop.DBus.Error.TimedOut"
ERROR_MATCH_RULE_NOT_FOUND = "org.freedesktop.DBus.Error.MatchRuleNotFound"
ERROR_MATCH_RULE_INVALID = "org.freedesktop.DBus.Error.MatchRuleInvalid"
ERROR_SPAWN_EXEC_FAILED = "org.freedesktop.DBus.Error.Spawn.ExecFailed"
ERROR_SPAWN_FORK_FAILED = "org.freedesktop.DBus.Error.Spawn.ForkFailed"
ERROR_SPAWN_CHILD_EXITED = "org.freedesktop.DBus.Error.Spawn.ChildExited"
ERROR_SPAWN_CHILD_SIGNALED = "org.freedesktop.DBus.Error.Spawn.ChildSignaled"
ERROR_SPAWN_FAILED = "org.freedesktop.DBus.Error.Spawn.Failed"
ERROR_SPAWN_SETUP_FAILED = "org.freedesktop.DBus.Error.Spawn.FailedToSetup"
ERROR_SPAWN_CONFIG_INVALID = "org.freedesktop.DBus.Error.Spawn.ConfigInvalid"
ERROR_SPAWN_SERVICE_INVALID = "org.freedesktop.DBus.Error.Spawn.ServiceNotValid"
ERROR_SPAWN_SERVICE_NOT_FOUND = "org.freedesktop.DBus.Error.Spawn.ServiceNotFound"
ERROR_SPAWN_PERMISSIONS_INVALID = "org.freedesktop.DBus.Error.Spawn.PermissionsInvalid"
ERROR_SPAWN_FILE_INVALID = "org.freedesktop.DBus.Error.Spawn.FileInvalid"
ERROR_SPAWN_NO_MEMORY = "org.freedesktop.DBus.Error.Spawn.NoMemory"
ERROR_UNIX_PROCESS_ID_UNKNOWN = "org.freedesktop.DBus.Error.UnixProcessIdUnknown"
ERROR_INVALID_SIGNATURE = "org.freedesktop.DBus.Error.InvalidSignature"
ERROR_INVALID_FILE_CONTENT = "org.freedesktop.DBus.Error.InvalidFileContent"
ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN = "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"
ERROR_ADT_AUDIT_DATA_UNKNOWN = "org.freedesktop.DBus.Error.AdtAuditDataUnknown"
ERROR_OBJECT_PATH_IN_USE = "org.freedesktop.DBus.Error.ObjectPathInUse"
ERROR_INCONSISTENT_MESSAGE = "org.freedesktop.DBus.Error.InconsistentMessage"
ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED = "org.freedesktop.DBus.Error.InteractiveAuthorizationRequired"
# XML introspection format
INTROSPECT_1_0_XML_NAMESPACE = "http://www.freedesktop.org/standards/dbus"
INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER = "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER = "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"
INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE = \
(
"<!DOCTYPE node PUBLIC \""
+
INTROSPECT_1_0_XML_PUBLIC_IDENTIFIER
+
"\"\n\"" + INTROSPECT_1_0_XML_SYSTEM_IDENTIFIER
+
"\">\n"
)
# from dbus-shared.h:
# well-known bus types
BusType = ct.c_uint
BUS_SESSION = 0
BUS_SYSTEM = 1
BUS_STARTER = 2
# results that a message handler can return
BusHandlerResult = ct.c_uint
HANDLER_RESULT_HANDLED = 0 # no need to try more handlers
HANDLER_RESULT_NOT_YET_HANDLED = 1 # see if other handlers want it
HANDLER_RESULT_NEED_MEMORY = 2 # try again later with more memory
# Bus names
SERVICE_DBUS = "org.freedesktop.DBus" # used to talk to the bus itself
# Paths
PATH_DBUS = "/org/freedesktop/DBus" # object path used to talk to the bus itself
PATH_LOCAL = "/org/freedesktop/DBus/Local" # path used in local/in-process-generated messages
# Interfaces
INTERFACE_DBUS = "org.freedesktop.DBus" # interface exported by the object with SERVICE_DBUS and PATH_DBUS
INTERFACE_MONITORING = "org.freedesktop.DBus.Monitoring" # monitoring interface exported by the dbus-daemon
INTERFACE_VERBOSE = "org.freedesktop.DBus.Verbose" # verbose interface exported by the dbus-daemon
INTERFACE_INTROSPECTABLE = "org.freedesktop.DBus.Introspectable" # interface supported by introspectable objects
INTERFACE_PROPERTIES = "org.freedesktop.DBus.Properties" # interface supported by objects with properties
INTERFACE_PEER = "org.freedesktop.DBus.Peer" # interface supported by most dbus peers
INTERFACE_LOCAL = "org.freedesktop.DBus.Local" # methods can only be invoked locally
# Owner flags for request_name
NAME_FLAG_ALLOW_REPLACEMENT = 0x1
NAME_FLAG_REPLACE_EXISTING = 0x2
NAME_FLAG_DO_NOT_QUEUE = 0x4
# Replies to request for a name
REQUEST_NAME_REPLY_PRIMARY_OWNER = 1
REQUEST_NAME_REPLY_IN_QUEUE = 2
REQUEST_NAME_REPLY_EXISTS = 3
REQUEST_NAME_REPLY_ALREADY_OWNER = 4
# Replies to releasing a name
RELEASE_NAME_REPLY_RELEASED = 1
RELEASE_NAME_REPLY_NON_EXISTENT = 2
RELEASE_NAME_REPLY_NOT_OWNER = 3
# Replies to service starts
START_REPLY_SUCCESS = 1
START_REPLY_ALREADY_RUNNING = 2
# from dbus-types.h:
bool_t = ct.c_uint
# from dbus-memory.h:
FreeFunction = ct.CFUNCTYPE(None, ct.c_void_p)
# from dbus-connection.h:
HandlerResult = ct.c_uint
class Error(ct.Structure) :
_fields_ = \
[
("name", ct.c_char_p),
("message", ct.c_char_p),
("padding", 2 * ct.c_void_p),
]
#end Error
ErrorPtr = ct.POINTER(Error)
WatchFlags = ct.c_uint
WATCH_READABLE = 1 << 0
WATCH_WRITABLE = 1 << 1
WATCH_ERROR = 1 << 2
WATCH_HANGUP = 1 << 3
DispatchStatus = ct.c_uint
DISPATCH_DATA_REMAINS = 0 # more data available
DISPATCH_COMPLETE = 1 # all available data has been processed
DISPATCH_NEED_MEMORY = 2 # not enough memory to continue
AddWatchFunction = ct.CFUNCTYPE(bool_t, ct.c_void_p, ct.c_void_p)
# add_watch(DBusWatch, user_data) returns success/failure
WatchToggledFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.c_void_p)
# watch_toggled(DBusWatch, user_data)
RemoveWatchFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.c_void_p)
# remove_watch(DBusWatch, user_data)
AddTimeoutFunction = ct.CFUNCTYPE(bool_t, ct.c_void_p, ct.c_void_p)
# add_timeout(DBusTimeout, user_data) returns success/failure
TimeoutToggledFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.c_void_p)
# timeout_toggled(DBusTimeout, user_data)
RemoveTimeoutFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.c_void_p)
# remove_timeout(DBusTimeout, user_data)
DispatchStatusFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.POINTER(DispatchStatus), ct.c_void_p)
# dispatch_status(DBusConnection, DBusDispatchStatus, user_data)
WakeupMainFunction = ct.CFUNCTYPE(None, ct.c_void_p)
# wakeup_main(user_data)
AllowUnixUserFunction = ct.CFUNCTYPE(bool_t, ct.c_void_p, ct.c_ulong, ct.c_void_p)
# allow_unix_user(DBusConnection, uid, user_data) returns success/failure
AllowWindowsUserFunction = ct.CFUNCTYPE(bool_t, ct.c_void_p, ct.c_void_p, ct.c_void_p)
# allow_windows_user(DBusConnection, user_sid, user_data)returns success/failure
PendingCallNotifyFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.c_void_p)
# notify(DBusPendingCall, user_data)
HandleMessageFunction = ct.CFUNCTYPE(HandlerResult, ct.c_void_p, ct.c_void_p, ct.c_void_p)
# handle_message(DBusConnection, DBusMessage, user_data)
ObjectPathUnregisterFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.c_void_p)
# unregister(DBusConnection, user_data)
ObjectPathMessageFunction = ct.CFUNCTYPE(HandlerResult, ct.c_void_p, ct.c_void_p, ct.c_void_p)
# handle_message(DBusConnection, DBusMessage, user_data)
class ObjectPathVTable(ct.Structure) :
pass
#end ObjectPathVTable
ObjectPathVTable._fields_ = \
[
("unregister_function", ObjectPathUnregisterFunction),
("message_function", ObjectPathMessageFunction),
("internal_pad1", ct.CFUNCTYPE(None, ct.c_void_p)),
("internal_pad2", ct.CFUNCTYPE(None, ct.c_void_p)),
("internal_pad3", ct.CFUNCTYPE(None, ct.c_void_p)),
("internal_pad4", ct.CFUNCTYPE(None, ct.c_void_p)),
]
ObjectPathVTablePtr = ct.POINTER(ObjectPathVTable)
# from dbus-pending-call.h:
TIMEOUT_INFINITE = 0x7fffffff
TIMEOUT_USE_DEFAULT = -1
# from dbus-message.h:
class MessageIter(ct.Structure) :
"contains no public fields."
_fields_ = \
[
("dummy1", ct.c_void_p),
("dummy2", ct.c_void_p),
("dummy3", ct.c_uint),
("dummy4", ct.c_int),
("dummy5", ct.c_int),
("dummy6", ct.c_int),
("dummy7", ct.c_int),
("dummy8", ct.c_int),
("dummy9", ct.c_int),
("dummy10", ct.c_int),
("dummy11", ct.c_int),
("pad1", ct.c_int),
("pad2", ct.c_void_p),
("pad3", ct.c_void_p),
]
#end MessageIter
MessageIterPtr = ct.POINTER(MessageIter)
# from dbus-server.h:
NewConnectionFunction = ct.CFUNCTYPE(None, ct.c_void_p, ct.c_void_p, ct.c_void_p)
# new_connection(DBusServer, DBusConnection, user_data)
# from dbus-signature.h:
class SignatureIter(ct.Structure) :
"contains no public fields."
_fields_ = \
[
("dummy1", ct.c_void_p),
("dummy2", ct.c_void_p),
("dummy8", ct.c_uint),
("dummy12", ct.c_int),
("dummy17", ct.c_int),
]
#end SignatureIter
SignatureIterPtr = ct.POINTER(SignatureIter)
#end DBUS
class DBUSX:
"additional definitions not part of the official interfaces"
DEFAULT_TIMEOUT = 25 # seconds, from dbus-connection-internal.h in libdbus source
# For reference implementation for how to connect to daemon,
# see libdbus sources, dbus/dbus-bus.c (internal_bus_get routine
# and stuff that it calls)
# environment variables used to find addresses of bus daemons
SESSION_BUS_ADDRESS_VAR = "DBUS_SESSION_BUS_ADDRESS"
SYSTEM_BUS_ADDRESS_VAR = "DBUS_SYSTEM_BUS_ADDRESS"
STARTER_BUS_ADDRESS_VAR = "DBUS_STARTER_ADDRESS"
STARTER_BUS_ADDRESS_TYPE = "DBUS_STARTER_BUS_TYPE"
# values for value of STARTER_BUS_ADDRESS_TYPE
# If cannot determine type, then default to session bus
BUS_TYPE_SESSION = "session"
BUS_TYPE_SYSTEM = "system"
SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"
# default system bus daemon address if value of SYSTEM_BUS_ADDRESS_VAR is not defined
SESSION_BUS_ADDRESS = "autolaunch:"
# default session bus daemon address if value of SESSION_BUS_ADDRESS_VAR is not defined
INTERFACE_OBJECT_MANAGER = "org.freedesktop.DBus.ObjectManager"
# no symbolic name for this in standard headers as yet
#end DBUSX
#+
# Useful stuff
#-
if hasattr(asyncio, "get_running_loop") :
# new in Python 3.7
get_running_loop = asyncio.get_running_loop
else :
# as long as I want to support pre-3.7...
get_running_loop = asyncio.get_event_loop
#end if
def get_event_loop() :
"Python docs indicate that asyncio.get_event_loop() is going away" \
" in its current form. But I still need to be able to attach objects" \
" to the default event loop from a non-coroutine context. So I" \
" reimplement its original semantics here."
return \
asyncio.get_event_loop_policy().get_event_loop()
#end get_event_loop
def _wderef(w_self, parent) :
self = w_self()
assert self != None, "%s has gone away" % parent
return \
self
#end _wderef
def call_async(func, funcargs = (), timeout = None, abort = None, loop = None) :
"invokes func on a separate temporary thread and returns a Future that" \
" can be used to wait for its completion and obtain its result. If timeout" \
" is not None, then waiters on the Future will get a TimeoutError exception" \
" if the function has not completed execution after that number of seconds." \
" This allows easy invocation of blocking I/O functions in an asyncio-" \
"compatible fashion. But note that the operation cannot be cancelled" \
" if the timeout elapses; instead, you can specify an abort callback" \
" which will be invoked with whatever result is eventually returned from" \
" func."
if loop == None :
loop = get_running_loop()
#end if
timeout_task = None
def func_done(ref_awaiting, result) :
awaiting = ref_awaiting()
if awaiting != None :
if not awaiting.done() :
awaiting.set_result(result)
if timeout_task != None :
timeout_task.cancel()
#end if
else :
if abort != None :
abort(result)
#end if
#end if
#end if
#end func_done
def do_func_timedout(ref_awaiting) :
awaiting = ref_awaiting()
if awaiting != None :
if not awaiting.done() :
awaiting.set_exception(TimeoutError())
# Python doesnt give me any (easy) way to cancel the thread running the
# do_func() call, so just let it run to completion, whereupon func_done()
# will get rid of the result. Even if I could delete the thread, can I be sure
# that would clean up memory and OS/library resources properly?
#end if
#end if
#end do_func_timedout
def do_func(ref_awaiting) :
# makes the blocking call on a separate thread.
result = func(*funcargs)
# A Future is not itself threadsafe, but I can thread-safely
# run a callback on the main thread to set it.
loop.call_soon_threadsafe(func_done, ref_awaiting, result)
#end do_func
#begin call_async
awaiting = loop.create_future()
ref_awaiting = weak_ref(awaiting)
# weak ref to avoid circular refs with loop
subthread = threading.Thread(target = do_func, args = (ref_awaiting,), daemon = True)
subthread.start()
if timeout != None :
timeout_task = loop.call_later(timeout, do_func_timedout, ref_awaiting)
#end if
return \
awaiting
#end call_async
#+
# Higher-level interface to type system
#-
class TYPE(enum.Enum) :
"D-Bus type codes wrapped up in an enumeration."
BYTE = ord('y') # 8-bit unsigned integer
BOOLEAN = ord('b') # boolean
INT16 = ord('n') # 16-bit signed integer
UINT16 = ord('q') # 16-bit unsigned integer
INT32 = ord('i') # 32-bit signed integer
UINT32 = ord('u') # 32-bit unsigned integer
INT64 = ord('x') # 64-bit signed integer
UINT64 = ord('t') # 64-bit unsigned integer
DOUBLE = ord('d') # 8-byte double in IEEE 754 format
STRING = ord('s') # UTF-8 encoded, nul-terminated Unicode string
OBJECT_PATH = ord('o') # D-Bus object path
SIGNATURE = ord('g') # D-Bus type signature
UNIX_FD = ord('h') # unix file descriptor
ARRAY = ord('a') # array of elements all of same type, or possibly dict
STRUCT = ord('r') # sequence of elements of arbitrary types
VARIANT = ord('v') # a single element of dynamic type
@property
def is_basic(self) :
"does this code represent a basic (non-container) type."
return \
self.value in DBUS.basic_to_ctypes
#end is_basic
#end TYPE
class Type :
"base class for all Types. The “signature” property returns the fully-encoded" \
" signature string for the entire Type."
__slots__ = ("code",)
def __init__(self, code) :
if not isinstance(code, TYPE) :
raise TypeError("only TYPE.xxx values allowed")
#end if
self.code = code
#end __init__
@property
def signature(self) :
raise NotImplementedError("subclass forgot to override signature property")
#end signature
def __eq__(t1, t2) :
raise NotImplementedError("subclass forgot to override __eq__ method")
#end __eq__
def validate(self, val) :
"returns val if it is an acceptable value of this Type, else raises" \
" TypeError or ValueError."
raise NotImplementedError("subclass forgot to override validate method")
#end validate
def __repr__(self) :
return \
"%s(sig = %s)" % (type(self).__name__, repr(self.signature))
#end __repr__
#end Type
class BasicType(Type) :
"a basic (non-container) type."
__slots__ = ()
def __init__(self, code) :
if not isinstance(code, TYPE) or not code.is_basic :
raise TypeError("only basic TYPE.xxx values allowed")
#end if
super().__init__(code)
#end __init__
def __repr__(self) :
return \
"%s(%s)" % (type(self).__name__, repr(self.code))
#end __repr__
@property
def signature(self) :
return \
chr(self.code.value)
#end signature
def __eq__(t1, t2) :
return \
isinstance(t2, BasicType) and t1.code == t2.code
#end __eq__
def validate(self, val) :
if self.code.value in DBUS.int_convert :
val = DBUS.int_convert[self.code.value](val)
elif self.code == TYPE.DOUBLE :
if not isinstance(val, float) :
raise TypeError("expecting a float, not %s: %s" % (type(val).__name__, repr(val)))
#end if
elif self.code == TYPE.UNIX_FD :
val = DBUS.subtype_uint32(val)
elif DBUS.basic_to_ctypes[self.code.value] == ct.c_char_p :
if not isinstance(val, str) :
raise TypeError("expecting a string, not %s: %s" % (type(val).__name__, repr(val)))
#end if
else :
raise RuntimeError("unknown basic type %s" % repr(self.code))
#end if
return \
val
#end validate
#end BasicType
class VariantType(Type) :
"the variant type--a single element of a type determined at run-time."
def __init__(self) :
super().__init__(TYPE.VARIANT)
#end __init__
@property
def signature(self) :
return \
chr(TYPE.VARIANT.value)
#end signature
def __repr__(self) :
return \
"%s()" % type(self).__name__
#end __repr__
def __eq__(t1, t2) :
return \
isinstance(t2, VariantType)
#end __eq__
def validate(self, val) :
if not isinstance(val, (tuple, list)) or len(val) != 2 :
raise ValueError("expecting a (type, value) pair")
#end if
valtype, val = val
valtype = parse_single_signature(valtype)
return \
(valtype, valtype.validate(val))
#end validate
#end VariantType
class StructType(Type) :
"a sequence of one or more arbitrary types (empty structs are not allowed)."
__slots__ = ("elttypes",)
def __init__(self, *types) :
if len(types) == 0 :
raise TypeError("must have at least one element type")
#end if
if not all(isinstance(t, Type) for t in types) :
raise TypeError("struct elements must be Types")
#end if
super().__init__(TYPE.STRUCT)
self.elttypes = tuple(types)
#end __init__
def __repr__(self) :
return \
"%s(%s)" % (type(self).__name__, repr(self.elttypes))
#end __repr__
@property
def signature(self) :
return \
"(%s)" % "".join(t.signature for t in self.elttypes)
#end signature
def __eq__(t1, t2) :
return \
(
isinstance(t2, StructType)
and
len(t1.elttypes) == len(t2.elttypes)
and
all(e1 == e2 for e1, e2 in zip(t1.elttypes, t2.elttypes))
)
#end __eq__
def validate(self, val) :
if not isinstance(val, (tuple, list)) or len(val) != len(self.elttypes) :
raise TypeError \
(
"need a list or tuple of %d elements, not %s" % (len(self.elttypes), repr(val))
)
#end if
return \
type(val)(elttype.validate(elt) for elttype, elt in zip(self.elttypes, val))
#end validate
#end StructType
class ArrayType(Type) :
"an array of zero or more elements all of the same type."
__slots__ = ("elttype",)
def __init__(self, elttype) :
if not isinstance(elttype, Type) :
raise TypeError("invalid array element type")
#end if
super().__init__(TYPE.ARRAY)
self.elttype = elttype
#end __init__
def __repr__(self) :
return \
"%s[%s]" % (type(self).__name__, repr(self.elttype))
#end __repr__
@property
def signature(self) :
return \
chr(TYPE.ARRAY.value) + self.elttype.signature
#end signature
def __eq__(t1, t2) :
return \
isinstance(t2, ArrayType) and t1.elttype == t2.elttype
#end __eq__
def validate(self, val) :
if not isinstance(val, (tuple, list)) :
raise TypeError("need a tuple or list, not %s: %s" % (type(val).__name__, repr(val)))
#end if
return \
type(val)(self.elttype.validate(elt) for elt in val)
#end validate
#end ArrayType
class DictType(Type) :
"a dictionary mapping zero or more keys to values."
__slots__ = ("keytype", "valuetype")
def __init__(self, keytype, valuetype) :
if not isinstance(keytype, BasicType) or not isinstance(valuetype, Type) :
raise TypeError("invalid dict key/value type")
#end if
super().__init__(TYPE.ARRAY)
self.keytype = keytype
self.valuetype = valuetype
#end keytype
def __repr__(self) :
return \
"%s[%s : %s]" % (type(self).__name__, repr(self.keytype), repr(self.valuetype))
#end __repr__
@property
def signature(self) :
return \
"%s{%s%s}" % (chr(TYPE.ARRAY.value), self.keytype.signature, self.valuetype.signature)
#end signature
@property
def entry_signature(self) :
"signature for a dict entry."
return \
"{%s%s}" % (self.keytype.signature, self.valuetype.signature)
#end entry_signature
def __eq__(t1, t2) :
return \
isinstance(t2, DictType) and t1.keytype == t2.keytype and t1.valuetype == t2.valuetype
#end __eq__
def validate(self, val) :
if not isinstance(val, dict) :
raise TypeError("need a dict, not %s: %s" % (type(val).__name__, repr(val)))
#end if
return \
type(val) \
(
(self.keytype.validate(key), self.valuetype.validate(val[key]))
for key in val
)
#end validate
#end DictType
def data_key(data) :
"returns a unique value that allows data to be used as a dict/set key."
if isinstance(data, (bytes, float, frozenset, int, str, tuple)) :
result = data
else :
# data itself is non-hashable
result = id(data)
#end if
return \
result
#end data_key
#+
# Library prototypes
#-
# from dbus-connection.h:
dbus.dbus_connection_open.restype = ct.c_void_p
dbus.dbus_connection_open.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_connection_open_private.restype = ct.c_void_p
dbus.dbus_connection_open_private.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_connection_ref.restype = ct.c_void_p
dbus.dbus_connection_ref.argtypes = (ct.c_void_p,)
dbus.dbus_connection_unref.restype = None
dbus.dbus_connection_unref.argtypes = (ct.c_void_p,)
dbus.dbus_connection_close.restype = None
dbus.dbus_connection_close.argtypes = (ct.c_void_p,)
dbus.dbus_connection_get_is_connected.restype = DBUS.bool_t
dbus.dbus_connection_get_is_connected.argtypes = (ct.c_void_p,)
dbus.dbus_connection_get_is_authenticated.restype = DBUS.bool_t
dbus.dbus_connection_get_is_authenticated.argtypes = (ct.c_void_p,)
dbus.dbus_connection_get_is_anonymous.restype = DBUS.bool_t
dbus.dbus_connection_get_is_anonymous.argtypes = (ct.c_void_p,)
dbus.dbus_connection_get_server_id.restype = ct.c_void_p
dbus.dbus_connection_get_server_id.argtypes = (ct.c_void_p,)
dbus.dbus_connection_can_send_type.restype = DBUS.bool_t
dbus.dbus_connection_can_send_type.argtypes = (ct.c_void_p, ct.c_int)
dbus.dbus_connection_set_exit_on_disconnect.restype = None
dbus.dbus_connection_set_exit_on_disconnect.argtypes = (ct.c_void_p, DBUS.bool_t)
dbus.dbus_connection_preallocate_send.restype = ct.c_void_p
dbus.dbus_connection_preallocate_send.argtypes = (ct.c_void_p,)
dbus.dbus_connection_free_preallocated_send.restype = None
dbus.dbus_connection_free_preallocated_send.argtypes = (ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_send_preallocated.restype = None
dbus.dbus_connection_send_preallocated.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.POINTER(ct.c_uint))
dbus.dbus_connection_has_messages_to_send.restype = DBUS.bool_t
dbus.dbus_connection_has_messages_to_send.argtypes = (ct.c_void_p,)
dbus.dbus_connection_send.restype = DBUS.bool_t
dbus.dbus_connection_send.argtypes = (ct.c_void_p, ct.c_void_p, ct.POINTER(ct.c_uint))
dbus.dbus_connection_send_with_reply.restype = DBUS.bool_t
dbus.dbus_connection_send_with_reply.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_int)
dbus.dbus_connection_send_with_reply_and_block.restype = ct.c_void_p
dbus.dbus_connection_send_with_reply_and_block.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_int, DBUS.ErrorPtr)
dbus.dbus_connection_flush.restype = None
dbus.dbus_connection_flush.argtypes = (ct.c_void_p,)
dbus.dbus_connection_read_write_dispatch.restype = DBUS.bool_t
dbus.dbus_connection_read_write_dispatch.argtypes = (ct.c_void_p, ct.c_int)
dbus.dbus_connection_read_write.restype = DBUS.bool_t
dbus.dbus_connection_read_write.argtypes = (ct.c_void_p, ct.c_int)
dbus.dbus_connection_borrow_message.restype = ct.c_void_p
dbus.dbus_connection_borrow_message.argtypes = (ct.c_void_p,)
dbus.dbus_connection_return_message.restype = None
dbus.dbus_connection_return_message.argtypes = (ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_steal_borrowed_message.restype = None
dbus.dbus_connection_steal_borrowed_message.argtypes = (ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_pop_message.restype = ct.c_void_p
dbus.dbus_connection_pop_message.argtypes = (ct.c_void_p,)
dbus.dbus_connection_get_dispatch_status.restype = ct.c_uint
dbus.dbus_connection_get_dispatch_status.argtypes = (ct.c_void_p,)
dbus.dbus_connection_dispatch.restype = ct.c_uint
dbus.dbus_connection_dispatch.argtypes = (ct.c_void_p,)
dbus.dbus_connection_set_watch_functions.restype = DBUS.bool_t
dbus.dbus_connection_set_watch_functions.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_set_timeout_functions.restype = DBUS.bool_t
dbus.dbus_connection_set_timeout_functions.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_set_wakeup_main_function.restype = None
dbus.dbus_connection_set_wakeup_main_function.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_set_dispatch_status_function.restype = None
dbus.dbus_connection_set_dispatch_status_function.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_get_unix_user.restype = DBUS.bool_t
dbus.dbus_connection_get_unix_user.argtypes = (ct.c_void_p, ct.POINTER(ct.c_ulong))
dbus.dbus_connection_get_unix_process_id.restype = DBUS.bool_t
dbus.dbus_connection_get_unix_process_id.argtypes = (ct.c_void_p, ct.POINTER(ct.c_ulong))
dbus.dbus_connection_get_adt_audit_session_data.restype = DBUS.bool_t
dbus.dbus_connection_get_adt_audit_session_data.argtypes = (ct.c_void_p, ct.c_void_p, ct.POINTER(ct.c_uint))
dbus.dbus_connection_set_unix_user_function.restype = None
dbus.dbus_connection_set_unix_user_function.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_get_windows_user.restype = DBUS.bool_t
dbus.dbus_connection_get_windows_user.argtypes = (ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_set_windows_user_function.restype = None
dbus.dbus_connection_set_windows_user_function.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_set_allow_anonymous.restype = None
dbus.dbus_connection_set_allow_anonymous.argtypes = (ct.c_void_p, DBUS.bool_t)
dbus.dbus_connection_set_route_peer_messages.restype = None
dbus.dbus_connection_set_route_peer_messages.argtypes = (ct.c_void_p, DBUS.bool_t)
dbus.dbus_connection_add_filter.restype = DBUS.bool_t
dbus.dbus_connection_add_filter.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_remove_filter.restype = None
dbus.dbus_connection_remove_filter.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_allocate_data_slot.restype = DBUS.bool_t
dbus.dbus_connection_allocate_data_slot.argtypes = (ct.POINTER(ct.c_uint),)
dbus.dbus_connection_free_data_slot.restype = None
dbus.dbus_connection_free_data_slot.argtypes = (ct.c_uint,)
dbus.dbus_connection_set_data.restype = DBUS.bool_t
dbus.dbus_connection_set_data.argtypes = (ct.c_void_p, ct.c_uint, ct.c_void_p, ct.c_void_p)
dbus.dbus_connection_get_data.restype = ct.c_void_p
dbus.dbus_connection_get_data.argtypes = (ct.c_void_p, ct.c_uint)
dbus.dbus_connection_set_change_sigpipe.restype = None
dbus.dbus_connection_set_change_sigpipe.argtypes = (DBUS.bool_t,)
dbus.dbus_connection_set_max_message_size.restype = None
dbus.dbus_connection_set_max_message_size.argtypes = (ct.c_void_p, ct.c_long)
dbus.dbus_connection_get_max_message_size.restype = ct.c_long
dbus.dbus_connection_get_max_message_size.argtypes = (ct.c_void_p,)
dbus.dbus_connection_set_max_received_size.restype = None
dbus.dbus_connection_set_max_received_size.argtypes = (ct.c_void_p, ct.c_long)
dbus.dbus_connection_get_max_received_size.restype = ct.c_long
dbus.dbus_connection_get_max_received_size.argtypes = (ct.c_void_p,)
dbus.dbus_connection_set_max_message_unix_fds.restype = None
dbus.dbus_connection_set_max_message_unix_fds.argtypes = (ct.c_void_p, ct.c_long)
dbus.dbus_connection_get_max_message_unix_fds.restype = ct.c_long
dbus.dbus_connection_get_max_message_unix_fds.argtypes = (ct.c_void_p,)
dbus.dbus_connection_set_max_received_unix_fds.restype = None
dbus.dbus_connection_set_max_received_unix_fds.argtypes = (ct.c_void_p, ct.c_long)
dbus.dbus_connection_get_max_received_unix_fds.restype = ct.c_long
dbus.dbus_connection_get_max_received_unix_fds.argtypes = (ct.c_void_p,)
dbus.dbus_connection_get_outgoing_size.restype = ct.c_long
dbus.dbus_connection_get_outgoing_size.argtypes = (ct.c_void_p,)
dbus.dbus_connection_get_outgoing_unix_fds.restype = ct.c_long
dbus.dbus_connection_get_outgoing_unix_fds.argtypes = (ct.c_void_p,)
dbus.dbus_connection_register_object_path.restype = DBUS.bool_t
dbus.dbus_connection_register_object_path.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ObjectPathVTablePtr, ct.c_void_p)
dbus.dbus_connection_try_register_object_path.restype = DBUS.bool_t
dbus.dbus_connection_try_register_object_path.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ObjectPathVTablePtr, ct.c_void_p, DBUS.ErrorPtr)
dbus.dbus_connection_register_fallback.restype = DBUS.bool_t
dbus.dbus_connection_register_fallback.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ObjectPathVTablePtr, ct.c_void_p)
dbus.dbus_connection_try_register_fallback.restype = DBUS.bool_t
dbus.dbus_connection_try_register_fallback.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ObjectPathVTablePtr, ct.c_void_p, DBUS.ErrorPtr)
dbus.dbus_connection_get_object_path_data.restype = DBUS.bool_t
dbus.dbus_connection_get_object_path_data.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_void_p)
dbus.dbus_connection_list_registered.restype = DBUS.bool_t
dbus.dbus_connection_list_registered.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_void_p)
dbus.dbus_connection_get_unix_fd.restype = DBUS.bool_t
dbus.dbus_connection_get_unix_fd.argtypes = (ct.c_void_p, ct.POINTER(ct.c_int))
dbus.dbus_connection_get_socket.restype = DBUS.bool_t
dbus.dbus_connection_get_socket.argtypes = (ct.c_void_p, ct.POINTER(ct.c_int))
dbus.dbus_connection_unregister_object_path.restype = DBUS.bool_t
dbus.dbus_connection_unregister_object_path.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_watch_get_unix_fd.restype = ct.c_int
dbus.dbus_watch_get_unix_fd.argtypes = (ct.c_void_p,)
dbus.dbus_watch_get_socket.restype = ct.c_int
dbus.dbus_watch_get_socket.argtypes = (ct.c_void_p,)
dbus.dbus_watch_get_flags.restype = ct.c_uint
dbus.dbus_watch_get_flags.argtypes = (ct.c_void_p,)
dbus.dbus_watch_get_data.restype = ct.c_void_p
dbus.dbus_watch_get_data.argtypes = (ct.c_void_p,)
dbus.dbus_watch_set_data.restype = None
dbus.dbus_watch_set_data.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_watch_handle.restype = DBUS.bool_t
dbus.dbus_watch_handle.argtypes = (ct.c_void_p, ct.c_uint)
dbus.dbus_watch_get_enabled.restype = DBUS.bool_t
dbus.dbus_watch_get_enabled.argtypes = (ct.c_void_p,)
dbus.dbus_timeout_get_interval.restype = ct.c_int
dbus.dbus_timeout_get_interval.argtypes = (ct.c_void_p,)
dbus.dbus_timeout_get_data.restype = ct.c_void_p
dbus.dbus_timeout_get_data.argtypes = (ct.c_void_p,)
dbus.dbus_timeout_set_data.restype = None
dbus.dbus_timeout_set_data.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_timeout_handle.restype = DBUS.bool_t
dbus.dbus_timeout_handle.argtypes = (ct.c_void_p,)
dbus.dbus_timeout_get_enabled.restype = DBUS.bool_t
dbus.dbus_timeout_get_enabled.argtypes = (ct.c_void_p,)
# from dbus-bus.h:
dbus.dbus_bus_get.restype = ct.c_void_p
dbus.dbus_bus_get.argtypes = (ct.c_uint, DBUS.ErrorPtr)
dbus.dbus_bus_get_private.restype = ct.c_void_p
dbus.dbus_bus_get_private.argtypes = (ct.c_uint, DBUS.ErrorPtr)
dbus.dbus_bus_register.restype = DBUS.bool_t
dbus.dbus_bus_register.argtypes = (ct.c_void_p, DBUS.ErrorPtr)
dbus.dbus_bus_set_unique_name.restype = DBUS.bool_t
dbus.dbus_bus_set_unique_name.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_bus_get_unique_name.restype = ct.c_char_p
dbus.dbus_bus_get_unique_name.argtypes = (ct.c_void_p,)
dbus.dbus_bus_get_unix_user.restype = ct.c_ulong
dbus.dbus_bus_get_unix_user.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_bus_get_id.restype = ct.c_void_p
dbus.dbus_bus_get_id.argtypes = (ct.c_void_p, DBUS.ErrorPtr)
dbus.dbus_bus_request_name.restype = ct.c_int
dbus.dbus_bus_request_name.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_uint, DBUS.ErrorPtr)
dbus.dbus_bus_release_name.restype = ct.c_int
dbus.dbus_bus_release_name.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_bus_name_has_owner.restype = DBUS.bool_t
dbus.dbus_bus_name_has_owner.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_bus_start_service_by_name.restype = DBUS.bool_t
dbus.dbus_bus_start_service_by_name.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_uint, ct.POINTER(ct.c_uint), DBUS.ErrorPtr)
dbus.dbus_bus_add_match.restype = None
dbus.dbus_bus_add_match.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_bus_remove_match.restype = None
dbus.dbus_bus_remove_match.argtypes = (ct.c_void_p, ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_error_init.restype = None
dbus.dbus_error_init.argtypes = (DBUS.ErrorPtr,)
dbus.dbus_error_free.restype = None
dbus.dbus_error_free.argtypes = (DBUS.ErrorPtr,)
dbus.dbus_move_error.restype = None
dbus.dbus_move_error.argtypes = (DBUS.ErrorPtr, DBUS.ErrorPtr)
dbus.dbus_error_has_name.restype = DBUS.bool_t
dbus.dbus_error_has_name.argtypes = (DBUS.ErrorPtr, ct.c_char_p)
dbus.dbus_error_is_set.restype = DBUS.bool_t
dbus.dbus_error_is_set.argtypes = (DBUS.ErrorPtr,)
dbus.dbus_set_error.restype = None
dbus.dbus_set_error.argtypes = (DBUS.ErrorPtr, ct.c_char_p, ct.c_char_p, ct.c_char_p)
# note I cant handle varargs
# from dbus-pending-call.h:
dbus.dbus_pending_call_ref.restype = ct.c_void_p
dbus.dbus_pending_call_ref.argtypes = (ct.c_void_p,)
dbus.dbus_pending_call_unref.restype = None
dbus.dbus_pending_call_unref.argtypes = (ct.c_void_p,)
dbus.dbus_pending_call_set_notify.restype = DBUS.bool_t
dbus.dbus_pending_call_set_notify.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_pending_call_cancel.restype = None
dbus.dbus_pending_call_cancel.argtypes = (ct.c_void_p,)
dbus.dbus_pending_call_get_completed.restype = DBUS.bool_t
dbus.dbus_pending_call_get_completed.argtypes = (ct.c_void_p,)
dbus.dbus_pending_call_steal_reply.restype = ct.c_void_p
dbus.dbus_pending_call_steal_reply.argtypes = (ct.c_void_p,)
dbus.dbus_pending_call_block.restype = None
dbus.dbus_pending_call_block.argtypes = (ct.c_void_p,)
dbus.dbus_pending_call_allocate_data_slot.restype = DBUS.bool_t
dbus.dbus_pending_call_allocate_data_slot.argtypes = (ct.POINTER(ct.c_int),)
dbus.dbus_pending_call_free_data_slot.restype = None
dbus.dbus_pending_call_free_data_slot.argtypes = (ct.c_int,)
dbus.dbus_pending_call_set_data.restype = DBUS.bool_t
dbus.dbus_pending_call_set_data.argtypes = (ct.c_void_p, ct.c_int, ct.c_void_p, ct.c_void_p)
dbus.dbus_pending_call_get_data.restype = ct.c_void_p
dbus.dbus_pending_call_get_data.argtypes = (ct.c_void_p, ct.c_int)
# from dbus-message.h:
dbus.dbus_message_new.restype = ct.c_void_p
dbus.dbus_message_new.argtypes = (ct.c_int,)
dbus.dbus_message_new_method_call.restype = ct.c_void_p
dbus.dbus_message_new_method_call.argtypes = (ct.c_char_p, ct.c_char_p, ct.c_char_p, ct.c_char_p)
dbus.dbus_message_new_method_return.restype = ct.c_void_p
dbus.dbus_message_new_method_return.argtypes = (ct.c_void_p,)
dbus.dbus_message_new_signal.restype = ct.c_void_p
dbus.dbus_message_new_signal.argtypes = (ct.c_char_p, ct.c_char_p, ct.c_char_p)
dbus.dbus_message_new_error.restype = ct.c_void_p
dbus.dbus_message_new_error.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_char_p)
dbus.dbus_message_new_error_printf.restype = ct.c_void_p
dbus.dbus_message_new_error_printf.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_char_p, ct.c_char_p)
# note I cant handle varargs
dbus.dbus_message_copy.restype = ct.c_void_p
dbus.dbus_message_copy.argtypes = (ct.c_void_p,)
dbus.dbus_message_ref.restype = ct.c_void_p
dbus.dbus_message_ref.argtypes = (ct.c_void_p,)
dbus.dbus_message_unref.restype = None
dbus.dbus_message_unref.argtypes = (ct.c_void_p,)
dbus.dbus_message_get_type.restype = ct.c_int
dbus.dbus_message_get_type.argtypes = (ct.c_void_p,)
dbus.dbus_message_set_path.restype = DBUS.bool_t
dbus.dbus_message_set_path.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_get_path.restype = ct.c_char_p
dbus.dbus_message_get_path.argtypes = (ct.c_void_p,)
dbus.dbus_message_has_path.restype = DBUS.bool_t
dbus.dbus_message_has_path.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_set_interface.restype = DBUS.bool_t
dbus.dbus_message_set_interface.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_get_interface.restype = ct.c_char_p
dbus.dbus_message_get_interface.argtypes = (ct.c_void_p,)
dbus.dbus_message_has_interface.restype = DBUS.bool_t
dbus.dbus_message_has_interface.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_set_member.restype = DBUS.bool_t
dbus.dbus_message_set_member.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_get_member.restype = ct.c_char_p
dbus.dbus_message_get_member.argtypes = (ct.c_void_p,)
dbus.dbus_message_has_member.restype = DBUS.bool_t
dbus.dbus_message_has_member.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_set_error_name.restype = DBUS.bool_t
dbus.dbus_message_set_error_name.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_get_error_name.restype = ct.c_char_p
dbus.dbus_message_get_error_name.argtypes = (ct.c_void_p,)
dbus.dbus_message_set_destination.restype = DBUS.bool_t
dbus.dbus_message_set_destination.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_get_destination.restype = ct.c_char_p
dbus.dbus_message_get_destination.argtypes = (ct.c_void_p,)
dbus.dbus_message_set_sender.restype = DBUS.bool_t
dbus.dbus_message_set_sender.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_get_sender.restype = ct.c_char_p
dbus.dbus_message_get_sender.argtypes = (ct.c_void_p,)
dbus.dbus_message_get_signature.restype = ct.c_char_p
dbus.dbus_message_get_signature.argtypes = (ct.c_void_p,)
dbus.dbus_message_set_no_reply.restype = None
dbus.dbus_message_set_no_reply.argtypes = (ct.c_void_p, DBUS.bool_t)
dbus.dbus_message_get_no_reply.restype = DBUS.bool_t
dbus.dbus_message_get_no_reply.argtypes = (ct.c_void_p,)
dbus.dbus_message_is_method_call.restype = DBUS.bool_t
dbus.dbus_message_is_method_call.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_char_p)
dbus.dbus_message_is_signal.restype = DBUS.bool_t
dbus.dbus_message_is_signal.argtypes = (ct.c_void_p, ct.c_char_p, ct.c_char_p)
dbus.dbus_message_is_error.restype = DBUS.bool_t
dbus.dbus_message_is_error.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_has_destination.restype = DBUS.bool_t
dbus.dbus_message_has_destination.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_has_sender.restype = DBUS.bool_t
dbus.dbus_message_has_sender.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_has_signature.restype = DBUS.bool_t
dbus.dbus_message_has_signature.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_message_get_serial.restype = ct.c_uint
dbus.dbus_message_get_serial.argtypes = (ct.c_void_p,)
dbus.dbus_message_set_serial.restype = None
dbus.dbus_message_set_serial.argtypes = (ct.c_void_p, ct.c_uint)
dbus.dbus_message_set_reply_serial.restype = DBUS.bool_t
dbus.dbus_message_set_reply_serial.argtypes = (ct.c_void_p, ct.c_uint)
dbus.dbus_message_get_reply_serial.restype = ct.c_uint
dbus.dbus_message_get_reply_serial.argtypes = (ct.c_void_p,)
dbus.dbus_message_set_auto_start.restype = None
dbus.dbus_message_set_auto_start.argtypes = (ct.c_void_p, DBUS.bool_t)
dbus.dbus_message_get_auto_start.restype = DBUS.bool_t
dbus.dbus_message_get_auto_start.argtypes = (ct.c_void_p,)
dbus.dbus_message_get_path_decomposed.restype = DBUS.bool_t
dbus.dbus_message_get_path_decomposed.argtypes = (ct.c_void_p, ct.c_void_p)
dbus.dbus_message_append_args.restype = DBUS.bool_t
dbus.dbus_message_append_args.argtypes = (ct.c_void_p, ct.c_int, ct.c_void_p, ct.c_int)
# note I cant handle varargs
# probably cannot make use of dbus.dbus_message_append_args_valist
dbus.dbus_message_get_args.restype = DBUS.bool_t
dbus.dbus_message_get_args.argtypes = (ct.c_void_p, DBUS.ErrorPtr, ct.c_int, ct.c_void_p, ct.c_int)
# note I cant handle varargs
# probably cannot make use of dbus.dbus_message_get_args_valist
dbus.dbus_message_contains_unix_fds.restype = DBUS.bool_t
dbus.dbus_message_contains_unix_fds.argtypes = (ct.c_void_p,)
dbus.dbus_message_iter_init.restype = DBUS.bool_t
dbus.dbus_message_iter_init.argtypes = (ct.c_void_p, DBUS.MessageIterPtr)
dbus.dbus_message_iter_has_next.restype = DBUS.bool_t
dbus.dbus_message_iter_has_next.argtypes = (DBUS.MessageIterPtr,)
dbus.dbus_message_iter_next.restype = DBUS.bool_t
dbus.dbus_message_iter_next.argtypes = (DBUS.MessageIterPtr,)
dbus.dbus_message_iter_get_signature.restype = ct.c_void_p
dbus.dbus_message_iter_next.argtypes = (DBUS.MessageIterPtr,)
dbus.dbus_message_iter_get_signature.restype = ct.c_void_p
dbus.dbus_message_iter_get_signature.argtypes = (DBUS.MessageIterPtr,)
dbus.dbus_message_iter_get_arg_type.restype = ct.c_int
dbus.dbus_message_iter_get_arg_type.argtypes = (DBUS.MessageIterPtr,)
dbus.dbus_message_iter_get_element_type.restype = ct.c_int
dbus.dbus_message_iter_get_element_type.argtypes = (DBUS.MessageIterPtr,)
dbus.dbus_message_iter_recurse.restype = None
dbus.dbus_message_iter_recurse.argtypes = (DBUS.MessageIterPtr, DBUS.MessageIterPtr)
dbus.dbus_message_iter_get_basic.restype = None
dbus.dbus_message_iter_get_basic.argtypes = (DBUS.MessageIterPtr, ct.c_void_p)
if hasattr(dbus, "dbus_message_iter_get_element_count") :
dbus.dbus_message_iter_get_element_count.restype = ct.c_int
dbus.dbus_message_iter_get_element_count.argtypes = (DBUS.MessageIterPtr,)
#end if
# dbus_message_iter_get_array_len deprecated
dbus.dbus_message_iter_get_fixed_array.restype = None
dbus.dbus_message_iter_get_fixed_array.argtypes = (DBUS.MessageIterPtr, ct.c_void_p, ct.POINTER(ct.c_int))
dbus.dbus_message_iter_init_append.restype = None
dbus.dbus_message_iter_init_append.argtypes = (ct.c_void_p, DBUS.MessageIterPtr)
dbus.dbus_message_iter_append_basic.restype = DBUS.bool_t
dbus.dbus_message_iter_append_basic.argtypes = (DBUS.MessageIterPtr, ct.c_int, ct.c_void_p)
dbus.dbus_message_iter_append_fixed_array.restype = DBUS.bool_t
dbus.dbus_message_iter_append_fixed_array.argtypes = (DBUS.MessageIterPtr, ct.c_int, ct.c_void_p, ct.c_int)
dbus.dbus_message_iter_open_container.restype = DBUS.bool_t
dbus.dbus_message_iter_open_container.argtypes = (DBUS.MessageIterPtr, ct.c_int, ct.c_char_p, DBUS.MessageIterPtr)
dbus.dbus_message_iter_close_container.restype = DBUS.bool_t
dbus.dbus_message_iter_close_container.argtypes = (DBUS.MessageIterPtr, DBUS.MessageIterPtr)
dbus.dbus_message_iter_abandon_container.restype = None
dbus.dbus_message_iter_abandon_container.argtypes = (DBUS.MessageIterPtr, DBUS.MessageIterPtr)
dbus.dbus_message_lock.restype = None
dbus.dbus_message_lock.argtypes = (DBUS.MessageIterPtr,)
dbus.dbus_set_error_from_message.restype = DBUS.bool_t
dbus.dbus_set_error_from_message.argtypes = (DBUS.ErrorPtr, ct.c_void_p)
dbus.dbus_message_allocate_data_slot.restype = DBUS.bool_t
dbus.dbus_message_allocate_data_slot.argtypes = (ct.POINTER(ct.c_int),)
dbus.dbus_message_free_data_slot.restype = None
dbus.dbus_message_free_data_slot.argtypes = (ct.POINTER(ct.c_int),)
dbus.dbus_message_set_data.restype = DBUS.bool_t
dbus.dbus_message_set_data.argtypes = (ct.c_void_p, ct.c_int, ct.c_void_p, ct.c_void_p)
dbus.dbus_message_get_data.restype = ct.c_void_p
dbus.dbus_message_get_data.argtypes = (ct.c_void_p, ct.c_int)
dbus.dbus_message_type_from_string.restype = ct.c_int
dbus.dbus_message_type_from_string.argtypes = (ct.c_char_p,)
dbus.dbus_message_type_to_string.restype = ct.c_char_p
dbus.dbus_message_type_to_string.argtypes = (ct.c_int,)
dbus.dbus_message_marshal.restype = DBUS.bool_t
dbus.dbus_message_marshal.argtypes = (ct.c_void_p, ct.c_void_p, ct.POINTER(ct.c_int))
dbus.dbus_message_demarshal.restype = ct.c_void_p
dbus.dbus_message_demarshal.argtypes = (ct.c_void_p, ct.c_int, DBUS.ErrorPtr)
dbus.dbus_message_demarshal_bytes_needed.restype = ct.c_int
dbus.dbus_message_demarshal_bytes_needed.argtypes = (ct.c_void_p, ct.c_int)
if hasattr(dbus, "dbus_message_set_allow_interactive_authorization") :
dbus.dbus_message_set_allow_interactive_authorization.restype = None
dbus.dbus_message_set_allow_interactive_authorization.argtypes = (ct.c_void_p, DBUS.bool_t)
#end if
if hasattr(dbus, "dbus_message_get_allow_interactive_authorization") :
dbus.dbus_message_get_allow_interactive_authorization.restype = DBUS.bool_t
dbus.dbus_message_get_allow_interactive_authorization.argtypes = (ct.c_void_p,)
#end if
# from dbus-memory.h:
dbus.dbus_malloc.restype = ct.c_void_p
dbus.dbus_malloc.argtypes = (ct.c_size_t,)
dbus.dbus_malloc0.restype = ct.c_void_p
dbus.dbus_malloc0.argtypes = (ct.c_size_t,)
dbus.dbus_realloc.restype = ct.c_void_p
dbus.dbus_realloc.argtypes = (ct.c_void_p, ct.c_size_t)
dbus.dbus_free.restype = None
dbus.dbus_free.argtypes = (ct.c_void_p,)
dbus.dbus_free_string_array.restype = None
dbus.dbus_free_string_array.argtypes = (ct.c_void_p,)
# from dbus-misc.h:
dbus.dbus_get_local_machine_id.restype = ct.c_void_p
dbus.dbus_get_local_machine_id.argtypes = ()
dbus.dbus_get_version.restype = None
dbus.dbus_get_version.argtypes = (ct.POINTER(ct.c_int), ct.POINTER(ct.c_int), ct.POINTER(ct.c_int))
dbus.dbus_setenv.restype = DBUS.bool_t
dbus.dbus_setenv.argtypes = (ct.c_char_p, ct.c_char_p)
# from dbus-address.h:
dbus.dbus_parse_address.restype = DBUS.bool_t
dbus.dbus_parse_address.argtypes = (ct.c_char_p, ct.c_void_p, ct.POINTER(ct.c_int), DBUS.ErrorPtr)
dbus.dbus_address_entry_get_value.restype = ct.c_char_p
dbus.dbus_address_entry_get_value.argtypes = (ct.c_void_p, ct.c_char_p)
dbus.dbus_address_entry_get_method.restype = ct.c_char_p
dbus.dbus_address_entry_get_method.argtypes = (ct.c_void_p,)
dbus.dbus_address_entries_free.restype = None
dbus.dbus_address_entries_free.argtypes = (ct.c_void_p,)
dbus.dbus_address_escape_value.restype = ct.c_void_p
dbus.dbus_address_escape_value.argtypes = (ct.c_char_p,)
dbus.dbus_address_unescape_value.restype = ct.c_void_p
dbus.dbus_address_unescape_value.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
# from dbus-signature.h:
dbus.dbus_signature_iter_init.restype = None
dbus.dbus_signature_iter_init.argtypes = (DBUS.SignatureIterPtr, ct.c_char_p)
dbus.dbus_signature_iter_get_current_type.restype = ct.c_int
dbus.dbus_signature_iter_get_current_type.argtypes = (DBUS.SignatureIterPtr,)
dbus.dbus_signature_iter_get_signature.restype = ct.c_void_p
dbus.dbus_signature_iter_get_signature.argtypes = (DBUS.SignatureIterPtr,)
dbus.dbus_signature_iter_get_element_type.restype = ct.c_int
dbus.dbus_signature_iter_get_element_type.argtypes = (DBUS.SignatureIterPtr,)
dbus.dbus_signature_iter_next.restype = DBUS.bool_t
dbus.dbus_signature_iter_next.argtypes = (DBUS.SignatureIterPtr,)
dbus.dbus_signature_iter_recurse.restype = None
dbus.dbus_signature_iter_recurse.argtypes = (DBUS.SignatureIterPtr, DBUS.SignatureIterPtr)
dbus.dbus_signature_validate.restype = DBUS.bool_t
dbus.dbus_signature_validate.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_signature_validate_single.restype = DBUS.bool_t
dbus.dbus_signature_validate_single.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_type_is_valid.restype = DBUS.bool_t
dbus.dbus_type_is_valid.argtypes = (ct.c_int,)
dbus.dbus_type_is_basic.restype = DBUS.bool_t
dbus.dbus_type_is_basic.argtypes = (ct.c_int,)
dbus.dbus_type_is_container.restype = DBUS.bool_t
dbus.dbus_type_is_container.argtypes = (ct.c_int,)
dbus.dbus_type_is_fixed.restype = DBUS.bool_t
dbus.dbus_type_is_fixed.argtypes = (ct.c_int,)
# from dbus-syntax.h:
dbus.dbus_validate_path.restype = DBUS.bool_t
dbus.dbus_validate_path.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_validate_interface.restype = DBUS.bool_t
dbus.dbus_validate_interface.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_validate_member.restype = DBUS.bool_t
dbus.dbus_validate_member.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_validate_error_name.restype = DBUS.bool_t
dbus.dbus_validate_error_name.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_validate_bus_name.restype = DBUS.bool_t
dbus.dbus_validate_bus_name.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_validate_utf8.restype = DBUS.bool_t
dbus.dbus_validate_utf8.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
# from dbus-server.h:
dbus.dbus_server_listen.restype = ct.c_void_p
dbus.dbus_server_listen.argtypes = (ct.c_char_p, DBUS.ErrorPtr)
dbus.dbus_server_ref.restype = ct.c_void_p
dbus.dbus_server_ref.argtypes = (ct.c_void_p,)
dbus.dbus_server_unref.restype = ct.c_void_p
dbus.dbus_server_unref.argtypes = (ct.c_void_p,)
dbus.dbus_server_disconnect.restype = None
dbus.dbus_server_disconnect.argtypes = (ct.c_void_p,)
dbus.dbus_server_get_is_connected.restype = DBUS.bool_t
dbus.dbus_server_get_is_connected.argtypes = (ct.c_void_p,)
dbus.dbus_server_get_address.restype = ct.c_void_p
dbus.dbus_server_get_address.argtypes = (ct.c_void_p,)
dbus.dbus_server_get_id.restype = ct.c_void_p
dbus.dbus_server_get_id.argtypes = (ct.c_void_p,)
dbus.dbus_server_set_new_connection_function.restype = None
dbus.dbus_server_set_new_connection_function.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_server_set_watch_functions.restype = DBUS.bool_t
dbus.dbus_server_set_watch_functions.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_server_set_timeout_functions.restype = DBUS.bool_t
dbus.dbus_server_set_timeout_functions.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
dbus.dbus_server_set_auth_mechanisms.restype = DBUS.bool_t
dbus.dbus_server_set_auth_mechanisms.argtypes = (ct.c_void_p, ct.c_void_p)
dbus.dbus_server_allocate_data_slot.restype = DBUS.bool_t
dbus.dbus_server_allocate_data_slot.argtypes = (ct.POINTER(ct.c_int),)
dbus.dbus_server_free_data_slot.restype = DBUS.bool_t
dbus.dbus_server_free_data_slot.argtypes = (ct.POINTER(ct.c_int),)
dbus.dbus_server_set_data.restype = DBUS.bool_t
dbus.dbus_server_set_data.argtypes = (ct.c_void_p, ct.c_int, ct.c_void_p, ct.c_void_p)
dbus.dbus_server_set_data.restype = ct.c_void_p
dbus.dbus_server_set_data.argtypes = (ct.c_void_p, ct.c_int)
# TODO dbus-threads.h <https://dbus.freedesktop.org/doc/api/html/group__DBusThreads.html>
# Seems like the only call worth making is dbus_threads_init_default.
#+
# High-level stuff follows
#-
class DBusError(Exception) :
"for raising an exception that reports a D-Bus error name and accompanying message."
__slots__ = ("name", "message")
def __init__(self, name, message) :
self.args = ("%s -- %s" % (name, message),)
self.name = name
self.message = message
#end __init__
#end DBusError
class CallFailed(Exception) :
"used internally for reporting general failure from calling a libdbus routine."
__slots__ = ("funcname",)
def __init__(self, funcname) :
self.args = ("%s failed" % funcname,)
self.funcname = funcname
#end __init__
#end CallFailed
class _Abort(Exception) :
pass
#end _Abort
class TaskKeeper :
"Base class for classes that need to call EventLoop.create_task() to" \
" schedule caller-created coroutines for execution. asyncio only keeps" \
" weak references to Task objects when they are not being scheduled," \
" so to keep them from disappearing unexpectedly, I maintain a list of" \
" strong references here, and clean them out as they end execution."
__slots__ = ("__weakref__", "loop", "_cur_tasks")
def _init(self) :
# avoid __init__ so I don't get passed spurious args
self.loop = None
self._cur_tasks = set()
#end _init
def create_task(self, coro) :
assert self.loop != None, "no event loop to attach coroutine to"
task = self.loop.create_task(coro)
task.add_done_callback(functools.partial(self._reaper, weak_ref(self)))
self._cur_tasks.add(task)
#end create_task
@staticmethod
def _reaper(self, task) :
self = self() # avoid reference circularity
self._cur_tasks.remove(task)
#end _reaper
if "loop" in asyncio.wait.__kwdefaults__ :
def wait(self, futures, *, timeout = None, return_when = asyncio.ALL_COMPLETED) :
"wrapper around asyncio.wait for compatibility with pre-Python-3.7."
return \
asyncio.wait(futures, loop = self.loop, timeout = timeout, return_when = return_when)
# No default loop in pre-3.7.
#end wait
else :
wait = staticmethod(asyncio.wait)
# no need to pass loop arg in ≥ 3.7, removed in ≥ 3.10.
#end if
#end TaskKeeper
# Misc: <https://dbus.freedesktop.org/doc/api/html/group__DBusMisc.html>
def get_local_machine_id() :
"returns a systemwide unique ID that is supposed to remain constant at least" \
" until the next reboot. Two processes seeing the same value for this can assume" \
" they are on the same machine."
c_result = dbus.dbus_get_local_machine_id()
if c_result == None :
raise CallFailed("dbus_get_local_machine_id")
#end if
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end get_local_machine_id
def get_version() :
"returns the libdbus library version as a tuple of integers (major, minor, micro)."
major = ct.c_int()
minor = ct.c_int()
micro = ct.c_int()
dbus.dbus_get_version(ct.byref(major), ct.byref(minor), ct.byref(micro))
return \
(major.value, minor.value, micro.value)
#end get_version
def setenv(key, value) :
key = key.encode()
if value != None :
value = value.encode()
#end if
if not dbus.dbus_setenv(key, value) :
raise CallFailed("dbus_setenv")
#end if
#end setenv
def unsetenv(key) :
setenv(key, None)
#end unsetenv
class Watch :
"wrapper around a DBusWatch object. Do not instantiate directly; they" \
" are created and destroyed by libdbus.\n" \
"\n" \
"A Watch is the basic mechanism for plugging libdbus-created file descriptors" \
" into your event loop. When created, they are passed to your add-watch callback" \
" to manage; and conversely, when deleted, your remove-watch callback is notified." \
" (These callbacks are ones you attach to Server and Connection objects.)\n" \
"\n" \
"Check the enabled property to decide if you need to pay attention to this Watch, and" \
" look at the flags to see if you need to check for pending reads, or writes, or both." \
" Call the handle() method with the appropriate flags when you see that reads or writes" \
" are pending."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusWatch.html>
__slots__ = ("__weakref__", "_dbobj",) # to forestall typos
_instances = WeakValueDictionary()
def __new__(celf, _dbobj) :
self = celf._instances.get(_dbobj)
if self == None :
self = super().__new__(celf)
self._dbobj = _dbobj
celf._instances[_dbobj] = self
#end if
return \
self
#end __new__
# no __del__ method -- no underlying dispose API call
@property
def unix_fd(self) :
"the underlying file descriptor for this Watch."
return \
dbus.dbus_watch_get_unix_fd(self._dbobj)
#end unix_fd
def fileno(self) :
"for use with Pythons “select” functions."
return \
self.unix_fd
#end fileno
@property
def socket(self) :
return \
dbus.dbus_watch_get_socket(self._dbobj)
#end socket
@property
def flags(self) :
"returns WATCH_READABLE and/or WATCH_WRITABLE, indicating what to watch for."
return \
dbus.dbus_watch_get_flags(self._dbobj)
#end flags
# TODO: get/set data
def handle(self, flags) :
"tells libdbus that there is something to be read or written." \
" flags are a combination of WATCH_xxx values."
return \
dbus.dbus_watch_handle(self._dbobj, flags) != 0
#end handle
@property
def enabled(self) :
"does libdbus want you to actually watch this Watch."
return \
dbus.dbus_watch_get_enabled(self._dbobj) != 0
#end enabled
#end Watch
class Timeout :
"wrapper around a DBusTimeout object. Do not instantiate directly; they" \
" are created and destroyed by libdbus.\n" \
"\n" \
" A Timeout is the basic mechanism for plugging libdbus-created timeouts" \
" into your event loop. When created, they are passed to your add-timeout" \
" callback to manage; and conversely, when deleted, your remove-timeout" \
" callback is notified. (These callbacks are ones you attach to Server and" \
" Connection objects.)\n" \
"\n" \
"Check the enabled property to decide if you need to pay attention to this" \
" Timeout. Call the handle() method when the timeout becomes due, as measured" \
" from when it was initially created or most recently enabled, whichever" \
" happened last."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusTimeout.html>
__slots__ = ("__weakref__", "_dbobj",) # to forestall typos
_instances = WeakValueDictionary()
def __new__(celf, _dbobj) :
self = celf._instances.get(_dbobj)
if self == None :
self = super().__new__(celf)
self._dbobj = _dbobj
celf._instances[_dbobj] = self
#end if
return \
self
#end __new__
# no __del__ method -- no underlying dispose API call
@property
def interval(self) :
"how long in float seconds until the timeout should fire."
return \
dbus.dbus_timeout_get_interval(self._dbobj) / 1000
#end interval
# TODO: get/set data
def handle(self) :
"tells libdbus the timeout has fired."
return \
dbus.dbus_timeout_handle(self._dbobj)
#end handle
@property
def enabled(self) :
"does libdbus want you to actually schedule this Timeout."
return \
dbus.dbus_timeout_get_enabled(self._dbobj) != 0
#end enabled
#end Timeout
class ObjectPathVTable(TaskKeeper) :
"wrapper around an ObjectPathVTable struct. You can instantiate directly, or call" \
" the init method. An additional feature beyond the underlying libdbus capabilities" \
" is the option to specify an asyncio event loop. If the message handler returns" \
" a coroutine, then an asyncio task is created to run it, and a result of" \
" DBUS.HANDLER_RESULT_HANDLED is returned on behalf of the message handler;" \
" that way, the message function can do the minimum beyond some initial filtering of" \
" the message, leaving the time-consuming part of the work to the coroutine."
__slots__ = \
(
"_dbobj",
# need to keep references to ctypes-wrapped functions
# so they don't disappear prematurely:
"_wrap_unregister_func",
"_wrap_message_func",
) # to forestall typos
def __init__(self, *, loop = None, unregister = None, message = None) :
super().__init__()
super()._init()
self._dbobj = DBUS.ObjectPathVTable()
self.loop = loop
self._wrap_unregister_func = None
self._wrap_message_func = None
if unregister != None :
self.set_unregister(unregister)
#end if
if message != None :
self.set_message(message)
#end if
#end __init__
@classmethod
def init(celf, *, loop = None, unregister = None, message = None) :
"for consistency with other classes that dont want caller to instantiate directly."
return \
celf \
(
loop = loop,
unregister = unregister,
message = message,
)
#end init
def set_unregister(self, unregister) :
def wrap_unregister(c_conn, c_user_data) :
conn = Connection(dbus.dbus_connection_ref(c_conn))
unregister(conn, conn._user_data.get(c_user_data))
#end wrap_unregister
#begin set_unregister
if unregister != None :
self._wrap_unregister_func = DBUS.ObjectPathUnregisterFunction(wrap_unregister)
else :
self._wrap_unregister_func = None
#end if
self._dbobj.unregister_function = self._wrap_unregister_func
return \
self
#end set_unregister
def set_message(self, message) :
w_self = weak_ref(self)
def wrap_message(c_conn, c_message, c_user_data) :
self = _wderef(w_self, "vtable")
conn = Connection(dbus.dbus_connection_ref(c_conn))
msg = Message(dbus.dbus_message_ref(c_message))
user_data = conn._user_data.get(c_user_data)
result = message(conn, msg, user_data)
if asyncio.iscoroutine(result) :
self.create_task(result)
result = DBUS.HANDLER_RESULT_HANDLED
#end if
return \
result
#end wrap_message
#begin set_message
if message != None :
self._wrap_message_func = DBUS.ObjectPathMessageFunction(wrap_message)
else :
self._wrap_message_func = None
#end if
self._dbobj.message_function = self._wrap_message_func
return \
self
#end set_message
#end ObjectPathVTable
class _DummyError :
# like an Error, but is never set and so will never raise.
@property
def is_set(self) :
return \
False
#end is_set
def raise_if_set(self) :
pass
#end raise_if_set
#end _DummyError
def _get_error(error) :
# Common routine which processes an optional user-supplied Error
# argument, and returns 2 Error-like objects: the first a real
# Error object to be passed to the libdbus call, the second is
# either the same Error object or a separate _DummyError object
# on which to call raise_if_set() afterwards. The procedure for
# using this is
#
# error, my_error = _get_error(error)
# ... call libdbus routine, passing error._dbobj ...
# my_error.raise_if_set()
#
# If the user passes None for error, then an internal Error object
# is created, and returned as both results. That way, if it is
# filled in by the libdbus call, calling raise_if_set() will
# automatically raise the exception.
# But if the user passed their own Error object, then it is
# returned as the first result, and a _DummyError as the second
# result. This means the raise_if_set() call becomes a noop, and
# it is up to the caller to check if their Error object was filled
# in or not.
if error != None and not isinstance(error, Error) :
raise TypeError("error must be an Error")
#end if
if error != None :
my_error = _DummyError()
else :
my_error = Error()
error = my_error
#end if
return \
error, my_error
#end _get_error
def _get_timeout(timeout) :
# accepts a timeout in float seconds and converts it to integer milliseconds
# as expected by libdbus. Special-cases DBUS.TIMEOUT_INFINITE and DBUS.TIMEOUT_USE_DEFAULT,
# allowing these to be passed through unchanged.
if not isinstance(timeout, int) or timeout not in (DBUS.TIMEOUT_INFINITE, DBUS.TIMEOUT_USE_DEFAULT) :
timeout = round(timeout * 1000)
#end if
return \
timeout
#end _get_timeout
def _loop_attach(self, loop, dispatch) :
# attaches a Server or Connection object to a given asyncio event loop.
# If loop is None, then the default asyncio loop is used. The actual loop
# value is also stored as the loop attribute of the object.
if loop == None :
try :
# if running within a task, current loop takes priority
loop = get_running_loop()
except RuntimeError :
# not running within a task, use default loop
loop = get_event_loop()
#end try
#end if
watches = [] # do I need to keep track of Watch objects?
timeouts = []
def call_dispatch() :
status = dispatch()
if status == DBUS.DISPATCH_NEED_MEMORY :
raise DBusError(DBUS.ERROR_NO_MEMORY, "not enough memory for connection dispatch")
#end if
if status == DBUS.DISPATCH_DATA_REMAINS :
loop.call_soon(call_dispatch)
#end if
#end call_dispatch
def add_remove_watch(watch, add) :
def handle_watch_event(flags) :
# seems I need to remove the watch and add it again to
# avoid an endless stream of notifications that cause
# excessive CPU usage -- asyncio bug?
add_remove_watch(watch, False)
watch.handle(flags)
if watch.enabled :
add_remove_watch(watch, True)
#end if
if dispatch != None :
call_dispatch()
#end if
#end handle_watch_event
#end add_remove_watch
if DBUS.WATCH_READABLE & watch.flags != 0 :
if add :
loop.add_reader(watch, handle_watch_event, DBUS.WATCH_READABLE)
else :
loop.remove_reader(watch)
#end if
#end if
if DBUS.WATCH_WRITABLE & watch.flags != 0 :
if add :
loop.add_writer(watch, handle_watch_event, DBUS.WATCH_WRITABLE)
else :
loop.remove_writer(watch)
#end if
#end if
#end add_remove_watch
def handle_add_watch(watch, data) :
if watch not in watches :
watches.append(watch)
add_remove_watch(watch, True)
#end if
return \
True
#end handle_add_watch
def handle_watch_toggled(watch, data) :
add_remove_watch(watch, watch.enabled)
#end handle_watch_toggled
def handle_remove_watch(watch, data) :
try :
pos = watches.index(watch)
except ValueError :
pos = None
#end try
if pos != None :
watches[pos : pos + 1] = []
add_remove_watch(watch, False)
#end if
#end handle_remove_watch
def handle_timeout(timeout) :
if timeout["due"] != None and timeout["due"] <= loop.time() and timeout["timeout"].enabled :
timeout["timeout"].handle()
#end if
#end handle_timeout
def handle_add_timeout(timeout, data) :
if not any(timeout == t["timeout"] for t in timeouts) :
entry = \
{
"timeout" : timeout,
"due" : (lambda : None, lambda : loop.time() + timeout.interval)[timeout.enabled](),
}
timeouts.append(entry)
if timeout.enabled :
loop.call_later(timeout.interval, handle_timeout, entry)
#end if
#end if
return \
True
#end handle_add_timeout
def handle_timeout_toggled(timeout, data) :
# not sure what to do if a Timeout gets toggled from enabled to disabled
# and then to enabled again; effectively I update the due time from
# the time of re-enabling.
search = iter(timeouts)
while True :
entry = next(search, None)
if entry == None :
break
#end if
if entry["timeout"] == timeout :
if timeout.enabled :
entry["due"] = loop.time() + timeout.enterval
loop.call_later(timeout.interval, handle_timeout, entry)
else :
entry["due"] = None
#end if
break
#end if
#end while
#end handle_timeout_toggled
def handle_remove_timeout(timeout, data) :
new_timeouts = []
for entry in timeouts :
if entry["timeout"] == timeout :
entry["due"] = None # in case already queued, avoid segfault in handle_timeout
else :
new_timeouts.append(entry)
#end if
#end for
timeouts[:] = new_timeouts
#end handle_remove_timeout
#begin _loop_attach
self.set_watch_functions \
(
add_function = handle_add_watch,
remove_function = handle_remove_watch,
toggled_function = handle_watch_toggled,
data = None
)
self.set_timeout_functions \
(
add_function = handle_add_timeout,
remove_function = handle_remove_timeout,
toggled_function = handle_timeout_toggled,
data = None
)
self.loop = loop
self = None # avoid circularity
#end _loop_attach
class _MatchActionEntry :
__slots__ = ("rule", "actions")
class _Action :
__slots__ = ("func", "user_data")
def __init__(self, func, user_data) :
self.func = func
self.user_data = user_data
#end __init__
def __eq__(a, b) :
# needed to allow equality comparison of set entries
return \
(
a.func == b.func
and
data_key(a.user_data) == data_key(b.user_data)
)
#end __eq__
def __hash__(self) :
return \
hash((self.func, data_key(self.user_data)))
#end __hash__
#end _Action
def __init__(self, rule) :
self.rule = rule
self.actions = set()
#end __init__
#end _MatchActionEntry
@enum.unique
class STOP_ON(enum.Enum) :
"set of conditions on which to raise StopAsyncIteration:\n" \
"\n" \
" TIMEOUT - timeout has elapsed\n" \
" CLOSED - server/connection has closed.\n" \
"\n" \
"Otherwise None will be returned on timeout, and the usual BrokenPipeError" \
" exception will be raised when the connection is closed."
TIMEOUT = 1
CLOSED = 2
#end STOP_ON
class Connection(TaskKeeper) :
"wrapper around a DBusConnection object. Do not instantiate directly; use the open" \
" or bus_get methods."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html>
__slots__ = \
(
"_dbobj",
"_filters",
"_match_actions",
"_receive_queue",
"_receive_queue_enabled",
"_awaiting_receive",
"_user_data",
# need to keep references to ctypes-wrapped functions
# so they don't disappear prematurely:
"_object_paths",
"_add_watch_function",
"_remove_watch_function",
"_toggled_watch_function",
"_free_watch_data",
"_add_timeout_function",
"_remove_timeout_function",
"_toggled_timeout_function",
"_free_timeout_data",
"_wakeup_main",
"_free_wakeup_main_data",
"_dispatch_status",
"_free_dispatch_status_data",
"_allow_unix_user",
"_free_unix_user_data",
) # to forestall typos
_instances = WeakValueDictionary()
_shared_connections = [None, None]
def __new__(celf, _dbobj) :
self = celf._instances.get(_dbobj)
if self == None :
self = super().__new__(celf)
super()._init(self)
self._dbobj = _dbobj
self._user_data = {}
self._filters = {}
self._match_actions = {}
self._receive_queue = None
self._receive_queue_enabled = set()
self._awaiting_receive = []
self._object_paths = {}
celf._instances[_dbobj] = self
else :
dbus.dbus_connection_unref(self._dbobj)
# lose extra reference created by caller
#end if
return \
self
#end __new__
def __del__(self) :
if self._dbobj != None :
if self.loop != None :
# remove via direct low-level libdbus calls
dbus.dbus_connection_set_watch_functions(self._dbobj, None, None, None, None, None)
dbus.dbus_connection_set_timeout_functions(self._dbobj, None, None, None, None, None)
self.loop = None
#end if
# Any entries still in super(TaskKeeper, self)._cur_tasks will be lost
# at this point. I leave it to asyncio to report them as destroyed
# while still pending, and the caller to notice this as a program bug.
dbus.dbus_connection_unref(self._dbobj)
self._dbobj = None
#end if
#end __del__
@classmethod
def open(celf, address, private, error = None) :
"opens a Connection to a specified address, separate from the" \
" system or session buses."
error, my_error = _get_error(error)
result = (dbus.dbus_connection_open, dbus.dbus_connection_open_private)[private](address.encode(), error._dbobj)
my_error.raise_if_set()
if result != None :
result = celf(result)
#end if
return \
result
#end open
@classmethod
async def open_async(celf, address, private, error = None, loop = None, timeout = DBUS.TIMEOUT_INFINITE) :
"opens a Connection to a specified address, separate from the" \
" system or session buses."
# There is no nonblocking version of dbus_connection_open/dbus_connection_open_private,
# so I invoke it in a separate thread.
if loop == None :
loop = get_running_loop()
#end if
error, my_error = _get_error(error)
if timeout == DBUS.TIMEOUT_USE_DEFAULT :
timeout = DBUSX.DEFAULT_TIMEOUT
elif timeout == DBUS.TIMEOUT_INFINITE :
timeout = None
#end if
try :
result = await call_async \
(
func = (dbus.dbus_connection_open, dbus.dbus_connection_open_private)[private],
funcargs = (address.encode(), error._dbobj),
timeout = timeout,
abort = dbus.dbus_connection_unref,
loop = loop
)
except TimeoutError :
result = None
error.set(DBUS.ERROR_TIMEOUT, "connection did not open in time")
#end try
my_error.raise_if_set()
if result != None :
result = celf(result)
result.attach_asyncio(loop)
#end if
return \
result
#end open_async
def _flush_awaiting_receive(self) :
if self._receive_queue != None :
while len(self._awaiting_receive) != 0 :
waiting = self._awaiting_receive.pop(0)
waiting.set_exception(BrokenPipeError("async receives have been disabled"))
#end while
#end if
#end _flush_awaiting_receive
def close(self) :
self._flush_awaiting_receive()
dbus.dbus_connection_close(self._dbobj)
#end close
@property
def is_connected(self) :
return \
dbus.dbus_connection_get_is_connected(self._dbobj) != 0
#end is_connected
@property
def is_authenticated(self) :
return \
dbus.dbus_connection_get_is_authenticated(self._dbobj) != 0
#end is_authenticated
@property
def is_anonymous(self) :
return \
dbus.dbus_connection_get_is_anonymous(self._dbobj) != 0
#end is_anonymous
@property
def server_id(self) :
"asks the server at the other end for its unique id."
c_result = dbus.dbus_connection_get_server_id(self._dbobj)
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end server_id
def can_send_type(self, type_code) :
"can this Connection send values of the specified TYPE_XXX code." \
" Mainly useful for checking if we can send TYPE_UNIX_FD values."
return \
dbus.dbus_connection_can_send_type(self._dbobj, type_code) != 0
#end can_send_type
def set_exit_on_disconnect(self, exit_on_disconnect) :
dbus.dbus_connection_set_exit_on_disconnect(self._dbobj, exit_on_disconnect)
#end set_exit_on_disconnect
def preallocate_send(self) :
result = dbus.dbus_connection_preallocate_send(self._dbobj)
if result == None :
raise CallFailed("dbus_connection_preallocate_send")
#end if
return \
PreallocatedSend(result, self)
#end preallocate_send
def send_preallocated(self, preallocated, message) :
if not isinstance(preallocated, PreallocatedSend) or not isinstance(message, Message) :
raise TypeError("preallocated must be a PreallocatedSend and message must be a Message")
#end if
assert not preallocated._sent, "preallocated has already been sent"
serial = ct.c_uint()
dbus.dbus_connection_send_preallocated(self._dbobj, preallocated._dbobj, message._dbobj, ct.byref(serial))
preallocated._sent = True
return \
serial.value
#end send_preallocated
def send(self, message) :
"puts a message in the outgoing queue."
if not isinstance(message, Message) :
raise TypeError("message must be a Message")
#end if
serial = ct.c_uint()
if not dbus.dbus_connection_send(self._dbobj, message._dbobj, ct.byref(serial)) :
raise CallFailed("dbus_connection_send")
#end if
return \
serial.value
#end send
def send_with_reply(self, message, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"puts a message in the outgoing queue and returns a PendingCall" \
" that you can use to obtain the reply."
if not isinstance(message, Message) :
raise TypeError("message must be a Message")
#end if
pending_call = ct.c_void_p()
if not dbus.dbus_connection_send_with_reply(self._dbobj, message._dbobj, ct.byref(pending_call), _get_timeout(timeout)) :
raise CallFailed("dbus_connection_send_with_reply")
#end if
if pending_call.value != None :
result = PendingCall(pending_call.value, self)
else :
result = None
#end if
return \
result
#end send_with_reply
def send_with_reply_and_block(self, message, timeout = DBUS.TIMEOUT_USE_DEFAULT, error = None) :
"sends a message, blocks the thread until the reply is available, and returns it."
if not isinstance(message, Message) :
raise TypeError("message must be a Message")
#end if
error, my_error = _get_error(error)
reply = dbus.dbus_connection_send_with_reply_and_block(self._dbobj, message._dbobj, _get_timeout(timeout), error._dbobj)
my_error.raise_if_set()
if reply != None :
result = Message(reply)
else :
result = None
#end if
return \
result
#end send_with_reply_and_block
async def send_await_reply(self, message, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"queues a message, suspends the coroutine (letting the event loop do" \
" other things) until the reply is available, and returns it."
if not isinstance(message, Message) :
raise TypeError("message must be a Message")
#end if
assert self.loop != None, "no event loop to attach coroutine to"
pending_call = ct.c_void_p()
if not dbus.dbus_connection_send_with_reply(self._dbobj, message._dbobj, ct.byref(pending_call), _get_timeout(timeout)) :
raise CallFailed("dbus_connection_send_with_reply")
#end if
if pending_call.value != None :
pending = PendingCall(pending_call.value, self)
else :
pending = None
#end if
reply = None # to begin with
if pending != None :
reply = await pending.await_reply()
#end if
return \
reply
#end send_await_reply
def flush(self) :
"makes sure all queued messages have been sent, blocking" \
" the thread until this is done."
dbus.dbus_connection_flush(self._dbobj)
#end flush
def read_write_dispatch(self, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"dispatches the first available message, if any. Otherwise blocks the" \
" thread until it can read or write, and does so before returning. Returns" \
" True as long as the Connection remains connected."
return \
dbus.dbus_connection_read_write_dispatch(self._dbobj, _get_timeout(timeout)) != 0
#end read_write_dispatch
def read_write(self, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"blocks the thread until something can be read or written on the Connection," \
" and does so, returning True. If the Connection has been disconnected," \
" immediately returns False."
return \
dbus.dbus_connection_read_write(self._dbobj, _get_timeout(timeout)) != 0
#end read_write
def borrow_message(self) :
"tries to peek at the next available message waiting to be read, returning" \
" None if these isnt one. Call the Messages return_borrowed() method" \
" to return it to the queue, or steal_borrowed() to confirm that you have" \
" read the message."
msg = dbus.dbus_connection_borrow_message(self._dbobj)
if msg != None :
msg = Message(msg)
msg._conn = self
msg._borrowed = True
#end if
return \
msg
#end borrow_message
# returning/stealing borrowed messages done with
# Message.return_borrowed and Message.steal_borrowed
def pop_message(self) :
"returns the next available incoming Message, if any, otherwise returns None." \
" Note this bypasses all message filtering/dispatching on this Connection."
message = dbus.dbus_connection_pop_message(self._dbobj)
if message != None :
message = Message(message)
#end if
return \
message
#end pop_message
@property
def dispatch_status(self) :
"checks the state of the incoming message queue; returns a DISPATCH_XXX code."
return \
dbus.dbus_connection_get_dispatch_status(self._dbobj)
#end dispatch_status
def dispatch(self) :
"processes any available data, adding messages into the incoming" \
" queue as appropriate. returns a DISPATCH_XXX code."
return \
dbus.dbus_connection_dispatch(self._dbobj)
#end dispatch
def set_watch_functions(self, add_function, remove_function, toggled_function, data, free_data = None) :
"sets the callbacks for libdbus to use to notify you of Watch objects it wants" \
" you to manage."
def wrap_add_function(c_watch, _data) :
return \
add_function(Watch(c_watch), data)
#end wrap_add_function
def wrap_remove_function(c_watch, _data) :
return \
remove_function(Watch(c_watch), data)
#end wrap_remove_function
def wrap_toggled_function(c_watch, _data) :
return \
toggled_function(Watch(c_watch), data)
#end wrap_toggled_function
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_watch_functions
self._add_watch_function = DBUS.AddWatchFunction(wrap_add_function)
self._remove_watch_function = DBUS.RemoveWatchFunction(wrap_remove_function)
if toggled_function != None :
self._toggled_watch_function = DBUS.WatchToggledFunction(wrap_toggled_function)
else :
self._toggled_watch_function = None
#end if
if free_data != None :
self._free_watch_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_watch_data = None
#end if
if not dbus.dbus_connection_set_watch_functions(self._dbobj, self._add_watch_function, self._remove_watch_function, self._toggled_watch_function, None, self._free_watch_data) :
raise CallFailed("dbus_connection_set_watch_functions")
#end if
#end set_watch_functions
def set_timeout_functions(self, add_function, remove_function, toggled_function, data, free_data = None) :
"sets the callbacks for libdbus to use to notify you of Timeout objects it wants" \
" you to manage."
def wrap_add_function(c_timeout, _data) :
return \
add_function(Timeout(c_timeout), data)
#end wrap_add_function
def wrap_remove_function(c_timeout, _data) :
return \
remove_function(Timeout(c_timeout), data)
#end wrap_remove_function
def wrap_toggled_function(c_timeout, _data) :
return \
toggled_function(Timeout(c_timeout), data)
#end wrap_toggled_function
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_timeout_functions
self._add_timeout_function = DBUS.AddTimeoutFunction(wrap_add_function)
self._remove_timeout_function = DBUS.RemoveTimeoutFunction(wrap_remove_function)
if toggled_function != None :
self._toggled_timeout_function = DBUS.TimeoutToggledFunction(wrap_toggled_function)
else :
self._toggled_timeout_function = None
#end if
if free_data != None :
self._free_timeout_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_timeout_data = None
#end if
if not dbus.dbus_connection_set_timeout_functions(self._dbobj, self._add_timeout_function, self._remove_timeout_function, self._toggled_timeout_function, None, self._free_timeout_data) :
raise CallFailed("dbus_connection_set_timeout_functions")
#end if
#end set_timeout_functions
def set_wakeup_main_function(self, wakeup_main, data, free_data = None) :
"sets the callback to use for libdbus to notify you that something has" \
" happened requiring processing on the Connection."
def wrap_wakeup_main(_data) :
wakeup_main(data)
#end wrap_wakeup_main
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_wakeup_main_function
if wakeup_main != None :
self._wakeup_main = DBUS.WakeupMainFunction(wrap_wakeup_main)
else :
self._wakeup_main = None
#end if
if free_data != None :
self._free_wakeup_main_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_wakeup_main_data = None
#end if
dbus.dbus_connection_set_wakeup_main_function(self._dbobj, self._wakeup_main, None, self._free_wakeup_main_data)
#end set_wakeup_main_function
def set_dispatch_status_function(self, function, data, free_data = None) :
"sets the callback to use for libdbus to notify you of a change in the" \
" dispatch status of the Connection."
w_self = weak_ref(self)
def wrap_dispatch_status(_conn, status, _data) :
function(_wderef(w_self, "connection"), status, data)
#end wrap_dispatch_status
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_dispatch_status_function
self._dispatch_status = DBUS.DispatchStatusFunction(wrap_dispatch_status)
if free_data != None :
self._free_wakeup_main_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_wakeup_main_data = None
#end if
dbus.dbus_connection_set_dispatch_status_function(self._dbobj, self._dispatch_status, None, self._free_wakeup_main_data)
#end set_dispatch_status_function
@property
def unix_fd(self) :
c_fd = ct.c_int()
if dbus.dbus_connection_get_unix_fd(self._dbobj, ct.byref(c_fd)) :
result = c_fd.value
else :
result = None
#end if
return \
result
#end unix_fd
def fileno(self) :
"for use with Pythons “select” functions."
return \
self.unix_fd
#end fileno
@property
def socket(self) :
c_fd = ct.c_int()
if dbus.dbus_connection_get_socket(self._dbobj, ct.byref(c_fd)) :
result = c_fd.value
else :
result = None
#end if
return \
result
#end socket
@property
def unix_process_id(self) :
c_pid = ct.c_ulong()
if dbus.dbus_connection_get_unix_process_id(self._dbobj, ct.byref(c_pid)) :
result = c_pid.value
else :
result = None
#end if
return \
result
#end unix_process_id
@property
def unix_user(self) :
c_uid = ct.c_ulong()
if dbus.dbus_connection_get_unix_user(self._dbobj, ct.byref(c_uid)) :
result = c_uid.value
else :
result = None
#end if
return \
result
#end unix_user
# TODO: get_adt
def set_unix_user_function(self, allow_unix_user, data, free_data = None) :
w_self = weak_ref(self)
def wrap_allow_unix_user(c_conn, uid, c_data) :
return \
allow_unix_user(_wderef(w_self, "connection"), uid, data)
#end wrap_allow_unix_user
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_unix_user_function
if allow_unix_user != None :
self._allow_unix_user = DBUS.AllowUnixUserFunction(wrap_allow_unix_user)
else :
self._allow_unix_user = None
#end if
if free_data != None :
self._free_unix_user_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_unix_user_data = None
#end if
dbus.dbus_connection_set_unix_user_function(self._dbobj, self._allow_unix_user, None, self._free_unix_user_data)
#end set_unix_user_function
def set_allow_anonymous(self, allow) :
dbus.dbus_connection_set_allow_anonymous(self._dbobj, allow)
#end set_allow_anonymous
def set_route_peer_messages(self, enable) :
dbus.dbus_connection_set_route_peer_messages(self._dbobj, enable)
#end set_route_peer_messages
def add_filter(self, function, user_data, free_data = None) :
"adds a filter callback that gets to look at all incoming messages" \
" before they get to the dispatch system. The same function can be added" \
" multiple times as long as the user_data is different."
w_self = weak_ref(self)
def wrap_function(c_conn, c_message, _data) :
self = _wderef(w_self, "connection")
message = Message(dbus.dbus_message_ref(c_message))
result = function(self, message, user_data)
if asyncio.iscoroutine(result) :
self.create_task(result)
result = DBUS.HANDLER_RESULT_HANDLED
#end if
return \
result
#end wrap_function
def wrap_free_data(_data) :
free_data(user_data)
#end wrap_free_data
#begin add_filter
filter_key = (function, data_key(user_data))
filter_value = \
{
"function" : DBUS.HandleMessageFunction(wrap_function),
"free_data" : (lambda : None, lambda : DBUS.FreeFunction(wrap_free_data))[free_data != None](),
}
# pass user_data id because libdbus identifies filter entry by both function address and user data address
if not dbus.dbus_connection_add_filter(self._dbobj, filter_value["function"], filter_key[1], filter_value["free_data"]) :
raise CallFailed("dbus_connection_add_filter")
#end if
self._filters[filter_key] = filter_value
# need to ensure wrapped functions dont disappear prematurely
#end add_filter
def remove_filter(self, function, user_data) :
"removes a message filter added by add_filter. The filter is identified" \
" by both the function object and the user_data that was passed."
filter_key = (function, data_key(user_data))
if filter_key not in self._filters :
raise KeyError("removing nonexistent Connection filter")
#end if
filter_value = self._filters[filter_key]
# pass user_data id because libdbus identifies filter entry by both function address and user data address
dbus.dbus_connection_remove_filter(self._dbobj, filter_value["function"], filter_key[1])
del self._filters[filter_key]
#end remove_filter
def register_object_path(self, path, vtable, user_data, error = None) :
"registers an ObjectPathVTable as a dispatch handler for a specified" \
" path within your object hierarchy."
if not isinstance(vtable, ObjectPathVTable) :
raise TypeError("vtable must be an ObjectPathVTable")
#end if
self._object_paths[path] = {"vtable" : vtable, "user_data" : user_data} # ensure it doesnt disappear prematurely
error, my_error = _get_error(error)
if user_data != None :
c_user_data = id(user_data)
self._user_data[c_user_data] = user_data
else :
c_user_data = None
#end if
dbus.dbus_connection_try_register_object_path(self._dbobj, path.encode(), vtable._dbobj, c_user_data, error._dbobj)
my_error.raise_if_set()
#end register_object_path
def register_fallback(self, path, vtable, user_data, error = None) :
"registers an ObjectPathVTable as a dispatch handler for an entire specified" \
" subtree within your object hierarchy."
if not isinstance(vtable, ObjectPathVTable) :
raise TypeError("vtable must be an ObjectPathVTable")
#end if
self._object_paths[path] = {"vtable" : vtable, "user_data" : user_data} # ensure it doesnt disappear prematurely
error, my_error = _get_error(error)
if user_data != None :
c_user_data = id(user_data)
self._user_data[c_user_data] = user_data
else :
c_user_data = None
#end if
dbus.dbus_connection_try_register_fallback(self._dbobj, path.encode(), vtable._dbobj, c_user_data, error._dbobj)
my_error.raise_if_set()
#end register_fallback
def unregister_object_path(self, path) :
"removes a previously-registered ObjectPathVTable handler at a specified" \
" point (single object or entire subtree) within your object hierarchy."
if path not in self._object_paths :
raise KeyError("unregistering unregistered path")
#end if
if not dbus.dbus_connection_unregister_object_path(self._dbobj, path.encode()) :
raise CallFailed("dbus_connection_unregister_object_path")
#end if
user_data = self._object_paths[path]["user_data"]
c_user_data = id(user_data)
nr_remaining_refs = sum(int(self._object_paths[p]["user_data"] == user_data) for p in self._object_paths if p != path)
if nr_remaining_refs == 0 :
try :
del self._user_data[c_user_data]
except KeyError :
pass
#end try
#end if
del self._object_paths[path]
#end unregister_object_path
def get_object_path_data(self, path) :
"returns the user_data you passed when previously registering an ObjectPathVTable" \
" that covers this path in your object hierarchy, or None if no suitable match" \
" could be found."
c_data_p = ct.c_void_p()
if not dbus.dbus_connection_get_object_path_data(self._dbobj, path.encode(), ct.byref(c_data_p)) :
raise CallFailed("dbus_connection_get_object_path_data")
#end if
return \
self._user_data.get(c_data_p.value)
#end get_object_path_data
def list_registered(self, parent_path) :
"lists all the object paths for which you have ObjectPathVTable handlers registered."
child_entries = ct.POINTER(ct.c_char_p)()
if not dbus.dbus_connection_list_registered(self._dbobj, parent_path.encode(), ct.byref(child_entries)) :
raise CallFailed("dbus_connection_list_registered")
#end if
result = []
i = 0
while True :
entry = child_entries[i]
if entry == None :
break
result.append(entry.decode())
i += 1
#end while
dbus.dbus_free_string_array(child_entries)
return \
result
#end list_registered
@staticmethod
def _queue_received_message(self, message, _) :
# message filter which queues messages as appropriate for receive_message_async.
# Must be static so same function object can be passed to all add_filter/remove_filter
# calls.
queueit = message.type in self._receive_queue_enabled
if queueit :
self._receive_queue.append(message)
while len(self._awaiting_receive) != 0 :
# wake them all up, because I dont know what message types
# each might be waiting for
waiting = self._awaiting_receive.pop(0)
waiting.set_result(True) # result actually ignored
#end while
#end if
return \
(DBUS.HANDLER_RESULT_NOT_YET_HANDLED, DBUS.HANDLER_RESULT_HANDLED)[queueit]
#end _queue_received_message
def enable_receive_message(self, queue_types) :
"enables/disables message types for reception via receive_message_async." \
" queue_types is a set or sequence of DBUS.MESSAGE_TYPE_XXX values for" \
" the types of messages to be put into the receive queue, or None to" \
" disable all message types; this replaces queue_types passed to" \
" any prior enable_receive_message_async call on this Connection."
assert self.loop != None, "no event loop to attach coroutines to"
enable = queue_types != None and len(queue_types) != 0
if (
enable
and
not all
(
m
in
(
DBUS.MESSAGE_TYPE_METHOD_CALL,
DBUS.MESSAGE_TYPE_METHOD_RETURN,
DBUS.MESSAGE_TYPE_ERROR,
DBUS.MESSAGE_TYPE_SIGNAL,
)
for m in queue_types
)
) :
raise TypeError("invalid message type in queue_types: %s" % repr(queue_types))
#end if
if enable :
if self._receive_queue == None :
self.add_filter(self._queue_received_message, None)
self._receive_queue = []
#end if
self._receive_queue_enabled.clear()
self._receive_queue_enabled.update(queue_types)
else :
if self._receive_queue != None :
self._flush_awaiting_receive()
self.remove_filter(self._queue_received_message, None)
self._receive_queue = None
#end if
#end if
#end enable_receive_message
async def receive_message_async(self, want_types = None, timeout = DBUS.TIMEOUT_INFINITE) :
"receives the first available queued message of an appropriate type, blocking" \
" if none is available and timeout is nonzero. Returns None if the timeout" \
" elapses without a suitable message becoming available. want_types can be" \
" None to receive any of the previously-enabled message types, or a set or" \
" sequence of DBUS.MESSAGE_TYPE_XXX values to look only for messages of those" \
" types.\n" \
"\n" \
"You must have previously made a call to enable_receive_message to enable" \
" queueing of one or more message types on this Connection."
assert self._receive_queue != None, "receive_message_async not enabled"
# should I check if want_types contains anything not in self._receive_queue_enabled?
if timeout == DBUS.TIMEOUT_USE_DEFAULT :
timeout = DBUSX.DEFAULT_TIMEOUT
#end if
if timeout != DBUS.TIMEOUT_INFINITE :
finish_time = self.loop.time() + timeout
else :
finish_time = None
#end if
result = ... # indicates “watch this space”
while True :
# keep rescanning queue until got something or timeout
index = 0 # start next queue scan
while True :
if index == len(self._receive_queue) :
# nothing currently suitable on queue
if (
timeout == 0
or
finish_time != None
and
self.loop.time() > finish_time
) :
# waited too long, give up
result = None
break
#end if
if not self.is_connected :
raise BrokenPipeError("Connection has been disconnected")
#end if
# wait and see if something turns up
awaiting = self.loop.create_future()
self._awaiting_receive.append(awaiting)
if finish_time != None :
wait_timeout = finish_time - self.loop.time()
else :
wait_timeout = None
#end if
await self.wait \
(
(awaiting,),
timeout = wait_timeout
)
# ignore done & pending results because they
# dont match up with future Im waiting for
try :
self._awaiting_receive.remove(awaiting)
except ValueError :
pass
#end try
awaiting.cancel()
# just to avoid “Future exception was never retrieved” message
break # start new queue scan
#end if
# check next queue item
msg = self._receive_queue[index]
if want_types == None or msg.type in want_types :
# caller wants this one
result = msg
self._receive_queue.pop(index) # remove msg from queue
break
#end if
index += 1
#end while
if result != ... :
# either got something or given up
break
#end while
return \
result
#end receive_message_async
def iter_messages_async(self, want_types = None, stop_on = None, timeout = DBUS.TIMEOUT_INFINITE) :
"wrapper around receive_message_async() to allow use with an async-for statement." \
" Lets you write\n" \
"\n" \
" async for message in «conn».iter_messages_async(«want_types», «stop_on», «timeout») :" \
" «process message»\n" \
" #end for\n" \
"\n" \
"to receive and process messages in a loop. stop_on is an optional set of" \
" STOP_ON.xxx values indicating the conditions under which the iterator will" \
" raise StopAsyncIteration to terminate the loop."
if stop_on == None :
stop_on = frozenset()
elif (
not isinstance(stop_on, (set, frozenset))
or
not all(isinstance(elt, STOP_ON) for elt in stop_on)
) :
raise TypeError("stop_on must be None or set of STOP_ON")
#end if
assert self._receive_queue != None, "receive_message_async not enabled"
return \
_MsgAiter(self, want_types, stop_on, timeout)
#end iter_messages_async
# TODO: allocate/free data slot -- staticmethods
# TODO: get/set data
def set_change_sigpipe(self, will_modify_sigpipe) :
dbus.dbus_connection_set_change_sigpipe(self._dbobj, will_modify_sigpipe)
#end set_change_sigpipe
@property
def max_message_size(self) :
return \
dbus.dbus_connection_get_max_message_size(self._dbobj)
#end max_message_size
@max_message_size.setter
def max_message_size(self, size) :
dbus.dbus_connection_set_max_message_size(self._dbobj, size)
#end max_message_size
@property
def max_received_size(self) :
return \
dbus.dbus_connection_get_max_received_size(self._dbobj)
#end max_received_size
@max_received_size.setter
def max_received_size(self, size) :
dbus.dbus_connection_set_max_received_size(self._dbobj, size)
#end max_received_size
@property
def max_message_unix_fds(self) :
return \
dbus.dbus_connection_get_max_message_unix_fds(self._dbobj)
#end max_message_unix_fds
@max_message_unix_fds.setter
def max_message_unix_fds(self, size) :
dbus.dbus_connection_set_max_message_unix_fds(self._dbobj, size)
#end max_message_unix_fds
@property
def max_received_unix_fds(self) :
return \
dbus.dbus_connection_get_max_received_unix_fds(self._dbobj)
#end max_received_unix_fds
@max_received_unix_fds.setter
def max_received_unix_fds(self, size) :
dbus.dbus_connection_set_max_received_unix_fds(self._dbobj, size)
#end max_received_unix_fds
@property
def outgoing_size(self) :
return \
dbus.dbus_connection_get_outgoing_size(self._dbobj)
#end outgoing_size
@property
def outgoing_unix_fds(self) :
return \
dbus.dbus_connection_get_outgoing_unix_fds(self._dbobj)
#end outgoing_unix_fds
@property
def has_messages_to_send(self) :
return \
dbus.dbus_connection_has_messages_to_send(self._dbobj) != 0
#end has_messages_to_send
# message bus APIs
# <https://dbus.freedesktop.org/doc/api/html/group__DBusBus.html>
@classmethod
def bus_get(celf, type, private, error = None) :
"returns a Connection to one of the predefined D-Bus buses; type is a BUS_xxx value."
error, my_error = _get_error(error)
result = (dbus.dbus_bus_get, dbus.dbus_bus_get_private)[private](type, error._dbobj)
my_error.raise_if_set()
if result != None :
result = celf(result)
#end if
return \
result
#end bus_get
@classmethod
async def bus_get_async(celf, type, private, error = None, loop = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
if loop == None :
loop = get_running_loop()
#end if
assert type in (DBUS.BUS_SESSION, DBUS.BUS_SYSTEM, DBUS.BUS_STARTER), \
"bus type must be BUS_SESSION, BUS_SYSTEM or BUS_STARTER"
if type == DBUS.BUS_STARTER :
starter_type = os.environ.get(DBUSX.STARTER_BUS_ADDRESS_TYPE)
is_system_bus = starter_type != None and starter_type == DBUSX.BUS_TYPE_SYSTEM
addr = os.environ.get(DBUSX.STARTER_BUS_ADDRESS_VAR)
else :
is_system_bus = type == DBUS.BUS_SYSTEM
addr = os.environ.get \
(
(DBUSX.SESSION_BUS_ADDRESS_VAR, DBUSX.SYSTEM_BUS_ADDRESS_VAR)[is_system_bus]
)
#end if
if not private and celf._shared_connections[is_system_bus] != None :
result = celf._shared_connections[is_system_bus]
else :
if addr == None :
addr = (DBUSX.SESSION_BUS_ADDRESS, DBUSX.SYSTEM_BUS_ADDRESS)[is_system_bus]
#end if
try :
result = await celf.open_async(addr, private, error, loop, timeout)
if error != None and error.is_set :
raise _Abort
#end if
await result.bus_register_async(error = error, timeout = timeout)
if error != None and error.is_set :
raise _Abort
#end if
if not private :
celf._shared_connections[is_system_bus] = result
#end if
except _Abort :
result = None
#end try
#end if
return \
result
#end bus_get_async
def bus_register(self, error = None) :
"Only to be used if you created the Connection with open() instead of bus_get();" \
" sends a “Hello” message to the D-Bus daemon to get a unique name assigned." \
" Can only be called once."
error, my_error = _get_error(error)
dbus.dbus_bus_register(self._dbobj, error._dbobj)
my_error.raise_if_set()
#end bus_register
async def bus_register_async(self, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"Only to be used if you created the Connection with open() instead of bus_get();" \
" sends a “Hello” message to the D-Bus daemon to get a unique name assigned." \
" Can only be called once."
assert self.loop != None, "no event loop to attach coroutine to"
assert self.bus_unique_name == None, "bus already registered"
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "Hello"
)
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
else :
self.bus_unique_name = reply.expect_return_objects("s")[0]
#end if
#end bus_register_async
@property
def bus_unique_name(self) :
"returns None if the bus connection has not been registered. Note that the" \
" unique_name can only be set once."
result = dbus.dbus_bus_get_unique_name(self._dbobj)
if result != None :
result = result.decode()
#end if
return \
result
#end bus_unique_name
@bus_unique_name.setter
def bus_unique_name(self, unique_name) :
if not dbus.dbus_bus_set_unique_name(self._dbobj, unique_name.encode()) :
raise CallFailed("dbus_bus_set_unique_name")
#end if
#end bus_unique_name
#+
# Calls to D-Bus Daemon
#-
@property
def bus_id(self) :
my_error = Error()
c_result = dbus.dbus_bus_get_id(self._dbobj, my_error._dbobj)
my_error.raise_if_set()
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end bus_id
@property
async def bus_id_async(self) :
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "GetId"
)
reply = await self.send_await_reply(message)
return \
reply.expect_return_objects("s")[0]
#end bus_id_async
def bus_get_unix_user(self, name, error = None) :
error, my_error = _get_error(error)
result = dbus.dbus_bus_get_unix_user(self._dbobj, name.encode(), error._dbobj)
my_error.raise_if_set()
return \
result
#end bus_get_unix_user
async def bus_get_unix_user_async(self, name, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "GetConnectionUnixUser"
)
message.append_objects("s", name)
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
result = None
else :
result = reply.expect_return_objects("u")[0]
#end if
return \
result
#end bus_get_unix_user_async
def bus_request_name(self, name, flags, error = None) :
"asks the D-Bus daemon to register the specified bus name on your behalf," \
" blocking the thread until the reply is received. flags is a combination of" \
" NAME_FLAG_xxx bits. Result will be a REQUEST_NAME_REPLY_xxx value or -1 on error."
error, my_error = _get_error(error)
result = dbus.dbus_bus_request_name(self._dbobj, name.encode(), flags, error._dbobj)
my_error.raise_if_set()
return \
result
#end bus_request_name
async def bus_request_name_async(self, name, flags, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"asks the D-Bus daemon to register the specified bus name on your behalf. flags is" \
" a combination of NAME_FLAG_xxx bits. Result will be a REQUEST_NAME_REPLY_xxx value" \
" or None on error."
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "RequestName"
)
message.append_objects("su", name, flags)
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
result = None
else :
result = reply.expect_return_objects("u")[0]
#end if
return \
result
#end bus_request_name_async
def bus_release_name(self, name, error = None) :
"asks the D-Bus daemon to release your registration of the specified bus name," \
" blocking the thread until the reply is received."
error, my_error = _get_error(error)
result = dbus.dbus_bus_release_name(self._dbobj, name.encode(), error._dbobj)
my_error.raise_if_set()
return \
result
#end bus_release_name
async def bus_release_name_async(self, name, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"asks the D-Bus daemon to release your registration of the specified bus name."
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "ReleaseName"
)
message.append_objects("s", name)
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
result = None
else :
result = reply.expect_return_objects("u")[0]
#end if
return \
result
#end bus_release_name_async
def bus_name_has_owner(self, name, error = None) :
"asks the D-Bus daemon if anybody has claimed the specified bus name, blocking" \
" the thread until the reply is received."
error, my_error = _get_error(error)
result = dbus.dbus_bus_name_has_owner(self._dbobj, name.encode(), error._dbobj)
my_error.raise_if_set()
return \
result
#end bus_name_has_owner
async def bus_name_has_owner_async(self, name, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"asks the D-Bus daemon if anybody has claimed the specified bus name."
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "NameHasOwner"
)
message.append_objects("s", name)
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
result = None
else :
result = reply.expect_return_objects("b")[0]
#end if
return \
result
#end bus_name_has_owner_async
def bus_start_service_by_name(self, name, flags = 0, error = None) :
error, my_error = _get_error(error)
outflags = ct.c_uint()
success = dbus.dbus_bus_start_service_by_name(self._dbobj, name.encode(), flags, ct.byref(outflags), error._dbobj)
my_error.raise_if_set()
return \
outflags.value
#end bus_start_service_by_name
async def bus_start_service_by_name_async(self, name, flags = 0, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "StartServiceByName"
)
message.append_objects("su", name, flags)
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
result = None
else :
result = reply.expect_return_objects("u")[0]
#end if
return \
result
#end bus_start_service_by_name
def bus_add_match(self, rule, error = None) :
"adds a match rule for messages you want to receive. By default you get all" \
" messages addressed to your bus name(s); but you can use this, for example," \
" to request notification of signals indicating useful events on the system."
error, my_error = _get_error(error)
dbus.dbus_bus_add_match(self._dbobj, format_rule(rule).encode(), error._dbobj)
my_error.raise_if_set()
#end bus_add_match
async def bus_add_match_async(self, rule, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"adds a match rule for messages you want to receive. By default you get all" \
" messages addressed to your bus name(s); but you can use this, for example," \
" to request notification of signals indicating useful events on the system."
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "AddMatch"
)
message.append_objects("s", format_rule(rule))
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
else :
reply.expect_return_objects("")
#end if
#end bus_add_match_async
def bus_remove_match(self, rule, error = None) :
"removes a previously-added match rule for messages you previously wanted" \
" to receive."
error, my_error = _get_error(error)
dbus.dbus_bus_remove_match(self._dbobj, format_rule(rule).encode(), error._dbobj)
my_error.raise_if_set()
#end bus_remove_match
async def bus_remove_match_async(self, rule, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"removes a previously-added match rule for messages you previously wanted" \
" to receive."
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_DBUS,
method = "RemoveMatch"
)
message.append_objects("s", format_rule(rule))
reply = await self.send_await_reply(message, timeout = timeout)
if error != None and reply.type == DBUS.MESSAGE_TYPE_ERROR :
reply.set_error(error)
else :
reply.expect_return_objects("")
#end if
#end bus_remove_match_async
@staticmethod
def _rule_action_match(self, message, _) :
# installed as a message filter to invoke actions corresponding to rules
# that the message matches. To avoid spurious method-not-handled errors
# from eavesdropping on method calls not addressed to me, this routine
# always returns a “handled” status. That means this same Connection
# object should not be used for both eavesdropping and for normal
# method calls.
handled = False
for entry in self._match_actions.values() :
if matches_rule(message, entry.rule) :
for action in entry.actions :
result = action.func(self, message, action.user_data)
if asyncio.iscoroutine(result) :
self.create_task(result)
#end if
#end for
handled = True # passed to at least one handler
#end if
#end for
return \
(DBUS.HANDLER_RESULT_NOT_YET_HANDLED, DBUS.HANDLER_RESULT_HANDLED)[handled]
#end _rule_action_match
def bus_add_match_action(self, rule, func, user_data, error = None) :
"adds a message filter that invokes func(conn, message, user_data)" \
" for each incoming message that matches the specified rule. Unlike" \
" the underlying add_filter and bus_add_match calls, this allows you" \
" to associate the action with the particular matching rule.\n" \
"\n" \
"Note that the message filter installed to process these rules always" \
" returns a DBUS.HANDLER_RESULT_HANDLED status; so either only use this" \
" to listen for signals, or do not use the same Connection object to" \
" handle normal method calls."
rulekey = format_rule(rule)
rule = unformat_rule(rule)
if rulekey not in self._match_actions :
self.bus_add_match(rulekey, error) # could fail here with bad rule
if error == None or not error.is_set :
if len(self._match_actions) == 0 :
self.add_filter(self._rule_action_match, None)
#end if
self._match_actions[rulekey] = _MatchActionEntry(rule)
#end if
#end if
if error == None or not error.is_set :
self._match_actions[rulekey].actions.add(_MatchActionEntry._Action(func, user_data))
#end if
#end bus_add_match_action
def bus_remove_match_action(self, rule, func, user_data, error = None) :
"removes a message filter previously installed with bus_add_match_action."
rulekey = format_rule(rule)
rule = unformat_rule(rule)
self._match_actions[rulekey].actions.remove(_MatchActionEntry._Action(func, user_data))
if len(self._match_actions[rulekey].actions) == 0 :
self.bus_remove_match(rulekey, error) # shouldnt fail!
del self._match_actions[rulekey]
if len(self._match_actions) == 0 :
self.remove_filter(self._rule_action_match, None)
#end if
#end if
#end bus_remove_match_action
async def bus_add_match_action_async(self, rule, func, user_data, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"adds a message filter that invokes func(conn, message, user_data)" \
" for each incoming message that matches the specified rule. Unlike" \
" the underlying add_filter and bus_add_match calls, this allows you" \
" to associate the action with the particular matching rule.\n" \
"\n" \
"Note that the message filter installed to process these rules always" \
" returns a DBUS.HANDLER_RESULT_HANDLED status; so either only use this" \
" to listen for signals, or do not use the same Connection object to" \
" handle normal method calls."
rulekey = format_rule(rule)
rule = unformat_rule(rule)
if rulekey not in self._match_actions :
await self.bus_add_match_async(rulekey, error, timeout) # could fail here with bad rule
if error == None or not error.is_set :
if len(self._match_actions) == 0 :
self.add_filter(self._rule_action_match, None)
#end if
self._match_actions[rulekey] = _MatchActionEntry(rule)
#end if
#end if
if error == None or not error.is_set :
self._match_actions[rulekey].actions.add(_MatchActionEntry._Action(func, user_data))
#end if
#end bus_add_match_action_async
async def bus_remove_match_action_async(self, rule, func, user_data, error = None, timeout = DBUS.TIMEOUT_USE_DEFAULT) :
"removes a message filter previously installed with bus_add_match_action."
rulekey = format_rule(rule)
rule = unformat_rule(rule)
self._match_actions[rulekey].actions.remove(_MatchActionEntry._Action(func, user_data))
if len(self._match_actions[rulekey].actions) == 0 :
await self.bus_remove_match_async(rulekey, error, timeout) # shouldnt fail!
del self._match_actions[rulekey]
if len(self._match_actions) == 0 :
self.remove_filter(self._rule_action_match, None)
#end if
#end if
#end bus_remove_match_action_async
def become_monitor(self, rules) :
"turns the connection into one that can only receive monitoring messages."
message = Message.new_method_call \
(
destination = DBUS.SERVICE_DBUS,
path = DBUS.PATH_DBUS,
iface = DBUS.INTERFACE_MONITORING,
method = "BecomeMonitor"
)
message.append_objects("asu", (list(format_rule(rule) for rule in rules)), 0)
self.send(message)
#end become_monitor
#+
# End calls to D-Bus Daemon
#-
def attach_asyncio(self, loop = None) :
"attaches this Connection object to an asyncio event loop. If none is" \
" specified, the default event loop (as returned from asyncio.get_event_loop()" \
" is used."
w_self = weak_ref(self)
# to avoid a reference cycle
def dispatch() :
return \
_wderef(w_self, "connection").dispatch()
#end dispatch
#begin attach_asyncio
assert self.loop == None, "already attached to an event loop"
_loop_attach(self, loop, dispatch)
#end attach_asyncio
#end Connection
class _MsgAiter :
# internal class for use by Connection.iter_messages_async (above).
def __init__(self, conn, want_types, stop_on, timeout) :
self.conn = conn
self.want_types = want_types
self.stop_on = stop_on
self.timeout = timeout
#end __init__
def __aiter__(self) :
# Im my own iterator.
return \
self
#end __aiter__
async def __anext__(self) :
stop_iter = False
try :
result = await self.conn.receive_message_async(self.want_types, self.timeout)
if result == None and STOP_ON.TIMEOUT in self.stop_on :
stop_iter = True
#end if
except BrokenPipeError :
if STOP_ON.CLOSED not in self.stop_on :
raise
#end if
stop_iter = True
#end try
if stop_iter :
raise StopAsyncIteration("Connection.receive_message_async terminating")
#end if
return \
result
#end __anext__
#end _MsgAiter
class Server(TaskKeeper) :
"wrapper around a DBusServer object. Do not instantiate directly; use" \
" the listen method.\n" \
"\n" \
"You only need this if you want to use D-Bus as a communication mechanism" \
" separate from the system/session buses provided by the D-Bus daemon: you" \
" create a Server object listening on a specified address, and clients can" \
" use Connection.open() to connect to you on that address."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusServer.html>
# Doesnt really need services of TaskKeeper for now, but might be
# useful in future
__slots__ = \
(
"_dbobj",
"_new_connections",
"_await_new_connections",
"max_new_connections",
"autoattach_new_connections",
# need to keep references to ctypes-wrapped functions
# so they don't disappear prematurely:
"_new_connection_function",
"_free_new_connection_data",
"_add_watch_function",
"_remove_watch_function",
"_toggled_watch_function",
"_free_watch_data",
"_add_timeout_function",
"_remove_timeout_function",
"_toggled_timeout_function",
"_free_timeout_data",
) # to forestall typos
_instances = WeakValueDictionary()
def __new__(celf, _dbobj) :
self = celf._instances.get(_dbobj)
if self == None :
self = super().__new__(celf)
super()._init(self)
self._dbobj = _dbobj
self._new_connections = None
self._await_new_connections = None
self.max_new_connections = None
self.autoattach_new_connections = True
self._new_connection_function = None
self._free_new_connection_data = None
self._add_watch_function = None
self._remove_watch_function = None
self._toggled_watch_function = None
self._free_watch_data = None
self._add_timeout_function = None
self._remove_timeout_function = None
self._toggled_timeout_function = None
self._free_timeout_data = None
celf._instances[_dbobj] = self
else :
dbus.dbus_server_unref(self._dbobj)
# lose extra reference created by caller
#end if
return \
self
#end __new__
def __del__(self) :
if self._dbobj != None :
if self.loop != None :
# remove via direct low-level libdbus calls
dbus.dbus_server_set_watch_functions(self._dbobj, None, None, None, None, None)
dbus.dbus_server_set_timeout_functions(self._dbobj, None, None, None, None, None)
self.loop = None
#end if
dbus.dbus_server_unref(self._dbobj)
self._dbobj = None
#end if
#end __del__
@classmethod
def listen(celf, address, error = None) :
error, my_error = _get_error(error)
result = dbus.dbus_server_listen(address.encode(), error._dbobj)
my_error.raise_if_set()
if result != None :
result = celf(result)
#end if
return \
result
#end listen
def _flush_awaiting_connect(self) :
if self._await_new_connections != None :
while len(self._await_new_connections) != 0 :
waiting = self._await_new_connections.pop(0)
waiting.set_exception(BrokenPipeError("async listens have been disabled"))
#end while
#end if
#end _flush_awaiting_connect
def disconnect(self) :
self._flush_awaiting_connect()
dbus.dbus_server_disconnect(self._dbobj)
#end disconnect
@property
def is_connected(self) :
return \
dbus.dbus_server_get_is_connected(self._dbobj) != 0
#end is_connected
@property
def address(self) :
c_result = dbus.dbus_server_get_address(self._dbobj)
if c_result == None :
raise CallFailed("dbus_server_get_address")
#end if
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end address
@property
def id(self) :
c_result = dbus.dbus_server_get_id(self._dbobj)
if c_result == None :
raise CallFailed("dbus_server_get_id")
#end if
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end id
def set_new_connection_function(self, function, data, free_data = None) :
"sets the callback for libdbus to notify you of a new incoming connection." \
" It is up to you to save the Connection object for later processing of" \
" messages, or close it to reject the connection attempt."
w_self = weak_ref(self)
def wrap_function(c_self, c_conn, _data) :
function(_wderef(w_self, "server"), Connection(dbus.dbus_connection_ref(c_conn)), data)
# even though this is a new connection, I still have to reference it
#end wrap_function
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_new_connection_function
assert self.loop == None, "new connections are being managed by an event loop"
self._new_connection_function = DBUS.NewConnectionFunction(wrap_function)
if free_data != None :
self._free_new_connection_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_new_connection_data = None
#end if
dbus.dbus_server_set_new_connection_function(self._dbobj, self._new_connection_function, None, self._free_new_connection_data)
#end set_new_connection_function
def set_watch_functions(self, add_function, remove_function, toggled_function, data, free_data = None) :
"sets the callbacks for libdbus to use to notify you of Watch objects it wants" \
" you to manage."
def wrap_add_function(c_watch, _data) :
return \
add_function(Watch(c_watch), data)
#end wrap_add_function
def wrap_remove_function(c_watch, _data) :
return \
remove_function(Watch(c_watch), data)
#end wrap_remove_function
def wrap_toggled_function(c_watch, _data) :
return \
toggled_function(Watch(c_watch), data)
#end wrap_toggled_function
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_watch_functions
self._add_watch_function = DBUS.AddWatchFunction(wrap_add_function)
self._remove_watch_function = DBUS.RemoveWatchFunction(wrap_remove_function)
if toggled_function != None :
self._toggled_watch_function = DBUS.WatchToggledFunction(wrap_toggled_function)
else :
self._toggled_watch_function = None
#end if
if free_data != None :
self._free_watch_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_watch_data = None
#end if
if not dbus.dbus_server_set_watch_functions(self._dbobj, self._add_watch_function, self._remove_watch_function, self._toggled_watch_function, None, self._free_watch_data) :
raise CallFailed("dbus_server_set_watch_functions")
#end if
#end set_watch_functions
def set_timeout_functions(self, add_function, remove_function, toggled_function, data, free_data = None) :
"sets the callbacks for libdbus to use to notify you of Timeout objects it wants" \
" you to manage."
def wrap_add_function(c_timeout, _data) :
return \
add_function(Timeout(c_timeout), data)
#end wrap_add_function
def wrap_remove_function(c_timeout, _data) :
return \
remove_function(Timeout(c_timeout), data)
#end wrap_remove_function
def wrap_toggled_function(c_timeout, _data) :
return \
toggled_function(Timeout(c_timeout), data)
#end wrap_toggled_function
def wrap_free_data(_data) :
free_data(data)
#end wrap_free_data
#begin set_timeout_functions
self._add_timeout_function = DBUS.AddTimeoutFunction(wrap_add_function)
self._remove_timeout_function = DBUS.RemoveTimeoutFunction(wrap_remove_function)
if toggled_function != None :
self._toggled_timeout_function = DBUS.TimeoutToggledFunction(wrap_toggled_function)
else :
self._toggled_timeout_function = None
#end if
if free_data != None :
self._free_timeout_data = DBUS.FreeFunction(wrap_free_data)
else :
self._free_timeout_data = None
#end if
if not dbus.dbus_server_set_timeout_functions(self._dbobj, self._add_timeout_function, self._remove_timeout_function, self._toggled_timeout_function, None, self._free_timeout_data) :
raise CallFailed("dbus_server_set_timeout_functions")
#end if
#end set_timeout_functions
def set_auth_mechanisms(self, mechanisms) :
nr_mechanisms = len(mechanisms)
c_mechanisms = (ct.c_char_p * (nr_mechanisms + 1))()
for i in range(nr_mechanisms) :
c_mechanisms[i] = mechanisms[i].encode()
#end if
c_mechanisms[nr_mechanisms] = None # marks end of array
if not dbus.dbus_server_set_auth_mechanisms(self._dbobj, c_mechanisms) :
raise CallFailed("dbus_server_set_auth_mechanisms")
#end if
#end set_auth_mechanisms
# TODO: allocate/free slot (static methods)
# TODO: get/set/data
def attach_asyncio(self, loop = None) :
"attaches this Server object to an asyncio event loop. If none is" \
" specified, the default event loop (as returned from asyncio.get_event_loop()" \
" is used.\n" \
"\n" \
"This call will also automatically attach a new_connection callback. You then use" \
" the await_new_connection coroutine to obtain new connections. If" \
" self.autoattach_new_connections, then Connection.attach_asyncio() will" \
" automatically be called to handle events for the new connection."
def new_connection(self, conn, user_data) :
if len(self._await_new_connections) != 0 :
awaiting = self._await_new_connections.pop(0)
awaiting.set_result(conn)
else :
# put it in _new_connections queue
if (
self.max_new_connections != None
and
len(self._new_connections) >= self.max_new_connections
) :
# too many connections pending, reject
conn.close()
else :
self._new_connections.append(conn)
#end if
#end if
#end new_connection
#begin attach_asyncio
assert self.loop == None, "already attached to an event loop"
assert self._new_connection_function == None, "already set a new-connection function"
self._new_connections = []
self._await_new_connections = []
self.set_new_connection_function(new_connection, None)
_loop_attach(self, loop, None)
#end attach_asyncio
async def await_new_connection(self, timeout = DBUS.TIMEOUT_INFINITE) :
"retrieves the next new Connection, if there is one available, otherwise" \
" suspends the current coroutine for up to the specified timeout duration" \
" while waiting for one to appear. Returns None if there is no new connection" \
" within that time."
assert self.loop != None, "no event loop to attach coroutine to"
if len(self._new_connections) != 0 :
result = self._new_connections.pop(0)
else :
if not self.is_connected :
raise BrokenPipeError("Server has been disconnected")
#end if
if timeout == 0 :
# might as well short-circuit the whole waiting process
result = None
else :
awaiting = self.loop.create_future()
self._await_new_connections.append(awaiting)
if timeout == DBUS.TIMEOUT_INFINITE :
timeout = None
else :
if timeout == DBUS.TIMEOUT_USE_DEFAULT :
timeout = DBUSX.DEFAULT_TIMEOUT
#end if
#end if
await self.wait \
(
(awaiting,),
timeout = timeout
)
# ignore done & pending results because they
# dont match up with future Im waiting for
if awaiting.done() :
result = awaiting.result()
else :
self._await_new_connections.pop(self._await_new_connections.index(awaiting))
result = None
#end if
#end if
#end if
if result != None and self.autoattach_new_connections :
result.attach_asyncio(self.loop)
#end if
return \
result
#end await_new_connection
def iter_connections_async(self, stop_on = None, timeout = DBUS.TIMEOUT_INFINITE) :
"wrapper around await_new_connection() to allow use with an async-for" \
" statement. Lets you write\n" \
"\n" \
" async for conn in «server».iter_connections_async(«timeout») :" \
" «accept conn»\n" \
" #end for\n" \
"\n" \
"to receive and process incoming connections in a loop. stop_on is an optional set of" \
" STOP_ON.xxx values indicating the conditions under which the iterator will" \
" raise StopAsyncIteration to terminate the loop."
assert self.loop != None, "no event loop to attach coroutine to"
if stop_on == None :
stop_on = frozenset()
elif (
not isinstance(stop_on, (set, frozenset))
or
not all(isinstance(elt, STOP_ON) for elt in stop_on)
) :
raise TypeError("stop_on must be None or set of STOP_ON")
#end if
return \
_SrvAiter(self, stop_on, timeout)
#end iter_connections_async
#end Server
class _SrvAiter :
# internal class for use by Server.iter_connections_async (above).
def __init__(self, srv, stop_on, timeout) :
self.srv = srv
self.stop_on = stop_on
self.timeout = timeout
#end __init__
def __aiter__(self) :
# Im my own iterator.
return \
self
#end __aiter__
async def __anext__(self) :
stop_iter = False
try :
result = await self.srv.await_new_connection(self.timeout)
if result == None and STOP_ON.TIMEOUT in self.stop_on :
stop_iter = True
#end if
except BrokenPipeError :
if STOP_ON.CLOSED not in self.stop_on :
raise
#end if
stop_iter = True
#end try
if stop_iter :
raise StopAsyncIteration("Server.iter_connections_async terminating")
#end if
return \
result
#end __anext__
#end _SrvAiter
class PreallocatedSend :
"wrapper around a DBusPreallocatedSend object. Do not instantiate directly;" \
" get from Connection.preallocate_send method."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusConnection.html>
__slots__ = ("__weakref__", "_dbobj", "_w_parent", "_sent") # to forestall typos
_instances = WeakValueDictionary()
def __new__(celf, _dbobj, _parent) :
self = celf._instances.get(_dbobj)
if self == None :
self = super().__new__(celf)
self._dbobj = _dbobj
self._w_parent = weak_ref(_parent)
self._sent = False
celf._instances[_dbobj] = self
else :
assert self._w_parent() == _parent
#end if
return \
self
#end __new__
def __del__(self) :
if self._dbobj != None :
parent = self._w_parent()
if parent != None and not self._sent :
dbus.dbus_connection_free_preallocated_send(parent._dbobj, self._dbobj)
#end if
self._dbobj = None
#end if
#end __del__
def send(self, message) :
"alternative to Connection.send_preallocated."
if not isinstance(message, Message) :
raise TypeError("message must be a Message")
#end if
assert not self._sent, "preallocated has already been sent"
parent = self._w_parent()
assert parent != None, "parent Connection has gone away"
serial = ct.c_uint()
dbus.dbus_connection_send_preallocated(parent._dbobj, self._dbobj, message._dbobj, ct.byref(serial))
self._sent = True
return \
serial.value
#end send
#end PreallocatedSend
class Message :
"wrapper around a DBusMessage object. Do not instantiate directly; use one of the" \
" new_xxx or copy methods, or Connection.pop_message or Connection.borrow_message."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusMessage.html>
__slots__ = ("__weakref__", "_dbobj", "_conn", "_borrowed") # to forestall typos
_instances = WeakValueDictionary()
def __new__(celf, _dbobj) :
self = celf._instances.get(_dbobj)
if self == None :
self = super().__new__(celf)
self._dbobj = _dbobj
self._conn = None
self._borrowed = False
celf._instances[_dbobj] = self
else :
dbus.dbus_message_unref(self._dbobj)
# lose extra reference created by caller
#end if
return \
self
#end __new__
def __del__(self) :
if self._dbobj != None :
assert not self._borrowed, "trying to dispose of borrowed message"
dbus.dbus_message_unref(self._dbobj)
self._dbobj = None
#end if
#end __del__
@classmethod
def new(celf, type) :
"type is one of the DBUS.MESSAGE_TYPE_xxx codes. Using one of the type-specific" \
" calls--new_error, new_method_call, new_method_return, new_signal--is probably" \
" more convenient."
result = dbus.dbus_message_new(type)
if result == None :
raise CallFailed("dbus_message_new")
#end if
return \
celf(result)
#end new
def new_error(self, name, message) :
"creates a new DBUS.MESSAGE_TYPE_ERROR message that is a reply to this Message."
result = dbus.dbus_message_new_error(self._dbobj, name.encode(), (lambda : None, lambda : message.encode())[message != None]())
if result == None :
raise CallFailed("dbus_message_new_error")
#end if
return \
type(self)(result)
#end new_error
# probably not much point trying to use new_error_printf
@classmethod
def new_method_call(celf, destination, path, iface, method) :
"creates a new DBUS.MESSAGE_TYPE_METHOD_CALL message."
result = dbus.dbus_message_new_method_call \
(
(lambda : None, lambda : destination.encode())[destination != None](),
path.encode(),
(lambda : None, lambda : iface.encode())[iface != None](),
method.encode(),
)
if result == None :
raise CallFailed("dbus_message_new_method_call")
#end if
return \
celf(result)
#end new_method_call
def new_method_return(self) :
"creates a new DBUS.MESSAGE_TYPE_METHOD_RETURN that is a reply to this Message."
result = dbus.dbus_message_new_method_return(self._dbobj)
if result == None :
raise CallFailed("dbus_message_new_method_return")
#end if
return \
type(self)(result)
#end new_method_return
@classmethod
def new_signal(celf, path, iface, name) :
"creates a new DBUS.MESSAGE_TYPE_SIGNAL message."
result = dbus.dbus_message_new_signal(path.encode(), iface.encode(), name.encode())
if result == None :
raise CallFailed("dbus_message_new_signal")
#end if
return \
celf(result)
#end new_signal
def copy(self) :
"creates a copy of this Message."
result = dbus.dbus_message_copy(self._dbobj)
if result == None :
raise CallFailed("dbus_message_copy")
#end if
return \
type(self)(result)
#end copy
@property
def type(self) :
"returns the DBUS.MESSAGE_TYPE_XXX code for this Message."
return \
dbus.dbus_message_get_type(self._dbobj)
#end type
# NYI append_args, get_args -- probably not useful, use my
# objects and append_objects convenience methods (below) instead
class ExtractIter :
"for iterating over the arguments in a Message for reading. Do not" \
" instantiate directly; get from Message.iter_init or ExtractIter.recurse.\n" \
"\n" \
"You can use this as a Python iterator, in a for-loop, passing" \
" it to the next() built-in function etc. Do not mix such usage with calls to" \
" the has_next() and next() methods."
__slots__ = ("_dbobj", "_parent", "_nulliter", "_startiter") # to forestall typos
def __init__(self, _parent) :
self._dbobj = DBUS.MessageIter()
self._parent = _parent
self._nulliter = False
self._startiter = True
#end __init__
@property
def has_next(self) :
return \
dbus.dbus_message_iter_has_next(self._dbobj)
#end has_next
def next(self) :
if self._nulliter or not dbus.dbus_message_iter_next(self._dbobj) :
raise StopIteration("end of message iterator")
#end if
self._startiter = False
return \
self
#end next
def __iter__(self) :
return \
self
#end __iter__
def __next__(self) :
if self._nulliter :
raise StopIteration("empty message iterator")
else :
if self._startiter :
self._startiter = False
else :
self.next()
#end if
#end if
return \
self
#end __next__
@property
def arg_type(self) :
"the type code for this argument."
return \
dbus.dbus_message_iter_get_arg_type(self._dbobj)
#end arg_type
@property
def element_type(self) :
"the contained element type of this argument, assuming it is of a container type."
return \
dbus.dbus_message_iter_get_element_type(self._dbobj)
#end element_type
def recurse(self) :
"creates a sub-iterator for recursing into a container argument."
subiter = type(self)(self)
dbus.dbus_message_iter_recurse(self._dbobj, subiter._dbobj)
return \
subiter
#end recurse
@property
def signature(self) :
c_result = dbus.dbus_message_iter_get_signature(self._dbobj)
if c_result == None :
raise CallFailed("dbus_message_iter_get_signature")
#end if
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end signature
@property
def basic(self) :
"returns the argument value, assuming it is of a non-container type."
argtype = self.arg_type
c_result_type = DBUS.basic_to_ctypes[argtype]
c_result = c_result_type()
dbus.dbus_message_iter_get_basic(self._dbobj, ct.byref(c_result))
if c_result_type == ct.c_char_p :
result = c_result.value.decode()
else :
result = c_result.value
#end if
if argtype in DBUS.basic_subclasses :
result = DBUS.basic_subclasses[argtype](result)
#end if
return \
result
#end basic
@property
def object(self) :
"returns the current iterator item as a Python object. Will recursively" \
" process container objects."
argtype = self.arg_type
if argtype in DBUS.basic_to_ctypes :
result = self.basic
elif argtype == DBUS.TYPE_ARRAY :
if self.element_type == DBUS.TYPE_DICT_ENTRY :
result = {}
subiter = self.recurse()
while True :
entry = next(subiter, None)
if entry == None or entry.arg_type == DBUS.TYPE_INVALID :
# TYPE_INVALID can be returned for an empty dict
break
if entry.arg_type != DBUS.TYPE_DICT_ENTRY :
raise RuntimeError("invalid dict entry type %d" % entry.arg_type)
#end if
key, value = tuple(x.object for x in entry.recurse())
result[key] = value
#end while
elif type_is_fixed_array_elttype(self.element_type) :
result = self.fixed_array
else :
result = list(x.object for x in self.recurse())
if len(result) != 0 and result[-1] == None :
# fudge for iterating into an empty array
result = result[:-1]
#end if
#end if
elif argtype == DBUS.TYPE_STRUCT :
result = list(x.object for x in self.recurse())
elif argtype == DBUS.TYPE_VARIANT :
subiter = self.recurse()
subiter = next(subiter)
result = (DBUS.Signature(subiter.signature), subiter.object)
elif argtype == DBUS.TYPE_INVALID :
# fudge for iterating into an empty array
result = None
else :
raise RuntimeError("unrecognized argtype %d" % argtype)
#end if
return \
result
#end object
if hasattr(dbus, "dbus_message_iter_get_element_count") :
@property
def element_count(self) :
"returns the count of contained elements, assuming the current argument" \
" is of a container type."
return \
dbus.dbus_message_iter_get_element_count(self._dbobj)
#end element_count
#end if
@property
def fixed_array(self) :
"returns the array elements, assuming the current argument is an array" \
" with a non-container element type."
c_element_type = DBUS.basic_to_ctypes[self.element_type]
c_result = ct.POINTER(c_element_type)()
c_nr_elts = ct.c_int()
subiter = self.recurse()
dbus.dbus_message_iter_get_fixed_array(subiter._dbobj, ct.byref(c_result), ct.byref(c_nr_elts))
result = []
for i in range(c_nr_elts.value) :
elt = c_result[i]
if c_element_type == ct.c_char_p :
elt = elt.value.decode()
#end if
result.append(elt)
#end for
return \
result
#end fixed_array
#end ExtractIter
class AppendIter :
"for iterating over the arguments in a Message for appending." \
" Do not instantiate directly; get from Message.iter_init_append or" \
" AppendIter.open_container."
__slots__ = ("_dbobj", "_parent") # to forestall typos
def __init__(self, _parent) :
self._dbobj = DBUS.MessageIter()
self._parent = _parent
#end __init__
def append_basic(self, type, value) :
"appends a single value of a non-container type."
if type in DBUS.int_convert :
value = DBUS.int_convert[type](value)
#end if
c_type = DBUS.basic_to_ctypes[type]
if c_type == ct.c_char_p :
if not isinstance(value, str) :
raise TypeError \
(
"expecting type %s, got %s" % (TYPE(type), builtins.type(value).__name__)
)
#end if
value = value.encode()
#end if
c_value = c_type(value)
if not dbus.dbus_message_iter_append_basic(self._dbobj, type, ct.byref(c_value)) :
raise CallFailed("dbus_message_iter_append_basic")
#end if
return \
self
#end append_basic
def append_fixed_array(self, element_type, values) :
"appends an array of elements of a non-container type."
c_elt_type = DBUS.basic_to_ctypes[element_type]
nr_elts = len(values)
c_arr = (nr_elts * c_elt_type)()
for i in range(nr_elts) :
if c_elt_type == ct.c_char_p :
c_arr[i] = values[i].encode()
else :
c_arr[i] = values[i]
#end if
#end for
c_arr_ptr = ct.pointer(c_arr)
if not dbus.dbus_message_iter_append_fixed_array(self._dbobj, element_type, ct.byref(c_arr_ptr), nr_elts) :
raise CallFailed("dbus_message_iter_append_fixed_array")
#end if
return \
self
#end append_fixed_array
def open_container(self, type, contained_signature) :
"starts appending an argument of a container type, returning a sub-iterator" \
" for appending the contents of the argument. Can be called recursively for" \
" containers of containers etc."
if contained_signature != None :
c_sig = contained_signature.encode()
else :
c_sig = None
#end if
subiter = builtins.type(self)(self)
if not dbus.dbus_message_iter_open_container(self._dbobj, type, c_sig, subiter._dbobj) :
raise CallFailed("dbus_message_iter_open_container")
#end if
return \
subiter
#end open_container
def close(self) :
"closes a sub-iterator, indicating the completion of construction" \
" of a container value."
assert self._parent != None, "cannot close top-level iterator"
if not dbus.dbus_message_iter_close_container(self._parent._dbobj, self._dbobj) :
raise CallFailed("dbus_message_iter_close_container")
#end if
return \
self._parent
#end close
def abandon(self) :
"closes a sub-iterator, indicating the abandonment of construction" \
" of a container value. The Message object is effectively unusable" \
" after this point and should be discarded."
assert self._parent != None, "cannot abandon top-level iterator"
dbus.dbus_message_iter_abandon_container(self._parent._dbobj, self._dbobj)
return \
self._parent
#end abandon
#end AppendIter
def iter_init(self) :
"creates an iterator for extracting the arguments of the Message."
iter = self.ExtractIter(None)
if dbus.dbus_message_iter_init(self._dbobj, iter._dbobj) == 0 :
iter._nulliter = True
#end if
return \
iter
#end iter_init
@property
def objects(self) :
"yields the arguments of the Message as Python objects."
for iter in self.iter_init() :
yield iter.object
#end for
#end objects
@property
def all_objects(self) :
"all the arguments of the Message as a list of Python objects."
return \
list(self.objects)
#end all_objects
def expect_objects(self, signature) :
"expects the arguments of the Message to conform to the given signature," \
" raising a TypeError if not. If they match, returns them as a list."
signature = unparse_signature(signature)
if self.signature != signature :
raise TypeError("message args dont match: expected “%s”, got “%s" % (signature, self.signature))
#end if
return \
self.all_objects
#end expect_objects
def expect_return_objects(self, signature) :
"expects the Message to be of type DBUS.MESSAGE_TYPE_METHOD_RETURN and its" \
" arguments to conform to the given signature. Raises the appropriate DBusError" \
" if the Message is of type DBUS.MESSAGE_TYPE_ERROR."
if self.type == DBUS.MESSAGE_TYPE_METHOD_RETURN :
result = self.expect_objects(signature)
elif self.type == DBUS.MESSAGE_TYPE_ERROR :
raise DBusError(self.error_name, self.expect_objects("s")[0])
else :
raise ValueError("unexpected message type %d" % self.type)
#end if
return \
result
#end expect_return_objects
def iter_init_append(self) :
"creates a Message.AppendIter for appending arguments to the Message."
iter = self.AppendIter(None)
dbus.dbus_message_iter_init_append(self._dbobj, iter._dbobj)
return \
iter
#end iter_init_append
def append_objects(self, signature, *args) :
"interprets Python values args according to signature and appends" \
" converted item(s) to the message args."
def append_sub(siglist, eltlist, appenditer) :
if len(siglist) != len(eltlist) :
raise ValueError \
(
"mismatch between signature entries %s and number of sequence elements %s"
%
(repr(siglist), repr(eltlist))
)
#end if
for elttype, elt in zip(siglist, eltlist) :
if isinstance(elttype, BasicType) :
appenditer.append_basic(elttype.code.value, elt)
elif isinstance(elttype, DictType) :
if not isinstance(elt, dict) :
raise TypeError("dict expected for %s" % repr(elttype))
#end if
subiter = appenditer.open_container(DBUS.TYPE_ARRAY, elttype.entry_signature)
for key in sorted(elt) : # might as well insert in some kind of predictable order
value = elt[key]
subsubiter = subiter.open_container(DBUS.TYPE_DICT_ENTRY, None)
append_sub([elttype.keytype, elttype.valuetype], [key, value], subsubiter)
subsubiter.close()
#end for
subiter.close()
elif isinstance(elttype, ArrayType) :
# append 0 or more elements matching elttype.elttype
arrelttype = elttype.elttype
if type_is_fixed_array_elttype(arrelttype.code.value) :
subiter = appenditer.open_container(DBUS.TYPE_ARRAY, arrelttype.signature)
subiter.append_fixed_array(arrelttype.code.value, elt)
subiter.close()
else :
subiter = appenditer.open_container(DBUS.TYPE_ARRAY, arrelttype.signature)
if not isinstance(elt, (tuple, list)) :
raise TypeError("expecting sequence of values for array")
#end if
for subval in elt :
append_sub([arrelttype], [subval], subiter)
#end for
subiter.close()
#end if
elif isinstance(elttype, StructType) :
if not isinstance(elt, (tuple, list)) :
raise TypeError("expecting sequence of values for struct")
#end if
subiter = appenditer.open_container(DBUS.TYPE_STRUCT, None)
append_sub(elttype.elttypes, elt, subiter)
subiter.close()
elif isinstance(elttype, VariantType) :
if not isinstance(elt, (list, tuple)) or len(elt) != 2 :
raise TypeError("sequence of 2 elements expected for variant: %s" % repr(elt))
#end if
actual_type = parse_single_signature(elt[0])
subiter = appenditer.open_container(DBUS.TYPE_VARIANT, actual_type.signature)
append_sub([actual_type], [elt[1]], subiter)
subiter.close()
else :
raise RuntimeError("unrecognized type %s" % repr(elttype))
#end if
#end for
#end append_sub
#begin append_objects
append_sub(parse_signature(signature), args, self.iter_init_append())
return \
self
#end append_objects
@property
def no_reply(self) :
"whether the Message is not expecting a reply."
return \
dbus.dbus_message_get_no_reply(self._dbobj) != 0
#end no_reply
@no_reply.setter
def no_reply(self, no_reply) :
dbus.dbus_message_set_no_reply(self._dbobj, no_reply)
#end no_reply
@property
def auto_start(self) :
return \
dbus.dbus_message_get_auto_start(self._dbobj) != 0
#end auto_start
@auto_start.setter
def auto_start(self, auto_start) :
dbus.dbus_message_set_auto_start(self._dbobj, auto_start)
#end auto_start
@property
def path(self) :
"the object path for a DBUS.MESSAGE_TYPE_METHOD_CALL or DBUS.DBUS.MESSAGE_TYPE_SIGNAL" \
" message."
result = dbus.dbus_message_get_path(self._dbobj)
if result != None :
result = DBUS.ObjectPath(result.decode())
#end if
return \
result
#end path
@path.setter
def path(self, object_path) :
if not dbus.dbus_message_set_path(self._dbobj, (lambda : None, lambda : object_path.encode())[object_path != None]()) :
raise CallFailed("dbus_message_set_path")
#end if
#end path
@property
def path_decomposed(self) :
"the object path for a DBUS.MESSAGE_TYPE_METHOD_CALL or DBUS.DBUS.MESSAGE_TYPE_SIGNAL" \
" message, decomposed into a list of the slash-separated components without the slashes."
path = ct.POINTER(ct.c_char_p)()
if not dbus.dbus_message_get_path_decomposed(self._dbobj, ct.byref(path)) :
raise CallFailed("dbus_message_get_path_decomposed")
#end if
if bool(path) :
result = []
i = 0
while True :
entry = path[i]
if entry == None :
break
result.append(entry.decode())
i += 1
#end while
dbus.dbus_free_string_array(path)
else :
result = None
#end if
return \
result
#end path_decomposed
@property
def interface(self) :
"the interface name for a DBUS.MESSAGE_TYPE_METHOD_CALL or DBUS.MESSAGE_TYPE_SIGNAL" \
" message."
result = dbus.dbus_message_get_interface(self._dbobj)
if result != None :
result = result.decode()
#end if
return \
result
#end interface
@interface.setter
def interface(self, iface) :
if not dbus.dbus_message_set_interface(self._dbobj, (lambda : None, lambda : iface.encode())[iface != None]()) :
raise CallFailed("dbus_message_set_interface")
#end if
#end interface
def has_interface(self, iface) :
return \
dbus.dbus_message_has_interface(self._dbobj, iface.encode()) != 0
#end has_interface
@property
def member(self) :
"the method name for a DBUS.MESSAGE_TYPE_METHOD_CALL message or the signal" \
" name for DBUS.MESSAGE_TYPE_SIGNAL."
result = dbus.dbus_message_get_member(self._dbobj)
if result != None :
result = result.decode()
#end if
return \
result
#end member
@member.setter
def member(self, member) :
if not dbus.dbus_message_set_member(self._dbobj, (lambda : None, lambda : member.encode())[member != None]()) :
raise CallFailed("dbus_message_set_member")
#end if
#end member
def has_member(self, member) :
return \
dbus.dbus_message_has_member(self._dbobj, member.encode()) != 0
#end has_member
@property
def error_name(self) :
"the error name for a DBUS.MESSAGE_TYPE_ERROR message."
result = dbus.dbus_message_get_error_name(self._dbobj)
if result != None :
result = result.decode()
#end if
return \
result
#end error_name
@error_name.setter
def error_name(self, error_name) :
if not dbus.dbus_message_set_error_name(self._dbobj, (lambda : None, lambda : error_name.encode())[error_name != None]()) :
raise CallFailed("dbus_message_set_error_name")
#end if
#end error_name
@property
def destination(self) :
"the bus name that the message is to be sent to."
result = dbus.dbus_message_get_destination(self._dbobj)
if result != None :
result = result.decode()
#end if
return \
result
#end destination
@destination.setter
def destination(self, destination) :
if not dbus.dbus_message_set_destination(self._dbobj, (lambda : None, lambda : destination.encode())[destination != None]()) :
raise CallFailed("dbus_message_set_destination")
#end if
#end destination
@property
def sender(self) :
result = dbus.dbus_message_get_sender(self._dbobj)
if result != None :
result = result.decode()
#end if
return \
result
#end sender
@sender.setter
def sender(self, sender) :
if not dbus.dbus_message_set_sender(self._dbobj, (lambda : None, lambda : sender.encode())[sender != None]()) :
raise CallFailed("dbus_message_set_sender")
#end if
#end sender
@property
def signature(self) :
result = dbus.dbus_message_get_signature(self._dbobj)
if result != None :
result = DBUS.Signature(result.decode())
#end if
return \
result
#end signature
def is_method_call(self, iface, method) :
return \
dbus.dbus_message_is_method_call(self._dbobj, iface.encode(), method.encode()) != 0
#end is_method_call
def is_signal(self, iface, signal_name) :
return \
dbus.dbus_message_is_signal(self._dbobj, iface.encode(), signal_name.encode()) != 0
#end is_signal
def is_error(self, iface, error_name) :
return \
dbus.dbus_message_is_error(self._dbobj, error_name.encode()) != 0
#end is_error
def has_destination(self, iface, destination) :
return \
dbus.dbus_message_has_destination(self._dbobj, destination.encode()) != 0
#end has_destination
def has_sender(self, iface, sender) :
return \
dbus.dbus_message_has_sender(self._dbobj, sender.encode()) != 0
#end has_sender
def has_signature(self, iface, signature) :
return \
dbus.dbus_message_has_signature(self._dbobj, signature.encode()) != 0
#end has_signature
def set_error(self, error) :
"fills in error if this is an error message, else does nothing. Returns" \
" whether it was an error message or not."
if not isinstance(error, Error) :
raise TypeError("error must be an Error")
#end if
return \
dbus.dbus_set_error_from_message(error._dbobj, self._dbobj) != 0
#end set_error
@property
def contains_unix_fds(self) :
return \
dbus.dbus_message_contains_unix_fds(self._dbobj) != 0
#end contains_unix_fds
@property
def serial(self) :
"the serial number of the Message, to be referenced in replies."
return \
dbus.dbus_message_get_serial(self._dbobj)
#end serial
@serial.setter
def serial(self, serial) :
dbus.dbus_message_set_serial(self._dbobj, serial)
#end serial
@property
def reply_serial(self) :
"the serial number of the original Message that that this" \
" DBUS.MESSAGE_TYPE_METHOD_RETURN message is a reply to."
return \
dbus.dbus_message_get_reply_serial(self._dbobj)
#end reply_serial
@reply_serial.setter
def reply_serial(self, serial) :
if not dbus.dbus_message_set_reply_serial(self._dbobj, serial) :
raise CallFailed("dbus_message_set_reply_serial")
#end if
#end serial
def lock(self) :
dbus.dbus_message_lock(self._dbobj)
#end lock
def return_borrowed(self) :
assert self._borrowed and self._conn != None
dbus.dbus_connection_return_message(self._conn._dbobj, self._dbobj)
self._borrowed = False
#end return_borrowed
def steal_borrowed(self) :
assert self._borrowed and self._conn != None
dbus.dbus_connection_steal_borrowed_message(self._conn._dbobj, self._dbobj)
self._borrowed = False
return \
self
#end steal_borrowed
# TODO: allocate/free data slot -- static methods
# (freeing slot can set passed-in var to -1 on actual free; do I care?)
# TODO: set/get data
@staticmethod
def type_from_string(type_str) :
"returns a MESSAGE_TYPE_xxx value."
return \
dbus.dbus_message_type_from_string(type_str.encode())
#end type_from_string
@staticmethod
def type_to_string(type) :
"type is a MESSAGE_TYPE_xxx value."
return \
dbus.dbus_message_type_to_string(type).decode()
#end type_to_string
def marshal(self) :
"serializes this Message into the wire protocol format and returns a bytes object."
buf = ct.POINTER(ct.c_ubyte)()
nr_bytes = ct.c_int()
if not dbus.dbus_message_marshal(self._dbobj, ct.byref(buf), ct.byref(nr_bytes)) :
raise CallFailed("dbus_message_marshal")
#end if
result = bytearray(nr_bytes.value)
ct.memmove \
(
ct.addressof((ct.c_ubyte * nr_bytes.value).from_buffer(result)),
buf,
nr_bytes.value
)
dbus.dbus_free(buf)
return \
result
#end marshal
@classmethod
def demarshal(celf, buf, error = None) :
"deserializes a bytes or array-of-bytes object from the wire protocol" \
" format into a Message object."
error, my_error = _get_error(error)
if isinstance(buf, bytes) :
baseadr = ct.cast(buf, ct.c_void_p).value
elif isinstance(buf, bytearray) :
baseadr = ct.addressof((ct.c_ubyte * len(buf)).from_buffer(buf))
elif isinstance(buf, array.array) and buf.typecode == "B" :
baseadr = buf.buffer_info()[0]
else :
raise TypeError("buf is not bytes, bytearray or array.array of bytes")
#end if
msg = dbus.dbus_message_demarshal(baseadr, len(buf), error._dbobj)
my_error.raise_if_set()
if msg != None :
msg = celf(msg)
#end if
return \
msg
#end demarshal
@classmethod
def demarshal_bytes_needed(celf, buf) :
"the number of bytes needed to deserialize a bytes or array-of-bytes" \
" object from the wire protocol format."
if isinstance(buf, bytes) :
baseadr = ct.cast(buf, ct.c_void_p).value
elif isinstance(buf, bytearray) :
baseadr = ct.addressof((ct.c_ubyte * len(buf)).from_buffer(buf))
elif isinstance(buf, array.array) and buf.typecode == "B" :
baseadr = buf.buffer_info()[0]
else :
raise TypeError("buf is not bytes, bytearray or array.array of bytes")
#end if
return \
dbus.dbus_message_demarshal_bytes_needed(baseadr, len(buf))
#end demarshal_bytes_needed
@property
def interactive_authorization(self) :
return \
dbus.dbus_message_get_interactive_authorization(self._dbobj)
#end interactive_authorization
@interactive_authorization.setter
def interactive_authorization(self, allow) :
dbus.dbus_message_set_interactive_authorization(self._dbobj, allow)
#end interactive_authorization
#end Message
class PendingCall :
"wrapper around a DBusPendingCall object. This represents a pending reply" \
" message that hasnt been received yet. Do not instantiate directly; libdbus" \
" creates these as the result from calling send_with_reply() on a Message."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusPendingCall.html>
__slots__ = \
(
"__weakref__",
"_dbobj",
"_w_conn",
"_wrap_notify",
"_wrap_free",
"_awaiting",
) # to forestall typos
_instances = WeakValueDictionary()
def __new__(celf, _dbobj, _conn) :
self = celf._instances.get(_dbobj)
if self == None :
self = super().__new__(celf)
self._dbobj = _dbobj
self._w_conn = weak_ref(_conn)
self._wrap_notify = None
self._wrap_free = None
self._awaiting = None
celf._instances[_dbobj] = self
else :
dbus.dbus_pending_call_unref(self._dbobj)
# lose extra reference created by caller
#end if
return \
self
#end __new__
def __del__(self) :
if self._dbobj != None :
dbus.dbus_pending_call_unref(self._dbobj)
self._dbobj = None
#end if
#end __del__
def set_notify(self, function, user_data, free_user_data = None) :
"sets the callback for libdbus to notify you that the pending message" \
" has become available. Note: it appears to be possible for your notifier" \
" to be called spuriously before the message is actually available."
w_self = weak_ref(self)
def wrap_notify(c_pending, c_user_data) :
function(_wderef(w_self, "pending call"), user_data)
#end _wrap_notify
def wrap_free(c_user_data) :
free_user_data(user_data)
#end _wrap_free
#begin set_notify
if function != None :
self._wrap_notify = DBUS.PendingCallNotifyFunction(wrap_notify)
else :
self._wrap_notify = None
#end if
if free_user_data != None :
self._wrap_free = DBUS.FreeFunction(wrap_free)
else :
self._wrap_free = None
#end if
if not dbus.dbus_pending_call_set_notify(self._dbobj, self._wrap_notify, None, self._wrap_free) :
raise CallFailed("dbus_pending_call_set_notify")
#end if
#end set_notify
def cancel(self) :
"tells libdbus you no longer care about the pending incoming message."
dbus.dbus_pending_call_cancel(self._dbobj)
if self._awaiting != None :
# This probably shouldnt occur. Looking at the source of libdbus,
# it doesnt keep track of any “cancelled” state for the PendingCall,
# it just detaches it from any notifications about an incoming reply.
self._awaiting.cancel()
#end if
#end cancel
@property
def completed(self) :
"checks whether the pending message is available."
return \
dbus.dbus_pending_call_get_completed(self._dbobj) != 0
#end completed
def steal_reply(self) :
"retrieves the Message, assuming it is actually available." \
" You should check PendingCall.completed returns True first."
result = dbus.dbus_pending_call_steal_reply(self._dbobj)
if result != None :
result = Message(result)
#end if
return \
result
#end steal_reply
async def await_reply(self) :
"retrieves the Message. If it is not yet available, suspends the" \
" coroutine (letting the event loop do other things) until it becomes" \
" available. On a timeout, libdbus will construct and return an error" \
" return message."
conn = self._w_conn()
assert conn != None, "parent Connection has gone away"
assert conn.loop != None, "no event loop on parent Connection to attach coroutine to"
if self._wrap_notify != None or self._awaiting != None :
raise asyncio.InvalidStateError("there is already a notify set on this PendingCall")
#end if
done = conn.loop.create_future()
self._awaiting = done
def pending_done(pending, wself) :
if not done.done() : # just in case of self.cancel() being called
self = wself()
# Note it seems to be possible for callback to be triggered spuriously
if self != None and self.completed :
done.set_result(self.steal_reply())
#end if
#end if
#end pending_done
self.set_notify(pending_done, weak_ref(self))
# avoid reference circularity self → pending_done → self
reply = await done
return \
reply
#end await_reply
def block(self) :
"blocks the current thread until the pending message has become available."
dbus.dbus_pending_call_block(self._dbobj)
#end block
# TODO: data slots (static methods), get/set data
#end PendingCall
class Error :
"wrapper around a DBusError object. You can create one by calling the init method."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusErrors.html>
__slots__ = ("_dbobj",) # to forestall typos
def __init__(self) :
dbobj = DBUS.Error()
dbus.dbus_error_init(dbobj)
self._dbobj = dbobj
#end __init__
def __del__(self) :
if self._dbobj != None :
dbus.dbus_error_free(self._dbobj)
self._dbobj = None
#end if
#end __del__
@classmethod
def init(celf) :
"for consistency with other classes that dont want caller to instantiate directly."
return \
celf()
#end init
def set(self, name, msg) :
"fills in the error name and message."
dbus.dbus_set_error(self._dbobj, name.encode(), b"%s", msg.encode())
#end set
@property
def is_set(self) :
"has the Error been filled in."
return \
dbus.dbus_error_is_set(self._dbobj) != 0
#end is_set
def has_name(self, name) :
"has the Error got the specified name."
return \
dbus.dbus_error_has_name(self._dbobj, name.encode()) != 0
#end has_name
@property
def name(self) :
"the name of the Error, if it has been filled in."
return \
(lambda : None, lambda : self._dbobj.name.decode())[self._dbobj.name != None]()
#end name
@property
def message(self) :
"the message string for the Error, if it has been filled in."
return \
(lambda : None, lambda : self._dbobj.message.decode())[self._dbobj.message != None]()
#end message
def raise_if_set(self) :
"raises a DBusError exception if this Error has been filled in."
if self.is_set :
raise DBusError(self.name, self.message)
#end if
#end raise_if_set
def set_from_message(self, message) :
"fills in this Error object from message if it is an error message." \
" Returns whether it was or not."
if not isinstance(message, Message) :
raise TypeError("message must be a Message")
#end if
return \
dbus.dbus_set_error_from_message(self._dbobj, message._dbobj) != 0
#end set_from_message
#end Error
class AddressEntries :
"wrapper for arrays of DBusAddressEntry values. Do not instantiate directly;" \
" get from AddressEntries.parse. This object behaves like an array; you can obtain" \
" the number of elements with len(), and use array subscripting to access the elements."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusAddress.html>
__slots__ = ("__weakref__", "_dbobj", "_nrelts") # to forestall typos
def __init__(self, _dbobj, _nrelts) :
self._dbobj = _dbobj
self._nrelts = _nrelts
#end __init__
def __del__(self) :
if self._dbobj != None :
dbus.dbus_address_entries_free(self._dbobj)
self._dbobj = None
#end if
#end __del__
class Entry :
"a single AddressEntry. Do not instantiate directly; get from AddressEntries[]." \
" This object behaves like a dictionary in that you can use keys to get values;" \
" however, there is no libdbus API to check what keys are present; unrecognized" \
" keys return a value of None."
__slots__ = ("_dbobj", "_parent", "_index") # to forestall typos
def __init__(self, _parent, _index) :
self._dbobj = _parent._dbobj
self._parent = weak_ref(_parent)
self._index = _index
#end __init__
@property
def method(self) :
assert self._parent() != None, "AddressEntries object has gone"
result = dbus.dbus_address_entry_get_method(self._dbobj[self._index])
if result != None :
result = result.decode()
#end if
return \
result
#end method
def get_value(self, key) :
assert self._parent() != None, "AddressEntries object has gone"
c_result = dbus.dbus_address_entry_get_value(self._dbobj[self._index], key.encode())
if c_result != None :
result = c_result.decode()
else :
result = None
#end if
return \
result
#end get_value
__getitem__ = get_value
#end Entry
@classmethod
def parse(celf, address, error = None) :
error, my_error = _get_error(error)
c_result = ct.POINTER(ct.c_void_p)()
nr_elts = ct.c_int()
if not dbus.dbus_parse_address(address.encode(), ct.byref(c_result), ct.byref(nr_elts), error._dbobj) :
c_result.contents = None
nr_elts.value = 0
#end if
my_error.raise_if_set()
if c_result.contents != None :
result = celf(c_result, nr_elts.value)
else :
result = None
#end if
return \
result
#end parse
def __len__(self) :
return \
self._nrelts
#end __len__
def __getitem__(self, index) :
if not isinstance(index, int) or index < 0 or index >= self._nrelts :
raise IndexError("AddressEntries[%d] out of range" % index)
#end if
return \
type(self).Entry(self, index)
#end __getitem__
#end AddressEntries
def address_escape_value(value) :
c_result = dbus.dbus_address_escape_value(value.encode())
if c_result == None :
raise CallFailed("dbus_address_escape_value")
#end if
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end address_escape_value
def address_unescape_value(value, error = None) :
error, my_error = _get_error(error)
c_result = dbus.dbus_address_unescape_value(value.encode(), error._dbobj)
my_error.raise_if_set()
if c_result != None :
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
elif not error.is_set :
raise CallFailed("dbus_address_unescape_value")
else :
result = None
#end if
return \
result
#end address_unescape_value
def format_rule(rule) :
"convenience routine to allow a match rule to be expressed as either" \
" a dict of {key : value} or the usual string \"key='value'\", automatically" \
" converting the former to the latter."
def escape_val(val) :
if "," in val :
if "'" in val :
out = "'"
in_quotes = True
for ch in val :
if ch == "'" :
if in_quotes :
out += "'"
in_quotes = False
#end if
out += "\\'"
else :
if not in_quotes :
out += "'"
in_quotes = True
#end if
out += ch
#end if
#end for
if in_quotes :
out += "'"
#end if
else :
out = "'" + val + "'"
#end if
else :
out = ""
for ch in val :
if ch in ("\\", "'") :
out += "\\"
#end if
out += ch
#end for
#end if
return \
out
#end escape_val
#begin format_rule
if isinstance(rule, str) :
pass
elif isinstance(rule, dict) :
rule = ",".join("%s=%s" % (k, escape_val(rule[k])) for k in sorted(rule))
# sort to ensure some kind of consistent ordering, just for
# appearances sake
else :
raise TypeError("rule “%s” must be a dict or string" % repr(rule))
#end if
return \
rule
#end format_rule
class _RuleParser :
# internal definitions for rule parsing.
class PARSE(enum.Enum) :
EXPECT_NAME = 1
EXPECT_UNQUOTED_VALUE = 2
EXPECT_ESCAPED = 3
EXPECT_QUOTED_VALUE = 4
#end PARSE
@classmethod
def unformat_rule(celf, rule) :
"converts a match rule string from the standard syntax to a dict of {key : value} entries."
if isinstance(rule, dict) :
pass
elif isinstance(rule, str) :
PARSE = celf.PARSE
parsed = {}
chars = iter(rule)
state = PARSE.EXPECT_NAME
curname = None
curval = None
while True :
ch = next(chars, None)
if ch == None :
if state == PARSE.EXPECT_ESCAPED :
raise SyntaxError("missing character after backslash")
elif state == PARSE.EXPECT_QUOTED_VALUE :
raise SyntaxError("missing closing apostrophe")
else : # state in (PARSE.EXPECT_NAME, PARSE.EXPECT_UNQUOTED_VALUE)
if curname != None :
if curval != None :
if curname in parsed :
raise SyntaxError("duplicated attribute “%s" % curname)
#end if
parsed[curname] = curval
else :
raise SyntaxError("missing value for attribute “%s" % curname)
#end if
#end if
#end if
break
#end if
if state == PARSE.EXPECT_ESCAPED :
if ch == "'" :
usech = ch
nextch = None
else :
usech = "\\"
nextch = ch
#end if
ch = usech
if curval == None :
curval = ch
else :
curval += ch
#end if
ch = nextch # None indicates already processed
state = PARSE.EXPECT_UNQUOTED_VALUE
#end if
if ch != None :
if ch == "," and state != PARSE.EXPECT_QUOTED_VALUE :
if state == PARSE.EXPECT_UNQUOTED_VALUE :
if curname in parsed :
raise SyntaxError("duplicated attribute “%s" % curname)
#end if
if curval == None :
curval = ""
#end if
parsed[curname] = curval
curname = None
curval = None
state = PARSE.EXPECT_NAME
else :
raise SyntaxError("unexpected comma")
#end if
elif ch == "\\" and state != PARSE.EXPECT_QUOTED_VALUE :
if state == PARSE.EXPECT_UNQUOTED_VALUE :
state = PARSE.EXPECT_ESCAPED
else :
raise SyntaxError("unexpected backslash")
#end if
elif ch == "=" and state != PARSE.EXPECT_QUOTED_VALUE :
if curname == None :
raise SyntaxError("empty attribute name")
#end if
if state == PARSE.EXPECT_NAME :
state = PARSE.EXPECT_UNQUOTED_VALUE
else :
raise SyntaxError("unexpected equals sign")
#end if
elif ch == "'" :
if state == PARSE.EXPECT_UNQUOTED_VALUE :
state = PARSE.EXPECT_QUOTED_VALUE
elif state == PARSE.EXPECT_QUOTED_VALUE :
state = PARSE.EXPECT_UNQUOTED_VALUE
else :
raise SyntaxError("unexpected apostrophe")
#end if
else :
if state == PARSE.EXPECT_NAME :
if curname == None :
curname = ch
else :
curname += ch
#end if
elif state in (PARSE.EXPECT_QUOTED_VALUE, PARSE.EXPECT_UNQUOTED_VALUE) :
if curval == None :
curval = ch
else :
curval += ch
#end if
else :
raise AssertionError("shouldnt occur: parse state %s" % repr(state))
#end if
#end if
#end if
#end while
rule = parsed
else :
raise TypeError("rule “%s” must be a dict or string" % repr(rule))
#end if
return \
rule
#end unformat_rule
#end _RuleParser
unformat_rule = _RuleParser.unformat_rule
del _RuleParser
def matches_rule(message, rule, destinations = None) :
"does Message message match against the specified rule."
if not isinstance(message, Message) :
raise TypeError("message must be a Message")
#end if
rule = unformat_rule(rule)
eavesdrop = rule.get("eavesdrop", "false") == "true"
def match_message_type(expect, actual) :
return \
actual == Message.type_from_string(expect)
#end match_message_type
def match_path_namespace(expect, actual) :
return \
(
actual != None
and
(
expect == actual
or
actual.startswith(expect) and (expect == "/" or actual[len(expect)] == "/")
)
)
#end match_path_namespace
def match_dotted_namespace(expect, actual) :
return \
(
actual != None
and
(
expect == actual
or
actual.startswith(expect) and actual[len(expect)] == "."
)
)
#end match_dotted_namespace
def get_nth_arg(msg, n, expect_types) :
msg_signature = parse_signature(msg.signature)
if n >= len(msg_signature) :
raise IndexError("arg nr %d beyond nr args %d" % (n, len(msg_signature)))
#end if
val = msg.all_objects[n]
valtype = msg_signature[n]
if valtype not in expect_types :
if False :
raise TypeError \
(
"expecting one of types %s, not %s for arg %d val %s"
%
((repr(expect_types), repr(valtype), n, repr(val)))
)
#end if
val = None # never match
#end if
return \
val
#end get_nth_arg
def get_arg_0_str(message) :
return \
get_nth_arg(message, 0, [BasicType(TYPE.STRING)])
#end get_arg_0_str
def match_arg_paths(expect, actual) :
return \
(
actual != None
and
(
expect == actual
or
expect.endswith("/") and actual.startswith(expect)
or
actual.endswith("/") and expect.startswith(actual)
)
)
#end match_arg_paths
match_types = \
( # note that message attribute value of None will fail to match
# any expected string value, which is exactly what we want
("type", None, match_message_type, None),
("sender", None, operator.eq, None),
("interface", None, operator.eq, None),
("member", None, operator.eq, None),
("path", None, operator.eq, None),
("destination", None, operator.eq, None),
("path_namespace", "path", match_path_namespace, None),
("arg0namespace", None, match_dotted_namespace, get_arg_0_str),
# “arg«n»path” handled specially below
)
#begin matches_rule
keys_used = set(rule.keys()) - {"eavesdrop"}
matches = \
(
eavesdrop
or
destinations == None
or
message.destination == None
or
message.destination in destinations
)
if matches :
try_matching = iter(match_types)
while True :
try_rule = next(try_matching, None)
if try_rule == None :
break
rulekey, attrname, action, accessor = try_rule
if attrname == None :
attrname = rulekey
#end if
if rulekey in rule :
if accessor != None :
val = accessor(message)
else :
val = getattr(message, attrname)
#end if
keys_used.remove(rulekey)
if not action(rule[rulekey], val) :
matches = False
break
#end if
#end if
#end while
#end if
if matches :
try_matching = iter(rule.keys())
while True :
try_key = next(try_matching, None)
if try_key == None :
break
if try_key.startswith("arg") and not try_key.endswith("namespace") :
argnr = try_key[3:]
is_path = argnr.endswith("path")
if is_path :
argnr = argnr[:-4]
#end if
argnr = int(argnr)
if not (0 <= argnr < 64) :
raise ValueError("argnr %d out of range" % argnr)
#end if
argval = get_nth_arg \
(
message,
argnr,
[BasicType(TYPE.STRING)] + ([], [BasicType(TYPE.OBJECT_PATH)])[is_path]
)
keys_used.remove(try_key)
if not (operator.eq, match_arg_paths)[is_path](rule[try_key], argval) :
matches = False
break
#end if
#end if
#end while
#end if
if matches and len(keys_used) != 0 :
# fixme: not checking for unrecognized rule keys if I didnt try matching them all
raise KeyError("unrecognized rule keywords: %s" % ", ".join(sorted(keys_used)))
#end if
return \
matches
#end matches_rule
class SignatureIter :
"wraps a DBusSignatureIter object. Do not instantiate directly; use the init" \
" and recurse methods."
# <https://dbus.freedesktop.org/doc/api/html/group__DBusSignature.html>
__slots__ = ("_dbobj", "_signature", "_startiter") # to forestall typos
@classmethod
def init(celf, signature) :
self = celf()
self._signature = ct.c_char_p(signature.encode()) # need to ensure storage stays valid
dbus.dbus_signature_iter_init(self._dbobj, self._signature)
return \
self
#end init
def __init__(self) :
self._dbobj = DBUS.SignatureIter()
self._signature = None # caller will set as necessary
self._startiter = True
#end __init__
def __iter__(self) :
return \
self
#end __iter__
def __next__(self) :
if self._startiter :
self._startiter = False
else :
self.next()
#end if
return \
self
#end __next__
def next(self) :
if dbus.dbus_signature_iter_next(self._dbobj) == 0 :
raise StopIteration("end of signature iterator")
#end if
self._startiter = False
return \
self
#end next
def recurse(self) :
subiter = type(self)()
dbus.dbus_signature_iter_recurse(self._dbobj, subiter._dbobj)
return \
subiter
#end recurse
@property
def current_type(self) :
return \
dbus.dbus_signature_iter_get_current_type(self._dbobj)
#end current_type
@property
def signature(self) :
c_result = dbus.dbus_signature_iter_get_signature(self._dbobj)
result = ct.cast(c_result, ct.c_char_p).value.decode()
dbus.dbus_free(c_result)
return \
result
#end signature
@property
def parsed_signature(self) :
return \
parse_single_signature(self.signature)
#end parsed_signature
@property
def element_type(self) :
return \
dbus.dbus_signature_iter_get_element_type(self._dbobj)
#end element_type
#end SignatureIter
def signature_validate(signature, error = None) :
"is signature a valid sequence of zero or more complete types."
error, my_error = _get_error(error)
result = dbus.dbus_signature_validate(signature.encode(), error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end signature_validate
def parse_signature(signature) :
"convenience routine for parsing a signature string into a list of Type()" \
" instances."
def process_subsig(sigelt) :
elttype = sigelt.current_type
if elttype in DBUS.basic_to_ctypes :
result = BasicType(TYPE(elttype))
elif elttype == DBUS.TYPE_ARRAY :
if sigelt.element_type == DBUS.TYPE_DICT_ENTRY :
subsig = sigelt.recurse()
subsubsig = subsig.recurse()
keytype = process_subsig(next(subsubsig))
valuetype = process_subsig(next(subsubsig))
result = DictType(keytype, valuetype)
else :
subsig = sigelt.recurse()
result = ArrayType(process_subsig(next(subsig)))
#end if
elif elttype == DBUS.TYPE_STRUCT :
result = []
subsig = sigelt.recurse()
for subelt in subsig :
result.append(process_subsig(subelt))
#end for
result = StructType(*result)
elif elttype == DBUS.TYPE_VARIANT :
result = VariantType()
else :
raise RuntimeError("unrecognized type %s" % bytes((elttype,)))
#end if
return \
result
#end process_subsig
#begin parse_signature
if isinstance(signature, (tuple, list)) :
if not all(isinstance(t, Type) for t in signature) :
raise TypeError("signature is list containing non-Type objects")
#end if
result = signature
elif isinstance(signature, Type) :
result = [signature]
elif isinstance(signature, str) :
signature_validate(signature)
result = []
if len(signature) != 0 :
sigiter = SignatureIter.init(signature)
for elt in sigiter :
result.append(process_subsig(elt))
#end for
#end if
else :
raise TypeError("signature must be list or str")
#end if
return \
result
#end parse_signature
def parse_single_signature(signature) :
result = parse_signature(signature)
if len(result) != 1 :
raise ValueError("only single type expected")
#end if
return \
result[0]
#end parse_single_signature
def unparse_signature(signature) :
"converts a signature from parsed form to string form."
signature = parse_signature(signature)
if not isinstance(signature, (tuple, list)) :
signature = [signature]
#end if
return \
DBUS.Signature("".join(t.signature for t in signature))
#end unparse_signature
def signature_validate_single(signature, error = None) :
"is signature a single valid type."
error, my_error = _get_error(error)
result = dbus.dbus_signature_validate_single(signature.encode(), error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end signature_validate_single
def type_is_valid(typecode) :
return \
dbus.dbus_type_is_valid(typecode) != 0
#end type_is_valid
def type_is_basic(typecode) :
return \
dbus.dbus_type_is_basic(typecode) != 0
#end type_is_basic
def type_is_container(typecode) :
return \
dbus.dbus_type_is_container(typecode) != 0
#end type_is_container
def type_is_fixed(typecode) :
return \
dbus.dbus_type_is_fixed(typecode) != 0
#end type_is_fixed
def type_is_fixed_array_elttype(typecode) :
"is typecode suitable as the element type of a fixed_array."
return \
type_is_fixed(typecode) and typecode != DBUS.TYPE_UNIX_FD
#end type_is_fixed_array_elttype
# syntax validation <https://dbus.freedesktop.org/doc/api/html/group__DBusSyntax.html>
def validate_path(path, error = None) :
error, my_error = _get_error(error)
result = dbus.dbus_validate_path(path.encode(), error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end validate_path
def valid_path(path) :
"returns path if valid, raising appropriate exception if not."
validate_path(path)
return \
path
#end valid_path
def split_path(path) :
"convenience routine for splitting a path into a list of components."
if isinstance(path, (tuple, list)) :
result = path # assume already split
elif path == "/" :
result = []
else :
if not path.startswith("/") or path.endswith("/") :
raise DBusError(DBUS.ERROR_INVALID_ARGS, "invalid path %s" % repr(path))
#end if
result = path.split("/")[1:]
#end if
return \
result
#end split_path
def unsplit_path(path) :
path = split_path(path)
if len(path) != 0 :
result = DBUS.ObjectPath("".join("/" + component for component in path))
else :
result = "/"
#end if
return \
result
#end unsplit_path
def validate_interface(name, error = None) :
error, my_error = _get_error(error)
result = dbus.dbus_validate_interface(name.encode(), error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end validate_interface
def valid_interface(name) :
"returns name if it is a valid interface name, raising appropriate exception if not."
validate_interface(name)
return \
name
#end valid_interface
def validate_member(name, error = None) :
error, my_error = _get_error(error)
result = dbus.dbus_validate_member(name.encode(), error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end validate_member
def valid_member(name) :
"returns name if it is a valid member name, raising appropriate exception if not."
validate_member(name)
return \
name
#end valid_member
def validate_error_name(name, error = None) :
error, my_error = _get_error(error)
result = dbus.dbus_validate_error_name(name.encode(), error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end validate_error_name
def valid_error_name(name) :
"returns name if it is a valid error name, raising appropriate exception if not."
validate_error_name(name)
return \
name
#end valid_error_name
def validate_bus_name(name, error = None) :
error, my_error = _get_error(error)
result = dbus.dbus_validate_bus_name(name.encode(), error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end validate_bus_name
def valid_bus_name(name) :
"returns name if it is a valid bus name, raising appropriate exception if not."
validate_bus_name(name)
return \
name
#end valid_bus_name
def validate_utf8(alleged_utf8, error = None) :
"alleged_utf8 must be null-terminated bytes."
error, my_error = _get_error(error)
result = dbus.dbus_validate_utf8(alleged_utf8, error._dbobj) != 0
my_error.raise_if_set()
return \
result
#end validate_utf8
def valid_utf8(alleged_utf8) :
"returns alleged_utf8 if it is a valid utf-8 bytes value, raising" \
" appropriate exception if not."
validate_utf8(alleged_utf8)
return \
alleged_utf8
#end valid_utf8
#+
# Introspection representation
#-
class _TagCommon :
def get_annotation(self, name) :
"returns the value of the annotation with the specified name, or None" \
" if none could be found"
annots = iter(self.annotations)
while True :
annot = next(annots, None)
if annot == None :
result = None
break
#end if
if annot.name == name :
result = annot.value
break
#end if
#end while
return \
result
#end get_annotation
@property
def is_deprecated(self) :
"is this interface/method/signal etc deprecated."
return \
self.get_annotation("org.freedesktop.DBus.Deprecated") == "true"
#end is_deprecated
def __repr__(self) :
celf = type(self)
return \
(
"%s(%s)"
%
(
celf.__name__,
", ".join
(
"%s = %s"
%
(name, repr(getattr(self, name)))
for name in celf.__slots__
),
)
)
#end __repr__
#end _TagCommon
class Introspection(_TagCommon) :
"high-level wrapper for the DBUS.INTERFACE_INTROSPECTABLE interface."
__slots__ = ("name", "interfaces", "nodes", "annotations")
tag_name = "node"
tag_attrs = ("name",)
tag_attrs_optional = {"name"}
class DIRECTION(enum.Enum) :
"argument direction."
IN = "in" # client to server
OUT = "out" # server to client
#end DIRECTION
class ACCESS(enum.Enum) :
"property access."
READ = "read"
WRITE = "write"
READWRITE = "readwrite"
#end ACCESS
class PROP_CHANGE_NOTIFICATION(enum.Enum) :
"how/if a changed property emits a notification signal."
NEW_VALUE = "true" # notification includes new value
INVALIDATES = "invalidates" # notification does not include new value
CONST = "const" # property shouldnt change
NONE = "false" # does not notify changes
#end PROP_CHANGE_NOTIFICATION
class Annotation(_TagCommon) :
__slots__ = ("name", "value")
tag_name = "annotation"
tag_attrs = ("name", "value")
tag_elts = {}
def __init__(self, name, value) :
self.name = name
self.value = value
#end __init__
#end Annotation
def _get_annotations(annotations) :
# common validation of annotations arguments.
if not all(isinstance(a, Introspection.Annotation) for a in annotations) :
raise TypeError("annotations must be Annotation instances")
#end if
return \
annotations
#end _get_annotations
class Interface(_TagCommon) :
__slots__ = ("name", "methods", "signals", "properties", "annotations")
tag_name = "interface"
tag_attrs = ("name",)
class Method(_TagCommon) :
__slots__ = ("name", "args", "annotations")
tag_name = "method"
tag_attrs = ("name",)
class Arg(_TagCommon) :
__slots__ = ("name", "type", "direction", "annotations")
tag_name = "arg"
tag_attrs = ("name", "type", "direction")
tag_attrs_optional = {"name"}
tag_elts = {}
attr_convert = {} # {"direction" : Introspection.DIRECTION} assigned below
def __init__(self, *, name = None, type, direction, annotations = ()) :
if not isinstance(direction, Introspection.DIRECTION) :
raise TypeError("direction must be an Introspection.DIRECTION.xxx enum")
#end if
self.name = name
self.type = parse_single_signature(type)
self.direction = direction
self.annotations = Introspection._get_annotations(annotations)
#end __init__
#end Arg
tag_elts = {"args" : Arg}
def __init__(self, name, args = (), annotations = ()) :
if not all(isinstance(a, self.Arg) for a in args) :
raise TypeError("args must be Arg instances")
#end if
self.name = name
self.args = list(args)
self.annotations = Introspection._get_annotations(annotations)
#end __init__
@property
def in_signature(self) :
return \
list(a.type for a in self.args if a.direction == Introspection.DIRECTION.IN)
#end in_signature
@property
def out_signature(self) :
return \
list \
(a.type for a in self.args if a.direction == Introspection.DIRECTION.OUT)
#end out_signature
@property
def expect_reply(self) :
"will there be replies to this request method."
return \
self.get_annotation("org.freedesktop.DBus.Method.NoReply") != "true"
#end expect_reply
#end Method
class Signal(_TagCommon) :
__slots__ = ("name", "args", "annotations")
tag_name = "signal"
tag_attrs = ("name",)
class Arg(_TagCommon) :
__slots__ = ("name", "type", "direction", "annotations")
tag_name = "arg"
tag_attrs = ("name", "type", "direction")
tag_attrs_optional = {"name", "direction"}
tag_elts = {}
attr_convert = {} # {"direction" : Introspection.DIRECTION} assigned below
def __init__(self, *, name = None, type, direction = None, annotations = ()) :
if direction != None and direction != Introspection.DIRECTION.OUT :
raise ValueError("direction can only be Introspection.DIRECTION.OUT")
#end if
self.name = name
self.type = parse_single_signature(type)
self.direction = direction
self.annotations = Introspection._get_annotations(annotations)
#end __init__
#end Arg
tag_elts = {"args" : Arg}
def __init__(self, name, args = (), annotations = ()) :
if not all(isinstance(a, self.Arg) for a in args) :
raise TypeError("args must be Arg instances")
#end if
self.name = name
self.args = list(args)
self.annotations = Introspection._get_annotations(annotations)
#end __init__
@property
def in_signature(self) :
return \
list(a.type for a in self.args)
#end in_signature
#end Signal
class Property(_TagCommon) :
__slots__ = ("name", "type", "access", "annotations")
tag_name = "property"
tag_attrs = ("name", "type", "access")
tag_elts = {}
attr_convert = {} # {"access" : Introspection.ACCESS} assigned below
def __init__(self, name, type, access, annotations = ()) :
if not isinstance(access, Introspection.ACCESS) :
raise TypeError("access must be an Introspection.ACCESS.xxx enum")
#end if
self.name = name
self.type = parse_single_signature(type)
self.access = access
self.annotations = Introspection._get_annotations(annotations)
#end __init__
#end Property
tag_elts = {"methods" : Method, "signals" : Signal, "properties" : Property}
def __init__(self, name, methods = (), signals = (), properties = (), annotations = ()) :
if not all(isinstance(m, self.Method) for m in methods) :
raise TypeError("methods must be Method instances")
#end if
if not all(isinstance(s, self.Signal) for s in signals) :
raise TypeError("signals must be Signal instances")
#end if
if not all(isinstance(p, self.Property) for p in properties) :
raise TypeError("properties must be Property instances")
#end if
self.name = name
self.methods = list(methods)
self.signals = list(signals)
self.properties = list(properties)
self.annotations = Introspection._get_annotations(annotations)
#end __init__
@property
def methods_by_name(self) :
"returns a dict associating all the methods with their names."
return \
dict((method.name, method) for method in self.methods)
#end methods_by_name
@property
def signals_by_name(self) :
"returns a dict associating all the signals with their names."
return \
dict((signal.name, signal) for signal in self.signals)
#end signals_by_name
@property
def properties_by_name(self) :
"returns a dict associating all the properties with their names."
return \
dict((prop.name, prop) for prop in self.properties)
#end properties_by_name
#end Interface
Interface.Method.Arg.attr_convert["direction"] = DIRECTION
Interface.Signal.Arg.attr_convert["direction"] = lambda x : (lambda : None, lambda : Introspection.DIRECTION(x))[x != None]()
Interface.Property.attr_convert["access"] = ACCESS
class StubInterface(_TagCommon) :
"use this as a replacement for an Interface that you dont want" \
" to see expanded, e.g. if it has already been seen."
__slots__ = ("name", "annotations")
tag_name = "interface"
tag_attrs = ("name",)
tag_elts = {}
def __init__(self, name) :
self.name = name
self.annotations = ()
#end __init__
#end StubInterface
class Node(_TagCommon) :
__slots__ = ("name", "interfaces", "nodes", "annotations")
tag_name = "node"
tag_attrs = ("name",)
def __init__(self, name, interfaces = (), nodes = (), annotations = ()) :
if not all(isinstance(i, (Introspection.Interface, Introspection.StubInterface)) for i in interfaces) :
raise TypeError("interfaces must be Interface or StubInterface instances")
#end if
if not all(isinstance(n, Introspection.Node) for n in nodes) :
raise TypeError("nodes must be Node instances")
#end if
self.name = name
self.interfaces = interfaces
self.nodes = nodes
self.annotations = Introspection._get_annotations(annotations)
#end __init__
@property
def interfaces_by_name(self) :
"returns a dict associating all the interfaces with their names."
return \
dict((iface.name, iface) for iface in self.interfaces)
#end interfaces_by_name
@property
def nodes_by_name(self) :
"returns a dict associating all the child nodes with their names."
return \
dict((node.name, node) for node in self.nodes)
#end nodes_by_name
#end Node
Node.tag_elts = {"interfaces" : Interface, "nodes" : Node}
tag_elts = {"interfaces" : Interface, "nodes" : Node}
def __init__(self, name = None, interfaces = (), nodes = (), annotations = ()) :
if not all(isinstance(i, self.Interface) for i in interfaces) :
raise TypeError("interfaces must be Interface instances")
#end if
if not all(isinstance(n, self.Node) for n in nodes) :
raise TypeError("nodes must be Node instances")
#end if
self.name = name
self.interfaces = list(interfaces)
self.nodes = list(nodes)
self.annotations = Introspection._get_annotations(annotations)
#end __init__
@property
def interfaces_by_name(self) :
"returns a dict associating all the interfaces with their names."
return \
dict((iface.name, iface) for iface in self.interfaces)
#end interfaces_by_name
@property
def nodes_by_name(self) :
"returns a dict associating all the nodes with their names."
return \
dict((node.name, node) for node in self.nodes)
#end nodes_by_name
@classmethod
def parse(celf, s) :
"generates an Introspection tree from the given XML string description."
def from_string_elts(celf, attrs, tree) :
elts = dict((k, attrs[k]) for k in attrs)
child_tags = dict \
(
(childclass.tag_name, childclass)
for childclass in tuple(celf.tag_elts.values()) + (Introspection.Annotation,)
)
children = []
for child in tree :
if child.tag not in child_tags :
raise KeyError("unrecognized tag %s" % child.tag)
#end if
childclass = child_tags[child.tag]
childattrs = {}
for attrname in childclass.tag_attrs :
if hasattr(childclass, "tag_attrs_optional") and attrname in childclass.tag_attrs_optional :
childattrs[attrname] = child.attrib.get(attrname, None)
else :
if attrname not in child.attrib :
raise ValueError("missing %s attribute for %s tag" % (attrname, child.tag))
#end if
childattrs[attrname] = child.attrib[attrname]
#end if
#end for
if hasattr(childclass, "attr_convert") :
for attr in childclass.attr_convert :
if attr in childattrs :
childattrs[attr] = childclass.attr_convert[attr](childattrs[attr])
#end if
#end for
#end if
children.append(from_string_elts(childclass, childattrs, child))
#end for
for child_tag, childclass in tuple(celf.tag_elts.items()) + ((), (("annotations", Introspection.Annotation),))[tree.tag != "annotation"] :
for child in children :
if isinstance(child, childclass) :
if child_tag not in elts :
elts[child_tag] = []
#end if
elts[child_tag].append(child)
#end if
#end for
#end for
return \
celf(**elts)
#end from_string_elts
#begin parse
tree = XMLElementTree.fromstring(s)
assert tree.tag == "node", "root of introspection tree must be <node> tag"
return \
from_string_elts(Introspection, {}, tree)
#end parse
def unparse(self, indent_step = 4, max_linelen = 72) :
"returns an XML string description of this Introspection tree."
out = io.StringIO()
def to_string(obj, indent) :
tag_name = obj.tag_name
attrs = []
for attrname in obj.tag_attrs :
attr = getattr(obj, attrname)
if attr != None :
if isinstance(attr, enum.Enum) :
attr = attr.value
elif isinstance(attr, Type) :
attr = unparse_signature(attr)
elif not isinstance(attr, str) :
raise TypeError("unexpected attribute type %s for %s" % (type(attr).__name__, repr(attr)))
#end if
attrs.append("%s=%s" % (attrname, quote_xml_attr(attr)))
#end if
#end for
has_elts = \
(
sum
(
len(getattr(obj, attrname))
for attrname in
tuple(obj.tag_elts.keys())
+
((), ("annotations",))
[not isinstance(obj, Introspection.Annotation)]
)
!=
0
)
out.write(" " * indent + "<" + tag_name)
if (
max_linelen != None
and
indent
+
len(tag_name)
+
sum((len(s) + 1) for s in attrs)
+
2
+
int(has_elts)
>
max_linelen
) :
out.write("\n")
for attr in attrs :
out.write(" " * (indent + indent_step))
out.write(attr)
out.write("\n")
#end for
out.write(" " * indent)
else :
for attr in attrs :
out.write(" ")
out.write(attr)
#end for
#end if
if not has_elts :
out.write("/")
#end if
out.write(">\n")
if has_elts :
for attrname in sorted(obj.tag_elts.keys()) + ["annotations"] :
for elt in getattr(obj, attrname) :
to_string(elt, indent + indent_step)
#end for
#end for
out.write(" " * indent + "</" + tag_name + ">\n")
#end if
#end to_string
#begin unparse
out.write(DBUS.INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE)
out.write("<node")
if self.name != None :
out.write(" name=%s" % quote_xml_attr(self.name))
#end if
out.write(">\n")
for elt in self.interfaces :
to_string(elt, indent_step)
#end for
for elt in self.nodes :
to_string(elt, indent_step)
#end for
out.write("</node>\n")
return \
out.getvalue()
#end unparse
#end Introspection
del _TagCommon
#+
# Standard interfaces
#-
standard_interfaces = \
{
DBUS.INTERFACE_PEER :
# note implementation of this is hard-coded inside libdbus
Introspection.Interface
(
name = DBUS.INTERFACE_PEER,
methods =
[
Introspection.Interface.Method(name = "Ping"),
Introspection.Interface.Method
(
name = "GetMachineId",
args =
[
Introspection.Interface.Method.Arg
(
name = "machine_uuid",
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.OUT,
),
]
),
],
),
DBUS.INTERFACE_LOCAL :
# note implementation of this is hard-coded inside, and specific to, libdbus
Introspection.Interface
(
name = DBUS.INTERFACE_LOCAL,
signals =
[
Introspection.Interface.Signal(name = "Disconnected"),
# auto-generated by libdbus with path = DBUS.PATH_LOCAL
# when connection is closed; cannot be explicitly sent by
# clients. Documented here:
# <https://lists.freedesktop.org/archives/dbus/2018-October/017587.html>
],
),
DBUS.INTERFACE_DBUS :
Introspection.Interface
(
name = DBUS.INTERFACE_DBUS,
methods =
[
Introspection.Interface.Method
(
name = "Hello",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.OUT,
), # returned unique name
]
),
Introspection.Interface.Method
(
name = "RequestName",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
), # name
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.IN,
), # flags DBUS.NAME_FLAG_xxx
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.OUT,
), # result DBUS.REQUEST_NAME_REPLY_xxx
]
),
Introspection.Interface.Method
(
name = "ReleaseName",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.OUT,
), # result DBUS.RELEASE_NAME_REPLY_xxx
]
),
Introspection.Interface.Method
(
name = "StartServiceByName",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
), # name
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.IN,
), # flags (currently unused)
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.OUT,
), # result DBUS.START_REPLY_xxx
]
),
Introspection.Interface.Method
(
name = "UpdateActivationEnvironment",
args =
[
Introspection.Interface.Method.Arg
(
type = DictType
(
keytype = BasicType(TYPE.STRING),
valuetype = BasicType(TYPE.STRING)
),
direction = Introspection.DIRECTION.IN,
), # environment
]
),
Introspection.Interface.Method
(
name = "NameHasOwner",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
), # name
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.BOOLEAN),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "ListNames",
args =
[
Introspection.Interface.Method.Arg
(
type = ArrayType(BasicType(TYPE.STRING)),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "ListActivatableNames",
args =
[
Introspection.Interface.Method.Arg
(
type = ArrayType(BasicType(TYPE.STRING)),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "AddMatch",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
]
),
Introspection.Interface.Method
(
name = "RemoveMatch",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
]
),
Introspection.Interface.Method
(
name = "GetNameOwner",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "ListQueuedOwners",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = ArrayType(BasicType(TYPE.STRING)),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "GetConnectionUnixUser",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "GetConnectionUnixProcessID",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "GetAdtAuditSessionData",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = ArrayType(BasicType(TYPE.BYTE)),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "GetConnectionSELinuxSecurityContext",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = ArrayType(BasicType(TYPE.BYTE)),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "ReloadConfig",
),
Introspection.Interface.Method
(
name = "GetId",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.OUT,
),
]
),
Introspection.Interface.Method
(
name = "GetConnectionCredentials",
args =
[
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
type = DictType(BasicType(TYPE.STRING), VariantType()),
direction = Introspection.DIRECTION.OUT,
),
]
),
],
signals =
[
Introspection.Interface.Signal
(
name = "NameOwnerChanged",
args =
[
Introspection.Interface.Signal.Arg
(
type = BasicType(TYPE.STRING),
), # bus name
Introspection.Interface.Signal.Arg
(
type = BasicType(TYPE.STRING),
), # old owner, empty if none
Introspection.Interface.Signal.Arg
(
type = BasicType(TYPE.STRING),
), # new owner, empty if none
]
),
Introspection.Interface.Signal
(
name = "NameLost", # sent to previous owner of name
args =
[
Introspection.Interface.Signal.Arg
(
type = BasicType(TYPE.STRING),
),
]
),
Introspection.Interface.Signal
(
name = "NameAcquired", # sent to new owner of name
args =
[
Introspection.Interface.Signal.Arg
(
type = BasicType(TYPE.STRING),
),
]
),
],
),
DBUS.INTERFACE_INTROSPECTABLE :
Introspection.Interface
(
name = DBUS.INTERFACE_INTROSPECTABLE,
methods =
[
Introspection.Interface.Method
(
name = "Introspect",
args =
[
Introspection.Interface.Method.Arg
(
name = "data",
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.OUT,
),
]
),
],
),
DBUS.INTERFACE_PROPERTIES :
Introspection.Interface
(
name = DBUS.INTERFACE_PROPERTIES,
methods =
[
Introspection.Interface.Method
(
name = "Get",
args =
[
Introspection.Interface.Method.Arg
(
name = "interface_name",
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
name = "property_name",
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
name = "value",
type = VariantType(),
direction = Introspection.DIRECTION.OUT,
),
],
),
Introspection.Interface.Method
(
name = "Set",
args =
[
Introspection.Interface.Method.Arg
(
name = "interface_name",
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
name = "property_name",
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
name = "value",
type = VariantType(),
direction = Introspection.DIRECTION.IN,
),
],
),
Introspection.Interface.Method
(
name = "GetAll",
args =
[
Introspection.Interface.Method.Arg
(
name = "interface_name",
type = BasicType(TYPE.STRING),
direction = Introspection.DIRECTION.IN,
),
Introspection.Interface.Method.Arg
(
name = "values",
type = DictType(BasicType(TYPE.STRING), VariantType()),
direction = Introspection.DIRECTION.OUT,
),
],
),
],
signals =
[
Introspection.Interface.Signal
(
name = "PropertiesChanged",
args =
[
Introspection.Interface.Signal.Arg
(
name = "interface_name",
type = BasicType(TYPE.STRING),
),
Introspection.Interface.Signal.Arg
(
name = "changed_properties",
type = DictType(BasicType(TYPE.STRING), VariantType()),
),
Introspection.Interface.Signal.Arg
(
name = "invalidated_properties",
type = ArrayType(BasicType(TYPE.STRING)),
),
],
),
],
),
DBUS.INTERFACE_MONITORING :
Introspection.Interface
(
name = DBUS.INTERFACE_MONITORING,
methods =
[
Introspection.Interface.Method
(
name = "BecomeMonitor",
args =
[
Introspection.Interface.Method.Arg
(
type = ArrayType(BasicType(TYPE.STRING)),
direction = Introspection.DIRECTION.IN,
), # match rules to add to the connection
Introspection.Interface.Method.Arg
(
type = BasicType(TYPE.UINT32),
direction = Introspection.DIRECTION.IN,
), # flags (currently unused)
],
),
],
),
DBUSX.INTERFACE_OBJECT_MANAGER :
Introspection.Interface
(
name = DBUSX.INTERFACE_OBJECT_MANAGER,
methods =
[
Introspection.Interface.Method
(
name = "GetManagedObjects",
args =
[
Introspection.Interface.Method.Arg
(
name = "objpath_interfaces_and_properties",
type = DictType
(
BasicType(TYPE.OBJECT_PATH),
DictType
(
BasicType(TYPE.STRING), # interface
DictType(BasicType(TYPE.STRING), VariantType())
# properties and values
)
),
direction = Introspection.DIRECTION.OUT,
),
],
),
],
signals =
[
Introspection.Interface.Signal
(
name = "InterfacesAdded",
args =
[
Introspection.Interface.Signal.Arg
(
name = "object_path",
type = BasicType(TYPE.OBJECT_PATH),
),
Introspection.Interface.Signal.Arg
(
name = "interfaces_and_properties",
type = DictType
(
BasicType(TYPE.STRING), # interface added/changed
DictType(BasicType(TYPE.STRING), VariantType())
# properties and values added
),
),
],
),
Introspection.Interface.Signal
(
name = "InterfacesRemoved",
args =
[
Introspection.Interface.Signal.Arg
(
name = "object_path",
type = BasicType(TYPE.OBJECT_PATH),
),
Introspection.Interface.Signal.Arg
(
name = "interfaces",
type = ArrayType(BasicType(TYPE.STRING)),
# interfaces removed
),
],
),
],
),
}
#+
# Cleanup
#-
def _atexit() :
# disable all __del__ methods at process termination to avoid segfaults
for cls in Connection, Server, PreallocatedSend, Message, PendingCall, Error, AddressEntries :
delattr(cls, "__del__")
#end for
#end _atexit
atexit.register(_atexit)
del _atexit