Skip to content

Add pydbus stubs #13921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions stubs/pydbus/METADATA.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version = "0.6.*"
requires = ["pygobject-stubs"]
upstream_repository = "https://github.com/LEW21/pydbus"

[tool.stubtest]
stubtest_requirements = ["pygobject"]
5 changes: 5 additions & 0 deletions stubs/pydbus/pydbus/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from gi.repository.GLib import Variant as Variant

from .bus import SessionBus as SessionBus, SystemBus as SystemBus, connect as connect

__all__ = ["SessionBus", "SystemBus", "Variant", "connect"]
2 changes: 2 additions & 0 deletions stubs/pydbus/pydbus/auto_names.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def auto_bus_name(bus_name: str) -> str: ...
def auto_object_path(bus_name: str, object_path: str | None = None) -> str: ...
218 changes: 218 additions & 0 deletions stubs/pydbus/pydbus/bus.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import types
from typing import Any, Generic, Literal, TypedDict, TypeVar, overload, type_check_only
from typing_extensions import Self

from gi.repository import Gio

from .bus_names import OwnMixin, WatchMixin
from .generic import bound_signal
from .proxy import ProxyMixin, _CompositeObject
from .publication import PublicationMixin
from .registration import RegistrationMixin
from .request_name import RequestNameMixin
from .subscription import SubscriptionMixin

_T = TypeVar("_T")
_ST = TypeVar("_ST") # signal type

def bus_get(type: Gio.BusType) -> Bus[object]: ...
def connect(address: str) -> Bus[object]: ...
@type_check_only
class _DBusOrgFreedesktopDBus:
# Incomplete interface to org.freedesktop.DBus
Features: list[str]

def GetId(self) -> str: ...

@type_check_only
class _DBusOrgFreedesktopPolicyKit1Authority:
# Incomplete interface to org.freedesktop.PolicyKit1.Authority
BackendFeatures: int # flags
BackendName: str
BackendVersion: str

@type_check_only
class OrgBluezAdapter1Dict(TypedDict, total=False):
Address: str
AddressType: Literal["public", "random"]
Alias: str
Class: int
Connectable: bool
Discoverable: bool
DiscoverableTimeout: int
Discovering: bool
Manufacturer: int
Modalias: str
Name: str
Pairable: bool
PairableTimeout: int
Powered: bool
Roles: list[str]
UUIDs: list[str]
Version: int

@type_check_only
class OrgBluezDevice1Dict(TypedDict, total=False):
Adapter: str
Address: str
AddressType: Literal["public", "random"]
Alias: str
Appearance: int
Blocked: bool
Bonded: bool
Class: int
Connected: bool
Icon: str
LegacyPairing: bool
Modalias: str
Name: str
Paired: bool
ServicesResolved: bool
Trusted: bool
UUIDs: list[str]
WakeAllowed: bool

@type_check_only
class OrgBluezInput1Dict(TypedDict, total=False):
ReconnectMode: str

@type_check_only
class OrgBluezMedia1Dict(TypedDict, total=False):
SupportedUUIDs: list[str]

@type_check_only
class OrgBluezMediaControl1Dict(TypedDict, total=False):
Connected: bool

@type_check_only
class OrgBluezBattery1Dict(TypedDict, total=False):
Percentage: int
Source: str

@type_check_only
class OrgBluezGattService1Dict(TypedDict, total=False):
Device: str
Handle: int
Includes: list[str]
Primary: bool
UUID: str

@type_check_only
class OrgBluezGattCharacteristic1Dict(TypedDict, total=False):
Flags: list[str]
Handle: int
MTU: int
Service: str
UUID: str
Value: list[int]

@type_check_only
class OrgBluezGattDescriptor1Dict(TypedDict, total=False):
Characteristic: str
Handle: int
UUID: str
Value: list[int]

@type_check_only
class OrgBluezLEAdvertisingManager1Dict(TypedDict, total=False):
ActiveInstances: int
SupportedCapabilities: dict[str, int] # e.g. MaxTxPower: 7
SupportedFeatures: list[str]
SupportedIncludes: list[str]
SupportedInstances: int
SupportedSecondaryChannels: list[str]

@type_check_only
class OrgBluezNetwork1Dict(TypedDict, total=False):
Connected: bool
Interface: str
UUID: str

