[gnome-music/wip/mschraal/type-checking] grltrackerwrapper: Add static type checking



commit c512dafdd7a30e38f890e4b4bef8bdf3b184fc64
Author: Marinus Schraal <mschraal gnome org>
Date:   Sat May 30 17:21:46 2020 +0200

    grltrackerwrapper: Add static type checking

 gnomemusic/grilowrappers/grltrackerwrapper.py | 164 +++++++++++++++++---------
 1 file changed, 109 insertions(+), 55 deletions(-)
---
diff --git a/gnomemusic/grilowrappers/grltrackerwrapper.py b/gnomemusic/grilowrappers/grltrackerwrapper.py
index d769a16a..95928b2c 100644
--- a/gnomemusic/grilowrappers/grltrackerwrapper.py
+++ b/gnomemusic/grilowrappers/grltrackerwrapper.py
@@ -22,15 +22,22 @@
 # code, but you are not obligated to do so.  If you do not wish to do so,
 # delete this exception statement from your version.
 
+from typing import Any, Callable, Dict, List, Optional
+import typing
+
 import gi
 gi.require_versions({"Gfm": "0.1", "Grl": "0.3", 'Tracker': "2.0"})
 from gi.repository import Gfm, Grl, GLib, GObject, Tracker
 
+if typing.TYPE_CHECKING:
+    from gnomemusic.application import Application
 from gnomemusic.corealbum import CoreAlbum
 from gnomemusic.coreartist import CoreArtist
 from gnomemusic.coredisc import CoreDisc
 from gnomemusic.coresong import CoreSong
-from gnomemusic.grilowrappers.grltrackerplaylists import GrlTrackerPlaylists
+from gnomemusic.trackerwrapper import TrackerWrapper
+from gnomemusic.grilowrappers.grltrackerplaylists import (
+    GrlTrackerPlaylists, Playlist)
 
 
 class GrlTrackerWrapper(GObject.GObject):
@@ -61,7 +68,9 @@ class GrlTrackerWrapper(GObject.GObject):
         Grl.METADATA_KEY_THUMBNAIL,
     ]
 
-    def __init__(self, source, application, tracker_wrapper):
+    def __init__(
+            self, source: Grl.Source, application: "Application",
+            tracker_wrapper: TrackerWrapper) -> None:
         """Initialize the Tracker wrapper
 
         :param Grl.TrackerSource source: The Tracker source to wrap
@@ -76,16 +85,17 @@ class GrlTrackerWrapper(GObject.GObject):
         self._songs_model = self._coremodel.props.songs
         self._source = None
         self._albums_model = self._coremodel.props.albums
-        self._album_ids = {}
+        self._album_ids: Dict[int, CoreAlbum] = {}
         self._artists_model = self._coremodel.props.artists
-        self._artist_ids = {}
-        self._hash = {}
+        self._artist_ids: Dict[int, CoreArtist] = {}
+        self._hash: Dict[int, CoreSong] = {}
         self._song_search_proxy = self._coremodel.props.songs_search_proxy
         self._album_search_model = self._coremodel.props.albums_search
         self._artist_search_model = self._coremodel.props.artists_search
-        self._batch_changed_media_ids = {}
+        self._batch_changed_media_ids: Dict[
+            Grl.SourceChangeType, List[int]] = {}
         self._content_changed_timeout = 0
-        self._tracker_playlists = None
+        self._tracker_playlists: Optional[GrlTrackerPlaylists] = None
         self._tracker_wrapper = tracker_wrapper
         self._window = application.props.window
 
@@ -105,11 +115,11 @@ class GrlTrackerWrapper(GObject.GObject):
         self._initial_artists_fill(self.props.source)
 
     @GObject.Property(type=Grl.Source, default=None)
-    def source(self):
+    def source(self) -> Grl.Source:
         return self._source
 
     @source.setter  # type: ignore
-    def source(self, new_source):
+    def source(self, new_source: Grl.Source):
         """Set a new grilo tracker source
 
         Everytime, the tracker plugin is loaded, a new source is
