PYTHON-SDBUS(1)
💖 python-sdbus 💖
okay so you're looking at python-sdbus(1), bestie. yes it's a whole manual page. no you don't have to read it all in one sitting. grab a snack 🍓
✨ NAME ✨
python-sdbus — python-sdbus
Python-sdbus is the python D-Bus library that aim to use the modern features of python
- Asyncio — https://docs.python.org/3/library/asyncio.html
- Type hints — https://docs.python.org/3/library/typing.html
- Based on fast sd-bus — https://www.freedesktop.org/software/systemd/man/sd-bus.html
- Unified client/server interface classes. Write interface class once.
- D-Bus methods can have keyword and default arguments.
like… "write interface class once"?? that's the whole pitch and honestly? ate. 💅
🌈 D-BUS 🌈
D-Bus is the inter-process communication standard commonly used on Linux desktop.
This documentation expects you to be familiar with D-Bus concepts and conventions.
If you are unfamiliar with D-Bus you might want to read following pages:
- Wikipedia page — https://en.wikipedia.org/wiki/D-Bus
- Lennart Poettering post about D-Bus — https://web.archive.org/web/20200522193008/http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html
- D-Bus specification by freedesktop.org — https://dbus.freedesktop.org/doc/dbus-specification.html
- Install D-Spy D-Bus debugger and observe services and objects on your D-Bus — https://apps.gnome.org/Dspy/
ngl D-Spy is so underrated, go peek at your own bus, it's giving main character energy 🔥
✨ General Information ✨
🫶 Blocking vs Async 🫶
Python-sdbus supports both blocking and async IO.
Regular python functions are always blocking.
Asyncio is a part of python standard library that allows non-blocking io.
Asyncio documentation — https://docs.python.org/3/library/asyncio.html
Generally blocking IO should only be used for simple scripts and programs that interact with existing D-Bus objects.
🌸 Blocking: 🌸
- Blocking is easier to initiate (no event loop)
- Properties behave exactly as Python properties do. (i.e. can assign with
=operator) - Only allows one request at a time.
- No D-Bus signals.
- Cannot serve objects, only interact with existing object on D-Bus.
Blocking quick start <>
Blocking API <>
⚡ Asyncio: ⚡
- Calls need to be
awaited. - Multiple requests at the same time.
- Serve object on D-Bus for other programs.
- D-Bus Signals.
Asyncio quick start <>
Asyncio API <>
🦋 D-Bus types conversion 🦋
D-Bus types reference — https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
Note: 🫢 Python integers are unlimited size but D-Bus integers are not. All integer types raiseOverflowErrorif you try to pass number outside the type size. Unsigned integers range is0 < (2**bit_size)-1.
like yes bestie the python int will happily hold a number the size of your feelings, but the bus said absolutely not. 💀
Okay so before we get into the type tea, just a quick note: signed integers range is -(2**(bit_size-1)) < (2**(bit_size-1))-1. Periodt. 💅
Now, let's talk type mapping, bestie. Here's the whole vibe of how D-Bus types vibe with Python types. I'm gonna list them all out for you, no skips. ✨
- Boolean — D-Bus type:
b, Python type:bool— True or False. 💖 - Byte — D-Bus type:
y, Python type:int— Unsigned 8-bit integer. But note: array of bytes (ay) has a different type in the python domain. 🙈 - Int16 — D-Bus type:
n, Python type:int— Signed 16-bit integer. - Uint16 — D-Bus type:
q, Python type:int— Unsigned 16-bit integer. - Int32 — D-Bus type:
i, Python type:int— Signed 32-bit integer. - Uint32 — D-Bus type:
u, Python type:int— Unsigned 32-bit integer. - Int64 — D-Bus type:
x, Python type:int— Signed 64-bit integer. - Uint64 — D-Bus type:
t, Python type:int— Unsigned 64-bit integer. - Double — D-Bus type:
d, Python type:float— Float point number. (Yes, that's what it says, bestie. It's a float.) ✨ - Unix FD — D-Bus type:
h, Python type:int— File descriptor. - String — D-Bus type:
s, Python type:str— String. - Object Path — D-Bus type:
o, Python type:str— Syntactically correct D-Bus object path. - Signature — D-Bus type:
g, Python type:str— D-Bus type signature. - Array — D-Bus type:
a, Python type:list— List of some single type. Example:asarray of strings. - Byte Array — D-Bus type:
ay, Python type:bytes— Array of bytes. Not a unique type in D-Bus but a different type in Python. Accepts bothbytesandbytearray. Used for binary data. - Struct — D-Bus type:
(), Python type:tuple— Tuple. Example:(isax)tuple of int, string and array of int. - Dictionary — D-Bus type:
a{}, Python type:dict— Dictionary with key type and value type. Note: Dictionary is always a part of array. I.E.a{si}is the dict with string keys and integer values.{si}is NOT a valid signature. 🚫 - Variant — D-Bus type:
v, Python type:tuple— Unknown type that can be any single type. In Python represented by a tuple of a signature string and a single type. Example:("s", "test")variant of a single string.
✨ Name conversions ✨
So here's the tea on names: D-Bus is all about that CamelCase life for method names, while Python is serving snake_case realness. When you decorate a method, the name gets auto-translated from snake_case to CamelCase. Like, close_notification becomes CloseNotification. But wait, there's more: all decorators have a parameter that lets you force the D-Bus name to whatever you want. Check the API docs for each decorator to see how. 💁♀️
🚌 Default bus 🚌
So, most object methods that take a bus as a parameter will just use a thread-local default bus connection if you don't explicitly pass a bus object. It's like a default setting, bestie.
If you're running as a user, the session bus is the default. Otherwise, it's the system bus. Periodt.
The request_default_bus_name_async (see #sdbus.default_bus.request_default_bus_name_async) and request_default_bus_name (see #sdbus.default_bus.request_default_bus_name) can be used to acquire a service name on the default bus. 🎀
Use sd_bus_open_user() (see #sdbus.sd_bus_open_user) and sd_bus_open_system() (see #sdbus.sd_bus_open_system) to acquire a specific bus connection.
The set_default_bus (see #sdbus.default_bus.set_default_bus) can be used to set the new thread-local bus. This should be done before any objects that take bus as an init argument are created. If no bus has been set the new bus will be initialized and set as thread-local default. 🔥
The bus can also be set as default for the current context using set_context_default_bus. The context here means the standard library's contextvars module context variables, which asyncio frameworks use all the time. And bestie, listen: context-local default bus has higher priority over thread-local default bus. 💅
🦋 Glossary 🦋
- Bus — object representing connection to D-Bus.
- Proxy — Python object that represents an object on D-Bus. Without proxy you manipulate messages directly.
- Remote — something that exists outside current Python process.
- Local — something that exists inside current Python scope.
- Service Name — a well known name that an process can acquire on D-Bus. For example, systemd acquires
org.freedesktop.systemd1name. - Signature — D-Bus type definition. Represented by a string. See D-Bus types conversion.
🎀 Contents 🎀
- Index
- API Index
- Search Page
✨ Common API ✨
These calls are shared between async and blocking API. both flavors, one vibe 🫶
🌸 Default bus 🌸
sdbus.default_bus.get_default_bus() -> SdBus
Get default bus. ✨
Returns context-local default bus if set or thread-local otherwise.
If no default bus is set initializes a new bus using sdbus.sd_bus_open() and sets it as a thread-local default bus.
sdbus.default_bus.set_default_bus(new_default: SdBus) -> None
Set thread-local default bus.
Should be called before creating any objects that will use default bus.
Default bus can be replaced but the change will only affect newly created objects. (existing objects already got their bus, they're not swapping, periodt)
sdbus.default_bus.set_context_default_bus(new_default: SdBus) -> Token[SdBus]
Set context-local default bus.
Should be called before creating any objects that will use default bus.
Default bus can be replaced but the change will only affect newly created objects.
Context-local default bus has higher priority over thread-local one but has to be explicitly set.
Returns
- Token that can be used to reset context bus back. See
contextvarsdocumentation for details.
async sdbus.default_bus.request_default_bus_name_async(new_name: str, allow_replacement: bool = False, replace_existing: bool = False, queue: bool = False) -> None
Asynchronously acquire a name on the default bus. 👑
Parameters
new_name— Name to acquire. Must be a valid D-Bus service name.allow_replacement— If name was acquired allow other D-Bus peers to take away the name.replace_existing— If current name owner allows, take away the name.queue— Queue up for name acquisition.SdBusRequestNameInQueueErrorwill be raised when successfully placed in queue. Ownership change signalname_owner_changedshould be monitored get notified when the name was acquired.
Raises Name request exceptions and other D-Bus exceptions.
sdbus.default_bus.request_default_bus_name(new_name: str, allow_replacement: bool = False, replace_existing: bool = False, queue: bool = False) -> None
Acquire a name on the default bus.
Blocks until a reply is received from D-Bus daemon. (yes bestie, it really does just sit there and wait 🫠)
Parameters
new_name— Name to acquire. Must be a valid D-Bus service name.allow_replacement— If name was acquired allow other D-Bus peers to take away the name.replace_existing— If current name owner allows, take away the name.queue— Queue up for name acquisition.SdBusRequestNameInQueueErrorwill be raised when successfully placed in queue. Ownership change signalname_owner_changedshould be monitored get notified when the name was acquired.
Raises Name request exceptions and other D-Bus exceptions.
🌈 D-Bus connections calls 🌈
sdbus.sd_bus_open()
Opens a new bus connection. The session bus will be opened when available or system bus otherwise. 🦋
Returns Session or system bus.
Return type SdBus
sdbus.sd_bus_open_user()
Opens a new user session bus connection.
Returns Session bus.
Return type SdBus
sdbus.sd_bus_open_system()
Opens a new system bus connection.
Returns System bus.
Return type SdBus
sdbus.sd_bus_open_system_remote(host)
Opens a new system bus connection on a remote host through SSH. Host can be prefixed with username@ and followed by :port and /machine_name as in systemd-nspawn container name. 🧚
Parameters
host(str) — Host name to connect.
Returns Remote system bus.
Return type SdBus
sdbus.sd_bus_open_system_machine(machine)
Opens a new system bus connection in a systemd-nspawn container. Machine name can be prefixed with username@. Special machine name .host indicates local system.
Parameters
machine(str) — Machine (container) name.
Returns Remote system bus.
Return type SdBus
sdbus.sd_bus_open_user_machine(machine)
Opens a new user session bus connection in a systemd-nspawn container. Opens root user bus session or can be prefixed with username@ for a specific user.
Parameters
machine(str) — Machine (container) name.
Returns Remote system bus.
Return type SdBus
💜 Helper functions 💜
sdbus.encode_object_path(prefix, external)
Encode that arbitrary string as a valid object path prefixed with prefix.
Parameters
prefix(str) — Prefix path. Must be a valid object path.external(str) — Arbitrary string to identify object.
Returns valid object path
Return type str
Example on how systemd encodes unit names on D-Bus:
from sdbus import encode_object_path
# System uses /org/freedesktop/systemd1/unit as prefix of all units
# dbus.service is a name of D-Bus unit but dot . is not a valid object path
s = encode_object_path('/org/freedesktop/systemd1/unit', 'dbus.service')
print(s)
# Prints: /org/freedesktop/systemd1/unit/dbus_2eservice
sdbus.decode_object_path(prefix, full_path)
Decode object name that was encoded with encode_object_path().
Parameters
prefix(str) — Prefix path. Must be a valid object path.full_path(str) — Full path to be decoded.
Returns Arbitrary name
Return type str
Example decoding systemd unit name:
from sdbus import decode_object_path
s = decode_object_path(
'/org/freedesktop/systemd1/unit',
'/org/freedesktop/systemd1/unit/dbus_2eservice'
)
print(s)
# Prints: dbus.service
⭐ Flags ⭐
Flags are int values that should be ORed to combine. stack 'em like friendship bracelets 🎀
Example, DbusDeprecatedFlag plus DbusHiddenFlag: DbusDeprecatedFlag | DbusHiddenFlag
sdbus.DbusDeprecatedFlag:int— Mark this method or property as deprecated in introspection data.sdbus.DbusHiddenFlag:int— Method or property will not show up in introspection data. 🙈sdbus.DbusUnprivilegedFlag:int— Mark this method or property as unprivileged. This means anyone can call it. Only works for system bus as user session bus is fully trusted by default.sdbus.DbusNoReplyFlag:int— This method does not have a reply message. It instantly returns and does not have any errors.sdbus.DbusPropertyConstFlag:int— Mark that this property does not change during object life time.sdbus.DbusPropertyEmitsChangeFlag:int— This property emits signal when it changes.sdbus.DbusPropertyEmitsInvalidationFlag:int— This property emits signal when it invalidates. (means the value changed but does not include new value in the signal)sdbus.DbusPropertyExplicitFlag:int— This property is too heavy to calculate so its not included inGetAllmethod call. (it's giving "don't ask me, I'm tired" 😭)sdbus.DbusSensitiveFlag:int— Data in messages in sensitive and will be scrubbed from memory after message is red.
👑 Blocking quick start 👑
🌸 Interface classes 🌸
Python-sdbus works by declaring interface classes.
Interface classes for blocking IO should be derived from DbusInterfaceCommon. 💅
The class constructor takes interface_name keyword to determine the D-Bus interface name for all D-Bus elements declared in the class body.
Example:
class ExampleInterface(DbusInterfaceCommon,
interface_name='org.example.myinterface'
):
...
Interface class body should contain the definitions of methods and properties using the decorators dbus_method() and dbus_property() respectively.
Example:
from sdbus import (DbusInterfaceCommon,
dbus_method, dbus_property)
class ExampleInterface(DbusInterfaceCommon,
interface_name='org.example.myinterface'
):
# Method that takes an integer and does not return anything
@dbus_method('u')
def close_notification(self, an_int: int) -> None:
raise NotImplementedError
# Read only property of int
@dbus_property()
def test_int(self) -> int:
raise NotImplementedError
This is an interface of that defines a one D-Bus method and one property. ✨ slay, that's the whole concept
so bestie, the actual body of that decorated function? never getting called. not even once. 🙈 instead the whole call gets routed through D-Bus over to another process, and that's where the real work happens. an interface can totally have non-decorated functions too — those just act like regular methods, no D-Bus magic attached. 💅
Blocking IO can only ever interact with D-Bus objects that already exist, and it can not be served up for other processes to interact with. see Blocking vs Async for the whole story. 🫶
🌸 Initiating proxy 🌸
DbusInterfaceCommon.__init__() takes service_name and object_path of the remote object that your shiny new object is gonna proxy to. that's it, that's the vibe. ✨
example — creating a proxy and calling a method:
...
# Initialize the object
d = ExampleInterface(
service_name='org.example.test',
object_path='/',
)
d.close_notification(1234)
⚠️ Note: successfully initiating a proxy object does NOT guarantee that the D-Bus object actually exists. it's giving "sure bestie, whatever you say" energy. 💀
💖 Methods 💖
methods are just functions wrapped with the dbus_method() decorator. iconic. 👑
if the remote object sends back an error reply, an exception with a base of DbusFailedError gets raised. peep Exceptions for the full list of what could get thrown at you.
the wrapped function will not be called. it's recommended to set the function to raise NotImplementedError so nobody gets confused. 🙈
example:
from sdbus import DbusInterfaceCommon, dbus_method
class ExampleInterface(...):
...
# Body of some class
@dbus_method('u')
def close_notification(self, an_int: int) -> None:
raise NotImplementedError
🎀 Properties 🎀
a D-Bus property gets defined by wrapping a function with the dbus_property() decorator. yes, we love a consistent API. 💅
example:
from sdbus import DbusInterfaceCommon, dbus_property
class ExampleInterface(...):
...
# Body of some class
# Property of str
@dbus_property('s')
def test_string(self) -> str:
raise NotImplementedError
the new property behaves very similarly to Python's own property() decorator. siblings fr fr. 🫶
# Initialize the proxy
d = ExampleInterface(
service_name='org.example.test',
object_path='/',
)
# Print it
print(d.test_string)
# Assign new string
d.test_string = 'some_string'
if the property is read-only, DbusPropertyReadOnlyError gets raised. no touchy. 🚫
🦋 Multiple interfaces 🦋
a single D-Bus object can have multiple interfaces with different methods and properties. main character energy, honestly. ✨
to make that happen, define multiple interface classes and do a multiple inheritance on all the interfaces the object has. go off. 👑
example:
from sdbus import DbusInterfaceCommon, dbus_method
class ExampleInterface(DbusInterfaceCommon,
interface_name='org.example.myinterface'
):
@dbus_method('i')
def example_method(self, an_int: int) -> None:
raise NotImplementedError
class TestInterface(DbusInterfaceCommon,
interface_name='org.example.test'
):
@dbus_method('as')
def test_method(self, str_array: list[str]) -> None:
raise NotImplementedError
class MultipleInterfaces(TestInterface, ExampleInterface):
...
the MultipleInterfaces class will have both test_method and example_method, and each one gets proxied to the correct interface name. (org.example.myinterface and org.example.test respectively, iykyk.) 💜
🔥 Blocking API 🔥
👑 Classes 👑
class sdbus.DbusInterfaceCommon(interface_name) — the D-Bus interface class itself. D-Bus methods and properties should be defined using the dbus_property() and dbus_method() decorators. 🌈
- Parameters
interface_name(str) — sets the D-Bus interface name that will be used for all the properties and methods defined in the body of the class. 🎀
__init__(service_name, object_path[, bus]) — init creates a proxy to a remote object. ✨
- Parameters
service_name(str) — remote object D-Bus connection name. for example, systemd usesorg.freedesktop.systemd1. 👀object_path(str) — remote object D-Bus path. should be a forward slash separated path. the starting object is usually/. example:/org/freedesktop/systemd/unit/dbus_2eservice.bus(SdBus) — optional D-Bus connection object. if you don't pass one, the default D-Bus gets used. no biggie. 💅
dbus_ping() — pings the remote service using D-Bus. super useful for testing whether the connection or the remote service is even alive. 🫶
⚠️ Warning: this method ignores the particular object path, which means it can NOT be used to test if an object exists. it's just checking the vibe of the service, not the specific object. 💀
dbus_machine_id() — returns the machine UUID of the D-Bus that the object is connected to. 🔑
- Returns — machine UUID
- Return type —
str
dbus_introspect() — get the D-Bus introspection XML. it's your responsibility to parse that data, bestie. 🧚
- Returns — string with introspection XML
- Return type —
str
properties_get_all_dict() — get all object properties as a dictionary where the keys are member names and the values are property values. 🌸
equivalent to the GetAll method of the org.freedesktop.DBus.Properties interface, except the member names get automatically translated to python names. (internally it calls GetAll for each interface used in the class definition. yes, it does the rounds. 💅)
- Parameters
on_unknown_member(str) — if an unknown D-Bus property was encountered, either raise an"error"(default),"ignore"the property, or"reuse"the D-Bus name for the member. pick your fighter. 👑
- Returns — dictionary of properties
- Return type —
dict[str, Any]
Example:
from sdbus import (DbusInterfaceCommon,
dbus_method, dbus_property)
class ExampleInterface(DbusInterfaceCommon,
interface_name='org.example.my'
):
# Method that takes an integer and does not return anything
@dbus_method('u')
def close_notification(self, an_int: int) -> None:
raise NotImplementedError
# Method that does not take any arguments and returns a list of str
@dbus_method()
def get_capabilities(self) -> list[str]:
raise NotImplementedError
# Method that takes a dict of {str: str} and returns an int
@dbus_method('a{ss}')
def count_entries(self, a_dict: dict[str, str]) -> int:
raise NotImplementedError
# Read only property of int
@dbus_property()
def test_int(self) -> int:
raise NotImplementedError
# Read/Write property of str
@dbus_property('s')
def test_string(self) -> str:
raise NotImplementedError
class sdbus.DbusObjectManagerInterface(interface_name) — this class is almost identical to DbusInterfaceCommon, but it implements the ObjectManager interface. same energy, extra power. ⭐
get_managed_objects() — get the objects this object manager is managing. 🗂️
- Returns — a triple nested dictionary that contains all the object paths with their property values.
dict[ObjectPath, dict[InterfaceName, dict[PropertyName, PropertyValue]]] - Return type —
dict[str, dict[str, dict[str, Any]]]
✨ Decorators ✨
@sdbus.dbus_method([input_signature[, flags[, method_name]]]) — define a D-Bus method. 🎀
the decorated function becomes linked to the D-Bus method. always use round brackets () even when you're not passing any arguments. no shortcuts, bestie. 💅
- Parameters
input_signature(str) — D-Bus input signature. defaults to"", meaning the method takes no arguments. required if the method takes any arguments. ✨flags(int) — modifies behavior. no effect on remote connections. defaults to0, meaning no special behavior. peep Flags for more. 👀method_name(str) — explicitly define the remote method name. usually not required, since the remote method name gets constructed based on the original method name. 🦋
defining methods example:
from sdbus import DbusInterfaceCommon, dbus_method
class ExampleInterface(DbusInterfaceCommon,
interface_name='org.example.my'
):
# Method that takes an integer and does not return anything
@dbus_method('u')
def close_notification(self, an_int: int) -> None:
raise NotImplementedError
# Method that does not take any arguments and returns a list of str
@dbus_method()
def get_capabilities(self) -> list[str]:
raise NotImplementedError
# Method that takes a dict of {str: str} and returns an int
@dbus_method('a{ss}')
def count_entries(self, a_dict: dict[str, str]) -> int:
raise NotImplementedError
calling methods example:
# Initialize the object
d = ExampleInterface(
service_name='org.example.test',
object_path='/',
)
d.close_notification(1234)
l = d.get_capabilities()
d.count_entries({'a': 'asdasdasd', 'b': 'hgterghead213d'})
@sdbus.dbus_property([property_signature[, flags[, property_name]]]) — define a D-Bus property. 💜
Property works exactly like the @property decorator would, no cap. Always use round brackets () even when you're not passing anything — yes, even then, bestie. 🙈
A read-only property? Just pass an empty D-Bus signature "" and you're done. And if anyone tries to assign to a read-only property, it will raise AttributeError. Periodt. 💅
Parameters
property_signature(str) — the D-Bus property signature. An empty signature""means it's a read-only property. Defaults to the empty signature"". You only need this for writable properties.flags(int) — tweaks the behaviour. Zero effect on remote connections. Defaults to0, which means no special behaviour at all. See Flags<#dbus-flags>.property_name(str) — explicitly set the remote property name. Usually you don't need this, because the remote property name gets constructed from the original method name anyway.
Defining properties example:
from sdbus import DbusInterfaceCommon, dbus_property
class ExampleInterface(DbusInterfaceCommon,
interface_name='org.example.myproperty'
):
# Property of int
@dbus_property('i')
def test_int(self) -> int:
raise NotImplementedError
# Property of str
@dbus_property('s')
def test_string(self) -> str:
raise NotImplementedError
Properties usage example:
# Initialize the object
d = ExampleInterface(
service_name='org.example.test',
object_path='/',
)
# Print the int
print(d.test_int)
# Assign new string
d.test_string = 'some_string'
# Print it
print(d.test_string)
- Index
<> - Module Index
<> - Search Page
<>
🌈 Asyncio quick start 🌈
🧚 Interface classes
Python-sdbus works by declaring interface classes. That's the whole vibe.
Interface classes for async IO should be derived from DbusInterfaceCommonAsync <#sdbus.DbusInterfaceCommonAsync>.
The class constructor takes the interface_name keyword to determine the D-Bus interface name for all the D-Bus elements you declared in the class body.
Example:
from sdbus import DbusInterfaceCommonAsync
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.myinterface'
):
...
Your interface class body should contain the definitions of methods, properties and signals, using decorators like dbus_method_async() <#sdbus.dbus_method_async>, dbus_property_async() <#sdbus.dbus_property_async> and dbus_signal_async() <#sdbus.dbus_signal_async>. Decorator stans, rise. ✨
Example:
from sdbus import (DbusInterfaceCommonAsync, dbus_method_async,
dbus_property_async, dbus_signal_async)
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.myinterface'
):
# Method that takes an integer and multiplies it by 2
@dbus_method_async('i', 'i')
async def double_int(self, an_int: int) -> None:
return an_int * 2
# Read only property of str
@dbus_property_async('s')
def read_string(self) -> int:
return 'Test'
# Signal with a list of strings
@dbus_signal_async('as')
def str_signal(self) -> list[str]:
raise NotImplementedError
🔮 Initiating proxy
DbusInterfaceCommonAsync <#sdbus.DbusInterfaceCommonAsync> gives you two methods for proxying remote objects.
DbusInterfaceCommonAsync.new_proxy() <#sdbus.DbusInterfaceCommonAsync.new_proxy> is a class method that bypasses the class __init__ and hands you back a proxy object. Skipping the line, iconic behaviour honestly.
DbusInterfaceCommonAsync._proxify() <#sdbus.DbusInterfaceCommonAsync._proxify> is the one you use inside your __init__ methods when your class is a proxy and nothing else.
Recommended move: make your proxy classes a subclass of the interface, like this:
from sdbus import DbusInterfaceCommonAsync
class ExampleInterface(...):
# Some interface class
...
class ExampleClient(ExampleInterface):
def __init__(self) -> None:
# Your client init can proxy to any object based on passed arguments.
self._proxify('org.example.test', '/')
Note: successfully initiating a proxy object does NOT guarantee that the D-Bus object actually exists. It's giving hopeful, not confirmed. 😭
👑 Serving objects
The DbusInterfaceCommonAsync.export_to_dbus() <#sdbus.DbusInterfaceCommonAsync.export_to_dbus> method exports your object to the D-Bus. Once you've called it, the object becomes visible on D-Bus for other processes to call. Main character moment fr fr.
Example using the ExampleInterface from before:
from sdbus import request_default_bus_name_async
loop = get_event_loop()
i = ExampleInterface()
async def start() -> None:
# Acquire a name on the bus
await request_default_bus_name_async('org.example.test')
# Start serving at / path
i.export_to_dbus('/')
loop.run_until_complete(start())
loop.run_forever()
🦋 Connection transparency
The interface objects are designed to be transparent about their connection status. What that means: if the object isn't proxied to a remote, the calls to your decorated methods still work in the local scope. Lowkey magical, highkey convenient. ✨
This is the call to a local object:
i = ExampleInterface()
async def test() -> None:
print(await i.double_int(5)) # Will print 10
And this is a call to a remote object at the 'org.example.test' service name and '/' path:
i = ExampleInterface.new_proxy('org.example.test', '/')
async def test() -> None:
print(await i.double_int(5)) # Will print 10
💜 Methods
Methods are async function calls wrapped with the dbus_method_async() <#sdbus.dbus_method_async> decorator. (Go peep the API reference for the decorator parameters, iykyk.)
Methods have to be async functions — if they're not, an AssertionError gets raised. No exceptions, well, actually, yes an exception. 💀
While method calls are async, there is an inherited timeout timer on every single method call.
To return an error to the caller, you need to raise an exception that has DbusFailedError <#sdbus.exceptions.DbusFailedError> as a base. Regular exceptions will not propagate. Sorry bestie, that's just the rules.
See Exceptions <>.
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_method_async
class ExampleInterface(...):
...
# Body of some class
# Method that takes a string
# and returns uppercase of that string
@dbus_method_async(
input_signature='s',
result_signature='s',
result_args_names=('uppercased', ) # This is optional but
# makes arguments have names in
# introspection data.
)
async def upper(self, str_to_up: str) -> str:
return str_to_up.upper()
Methods behave the exact same way Python methods would:
print(await example_object.upper('test')) # prints TEST
🎀 Properties
Properties are a single value that can be read and written. Simple. Elegant. Slay.
To declare a read-only property, you decorate a regular function with the dbus_property_async() <#sdbus.dbus_property_async> decorator.
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_property_async
class ExampleInterface(...):
...
# Body of some class
# Read only property. No setter defined.
@dbus_property_async('i')
def read_only_number(self) -> int:
return 10
To create a read/write property, you decorate the setter function with the setter attribute of your getter function. Yes, decorator-on-decorator energy. 💅
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_property_async
class ExampleInterface(...):
...
# Body of some class
# Read/write property. First define getter.
@dbus_property_async('s')
def read_write_str(self) -> str:
return self.s
# Now create setter. Method name does not matter.
@read_write_str.setter # Use the property setter method as decorator
def read_write_str_setter(self, new_str: str) -> None:
self.s = new_str
Properties are supposed to be lightweight. Make sure you don't block the event loop with a getter or a setter, bestie. Do not do that to us. 😭
Async properties do not behave the same way as the property() decorator does. Different girl, different rules.
To get the value of the property you can either directly await on the property, or use the get_async() method. (That one also needs to be awaited.)
To set a property, use the set_async() method.
Example:
...
# Somewhere in async function
# Assume we have example_object of class defined above
print(await example_object.read_write_str) # Print the value of read_write_str
...
# Set read_write_str to new value
await example_object.read_write_str.set_async('test')
🌸 Signals
To define a D-Bus signal, wrap a function with the dbus_signal_async() <#sdbus.dbus_signal_async> decorator.
The function is only used for type hints information. Recommended move: just put raise NotImplementedError in the body of the function and move on with your day.
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_signal_async
class ExampleInterface(...):
...
# Body of some class
@dbus_signal_async('s')
def name_changed(self) -> str:
raise NotImplementedError
To catch a signal, use an async for loop:
async for x in example_object.name_changed:
print(x)
Warning: if you're creating an asyncio task to listen on signals, make sure you bind it to a variable and keep it referenced — otherwise the garbage collector will destroy your task. Rude, but true. 💀
A signal can be emitted with the emit <#sdbus.DbusSignalAsync.emit> method.
Example:
example_object.name_changed.emit('test')
Signals can also be caught from multiple D-Bus objects using the catch_anywhere <#sdbus.DbusSignalAsync.catch_anywhere> method. The async iterator will yield the path of the object that emitted the signal, plus the signal data. Two-for-one, we love to see it. 🫶
catch_anywhere <#sdbus.DbusSignalAsync.catch_anywhere> can be called from the class, but in that case the service name must be provided.
Example:
async for path, x in ExampleInterface.name_changed.catch_anywhere('org.example.test'):
print(f"On {path} caught: {x}")
✨ Subclass Overrides ✨
Okay so listen 💖 if you're defining a subclass that overrides a declared D-Bus method or property, you cannot just vibe your way through it — you have to use the dbus_method_async_override() and dbus_property_async_override() decorators. That's the rule, bestie, no cap.
- An overridden property can decorate a new setter. Yes queen, you get to redecorate 💅
- Overridden methods should take the same number and type of arguments. Same shape, new energy ✨
Example, because we love a visual:
from sdbus import (dbus_method_async_override,
dbus_property_async_override)
# Some subclass
class SubclassInterface(...):
...
@dbus_method_async_override()
async def upper(self, str_to_up: str) -> str:
return 'Upper: ' + str_to_up.upper()
@dbus_property_async_override()
def str_prop(self) -> str:
return 'Test property' + self.s
# Setter needs to be decorated again to override
@str_prop.setter
def str_prop_setter(self, new_s: str) -> None:
self.s = new_s.upper()
🌈 Multiple interfaces 🌈
A single D-Bus object can absolutely serve multiple interfaces with different methods and properties. It's giving multitasking main character energy 👑
To pull this off, define multiple interface classes and then do a multiple inheritance on all the interfaces your object has. That's it, that's the trick 🦋
Example:
from sdbus import DbusInterfaceCommonAsync
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.myinterface'
):
@dbus_method_async('i', 'i')
async def double_int(self, an_int: int) -> None:
return an_int * 2
class TestInterface(DbusInterfaceCommonAsync,
interface_name='org.example.test'
):
@dbus_method_async('as', 's')
async def join_str(self, str_array: list[str]) -> str:
return ''.join(str_array)
class MultipleInterfaces(TestInterface, ExampleInterface):
...
So the MultipleInterfaces class will have both test_method and example_method, and they'll be wired to the correct interface names — org.example.myinterface and org.example.test respectively. Iconic. Periodt. 💖
🌸 Asyncio API 🌸
💜 Classes 💜
class sdbus.DbusInterfaceCommonAsync(interface_name)
The D-Bus async interface class, the blueprint, the moment 🫶 Your D-Bus methods and properties should be defined using the dbus_property_async(), dbus_signal_async(), and dbus_method_async() decorators.
Note: Do not forget to call super().__init__() in your derived classes' init calls, bestie 😭 it sets up important attributes and without it things just break.
Parameters
interface_name(str) — Sets the D-Bus interface name that will be used for all properties, methods and signals defined in the body of the class.serving_enabled(bool) — If set toTruethe interface will not be served on D-Bus. Mostly used for interfaces that sd-bus already provides such asorg.freedesktop.DBus.Peer.
async dbus_ping()
Pings the remote service using D-Bus. Super useful for testing whether your connection or the remote service is alive 🩺
Warning: This method ignores the particular object path, meaning it can NOT be used to test if an object exists. So don't come for it with that job, it said what it said 💀
async dbus_machine_id()
Returns the machine UUID of the D-Bus the object is connected to.
Returns — machine UUID
Return type — str
async dbus_introspect()
Get the D-Bus introspection XML. It is the user's responsibility to parse that data — we hand you the XML, you do the rest, that's the deal ✨
Returns — string with introspection XML
Return type — str
async properties_get_all_dict()
Get all object properties as a dictionary where the keys are member names and the values are property values. Equivalent to the GetAll method of the org.freedesktop.DBus.Properties interface, except the member names are automatically translated to Python names. (Internally it calls it for each interface used in the class definition. Efficient? We love to see it 🌟)
Parameters
on_unknown_member(str) — If an unknown D-Bus property was encountered, either raise an"error"(default),"ignore"the property, or"reuse"the D-Bus name for the member.
Returns — dictionary of properties
Return type — dict[str, Any]
properties_changed: tuple[str, dict[str, tuple[str, Any]], list[str]]
Signal when one of the object's properties changes 📢
sdbus.utils.parse.parse_properties_changed() can be used to transform this signal data into an easier-to-work-with dictionary. Very much a glow-up moment ✨
Signal data is:
- Interface name (str) — Name of the interface where the property changed.
- Changed properties (
dict[str, tuple[str, Any]]) — Dictionary whose keys are names of properties changed and whose values are variants of the new value. - Invalidated properties (
list[str]) — List of property names changed but no new value had been provided.
_proxify(bus, service_name, object_path)
Begin proxying to a remote D-Bus object. Gateway era 🚪
Parameters
service_name(str) — Remote object D-Bus connection name. For example, systemd usesorg.freedesktop.systemd1.object_path(str) — Remote object D-Bus path. Should be a forward slash separated path. Starting object is usually/. Example:/org/freedesktop/systemd/unit/dbus_2eservice.bus(SdBus) — Optional D-Bus connection object. If not passed, the default D-Bus will be used.
classmethod new_proxy(bus, service_name, object_path)
Create a new proxy object and bypass __init__. Sneaky, but make it fashion 💅
Parameters
service_name(str) — Remote object D-Bus connection name. For example, systemd usesorg.freedesktop.systemd1.object_path(str) — Remote object D-Bus path. Should be a forward slash separated path. Starting object is usually/. Example:/org/freedesktop/systemd/unit/dbus_2eservice.bus(SdBus) — Optional D-Bus connection object. If not passed, the default D-Bus will be used.
export_to_dbus(object_path, bus)
Your object will appear and become callable on D-Bus 🎉
Returns a handle that can either be used as a context manager to remove the object from D-Bus, or you can call the handle's .stop() method to remove the object from D-Bus.
Returns a handle that can be used to remove the object from D-Bus by either using it as a context manager or by calling the .stop() method of the handle.
with dbus_object.export_to_dbus("/"):
# dbus_object can be called from D-Bus inside this
# with block.
...
...
handle = dbus_object2.export_to_dbus("/")
# dbus_object2 can be called from D-Bus between these statements
handle.stop()
...
dbus_object3.export_to_dbus("/")
# dbus_object3 can be called from D-Bus until all references are
# dropped.
del dbus_object3
If the handle is discarded the object will remain exported until it gets deallocated. So don't just throw it away and act surprised, bestie 😭
Changed in version 0.12.0: Added a handle return.
Parameters
object_path(str) — Object path that it will be available at.bus(SdBus) — Optional D-Bus connection object. If not passed, the default D-Bus will be used.
Returns — Handle to control the export.
class sdbus.DbusObjectManagerInterfaceAsync(interface_name)
This class is almost identical to DbusInterfaceCommonAsync, but it implements the ObjectManager interface (https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager). Like the same girl but with extra responsibilities 👑
Example of serving objects with ObjectManager:
my_object_manager = DbusObjectManagerInterfaceAsync()
my_object_manager.export_to_dbus('/object/manager')
managed_object = DbusInterfaceCommonAsync()
my_object_manager.export_with_manager('/object/manager/example', managed_object)
async get_managed_objects()
Get the objects this object manager is managing 🗂️
sdbus.utils.parse.parse_get_managed_objects() can be used to make the returned data easier to work with.
Returns — Triple nested dictionary that contains all the object paths with their property values.
dict[ObjectPath, dict[InterfaceName,
dict[PropertyName, PropertyValue]]]
Return type — dict[str, dict[str, dict[str, Any]]]
interfaces_added: tuple[str, dict[str, dict[str, Any]]]
Signal when a new object is added, or an existing object gains a new interface ✨
sdbus.utils.parse.parse_interfaces_added() can be used to make the signal data easier to work with.
Signal data is:
- Object path (str) — Path to object that was added or modified.
- Object interfaces and properties (
dict[str, dict[str, Any]]]) —dict[InterfaceName, dict[PropertyName, PropertyValue]]
💖 interfaces_removed: tuple[str, list[str]] 💖
okay so this is the signal that goes off when an existing object — or an interface on an existing object — gets removed. ✨
If you want to make that signal data easier to work with, sdbus.utils.parse.parse_interfaces_removed() is literally right there for you, bestie. 🫶
The signal data is giving:
- Object path (
str) — path to the object that was removed or modified. - Interfaces list (
list[str]) — the interface names that were removed.
🦋 export_with_manager(object_path, object_to_export, bus) 🦋
Exports an object to D-Bus and emits a signal saying it was added. Two birds, one slay. 💅
ObjectManager must be exported first. No skipping ahead, that's the rule. 🎀
The path should be a subpath of wherever ObjectManager was exported. Example: if ObjectManager was exported to /object/manager, then the managed object can be exported at /object/manager/test.
ObjectManager will keep the reference to the object. (Possessive? Yes. Supportive? Also yes. 👑)
It returns a handle you can use to remove the object from D-Bus and drop the reference to it — either by using it as a context manager, or by calling the .stop() method on the handle. The signal gets emitted once the object stops being exported.
manager = DbusObjectManagerInterfaceAsync()
manager.export_to_dbus('/object/manager')
with manager.export_with_manager("/object/manager/example", dbus_object):
# dbus_object can be called from D-Bus inside this
# with block.
...
# Removed signal will be emitted once the with block exits
...
handle = manager.export_with_manager("/object/manager/example", dbus_object2)
# dbus_object2 can be called from D-Bus between these statements
handle.stop()
# Removed signal will be emitted once the .stop() method is called
If the handle gets discarded, the object stays exported until it gets removed from the manager with remove_managed_object() and the object gets deallocated. so don't just ghost your handles, okay? 😭
Changed in version 0.12.0: added a handle return.
Parameters
object_path(str) — object path that it will be available at.object_to_export(DbusInterfaceCommonAsync) — object to export to D-Bus.bus(SdBus) — optional D-Bus connection object. If it isn't passed, the default D-Bus will be used.
Raises RuntimeError — ObjectManager was not exported.
Returns — handle to control the export.
🧚 remove_managed_object(managed_object) 🧚
Emits a signal that the object was removed.
Releases the reference to the object.
Caution: the object will still be accessible over D-Bus until all references to it get removed. 👀 Like, it's leaving, but it hasn't left yet.
Parameters
managed_object(DbusInterfaceCommonAsync) — object to remove fromObjectManager.
Raises
RuntimeError—ObjectManagerwas not exported.KeyError— the object you passed is not managed byObjectManager.
🎀 Decorators 🎀
✨ @sdbus.dbus_method_async([input_signature[, result_signature[, flags[, result_args_names[, input_args_names[, method_name]]]]]]) ✨
Defines a method.
The underlying function must be a coroutine function. fr fr.
Parameters
input_signature(str) — D-Bus input signature. Defaults to"", meaning the method takes no arguments. Required if you intend to connect to a remote object.result_signature(str) — D-Bus result signature. Defaults to"", meaning the method returns an empty reply on success. Required if you intend to serve the object.flags(int) —
modifies behavior. No effect on remote connections. Defaults to 0, meaning no special behavior.
See Flags.
result_args_names(Sequence[str]) —
sequence of result argument names.
These names will show up in introspection data but otherwise have no effect.
The sequence can be a list, tuple, etc... The number of elements in the sequence should match the number of result arguments, otherwise SdBusLibraryError will be raised.
Defaults to result arguments being nameless.
input_args_names(Sequence[str]) —
sequence of input argument names.
These names will show up in introspection data but otherwise have no effect.
The sequence can be a list, tuple, etc... The number of elements in the sequence should match the number of result arguments, otherwise RuntimeError will be raised.
If result_args_names has been passed then Python function argument names will be used, otherwise input arguments will be nameless.
method_name(str) — force a specific D-Bus method name instead of having it based on the Python function name.
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_method_async
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.test'
):
# Method that takes a string
# and returns uppercase of that string
@dbus_method_async(
input_signature='s',
result_signature='s',
result_args_names=('uppercased', ) # This is optional but
# makes arguments have names in
# introspection data.
)
async def upper(self, str_to_up: str) -> str:
return str_to_up.upper()
🌸 @sdbus.dbus_property_async(property_signature[, flags[, property_name]]) 🌸
Declares a D-Bus property.
The underlying function has to be a regular def function. (Not async, bestie — read the room. 💅)
The property will be read-only or read/write based on whether a setter was declared.
Warning: properties are supposed to be lightweight to get or set. Make sure the property getter or setter doesn't do heavy IO or computation, because that will block other methods or properties. Do not put your whole main character monologue in a getter. 😭
Parameters
property_signature(str) — property D-Bus signature. Has to be a single type or container.flags(int) —
modifies behavior. No effect on remote connections. Defaults to 0, meaning no special behavior.
See Flags.
property_name(str) — force a specific property name instead of constructing it based on the Python function name.
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_property_async
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.test'
):
def __init__(self) -> None:
# This is just a generic init
self.i = 12345
self.s = 'test'
# Read only property. No setter defined.
@dbus_property_async('i')
def read_only_number(self) -> int:
return self.i
# Read/write property. First define getter.
@dbus_property_async('s')
def read_write_str(self) -> str:
return self.s
# Now create setter. Method name does not matter.
@read_write_str.setter # Use the property setter method as decorator
def read_write_str_setter(self, new_str: str) -> None:
self.s = new_str
👑 class sdbus.DbusPropertyAsync 👑
Properties have the following methods:
@setter(set_function)— defines the setter function. This makes the property read/write instead of read-only.
See the example for how to use it.
@setter_private(set_function)— defines the private setter function. The setter can be called locally, but the property will be read-only from D-Bus.
Calling the setter locally will emit a properties_changed signal to D-Bus.
Changed in version 0.12.0: can now be used in overrides.
async get_async()— get the property value.
The property can also be directly awaited instead of calling this method. iconic behaviour honestly ✨
async set_async(new_value)— set the property value.
🌈 @sdbus.dbus_signal_async([signal_signature[, signal_args_names[, flags[, signal_name]]]]) 🌈
Defines a D-Bus signal.
The underlying function's return type hint is used for signal type hints.
Parameters
signal_signature(str) — signal D-Bus signature. Defaults to an empty signal.signal_args_names(Sequence[str]) —
sequence of signal argument names.
These names will show up in introspection data but otherwise have no effect.
The sequence can be a list, tuple, etc... The number of elements in the sequence should match the number of result arguments, otherwise RuntimeError will be raised.
Defaults to result arguments being nameless.
flags(int) —- modifies behavior. No effect on remote connections. Defaults to
0meaning no special behavior.
See Flags.
signal_name(str) — forces a specific signal name instead of letting it be based on the Python function name.
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_signal_async
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.signal'
):
@dbus_signal_async('s')
def name_changed(self) -> str:
raise NotImplementedError
🫧 class sdbus.DbusSignalAsync
ok so signals come with these methods, bestie:
catch()— catches D-Bus signals using the async generator for loop:async for x in something.some_signal.catch():. This is the main way to await new events, fr. Both remote and local objects operate the same way. Signal objects can also be async iterated directly:async for x in something.some_signal.catch_anywhere(service_name, bus)— catches a signal independent of path. Yields a tuple of the path of the object that emitted the signal, and the signal data. Like this:async for path, data in something.some_signal.catch_anywhere():. This method can be called from both a proxy object and a class. However, it cannot be called on local objects and will raiseNotImplementedError. 👀
Parameters:
service_name(str) — the service name that the signals belong to. Required if called from a class. When called from a proxy object, the service name of the proxy will be used.bus(str) — optional D-Bus connection object. If not passed, when called from a proxy the bus connected to the proxy will be used, or when called from a class the default bus will be used.
emit(args)— emits a new signal withargsdata.
🌸 @sdbus.dbus_method_async_override
Overrides the method, periodt. The method name should match the super class method name that you want to override. The new method should take the same arguments. You must add round brackets to the decorator. (yes, actually add them, do not skip this 💀)
Example:
from sdbus import (DbusInterfaceCommonAsync, dbus_method_async
dbus_method_async_override)
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.test'
):
# Original call
@dbus_method_async('s', 's')
async def upper(self, str_to_up: str) -> str:
return str_to_up.upper()
class ExampleOverride(ExampleInterface):
@dbus_method_async_override()
async def upper(self, str_to_up: str) -> str:
return 'Upper: ' + str_to_up.upper()
💅 @sdbus.dbus_property_async_override
Overrides a property. You must add round brackets to the decorator, no exceptions.
Example:
from sdbus import (DbusInterfaceCommonAsync, dbus_property_async
dbus_property_async_override)
class ExampleInterface(DbusInterfaceCommonAsync,
interface_name='org.example.test'
):
def __init__(self) -> None:
self.s = 'aaaaaaaaa'
# Original property
@dbus_property_async('s')
def str_prop(self) -> str:
return self.s
@str_prop.setter
def str_prop_setter(self, new_s: str) -> None:
self.s = new_s
class ExampleOverride(ExampleInterface):
@dbus_property_async_override()
def str_prop(self) -> str:
return 'Test property' + self.s
# Setter needs to be decorated again to override
@str_prop.setter
def str_prop_setter(self, new_s: str) -> None:
self.s = new_s.upper()
🦋 Asyncio advanced topics
✨ Signals without data
D-Bus lowkey lets signals carry no data at all. Those signals have the type signature "" — yes, the empty string. it's giving nothing, literally.
To emit such signals, the emit must be explicitly called with None. (not empty, not nothing — None. don't fight it 🫶)
Example of an empty signal:
from asyncio import new_event_loop
from sdbus import DbusInterfaceCommonAsync, dbus_signal_async
class ExampleInterface(
DbusInterfaceCommonAsync,
interface_name="org.example.signal"
):
@dbus_signal_async("")
def name_invalidated(self) -> None:
raise NotImplementedError
test_object = ExampleInterface()
async def emit_empty_signal() -> None:
test_object.export_to_dbus("/")
test_object.name_invalidated.emit(None)
loop = new_event_loop()
loop.run_until_complete(emit_empty_signal())
💀 Exceptions
🎀 Error name bound exceptions
These exceptions are bound to specific D-Bus error names. For example, DbusFailedError is bound to the org.freedesktop.DBus.Error.Failed error name.
This means that if the remote object sends an error message with this error name, Python will receive this exception. 👑
When raised in a method callback, an error message will be sent back to the caller.
See list of error exceptions.
🌈 New error bound exceptions
If you want to create a new error bound exception, you should subclass it from DbusFailedError and provide a unique dbus_error_name attribute in the exception body definition.
Example:
class DbusExampleError(DbusFailedError):
dbus_error_name = 'org.example.Error'
If dbus_error_name is not unique, a ValueError will be raised.
Defining an exception will automatically bind incoming error messages to this new exception.
Existing exceptions can be manually binded using the map_exception_to_dbus_error() function.
🍓 Python built-in exceptions
All Python built-in exceptions are mapped to D-Bus errors.
The D-Bus error name is created by appending org.python.Error. to the exception name.
For example, AssertionError is bound to the org.python.Error.AssertionError name.
💜 Functions
sdbus.exceptions.map_exception_to_dbus_error(exception, dbus_error_name)— maps an exception to a D-Bus error. The error name must be unique.
Parameters:
exception(Type[Exception]) — the exception to bind.dbus_error_name(str) — the D-Bus error name to bind to.
🔥 Other exceptions
exception sdbus.exceptions.SdBusBaseError— the base exception for all exceptions defined in sdbus.exception sdbus.exceptions.SdBusUnmappedMessageError— message error that is unmapped. Theexceptionsargument is a tuple of error name and error message.exception sdbus.exceptions.SdBusLibraryError— thesd-buslibrary returned an error. The exception message contains the line number and the error name.
🌸 Name request exceptions
These exceptions will be raised if an error related to ownership of D-Bus names occurs when calling request_default_bus_name_async() or request_default_bus_name().
exception sdbus.exceptions.SdBusRequestNameError— common base exception for any name ownership error.exception sdbus.exceptions.SdBusRequestNameInQueueError— someone already owns the name, but the request has been placed in queue.exception sdbus.exceptions.SdBusRequestNameExistsError— someone already owns the name.exception sdbus.exceptions.SdBusRequestNameAlreadyOwnerError— the caller already owns the name.
👑 Error name exception list
exception sdbus.exceptions.DbusFailedError— generic failure exception. Recommended to subclass to create a new exception.dbus_error_name: str = org.freedesktop.DBus.Error.Failedexception sdbus.exceptions.DbusNoMemoryError— remote object is out of memory.dbus_error_name: str = org.freedesktop.DBus.Error.NoMemoryexception sdbus.exceptions.DbusServiceUnknownError— no service with such name exists. Probably should only be raised by the bus daemon.dbus_error_name: str = org.freedesktop.DBus.Error.ServiceUnknownexception sdbus.exceptions.DbusNameHasNoOwnerError— no process owns the name you called. Probably should only be raised by the bus daemon.dbus_error_name: str = org.freedesktop.DBus.Error.NameHasNoOwnerexception sdbus.exceptions.DbusNoReplyError— timeout on reply.dbus_error_name: str = org.freedesktop.DBus.Error.NoReplyexception sdbus.exceptions.DbusIOError— input/output error.dbus_error_name: str = org.freedesktop.DBus.Error.IOErrorexception sdbus.exceptions.DbusBadAddressError— bad address.dbus_error_name: str = org.freedesktop.DBus.Error.BadAddressexception sdbus.exceptions.DbusNotSupportedError— something is unsupported on this platform.dbus_error_name: str = org.freedesktop.DBus.Error.NotSupportedexception sdbus.exceptions.DbusLimitsExceededError— some resource was exhausted (for example, file descriptors).dbus_error_name: str = org.freedesktop.DBus.Error.LimitsExceededexception sdbus.exceptions.DbusAccessDeniedError— the caller does not have enough privileges.dbus_error_name: str = org.freedesktop.DBus.Error.AccessDeniedexception sdbus.exceptions.DbusAuthFailedError— authentication failed.dbus_error_name: str = org.freedesktop.DBus.Error.AuthFailedexception sdbus.exceptions.DbusNoServerError— unable to connect to bus.dbus_error_name: str = org.freedesktop.DBus.Error.NoServerexception sdbus.exceptions.DbusTimeoutError— socket timeout. This is different fromDbusNoReplyErrorbecause here the connection to the bus timed out, not the remote object failing to reply.dbus_error_name: str = org.freedesktop.DBus.Error.Timeout
💜 sdbus.exceptions.DbusNoNetworkError
No network access, bestie. 😭
You bump into this one when you're running D-Bus over TCP or SSH.
dbus_error_name: str = org.freedesktop.DBus.Error.NoNetwork
🦋 sdbus.exceptions.DbusAddressInUseError
The address is already taken — someone else got there first, no cap. 💅
dbus_error_name: str = org.freedesktop.DBus.Error.AddressInUse
🎀 sdbus.exceptions.DbusDisconnectedError
You got disconnected from the bus. it's giving ghosted fr fr. 🫶
dbus_error_name: str = org.freedesktop.DBus.Error.Disconnected
✨ sdbus.exceptions.DbusInvalidArgsError
The method call args are invalid — wrong vibes, wrong types. 🙈
dbus_error_name: str = org.freedesktop.DBus.Error.InvalidArgs
🌸 sdbus.exceptions.DbusFileNotFoundError
File not found. literally cannot find it. 💀
dbus_error_name: str = org.freedesktop.DBus.Error.FileNotFound
🍓 sdbus.exceptions.DbusFileExistsError
Generic failure exception. 😭
Recommended to subclass to create a new exception.
dbus_error_name: str = org.freedesktop.DBus.Error.FileExists
👑 sdbus.exceptions.DbusUnknownMethodError
Unknown D-Bus method — that method is not on the menu, periodt.
dbus_error_name: str = org.freedesktop.DBus.Error.UnknownMethod
🌈 sdbus.exceptions.DbusUnknownObjectError
Unknown D-Bus object. who?? never met her. 💅
dbus_error_name: str = org.freedesktop.DBus.Error.UnknownObject
⭐ sdbus.exceptions.DbusUnknownInterfaceError
Unknown D-Bus interface.
dbus_error_name: str = org.freedesktop.DBus.Error.UnknownInterface
🧚 sdbus.exceptions.DbusUnknownPropertyError
Unknown D-Bus property.
dbus_error_name: str = org.freedesktop.DBus.Error.UnknownProperty
💖 sdbus.exceptions.DbusPropertyReadOnlyError
That D-Bus property is read only — look with your eyes, not with your hands. 🙅
dbus_error_name: str = org.freedesktop.DBus.Error.PropertyReadOnly
🦋 sdbus.exceptions.DbusUnixProcessIdUnknownError
PID does not exists. (their words, not mine 😭)
dbus_error_name: str = org.freedesktop.DBus.Error.UnixProcessIdUnknown
🎀 sdbus.exceptions.DbusInvalidSignatureError
Invalid D-Bus type signature. the signature did NOT slay. 💀
dbus_error_name: str = org.freedesktop.DBus.Error.InvalidSignature
✨ sdbus.exceptions.DbusInvalidFileContentError
Invalid file content.
dbus_error_name: str = org.freedesktop.DBus.Error.InvalidFileContent
🌸 sdbus.exceptions.DbusInconsistentMessageError
D-Bus message is malformed — it came out wrong and everyone can tell. 😭
dbus_error_name: str = org.freedesktop.DBus.Error.InconsistentMessage
🍓 sdbus.exceptions.DbusMatchRuleNotFound
Match rule does not exist.
dbus_error_name: str = org.freedesktop.DBus.Error.MatchRuleNotFound
👑 sdbus.exceptions.DbusMatchRuleInvalidError
Match rule is invalid.
dbus_error_name: str = org.freedesktop.DBus.Error.MatchRuleInvalid
🌈 sdbus.exceptions.DbusInteractiveAuthorizationRequiredError
Requires interactive authorization — yes bestie, you gotta actually show up and approve it. 🙋
dbus_error_name: str = org.freedesktop.DBus.Error.InteractiveAuthorizationRequired
🧰 Utilities 🧰
🔍 Parsing utilities
Parse unweildy D-Bus structures in to Python native objects and names. (yes "unweildy" — we keep it real around here 🫶) Available under sdbus.utils.parse subpackage.
sdbus.utils.parse.parse_properties_changed(interface:
InterfacesInputElements, properties_changed_data:
DBUS_PROPERTIES_CHANGED_TYPING, on_unknown_member: OnUnknownMember =
'error') -> dict[str, Any]
Parse data from properties_changed <#sdbus.DbusInterfaceCommonAsync.properties_changed> signal.
Parses changed properties from a single D-Bus object. The object's interface class must be known in advance and passed as a first argument.
Member names will be translated to python defined names. Invalidated properties will have a value of None.
Parameters
- interface — Takes either D-Bus interface class or its object.
- properties_changed_data — Tuple caught from signal.
- on_unknown_member — If an unknown D-Bus property was encountered either raise an
"error"(default),"ignore"the property or"reuse"the D-Bus name for the member.
Returns
Dictionary of changed properties with keys translated to python names. Invalidated properties will have value of None.
sdbus.utils.parse.parse_interfaces_added(interfaces: InterfacesInput,
interfaces_added_data: tuple[str, dict[str, dict[str, Any]]],
on_unknown_interface: OnUnknownInterface = 'error', on_unknown_member:
OnUnknownMember = 'error', *, use_interface_subsets: bool = False) ->
tuple[str, InterfacesBaseTypes | None, dict[str, Any]]
Parse data from interfaces_added <#sdbus.DbusObjectManagerInterfaceAsync.interfaces_added> signal.
Takes the possible interface classes and the signal data. Returns the path of new object, the class of the added object (if it matched one of passed interface classes) and the dictionary of python named properties and their values.
The passed interfaces can be async or blocking, the class or an instantiated object, a single item or an iterable of interfaces.
Parameters
- interfaces — Possible interfaces that were added.
- interfaces_added_data — Tuple caught from signal.
- on_unknown_interface — If an unknown D-Bus interface was encountered either raise an
"error"(default) or return"none"instead of interface class. - on_unknown_member — If an unknown D-Bus property was encountered either raise an
"error"(default),"ignore"the property or"reuse"the D-Bus name for the member. - use_interface_subsets — Use the subset of interfaces as a valid match. For example, the class that implements
org.example.foowould be matched with an data consising of bothorg.example.fooandorg.example.bar. The classes implementing more interfaces will have higher priority over the ones implementing fewer.
Returns
Path of new added object, object's class (or None) and dictionary of python translated members and their values.
sdbus.utils.parse.parse_interfaces_removed(interfaces: InterfacesInput,
interfaces_removed_data: tuple[str, list[str]], on_unknown_interface:
OnUnknownInterface = 'error', *, use_interface_subsets: bool = False) ->
tuple[str, InterfacesBaseTypes | None]
Parse data from interfaces_added <#sdbus.DbusObjectManagerInterfaceAsync.interfaces_removed> signal. (yes it says interfaces_added here, we are not editing it, iykyk 😭)
Takes the possible interface classes and the signal data. Returns the path and the matched class of removed object. (if it matched one of passed interface classes)
The passed interfaces can be async or blocking, the class or an instantiated object, a single item or an iterable of interfaces.
Parameters
- interfaces — Possible interfaces that were removed.
- interfaces_added_data — Tuple caught from signal.
- on_unknown_member — If an unknown D-Bus interface was encountered either raise an
"error"(default) or return"none"instead of interface class. - use_interface_subsets — Use the subset of interfaces as a valid match. For example, the class that implements
org.example.foowould be matched with an data consising of bothorg.example.fooandorg.example.bar. The classes implementing more interfaces will have higher priority over the ones implementing fewer.
Returns
Path of removed object and object's class (or None).
sdbus.utils.parse.parse_get_managed_objects(interfaces: InterfacesInput,
managed_objects_data: dict[str, dict[str, dict[str, Any]]],
on_unknown_interface: OnUnknownInterface = 'error', on_unknown_member:
OnUnknownMember = 'error', *, use_interface_subsets: bool = False) ->
ParseGetManaged
Parse data from get_managed_objects <#sdbus.DbusObjectManagerInterfaceAsync.get_managed_objects> call.
Takes the possible interface classes and the method's returned data. Returns a dictionary where keys a paths of the managed objects and value is a tuple of class of the object and dictionary of its python named properties and their values.
The passed interfaces can be async or blocking, the class or an instantiated object, a single item or an iterable of interfaces.
Parameters
- interfaces — Possible interfaces of the managed objects.
- managed_objects_data — Data returned by
get_managed_objectscall. - on_unknown_interface — If an unknown D-Bus interface was encountered either raise an
"error"(default) or return"none"instead of interface class. - on_unknown_member — If an unknown D-Bus property was encountered either raise an
"error"(default),"ignore"the property or"reuse"the D-Bus name for the member. - use_interface_subsets — Use the subset of interfaces as a valid match. For example, the class that implements
org.example.foowould be matched with an data consising of bothorg.example.fooandorg.example.bar. The classes implementing more interfaces will have higher priority over the ones implementing fewer.
Returns
Dictionary where keys are paths and values are tuples of managed objects classes and their properties data.
New in version 0.12.0. ⭐
🔎 Inspect utilities
Inspect D-Bus objects and retrieve their D-Bus related attributes such as D-Bus object paths and etc... Available under sdbus.utils.inspect subpackage. 👀
sdbus.utils.inspect.inspect_dbus_bus(obj: DbusInterfaceBase |
DbusInterfaceBaseAsync) -> SdBus | None
Return D-Bus bus used by the object.
- If called on D-Bus proxies or exported local D-Bus objects returns bus object.
- If called on local D-Bus objects that had not been exported returns
None. - If called on an object that is unrelated to D-Bus raises
TypeError.
Parameters
- obj — Object to inspect.
D-Bus bus object, bestie. That's the return value right there.
New in version 0.14.1.
sdbus.utils.inspect.inspect_dbus_path(obj: DbusInterfaceBase | DbusInterfaceBaseAsync, bus: SdBus | None = None) -> str
okay so this one hands you back the D-Bus path of an object. 🛣️
- If you call it on a D-Bus proxy, you get the path of the proxied object. simple as that.
- If you call it on a local D-Bus object, you get the exported D-Bus path. But heads up — if the object isn't exported, it raises
LookupError. 💀 - If you call it on an object that's just... not related to D-Bus at all?
TypeError. wrong vibe entirely. 🙈
The object's path gets inspected in the context of the given bus, and if the object is attached to a different bus, LookupError gets raised. If the bus argument isn't given, or is None, then it checks against the default bus. no cap.
Parameters
obj— Object to inspect.bus— Bus to inspect against. If not given or isNonethe default bus will be used.
Returns
D-Bus path of the object.
New in version 0.13.0.
✨ Examples ✨
🧚 Asyncio client and server 🧚
In this example we create a simple example server and client. iconic behavior.
There are 3 files:
example_interface.py— File that contains the interface definition.example_server.py— Server.example_client.py— Client.
example_interface.py file:
from sdbus import (DbusInterfaceCommonAsync, dbus_method_async,
dbus_property_async, dbus_signal_async)
# This is file only contains interface definition for easy import
# in server and client files
class ExampleInterface(
DbusInterfaceCommonAsync,
interface_name='org.example.interface'
):
@dbus_method_async(
input_signature='s',
result_signature='s',
)
async def upper(self, string: str) -> str:
return string.upper()
@dbus_property_async(
property_signature='s',
)
def hello_world(self) -> str:
return 'Hello, World!'
@dbus_signal_async(
signal_signature='i'
)
def clock(self) -> int:
raise NotImplementedError
example_server.py file:
from asyncio import get_event_loop, sleep
from random import randint
from time import time
from example_interface import ExampleInterface
from sdbus import request_default_bus_name_async
loop = get_event_loop()
export_object = ExampleInterface()
async def clock() -> None:
"""
This coroutine will sleep a random time and emit a signal with current clock
"""
while True:
await sleep(randint(2, 7)) # Sleep a random time
current_time = int(time()) # The interface we defined uses integers
export_object.clock.emit(current_time)
async def startup() -> None:
"""Perform async startup actions"""
# Acquire a known name on the bus
# Clients will use that name to address to this server
await request_default_bus_name_async('org.example.test')
# Export the object to D-Bus
export_object.export_to_dbus('/')
loop.run_until_complete(startup())
task_clock = loop.create_task(clock())
loop.run_forever()
example_client.py file:
from asyncio import get_event_loop
from example_interface import ExampleInterface
# Create a new proxy object
example_object = ExampleInterface.new_proxy('org.example.test', '/')
async def print_clock() -> None:
# Use async for loop to print clock signals we receive
async for x in example_object.clock:
print('Got clock: ', x)
async def call_upper() -> None:
s = 'test string'
s_after = await example_object.upper(s)
print('Initial string: ', s)
print('After call: ', s_after)
async def get_hello_world() -> None:
print('Remote property: ', await example_object.hello_world)
loop = get_event_loop()
# Always binds your tasks to a variable
task_upper = loop.create_task(call_upper())
task_clock = loop.create_task(print_clock())
task_hello_world = loop.create_task(get_hello_world())
loop.run_forever()
Start server before client. python example_server.py 🫶
In separated terminal start client. python example_client.py
Use CTRL-C to close client and server.
You can also use ExampleInterface as a local object:
from asyncio import run
from example_interface import ExampleInterface
example_object = ExampleInterface()
async def test() -> None:
print(await example_object.upper('test'))
print(await example_object.hello_world)
run(test())
📦 Interfaces repository 📦
python-sdbus includes two namespace packages sdbus_async and sdbus_block which are used for proxies. ✨
For example, D-Bus daemon interface (which comes by default) can be found under sdbus_async.dbus_daemon for async binds and sdbus_block.dbus_daemon for blocking binds. iykyk.
👑 Known proxies 👑
🌈 D-Bus daemon interface 🌈
class sdbus_async.dbus_daemon.FreedesktopDbus(bus: SdBus | None = None)
D-Bus daemon. 🦋
This is the D-Bus daemon interface. Used for querying D-Bus state.
D-Bus interface object path and service name is predetermined. (at 'org.freedesktop.DBus', '/org/freedesktop/DBus')
Parameters
bus(SdBus) — Optional D-Bus connection. If not passed the default D-Bus will be used.
async get_connection_pid(service_name: str) -> int
D-Bus Method
Get process ID that owns a specified name.
Parameters
service_name(str) — Service name to query.
Returns
PID of name owner
Raises DbusNameHasNoOwnerError (#sdbus.exceptions.DbusNameHasNoOwnerError) — Nobody owns that name 😭
async get_connection_uid(service_name: str) -> int
D-Bus Method
Get process user ID that owns a specified name.
Parameters
service_name(str) — Service name to query.
Returns
User ID of name owner
Raises DbusNameHasNoOwnerError (#sdbus.exceptions.DbusNameHasNoOwnerError) — Nobody owns that name 😭
async get_id() -> str
D-Bus Method
Returns machine id where bus is run. (stored in /etc/machine-id)
Returns
Machine id
async get_name_owner(service_name: str) -> str
D-Bus Method
Returns unique bus name (i.e. ':1.94') for given service name.
Parameters
service_name(str) — Service name to query.
Returns
Unique bus name.
Raises DbusNameHasNoOwnerError (#sdbus.exceptions.DbusNameHasNoOwnerError) — Nobody owns that name 😭
async list_activatable_names() -> list[str]
D-Bus Method
Lists all activatable services names.
Returns
List of all names.
async list_names() -> list[str]
D-Bus Method
List all services and connections currently of the bus.
Returns
List of all current names.
async name_has_owner(service_name: str) -> bool
D-Bus Method
Return True if someone already owns the name, False if nobody does.
Parameters
service_name(str) — Service name to query.
Returns
Is the name owned?
async start_service_by_name(service_name: str, flags: int = 0) -> int
D-Bus Method
Starts a specified service.
Flags parameter is not used currently and should be omitted or set to 0. 🚩
Parameters
service_name(str) — Service name to start.flags(int) — Not used. Omit or pass0.
Returns
1 on success, 2 if already started.
features
D-Bus property
Python type: list[str]
D-Bus type: as
List of D-Bus daemon features.
Features include:
'AppArmor'— Messages filtered by AppArmor on this bus.'HeaderFiltering'— Messages are filtered if they have incorrect header fields.'SELinux'— Messages filtered by SELinux on this bus.'SystemdActivation'— services activated by systemd if their.servicefile specifies a D-Bus name.
interfaces
D-Bus property
Python type: list[str]
D-Bus type: as
Extra D-Bus daemon interfaces
name_acquired
D-Bus signal
Python type: str
D-Bus type: s
Signal when current process acquires a bus name. 👑
name_lost
D-Bus signal
Python type: str
D-Bus type: s
Signal when current process loses a bus name. 💀
name_owner_changed
D-Bus signal
Python type: tuple[str, str, str]
D-Bus type: sss
Signal when some name on a bus changes owner.
Is a tuple of:
- The name that acquired or lost
- Old owner (by unique bus name) or empty string if no one owned it
- New owner (by unique bus name) or empty string if no one owns it now
This list contains the known python-sdbus interface collections:
- D-Bus daemon interface. Built-in.
- Notifications
https://github.com/python-sdbus/python-sdbus-notifications - NetworkManager
https://github.com/python-sdbus/python-sdbus-networkmanager - Secrets
https://github.com/python-sdbus/python-sdbus-secrets
✨ Interface code generator ✨
Okay so python-sdbus can literally generate interface code from D-Bus introspection XML — either from a file or a live object on D-Bus. Right now async interface code is generated by default. If you want blocking interfaces, just pass the --block option. 💅
Running the code generator requires Jinja to be installed (link: https://palletsprojects.com/projects/jinja/). 🎀
⚠️ Warning: Do NOT send the generator result to exec() function. Interface code MUST be inspected before running. Like, fr fr, don't just run random generated code, bestie. 😭
The generated interface code will be syntactically correct but NOT stylistically. It is recommended running a code formatter on the generated code. (for example black) ✨
Generating from XML files
To run generator on files (such as found under /usr/share/dbus-1/interfaces/ folder) execute the sdbus module with gen-from-file first argument and file paths to introspection XML files:
python -m sdbus gen-from-file /usr/share/dbus-1/interfaces/org.gnome.Shell.Screenshot.xml
The generated interface code will be printed in to stdout. You can use shell redirection > to save it in to file. 🌸
Multiple interface files can be passed which generates a file containing multiple interfaces.
Generating from run-time introspection
To run generator on some service on the D-Bus execute the sdbus module with gen-from-connection first argument, the service connection name as second and one or more object paths:
python -m sdbus gen-from-connection org.freedesktop.systemd1 /org/freedesktop/systemd1
The generated interface code will be printed in to stdout. You can use shell redirection > to save it in to file.
Multiple object paths can be passed which generates a file containing all interfaces encountered in the objects.
Pass --system option to use system bus instead of session bus. 🦋
Renaming interfaces and members
New in version 0.13.0.
Some interface and member names might conflict with Python keywords when converted from D-Bus introspection to Python code by generator. The CLI interface allow to override the particular interface and member names using the --select-* and --set-name options. The selector options move the cursor to a particular interface and member.
Available override options:
--set-name— Sets the name of currently selected element as it would be in generated Python code. Can be used if either interface or member is selected.--select-interface— Selects the interface using its D-Bus name.--select-method— Selects the method using its D-Bus name. An interface must be selected first.--select-property— Selects the property using its D-Bus name. An interface must be selected first.--select-signal— Selects the signal using its D-Bus name. An interface must be selected first.
For example, an org.example.Interface interface has a property called Class. When automatically converted the name will become class which is a reserved Python keyword. 😱
Using these CLI options it is possible to override the name of the property and class:
python -m sdbus gen-from-file \
org.example.interface.xml \
--select-interface org.example.Interface \
--set-name Example \
--select-property Class \
--set-name example_class
This will generate following Python code:
class Example:
@dbus_property_async(
property_signature="s",
)
def example_class(self) -> str:
raise NotImplementedError
Autodoc extensions
Python-sdbus has an extension for Sphinx autodoc that can document D-Bus interfaces. 🦋
To use it include "sdbus.autodoc" extension in your conf.py file.
extensions = ['sdbus.autodoc']
The extension can document interface class bodies. For example, python-sdbus-networkmanager (link: https://github.com/python-sdbus/python-sdbus-networkmanager) uses it to document the classes.
.. autoclass:: sdbus_async.networkmanager.NetworkManagerDeviceBluetoothInterfaceAsync
:members:
⚠️ Warning: Autodoc extension is early in development and has multiple issues. For example, the inheritance :inherited-members: does not work on the D-Bus elements.
Writing docstrings
The D-Bus methods should be documented same way as the regular function would. See Sphinx documentation on possible fields (link: https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#info-field-lists)
Example docstring for a D-Bus method:
@dbus_method_async('s', method_name='GetConnectionUnixProcessID')
async def get_connection_pid(self, service_name: str) -> int:
"""Get process ID that owns a specified name.
:param service_name: Service name to query.
:return: PID of name owner
:raises DbusNameHasNoOwnerError: Nobody owns that name
"""
raise NotImplementedError
D-Bus properties and signals will be annotated with type taken from the stub function.
@dbus_property_async('as')
def features(self) -> list[str]:
"""List of D-Bus daemon features.
Features include:
* 'AppArmor' - Messages filtered by AppArmor on this bus.
* 'HeaderFiltering' - Messages are filtered if they have incorrect \
header fields.
* 'SELinux' - Messages filtered by SELinux on this bus.
* 'SystemdActivation' - services activated by systemd if their \
.service file specifies a D-Bus name.
"""
raise NotImplementedError
No parameters are supported at the moment for properties and signals.
Unit testing
Python-sdbus provides several utilities to enable unit testing.
class sdbus.unittest.IsolatedDbusTestCase
Extension of unittest.IsolatedAsyncioTestCase (link: https://docs.python.org/3/library/unittest.html#unittest.IsolatedAsyncioTestCase) from standard library.
Creates an isolated instance of session D-Bus. The D-Bus will be closed and cleaned up after tests are finished.
Requires dbus-daemon executable be installed.
Example:
from sdbus import DbusInterfaceCommonAsync, dbus_method_async
from sdbus.unittest import IsolatedDbusTestCase
class TestInterface(DbusInterfaceCommonAsync,
interface_name='org.test.test',
):
@dbus_method_async("s", "s")
async def upper(self, string: str) -> str:
"""Uppercase the input"""
return string.upper()
def initialize_object() -> tuple[TestInterface, TestInterface]:
test_object = TestInterface()
test_object.export_to_dbus('/')
test_object_connection = TestInterface.new_proxy(
"org.example.test", '/')
return test_object, test_object_connection
class TestProxy(IsolatedDbusTestCase):
async def asyncSetUp(self) -> None:
await super().asyncSetUp()
await self.bus.request_name_async("org.example.test", 0)
async def test_method_kwargs(self) -> None:
test_object, test_object_connection = initialize_object()
self.assertEqual(
'TEST',
await test_object_connection.upper('test'),
)
bus: SdBus
Bus instance connected to isolated D-Bus environment.
It is also set as a default bus.
assertDbusSignalEmits(signal, timeout=1)
Assert that a given signal was emitted at least once within the given timeout.
Parameters
signal-- D-Bus signal object. Can be a signal from either local or proxy object.timeout(Union[int, float]) -- Maximum wait time until first captured signal.
Should be used as an async context manager. The context manager exits as soon as first signal is captured.
The object returned by context manager has following attributes:
output: list[Any] List of captured data.
Example:
async with self.assertDbusSignalEmits(test_object.test_signal) as signal_record:
test_object.test_signal.emit("test")
self.assertEqual(["test"], signal_record.output)
New in version 0.12.0.
API Index
Common
get_default_bus()request_default_bus_name_async()set_default_bus()decode_object_path()<#sdbus.decode_object_path>encode_object_path()<#sdbus.encode_object_path>sd_bus_open_system()<#sdbus.sd_bus_open_system>sd_bus_open_user()<#sdbus.sd_bus_open_user>DbusDeprecatedFlag<#sdbus.DbusDeprecatedFlag>DbusHiddenFlag<#sdbus.DbusHiddenFlag>DbusNoReplyFlag<#sdbus.DbusNoReplyFlag>DbusPropertyConstFlag<#sdbus.DbusPropertyConstFlag>DbusPropertyEmitsChangeFlag<#sdbus.DbusPropertyEmitsChangeFlag>DbusPropertyEmitsInvalidationFlag<#sdbus.DbusPropertyEmitsInvalidationFlag>DbusPropertyExplicitFlag<#sdbus.DbusPropertyExplicitFlag>DbusSensitiveFlag<#sdbus.DbusSensitiveFlag>DbusUnprivilegedFlag<#sdbus.DbusUnprivilegedFlag>
Asyncio
DbusInterfaceCommonAsync<#sdbus.DbusInterfaceCommonAsync>dbus_method_async()<#sdbus.dbus_method_async>dbus_method_async_override()<#sdbus.dbus_method_async_override>dbus_property_async()<#sdbus.dbus_property_async>dbus_property_async_override()<#sdbus.dbus_property_async_override>dbus_signal_async()<#sdbus.dbus_signal_async>
Blocking
DbusInterfaceCommon<#sdbus.DbusInterfaceCommon>dbus_method()<#sdbus.dbus_method>dbus_property()<#sdbus.dbus_property>
Exceptions
exceptions.DbusAccessDeniedError<#sdbus.exceptions.DbusAccessDeniedError>exceptions.DbusAccessDeniedError<#sdbus.exceptions.DbusAccessDeniedError>exceptions.DbusAddressInUseError<#sdbus.exceptions.DbusAddressInUseError>exceptions.DbusAuthFailedError<#sdbus.exceptions.DbusAuthFailedError>exceptions.DbusBadAddressError<#sdbus.exceptions.DbusBadAddressError>exceptions.DbusDisconnectedError<#sdbus.exceptions.DbusDisconnectedError>exceptions.DbusFailedError<#sdbus.exceptions.DbusFailedError>exceptions.DbusFileExistsError<#sdbus.exceptions.DbusFileExistsError>
okay bestie, deep breath, because this is the part where we list every single exception this library can throw at you. no skipping, no "etc.", we're doing all of them. consider this your emotional support index 💖
exceptions.DbusFileNotFoundError <#sdbus.exceptions.DbusFileNotFoundError>exceptions.DbusInconsistentMessageError <#sdbus.exceptions.DbusInconsistentMessageError>exceptions.DbusInteractiveAuthorizationRequiredError <#sdbus.exceptions.DbusInteractiveAuthorizationRequiredError>exceptions.DbusInvalidArgsError <#sdbus.exceptions.DbusInvalidArgsError>exceptions.DbusInvalidFileContentError <#sdbus.exceptions.DbusInvalidFileContentError>exceptions.DbusInvalidSignatureError <#sdbus.exceptions.DbusInvalidSignatureError>exceptions.DbusIOError <#sdbus.exceptions.DbusIOError>exceptions.DbusLimitsExceededError <#sdbus.exceptions.DbusLimitsExceededError>exceptions.DbusMatchRuleInvalidError <#sdbus.exceptions.DbusMatchRuleInvalidError>exceptions.DbusMatchRuleNotFound <#sdbus.exceptions.DbusMatchRuleNotFound>exceptions.DbusNameHasNoOwnerError <#sdbus.exceptions.DbusNameHasNoOwnerError>exceptions.DbusNoMemoryError <#sdbus.exceptions.DbusNoMemoryError>exceptions.DbusNoNetworkError <#sdbus.exceptions.DbusNoNetworkError>exceptions.DbusNoReplyError <#sdbus.exceptions.DbusNoReplyError>exceptions.DbusNoServerError <#sdbus.exceptions.DbusNoServerError>exceptions.DbusNotSupportedError <#sdbus.exceptions.DbusNotSupportedError>exceptions.DbusPropertyReadOnlyError <#sdbus.exceptions.DbusPropertyReadOnlyError>exceptions.DbusServiceUnknownError <#sdbus.exceptions.DbusServiceUnknownError>exceptions.DbusTimeoutError <#sdbus.exceptions.DbusTimeoutError>exceptions.DbusUnixProcessIdUnknownError <#sdbus.exceptions.DbusUnixProcessIdUnknownError>exceptions.DbusUnknownInterfaceError <#sdbus.exceptions.DbusUnknownInterfaceError>exceptions.DbusUnknownMethodError <#sdbus.exceptions.DbusUnknownMethodError>exceptions.DbusUnknownObjectError <#sdbus.exceptions.DbusUnknownObjectError>exceptions.DbusUnknownPropertyError <#sdbus.exceptions.DbusUnknownPropertyError>exceptions.SdBusBaseError <#sdbus.exceptions.SdBusBaseError>exceptions.SdBusLibraryError <#sdbus.exceptions.SdBusLibraryError>exceptions.SdBusUnmappedMessageError <#sdbus.exceptions.SdBusUnmappedMessageError>exceptions.map_exception_to_dbus_error() <#sdbus.exceptions.map_exception_to_dbus_error>exceptions.SdBusRequestNameError <#sdbus.exceptions.SdBusRequestNameError>exceptions.SdBusRequestNameInQueueError <#sdbus.exceptions.SdBusRequestNameInQueueError>exceptions.SdBusRequestNameExistsError <#sdbus.exceptions.SdBusRequestNameExistsError>exceptions.SdBusRequestNameAlreadyOwnerError <#sdbus.exceptions.SdBusRequestNameAlreadyOwnerError>
like, that's the whole roster. the way map_exception_to_dbus_error() is sitting there in the middle of the error girls like it's not literally the vibe translator 😭 iconic behaviour honestly.
and then, the navigation crumbs, because even a manual page needs a little map:
- Index
<> - API Index
<> - Search Page
<>
👑 Author 👑
igo95862 wrote this whole thing. absolute main character energy, we bow 🫶
and that's a wrap on the entire page, bestie!! this was PYTHON-SDBUS(1) and the version you're holding in your hands is dated Mar 20, 2026. go forth and talk to D-Bus, you're gonna slay 💅✨🌸
📦 where this page comes from
- ubuntu resolute: python3-sdbus 0.14.2-1build1 · LGPL-2.1-or-later
- ubuntu stonking: python3-sdbus 0.14.2-1build1 · LGPL-2.1-or-later
- 💖 girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms