PythonistaGuild / TwitchIO

An Async Bot/API wrapper for Twitch made in Python.
https://twitchio.dev
MIT License
791 stars 163 forks source link

Fix an exception when calling User.fetch_bits_leaderboard method without started_at argument #360

Closed graille closed 1 year ago

graille commented 1 year ago

Pull request summary

When calling "User.fetch_bits_leaderboard", the following error was raised:

Traceback (most recent call last): File "\TwitchIO\test.py", line 60, in asyncio.run(main()) File "\Python39\lib\asyncio\runners.py", line 44, in run return loop.run_until_complete(main) File "\Python39\lib\asyncio\base_events.py", line 647, in run_until_complete return future.result() File "\TwitchIO\test.py", line 32, in main print("therealgraille.fetch_bits_leaderboard:", await therealgraille.fetch_bits_leaderboard(token=TOKEN)) File "\TwitchIO\twitchio\user.py", line 283, in fetch_bits_leaderboard data = await self._http.get_bits_board(token, period, user_id, started_at) File "\TwitchIO\twitchio\http.py", line 321, in get_bits_board route = Route( File "\TwitchIO\twitchio\http.py", line 72, in init self.path = self.path.with_query(query) File "\Python39\lib\site-packages\yarl_url.py", line 977, in with_query new_query = self._get_str_query(*args, **kwargs) File "\Python39\lib\site-packages\yarl_url.py", line 951, in _get_str_query query = "&".join( File "\Python39\lib\site-packages\yarl_url.py", line 952, in quoter(k) + "=" + quoter(self._query_var(v)) for k, v in query File "\Python39\lib\site-packages\yarl_url.py", line 916, in _query_var raise TypeError( TypeError: Invalid variable type: value should be str, int or float, got None of type <class 'NoneType'>

This was because the query contained some None values (started_atparameter wasn't specified) :

async def get_bits_board(
    self, token: str, period: str = "all", user_id: str = None, started_at: datetime.datetime = None
):
    assert period in {"all", "day", "week", "month", "year"}
    route = Route(
        "GET",
        "bits/leaderboard",
        "",
        query=[
            ("period", period),
            ("started_at", started_at.isoformat() if started_at else None), # <--- HERE
            ("user_id", user_id),
        ],
        token=token,
    )
    return await self.request(route, full_body=True, paginate=False)

the same thing happen in get_clips(), but get_clips() add a step to eliminate the None values, which get_bits_board() doesn't :

query = [x for x in q if x[1] is not None]

To generalize this, I removed this line from get_clips() and add a check of entire query in Route class. This fix the issue.

After this fix, another Exception was raised:

Traceback (most recent call last): File "\TwitchIO\test.py", line 60, in asyncio.run(main()) File "\Python39\lib\asyncio\runners.py", line 44, in run return loop.run_until_complete(main) File "\Python39\lib\asyncio\base_events.py", line 647, in run_until_complete return future.result() File "\TwitchIO\test.py", line 32, in main print("therealgraille.fetch_bits_leaderboard:", await therealgraille.fetch_bits_leaderboard(token=TOKEN)) File "\TwitchIO\twitchio\user.py", line 284, in fetch_bits_leaderboard return BitsLeaderboard(self._http, data) File "\TwitchIO\twitchio\models.py", line 100, in init self.started_at = datetime.datetime.fromisoformat(data["date_range"]["started_at"]) ValueError: Invalid isoformat string: ''

This error appears when the result of the request is empty (especialy the values of started_at and ended_at in the server response). I added a condition to check when it happens.

Checklist

chillymosh commented 1 year ago

Fixed in ca716d8 as there were a few other things to catch.