Skip to content

Commit 8564580

Browse files
committed
Add sdbus.default_bus.set_context_default_bus
Sets the context-local default bus. The context bus only used when explicitly set to avoid bus initialization spam. Only having thread-local buses seemed very limiting.
1 parent 15cc378 commit 8564580

File tree

3 files changed

+52
-12
lines changed

3 files changed

+52
-12
lines changed

docs/general.rst

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ See API documentation for a particular decorator.
130130

131131

132132
Default bus
133-
++++++++++++++++++++++++++
133+
+++++++++++
134134

135135
Most object methods that take a bus as a parameter
136136
will use a thread-local default bus connection if a bus object
@@ -139,17 +139,23 @@ is not explicitly passed.
139139
Session bus is default bus when running as a user and
140140
system bus otherwise.
141141

142-
:py:func:`request_default_bus_name_async` can be used to acquire
143-
a service name on default bus.
142+
The :py:func:`request_default_bus_name_async <sdbus.default_bus.request_default_bus_name_async>`
143+
and :py:func:`request_default_bus_name <sdbus.default_bus.request_default_bus_name>`
144+
can be used to acquire a service name on the default bus.
144145

145146
Use :py:func:`sd_bus_open_user` and :py:func:`sd_bus_open_system` to
146147
acquire a specific bus connection.
147148

148-
Set the default connection to a new default with :py:func:`set_default_bus`.
149-
This should be done before any object that take bus as an init argument are created.
149+
The :py:func:`set_default_bus <sdbus.default_bus.set_default_bus>` can be used to set the new
150+
thread-local bus. This should be done before any objects that take bus as
151+
an init argument are created. If no bus has been set the new bus will
152+
be initialized and set as thread-local default.
150153

151-
In the future there will be a better way to create and acquire
152-
new bus connections.
154+
The bus can also be set as default for the current context using
155+
:py:func:`set_context_default_bus <sdbus.default_bus.set_context_default_bus>`.
156+
The context refers to the standard library's ``contextvars`` module context variables
157+
frequently used in asyncio frameworks. Context-local default bus has higher priority over
158+
thread-local default bus.
153159

154160
Glossary
155161
+++++++++++++++++++++

src/sdbus/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
get_default_bus,
7575
request_default_bus_name,
7676
request_default_bus_name_async,
77+
set_context_default_bus,
7778
set_default_bus,
7879
)
7980
from .sd_bus_internals import (
@@ -145,6 +146,7 @@
145146
"get_default_bus",
146147
"request_default_bus_name",
147148
"request_default_bus_name_async",
149+
"set_context_default_bus",
148150
"set_default_bus",
149151

150152
'DbusDeprecatedFlag',

src/sdbus/default_bus.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from __future__ import annotations
2121

2222
import threading
23+
from contextvars import ContextVar, Token
2324
from logging import getLogger
2425
from typing import TYPE_CHECKING
2526

@@ -43,6 +44,7 @@ class DefaultBusTLStorage(threading.local):
4344

4445

4546
bus_tls = DefaultBusTLStorage()
47+
bus_contextvar: ContextVar[SdBus] = ContextVar("DEFAULT_BUS")
4648

4749

4850
def _get_defaul_bus_tls() -> Optional[SdBus]:
@@ -54,10 +56,20 @@ def _set_default_bus_tls(new_bus: Optional[SdBus]) -> None:
5456

5557

5658
def get_default_bus() -> SdBus:
57-
"""Get default thread-local bus."""
58-
current_bus = _get_defaul_bus_tls()
59-
if current_bus is not None:
60-
return current_bus
59+
"""Get default bus.
60+
61+
Returns context-local default bus if set or
62+
thread-local otherwise.
63+
64+
If no default bus is set initializes a new bus using
65+
:py:func:`sdbus.sd_bus_open` and sets it as a thread-local
66+
default bus.
67+
"""
68+
if (context_bus := bus_contextvar.get(None)) is not None:
69+
return context_bus
70+
71+
if (tls_bus := _get_defaul_bus_tls()) is not None:
72+
return tls_bus
6173
else:
6274
new_bus = sd_bus_open()
6375
logger.info(
@@ -69,7 +81,7 @@ def get_default_bus() -> SdBus:
6981

7082

7183
def set_default_bus(new_default: SdBus) -> None:
72-
"""Set default thread-local bus.
84+
"""Set thread-local default bus.
7385
7486
Should be called before creating any objects that will use
7587
default bus.
@@ -80,6 +92,25 @@ def set_default_bus(new_default: SdBus) -> None:
8092
_set_default_bus_tls(new_default)
8193

8294

95+
def set_context_default_bus(new_default: SdBus) -> Token[SdBus]:
96+
"""Set context-local default bus.
97+
98+
Should be called before creating any objects that will use
99+
default bus.
100+
101+
Default bus can be replaced but the change will only affect
102+
newly created objects.
103+
104+
Context-local default bus has higher priority over thread-local one
105+
but has to be explicitly set.
106+
107+
:returns:
108+
Token that can be used to reset context bus back.
109+
See ``contextvars`` documentation for details.
110+
"""
111+
return bus_contextvar.set(new_default)
112+
113+
83114
def _prepare_request_name_flags(
84115
allow_replacement: bool,
85116
replace_existing: bool,
@@ -167,6 +198,7 @@ def request_default_bus_name(
167198
__all__ = (
168199
"get_default_bus",
169200
"set_default_bus",
201+
"set_context_default_bus",
170202
"request_default_bus_name_async",
171203
"request_default_bus_name",
172204
)

0 commit comments

Comments
 (0)