fixes for python 3.9
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 5 Apr 2022 11:42:00 +0000 (13:42 +0200)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 5 Apr 2022 11:42:00 +0000 (13:42 +0200)
type hints giving runtime issues so revert to old style

13 files changed:
.pre-commit-config.yaml
examples/full.py
music_assistant/controllers/players.py
music_assistant/helpers/database.py
music_assistant/helpers/typing.py
music_assistant/mass.py
music_assistant/models/media_controller.py
music_assistant/models/media_items.py
music_assistant/models/player_queue.py
music_assistant/models/provider.py
music_assistant/providers/filesystem.py
music_assistant/providers/tunein.py
setup.cfg

index f538b1610729f01ce0f95b1bd1819d225ee8af5c..34a6d198d87c853afdcba276ae72fac8409e5385 100644 (file)
@@ -25,6 +25,11 @@ repos:
       - id: mypy
         additional_dependencies: [types-all]
         exclude: ^examples/
+  - repo: https://github.com/pycqa/pydocstyle
+    rev: 6.1.1
+    hooks:
+      - id: pydocstyle
+        exclude: ^examples/|^.venv/|^.vscode/
   - repo: local
     hooks:
       - id: pylint
@@ -34,4 +39,3 @@ repos:
         types: [python]
         args: ["-rn", "-sn", "--rcfile=pylintrc", "--fail-on=I"]
         exclude: ^examples/|^.venv/|^.vscode/
-
index e8d46f66cd0cd9862a6acf10e36b4b3483e0f0fa..d01b6f3e17f727a97084b61cd3a847dc61957723 100644 (file)
@@ -85,12 +85,12 @@ mass = MusicAssistant(f"sqlite:///{db_file}")
 
 
 providers = []
-if args.spotify_username and args.spotify_password:
-    providers.append(SpotifyProvider(args.spotify_username, args.spotify_password))
-if args.qobuz_username and args.qobuz_password:
-    providers.append(QobuzProvider(args.qobuz_username, args.qobuz_password))
-if args.tunein_username:
-    providers.append(TuneInProvider(args.tunein_username))
+if args.spotify_username and args.spotify_password:
+    providers.append(SpotifyProvider(args.spotify_username, args.spotify_password))
+if args.qobuz_username and args.qobuz_password:
+    providers.append(QobuzProvider(args.qobuz_username, args.qobuz_password))
+if args.tunein_username:
+    providers.append(TuneInProvider(args.tunein_username))
 if args.musicdir:
     providers.append(FileSystemProvider(args.musicdir, args.playlistdir))
 
index e4129ef32787cc1be43b7c669359bb79cfdfb69c..dcbe672d7cd93cf74762bd4bf7cf9260f9408183 100755 (executable)
@@ -1,7 +1,7 @@
 """Logic to play music from MusicProviders to supported players."""
 from __future__ import annotations
 
-from typing import Dict, Tuple
+from typing import Dict, Tuple, Union
 
 from music_assistant.constants import EventType
 from music_assistant.controllers.stream import StreamController
@@ -10,7 +10,7 @@ from music_assistant.models.errors import AlreadyRegisteredError
 from music_assistant.models.player import Player, PlayerGroup
 from music_assistant.models.player_queue import PlayerQueue
 
-PlayerType = Player | PlayerGroup
+PlayerType = Union[Player, PlayerGroup]
 
 DB_TABLE = "queue_settings"
 
index 4da063b8bc0d8166083ad4cee595f37234640676..b34813fe7ec7666cd03ba2e037e863450292d969 100755 (executable)
@@ -2,7 +2,7 @@
 from __future__ import annotations
 
 from contextlib import asynccontextmanager
-from typing import Any, Dict, List, Mapping
+from typing import Any, Dict, List, Mapping, Optional
 
 from databases import Database as Db
 from databases import DatabaseURL