# This is for keys under /org/bluez/hci0/*
# Refer to https://github.com/bluez/bluez/blob/master/doc/ for interface details
_OrgBluezDict = TypedDict(
"_OrgBluezDict",
{
"org.bluez.Adapter1": OrgBluezAdapter1Dict,
"org.bluez.Battery1": OrgBluezBattery1Dict,
"org.bluez.Device1": OrgBluezDevice1Dict,
"org.bluez.GattCharacteristic1": OrgBluezGattCharacteristic1Dict,
"org.bluez.GattDescriptor1": OrgBluezGattDescriptor1Dict,
"org.bluez.GattService1": OrgBluezGattService1Dict,
"org.bluez.Input1": OrgBluezInput1Dict,
"org.bluez.LEAdvertisingManager1": OrgBluezLEAdvertisingManager1Dict,
"org.bluez.Media1": OrgBluezMedia1Dict,
"org.bluez.Network1": OrgBluezNetwork1Dict,
"org.bluez.MediaControl1": OrgBluezMediaControl1Dict,
# Dicts below are always empty because they have no properties. However, the key existence may still be useful
# for introspection.
"org.bluez.AgentManager1": dict[str, Any],
"org.bluez.BatteryProviderManager1": dict[str, Any],
"org.bluez.NetworkServer1": dict[str, Any],
"org.bluez.ProfileManager1": dict[str, Any],
"org.freedesktop.DBus.Introspectable": dict[str, Any],
"org.freedesktop.DBus.Properties": dict[str, Any],
},
total=False,
)

@type_check_only
class _OrgFreedesktopDBusObjectManager:
@staticmethod
def GetManagedObjects() -> dict[str, _OrgBluezDict]: ...

@type_check_only
class _OrgBluez(_CompositeObject[_T]):
def __getitem__( # type: ignore[override]
self, key: Literal["org.freedesktop.DBus.ObjectManager"]
) -> _OrgFreedesktopDBusObjectManager: ...

@type_check_only
class _OrgFreedesktopNotifications(_CompositeObject[_T], Generic[_T, _ST]):
Inhibited: bool
ActivationToken: bound_signal[_ST]
ActionInvoked: bound_signal[_ST]
NotificationClosed: bound_signal[_ST]

def CloseNotification(self, id: int) -> None: ...
def GetCapabilities(self) -> list[str]: ... # We could use Literal[] here but KDE also returns its own not in the spec.
def GetServerInformation(self) -> tuple[str, str, str, str]: ...
def Inhibit(self, name: str, reason: str, hints: dict[str, bool | bytes | int | str]) -> int | None: ...
def Notify(
self,
app_name: str,
replaces_id: int,
app_icon: str,
summary: str,
body: str,
actions: list[str],
hints: dict[str, bool | bytes | int | str], # See https://specifications.freedesktop.org/notification-spec/1.3/hints.html
expire_timeout: int,
) -> int: ...
def UnInhibit(self, key: int) -> int | None: ...

class Bus(ProxyMixin[_T], RequestNameMixin[_T], OwnMixin, WatchMixin, SubscriptionMixin, RegistrationMixin[_T], PublicationMixin):
Type: type[Gio.BusType] = ...
autoclose: bool
con: Gio.DBusConnection

def __init__(self, gio_con: Gio.DBusConnection) -> None: ...
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: types.TracebackType | None
) -> None: ...
@property
def dbus(self) -> _DBusOrgFreedesktopDBus: ...
@property
def polkit_authority(self) -> _DBusOrgFreedesktopPolicyKit1Authority: ...
@overload # type: ignore[override]
def get(self, bus_name: Literal[".Notifications"]) -> _OrgFreedesktopNotifications[_T, object]: ...
@overload
def get(self, bus_name: Literal["org.freedesktop.Notifications"]) -> _OrgFreedesktopNotifications[_T, object]: ...
@overload
def get(self, bus_name: Literal["org.bluez"], object_path: Literal["/"]) -> _OrgBluez[_T]: ...
@overload
def get(self, bus_name: str, object_path: str) -> Any: ...

def SystemBus() -> Bus[object]: ...
def SessionBus() -> Bus[object]: ...
53 changes: 53 additions & 0 deletions stubs/pydbus/pydbus/bus_names.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from collections.abc import Callable

from gi.repository import Gio

from .exitable import Exitable

class NameOwner(Exitable):
Flags: Gio.BusNameOwnerFlags