@@ -126,21 +136,25 @@ class GrlTrackerWrapper(GObject.GObject):
         self._content_changed_id = self._source.connect(
             "content-changed", self._batch_content_changed)
 
-    def _batch_content_changed(self, source, medias, change_type, loc_unknown):
+    def _batch_content_changed(
+            self, source: Grl.Source, medias: List[Grl.Media],
+            change_type: Grl.SourceChangeType, loc_unknown: bool) -> None:
         if medias == []:
             return
 
         if change_type not in self._batch_changed_media_ids.keys():
             self._batch_changed_media_ids[change_type] = []
 
-        [self._batch_changed_media_ids[change_type].append(media.get_id())
-         for media in medias if media.is_audio() or media.is_container()]
+        [self._batch_changed_media_ids[  # type: ignore
+            change_type].append(media.get_id())
+            for media in medias
+            if media.is_audio() or media.is_container()]
 
         if self._content_changed_timeout == 0:
             self._content_changed_timeout = GLib.timeout_add(
                 250, self._on_content_changed)
 
-    def _on_content_changed(self):
+    def _on_content_changed(self) -> bool:
         for change_type in self._batch_changed_media_ids.keys():
             media_ids = self._batch_changed_media_ids[change_type]
 
@@ -167,8 +181,8 @@ class GrlTrackerWrapper(GObject.GObject):
 
         return GLib.SOURCE_REMOVE
 
-    def _check_album_change(self):
-        album_ids = {}
+    def _check_album_change(self) -> None:
+        album_ids: Dict[int, CoreAlbum] = {}
 
         query = """
         SELECT
@@ -193,7 +207,9 @@ class GrlTrackerWrapper(GObject.GObject):
             'location_filter': self._tracker_wrapper.location_filter()
         }
 
-        def check_album_cb(source, op_id, media, remaining, error):
+        def check_album_cb(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: str) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 return
@@ -229,8 +245,8 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, self.METADATA_KEYS, options, check_album_cb)
 
-    def _check_artist_change(self):
-        artist_ids = {}
+    def _check_artist_change(self) -> None:
+        artist_ids: Dict[int, CoreArtist] = {}
 
         query = """
         SELECT
@@ -254,7 +270,9 @@ class GrlTrackerWrapper(GObject.GObject):
             'location_filter': self._tracker_wrapper.location_filter()
         }
 
-        def check_artist_cb(source, op_id, media, remaining, error):
+        def check_artist_cb(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 return
@@ -289,7 +307,7 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, self.METADATA_KEYS, options, check_artist_cb)
 
-    def _remove_media(self, media_ids):
+    def _remove_media(self, media_ids: List[int]) -> None:
         for media_id in media_ids:
             try:
                 coresong = self._hash.pop(media_id)
@@ -305,8 +323,8 @@ class GrlTrackerWrapper(GObject.GObject):
                     self._songs_model.remove(idx)
                     break
 