@@ -21,7 +21,7 @@ class Database:
         self.logger = mass.logger.getChild("db")
 
     @asynccontextmanager
-    async def get_db(self, db: Db | None = None) -> Db:
+    async def get_db(self, db: Optional[Db] = None) -> Db:
         """Context manager helper to get the active db connection."""
         if db is not None:
             yield db
@@ -30,7 +30,11 @@ class Database:
                 yield _db
 
     async def get_rows(
-        self, table: str, match: dict = None, order_by: str = None, db: Db | None = None
+        self,
+        table: str,
+        match: dict = None,
+        order_by: str = None,
+        db: Optional[Db] = None,
     ) -> List[Mapping]:
         """Get all rows for given table."""
         async with self.get_db(db) as _db:
@@ -42,14 +46,14 @@ class Database:
             return await _db.fetch_all(sql_query, match)
 
     async def get_rows_from_query(
-        self, query: str, db: Db | None = None
+        self, query: str, db: Optional[Db] = None
     ) -> List[Mapping]:
         """Get all rows for given custom query."""
         async with self.get_db(db) as _db:
             return await _db.fetch_all(query)
 
     async def search(
-        self, table: str, search: str, column: str = "name", db: Db | None = None
+        self, table: str, search: str, column: str = "name", db: Optional[Db] = None
     ) -> List[Mapping]:
         """Search table by column."""
         async with self.get_db(db) as _db:
@@ -57,7 +61,7 @@ class Database:
             return await _db.fetch_all(sql_query)
 
     async def get_row(
-        self, table: str, match: Dict[str, Any] = None, db: Db | None = None
+        self, table: str, match: Dict[str, Any] = None, db: Optional[Db] = None
     ) -> Mapping | None:
         """Get single row for given table where column matches keys/values."""
         async with self.get_db(db) as _db:
@@ -66,7 +70,7 @@ class Database:
             return await _db.fetch_one(sql_query, match)
 
     async def insert_or_replace(
-        self, table: str, values: Dict[str, Any], db: Db | None = None
+        self, table: str, values: Dict[str, Any], db: Optional[Db] = None
     ) -> Mapping:
         """Insert or replace data in given table."""
         async with self.get_db(db) as _db:
@@ -87,7 +91,7 @@ class Database:
         table: str,
         match: Dict[str, Any],
         values: Dict[str, Any],
-        db: Db | None = None,
+        db: Optional[Db] = None,
     ) -> Mapping:
         """Update record."""
         async with self.get_db(db) as _db:
@@ -101,7 +105,7 @@ class Database:
             return await self.get_row(table, match, db=_db)
 
     async def delete(
-        self, table: str, match: Dict[str, Any], db: Db | None = None
+        self, table: str, match: Dict[str, Any], db: Optional[Db] = None
     ) -> None:
         """Delete data in given table."""
         async with self.get_db(db) as _db:
index 1caf05f9f0321ab7baff4dfeb3c1b40c7e3e0de8..c5337216ade2c243a4de1f9dce94c92a65260984 100644 (file)
@@ -1,22 +1,17 @@
 """Typing helper."""
 from __future__ import annotations
 
-from typing import TYPE_CHECKING, Any, List, Optional
+from typing import TYPE_CHECKING, List, Optional
 
 # pylint: disable=invalid-name
 if TYPE_CHECKING:
     from music_assistant.mass import (
-        MusicAssistant,
-        EventDetails,
         EventCallBackType,
         EventSubscriptionType,
+        MusicAssistant,
     )
     from music_assistant.models.media_items import MediaType
-    from music_assistant.models.player import (
-        PlayerQueue,
-        QueueItem,
-    )
-    from music_assistant.models.player import Player
+    from music_assistant.models.player import Player, PlayerQueue, QueueItem
 
 else:
     MusicAssistant = "MusicAssistant"
@@ -25,7 +20,6 @@ else:
     StreamDetails = "StreamDetails"
     Player = "Player"
     MediaType = "MediaType"
