From: Jc2k Date: Sat, 29 Jun 2024 21:27:48 +0000 (+0100) Subject: Run mypy on CI (#1413) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=d487b37ab00d9690319938db29831e2357010cf3;p=music-assistant-server.git Run mypy on CI (#1413) --- diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2002eb64..27d3ba93 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -91,13 +91,13 @@ repos: # types: [text] # entry: scripts/run-in-env.sh trailing-whitespace-fixer # stages: [commit, push, manual] - # - id: mypy - # name: mypy - # entry: scripts/run-in-env.sh mypy - # language: script - # types: [python] - # require_serial: true - # files: ^(music_assistant|pylint)/.+\.py$ + - id: mypy + name: mypy + entry: scripts/run-in-env.sh mypy + language: script + types: [python] + require_serial: true + pass_filenames: false - id: gen_requirements_all name: gen_requirements_all entry: scripts/run-in-env.sh python3 -m scripts.gen_requirements_all diff --git a/music_assistant/server/helpers/tags.py b/music_assistant/server/helpers/tags.py index be8d66f4..a28e72c7 100644 --- a/music_assistant/server/helpers/tags.py +++ b/music_assistant/server/helpers/tags.py @@ -115,7 +115,7 @@ class AudioTags: return "" @property - def album(self) -> str: + def album(self) -> str | None: """Return album tag (as-is) if present.""" return self.tags.get("album") diff --git a/music_assistant/server/providers/jellyfin/__init__.py b/music_assistant/server/providers/jellyfin/__init__.py index 6d2269eb..a41f8724 100644 --- a/music_assistant/server/providers/jellyfin/__init__.py +++ b/music_assistant/server/providers/jellyfin/__init__.py @@ -9,7 +9,7 @@ from asyncio import TaskGroup from collections.abc import AsyncGenerator from aiojellyfin import MediaLibrary as JellyMediaLibrary -from aiojellyfin import SessionConfiguration, authenticate_by_name +from aiojellyfin import NotFound, SessionConfiguration, authenticate_by_name from aiojellyfin import Track as JellyTrack from music_assistant.common.models.config_entries import ( @@ -370,10 +370,11 @@ class JellyfinProvider(MusicProvider): async def get_album(self, prov_album_id: str) -> Album: """Get full album details by id.""" - if jellyfin_album := await self._client.get_album(prov_album_id): - return parse_album(self.logger, self.instance_id, self._client, jellyfin_album) - msg = f"Item {prov_album_id} not found" - raise MediaNotFoundError(msg) + try: + album = await self._client.get_album(prov_album_id) + except NotFound: + raise MediaNotFoundError(f"Item {prov_album_id} not found") + return parse_album(self.logger, self.instance_id, self._client, album) async def get_album_tracks(self, prov_album_id: str) -> list[Track]: """Get album tracks for given album id.""" @@ -403,24 +404,27 @@ class JellyfinProvider(MusicProvider): artist.mbid = UNKNOWN_ARTIST_ID_MBID return artist - if jellyfin_artist := await self._client.get_artist(prov_artist_id): - return parse_artist(self.logger, self.instance_id, self._client, jellyfin_artist) - msg = f"Item {prov_artist_id} not found" - raise MediaNotFoundError(msg) + try: + jellyfin_artist = await self._client.get_artist(prov_artist_id) + except NotFound: + raise MediaNotFoundError(f"Item {prov_artist_id} not found") + return parse_artist(self.logger, self.instance_id, self._client, jellyfin_artist) async def get_track(self, prov_track_id: str) -> Track: """Get full track details by id.""" - if jellyfin_track := await self._client.get_track(prov_track_id): - return parse_track(self.logger, self.instance_id, self._client, jellyfin_track) - msg = f"Item {prov_track_id} not found" - raise MediaNotFoundError(msg) + try: + track = await self._client.get_track(prov_track_id) + except NotFound: + raise MediaNotFoundError(f"Item {prov_track_id} not found") + return parse_track(self.logger, self.instance_id, self._client, track) async def get_playlist(self, prov_playlist_id: str) -> Playlist: """Get full playlist details by id.""" - if jellyfin_playlist := await self._client.get_playlist(prov_playlist_id): - return parse_playlist(self.instance_id, self._client, jellyfin_playlist) - msg = f"Item {prov_playlist_id} not found" - raise MediaNotFoundError(msg) + try: + playlist = await self._client.get_playlist(prov_playlist_id) + except NotFound: + raise MediaNotFoundError(f"Item {prov_playlist_id} not found") + return parse_playlist(self.instance_id, self._client, playlist) async def get_playlist_tracks( self, prov_playlist_id: str, offset: int, limit: int @@ -435,8 +439,6 @@ class JellyfinProvider(MusicProvider): playlist_items = await self._client.tracks( jellyfin_playlist[ITEM_KEY_ID], enable_user_data=True, fields=TRACK_FIELDS ) - if not playlist_items: - return result for index, jellyfin_track in enumerate(playlist_items["Items"], 1): try: if track := parse_track( @@ -487,7 +489,7 @@ class JellyfinProvider(MusicProvider): path=url, ) - async def get_similar_tracks(self, prov_track_id, limit=25) -> list[Track]: + async def get_similar_tracks(self, prov_track_id: str, limit: int = 25) -> list[Track]: """Retrieve a dynamic list of tracks based on the provided item.""" resp = await self._client.get_similar_tracks(prov_track_id, limit=limit) return [ diff --git a/music_assistant/server/providers/jellyfin/manifest.json b/music_assistant/server/providers/jellyfin/manifest.json index 27cd9827..7832540c 100644 --- a/music_assistant/server/providers/jellyfin/manifest.json +++ b/music_assistant/server/providers/jellyfin/manifest.json @@ -4,7 +4,7 @@ "name": "Jellyfin Media Server Library", "description": "Support for the Jellyfin streaming provider in Music Assistant.", "codeowners": ["@lokiberra", "@Jc2k"], - "requirements": ["aiojellyfin==0.9.0"], + "requirements": ["aiojellyfin==0.9.1"], "documentation": "https://music-assistant.io/music-providers/jellyfin/", "multi_instance": true } diff --git a/music_assistant/server/providers/jellyfin/parsers.py b/music_assistant/server/providers/jellyfin/parsers.py index a552fbc8..907f44b4 100644 --- a/music_assistant/server/providers/jellyfin/parsers.py +++ b/music_assistant/server/providers/jellyfin/parsers.py @@ -8,11 +8,7 @@ from typing import TYPE_CHECKING from aiojellyfin.const import ImageType as JellyImageType -from music_assistant.common.models.enums import ( - ContentType, - ImageType, - MediaType, -) +from music_assistant.common.models.enums import ContentType, ImageType, MediaType from music_assistant.common.models.errors import InvalidDataError from music_assistant.common.models.media_items import ( Album, @@ -190,7 +186,7 @@ def parse_track( track.disc_number = jellyfin_track.get(ITEM_KEY_PARENT_INDEX_NUM, 0) track.track_number = jellyfin_track.get("IndexNumber", 0) - if track.track_number >= 0: + if track.track_number is not None and track.track_number >= 0: track.position = track.track_number track.metadata.images = _get_artwork(instance_id, client, jellyfin_track) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..cf337fb1 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,24 @@ +[mypy] +python_version = 3.12 +platform = linux +show_error_codes = true +follow_imports = silent +local_partial_types = true +strict_equality = true +no_implicit_optional = true +warn_incomplete_stub = true +warn_redundant_casts = true +warn_unused_configs = true +warn_unused_ignores = true +enable_error_code = ignore-without-code, redundant-self, truthy-iterable +disable_error_code = annotation-unchecked, import-not-found, import-untyped +extra_checks = false +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +warn_return_any = true +warn_unreachable = true +packages=tests,music_assistant.client,music_assistant.server.providers.jellyfin diff --git a/requirements_all.txt b/requirements_all.txt index 49c6a98f..1aadd07d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -4,7 +4,7 @@ Brotli>=1.0.9 aiodns>=3.0.0 aiofiles==24.1.0 aiohttp==3.9.5 -aiojellyfin==0.9.0 +aiojellyfin==0.9.1 aiorun==2024.5.1 aioslimproto==3.0.1 aiosqlite==0.20.0 diff --git a/tests/test_tags.py b/tests/test_tags.py index 382d25b1..be9a94f9 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -9,7 +9,7 @@ RESOURCES_DIR = pathlib.Path(__file__).parent.resolve().joinpath("fixtures") FILE_1 = str(RESOURCES_DIR.joinpath("MyArtist - MyTitle.mp3")) -async def test_parse_metadata_from_id3tags(): +async def test_parse_metadata_from_id3tags() -> None: """Test parsing of parsing metadata from ID3 tags.""" filename = str(RESOURCES_DIR.joinpath("MyArtist - MyTitle.mp3")) _tags = await tags.parse_tags(filename) @@ -24,26 +24,26 @@ async def test_parse_metadata_from_id3tags(): assert _tags.musicbrainz_releasegroupid == "abcdefg" assert _tags.musicbrainz_recordingid == "abcdefg" # test parsing disc/track number + _tags.tags["disc"] = "" + assert _tags.disc is None _tags.tags["disc"] = "1" assert _tags.disc == 1 _tags.tags["disc"] = "1/1" assert _tags.disc == 1 - _tags.tags["disc"] = "" - assert _tags.disc is None # test parsing album year + _tags.tags["date"] = "blah" + assert _tags.year is None + _tags.tags.pop("date", None) + assert _tags.year is None _tags.tags["date"] = "2022" assert _tags.year == 2022 _tags.tags["date"] = "2022-05-05" assert _tags.year == 2022 - _tags.tags["date"] = "blah" - assert _tags.year is None _tags.tags["date"] = "" assert _tags.year is None - _tags.tags.pop("date", None) - assert _tags.year is None -async def test_parse_metadata_from_filename(): +async def test_parse_metadata_from_filename() -> None: """Test parsing of parsing metadata from filename.""" filename = str(RESOURCES_DIR.joinpath("MyArtist - MyTitle without Tags.mp3")) _tags = await tags.parse_tags(filename) @@ -59,7 +59,7 @@ async def test_parse_metadata_from_filename(): assert _tags.musicbrainz_recordingid is None -async def test_parse_metadata_from_invalid_filename(): +async def test_parse_metadata_from_invalid_filename() -> None: """Test parsing of parsing metadata from (invalid) filename.""" filename = str(RESOURCES_DIR.joinpath("test.mp3")) _tags = await tags.parse_tags(filename)