Skip to content

Commit

Permalink
Make id field within Artist optional (#663)
Browse files Browse the repository at this point in the history
  • Loading branch information
es3n1n authored Jan 18, 2025
1 parent 2a09dcb commit 15fc42b
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 21 deletions.
20 changes: 17 additions & 3 deletions tests/test_artist.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import pytest

from yandex_music import Artist
from yandex_music.exceptions import IdMissingError


class TestArtist:
Expand Down Expand Up @@ -70,10 +73,14 @@ def test_de_list_none(self, client):
assert Artist.de_list([], client) == []

def test_de_json_required(self, client, cover):
json_dict = {'id': self.id}
artist = Artist.de_json(json_dict, client)
# We don't have any required fields anymore,
# so just make sure we don't throw any errors.
Artist.de_json({}, client)

assert artist.id == self.id
def test_de_json_ugc(self, client):
# An example of UGC artist from #663
artist = Artist.de_json({'name': self.name}, client)
assert artist.name == self.name

def test_de_json_all(
self, client, cover, counts, ratings, link, track_without_artists, description, artist_decomposed
Expand Down Expand Up @@ -154,3 +161,10 @@ def test_equality(self, cover):
assert a is not b

assert a == c

def test_id_required(self, client):
artist = Artist.de_json({'name': self.name}, client)

# Make sure we throw an error if we try to access the id_required property when id is None
with pytest.raises(IdMissingError):
_ = artist.id_required
54 changes: 36 additions & 18 deletions yandex_music/artist/artist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import TYPE_CHECKING, Any, List, Optional, Union

from yandex_music import YandexMusicModel
from yandex_music.exceptions import IdMissingError
from yandex_music.utils import model

if TYPE_CHECKING:
Expand All @@ -23,7 +24,7 @@ class Artist(YandexMusicModel):
"""Класс, представляющий исполнителя.
Attributes:
id (:obj:`int`): Уникальный идентификатор.
id (:obj:`int`, optional): Уникальный идентификатор.
error (:obj:`str`, optional): Сообщение об ошибке с объяснением почему не вернуло исполнителя.
reason (:obj:`str`, optional): Причина отсутствия исполнителя (сообщение об ошибке).
name (:obj:`str`, optional): Название.
Expand Down Expand Up @@ -57,7 +58,7 @@ class Artist(YandexMusicModel):
client (:obj:`yandex_music.Client`): Клиент Yandex Music.
"""

id: int
id: Optional[int] = None
error: Optional[str] = None
reason: Optional[str] = None
name: Optional[str] = None
Expand Down Expand Up @@ -92,6 +93,21 @@ class Artist(YandexMusicModel):
def __post_init__(self) -> None:
self._id_attrs = (self.id, self.name, self.cover)

@property
def id_required(self) -> int:
"""Возвращает ID исполнителя, удостоверяясь, что он указан.
Raises:
IdMissingError: Если ID исполнителя не установлен.
Returns:
:obj:`int`: Уникальный идентификатор исполнителя.
"""
if self.id is None:
raise IdMissingError('Artist.id is required')

return self.id

def get_op_image_url(self, size: str = '200x200') -> str:
"""Возвращает URL OP обложки.
Expand Down Expand Up @@ -219,72 +235,72 @@ async def download_op_image_bytes_async(self, size: str = '200x200') -> bytes:
def like(self, *args: Any, **kwargs: Any) -> bool:
"""Сокращение для::
client.users_likes_artists_add(artist.id, user.id *args, **kwargs)
client.users_likes_artists_add(artist.id_required, user.id *args, **kwargs)
"""
assert self.valid_client(self.client)
return self.client.users_likes_artists_add(self.id, self.client.account_uid, *args, **kwargs)
return self.client.users_likes_artists_add(self.id_required, self.client.account_uid, *args, **kwargs)

async def like_async(self, *args: Any, **kwargs: Any) -> bool:
"""Сокращение для::
await client.users_likes_artists_add(artist.id, user.id *args, **kwargs)
await client.users_likes_artists_add(artist.id_required, user.id *args, **kwargs)
"""
assert self.valid_async_client(self.client)
return await self.client.users_likes_artists_add(self.id, self.client.account_uid, *args, **kwargs)
return await self.client.users_likes_artists_add(self.id_required, self.client.account_uid, *args, **kwargs)

def dislike(self, *args: Any, **kwargs: Any) -> bool:
"""Сокращение для::
client.users_likes_artists_remove(artist.id, user.id *args, **kwargs)
client.users_likes_artists_remove(artist.id_required, user.id *args, **kwargs)
"""
assert self.valid_client(self.client)
return self.client.users_likes_artists_remove(self.id, self.client.account_uid, *args, **kwargs)
return self.client.users_likes_artists_remove(self.id_required, self.client.account_uid, *args, **kwargs)

async def dislike_async(self, *args: Any, **kwargs: Any) -> bool:
"""Сокращение для::
await client.users_likes_artists_remove(artist.id, user.id *args, **kwargs)
await client.users_likes_artists_remove(artist.id_required, user.id *args, **kwargs)
"""
assert self.valid_async_client(self.client)
return await self.client.users_likes_artists_remove(self.id, self.client.account_uid, *args, **kwargs)
return await self.client.users_likes_artists_remove(self.id_required, self.client.account_uid, *args, **kwargs)

def get_tracks(self, page: int = 0, page_size: int = 20, *args: Any, **kwargs: Any) -> Optional['ArtistTracks']:
"""Сокращение для::
client.artists_tracks(artist.id, page, page_size, *args, **kwargs)
client.artists_tracks(artist.id_required, page, page_size, *args, **kwargs)
"""
assert self.valid_client(self.client)
return self.client.artists_tracks(self.id, page, page_size, *args, **kwargs)
return self.client.artists_tracks(self.id_required, page, page_size, *args, **kwargs)

async def get_tracks_async(
self, page: int = 0, page_size: int = 20, *args: Any, **kwargs: Any
) -> Optional['ArtistTracks']:
"""Сокращение для::
await client.artists_tracks(artist.id, page, page_size, *args, **kwargs)
await client.artists_tracks(artist.id_required, page, page_size, *args, **kwargs)
"""
assert self.valid_async_client(self.client)
return await self.client.artists_tracks(self.id, page, page_size, *args, **kwargs)
return await self.client.artists_tracks(self.id_required, page, page_size, *args, **kwargs)

def get_albums(
self, page: int = 0, page_size: int = 20, sort_by: str = 'year', *args: Any, **kwargs: Any
) -> Optional['ArtistAlbums']:
"""Сокращение для::
client.artists_direct_albums(artist.id, page, page_size, sort_by, *args, **kwargs)
client.artists_direct_albums(artist.id_required, page, page_size, sort_by, *args, **kwargs)
"""
assert self.valid_client(self.client)
return self.client.artists_direct_albums(self.id, page, page_size, sort_by, *args, **kwargs)
return self.client.artists_direct_albums(self.id_required, page, page_size, sort_by, *args, **kwargs)

async def get_albums_async(
self, page: int = 0, page_size: int = 20, sort_by: str = 'year', *args: Any, **kwargs: Any
) -> Optional['ArtistAlbums']:
"""Сокращение для::
await client.artists_direct_albums(artist.id, page, page_size, sort_by, *args, **kwargs)
await client.artists_direct_albums(artist.id_required, page, page_size, sort_by, *args, **kwargs)
"""
assert self.valid_async_client(self.client)
return await self.client.artists_direct_albums(self.id, page, page_size, sort_by, *args, **kwargs)
return await self.client.artists_direct_albums(self.id_required, page, page_size, sort_by, *args, **kwargs)

@classmethod
def de_json(cls, data: 'JSONType', client: 'ClientType') -> Optional['Artist']:
Expand Down Expand Up @@ -328,6 +344,8 @@ def de_json(cls, data: 'JSONType', client: 'ClientType') -> Optional['Artist']:

# camelCase псевдонимы

#: Псевдоним для :attr:`id_required`
idRequired = id_required
#: Псевдоним для :attr:`get_op_image_url`
getOpImageUrl = get_op_image_url
#: Псевдоним для :attr:`get_og_image_url`
Expand Down
4 changes: 4 additions & 0 deletions yandex_music/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ class InvalidBitrateError(YandexMusicError):
"""Класс исключения, вызываемого при попытке загрузки трека с недоступным битрейтом."""


class IdMissingError(YandexMusicError):
"""Класс исключения, вызываемого при попытке использования отсутствующего ID."""


class NetworkError(YandexMusicError):
"""Базовый класс исключений, вызываемых для ошибок, связанных с запросами к серверу."""

Expand Down

0 comments on commit 15fc42b

Please sign in to comment.