-    EventDetails = Any | None
     EventCallBackType = "EventCallBackType"
     EventSubscriptionType = "EventSubscriptionType"
 
index 178c7871af323299f95a2c81c44f7fdff253c5ca..d36dcdf5d0a0689d5643d8c416a7920b50343d82 100644 (file)
@@ -4,7 +4,7 @@ from __future__ import annotations
 import asyncio
 import logging
 from time import time
-from typing import Any, Callable, Coroutine, Optional, Tuple
+from typing import Any, Callable, Coroutine, Optional, Tuple, Union
 
 import aiohttp
 from databases import DatabaseURL
@@ -17,8 +17,7 @@ from music_assistant.helpers.cache import Cache
 from music_assistant.helpers.database import Database
 from music_assistant.helpers.util import create_task
 
-EventDetails = Any | None
-EventCallBackType = Callable[[EventType, EventDetails], None]
+EventCallBackType = Callable[[EventType, Any], None]
 EventSubscriptionType = Tuple[EventCallBackType, Optional[Tuple[EventType]]]
 
 
@@ -29,7 +28,7 @@ class MusicAssistant:
         self,
         db_url: DatabaseURL,
         stream_port: int = 8095,
-        session: aiohttp.ClientSession | None = None,
+        session: Optional[aiohttp.ClientSession] = None,
     ) -> None:
         """
         Create an instance of MusicAssistant.
@@ -83,9 +82,7 @@ class MusicAssistant:
             await self.http_session.connector.close()
         self.http_session.detach()
 
-    def signal_event(
-        self, event_type: EventType, event_details: EventDetails = None
-    ) -> None:
+    def signal_event(self, event_type: EventType, event_details: Any = None) -> None:
         """
         Signal (systemwide) event.
 
