From 5bae5cdddb5ec8cc5878744de9ed24606e7a13f8 Mon Sep 17 00:00:00 2001 From: Christopher Schramm Date: Fri, 13 Dec 2024 15:17:39 +0100 Subject: [PATCH] More direct types Replace all imports from typing_extensions with standard typing, move them out of TYPE_CHECKING checks and use defined types directly instead of as strings --- blueman/bluemantyping.py | 11 ++--- blueman/gui/CommonUi.py | 9 ++-- blueman/gui/GenericList.py | 24 ++++------ blueman/gui/applet/PluginDialog.py | 9 ++-- blueman/main/DbusService.py | 7 +-- blueman/main/PulseAudioUtils.py | 46 ++++++++++--------- blueman/main/applet/BluezAgent.py | 9 ++-- blueman/main/indicators/GtkStatusIcon.py | 21 ++++----- blueman/main/indicators/IndicatorInterface.py | 6 +-- blueman/main/indicators/StatusNotifierItem.py | 24 +++++----- blueman/plugins/BasePlugin.py | 33 ++++++------- blueman/plugins/ServicePlugin.py | 6 +-- blueman/plugins/applet/Menu.py | 32 ++++++------- blueman/plugins/applet/PulseAudioProfile.py | 33 ++++++------- blueman/plugins/applet/RecentConns.py | 41 ++++++++--------- blueman/plugins/applet/TransferService.py | 32 ++++++------- blueman/plugins/manager/PulseAudioProfile.py | 11 ++--- blueman/plugins/services/Network.py | 7 +-- stubs/_blueman.pyi | 3 +- 19 files changed, 163 insertions(+), 201 deletions(-) diff --git a/blueman/bluemantyping.py b/blueman/bluemantyping.py index 47ff8cfef..a2426aee7 100644 --- a/blueman/bluemantyping.py +++ b/blueman/bluemantyping.py @@ -1,16 +1,15 @@ -from typing import Union, TYPE_CHECKING, NewType +from typing import NewType, Protocol, Union from gi.repository import GObject -if TYPE_CHECKING: - from typing_extensions import Protocol - class _HasGType(Protocol): - __gtype__: GObject.GType +class _HasGType(Protocol): + __gtype__: GObject.GType + # Actually supported types are int, bool, str, float, and object but no subclasses, see # https://github.com/GNOME/pygobject/blob/ac576400ecd554879c906791e6638d64bb8bcc2a/gi/pygi-type.c#L498 # (We shield the possibility to provide a str to avoid errors) -GSignals = dict[str, tuple[GObject.SignalFlags, None, tuple[Union[None, type, GObject.GType, "_HasGType"], ...]]] +GSignals = dict[str, tuple[GObject.SignalFlags, None, tuple[Union[None, type, GObject.GType, _HasGType], ...]]] ObjectPath = NewType("ObjectPath", str) BtAddress = NewType("BtAddress", str) diff --git a/blueman/gui/CommonUi.py b/blueman/gui/CommonUi.py index 1c6e60d6b..f2ae68bc4 100644 --- a/blueman/gui/CommonUi.py +++ b/blueman/gui/CommonUi.py @@ -1,6 +1,6 @@ from datetime import datetime from gettext import gettext as _ -from typing import overload, TYPE_CHECKING +from typing import overload, Literal from blueman.Constants import WEBSITE, VERSION @@ -8,9 +8,6 @@ gi.require_version("Gtk", "3.0") from gi.repository import Gtk -if TYPE_CHECKING: - from typing import Literal - class ErrorDialog(Gtk.MessageDialog): def __init__(self, markup: str, secondary_markup: str | None = None, excp: object | None = None, @@ -40,12 +37,12 @@ def __init__(self, markup: str, secondary_markup: str | None = None, excp: objec @overload -def show_about_dialog(app_name: str, run: "Literal[True]" = True, parent: Gtk.Window | None = None) -> None: +def show_about_dialog(app_name: str, run: Literal[True] = True, parent: Gtk.Window | None = None) -> None: ... @overload -def show_about_dialog(app_name: str, run: "Literal[False]", parent: Gtk.Window | None = None) -> Gtk.AboutDialog: +def show_about_dialog(app_name: str, run: Literal[False], parent: Gtk.Window | None = None) -> Gtk.AboutDialog: ... diff --git a/blueman/gui/GenericList.py b/blueman/gui/GenericList.py index fb9aec0d7..f4d0ce225 100644 --- a/blueman/gui/GenericList.py +++ b/blueman/gui/GenericList.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Any +from typing import Any, TypedDict from collections.abc import Iterable, Mapping, Callable, Collection import gi @@ -6,21 +6,17 @@ from gi.repository import Gtk -if TYPE_CHECKING: - from typing_extensions import TypedDict +class _ListDataDictBase(TypedDict): + id: str + type: type - class _ListDataDictBase(TypedDict): - id: str - type: type - class ListDataDict(_ListDataDictBase, total=False): - renderer: Gtk.CellRenderer - render_attrs: Mapping[str, int] - view_props: Mapping[str, object] - celldata_func: tuple[Callable[[Gtk.TreeViewColumn, Gtk.CellRenderer, Gtk.TreeModelFilter, Gtk.TreeIter, Any], - None], Any] -else: - ListDataDict = dict +class ListDataDict(_ListDataDictBase, total=False): + renderer: Gtk.CellRenderer + render_attrs: Mapping[str, int] + view_props: Mapping[str, object] + celldata_func: tuple[Callable[[Gtk.TreeViewColumn, Gtk.CellRenderer, Gtk.TreeModelFilter, Gtk.TreeIter, Any], + None], Any] # noinspection PyAttributeOutsideInit diff --git a/blueman/gui/applet/PluginDialog.py b/blueman/gui/applet/PluginDialog.py index 1dcd478ce..d6579501f 100644 --- a/blueman/gui/applet/PluginDialog.py +++ b/blueman/gui/applet/PluginDialog.py @@ -5,14 +5,15 @@ from blueman.main.Builder import Builder from blueman.main.PluginManager import PluginManager from blueman.plugins.AppletPlugin import AppletPlugin +from blueman.plugins.BasePlugin import Option, BasePlugin import gi gi.require_version("Gtk", "3.0") gi.require_version("Gdk", "3.0") from gi.repository import Gtk, Gdk, Gio, GLib, GObject + if TYPE_CHECKING: - from blueman.plugins.BasePlugin import Option, BasePlugin from blueman.main.Applet import BluemanApplet @@ -69,13 +70,13 @@ def construct_settings(self) -> None: label = Gtk.Label(label="" + v["desc"] + "", wrap=True, use_markup=True, xalign=0.0) self.pack_start(label, False, False, 0) - def handle_change(self, widget: Gtk.Widget, opt: str, params: "Option", prop: str) -> None: + def handle_change(self, widget: Gtk.Widget, opt: str, params: Option, prop: str) -> None: val = params["type"](getattr(widget.props, prop)) logging.debug(f"changed {opt} {val}") self.inst.set_option(opt, val) - def get_control_widget(self, opt: str, params: "Option") -> Gtk.Widget: + def get_control_widget(self, opt: str, params: Option) -> Gtk.Widget: if params["type"] == bool: c = Gtk.CheckButton(label=params["name"]) @@ -329,7 +330,7 @@ def populate(self) -> None: self.model.insert_sorted(plugin_item, self._model_sort_func) self.listbox.select_row(self.listbox.get_row_at_index(0)) - _T = TypeVar("_T", bound="BasePlugin") + _T = TypeVar("_T", bound=BasePlugin) def plugin_state_changed(self, _plugins: PluginManager[_T], name: str, loaded: bool) -> None: logging.debug(f"{name} {loaded}") diff --git a/blueman/main/DbusService.py b/blueman/main/DbusService.py index 839241546..e60db375b 100644 --- a/blueman/main/DbusService.py +++ b/blueman/main/DbusService.py @@ -1,14 +1,11 @@ import logging import sys import traceback -from typing import Any, overload, TYPE_CHECKING +from typing import Any, overload, Literal from collections.abc import Callable, Collection, Mapping from gi.repository import Gio, GLib -if TYPE_CHECKING: - from typing import Literal - class DbusError(Exception): _name = "org.blueman.Error" @@ -46,7 +43,7 @@ def add_method(self, name: str, arguments: tuple[str, ...], return_values: str | @overload def add_method(self, name: str, arguments: tuple[str, ...], return_values: tuple[str, ...], method: Callable[..., Any], pass_sender: bool = False, - is_async: "Literal[False]" = False) -> None: + is_async: Literal[False] = False) -> None: ... def add_method(self, name: str, arguments: tuple[str, ...], return_values: str | tuple[str, ...], diff --git a/blueman/main/PulseAudioUtils.py b/blueman/main/PulseAudioUtils.py index de1fc122a..3f927ac65 100644 --- a/blueman/main/PulseAudioUtils.py +++ b/blueman/main/PulseAudioUtils.py @@ -1,6 +1,6 @@ from ctypes import * from enum import IntEnum -from typing import TYPE_CHECKING, Optional, Any +from typing import TYPE_CHECKING, Any, Optional, TypedDict from collections.abc import Callable, Mapping from gi.repository import GObject @@ -19,23 +19,25 @@ if TYPE_CHECKING: from ctypes import _FuncPointer, _NamedFuncPointer, _Pointer - from typing_extensions import TypedDict - - class CardProfileInfo(TypedDict): - name: str - description: str - n_sinks: int - n_sources: int - priority: int - - class CardInfo(TypedDict): - name: str - proplist: dict[str, str] - owner_module: int - driver: str - index: int - profiles: list[CardProfileInfo] - active_profile: str + + +class CardProfileInfo(TypedDict): + name: str + description: str + n_sinks: int + n_sources: int + priority: int + + +class CardInfo(TypedDict): + name: str + proplist: dict[str, str] + owner_module: int + driver: str + index: int + profiles: list[CardProfileInfo] + active_profile: str + pa_glib_mainloop_new = libpulse_glib.pa_glib_mainloop_new pa_glib_mainloop_new.argtypes = [c_void_p] @@ -284,7 +286,7 @@ def wrapper(_context: c_void_p, res: int, data: "_FuncPointer") -> None: logging.error(func.__name__) pa_operation_unref(op) - def __card_info(self, card_info: "_Pointer[PaCardInfo]") -> "CardInfo": + def __card_info(self, card_info: "_Pointer[PaCardInfo]") -> CardInfo: return { "name": card_info[0].name.decode("UTF-8"), "proplist": self.__get_proplist(card_info[0].proplist), @@ -301,10 +303,10 @@ def __card_info(self, card_info: "_Pointer[PaCardInfo]") -> "CardInfo": "active_profile": card_info[0].active_profile[0].name.decode("UTF-8") } - def list_cards(self, callback: Callable[[Mapping[str, "CardInfo"]], None]) -> None: + def list_cards(self, callback: Callable[[Mapping[str, CardInfo]], None]) -> None: self.check_connected() - data: dict[str, "CardInfo"] = {} + data: dict[str, CardInfo] = {} def handler(entry_info: Optional["_Pointer[PaCardInfo]"], end: bool) -> None: if end: @@ -319,7 +321,7 @@ def handler(entry_info: Optional["_Pointer[PaCardInfo]"], end: bool) -> None: self.__init_list_callback(pa_context_get_card_info_list, pa_card_info_cb_t, handler) - def get_card(self, card: int, callback: Callable[["CardInfo"], None]) -> None: + def get_card(self, card: int, callback: Callable[[CardInfo], None]) -> None: self.check_connected() def handler(entry_info: Optional["_Pointer[PaCardInfo]"], end: bool) -> None: diff --git a/blueman/main/applet/BluezAgent.py b/blueman/main/applet/BluezAgent.py index 1ebe374db..9c782f8e9 100644 --- a/blueman/main/applet/BluezAgent.py +++ b/blueman/main/applet/BluezAgent.py @@ -2,7 +2,7 @@ from gettext import gettext as _ from html import escape from xml.etree import ElementTree -from typing import overload, TYPE_CHECKING, Any +from typing import overload, Any, Literal from collections.abc import Callable from blueman.bluemantyping import ObjectPath @@ -20,9 +20,6 @@ gi.require_version("Gtk", "3.0") from gi.repository import Gtk -if TYPE_CHECKING: - from typing import Literal - class BluezErrorCanceled(DbusError): _name = "org.bluez.Error.Canceled" @@ -104,7 +101,7 @@ def get_device_string(self, object_path: ObjectPath) -> str: def ask_passkey( self, dialog_msg: str, - is_numeric: "Literal[True]", + is_numeric: Literal[True], object_path: ObjectPath, ok: Callable[[int], None], err: Callable[[BluezErrorCanceled | BluezErrorRejected], None] @@ -115,7 +112,7 @@ def ask_passkey( def ask_passkey( self, dialog_msg: str, - is_numeric: "Literal[False]", + is_numeric: Literal[False], object_path: ObjectPath, ok: Callable[[str], None], err: Callable[[BluezErrorCanceled | BluezErrorRejected], None] diff --git a/blueman/main/indicators/GtkStatusIcon.py b/blueman/main/indicators/GtkStatusIcon.py index b7b8d75f6..79bca5d85 100644 --- a/blueman/main/indicators/GtkStatusIcon.py +++ b/blueman/main/indicators/GtkStatusIcon.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, overload, cast +from typing import overload, cast, Protocol from collections.abc import Callable, Iterable import gi @@ -9,28 +9,25 @@ gi.require_version("Gtk", "3.0") from gi.repository import Gtk from blueman.Functions import create_menuitem +from blueman.plugins.applet.Menu import MenuItemDict, SubmenuItemDict -if TYPE_CHECKING: - from typing_extensions import Protocol - from blueman.plugins.applet.Menu import MenuItemDict, SubmenuItemDict - - class MenuItemActivator(Protocol): - def __call__(self, *idx: int) -> None: - ... +class MenuItemActivator(Protocol): + def __call__(self, *idx: int) -> None: + ... @overload -def build_menu(items: Iterable[tuple[int, "MenuItemDict"]], activate: "MenuItemActivator") -> Gtk.Menu: +def build_menu(items: Iterable[tuple[int, MenuItemDict]], activate: MenuItemActivator) -> Gtk.Menu: ... @overload -def build_menu(items: Iterable[tuple[int, "SubmenuItemDict"]], activate: Callable[[int], None]) -> Gtk.Menu: +def build_menu(items: Iterable[tuple[int, SubmenuItemDict]], activate: Callable[[int], None]) -> Gtk.Menu: ... -def build_menu(items: Iterable[tuple[int, "SubmenuItemDict"]], activate: Callable[..., None]) -> Gtk.Menu: +def build_menu(items: Iterable[tuple[int, SubmenuItemDict]], activate: Callable[..., None]) -> Gtk.Menu: menu = Gtk.Menu() for index, item in items: if 'text' in item and 'icon_name' in item: @@ -91,5 +88,5 @@ def _update_tooltip(self) -> None: def set_visibility(self, visible: bool) -> None: self.indicator.props.visible = visible - def set_menu(self, menu: Iterable["MenuItemDict"]) -> None: + def set_menu(self, menu: Iterable[MenuItemDict]) -> None: self._menu = build_menu(((item["id"], item) for item in menu), self._on_activate) diff --git a/blueman/main/indicators/IndicatorInterface.py b/blueman/main/indicators/IndicatorInterface.py index 1e67d46c0..c77938ac8 100644 --- a/blueman/main/indicators/IndicatorInterface.py +++ b/blueman/main/indicators/IndicatorInterface.py @@ -1,9 +1,7 @@ from abc import abstractmethod, ABCMeta -from typing import TYPE_CHECKING from collections.abc import Iterable -if TYPE_CHECKING: - from blueman.plugins.applet.Menu import MenuItemDict +from blueman.plugins.applet.Menu import MenuItemDict class IndicatorNotAvailable(RuntimeError): @@ -28,5 +26,5 @@ def set_visibility(self, visible: bool) -> None: ... @abstractmethod - def set_menu(self, menu: Iterable["MenuItemDict"]) -> None: + def set_menu(self, menu: Iterable[MenuItemDict]) -> None: ... diff --git a/blueman/main/indicators/StatusNotifierItem.py b/blueman/main/indicators/StatusNotifierItem.py index 42a55088b..d45bec5f0 100644 --- a/blueman/main/indicators/StatusNotifierItem.py +++ b/blueman/main/indicators/StatusNotifierItem.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from typing import TYPE_CHECKING, Union, TypeVar, Any +from typing import Union, TypeVar, Any from collections.abc import Iterable, Callable from gi.repository import Gio, GLib, Pango @@ -7,16 +7,14 @@ from blueman.main.DbusService import DbusService from blueman.main.Tray import BluemanTray from blueman.main.indicators.IndicatorInterface import IndicatorInterface, IndicatorNotAvailable - -if TYPE_CHECKING: - from blueman.plugins.applet.Menu import MenuItemDict, SubmenuItemDict - from blueman.main.indicators.GtkStatusIcon import MenuItemActivator +from blueman.main.indicators.GtkStatusIcon import MenuItemActivator +from blueman.plugins.applet.Menu import MenuItemDict, SubmenuItemDict class MenuService(DbusService): - def __init__(self, on_activate_menu_item: "MenuItemActivator") -> None: + def __init__(self, on_activate_menu_item: MenuItemActivator) -> None: super().__init__(None, "com.canonical.dbusmenu", "/org/blueman/sni/menu", Gio.BusType.SESSION) - self._items: OrderedDict[int, "MenuItemDict"] = OrderedDict() + self._items: OrderedDict[int, MenuItemDict] = OrderedDict() self._revision = 0 self._revision_advertised = -1 self._on_activate = on_activate_menu_item @@ -33,7 +31,7 @@ def __init__(self, on_activate_menu_item: "MenuItemActivator") -> None: GLib.timeout_add(100, self._advertise_revision) - def set_items(self, items: Iterable["MenuItemDict"]) -> None: + def set_items(self, items: Iterable[MenuItemDict]) -> None: self._items = OrderedDict((item["id"], item) for item in items) self._revision += 1 @@ -54,26 +52,26 @@ def _get_layout(self, parent_id: int, _recursion_depth: int, _property_names: li return self._revision, (parent_id, self._render_item(item), self._render_submenu(item, parent_id)) return self._revision, (parent_id, self._render_item(item), []) - def _render_submenu(self, item: "MenuItemDict", idx: int) -> list[GLib.Variant]: + def _render_submenu(self, item: MenuItemDict, idx: int) -> list[GLib.Variant]: if "submenu" in item: return self._render_menu(enumerate(item["submenu"], idx + 1), lambda _item, _isx: []) else: return [] - _T = TypeVar("_T", bound="SubmenuItemDict") + _T = TypeVar("_T", bound=SubmenuItemDict) def _render_menu(self, items: Iterable[tuple[int, _T]], submenu_callback: Callable[[_T, int], list[GLib.Variant]] ) -> list[GLib.Variant]: return [GLib.Variant("(ia{sv}av)", (idx, self._render_item(item), submenu_callback(item, idx))) for (idx, item) in items] - def _iterate_items(self) -> Iterable[tuple[int, "SubmenuItemDict"]]: + def _iterate_items(self) -> Iterable[tuple[int, SubmenuItemDict]]: for item in self._items.values(): yield item["id"] << 8, item if "submenu" in item: yield from enumerate(item["submenu"], (item["id"] << 8) + 1) - def _render_item(self, item: Union["MenuItemDict", "SubmenuItemDict"]) -> dict[str, GLib.Variant]: + def _render_item(self, item: Union[MenuItemDict, SubmenuItemDict]) -> dict[str, GLib.Variant]: if "text" in item and "icon_name" in item: label = Pango.parse_markup(item["text"], -1, "\0")[2] if item.get("markup", False) else item["text"] props = { @@ -175,5 +173,5 @@ def set_visibility(self, visible: bool) -> None: self._sni.Status = status = "Active" if visible else "Passive" self._sni.emit_signal("NewStatus", status) - def set_menu(self, menu: Iterable["MenuItemDict"]) -> None: + def set_menu(self, menu: Iterable[MenuItemDict]) -> None: self._sni.menu.set_items(menu) diff --git a/blueman/plugins/BasePlugin.py b/blueman/plugins/BasePlugin.py index bb0e645a7..626f8f07e 100644 --- a/blueman/plugins/BasePlugin.py +++ b/blueman/plugins/BasePlugin.py @@ -1,29 +1,26 @@ import logging import weakref from gettext import gettext as _ -from typing import TYPE_CHECKING, Any, TypeVar +from typing import Any, TypeVar, TypedDict from gi.repository import Gio -if TYPE_CHECKING: - from typing_extensions import TypedDict +# type is actually Type[T] and default is T but this is not supported by Python 3.10's typing.TypedDict +class OptionBase(TypedDict): + type: type + default: Any - # type is actually Type[T] and default is T but this is not supported https://github.com/python/mypy/issues/3863 - class OptionBase(TypedDict): - type: type - default: Any - class Option(OptionBase, total=False): - name: str - desc: str - range: tuple[int, int] +class Option(OptionBase, total=False): + name: str + desc: str + range: tuple[int, int] - class GSettings(TypedDict): - schema: str - path: None -else: - Option = dict + +class GSettings(TypedDict): + schema: str + path: None class BasePlugin: @@ -39,9 +36,9 @@ class BasePlugin: __instance__ = None - __gsettings__: "GSettings" + __gsettings__: GSettings - __options__: dict[str, "Option"] = {} + __options__: dict[str, Option] = {} def __init__(self, *_args: object) -> None: if self.__options__: diff --git a/blueman/plugins/ServicePlugin.py b/blueman/plugins/ServicePlugin.py index d8c13f37f..7ece200c3 100644 --- a/blueman/plugins/ServicePlugin.py +++ b/blueman/plugins/ServicePlugin.py @@ -1,4 +1,4 @@ -from typing import Union, TYPE_CHECKING +from typing import Literal, Union, TYPE_CHECKING import gi @@ -6,8 +6,6 @@ from gi.repository import Gtk if TYPE_CHECKING: - from typing import Literal - from blueman.main.Services import BluemanServices @@ -48,7 +46,7 @@ def on_unload(self) -> None: pass # return true if apply button should be sensitive or false if not. -1 to force disabled - def on_query_apply_state(self) -> Union[bool, "Literal[-1]"]: + def on_query_apply_state(self) -> Union[bool, Literal[-1]]: return False def on_apply(self) -> None: diff --git a/blueman/plugins/applet/Menu.py b/blueman/plugins/applet/Menu.py index 1df16bfb5..f3a171d14 100644 --- a/blueman/plugins/applet/Menu.py +++ b/blueman/plugins/applet/Menu.py @@ -1,33 +1,33 @@ from gettext import gettext as _ -from typing import TYPE_CHECKING from collections.abc import Iterable, Callable, Iterator, Mapping, Sequence +from typing import TypedDict from gi.repository import GLib from blueman.plugins.AppletPlugin import AppletPlugin -if TYPE_CHECKING: - from typing_extensions import TypedDict - class SubmenuItemDict(TypedDict): - text: str - markup: bool - icon_name: str - sensitive: bool - tooltip: str | None - callback: Callable[[], None] +class SubmenuItemDict(TypedDict): + text: str + markup: bool + icon_name: str + sensitive: bool + tooltip: str | None + callback: Callable[[], None] - class MenuItemDictBase(SubmenuItemDict): - id: int - class MenuItemDict(MenuItemDictBase, total=False): - submenu: Iterable["SubmenuItemDict"] +class MenuItemDictBase(SubmenuItemDict): + id: int + + +class MenuItemDict(MenuItemDictBase, total=False): + submenu: Iterable[SubmenuItemDict] class MenuItem: def __init__(self, menu_plugin: "Menu", owner: AppletPlugin, priority: tuple[int, int], text: str | None, markup: bool, icon_name: str | None, tooltip: str | None, callback: Callable[[], None] | None, - submenu_function: Callable[[], Iterable["SubmenuItemDict"]] | None, visible: bool, sensitive: bool): + submenu_function: Callable[[], Iterable[SubmenuItemDict]] | None, visible: bool, sensitive: bool): self._menu_plugin = menu_plugin self._owner = owner self._priority = priority @@ -127,7 +127,7 @@ def on_load(self) -> None: def add(self, owner: AppletPlugin, priority: int | tuple[int, int], text: str | None = None, markup: bool = False, icon_name: str | None = None, tooltip: str | None = None, callback: Callable[[], None] | None = None, - submenu_function: Callable[[], Iterable["SubmenuItemDict"]] | None = None, + submenu_function: Callable[[], Iterable[SubmenuItemDict]] | None = None, visible: bool = True, sensitive: bool = True) -> MenuItem: if isinstance(priority, int): diff --git a/blueman/plugins/applet/PulseAudioProfile.py b/blueman/plugins/applet/PulseAudioProfile.py index 975438320..60abfb922 100644 --- a/blueman/plugins/applet/PulseAudioProfile.py +++ b/blueman/plugins/applet/PulseAudioProfile.py @@ -1,19 +1,16 @@ import logging from gettext import gettext as _ from html import escape -from typing import TYPE_CHECKING, Any +from typing import Any from collections.abc import Mapping, Callable -from blueman.main.PulseAudioUtils import EventType, PulseAudioUtils +from blueman.bluez.Device import Device +from blueman.main.PulseAudioUtils import CardInfo, CardProfileInfo, EventType, PulseAudioUtils from blueman.plugins.AppletPlugin import AppletPlugin +from blueman.plugins.applet.Menu import MenuItem, SubmenuItemDict from blueman.Sdp import (AUDIO_SINK_SVCLASS_ID, AUDIO_SOURCE_SVCLASS_ID, ServiceUUID) -if TYPE_CHECKING: - from blueman.bluez.Device import Device - from blueman.main.PulseAudioUtils import CardInfo, CardProfileInfo - from blueman.plugins.applet.Menu import MenuItem, SubmenuItemDict - class AudioProfiles(AppletPlugin): __depends__ = ["Menu"] @@ -21,8 +18,8 @@ class AudioProfiles(AppletPlugin): __author__ = "Abhijeet Viswa" def on_load(self) -> None: - self._devices: dict[str, "CardInfo"] = {} - self._device_menus: dict[str, "MenuItem"] = {} + self._devices: dict[str, CardInfo] = {} + self._device_menus: dict[str, MenuItem] = {} self._menu = self.parent.Plugins.Menu @@ -36,7 +33,7 @@ def generate_menu(self) -> None: if device['Connected']: self.request_device_profile_menu(device) - def request_device_profile_menu(self, device: "Device") -> None: + def request_device_profile_menu(self, device: Device) -> None: audio_source = False for uuid in device['UUIDs']: if ServiceUUID(uuid).short_uuid in (AUDIO_SOURCE_SVCLASS_ID, AUDIO_SINK_SVCLASS_ID): @@ -53,14 +50,14 @@ def request_device_profile_menu(self, device: "Device") -> None: else: self.add_device_profile_menu(device) - def add_device_profile_menu(self, device: "Device") -> None: - def _activate_profile_wrapper(device: "Device", profile: "CardProfileInfo") -> Callable[[], None]: + def add_device_profile_menu(self, device: Device) -> None: + def _activate_profile_wrapper(device: Device, profile: CardProfileInfo) -> Callable[[], None]: def _wrapper() -> None: self.on_activate_profile(device, profile) return _wrapper - def _generate_profiles_menu(info: "CardInfo") -> list["SubmenuItemDict"]: - items: list["SubmenuItemDict"] = [] + def _generate_profiles_menu(info: CardInfo) -> list[SubmenuItemDict]: + items: list[SubmenuItemDict] = [] if not info: return items for profile in info["profiles"]: @@ -86,8 +83,8 @@ def _generate_profiles_menu(info: "CardInfo") -> list["SubmenuItemDict"]: submenu_function=lambda: _generate_profiles_menu(info)) self._device_menus[device['Address']] = menu - def query_pa(self, device: "Device") -> None: - def list_cb(cards: Mapping[str, "CardInfo"]) -> None: + def query_pa(self, device: Device) -> None: + def list_cb(cards: Mapping[str, CardInfo]) -> None: for c in cards.values(): if c["proplist"]["device.string"] == device['Address']: self._devices[device['Address']] = c @@ -97,7 +94,7 @@ def list_cb(cards: Mapping[str, "CardInfo"]) -> None: pa = PulseAudioUtils() pa.list_cards(list_cb) - def on_activate_profile(self, device: "Device", profile: "CardProfileInfo") -> None: + def on_activate_profile(self, device: Device, profile: CardProfileInfo) -> None: pa = PulseAudioUtils() c = self._devices[device['Address']] @@ -111,7 +108,7 @@ def on_result(res: int) -> None: def on_pa_event(self, utils: PulseAudioUtils, event: int, idx: int) -> None: logging.debug(f"{event} {idx}") - def get_card_cb(card: "CardInfo") -> None: + def get_card_cb(card: CardInfo) -> None: drivers = ("module-bluetooth-device.c", "module-bluez4-device.c", "module-bluez5-device.c") diff --git a/blueman/plugins/applet/RecentConns.py b/blueman/plugins/applet/RecentConns.py index 0a2aa9afb..425ba6e24 100644 --- a/blueman/plugins/applet/RecentConns.py +++ b/blueman/plugins/applet/RecentConns.py @@ -3,7 +3,7 @@ import html import time import logging -from typing import TYPE_CHECKING, cast +from typing import cast, TypedDict from collections.abc import Callable from blueman.bluemantyping import ObjectPath, BtAddress @@ -12,29 +12,24 @@ from blueman.gui.Notification import Notification from blueman.Sdp import ServiceUUID from blueman.plugins.AppletPlugin import AppletPlugin -from blueman.plugins.applet.Menu import MenuItem +from blueman.plugins.applet.Menu import Menu, MenuItem, SubmenuItemDict from blueman.plugins.applet.PowerManager import PowerManager, PowerStateListener -if TYPE_CHECKING: - from blueman.plugins.applet.Menu import Menu, SubmenuItemDict - from typing_extensions import TypedDict +class _ItemBase(TypedDict): + adapter: str + address: str + alias: str + icon: str + name: str + uuid: str - class _ItemBase(TypedDict): - adapter: str - address: str - alias: str - icon: str - name: str - uuid: str - class Item(_ItemBase): - time: float - device: str - mitem: SubmenuItemDict | None +class Item(_ItemBase): + time: float + device: str + mitem: SubmenuItemDict | None - class StoredIcon(_ItemBase): - time: str REGISTRY_VERSION = 0 @@ -60,7 +55,7 @@ class RecentConns(AppletPlugin, PowerStateListener): } def on_load(self) -> None: - self.__menuitems: list["SubmenuItemDict"] = [] + self.__menuitems: list[SubmenuItemDict] = [] self._rebuild_menu() @@ -145,7 +140,7 @@ def notify(self, object_path: ObjectPath, uuid: str) -> None: self._rebuild() - def on_item_activated(self, item: "Item") -> None: + def on_item_activated(self, item: Item) -> None: logging.info(f"Connect {item['address']} {item['uuid']}") assert item["mitem"] is not None @@ -169,9 +164,9 @@ def err(reason: Exception | str) -> None: self.parent.Plugins.DBusService.connect_service(ObjectPath(item["device"]), item["uuid"], reply, err) - def _build_menu_item(self, item: "Item") -> "SubmenuItemDict": + def _build_menu_item(self, item: Item) -> SubmenuItemDict: alias = html.escape(item["alias"]) - mitem: "SubmenuItemDict" = { + mitem: SubmenuItemDict = { "text": _("%(service)s on %(device)s") % {"service": item["name"], "device": alias}, "markup": True, "icon_name": item["mitem"]["icon_name"] if item["mitem"] is not None else item["icon"], @@ -208,7 +203,7 @@ def _get_device_path(self, adapter_path: ObjectPath, address: BtAddress) -> str return device.get_object_path() if device is not None else None - def _get_items(self) -> list["Item"]: + def _get_items(self) -> list[Item]: return sorted( ({ "adapter": i["adapter"], diff --git a/blueman/plugins/applet/TransferService.py b/blueman/plugins/applet/TransferService.py index e394c702a..4f236e5ae 100644 --- a/blueman/plugins/applet/TransferService.py +++ b/blueman/plugins/applet/TransferService.py @@ -4,7 +4,7 @@ import shutil import logging from html import escape -from typing import TYPE_CHECKING, Optional, Union +from typing import Optional, TypedDict, Union from collections.abc import Callable from blueman.bluemantyping import ObjectPath, BtAddress @@ -20,21 +20,21 @@ from gi.repository import GLib, Gio -if TYPE_CHECKING: - from typing_extensions import TypedDict - class TransferDict(TypedDict): - path: str - size: int | None - name: str +class TransferDict(TypedDict): + path: str + size: int | None + name: str + + +class PendingTransferDict(TypedDict): + transfer_path: str + address: BtAddress + root: str + filename: str + size: int | None + name: str - class PendingTransferDict(TypedDict): - transfer_path: str - address: BtAddress - root: str - filename: str - size: int | None - name: str NotificationType = Union[_NotificationBubble, _NotificationDialog] @@ -63,8 +63,8 @@ def __init__(self, applet: BluemanApplet): self._allowed_devices: list[str] = [] self._notification: NotificationType | None = None - self._pending_transfer: Optional["PendingTransferDict"] = None - self.transfers: dict[str, "TransferDict"] = {} + self._pending_transfer: Optional[PendingTransferDict] = None + self.transfers: dict[str, TransferDict] = {} def register_at_manager(self) -> None: AgentManager().register_agent(self.__agent_path) diff --git a/blueman/plugins/manager/PulseAudioProfile.py b/blueman/plugins/manager/PulseAudioProfile.py index c0621b777..35648eddf 100644 --- a/blueman/plugins/manager/PulseAudioProfile.py +++ b/blueman/plugins/manager/PulseAudioProfile.py @@ -1,6 +1,5 @@ from gettext import gettext as _ import logging -from typing import TYPE_CHECKING from collections.abc import Mapping, Sequence from blueman.bluez.Device import Device @@ -14,14 +13,12 @@ gi.require_version("Gtk", "3.0") from gi.repository import Gtk - -if TYPE_CHECKING: - from blueman.main.PulseAudioUtils import CardInfo # noqa: F401 +from blueman.main.PulseAudioUtils import CardInfo class PulseAudioProfile(ManagerPlugin, MenuItemsProvider): def on_load(self) -> None: - self.devices: dict[str, "CardInfo"] = {} + self.devices: dict[str, CardInfo] = {} self.deferred: list[Device] = [] @@ -45,7 +42,7 @@ def regenerate_with_device(self, device_addr: str) -> None: def on_pa_event(self, utils: PulseAudioUtils, event: int, idx: int) -> None: logging.debug(f"{event} {idx}") - def get_card_cb(card: "CardInfo") -> None: + def get_card_cb(card: CardInfo) -> None: drivers = ("module-bluetooth-device.c", "module-bluez4-device.c", "module-bluez5-device.c") @@ -66,7 +63,7 @@ def get_card_cb(card: "CardInfo") -> None: utils.get_card(idx, get_card_cb) def query_pa(self, device: Device, item: Gtk.MenuItem) -> None: - def list_cb(cards: Mapping[str, "CardInfo"]) -> None: + def list_cb(cards: Mapping[str, CardInfo]) -> None: for c in cards.values(): if c["proplist"]["device.string"] == device['Address']: self.devices[device['Address']] = c diff --git a/blueman/plugins/services/Network.py b/blueman/plugins/services/Network.py index ec7135118..c269aac63 100644 --- a/blueman/plugins/services/Network.py +++ b/blueman/plugins/services/Network.py @@ -2,7 +2,7 @@ from random import randint import logging import ipaddress -from typing import cast, Union, TYPE_CHECKING +from typing import cast, Literal, Union from blueman.Functions import have, get_local_interfaces from blueman.main.Builder import Builder @@ -15,9 +15,6 @@ gi.require_version("Gtk", "3.0") from gi.repository import Gio, Gtk, GObject -if TYPE_CHECKING: - from typing import Literal - class Network(ServicePlugin): __plugin_info__ = (_("Network"), "network-workgroup") @@ -108,7 +105,7 @@ def ip_check(self) -> None: entry.props.secondary_icon_name = None - def on_query_apply_state(self) -> Union[bool, "Literal[-1]"]: + def on_query_apply_state(self) -> Union[bool, Literal[-1]]: opts = self.get_options() if not opts: return False diff --git a/stubs/_blueman.pyi b/stubs/_blueman.pyi index 59d93181d..8bfadf3dd 100644 --- a/stubs/_blueman.pyi +++ b/stubs/_blueman.pyi @@ -1,5 +1,4 @@ -from typing import List, Dict, Optional -from typing_extensions import TypedDict +from typing import List, Dict, Optional, TypedDict ERR: Dict[int, str] RFCOMM_HANGUP_NOW: int