def test_get_quargs(): client = Trakt("", "") p = Path("a", {}, filters={"query", "genres"}, extended=["metadata"]) assert p.is_valid(client, extended=True, query="xyz", genres=["a", "b"]) _, quargs = p.get_path_and_qargs() expected = {"genres": "a,b", "query": "xyz", "extended": "metadata"} assert quargs == expected
class RecommendationsI(SuiteInterface): name = "recommendations" paths = { "get_movie_recommendations": Path( "recommendations/movies", [Movie], validators=[AuthRequiredValidator(), IGNORE_COLLECTED_VALIDATOR], extended=["full"], qargs=["ignore_collected"], ), "hide_movie": Path( "recommendations/movies/!id", {}, methods="DELETE", validators=[AuthRequiredValidator(), ID_VALIDATOR], ), "get_show_recommendations": Path( "recommendations/shows", [Show], validators=[AuthRequiredValidator(), IGNORE_COLLECTED_VALIDATOR], extended=["full"], qargs=["ignore_collected"], ), "hide_show": Path( "recommendations/shows/!id", {}, methods="DELETE", validators=[AuthRequiredValidator(), ID_VALIDATOR], ), } def get_movie_recommendations( self, *, ignore_collected: bool = False, **kwargs ) -> List[Movie]: return self.run( "get_movie_recommendations", **kwargs, ignore_collected=ignore_collected ) def hide_movie(self, *, movie: Union[Movie, str, int], **kwargs) -> None: id = self._generic_get_id(movie) self.run("hide_movie", **kwargs, id=id) def get_show_recommendations( self, *, ignore_collected: bool = False, **kwargs ) -> List[Show]: return self.run( "get_show_recommendations", **kwargs, ignore_collected=ignore_collected ) def hide_show(self, *, show: Union[Movie, str, int], **kwargs) -> None: id = self._generic_get_id(show) self.run("hide_show", **kwargs, id=id)
def test_multiple_validators_for_field(): p = Path( "?a", {}, validators=[ PerArgValidator("a", lambda x: x > 10), PerArgValidator("a", lambda x: x % 3 == 0), ], ) assert p.is_valid(None, a=30) for val in [6, 13]: with pytest.raises(ArgumentError): p.is_valid(None, a=val)
class NetworksI(SuiteInterface): name = "networks" paths = {"get_networks": Path("networks", [Network], cache_level="basic")} def get_networks(self, **kwargs: Any) -> List[Network]: return self.run("get_networks", **kwargs)
def exec_path_call(self, path: Path, extra_quargs: Optional[Dict[str, str]] = None, **kwargs: Any) -> ApiResponse: caching_enabled = self._should_use_cache(path, kwargs.get("no_cache", False)) api_path, query_args = path.get_path_and_qargs() query_args.update(extra_quargs or {}) api_response = self.client.http.request( api_path, method=path.method, query_args=query_args, data=kwargs.get("data"), use_cache=caching_enabled, **kwargs, ) api_response.parsed = json_parser.parse_tree(api_response.json, path.response_structure) if caching_enabled: # only runs if there were no errors last_request = cast(FrozenRequest, self.client.http.last_request) self.client.cache.set(last_request) return api_response
class ListsI(SuiteInterface): name = "lists" paths = { "get_trending": Path( "lists/trending", [ListResponse], extended=["full"], pagination=True ), "get_popular": Path( "lists/popular", [ListResponse], extended=["full"], pagination=True ), } def get_trending(self, **kwargs: Any) -> PaginationIterator[ListResponse]: return self.run("get_trending", **kwargs) def get_popular(self, **kwargs: Any) -> PaginationIterator[ListResponse]: return self.run("get_popular", **kwargs)
def test_add_quargs(): client = mk_mock_client({".*": [[], 200]}) path = Path("a", [None], qargs=["arg"]) executor = Executor(client) _ = executor.run(path=path, arg="abc") req = client.http._requests.req_map["a"][0] assert req["path"].endswith(r"/a?arg=abc")
class GenresI(SuiteInterface): name = "genres" paths = { "get_genres": Path( "genres/!type", [Genre], validators=[TYPE_MOVIES_SHOWS], cache_level="basic" ) } def get_genres(self, *, type: str, **kwargs: Any) -> List[Genre]: return self.run("get_genres", type=type, **kwargs)
def _make_path(self, resource_path: str, return_type: Any) -> Path: extra_validators = [AuthRequiredValidator() ] if "my/" in resource_path else [] return Path( "calendars/" + resource_path, return_type, extended=["full"], filters=COMMON_FILTERS | SHOWS_FILTERS, validators=self.COMMON_VALIDATORS + extra_validators, # type: ignore )
def _make_path(self, resource_path: str, return_type: Any) -> Path: extra_validators: List[Validator] = [] if "?period" in resource_path: extra_validators.append( PerArgValidator("period", lambda p: p in PERIOD_VALUES)) return Path( self.name + "/" + resource_path, return_type, extended=["full"], filters=COMMON_FILTERS, pagination=True, validators=extra_validators, )
class CertificationsI(SuiteInterface): name = "certifications" paths = { "get_certifications": Path( "certifications/!type", {"us": [Certification]}, validators=[TYPE_MOVIES_SHOWS], cache_level="basic", ) } def get_certifications(self, *, type: str, **kwargs: Any) -> List[Certification]: ret = self.run("get_certifications", type=type, **kwargs) return ret["us"]
class CountriesI(SuiteInterface): name = "countries" paths = { "get_countries": Path( "countries/!type", [Country], aliases=["get_countries", ""], validators=[TYPE_MOVIES_SHOWS], cache_level="basic", ) } def get_countries(self, *, type: str, **kwargs: Any) -> List[Country]: return self.run("get_countries", type=type, **kwargs)
def test_required_args(): client = Trakt("", "") p = Path("aaa/!b/ccc/?d", [{"a": str}]) assert p.methods == ["GET"] assert p.args == ["!b", "?d"] default_alias = "aaa.ccc" assert p.aliases == [default_alias] assert p.does_match(default_alias) with pytest.raises(ArgumentError): p.is_valid(client) with pytest.raises( ArgumentError): # intentional, assert didn't bind any values p.is_valid(client) assert p.is_valid(client, b=10) assert p.get_path_and_qargs() == ("aaa/10/ccc", {})
def test_optional_args(): client = Trakt("", "") p = Path("calendars/all/shows/new/?start_date/?days", [{"a": str}]) assert p.methods == ["GET"] assert p.args == ["?start_date", "?days"] default_alias = "calendars.all.shows.new" assert p.aliases == [default_alias] assert p.does_match(default_alias) assert not p.does_match(default_alias[1:]) assert p.is_valid(client) assert p.get_path_and_qargs() == ("calendars/all/shows/new", {}) assert p.is_valid(client, start_date="2018-10-10") assert p.get_path_and_qargs() == ("calendars/all/shows/new/2018-10-10", {})
class CheckinI(SuiteInterface): name = "checkin" paths = { "delete_active_checkins": Path( "checkin", {}, methods="DELETE", validators=[AuthRequiredValidator()] ), "check_into_episode": Path( "checkin", EpisodeCheckin, methods="POST", validators=[AuthRequiredValidator(), MESSAGE_VALIDATOR], ), "check_into_movie": Path( "checkin", MovieCheckin, methods="POST", validators=[AuthRequiredValidator(), MESSAGE_VALIDATOR], ), } @auth_required def check_into( self, *, movie: Optional[Union[Movie, Dict[str, Any]]] = None, episode: Optional[Union[Episode, Dict[str, Any]]] = None, **kwargs: Any, ) -> Union[EpisodeCheckin, MovieCheckin]: if movie and episode: raise ArgumentError("you must provide exactly one of: [episode, movie]") if movie: return self.check_into_movie(movie=movie, **kwargs) elif episode: return self.check_into_episode(episode=episode, **kwargs) else: raise ArgumentError("you must provide exactly one of: [episode, movie]") def check_into_episode( self, *, episode: Union[Episode, Dict[str, Any]], message: Optional[str] = None, sharing: Optional[Union[Sharing, Dict[str, str]]] = None, **kwargs, ) -> EpisodeCheckin: data: Dict[str, Any] = self._prepare_common_data( **kwargs, message=message, sharing=sharing ) if isinstance(episode, Episode): episode = {"ids": {"trakt": self._generic_get_id(episode)}} data["episode"] = episode if "show" in kwargs: show = kwargs["show"] if isinstance(show, Show): data["show"] = {"ids": {"trakt": self._generic_get_id(show)}} elif isinstance(show, dict): data["show"] = show else: raise ArgumentError("show: invalid argument value") return self.run("check_into_episode", **data, body=data) def check_into_movie( self, *, movie: Union[Movie, Dict[str, Any]], message: Optional[str] = None, sharing: Optional[Union[Sharing, Dict[str, str]]] = None, **kwargs, ) -> MovieCheckin: data = self._prepare_common_data(**kwargs, message=message, sharing=sharing) if isinstance(movie, Movie): movie = {"ids": {"trakt": self._generic_get_id(movie)}} data["movie"] = movie return self.run("check_into_movie", **data, body=data) def _prepare_common_data( self, **kwargs: Any ) -> Dict[str, Union[str, Dict[str, str]]]: d: Dict[str, Union[str, Dict[str, str]]] = {} if "sharing" in kwargs and kwargs["sharing"] is not None: if isinstance(kwargs["sharing"], Sharing): d["sharing"] = asdict(kwargs["sharing"]) elif isinstance(kwargs["sharing"], dict): d["sharing"] = kwargs["sharing"] else: raise ArgumentError("sharing: invalid argument value") for f in ["message", "venue_id", "venue_name", "app_version", "app_date"]: if f in kwargs: v = kwargs[f] if v is not None: d[f] = v return d def delete_active_checkins(self, **kwargs: Any) -> None: self.run("delete_active_checkins", **kwargs)
class SeasonsI(SuiteInterface): name = "seasons" paths = { "get_all_seasons": Path( "shows/!id/seasons", [Season], extended=["full", "episodes"], validators=[ID_VALIDATOR], cache_level="basic", ), "get_season": Path( "shows/!id/seasons/!season", [Episode], extended=["full", "episodes"], validators=[ ID_VALIDATOR, SEASON_ID_VALIDATOR, PerArgValidator( "translations", lambda t: isinstance(t, str) and (t == "all" or len(t) == 2), ), ], qargs=["translations"], cache_level="basic", ), "get_comments": Path( "shows/!id/seasons/!season/comments/?sort", [Comment], validators=[ ID_VALIDATOR, SEASON_ID_VALIDATOR, PerArgValidator("sort", lambda s: s in COMMENT_SORT_VALUES), ], pagination=True, ), "get_lists": Path( "shows/!id/seasons/!season/lists/?type/?sort", [TraktList], validators=[ ID_VALIDATOR, SEASON_ID_VALIDATOR, PerArgValidator("type", lambda t: t in LIST_TYPE_VALUES), PerArgValidator("sort", lambda s: s in LIST_SORT_VALUES), ], pagination=True, ), "get_ratings": Path( "shows/!id/seasons/!season/ratings", RatingsSummary, validators=[ID_VALIDATOR, SEASON_ID_VALIDATOR], ), "get_stats": Path( "shows/!id/seasons/!season/stats", SeasonEpisodeStats, validators=[ID_VALIDATOR, SEASON_ID_VALIDATOR], ), "get_users_watching": Path( "shows/!id/seasons/!season/watching", [User], extended=["full"], validators=[ID_VALIDATOR, SEASON_ID_VALIDATOR], ), } def get_all_seasons(self, *, show: Union[Show, str, int], season: Union[Season, str, int], **kwargs) -> List[Season]: id = self._generic_get_id(show) season = self._generic_get_id(season) return self.run("get_all_seasons", **kwargs, id=id, season=season) def get_season(self, *, show: Union[Show, str, int], season: Union[Season, str, int], **kwargs) -> List[Episode]: id = self._generic_get_id(show) season = self._generic_get_id(season) return self.run("get_season", **kwargs, id=id, season=season) def get_comments(self, *, show: Union[Show, str, int], season: Union[Season, str, int], sort: str = "newest", **kwargs) -> PaginationIterator[Comment]: id = self._generic_get_id(show) season = self._generic_get_id(season) return self.run("get_comments", **kwargs, sort=sort, id=id, season=season) def get_lists(self, *, show: Union[Show, str, int], season: Union[Season, str, int], type: str = "personal", sort: str = "popular", **kwargs) -> PaginationIterator[TraktList]: id = self._generic_get_id(show) season = self._generic_get_id(season) return self.run("get_lists", **kwargs, type=type, sort=sort, id=id, season=season) def get_ratings(self, *, show: Union[Show, str, int], season: Union[Season, str, int], **kwargs) -> RatingsSummary: id = self._generic_get_id(show) season = self._generic_get_id(season) return self.run("get_ratings", **kwargs, id=id, season=season) def get_stats(self, *, show: Union[Show, str, int], season: Union[Season, str, int], **kwargs) -> SeasonEpisodeStats: id = self._generic_get_id(show) season = self._generic_get_id(season) return self.run("get_stats", **kwargs, id=id, season=season) def get_users_watching(self, *, show: Union[Show, str, int], season: Union[Season, str, int], **kwargs) -> List[User]: id = self._generic_get_id(show) season = self._generic_get_id(season) return self.run("get_users_watching", **kwargs, id=id, season=season)
class CommentsI(SuiteInterface): name = "comments" paths = { "post_comment": Path( "comments", CommentResponse, methods=["POST"], validators=[AuthRequiredValidator(), COMMENT_TEXT_VALIDATOR], ), "get_comment": Path("comments/!id", Comment, validators=[COMMENT_ID_VALIDATOR]), "update_comment": Path( "comments/!id", Comment, methods="PUT", validators=[COMMENT_ID_VALIDATOR, COMMENT_TEXT_VALIDATOR], ), "delete_comment": Path( "comments/!id", {}, methods="DELETE", validators=[COMMENT_ID_VALIDATOR] ), "get_replies": Path( "comments/!id/replies", [Comment], validators=[COMMENT_ID_VALIDATOR], pagination=True, ), "post_reply": Path( "comments/!id/replies", Comment, validators=[ AuthRequiredValidator(), COMMENT_ID_VALIDATOR, COMMENT_TEXT_VALIDATOR, ], ), "get_item": Path( "comments/!id/item", CommentItemOnly, validators=[COMMENT_ID_VALIDATOR], extended=["full"], ), "get_likes": Path( "comments/!id/likes", [CommentLiker], validators=[COMMENT_ID_VALIDATOR], pagination=True, ), "like_comment": Path( "comments/!id/like", {}, methods=["POST"], validators=[AuthRequiredValidator(), COMMENT_ID_VALIDATOR], ), "remove_like": Path( "comments/!id/like", {}, methods=["DELETE"], validators=[AuthRequiredValidator(), COMMENT_ID_VALIDATOR], ), "get_trending": Path( "comments/trending/?comment_type/?type", [CommentAndItem], validators=TRENDING_RECENT_UPDATED_VALIDATORS, pagination=True, extended=["full"], ), "get_recently_created": Path( "comments/recent/?comment_type/?type", [CommentAndItem], validators=TRENDING_RECENT_UPDATED_VALIDATORS, pagination=True, extended=["full"], ), "get_recently_updated": Path( "comments/updates/?comment_type/?type", [CommentAndItem], validators=TRENDING_RECENT_UPDATED_VALIDATORS, pagination=True, extended=["full"], ), } def post_comment( self, *, item: Union[str, int, Movie, Season, Show, Episode], comment: str, spoiler: bool = False, sharing: Optional[Union[Sharing, Dict[str, bool]]] = None, **kwargs ) -> CommentResponse: body: Dict[str, Union[str, int, Dict[str, bool]]] = { "item_id": self._generic_get_id(item), "comment": comment, "spoiler": spoiler, } if sharing: if isinstance(sharing, Sharing): sharing = asdict(sharing) body["sharing"] = sharing return self.run("post_comment", **kwargs, body=body, comment=comment) def get_comment(self, *, id: Union[Comment, str, int], **kwargs) -> Comment: id = int(self._generic_get_id(id)) return self.run("get_comment", **kwargs, id=id) def update_comment( self, *, id: Union[Comment, str, int], comment: str, spoiler: bool = False, **kwargs ) -> Comment: body = {"id": self._generic_get_id(id), "comment": comment, "spoiler": spoiler} return self.run("update_comment", **kwargs, body=body, id=id, comment=comment) def delete_comment(self, *, id: Union[Comment, str, int], **kwargs) -> None: id = int(self._generic_get_id(id)) self.run("delete_comment", **kwargs, id=id) def get_replies( self, *, id: Union[Comment, str, int], **kwargs ) -> PaginationIterator[Comment]: id = int(self._generic_get_id(id)) return self.run("get_replies", **kwargs, id=id) def post_reply( self, *, id: Union[Comment, str, int], comment: str, spoiler: bool = False, **kwargs ) -> PaginationIterator[Comment]: id = int(self._generic_get_id(id)) body = {"comment": comment, "spoiler": spoiler} return self.run("post_reply", **kwargs, id=id, body=body, comment=comment) def get_item(self, *, id: Union[Comment, str, int], **kwargs) -> CommentItemOnly: id = int(self._generic_get_id(id)) return self.run("get_item", **kwargs, id=id) def get_likes( self, *, id: Union[Comment, str, int], **kwargs ) -> List[CommentLiker]: id = int(self._generic_get_id(id)) return self.run("get_likes", **kwargs, id=id) def like_comment(self, *, id: Union[Comment, str, int], **kwargs) -> None: id = int(self._generic_get_id(id)) self.run("like_comment", **kwargs, id=id) def remove_like(self, *, id: Union[Comment, str, int], **kwargs) -> None: id = int(self._generic_get_id(id)) self.run("remove_like", **kwargs, id=id) def get_trending( self, *, comment_type: str = "all", type: str = "all", include_replies: bool = False, **kwargs ) -> List[CommentAndItem]: return self.run( "get_trending", **kwargs, comment_type=comment_type, type=type, include_replies=include_replies ) def get_recently_created( self, *, comment_type: str = "all", type: str = "all", include_replies: bool = False, **kwargs ) -> List[CommentAndItem]: return self.run( "get_recently_created", **kwargs, comment_type=comment_type, type=type, include_replies=include_replies ) def get_recently_updated( self, *, comment_type: str = "all", type: str = "all", include_replies: bool = False, **kwargs ) -> List[CommentAndItem]: return self.run( "get_recently_updated", **kwargs, comment_type=comment_type, type=type, include_replies=include_replies )
def _make_path(self, resource_path: str, return_type: Any) -> Path: return Path( self.name + "/" + resource_path, return_type, validators=[AuthRequiredValidator(), PROGRESS_VALIDATOR], )
class MoviesI(SuiteInterface): name = "movies" base_paths = { "get_trending": ["trending", [TrendingMovie]], "get_popular": ["popular", [Movie]], "get_most_played": ["played/?period", [MovieWithStats]], "get_most_watched": ["watched/?period", [MovieWithStats]], "get_most_collected": ["collected/?period", [MovieWithStats]], "get_most_anticipated": ["anticipated", [AnticipatedMovie]], } paths = { "get_box_office": Path("movies/boxoffice", [BoxOffice], extended=["full"], cache_level="basic"), "get_recently_updated": Path( "movies/updates/?start_date", [UpdatedMovie], extended=["full"], pagination=True, validators=[PerArgValidator("start_date", is_date)], ), "get_summary": Path("movies/!id", Movie, extended=["full"], cache_level="basic"), "get_aliases": Path("movies/!id/aliases", [Alias], cache_level="basic"), "get_releases": Path( "movies/!id/releases/?country", [MovieRelease], validators=[ PerArgValidator("country", lambda c: isinstance(c, str) and len(c) == 2) ], cache_level="basic", ), "get_translations": Path( "movies/!id/translations/?language", [MovieTranslation], validators=[ PerArgValidator("language", lambda c: isinstance(c, str) and len(c) == 2) ], cache_level="basic", ), "get_comments": Path( "movies/!id/comments/?sort", [Comment], validators=[ PerArgValidator("sort", lambda s: s in COMMENT_SORT_VALUES) ], pagination=True, ), "get_lists": Path( "movies/!id/lists/?type/?sort", [TraktList], validators=[ PerArgValidator("type", lambda t: t in LIST_TYPE_VALUES), PerArgValidator("sort", lambda s: s in LIST_SORT_VALUES), ], pagination=True, ), "get_people": Path("movies/!id/people", CastCrewList, extended=["full"], cache_level="basic"), "get_ratings": Path("movies/!id/ratings", RatingsSummary), "get_related": Path( "movies/!id/related", [Movie], extended=["full"], pagination=True, cache_level="basic", ), "get_stats": Path("movies/!id/stats", MovieStats), "get_users_watching": Path("movies/!id/watching", [User], extended=["full"]), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for k, r in self.base_paths.items(): self.paths[k] = self._make_path(*r) def _make_path(self, resource_path: str, return_type: Any) -> Path: extra_validators: List[Validator] = [] if "?period" in resource_path: extra_validators.append( PerArgValidator("period", lambda p: p in PERIOD_VALUES)) return Path( self.name + "/" + resource_path, return_type, extended=["full"], filters=COMMON_FILTERS, pagination=True, validators=extra_validators, ) def get_trending(self, **kwargs) -> PaginationIterator[TrendingMovie]: return self.run("get_trending", **kwargs) def get_popular(self, **kwargs) -> PaginationIterator[Movie]: return self.run("get_popular", **kwargs) def get_most_played(self, *, period: str = "weekly", **kwargs) -> PaginationIterator[MovieWithStats]: return self.run("get_most_played", **kwargs, period=period) def get_most_watched(self, *, period: str = "weekly", **kwargs) -> PaginationIterator[MovieWithStats]: return self.run("get_most_watched", **kwargs, period=period) def get_most_collected(self, *, period: str = "weekly", **kwargs) -> PaginationIterator[MovieWithStats]: return self.run("get_most_collected", **kwargs, period=period) def get_most_anticipated(self, **kwargs) -> PaginationIterator[AnticipatedMovie]: return self.run("get_most_anticipated", **kwargs) def get_box_office(self, **kwargs) -> List[BoxOffice]: return self.run("get_box_office", **kwargs) def get_recently_updated(self, *, start_date: Optional[str] = None, **kwargs) -> PaginationIterator[UpdatedMovie]: return self.run("get_recently_updated", **kwargs, start_date=start_date) def get_summary(self, *, movie: Union[Movie, str, int], **kwargs) -> Movie: movie_id = self._get_movie_id(movie) return self.run("get_summary", **kwargs, id=movie_id) def get_aliases(self, *, movie: Union[Movie, str, int], **kwargs) -> List[Alias]: movie_id = self._get_movie_id(movie) return self.run("get_aliases", **kwargs, id=movie_id) def get_releases(self, *, movie: Union[Movie, str, int], country: Optional[str] = None, **kwargs) -> List[MovieRelease]: extra_kwargs = {"id": self._get_movie_id(movie)} if country: extra_kwargs["country"] = country return self.run("get_releases", **kwargs, **extra_kwargs) def get_translations(self, *, movie: Union[Movie, str, int], language: Optional[str] = None, **kwargs) -> List[MovieTranslation]: extra_kwargs = {"id": self._get_movie_id(movie)} if language: extra_kwargs["language"] = language return self.run("get_translations", **kwargs, **extra_kwargs) def get_comments(self, *, movie: Union[Movie, str, int], sort: str = "newest", **kwargs) -> PaginationIterator[Comment]: movie_id = self._get_movie_id(movie) return self.run("get_comments", **kwargs, sort=sort, id=movie_id) def get_lists(self, *, movie: Union[Movie, str, int], type: str = "personal", sort: str = "popular", **kwargs) -> PaginationIterator[TraktList]: movie_id = self._get_movie_id(movie) return self.run("get_lists", **kwargs, type=type, sort=sort, id=movie_id) def get_people(self, *, movie: Union[Movie, str, int], **kwargs) -> CastCrewList: return self.run("get_people", **kwargs, id=self._get_movie_id(movie)) def get_ratings(self, *, movie: Union[Movie, str, int], **kwargs) -> RatingsSummary: return self.run("get_ratings", **kwargs, id=self._get_movie_id(movie)) def get_related(self, *, movie: Union[Movie, str, int], **kwargs) -> PaginationIterator[Movie]: return self.run("get_related", **kwargs, id=self._get_movie_id(movie)) def get_stats(self, *, movie: Union[Movie, str, int], **kwargs) -> MovieStats: return self.run("get_stats", **kwargs, id=self._get_movie_id(movie)) def get_users_watching(self, *, movie: Union[Movie, str, int], **kwargs) -> List[User]: return self.run("get_users_watching", **kwargs, id=self._get_movie_id(movie)) def _get_movie_id(self, movie: Union[Movie, str, int]) -> str: return str(self._generic_get_id(movie))
def test_aliases(): p = Path("a/b/c", {}, aliases=["", "xyz"]) assert p.does_match("xyz") assert p.does_match("") assert not p.does_match("a")
class ShowsI(SuiteInterface): name = "shows" base_paths = { "get_trending": ["trending", [TrendingShow]], "get_popular": ["popular", [Show]], "get_most_played": ["played/?period", [ShowWithStats]], "get_most_watched": ["watched/?period", [ShowWithStats]], "get_most_collected": ["collected/?period", [ShowWithStats]], "get_most_anticipated": ["anticipated", [AnticipatedShow]], } paths = { "get_recently_updated": Path( "shows/updates/?start_date", [UpdatedShow], extended=["full"], pagination=True, validators=[PerArgValidator("start_date", is_date)], ), "get_summary": Path( "shows/!id", Show, extended=["full"], validators=[ID_VALIDATOR], cache_level="basic", ), "get_aliases": Path("shows/!id/aliases", [Alias], validators=[ID_VALIDATOR], cache_level="basic"), "get_translations": Path( "shows/!id/translations/?language", [ShowTranslation], validators=[ ID_VALIDATOR, PerArgValidator("language", lambda c: isinstance(c, str) and len(c) == 2), ], cache_level="basic", ), "get_comments": Path( "shows/!id/comments/?sort", [Comment], validators=[ ID_VALIDATOR, PerArgValidator("sort", lambda s: s in COMMENT_SORT_VALUES), ], pagination=True, ), "get_lists": Path( "shows/!id/lists/?type/?sort", [TraktList], validators=[ ID_VALIDATOR, PerArgValidator("type", lambda t: t in LIST_TYPE_VALUES), PerArgValidator("sort", lambda s: s in LIST_SORT_VALUES), ], pagination=True, ), "get_collection_progress": Path( "shows/!id/progress/collection", ShowCollectionProgress, validators=PROGRESS_VALIDATORS, qargs=["hidden", "specials", "count_specials", "last_activity"], ), "get_watched_progress": Path( "shows/!id/progress/watched", ShowWatchedProgress, validators=PROGRESS_VALIDATORS, qargs=["hidden", "specials", "count_specials", "last_activity"], ), "get_people": Path( "shows/!id/people", CastCrewList, extended=["full"], validators=[ID_VALIDATOR], cache_level="basic", ), "get_ratings": Path("shows/!id/ratings", RatingsSummary, validators=[ID_VALIDATOR]), "get_related": Path( "shows/!id/related", [Show], extended=["full"], pagination=True, validators=[ID_VALIDATOR], cache_level="basic", ), "get_stats": Path("shows/!id/stats", ShowStats, validators=[ID_VALIDATOR]), "get_users_watching": Path("shows/!id/watching", [User], extended=["full"], validators=[ID_VALIDATOR]), "get_next_episode": Path( "shows/!id/next_episode", Union[Episode, Dict[str, Any]], extended=["full"], validators=[ID_VALIDATOR], ), "get_last_episode": Path( "shows/!id/last_episode", Union[Episode, Dict[str, Any]], extended=["full"], validators=[ID_VALIDATOR], ), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for k, r in self.base_paths.items(): self.paths[k] = self._make_path(*r) def _make_path(self, resource_path: str, return_type: Any) -> Path: extra_validators: List[Validator] = [] if "?period" in resource_path: extra_validators.append( PerArgValidator("period", lambda p: p in PERIOD_VALUES)) return Path( self.name + "/" + resource_path, return_type, extended=["full"], filters=COMMON_FILTERS | SHOWS_FILTERS, pagination=True, validators=extra_validators, ) def get_trending(self, **kwargs) -> PaginationIterator[TrendingShow]: return self.run("get_trending", **kwargs) def get_popular(self, **kwargs) -> PaginationIterator[Show]: return self.run("get_popular", **kwargs) def get_most_played(self, *, period: str = "weekly", **kwargs) -> PaginationIterator[ShowWithStats]: return self.run("get_most_played", **kwargs, period=period) def get_most_watched(self, *, period: str = "weekly", **kwargs) -> PaginationIterator[ShowWithStats]: return self.run("get_most_watched", **kwargs, period=period) def get_most_collected(self, *, period: str = "weekly", **kwargs) -> PaginationIterator[ShowWithStats]: return self.run("get_most_collected", **kwargs, period=period) def get_most_anticipated(self, **kwargs) -> PaginationIterator[AnticipatedShow]: return self.run("get_most_anticipated", **kwargs) def get_recently_updated(self, *, start_date: Optional[str] = None, **kwargs) -> PaginationIterator[UpdatedShow]: return self.run("get_recently_updated", **kwargs, start_date=start_date) def get_summary(self, *, show: Union[Show, str, int], **kwargs) -> PaginationIterator[Show]: id = self._generic_get_id(show) return self.run("get_summary", **kwargs, id=id) def get_aliases(self, *, show: Union[Show, str, int], **kwargs) -> List[Alias]: id = self._generic_get_id(show) return self.run("get_aliases", **kwargs, id=id) def get_translations(self, *, show: Union[Show, str, int], language: Optional[str] = None, **kwargs) -> List[ShowTranslation]: extra_kwargs = {"id": self._generic_get_id(show)} if language: extra_kwargs["language"] = language return self.run("get_translations", **kwargs, **extra_kwargs) def get_comments(self, *, show: Union[Show, str, int], sort: str = "newest", **kwargs) -> PaginationIterator[Comment]: id = self._generic_get_id(show) return self.run("get_comments", **kwargs, sort=sort, id=id) def get_lists(self, *, show: Union[Show, str, int], type: str = "personal", sort: str = "popular", **kwargs) -> PaginationIterator[TraktList]: id = self._generic_get_id(show) return self.run("get_lists", **kwargs, type=type, sort=sort, id=id) def get_collection_progress(self, *, show: Union[Show, str, int], hidden: bool = False, specials: bool = False, count_specials: bool = True, **kwargs) -> ShowCollectionProgress: return self.run("get_collection_progress", **kwargs, id=self._generic_get_id(show), hidden=hidden, specials=specials, count_specials=count_specials) def get_watched_progress(self, *, show: Union[Show, str, int], hidden: bool = False, specials: bool = False, count_specials: bool = True, **kwargs) -> ShowCollectionProgress: return self.run("get_watched_progress", **kwargs, id=self._generic_get_id(show), hidden=hidden, specials=specials, count_specials=count_specials) def get_people(self, *, show: Union[Show, str, int], **kwargs) -> CastCrewList: return self.run("get_people", **kwargs, id=self._generic_get_id(show)) def get_ratings(self, *, show: Union[Show, str, int], **kwargs) -> RatingsSummary: return self.run("get_ratings", **kwargs, id=self._generic_get_id(show)) def get_related(self, *, show: Union[Show, str, int], **kwargs) -> PaginationIterator[Show]: return self.run("get_related", **kwargs, id=self._generic_get_id(show)) def get_stats(self, *, show: Union[Show, str, int], **kwargs) -> ShowStats: return self.run("get_stats", **kwargs, id=self._generic_get_id(show)) def get_users_watching(self, *, show: Union[Show, str, int], **kwargs) -> List[User]: return self.run("get_users_watching", **kwargs, id=self._generic_get_id(show)) def get_next_episode(self, *, show: Union[Show, str, int], **kwargs) -> Optional[Episode]: resp = self.run("get_next_episode", **kwargs, id=self._generic_get_id(show), return_extras=True) return None if resp.code == 204 else resp.parsed def get_last_episode(self, *, show: Union[Show, str, int], **kwargs) -> Optional[Episode]: resp = self.run("get_last_episode", **kwargs, id=self._generic_get_id(show), return_extras=True) return None if resp.code == 204 else resp.parsed
class PeopleI(SuiteInterface): name = "people" paths = { "get_person": Path( "people/!id", Person, validators=[PERSON_ID_VALIDATOR], extended=["full"], cache_level="basic", ), "get_movie_credits": Path( "people/!id/movies", MovieCredits, validators=[PERSON_ID_VALIDATOR], extended=["full"], cache_level="basic", ), "get_show_credits": Path( "people/!id/shows", ShowCredits, validators=[PERSON_ID_VALIDATOR], extended=["full"], cache_level="basic", ), "get_lists": Path( "people/!id/lists/?type/?sort", [TraktList], validators=[ PERSON_ID_VALIDATOR, PerArgValidator("type", lambda t: t in LIST_TYPE_VALUES), PerArgValidator("sort", lambda s: s in LIST_SORT_VALUES), ], extended=["full"], cache_level="basic", ), } def get_person(self, person: Union[Person, str, int], **kwargs) -> Person: id = self._get_person_id(person) return self.run("get_person", **kwargs, id=id) def get_movie_credits(self, person: Union[Person, str, int], **kwargs) -> MovieCredits: id = self._get_person_id(person) return self.run("get_movie_credits", **kwargs, id=id) def get_show_credits(self, person: Union[Person, str, int], **kwargs) -> ShowCredits: id = self._get_person_id(person) return self.run("get_show_credits", **kwargs, id=id) def get_lists(self, person: Union[Person, str, int], **kwargs) -> List[TraktList]: id = self._get_person_id(person) return self.run("get_lists", **kwargs, id=id) def _get_person_id(self, p: Union[Person, int, str]) -> str: return str(self._generic_get_id(item=p))
def test_extended(): client = Trakt("", "") p = Path("a", {}, extended=["full"]) assert p.is_valid(client) assert p.is_valid(client, extended="full") assert p.is_valid(client, extended=True) with pytest.raises(ArgumentError): p.is_valid(client, extended="meta") p.is_valid(client, extended=True) _, quargs = p.get_path_and_qargs() assert "extended" in quargs and quargs["extended"] == "full" p = Path("a", {}) p.is_valid(client) _, quargs = p.get_path_and_qargs() assert "extended" not in quargs
class SearchI(SuiteInterface): name = "search" paths = { "text_query": Path( # type: ignore "search/!type", [SearchResult], extended=["full"], filters=ALL_FILTERS, pagination=True, validators=[ PerArgValidator( "type", lambda t: all(x in MEDIA_TYPES for x in t.split(","))), PerArgValidator("query", lambda q: isinstance(q, str) and q), PerArgValidator( "fields", lambda f: all(x in POSSIBLE_FIELDS for x in f.split(","))), ], qargs=["fields"], cache_level="basic", ), "id_lookup": Path( # type: ignore "search/!id_type/!id", [SearchResult], extended=["full"], filters=ALL_FILTERS, pagination=True, validators=[ PerArgValidator("id", lambda t: isinstance(t, (int, str))), PerArgValidator("id_type", lambda it: it in ID_TYPES), PerArgValidator( "type", lambda f: all(x in MEDIA_TYPES for x in f.split(","))), ], qargs=["type"], cache_level="basic", ), } def text_query(self, type: Union[str, List[str]], query: str, fields: Optional[Union[str, List[str]]] = None, **kwargs) -> PaginationIterator[SearchResult]: type = [type] if isinstance(type, str) else type type = ",".join(type) req = {"type": type, "query": query} if fields: fields = [fields] if isinstance(fields, str) else fields req["fields"] = ",".join(fields) return self.run("text_query", **kwargs, **req) def id_lookup(self, id_type: str, id: Union[str, int], type: Optional[Union[str, List[str]]] = None, **kwargs) -> PaginationIterator[SearchResult]: req = {"id_type": id_type, "id": id} if type: type = [type] if isinstance(type, str) else type req["type"] = ",".join(type) return self.run("id_lookup", **kwargs, **req)
def test_filters(): client = Trakt("", "") p = Path("a", {}) with pytest.raises(ArgumentError): p.is_valid(client, genres="genre") p = Path("a", {}, filters={"query", "genres"}) assert p.is_valid(client, query="xyz") with pytest.raises(ArgumentError): p.is_valid(client, query=["xyz", "abc"]) assert p.is_valid(client, genres="genre") assert p.is_valid(client, genres=["abc", "xyz"]) with pytest.raises(ArgumentError): p.is_valid(client, query=[100, "abc"])
class EpisodesI(SuiteInterface): name = "episodes" paths = { "get_episode": Path( "shows/!id/seasons/!season/episodes/!episode", Episode, extended=["full"], validators=[ID_VALIDATOR, SEASON_ID_VALIDATOR, EPISODE_ID_VALIDATOR], ), "get_translations": Path( "shows/!id/seasons/!season/episodes/!episode/translations/?language", [EpisodeTranslation], validators=[ ID_VALIDATOR, SEASON_ID_VALIDATOR, EPISODE_ID_VALIDATOR, PerArgValidator("language", lambda s: isinstance(s, str)), ], ), "get_comments": Path( "shows/!id/seasons/!season/episodes/!episode/comments/?sort", [Comment], validators=[ ID_VALIDATOR, SEASON_ID_VALIDATOR, EPISODE_ID_VALIDATOR, PerArgValidator("sort", lambda s: s in COMMENT_SORT_VALUES), ], pagination=True, ), "get_lists": Path( "shows/!id/seasons/!season/episodes/!episode/lists/?type/?sort", [TraktList], validators=[ ID_VALIDATOR, SEASON_ID_VALIDATOR, EPISODE_ID_VALIDATOR, PerArgValidator("type", lambda t: t in LIST_TYPE_VALUES), PerArgValidator("sort", lambda s: s in LIST_SORT_VALUES), ], pagination=True, ), "get_ratings": Path( "shows/!id/seasons/!season/episodes/!episode/ratings", RatingsSummary, validators=[ID_VALIDATOR, SEASON_ID_VALIDATOR, EPISODE_ID_VALIDATOR], ), "get_stats": Path( "shows/!id/seasons/!season/episodes/!episode/stats", SeasonEpisodeStats, validators=[ID_VALIDATOR, SEASON_ID_VALIDATOR, EPISODE_ID_VALIDATOR], ), "get_users_watching": Path( "shows/!id/seasons/!season/episodes/!episode/watching", [User], extended=["full"], validators=[ID_VALIDATOR, SEASON_ID_VALIDATOR, EPISODE_ID_VALIDATOR], ), } def get_episode( self, *, show: Union[Show, str, int], season: Union[Season, str, int], episode: Union[Episode, int, str], **kwargs ) -> Season: id = self._generic_get_id(show) season = self._generic_get_id(season) episode = self._generic_get_id(episode) return self.run("get_episode", **kwargs, id=id, season=season, episode=episode) def get_comments( self, *, show: Union[Show, str, int], season: Union[Season, str, int], episode: Union[Episode, int, str], sort: str = "newest", **kwargs ) -> PaginationIterator[Comment]: id = self._generic_get_id(show) season = self._generic_get_id(season) episode = self._generic_get_id(episode) return self.run( "get_comments", **kwargs, sort=sort, id=id, season=season, episode=episode ) def get_translations( self, *, show: Union[Show, str, int], season: Union[Season, str, int], episode: Union[Episode, int, str], sort: str = "newest", **kwargs ) -> List[Comment]: id = self._generic_get_id(show) season = self._generic_get_id(season) episode = self._generic_get_id(episode) return self.run( "get_translations", **kwargs, sort=sort, id=id, season=season, episode=episode ) def get_lists( self, *, show: Union[Show, str, int], season: Union[Season, str, int], episode: Union[Episode, int, str], type: str = "personal", sort: str = "popular", **kwargs ) -> PaginationIterator[TraktList]: id = self._generic_get_id(show) season = self._generic_get_id(season) episode = self._generic_get_id(episode) return self.run( "get_lists", **kwargs, type=type, sort=sort, id=id, season=season, episode=episode ) def get_ratings( self, *, show: Union[Show, str, int], season: Union[Season, str, int], episode: Union[Episode, int, str], **kwargs ) -> RatingsSummary: id = self._generic_get_id(show) season = self._generic_get_id(season) episode = self._generic_get_id(episode) return self.run("get_ratings", **kwargs, id=id, season=season, episode=episode) def get_stats( self, *, show: Union[Show, str, int], season: Union[Season, str, int], episode: Union[Episode, int, str], **kwargs ) -> SeasonEpisodeStats: id = self._generic_get_id(show) season = self._generic_get_id(season) episode = self._generic_get_id(episode) return self.run("get_stats", **kwargs, id=id, season=season, episode=episode) def get_users_watching( self, *, show: Union[Show, str, int], season: Union[Season, str, int], episode: Union[Episode, int, str], **kwargs ) -> List[User]: id = self._generic_get_id(show) season = self._generic_get_id(season) episode = self._generic_get_id(episode) return self.run( "get_users_watching", **kwargs, id=id, season=season, episode=episode )