@@ -99,7 +96,7 @@ class MusicAssistant:
     def subscribe(
         self,
         cb_func: EventCallBackType,
-        event_filter: EventType | Tuple[EventType] | None = None,
+        event_filter: Union[EventType, Tuple[EventType], None] = None,
     ) -> Callable:
         """
         Add callback to event listeners.
@@ -120,7 +117,7 @@ class MusicAssistant:
 
         return remove_listener
 
-    def add_job(self, job: Coroutine, name: str | None = None) -> None:
+    def add_job(self, job: Coroutine, name: Optional[str] = None) -> None:
         """Add job to be (slowly) processed in the background (one by one)."""
         if not name:
             name = job.__qualname__ or job.__name__
index eaac3747e38780e4c20cb488d2bc6f2b6ce8b63d..08675cf3222a8f5ff1d2a8cea8874ec150fef4dc 100644 (file)
@@ -2,7 +2,7 @@
 from __future__ import annotations
 
 from abc import ABCMeta, abstractmethod
-from typing import Generic, List, Tuple, TypeVar
+from typing import Generic, List, Optional, Tuple, TypeVar
 
 from music_assistant.helpers.cache import cached
 from music_assistant.helpers.typing import MusicAssistant
@@ -129,7 +129,7 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta):
                 return (prov.provider, prov.item_id)
         return None, None
 
-    async def get_db_items(self, custom_query: str | None = None) -> List[ItemCls]:
+    async def get_db_items(self, custom_query: Optional[str] = None) -> List[ItemCls]:
         """Fetch all records from database."""
         if custom_query is not None:
             func = self.mass.database.get_rows_from_query(custom_query)
index a2ec12fac359ad63e3e340b161e4fb0e57b4d2ac..82166fe6338ca9328be9eba1614253aa7da24bcc 100755 (executable)
@@ -3,7 +3,7 @@ from __future__ import annotations
 
 from dataclasses import dataclass, field
 from enum import Enum, IntEnum
-from typing import Any, Dict, List, Mapping
+from typing import Any, Dict, List, Mapping, Optional, Union
 
 from mashumaro import DataClassDictMixin
 from music_assistant.helpers.json import json
@@ -57,7 +57,7 @@ class MediaItem(DataClassDictMixin):
     item_id: str
     provider: str
     name: str
-    sort_name: str | None = None
+    sort_name: Optional[str] = None
     metadata: Dict[str, Any] = field(default_factory=dict)
     provider_ids: List[MediaItemProviderId] = field(default_factory=list)
     in_library: bool = False
@@ -162,10 +162,10 @@ class Album(MediaItem):
 
     media_type: MediaType = MediaType.ALBUM
     version: str = ""
-    year: int | None = None
-    artist: ItemMapping | Artist | None = None
+    year: Optional[int] = None
+    artist: Union[ItemMapping, Artist, None] = None
     album_type: AlbumType = AlbumType.UNKNOWN
-    upc: str | None = None
+    upc: Optional[str] = None
 
     def __hash__(self):
         """Return custom hash."""
@@ -180,13 +180,13 @@ class Track(MediaItem):
     duration: int = 0
     version: str = ""
     isrc: str = ""
-    artists: List[ItemMapping | Artist] = field(default_factory=list)
+    artists: List[Union[ItemMapping, Artist]] = field(default_factory=list)
     # album track only
-    album: ItemMapping | Album | None = None
-    disc_number: int | None = None
-    track_number: int | None = None
+    album: Union[ItemMapping, Album, None] = None
+    disc_number: Optional[int] = None
+    track_number: Optional[int] = None
     # playlist track only
-    position: int | None = None
+    position: Optional[int] = None
 
     def __hash__(self):
         """Return custom hash."""
@@ -222,7 +222,7 @@ def create_uri(media_type: MediaType, provider_id: str, item_id: str):
     return f"{provider_id}://{media_type.value}/{item_id}"
 
 
-MediaItemType = Artist | Album | Track | Radio | Playlist
+MediaItemType = Union[Artist, Album, Track, Radio, Playlist]
 
 
 class StreamType(Enum):
@@ -291,9 +291,9 @@ class StreamDetails(DataClassDictMixin):
     details: Dict[str, Any] = field(default_factory=dict)
     seconds_played: int = 0
     gain_correct: float = 0
-    loudness: float | None = None
-    sample_rate: int | None = None
-    bit_depth: int | None = None
+    loudness: Optional[float] = None
+    sample_rate: Optional[int] = None
+    bit_depth: Optional[int] = None
     channels: int = 2
     media_type: MediaType = MediaType.TRACK
     queue_id: str = None
index 5585947f563f584390d639f8af5b7b7e30294fae..f1952ce2a3b42669c2a6ae41aebfd6026c81178a 100644 (file)
@@ -7,7 +7,7 @@ import time
 from asyncio import Task, TimerHandle
 from dataclasses import dataclass
 from enum import Enum
-from typing import TYPE_CHECKING, List, Tuple
+from typing import TYPE_CHECKING, List, Optional, Tuple, Union
 from uuid import uuid4
 
 from mashumaro import DataClassDictMixin
@@ -38,10 +38,10 @@ class QueueItem(DataClassDictMixin):
 
     uri: str
     name: str = ""
-    duration: int | None = None
+    duration: Optional[int] = None
     item_id: str = ""
     sort_index: int = 0
-    streamdetails: StreamDetails | None = None
+    streamdetails: Optional[StreamDetails] = None
     is_media_item: bool = False
 
     def __post_init__(self):
@@ -78,9 +78,9 @@ class PlayerQueue:
         self._volume_normalization_enabled: bool = True
         self._volume_normalization_target: int = -23
 
-        self._current_index: int | None = None
+        self._current_index: Optional[int] = None
         self._current_item_time: int = 0
-        self._last_item: QueueItem | None = None
+        self._last_item: Optional[QueueItem] = None
         self._start_index: int = 0
         self._next_index: int = 0
         self._last_state = PlayerState.IDLE
@@ -89,7 +89,7 @@ class PlayerQueue:
         self._update_task: Task = None
         self._signal_next: bool = False
         self._last_player_update: int = 0
-        self._stream_url: str | None = None
+        self._stream_url: Optional[str] = None
 
     async def setup(self) -> None:
         """Handle async setup of instance."""
@@ -149,7 +149,7 @@ class PlayerQueue:
         return self._items
 
     @property
-    def current_index(self) -> int | None:
+    def current_index(self) -> Optional[int]:
         """
         Return the current index of the queue.
 
