From e1da90ce91324a1ff98f5c85999ba918c81a15e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Sun, 16 Jan 2022 01:44:42 +0200 Subject: [PATCH 1/7] Use NamedTuple typing --- trakt/core.py | 36 +++++++++++++++++++++++++++++------- trakt/movies.py | 19 ++++++++++++++----- trakt/tv.py | 8 ++++++-- trakt/users.py | 28 +++++++++++++++++++--------- 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/trakt/core.py b/trakt/core.py index dfd11f9..0b26ce2 100644 --- a/trakt/core.py +++ b/trakt/core.py @@ -7,10 +7,10 @@ import os import sys import time -from collections import namedtuple from datetime import datetime, timedelta, timezone from functools import wraps from json import JSONDecodeError +from typing import NamedTuple from urllib.parse import urljoin import requests @@ -362,12 +362,34 @@ def init(*args, **kwargs): return auth_method.get(AUTH_METHOD, PIN_AUTH)(*args, **kwargs) -Airs = namedtuple('Airs', ['day', 'time', 'timezone']) -Alias = namedtuple('Alias', ['title', 'country']) -Genre = namedtuple('Genre', ['name', 'slug']) -Comment = namedtuple('Comment', ['id', 'parent_id', 'created_at', 'comment', - 'spoiler', 'review', 'replies', 'user', - 'updated_at', 'likes', 'user_rating']) +class Airs(NamedTuple): + day: str + time: str + timezone: str + + +class Alias(NamedTuple): + title: str + country: str + + +class Genre(NamedTuple): + name: str + slug: str + + +class Comment(NamedTuple): + id: str + parent_id: str + created_at: str + comment: str + spoiler: str + review: str + replies: str + user: str + updated_at: str + likes: str + user_rating: str def _validate_token(s): diff --git a/trakt/movies.py b/trakt/movies.py index fea6372..4d299b7 100644 --- a/trakt/movies.py +++ b/trakt/movies.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """Interfaces to all of the Movie objects offered by the Trakt.tv API""" -from collections import namedtuple +from typing import NamedTuple from trakt.core import Alias, Comment, Genre, delete, get from trakt.mixins import IdsMixin @@ -16,8 +16,13 @@ 'trending_movies', 'updated_movies', 'Release', 'Movie', 'Translation'] -Translation = namedtuple('Translation', ['title', 'overview', 'tagline', - 'language']) + +# FIXME: same symbol in tv module +class Translation(NamedTuple): + title: str + overview: str + tagline: str + language: str @delete @@ -76,8 +81,12 @@ def updated_movies(timestamp=None): yield to_ret -Release = namedtuple('Release', ['country', 'certification', 'release_date', - 'note', 'release_type']) +class Release(NamedTuple): + country: str + certification: str + release_date: str + note: str + release_type: str class Movie(IdsMixin): diff --git a/trakt/tv.py b/trakt/tv.py index e6291a8..91db5b5 100644 --- a/trakt/tv.py +++ b/trakt/tv.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Interfaces to all of the TV objects offered by the Trakt.tv API""" -from collections import namedtuple from datetime import datetime, timedelta +from typing import NamedTuple from urllib.parse import urlencode from trakt.core import Airs, Alias, Comment, Genre, delete, get @@ -22,7 +22,11 @@ 'TVSeason', 'Translation'] -Translation = namedtuple('Translation', ['title', 'overview', 'language']) +# FIXME: same symbol in movie module +class Translation(NamedTuple): + title: str + overview: str + language: str @delete diff --git a/trakt/users.py b/trakt/users.py index 90717ef..0da9c59 100644 --- a/trakt/users.py +++ b/trakt/users.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Interfaces to all of the User objects offered by the Trakt.tv API""" from collections import namedtuple +from typing import Any, NamedTuple from trakt.core import delete, get, post from trakt.mixins import IdsMixin @@ -14,8 +15,10 @@ 'get_user_settings', 'unfollow'] -class Request(namedtuple('Request', ['id', 'requested_at', 'user'])): - __slots__ = () +class Request(NamedTuple): + id: int + user: Any + requested_at: str @post def approve(self): @@ -62,13 +65,20 @@ def unfollow(user_name): yield 'users/{username}/follow'.format(username=slugify(user_name)) -class UserList(namedtuple('UserList', ['name', 'description', 'privacy', - 'share_link', 'type', 'display_numbers', - 'allow_comments', 'sort_by', - 'sort_how', 'created_at', - 'updated_at', 'item_count', - 'comment_count', 'likes', 'ids', - 'user', 'creator']), IdsMixin): +UserListFields = [ + 'name', 'description', 'privacy', + 'share_link', 'type', 'display_numbers', + 'allow_comments', 'sort_by', + 'sort_how', 'created_at', + 'updated_at', 'item_count', + 'comment_count', 'likes', 'ids', + 'user', 'creator', +] +# Can't have NamedTuple and __init__, so this stays as namedtuple() +UserListTuple = namedtuple('UserList', UserListFields) + + +class UserList(UserListTuple, IdsMixin): """A list created by a Trakt.tv :class:`User`""" def __init__(self, *args, ids=None, **kwargs): From aa6a0799e6ae9001dc1921cf1dbaff7e0cbebdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Tue, 18 Apr 2023 23:14:51 +0300 Subject: [PATCH 2/7] Add DataClassMixin mixin factory --- trakt/mixins.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/trakt/mixins.py b/trakt/mixins.py index 4b39195..6520d67 100644 --- a/trakt/mixins.py +++ b/trakt/mixins.py @@ -4,6 +4,23 @@ __author__ = 'Jon Nappi, Elan Ruusamäe' +def data_class_factory(data_class): + """ + A Mixin for inheriting @dataclass or NamedTuple, via composition rather inheritance. + """ + class DataClassMixinClass: + def __init__(self, **kwargs): + self.data = data_class(**kwargs) + + def __getattr__(self, item): + return getattr(self.data, item) + + return DataClassMixinClass + + +DataClassMixin = data_class_factory + + class IdsMixin: """ Provides Mixin to translate "ids" array From 16c3ded7f3abce2afac261f64bc91a0b749e2f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Tue, 18 Apr 2023 23:16:56 +0300 Subject: [PATCH 3/7] Use DataClassMixin helper --- trakt/users.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trakt/users.py b/trakt/users.py index 0da9c59..de02aa8 100644 --- a/trakt/users.py +++ b/trakt/users.py @@ -4,7 +4,7 @@ from typing import Any, NamedTuple from trakt.core import delete, get, post -from trakt.mixins import IdsMixin +from trakt.mixins import DataClassMixin, IdsMixin from trakt.movies import Movie from trakt.people import Person from trakt.tv import TVEpisode, TVSeason, TVShow @@ -78,11 +78,11 @@ def unfollow(user_name): UserListTuple = namedtuple('UserList', UserListFields) -class UserList(UserListTuple, IdsMixin): +class UserList(DataClassMixin(UserListTuple), IdsMixin): """A list created by a Trakt.tv :class:`User`""" - def __init__(self, *args, ids=None, **kwargs): - super().__init__() + def __init__(self, ids=None, **kwargs): + super().__init__(**kwargs) self._ids = ids self._items = list() From 9279b5a136b3f93695441472950f44c1741813c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Tue, 18 Apr 2023 23:17:58 +0300 Subject: [PATCH 4/7] Adds ids initializer for IdsMixin --- trakt/mixins.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/trakt/mixins.py b/trakt/mixins.py index 6520d67..52d61f2 100644 --- a/trakt/mixins.py +++ b/trakt/mixins.py @@ -31,8 +31,10 @@ class IdsMixin: __ids = ['imdb', 'slug', 'tmdb', 'trakt'] - def __init__(self): - self._ids = {} + def __init__(self, ids=None): + if ids is None: + ids = {} + self._ids = ids @property def ids(self): From c00d1c4174dabfda40cff506ad36f5eff60c927c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Tue, 18 Apr 2023 23:19:41 +0300 Subject: [PATCH 5/7] Add IdsMixin slug getter/setter --- trakt/mixins.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/trakt/mixins.py b/trakt/mixins.py index 52d61f2..d4b0d15 100644 --- a/trakt/mixins.py +++ b/trakt/mixins.py @@ -70,3 +70,11 @@ def tvdb(self): @property def tvrage(self): return self._ids.get('tvrage', None) + + @property + def slug(self): + return self._ids.get('slug', None) + + @slug.setter + def slug(self, value): + self._ids['slug'] = value From 973767dfb8734449c6ea01a7c07943a368d90ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Tue, 18 Apr 2023 23:20:37 +0300 Subject: [PATCH 6/7] Cleanup duplicate slug property --- trakt/users.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/trakt/users.py b/trakt/users.py index de02aa8..a7d22b0 100644 --- a/trakt/users.py +++ b/trakt/users.py @@ -90,10 +90,6 @@ def __iter__(self): """Iterate over the items in this user list""" return self._items.__iter__() - @property - def slug(self): - return self._ids.get('slug', None) - @classmethod @post def create(cls, name, creator, description=None, privacy='private', From 3c3d379a10af4a1d153522bba914989ba43b1afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Tue, 18 Apr 2023 23:21:50 +0300 Subject: [PATCH 7/7] Change UserListTuple to class inheriting NamedTuple --- trakt/users.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/trakt/users.py b/trakt/users.py index a7d22b0..4a8b33e 100644 --- a/trakt/users.py +++ b/trakt/users.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- """Interfaces to all of the User objects offered by the Trakt.tv API""" -from collections import namedtuple from typing import Any, NamedTuple from trakt.core import delete, get, post @@ -65,17 +64,23 @@ def unfollow(user_name): yield 'users/{username}/follow'.format(username=slugify(user_name)) -UserListFields = [ - 'name', 'description', 'privacy', - 'share_link', 'type', 'display_numbers', - 'allow_comments', 'sort_by', - 'sort_how', 'created_at', - 'updated_at', 'item_count', - 'comment_count', 'likes', 'ids', - 'user', 'creator', -] -# Can't have NamedTuple and __init__, so this stays as namedtuple() -UserListTuple = namedtuple('UserList', UserListFields) +class UserListTuple(NamedTuple): + name: str + description: str + privacy: str + share_link: str + type: str + display_numbers: bool + allow_comments: bool + sort_by: str + sort_how: str + created_at: str + updated_at: str + item_count: int + comment_count: int + likes: int + user: Any + creator: str class UserList(DataClassMixin(UserListTuple), IdsMixin):