From 5bdc8454a87ec7a00da2a0a795a039f8bfd0109e Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Fri, 2 May 2025 00:58:47 -0400 Subject: [PATCH 01/22] Add pydbus stubs --- stubs/pydbus/METADATA.toml | 3 + stubs/pydbus/pydbus/__init__.pyi | 5 + stubs/pydbus/pydbus/auto_names.pyi | 2 + stubs/pydbus/pydbus/bus.pyi | 209 ++++++++++++++++++++ stubs/pydbus/pydbus/bus_names.pyi | 53 +++++ stubs/pydbus/pydbus/exitable.pyi | 14 ++ stubs/pydbus/pydbus/generic.pyi | 43 ++++ stubs/pydbus/pydbus/identifier.pyi | 2 + stubs/pydbus/pydbus/method_call_context.pyi | 27 +++ stubs/pydbus/pydbus/proxy.pyi | 27 +++ stubs/pydbus/pydbus/proxy_method.pyi | 19 ++ stubs/pydbus/pydbus/proxy_property.pyi | 18 ++ stubs/pydbus/pydbus/proxy_signal.pyi | 35 ++++ stubs/pydbus/pydbus/publication.pyi | 25 +++ stubs/pydbus/pydbus/registration.pyi | 44 +++++ stubs/pydbus/pydbus/request_name.pyi | 13 ++ stubs/pydbus/pydbus/subscription.pyi | 37 ++++ stubs/pydbus/pydbus/timeout.pyi | 1 + 18 files changed, 577 insertions(+) create mode 100644 stubs/pydbus/METADATA.toml create mode 100644 stubs/pydbus/pydbus/__init__.pyi create mode 100644 stubs/pydbus/pydbus/auto_names.pyi create mode 100644 stubs/pydbus/pydbus/bus.pyi create mode 100644 stubs/pydbus/pydbus/bus_names.pyi create mode 100644 stubs/pydbus/pydbus/exitable.pyi create mode 100644 stubs/pydbus/pydbus/generic.pyi create mode 100644 stubs/pydbus/pydbus/identifier.pyi create mode 100644 stubs/pydbus/pydbus/method_call_context.pyi create mode 100644 stubs/pydbus/pydbus/proxy.pyi create mode 100644 stubs/pydbus/pydbus/proxy_method.pyi create mode 100644 stubs/pydbus/pydbus/proxy_property.pyi create mode 100644 stubs/pydbus/pydbus/proxy_signal.pyi create mode 100644 stubs/pydbus/pydbus/publication.pyi create mode 100644 stubs/pydbus/pydbus/registration.pyi create mode 100644 stubs/pydbus/pydbus/request_name.pyi create mode 100644 stubs/pydbus/pydbus/subscription.pyi create mode 100644 stubs/pydbus/pydbus/timeout.pyi diff --git a/stubs/pydbus/METADATA.toml b/stubs/pydbus/METADATA.toml new file mode 100644 index 000000000000..b4f780eb81d2 --- /dev/null +++ b/stubs/pydbus/METADATA.toml @@ -0,0 +1,3 @@ +version = "0.6.*" +requires = ["pygobject-stubs"] +upstream_repository = "https://github.com/LEW21/pydbus" diff --git a/stubs/pydbus/pydbus/__init__.pyi b/stubs/pydbus/pydbus/__init__.pyi new file mode 100644 index 000000000000..7e9aa91442eb --- /dev/null +++ b/stubs/pydbus/pydbus/__init__.pyi @@ -0,0 +1,5 @@ +from gi.repository.GLib import Variant + +from .bus import SessionBus, SystemBus, connect + +__all__ = ["SessionBus", "SystemBus", "Variant", "connect"] diff --git a/stubs/pydbus/pydbus/auto_names.pyi b/stubs/pydbus/pydbus/auto_names.pyi new file mode 100644 index 000000000000..c83d0475feae --- /dev/null +++ b/stubs/pydbus/pydbus/auto_names.pyi @@ -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: ... diff --git a/stubs/pydbus/pydbus/bus.pyi b/stubs/pydbus/pydbus/bus.pyi new file mode 100644 index 000000000000..407ab283d658 --- /dev/null +++ b/stubs/pydbus/pydbus/bus.pyi @@ -0,0 +1,209 @@ +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] + +# This is for keys under /org/bluez/hci0/* +_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.MediaControl1": OrgBluezMediaControl1Dict, + # The following always appear as empty dictionaries on my system. + "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]: ... diff --git a/stubs/pydbus/pydbus/bus_names.pyi b/stubs/pydbus/pydbus/bus_names.pyi new file mode 100644 index 000000000000..be22145e876a --- /dev/null +++ b/stubs/pydbus/pydbus/bus_names.pyi @@ -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], None], + name_lost_handler: Callable[[str], None], + ) -> 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], None], + name_vanished_handler: Callable[[str], None], + ) -> 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], None] | None = ..., + name_lost: Callable[[str], None] | None = ..., + ) -> NameOwner: ... + +class WatchMixin: + NameWatcherFlags: Gio.BusNameWatcherFlags + + def watch_name( + self, + name: str, + flags: Gio.BusNameWatcherFlags = ..., + name_appeared: Callable[[str], None] | None = ..., + name_vanished: Callable[[str], None] | None = ..., + ) -> NameWatcher: ... diff --git a/stubs/pydbus/pydbus/exitable.pyi b/stubs/pydbus/pydbus/exitable.pyi new file mode 100644 index 000000000000..71cc09bedd77 --- /dev/null +++ b/stubs/pydbus/pydbus/exitable.pyi @@ -0,0 +1,14 @@ +import types +from collections.abc import Iterable +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: Iterable[str]) -> Exitable: ... diff --git a/stubs/pydbus/pydbus/generic.pyi b/stubs/pydbus/pydbus/generic.pyi new file mode 100644 index 000000000000..8b0336875fd5 --- /dev/null +++ b/stubs/pydbus/pydbus/generic.pyi @@ -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) diff --git a/stubs/pydbus/pydbus/identifier.pyi b/stubs/pydbus/pydbus/identifier.pyi new file mode 100644 index 000000000000..32b5d6a1eea0 --- /dev/null +++ b/stubs/pydbus/pydbus/identifier.pyi @@ -0,0 +1,2 @@ +def isident(s: str) -> bool: ... +def filter_identifier(name: str) -> str: ... diff --git a/stubs/pydbus/pydbus/method_call_context.pyi b/stubs/pydbus/pydbus/method_call_context.pyi new file mode 100644 index 000000000000..c9d77f57abcf --- /dev/null +++ b/stubs/pydbus/pydbus/method_call_context.pyi @@ -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: ... diff --git a/stubs/pydbus/pydbus/proxy.pyi b/stubs/pydbus/pydbus/proxy.pyi new file mode 100644 index 000000000000..d403414c550c --- /dev/null +++ b/stubs/pydbus/pydbus/proxy.pyi @@ -0,0 +1,27 @@ +from typing import Generic, TypeVar, type_check_only +from typing_extensions import Self +from xml.etree.ElementTree import Element + +from .bus import Bus + +_T = TypeVar("_T") + +class ProxyMixin(Generic[_T]): + def get(self, bus_name: str, object_path: str | None = None, *, timeout: int | None = None) -> _CompositeObject[_T]: ... + +class ProxyObject(Generic[_T]): + def __init__(self, bus: Bus[_T], bus_name: str, path: str, object: Self | None = None) -> None: ... + def __getattr__(self, name: str) -> _T: ... + def __setattr__(self, name: str, value: _T) -> None: ... + +@type_check_only +class _CompositeObject(ProxyObject[_T]): # Inside CompositeInterface + def __getitem__(self, iface: str) -> ProxyObject[_T]: ... + +@type_check_only +class _interface(ProxyObject[_T]): # inside Interface + @staticmethod + def _Introspect() -> None: ... + +def Interface(iface: Element) -> _interface[object]: ... +def CompositeInterface(introspection: Element) -> _CompositeObject[object]: ... diff --git a/stubs/pydbus/pydbus/proxy_method.pyi b/stubs/pydbus/pydbus/proxy_method.pyi new file mode 100644 index 000000000000..2b29ba1f8353 --- /dev/null +++ b/stubs/pydbus/pydbus/proxy_method.pyi @@ -0,0 +1,19 @@ +from _typeshed import Unused +from inspect import Signature +from typing import Generic, TypeVar + +from .proxy import ProxyObject + +_CT = TypeVar("_CT") # __call__ return type +_GT = TypeVar("_GT") # __get__ return type +_PT = TypeVar("_PT") # ProxyObject type +put_signature_in_doc: bool = False + +class DBUSSignature(Signature): ... + +class ProxyMethod(Generic[_GT, _CT, _PT]): + __signature__: DBUSSignature + + def __init__(self, iface_name: str, method: str) -> None: ... + def __call__(self, instance: ProxyObject[_PT], *args: object, timeout: int | None = None) -> _CT: ... + def __get__(self, instance: ProxyObject[_PT], owner: Unused) -> _GT: ... diff --git a/stubs/pydbus/pydbus/proxy_property.pyi b/stubs/pydbus/pydbus/proxy_property.pyi new file mode 100644 index 000000000000..6e2b2c604047 --- /dev/null +++ b/stubs/pydbus/pydbus/proxy_property.pyi @@ -0,0 +1,18 @@ +from _typeshed import Unused +from typing import Generic, TypeVar, overload +from typing_extensions import Self +from xml.etree.ElementTree import Element + +from .proxy import ProxyObject + +_T = TypeVar("_T") + +class ProxyProperty(Generic[_T]): + def __init__(self, iface_name: str, property: Element) -> None: ... + @overload + def __get__(self, instance: None, owner: Unused) -> Self: ... + @overload + def __get__(self, instance: ProxyObject[_T], owner: Unused) -> _T: ... + @overload + def __get__(self, instance: ProxyObject[_T] | None, owner: Unused) -> Self | _T: ... + def __set__(self, instance: ProxyObject[_T] | None, value: _T) -> None: ... diff --git a/stubs/pydbus/pydbus/proxy_signal.pyi b/stubs/pydbus/pydbus/proxy_signal.pyi new file mode 100644 index 000000000000..ecd6d6df7ddb --- /dev/null +++ b/stubs/pydbus/pydbus/proxy_signal.pyi @@ -0,0 +1,35 @@ +from _typeshed import Unused +from collections.abc import Callable +from typing import Generic, TypeVar, overload +from typing_extensions import Self +from xml.etree.ElementTree import Element + +from .generic import bound_signal +from .proxy import ProxyObject +from .subscription import Subscription + +_PT = TypeVar("_PT") # ProxyObject type +_T = TypeVar("_T") + +class ProxySignal(Generic[_T, _PT]): + def __init__(self, iface_name: str, signal: Element) -> None: ... + def connect(self, object: str, callback: Callable[..., None]) -> Subscription: ... + @overload + def __get__(self, instance: None, owner: Unused) -> Self: ... + @overload + def __get__(self, instance: ProxyObject[_PT], owner: Unused) -> bound_signal[_T]: ... + @overload + def __get__(self, instance: ProxyObject[_PT] | None, owner: Unused) -> bound_signal[_T] | Self: ... + def __set__(self, instance: Unused, value: Unused) -> None: ... # Always raises + +class OnSignal(Generic[_T, _PT]): + signal: ProxySignal[_T, _PT] + + def __init__(self, signal: ProxySignal[_T, _PT]) -> None: ... + @overload + def __get__(self, instance: None, owner: Unused) -> Self: ... + @overload + def __get__(self, instance: ProxyObject[_PT], owner: Unused) -> _T: ... + @overload + def __get__(self, instance: ProxyObject[_PT] | None, owner: Unused) -> _T: ... + def __set__(self, instance: ProxyObject[_PT] | None, value: _T) -> None: ... diff --git a/stubs/pydbus/pydbus/publication.pyi b/stubs/pydbus/pydbus/publication.pyi new file mode 100644 index 000000000000..62735284beeb --- /dev/null +++ b/stubs/pydbus/pydbus/publication.pyi @@ -0,0 +1,25 @@ +from collections.abc import Iterable +from typing import TypeVar + +from .bus import Bus +from .exitable import Exitable + +_T = TypeVar("_T") + +class Publication(Exitable): + def __init__( + self, + bus: Bus[_T], + bus_name: str, + *objects: Iterable[tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str], + allow_replacement: bool = True, + replace: bool = False, + ) -> None: ... + def unpublish(self) -> None: ... # added by ExitableWithAliases('unpublish') + +class PublicationMixin: + def publish( + self, + bus_name: str, + *objects: Iterable[tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str], + ) -> Publication: ... diff --git a/stubs/pydbus/pydbus/registration.pyi b/stubs/pydbus/pydbus/registration.pyi new file mode 100644 index 000000000000..a98135fc2250 --- /dev/null +++ b/stubs/pydbus/pydbus/registration.pyi @@ -0,0 +1,44 @@ +from typing import Generic, TypeVar +from xml.etree.ElementTree import Element + +from gi.repository import Gio, GLib + +from .bus import Bus +from .exitable import Exitable +from .generic import signal + +_T = TypeVar("_T") + +class ObjectWrapper(Exitable, Generic[_T]): + object: str + outargs: dict[str, GLib.Variant] + readable_properties: dict[str, GLib.Variant] + writable_properties: dict[str, GLib.Variant] + + def __init__(self, object: str, interfaces: Element) -> None: ... + + SignalEmitted: signal[_T] + + def call_method( + self, + connection: Gio.DBusConnection, + sender: str, + object_path: str, + interface_name: str, + method_name: str, + parameters: GLib.Variant, + invocation: Gio.DBusMethodInvocation, + ) -> None: ... + def Get(self, interface_name: str, property_name: str) -> GLib.Variant: ... + def GetAll(self, interface_name: str) -> dict[str, GLib.Variant]: ... + def Set(self, interface_name: str, property_name: str, value: GLib.Variant) -> None: ... + def unwrap(self) -> None: ... # added by ExitableWithAliases('unwrap') + +class ObjectRegistration(Exitable, Generic[_T]): + def __init__( + self, bus: Bus[_T], path: str, interfaces: Element, wrapper: ObjectWrapper[_T], own_wrapper: bool = False + ) -> None: ... + def unregister(self) -> None: ... # added by ExitableWithAliases('unregister') + +class RegistrationMixin(Generic[_T]): + def register_object(self, path: str, object: str, node_info: str | list[str] | tuple[str, ...]) -> ObjectRegistration[_T]: ... diff --git a/stubs/pydbus/pydbus/request_name.pyi b/stubs/pydbus/pydbus/request_name.pyi new file mode 100644 index 000000000000..73082ed5bdec --- /dev/null +++ b/stubs/pydbus/pydbus/request_name.pyi @@ -0,0 +1,13 @@ +from typing import Generic, TypeVar + +from .bus import Bus +from .exitable import Exitable + +_T = TypeVar("_T") + +class NameOwner(Exitable, Generic[_T]): + def __init__(self, bus: Bus[_T], name: str, allow_replacement: bool, replace: bool) -> None: ... + def unown(self) -> None: ... # added by ExitableWithAliases('unown') + +class RequestNameMixin(Generic[_T]): + def request_name(self, name: str, allow_replacement: bool = True, replace: bool = False) -> NameOwner[_T]: ... diff --git a/stubs/pydbus/pydbus/subscription.pyi b/stubs/pydbus/pydbus/subscription.pyi new file mode 100644 index 000000000000..1793ee30d96f --- /dev/null +++ b/stubs/pydbus/pydbus/subscription.pyi @@ -0,0 +1,37 @@ +from collections.abc import Callable + +from gi.repository import Gio +from gi.repository.GLib import Variant + +from .exitable import Exitable + +class Subscription(Exitable): + Flags: Gio.DBusSignalFlags + + def __init__( + self, + con: Gio.DBusConnection, + sender: str, + iface: str, + member: str | None, + object: str | None, + arg0: str | None, + flags: Gio.DBusSignalFlags, + callback: Callable[[str, str, str, str, Variant], None] | None, + ) -> None: ... + def unsubscribe(self) -> None: ... # added by ExitableWithAliases('unsubscribe') + def disconnect(self) -> None: ... # added by ExitableWithAliases('disconnect') + +class SubscriptionMixin: + SubscriptionFlags: Gio.DBusSignalFlags + + def subscribe( + self, + sender: str | None = None, + iface: str | None = None, + signal: str | None = None, + object: str | None = None, + arg0: str | None = None, + flags: Gio.DBusSignalFlags = ..., + signal_fired: Callable[[str, str, str, str, Variant], None] | None = None, + ) -> Subscription: ... diff --git a/stubs/pydbus/pydbus/timeout.pyi b/stubs/pydbus/pydbus/timeout.pyi new file mode 100644 index 000000000000..9f3973cf2cfa --- /dev/null +++ b/stubs/pydbus/pydbus/timeout.pyi @@ -0,0 +1 @@ +def timeout_to_glib(timeout: int | None) -> int: ... From 96adc5749e0ae18b631eb23f2cba77caa1dbdf4a Mon Sep 17 00:00:00 2001 From: Tatsh Date: Tue, 6 May 2025 11:11:20 -0400 Subject: [PATCH 02/22] pydbus.bus: re-exports Co-authored-by: Sebastian Rittau --- stubs/pydbus/pydbus/__init__.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/pydbus/pydbus/__init__.pyi b/stubs/pydbus/pydbus/__init__.pyi index 7e9aa91442eb..d3dc290c59ea 100644 --- a/stubs/pydbus/pydbus/__init__.pyi +++ b/stubs/pydbus/pydbus/__init__.pyi @@ -1,5 +1,5 @@ -from gi.repository.GLib import Variant +from gi.repository.GLib import Variant as Variant -from .bus import SessionBus, SystemBus, connect +from .bus import SessionBus as SessionBus, SystemBus as SystemBus, connect as connect __all__ = ["SessionBus", "SystemBus", "Variant", "connect"] From 5f3184edc89e679c4e82e64d3489b39b500ef538 Mon Sep 17 00:00:00 2001 From: Tatsh Date: Tue, 6 May 2025 11:12:31 -0400 Subject: [PATCH 03/22] pydbus.exitable: fix *exit_methods type Co-authored-by: Sebastian Rittau --- stubs/pydbus/pydbus/exitable.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/pydbus/pydbus/exitable.pyi b/stubs/pydbus/pydbus/exitable.pyi index 71cc09bedd77..73dab7056ba6 100644 --- a/stubs/pydbus/pydbus/exitable.pyi +++ b/stubs/pydbus/pydbus/exitable.pyi @@ -11,4 +11,4 @@ class Exitable: traceback: types.TracebackType | None = None, ) -> None: ... -def ExitableWithAliases(*exit_methods: Iterable[str]) -> Exitable: ... +def ExitableWithAliases(*exit_methods: str) -> Exitable: ... From b3879308e3170273e2037bb5fc6cc263cfe88650 Mon Sep 17 00:00:00 2001 From: Tatsh Date: Tue, 6 May 2025 11:12:51 -0400 Subject: [PATCH 04/22] pydbus.publication: fix *objects type Co-authored-by: Sebastian Rittau --- stubs/pydbus/pydbus/publication.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/pydbus/pydbus/publication.pyi b/stubs/pydbus/pydbus/publication.pyi index 62735284beeb..a73103ce9a9d 100644 --- a/stubs/pydbus/pydbus/publication.pyi +++ b/stubs/pydbus/pydbus/publication.pyi @@ -11,7 +11,7 @@ class Publication(Exitable): self, bus: Bus[_T], bus_name: str, - *objects: Iterable[tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str], + *objects: tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str, allow_replacement: bool = True, replace: bool = False, ) -> None: ... From 831572adbe8b698f057f65dce3d0d55765bb3f7f Mon Sep 17 00:00:00 2001 From: Tatsh Date: Tue, 6 May 2025 11:13:08 -0400 Subject: [PATCH 05/22] pydbus.publication: fix *objects type Co-authored-by: Sebastian Rittau --- stubs/pydbus/pydbus/publication.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/pydbus/pydbus/publication.pyi b/stubs/pydbus/pydbus/publication.pyi index a73103ce9a9d..081ef385a934 100644 --- a/stubs/pydbus/pydbus/publication.pyi +++ b/stubs/pydbus/pydbus/publication.pyi @@ -21,5 +21,5 @@ class PublicationMixin: def publish( self, bus_name: str, - *objects: Iterable[tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str], + *objects: tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str, ) -> Publication: ... From b5f20c603e93bd98673ae4d53425fceb96ddd048 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 15:14:50 +0000 Subject: [PATCH 06/22] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stubs/pydbus/pydbus/exitable.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/stubs/pydbus/pydbus/exitable.pyi b/stubs/pydbus/pydbus/exitable.pyi index 73dab7056ba6..4fc88828243a 100644 --- a/stubs/pydbus/pydbus/exitable.pyi +++ b/stubs/pydbus/pydbus/exitable.pyi @@ -1,5 +1,4 @@ import types -from collections.abc import Iterable from typing_extensions import Self class Exitable: From e516b26ca19fae7f337f11e0600d4e0001352ce0 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Tue, 6 May 2025 11:17:26 -0400 Subject: [PATCH 07/22] pydbus: allow callbacks with return values other than None --- stubs/pydbus/pydbus/bus_names.pyi | 16 ++++++++-------- stubs/pydbus/pydbus/proxy_signal.pyi | 2 +- stubs/pydbus/pydbus/subscription.pyi | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/stubs/pydbus/pydbus/bus_names.pyi b/stubs/pydbus/pydbus/bus_names.pyi index be22145e876a..5393374fc745 100644 --- a/stubs/pydbus/pydbus/bus_names.pyi +++ b/stubs/pydbus/pydbus/bus_names.pyi @@ -12,8 +12,8 @@ class NameOwner(Exitable): con: Gio.DBusConnection, name: str, flags: Gio.BusNameOwnerFlags, - name_aquired_handler: Callable[[str], None], - name_lost_handler: Callable[[str], None], + name_aquired_handler: Callable[[str], object], + name_lost_handler: Callable[[str], object], ) -> None: ... def unown(self) -> None: ... # added by ExitableWithAliases('unown') @@ -25,8 +25,8 @@ class NameWatcher(Exitable): con: Gio.DBusConnection, name: str, flags: Gio.BusNameWatcherFlags, - name_appeared_handler: Callable[[str], None], - name_vanished_handler: Callable[[str], None], + name_appeared_handler: Callable[[str], object], + name_vanished_handler: Callable[[str], object], ) -> None: ... def unwatch(self) -> None: ... # added by ExitableWithAliases('unwatch') @@ -37,8 +37,8 @@ class OwnMixin: self, name: str, flags: Gio.BusNameOwnerFlags = ..., - name_aquired: Callable[[str], None] | None = ..., - name_lost: Callable[[str], None] | None = ..., + name_aquired: Callable[[str], object] | None = ..., + name_lost: Callable[[str], object] | None = ..., ) -> NameOwner: ... class WatchMixin: @@ -48,6 +48,6 @@ class WatchMixin: self, name: str, flags: Gio.BusNameWatcherFlags = ..., - name_appeared: Callable[[str], None] | None = ..., - name_vanished: Callable[[str], None] | None = ..., + name_appeared: Callable[[str], object] | None = ..., + name_vanished: Callable[[str], object] | None = ..., ) -> NameWatcher: ... diff --git a/stubs/pydbus/pydbus/proxy_signal.pyi b/stubs/pydbus/pydbus/proxy_signal.pyi index ecd6d6df7ddb..16069a2d0716 100644 --- a/stubs/pydbus/pydbus/proxy_signal.pyi +++ b/stubs/pydbus/pydbus/proxy_signal.pyi @@ -13,7 +13,7 @@ _T = TypeVar("_T") class ProxySignal(Generic[_T, _PT]): def __init__(self, iface_name: str, signal: Element) -> None: ... - def connect(self, object: str, callback: Callable[..., None]) -> Subscription: ... + def connect(self, object: str, callback: Callable[..., object]) -> Subscription: ... @overload def __get__(self, instance: None, owner: Unused) -> Self: ... @overload diff --git a/stubs/pydbus/pydbus/subscription.pyi b/stubs/pydbus/pydbus/subscription.pyi index 1793ee30d96f..abda003e8773 100644 --- a/stubs/pydbus/pydbus/subscription.pyi +++ b/stubs/pydbus/pydbus/subscription.pyi @@ -17,7 +17,7 @@ class Subscription(Exitable): object: str | None, arg0: str | None, flags: Gio.DBusSignalFlags, - callback: Callable[[str, str, str, str, Variant], None] | None, + callback: Callable[[str, str, str, str, Variant], object] | None, ) -> None: ... def unsubscribe(self) -> None: ... # added by ExitableWithAliases('unsubscribe') def disconnect(self) -> None: ... # added by ExitableWithAliases('disconnect') @@ -33,5 +33,5 @@ class SubscriptionMixin: object: str | None = None, arg0: str | None = None, flags: Gio.DBusSignalFlags = ..., - signal_fired: Callable[[str, str, str, str, Variant], None] | None = None, + signal_fired: Callable[[str, str, str, str, Variant], object] | None = None, ) -> Subscription: ... From e1f4298b5ed3eb1f808f70d0b9aa8fba13b9d39e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 15:19:12 +0000 Subject: [PATCH 08/22] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stubs/pydbus/pydbus/publication.pyi | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/stubs/pydbus/pydbus/publication.pyi b/stubs/pydbus/pydbus/publication.pyi index 081ef385a934..c668b4950ee3 100644 --- a/stubs/pydbus/pydbus/publication.pyi +++ b/stubs/pydbus/pydbus/publication.pyi @@ -1,4 +1,3 @@ -from collections.abc import Iterable from typing import TypeVar from .bus import Bus @@ -19,7 +18,5 @@ class Publication(Exitable): class PublicationMixin: def publish( - self, - bus_name: str, - *objects: tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str, + self, bus_name: str, *objects: tuple[str, str, str | list[str] | tuple[str, ...]] | tuple[str, str] | tuple[str] | str ) -> Publication: ... From ad87756cdc681298f6a211120ee91661569c0e96 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Tue, 6 May 2025 16:10:35 -0400 Subject: [PATCH 09/22] pydbus: add stubtest_requirements --- stubs/pydbus/METADATA.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stubs/pydbus/METADATA.toml b/stubs/pydbus/METADATA.toml index b4f780eb81d2..f158414ca994 100644 --- a/stubs/pydbus/METADATA.toml +++ b/stubs/pydbus/METADATA.toml @@ -1,3 +1,6 @@ version = "0.6.*" requires = ["pygobject-stubs"] upstream_repository = "https://github.com/LEW21/pydbus" + +[tool.stubtest] +stubtest_requirements = ["pygobject"] From 4345913ed7dece9b7654b4f41be2d6e3bdd30155 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Tue, 6 May 2025 16:23:08 -0400 Subject: [PATCH 10/22] pydbus.bus: bluez: add org.bluez.Network1 --- stubs/pydbus/pydbus/bus.pyi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/stubs/pydbus/pydbus/bus.pyi b/stubs/pydbus/pydbus/bus.pyi index 407ab283d658..16ccc4763e6f 100644 --- a/stubs/pydbus/pydbus/bus.pyi +++ b/stubs/pydbus/pydbus/bus.pyi @@ -122,6 +122,12 @@ class OrgBluezLEAdvertisingManager1Dict(TypedDict, total=False): 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/* _OrgBluezDict = TypedDict( "_OrgBluezDict", @@ -135,6 +141,7 @@ _OrgBluezDict = TypedDict( "org.bluez.Input1": OrgBluezInput1Dict, "org.bluez.LEAdvertisingManager1": OrgBluezLEAdvertisingManager1Dict, "org.bluez.Media1": OrgBluezMedia1Dict, + "org.bluez.Network1": OrgBluezNetwork1Dict, "org.bluez.MediaControl1": OrgBluezMediaControl1Dict, # The following always appear as empty dictionaries on my system. "org.bluez.AgentManager1": dict[str, Any], From 52f947c4c7496b2e0a6ffb760758daeb85c91f67 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Tue, 6 May 2025 16:25:33 -0400 Subject: [PATCH 11/22] pydbus.bus: bluez: better notes on the empty dicts --- stubs/pydbus/pydbus/bus.pyi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stubs/pydbus/pydbus/bus.pyi b/stubs/pydbus/pydbus/bus.pyi index 16ccc4763e6f..e286ce758131 100644 --- a/stubs/pydbus/pydbus/bus.pyi +++ b/stubs/pydbus/pydbus/bus.pyi @@ -129,6 +129,7 @@ class OrgBluezNetwork1Dict(TypedDict, total=False): 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", { @@ -143,7 +144,8 @@ _OrgBluezDict = TypedDict( "org.bluez.Media1": OrgBluezMedia1Dict, "org.bluez.Network1": OrgBluezNetwork1Dict, "org.bluez.MediaControl1": OrgBluezMediaControl1Dict, - # The following always appear as empty dictionaries on my system. + # 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], From 479ce7f5f0795f59226b9c92f239eb1c2d02e4b0 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 7 May 2025 12:29:41 +0200 Subject: [PATCH 12/22] Add stubtest APT dependencies --- stubs/pydbus/METADATA.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/stubs/pydbus/METADATA.toml b/stubs/pydbus/METADATA.toml index f158414ca994..104d8aa31f26 100644 --- a/stubs/pydbus/METADATA.toml +++ b/stubs/pydbus/METADATA.toml @@ -4,3 +4,4 @@ upstream_repository = "https://github.com/LEW21/pydbus" [tool.stubtest] stubtest_requirements = ["pygobject"] +apt_dependencies = ["libcairo2-dev"] From 52d66ede347d19085c6b557a53ffc97f0a593736 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 7 May 2025 12:32:33 +0200 Subject: [PATCH 13/22] Add libgirepository1.0-dev APT dependency --- stubs/pydbus/METADATA.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/pydbus/METADATA.toml b/stubs/pydbus/METADATA.toml index 104d8aa31f26..9b0cf6918114 100644 --- a/stubs/pydbus/METADATA.toml +++ b/stubs/pydbus/METADATA.toml @@ -4,4 +4,4 @@ upstream_repository = "https://github.com/LEW21/pydbus" [tool.stubtest] stubtest_requirements = ["pygobject"] -apt_dependencies = ["libcairo2-dev"] +apt_dependencies = ["libcairo2-dev", "libgirepository1.0-dev"] From e2d6cb9c97bf9cfe5120003d52d91fe4a4d02f01 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Wed, 7 May 2025 12:36:51 +0200 Subject: [PATCH 14/22] Use libgirepository-2.0-dev --- stubs/pydbus/METADATA.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/pydbus/METADATA.toml b/stubs/pydbus/METADATA.toml index 9b0cf6918114..b32563e3b23c 100644 --- a/stubs/pydbus/METADATA.toml +++ b/stubs/pydbus/METADATA.toml @@ -4,4 +4,4 @@ upstream_repository = "https://github.com/LEW21/pydbus" [tool.stubtest] stubtest_requirements = ["pygobject"] -apt_dependencies = ["libcairo2-dev", "libgirepository1.0-dev"] +apt_dependencies = ["libcairo2-dev", "libgirepository-2.0-dev"] From fb0e57b64a930d275a76c992871225c657dcf46e Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 07:12:57 -0400 Subject: [PATCH 15/22] pydbus: do not export re-export Variant --- stubs/pydbus/pydbus/__init__.pyi | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stubs/pydbus/pydbus/__init__.pyi b/stubs/pydbus/pydbus/__init__.pyi index d3dc290c59ea..f65af167cf7a 100644 --- a/stubs/pydbus/pydbus/__init__.pyi +++ b/stubs/pydbus/pydbus/__init__.pyi @@ -1,5 +1,3 @@ -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"] +__all__ = ["SessionBus", "SystemBus", "connect"] From 072292b48c9e02dedfff5c507701278cc01a0ef0 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 07:14:00 -0400 Subject: [PATCH 16/22] pydbus: move get() overloads to correct location --- stubs/pydbus/pydbus/bus.pyi | 174 +-------------------------------- stubs/pydbus/pydbus/proxy.pyi | 175 +++++++++++++++++++++++++++++++++- 2 files changed, 174 insertions(+), 175 deletions(-) diff --git a/stubs/pydbus/pydbus/bus.pyi b/stubs/pydbus/pydbus/bus.pyi index e286ce758131..24e14aa0f474 100644 --- a/stubs/pydbus/pydbus/bus.pyi +++ b/stubs/pydbus/pydbus/bus.pyi @@ -1,19 +1,17 @@ import types -from typing import Any, Generic, Literal, TypedDict, TypeVar, overload, type_check_only +from typing import TypeVar, 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 .proxy import ProxyMixin 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]: ... @@ -31,166 +29,6 @@ class _DBusOrgFreedesktopPolicyKit1Authority: 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 @@ -205,14 +43,6 @@ class Bus(ProxyMixin[_T], RequestNameMixin[_T], OwnMixin, WatchMixin, Subscripti 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]: ... diff --git a/stubs/pydbus/pydbus/proxy.pyi b/stubs/pydbus/pydbus/proxy.pyi index d403414c550c..591d112d192d 100644 --- a/stubs/pydbus/pydbus/proxy.pyi +++ b/stubs/pydbus/pydbus/proxy.pyi @@ -1,13 +1,142 @@ -from typing import Generic, TypeVar, type_check_only +from typing import Any, Generic, Literal, TypedDict, TypeVar, overload, type_check_only from typing_extensions import Self from xml.etree.ElementTree import Element from .bus import Bus +from .generic import bound_signal _T = TypeVar("_T") +_ST = TypeVar("_ST") # signal type -class ProxyMixin(Generic[_T]): - def get(self, bus_name: str, object_path: str | None = None, *, timeout: int | None = None) -> _CompositeObject[_T]: ... +@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]: ... class ProxyObject(Generic[_T]): def __init__(self, bus: Bus[_T], bus_name: str, path: str, object: Self | None = None) -> None: ... @@ -18,6 +147,46 @@ class ProxyObject(Generic[_T]): class _CompositeObject(ProxyObject[_T]): # Inside CompositeInterface def __getitem__(self, iface: str) -> ProxyObject[_T]: ... +@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 ProxyMixin(Generic[_T]): + @overload + 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 | None = None, *, timeout: int | None = None) -> _CompositeObject[_T]: ... + @type_check_only class _interface(ProxyObject[_T]): # inside Interface @staticmethod From 84655bb0cb869f54d9ca9126606d03010a3e01cc Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 07:20:58 -0400 Subject: [PATCH 17/22] pydbus.generic: bound_signal: add missing field --- stubs/pydbus/pydbus/generic.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/stubs/pydbus/pydbus/generic.pyi b/stubs/pydbus/pydbus/generic.pyi index 8b0336875fd5..1cad2074387d 100644 --- a/stubs/pydbus/pydbus/generic.pyi +++ b/stubs/pydbus/pydbus/generic.pyi @@ -20,6 +20,7 @@ _T = TypeVar("_T") class bound_signal(Generic[_T]): __signal__: signal[_T] + __self__: _T def __init__(self, signal: signal[_T], instance: _T) -> None: ... @property From f4d3bddd3c9d8e9b8fef6912b95248c0c1c8294d Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 07:21:36 -0400 Subject: [PATCH 18/22] pydbus.identifier: isident: fix declaration --- stubs/pydbus/pydbus/identifier.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/pydbus/pydbus/identifier.pyi b/stubs/pydbus/pydbus/identifier.pyi index 32b5d6a1eea0..480a2c9e341b 100644 --- a/stubs/pydbus/pydbus/identifier.pyi +++ b/stubs/pydbus/pydbus/identifier.pyi @@ -1,2 +1,2 @@ -def isident(s: str) -> bool: ... +def isident(s: str, /) -> bool: ... def filter_identifier(name: str) -> str: ... From 10c154535694ddf5afbc31cb926c263eb5be8f01 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 07:26:28 -0400 Subject: [PATCH 19/22] pydbus: add stubtest_allowlist --- stubs/pydbus/@tests/stubtest_allowlist.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 stubs/pydbus/@tests/stubtest_allowlist.txt diff --git a/stubs/pydbus/@tests/stubtest_allowlist.txt b/stubs/pydbus/@tests/stubtest_allowlist.txt new file mode 100644 index 000000000000..b6974a709d1b --- /dev/null +++ b/stubs/pydbus/@tests/stubtest_allowlist.txt @@ -0,0 +1,10 @@ +# Probably not an intentional export +pydbus.Variant +# This is only used to add a method to Gio.DBusConnection. Likely only for internal use. +pydbus.bus.pydbus_property +# Flag value default is literal 0, but we can use nice enums for these +pydbus.bus_names.OwnMixin.own_name +pydbus.bus_names.WatchMixin.watch_name +pydbus.subscription.SubscriptionMixin.subscribe +# Necessary to access D-Bus methods and properties +pydbus.proxy.ProxyObject.__getattr__ From eac59229f25c52e6eb0f1726dcd8af44891d2bbc Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 08:13:47 -0400 Subject: [PATCH 20/22] pydbus.proxy: fix names of typecheck-only types --- stubs/pydbus/pydbus/proxy.pyi | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/stubs/pydbus/pydbus/proxy.pyi b/stubs/pydbus/pydbus/proxy.pyi index 591d112d192d..a241c8b90869 100644 --- a/stubs/pydbus/pydbus/proxy.pyi +++ b/stubs/pydbus/pydbus/proxy.pyi @@ -9,7 +9,7 @@ _T = TypeVar("_T") _ST = TypeVar("_ST") # signal type @type_check_only -class OrgBluezAdapter1Dict(TypedDict, total=False): +class _OrgBluezAdapter1Dict(TypedDict, total=False): # noqa: Y049 Address: str AddressType: Literal["public", "random"] Alias: str @@ -29,7 +29,7 @@ class OrgBluezAdapter1Dict(TypedDict, total=False): Version: int @type_check_only -class OrgBluezDevice1Dict(TypedDict, total=False): +class _OrgBluezDevice1Dict(TypedDict, total=False): # noqa: Y049 Adapter: str Address: str AddressType: Literal["public", "random"] @@ -50,24 +50,24 @@ class OrgBluezDevice1Dict(TypedDict, total=False): WakeAllowed: bool @type_check_only -class OrgBluezInput1Dict(TypedDict, total=False): +class _OrgBluezInput1Dict(TypedDict, total=False): # noqa: Y049 ReconnectMode: str @type_check_only -class OrgBluezMedia1Dict(TypedDict, total=False): +class _OrgBluezMedia1Dict(TypedDict, total=False): # noqa: Y049 SupportedUUIDs: list[str] @type_check_only -class OrgBluezMediaControl1Dict(TypedDict, total=False): +class _OrgBluezMediaControl1Dict(TypedDict, total=False): # noqa: Y049 Connected: bool @type_check_only -class OrgBluezBattery1Dict(TypedDict, total=False): +class _OrgBluezBattery1Dict(TypedDict, total=False): # noqa: Y049 Percentage: int Source: str @type_check_only -class OrgBluezGattService1Dict(TypedDict, total=False): +class _OrgBluezGattService1Dict(TypedDict, total=False): # noqa: Y049 Device: str Handle: int Includes: list[str] @@ -75,7 +75,7 @@ class OrgBluezGattService1Dict(TypedDict, total=False): UUID: str @type_check_only -class OrgBluezGattCharacteristic1Dict(TypedDict, total=False): +class _OrgBluezGattCharacteristic1Dict(TypedDict, total=False): # noqa: Y049 Flags: list[str] Handle: int MTU: int @@ -84,14 +84,14 @@ class OrgBluezGattCharacteristic1Dict(TypedDict, total=False): Value: list[int] @type_check_only -class OrgBluezGattDescriptor1Dict(TypedDict, total=False): +class _OrgBluezGattDescriptor1Dict(TypedDict, total=False): # noqa: Y049 Characteristic: str Handle: int UUID: str Value: list[int] @type_check_only -class OrgBluezLEAdvertisingManager1Dict(TypedDict, total=False): +class _OrgBluezLEAdvertisingManager1Dict(TypedDict, total=False): # noqa: Y049 ActiveInstances: int SupportedCapabilities: dict[str, int] # e.g. MaxTxPower: 7 SupportedFeatures: list[str] @@ -100,7 +100,7 @@ class OrgBluezLEAdvertisingManager1Dict(TypedDict, total=False): SupportedSecondaryChannels: list[str] @type_check_only -class OrgBluezNetwork1Dict(TypedDict, total=False): +class _OrgBluezNetwork1Dict(TypedDict, total=False): # noqa: Y049 Connected: bool Interface: str UUID: str @@ -110,17 +110,17 @@ class OrgBluezNetwork1Dict(TypedDict, total=False): _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, + "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], From ce7e0c3801391ce7749e3a076156fb10c595cbed Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 08:44:42 -0400 Subject: [PATCH 21/22] pydbus: add __all__ to stubtest_allowlist --- stubs/pydbus/@tests/stubtest_allowlist.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/pydbus/@tests/stubtest_allowlist.txt b/stubs/pydbus/@tests/stubtest_allowlist.txt index b6974a709d1b..4d74dce9fceb 100644 --- a/stubs/pydbus/@tests/stubtest_allowlist.txt +++ b/stubs/pydbus/@tests/stubtest_allowlist.txt @@ -1,5 +1,5 @@ -# Probably not an intentional export -pydbus.Variant +# Ignore Variant as it is probably not an intentional export +pydbus.__all__ # This is only used to add a method to Gio.DBusConnection. Likely only for internal use. pydbus.bus.pydbus_property # Flag value default is literal 0, but we can use nice enums for these From c20c3dbbd037fa98a2837e5a05d942a0ca659050 Mon Sep 17 00:00:00 2001 From: Andrew Udvare Date: Wed, 7 May 2025 11:03:24 -0400 Subject: [PATCH 22/22] pydbus: add Variant to stubtest_allowlist --- stubs/pydbus/@tests/stubtest_allowlist.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stubs/pydbus/@tests/stubtest_allowlist.txt b/stubs/pydbus/@tests/stubtest_allowlist.txt index 4d74dce9fceb..ee0645122709 100644 --- a/stubs/pydbus/@tests/stubtest_allowlist.txt +++ b/stubs/pydbus/@tests/stubtest_allowlist.txt @@ -1,5 +1,6 @@ -# Ignore Variant as it is probably not an intentional export +# Ignore Variant as it is probably not an intentional export= pydbus.__all__ +pydbus.Variant # This is only used to add a method to Gio.DBusConnection. Likely only for internal use. pydbus.bus.pydbus_property # Flag value default is literal 0, but we can use nice enums for these