@@ -173,7 +173,7 @@ class PlayerQueue:
         return self._items[self._current_index]
 
     @property
-    def next_index(self) -> int | None:
+    def next_index(self) -> Optional[int]:
         """
         Return the next index for this PlayerQueue.
 
@@ -226,7 +226,7 @@ class PlayerQueue:
             return None
         return next((x for x in self.items if x.item_id == queue_item_id), None)
 
-    def index_by_id(self, queue_item_id: str) -> int | None:
+    def index_by_id(self, queue_item_id: str) -> Optional[int]:
         """Get index by queue_item_id."""
         for index, item in enumerate(self.items):
             if item.item_id == queue_item_id:
@@ -400,7 +400,7 @@ class PlayerQueue:
                 "resume queue requested for %s but queue is empty", self.queue_id
             )
 
-    async def play_index(self, index: int | str) -> None:
+    async def play_index(self, index: Union[int, str]) -> None:
         """Play item at index (or item_id) X in queue."""
         if not isinstance(index, int):
             index = self.index_by_id(index)
index 8346ba946b9aeb0d700db146c472658d5aab2e89..9ea4a57adc085e730131801408ce48c964613c8f 100644 (file)
@@ -1,10 +1,9 @@
 """Model for a Music Providers."""
-
 from __future__ import annotations
 
 from abc import abstractmethod
 from logging import Logger
-from typing import List
+from typing import List, Optional
 
 from music_assistant.helpers.typing import MusicAssistant
 from music_assistant.models.media_items import (
@@ -58,7 +57,7 @@ class MusicProvider:
         return self._attr_supported_mediatypes
 
     async def search(
-        self, search_query: str, media_types=List[MediaType] | None, limit: int = 5
+        self, search_query: str, media_types=Optional[List[MediaType]], limit: int = 5
     ) -> List[MediaItemType]:
         """
         Perform search on musicprovider.
index 6b538e591eb502640edcfe4625b7cd856fba6724..931f600995711a3eab1fb4377eb6e9ca1449096c 100644 (file)
@@ -47,7 +47,7 @@ class FileSystemProvider(MusicProvider):
     Should be compatible with LMS
     """
 
-    def __init__(self, music_dir: str, playlist_dir: str | None = None) -> None:
+    def __init__(self, music_dir: str, playlist_dir: Optional[str] = None) -> None:
         """
         Initialize the Filesystem provider.
 
index f392aa4ed9be470e12dee5df49179cf3eee85f5a..f151d8349b170661016c9e69114915d4637872e9 100644 (file)
@@ -20,7 +20,7 @@ from music_assistant.models.provider import MusicProvider
 class TuneInProvider(MusicProvider):
     """Provider implementation for Tune In."""
 
-    def __init__(self, username: str | None) -> None:
+    def __init__(self, username: Optional[str]) -> None:
         """Initialize the provider."""
         self._attr_id = "tunein"
         self._attr_name = "Tune-in Radio"
index 81fb9d19c7a38552c850f119db20d49354080dd0..ec9f535a4d171fc5e1ca8ff06d3656101aed833d 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -24,7 +24,7 @@ use_parentheses = True
 line_length = 88
 
 [mypy]
-python_version = 3.7
+python_version = 3.9
 ignore_errors = true
 follow_imports = silent
 ignore_missing_imports = true