-    def _song_media_query(self, media_ids):
-        media_ids = str(media_ids)[1:-1]
+    def _song_media_query(self, ids: List[int]) -> str:
+        media_ids = str(ids)[1:-1]
 
         query = """
         SELECT DISTINCT
@@ -340,9 +358,11 @@ class GrlTrackerWrapper(GObject.GObject):
 
         return query
 
-    def _changed_media(self, media_ids):
+    def _changed_media(self, media_ids: List[int]) -> None:
 
-        def _update_changed_media(source, op_id, media, remaining, error):
+        def _update_changed_media(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 return
@@ -369,11 +389,13 @@ class GrlTrackerWrapper(GObject.GObject):
             self._song_media_query(media_ids), self.METADATA_KEYS,
             options, _update_changed_media)
 
-    def _initial_songs_fill(self, source):
+    def _initial_songs_fill(self, source: Grl.Source) -> None:
         self._window.notifications_popup.push_loading()
-        songs_added = []
+        songs_added: List[int] = []
 
-        def _add_to_model(source, op_id, media, remaining, error):
+        def _add_to_model(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -434,11 +456,13 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, self.METADATA_KEYS, options, _add_to_model)
 
-    def _initial_albums_fill(self, source):
+    def _initial_albums_fill(self, source: Grl.Source) -> None:
         self._window.notifications_popup.push_loading()
-        albums_added = []
+        albums_added: List[int] = []
 
-        def _add_to_albums_model(source, op_id, media, remaining, error):
+        def _add_to_albums_model(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -488,11 +512,13 @@ class GrlTrackerWrapper(GObject.GObject):
 
         source.query(query, self.METADATA_KEYS, options, _add_to_albums_model)
 
-    def _initial_artists_fill(self, source):
+    def _initial_artists_fill(self, source: Grl.Source) -> None:
         self._window.notifications_popup.push_loading()
-        artists_added = []
+        artists_added: List[int] = []
 
-        def _add_to_artists_model(source, op_id, media, remaining, error):
+        def _add_to_artists_model(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -540,7 +566,8 @@ class GrlTrackerWrapper(GObject.GObject):
         source.query(
             query, [Grl.METADATA_KEY_ARTIST], options, _add_to_artists_model)
 
-    def get_artist_albums(self, media, model):
+    def get_artist_albums(
+            self, media: Grl.Source, model: Gfm.FilterListModel) -> None:
         """Get all albums by an artist
 
         :param Grl.Media media: The media with the artist id
@@ -572,9 +599,11 @@ class GrlTrackerWrapper(GObject.GObject):
             'location_filter': self._tracker_wrapper.location_filter()
         }
 
-        albums = []
+        albums: List[Grl.Media] = []
 
-        def query_cb(source, op_id, media, remaining, error):
+        def query_cb(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -587,7 +616,8 @@ class GrlTrackerWrapper(GObject.GObject):
 
             albums.append(media)
 
-        def albums_filter(corealbum, albums):
+        def albums_filter(
+                corealbum: CoreAlbum, albums: List[Grl.Media]) -> bool:
             for media in albums:
                 if media.get_id() == corealbum.props.media.get_id():
                     return True
@@ -598,7 +628,8 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, [Grl.METADATA_KEY_TITLE], options, query_cb)
 
