From 7b487758f5e23616e434144e7851dba7530494db Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Tue, 5 Apr 2022 13:42:00 +0200 Subject: [PATCH] fixes for python 3.9 type hints giving runtime issues so revert to old style --- .pre-commit-config.yaml | 6 ++++- examples/full.py | 12 +++++----- music_assistant/controllers/players.py | 4 ++-- music_assistant/helpers/database.py | 22 ++++++++++------- music_assistant/helpers/typing.py | 12 +++------- music_assistant/mass.py | 15 +++++------- music_assistant/models/media_controller.py | 4 ++-- music_assistant/models/media_items.py | 28 +++++++++++----------- music_assistant/models/player_queue.py | 20 ++++++++-------- music_assistant/models/provider.py | 5 ++-- music_assistant/providers/filesystem.py | 2 +- music_assistant/providers/tunein.py | 2 +- setup.cfg | 2 +- 13 files changed, 66 insertions(+), 68 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f538b161..34a6d198 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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/ - diff --git a/examples/full.py b/examples/full.py index e8d46f66..d01b6f3e 100644 --- a/examples/full.py +++ b/examples/full.py @@ -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)) diff --git a/music_assistant/controllers/players.py b/music_assistant/controllers/players.py index e4129ef3..dcbe672d 100755 --- a/music_assistant/controllers/players.py +++ b/music_assistant/controllers/players.py @@ -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" diff --git a/music_assistant/helpers/database.py b/music_assistant/helpers/database.py index 4da063b8..b34813fe 100755 --- a/music_assistant/helpers/database.py +++ b/music_assistant/helpers/database.py @@ -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: diff --git a/music_assistant/helpers/typing.py b/music_assistant/helpers/typing.py index 1caf05f9..c5337216 100644 --- a/music_assistant/helpers/typing.py +++ b/music_assistant/helpers/typing.py @@ -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" diff --git a/music_assistant/mass.py b/music_assistant/mass.py index 178c7871..d36dcdf5 100644 --- a/music_assistant/mass.py +++ b/music_assistant/mass.py @@ -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__ diff --git a/music_assistant/models/media_controller.py b/music_assistant/models/media_controller.py index eaac3747..08675cf3 100644 --- a/music_assistant/models/media_controller.py +++ b/music_assistant/models/media_controller.py @@ -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) diff --git a/music_assistant/models/media_items.py b/music_assistant/models/media_items.py index a2ec12fa..82166fe6 100755 --- a/music_assistant/models/media_items.py +++ b/music_assistant/models/media_items.py @@ -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 diff --git a/music_assistant/models/player_queue.py b/music_assistant/models/player_queue.py index 5585947f..f1952ce2 100644 --- a/music_assistant/models/player_queue.py +++ b/music_assistant/models/player_queue.py @@ -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) diff --git a/music_assistant/models/provider.py b/music_assistant/models/provider.py index 8346ba94..9ea4a57a 100644 --- a/music_assistant/models/provider.py +++ b/music_assistant/models/provider.py @@ -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. diff --git a/music_assistant/providers/filesystem.py b/music_assistant/providers/filesystem.py index 6b538e59..931f6009 100644 --- a/music_assistant/providers/filesystem.py +++ b/music_assistant/providers/filesystem.py @@ -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. diff --git a/music_assistant/providers/tunein.py b/music_assistant/providers/tunein.py index f392aa4e..f151d834 100644 --- a/music_assistant/providers/tunein.py +++ b/music_assistant/providers/tunein.py @@ -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" diff --git a/setup.cfg b/setup.cfg index 81fb9d19..ec9f535a 100644 --- 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 -- 2.34.1