def __init__(
self,
con: Gio.DBusConnection,
name: str,
flags: Gio.BusNameOwnerFlags,
name_aquired_handler: Callable[[str], object],
name_lost_handler: Callable[[str], object],
) -> None: ...
def unown(self) -> None: ... # added by ExitableWithAliases('unown')

class NameWatcher(Exitable):
Flags: Gio.BusNameWatcherFlags

def __init__(
self,
con: Gio.DBusConnection,
name: str,
flags: Gio.BusNameWatcherFlags,
name_appeared_handler: Callable[[str], object],
name_vanished_handler: Callable[[str], object],
) -> None: ...
def unwatch(self) -> None: ... # added by ExitableWithAliases('unwatch')

class OwnMixin:
NameOwnerFlags: Gio.BusNameOwnerFlags

def own_name(
self,
name: str,
flags: Gio.BusNameOwnerFlags = ...,
name_aquired: Callable[[str], object] | None = ...,
name_lost: Callable[[str], object] | None = ...,
) -> NameOwner: ...

class WatchMixin:
NameWatcherFlags: Gio.BusNameWatcherFlags

def watch_name(
self,
name: str,
flags: Gio.BusNameWatcherFlags = ...,
name_appeared: Callable[[str], object] | None = ...,
name_vanished: Callable[[str], object] | None = ...,
) -> NameWatcher: ...
13 changes: 13 additions & 0 deletions stubs/pydbus/pydbus/exitable.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import types
from typing_extensions import Self

class Exitable:
def __enter__(self) -> Self: ...
def __exit__(
self,
exc_type: type[BaseException] | None = None,
exc_value: BaseException | None = None,
traceback: types.TracebackType | None = None,
) -> None: ...

def ExitableWithAliases(*exit_methods: str) -> Exitable: ...
43 changes: 43 additions & 0 deletions stubs/pydbus/pydbus/generic.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import types
from _typeshed import Unused
from collections.abc import Callable
from typing import Generic, TypeVar, overload
from typing_extensions import Self

class subscription:
callback_list: list[Callable[..., object]]
callback: Callable[..., object]

def __init__(self, callback_list: list[Callable[..., object]], callback: Callable[..., object]) -> None: ...
def unsubscribe(self) -> None: ...
def disconnect(self) -> None: ...
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: types.TracebackType | None
) -> None: ...

_T = TypeVar("_T")

class bound_signal(Generic[_T]):
__signal__: signal[_T]

def __init__(self, signal: signal[_T], instance: _T) -> None: ...
@property
def callbacks(self) -> list[Callable[..., object]]: ...
def connect(self, callback: Callable[..., object]) -> subscription: ...
def emit(self, *args: object) -> None: ...
def __call__(self, *args: object) -> None: ...

class signal(Generic[_T]):
map: dict[object, Callable[..., object]]

def __init__(self) -> None: ...
def connect(self, object: str, callback: Callable[..., object]) -> subscription: ...
def emit(self, object: str, *args: object) -> None: ...
@overload
def __get__(self, instance: None, owner: Unused) -> Self: ...
@overload
def __get__(self, instance: _T, owner: Unused) -> bound_signal[_T]: ...
def __set__(self, instance: Unused, value: Unused) -> None: ... # Always raises

bound_method: Callable[..., None] # type(signal().emit)
2 changes: 2 additions & 0 deletions stubs/pydbus/pydbus/identifier.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def isident(s: str) -> bool: ...
def filter_identifier(name: str) -> str: ...
27 changes: 27 additions & 0 deletions stubs/pydbus/pydbus/method_call_context.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import Generic, NamedTuple, TypeVar

from gi.repository import Gio

from .bus import Bus

_T = TypeVar("_T")

class AuthorizationResult(NamedTuple):
is_authorized: bool
is_challenge: bool
details: dict[str, str]

class MethodCallContext(Generic[_T]):
def __init__(self, gdbus_method_invocation: Gio.DBusMethodInvocation) -> None: ...
@property
def bus(self) -> Bus[_T]: ...
@property
def sender(self) -> str: ...
@property
def object_path(self) -> str: ...
@property
def interface_name(self) -> str: ...
@property
def method_name(self) -> str: ...
def check_authorization(self, action_id: str, details: dict[str, str], interactive: bool = False) -> AuthorizationResult: ...
def is_authorized(self, action_id: str, details: dict[str, str], interactive: bool = False) -> bool: ...
Loading
Loading