-    def get_album_discs(self, media, disc_model):
+    def get_album_discs(
+            self, media: Grl.Media, disc_model: Gfm.SortListModel) -> None:
         """Get all discs of an album
 
         :param Grl.Media media: The media with the album id
@@ -624,7 +655,9 @@ class GrlTrackerWrapper(GObject.GObject):
             'location_filter': self._tracker_wrapper.location_filter()
         }
 
-        def _disc_nr_cb(source, op_id, media, remaining, error):
+        def _disc_nr_cb(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -642,7 +675,8 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, [Grl.METADATA_KEY_ALBUM_DISC_NUMBER], options, _disc_nr_cb)
 
-    def populate_album_disc_songs(self, media, disc_nr, callback):
+    def populate_album_disc_songs(
+            self, media: Grl.Media, disc_nr: int, callback: Callable) -> None:
         # FIXME: Pass a model and fill it.
         # FIXME: The query is similar to the other song queries, reuse
         # if possible.
@@ -688,7 +722,7 @@ class GrlTrackerWrapper(GObject.GObject):
         options = self._fast_options.copy()
         self.props.source.query(query, self.METADATA_KEYS, options, callback)
 
-    def search(self, text):
+    def search(self, text: str) -> None:
         # FIXME: Searches are limited to not bog down the UI with
         # widget creation ({List,Flow}Box limitations). The limit is
         # arbitrarily set to 50 and set in the Tracker query. It should
@@ -741,10 +775,12 @@ class GrlTrackerWrapper(GObject.GObject):
 
         artist_filter_ids = []
 
-        def artist_filter(coreartist):
+        def artist_filter(coreartist: CoreArtist) -> bool:
             return coreartist.media.get_id() in artist_filter_ids
 
-        def artist_search_cb(source, op_id, media, remaining, error):
+        def artist_search_cb(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -796,10 +832,12 @@ class GrlTrackerWrapper(GObject.GObject):
 
         album_filter_ids = []
 
-        def album_filter(corealbum):
+        def album_filter(corealbum: CoreAlbum) -> bool:
             return corealbum.media.get_id() in album_filter_ids
 
-        def albums_search_cb(source, op_id, media, remaining, error):
+        def albums_search_cb(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -857,10 +895,12 @@ class GrlTrackerWrapper(GObject.GObject):
 
         filter_ids = []
 
-        def songs_filter(coresong):
+        def songs_filter(coresong: CoreSong) -> bool:
             return coresong.media.get_id() in filter_ids
 
-        def songs_search_cb(source, op_id, media, remaining, error):
+        def songs_search_cb(
+                source: Grl.Source, op_id: int, media: Grl.Media,
+                remaining: int, error: Optional[GLib.Error]) -> None:
             if error:
                 self._log.warning("Error: {}".format(error))
                 self._window.notifications_popup.pop_loading()
@@ -878,7 +918,8 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, self.METADATA_KEYS, options, songs_search_cb)
 
-    def get_album_art_for_item(self, coresong, callback):
+    def get_album_art_for_item(
+            self, coresong: CoreSong, callback: Callable) -> None:
         """Placeholder until we got a better solution
         """
         item_id = coresong.props.media.get_id()
@@ -897,7 +938,7 @@ class GrlTrackerWrapper(GObject.GObject):
         self.props.source.query(
             query, self.METADATA_THUMBNAIL_KEYS, full_options, callback)
 
-    def _get_album_for_album_id(self, album_id):
+    def _get_album_for_album_id(self, album_id: str) -> str:
         # Even though we check for the album_artist, we fill
         # the artist key, since Grilo coverart plugins use
         # only that key for retrieval.
@@ -939,7 +980,7 @@ class GrlTrackerWrapper(GObject.GObject):
 
         return query
 
-    def _get_album_for_song_id(self, song_id):
+    def _get_album_for_song_id(self, song_id: str) -> str:
         # See get_album_for_album_id comment.
         query = """
         SELECT DISTINCT
@@ -982,10 +1023,12 @@ class GrlTrackerWrapper(GObject.GObject):
 
         return query
 
-    def get_artist_art(self, coreartist):
+    def get_artist_art(self, coreartist: CoreArtist) -> None:
         media = coreartist.props.media
 
-        def _resolve_cb(source, op_id, resolved_media, data, error):
+        def _resolve_cb(
+                source: Grl.Source, op_id: int, resolved_media: Grl.Media,
+                data: Optional[object], error: Optional[GLib.Error]) -> None:
             if resolved_media.get_thumbnail() is None:
                 coreartist.props.thumbnail = ""
                 return
@@ -1001,25 +1044,36 @@ class GrlTrackerWrapper(GObject.GObject):
             media, [Grl.METADATA_KEY_THUMBNAIL], full_options, _resolve_cb,
             None)
 
-    def stage_playlist_deletion(self, playlist):
+    def stage_playlist_deletion(self, playlist: Optional[Playlist]) -> None:
         """Prepares playlist deletion.
 
         :param Playlist playlist: playlist
         """
+        if self._tracker_playlists is None:
+            return
+
         self._tracker_playlists.stage_playlist_deletion(playlist)
 
-    def finish_playlist_deletion(self, playlist, deleted):
+    def finish_playlist_deletion(
+            self, playlist: Optional[Playlist], deleted: bool) -> None:
         """Finishes playlist deletion.
 
         :param Playlist playlist: playlist
         :param bool deleted: indicates if the playlist has been deleted
         """
+        if self._tracker_playlists is None:
+            return
+
         self._tracker_playlists.finish_playlist_deletion(playlist, deleted)
 
-    def create_playlist(self, playlist_title, callback):
+    def create_playlist(
+            self, playlist_title: str, callback: Callable) -> None:
         """Creates a new user playlist.
 
         :param str playlist_title: playlist title
         :param callback: function to perform once, the playlist is created
         """
+        if self._tracker_playlists is None:
+            return
+
         self._tracker_playlists.create_playlist(playlist_title, callback)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]