Browse Source

Fix type errors and potentially unbound code in http.py

pull/7167/head
Rapptz 4 years ago
parent
commit
7d9074db8a
  1. 63
      discord/http.py

63
discord/http.py

@ -42,6 +42,7 @@ from typing import (
Tuple, Tuple,
Type, Type,
TypeVar, TypeVar,
Union,
) )
from urllib.parse import quote as _uriquote from urllib.parse import quote as _uriquote
import weakref import weakref
@ -51,6 +52,7 @@ import aiohttp
from .errors import HTTPException, Forbidden, NotFound, LoginFailure, DiscordServerError, GatewayNotFound from .errors import HTTPException, Forbidden, NotFound, LoginFailure, DiscordServerError, GatewayNotFound
from .gateway import DiscordClientWebSocketResponse from .gateway import DiscordClientWebSocketResponse
from . import __version__, utils from . import __version__, utils
from .utils import MISSING
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -93,7 +95,7 @@ if TYPE_CHECKING:
Response = Coroutine[Any, Any, T] Response = Coroutine[Any, Any, T]
async def json_or_text(response: aiohttp.ClientResponse) -> Any: async def json_or_text(response: aiohttp.ClientResponse) -> Union[Dict[str, Any], str]:
text = await response.text(encoding='utf-8') text = await response.text(encoding='utf-8')
try: try:
if response.headers['content-type'] == 'application/json': if response.headers['content-type'] == 'application/json':
@ -170,7 +172,7 @@ class HTTPClient:
) -> None: ) -> None:
self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() if loop is None else loop self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() if loop is None else loop
self.connector = connector self.connector = connector
self.__session: Optional[aiohttp.ClientSession] = None # filled in static_login self.__session: aiohttp.ClientSession = MISSING # filled in static_login
self._locks: weakref.WeakValueDictionary = weakref.WeakValueDictionary() self._locks: weakref.WeakValueDictionary = weakref.WeakValueDictionary()
self._global_over: asyncio.Event = asyncio.Event() self._global_over: asyncio.Event = asyncio.Event()
self._global_over.set() self._global_over.set()
@ -223,7 +225,7 @@ class HTTPClient:
self._locks[bucket] = lock self._locks[bucket] = lock
# header creation # header creation
headers = { headers: Dict[str, str] = {
'User-Agent': self.user_agent, 'User-Agent': self.user_agent,
} }
@ -254,6 +256,8 @@ class HTTPClient:
# wait until the global lock is complete # wait until the global lock is complete
await self._global_over.wait() await self._global_over.wait()
response: Optional[aiohttp.ClientResponse] = None
data: Optional[Union[Dict[str, Any], str]] = None
await lock.acquire() await lock.acquire()
with MaybeUnlock(lock) as maybe_lock: with MaybeUnlock(lock) as maybe_lock:
for tries in range(5): for tries in range(5):
@ -268,36 +272,36 @@ class HTTPClient:
kwargs['data'] = form_data kwargs['data'] = form_data
try: try:
async with self.__session.request(method, url, **kwargs) as r: async with self.__session.request(method, url, **kwargs) as response:
log.debug('%s %s with %s has returned %s', method, url, kwargs.get('data'), r.status) log.debug('%s %s with %s has returned %s', method, url, kwargs.get('data'), response.status)
# even errors have text involved in them so this is safe to call # even errors have text involved in them so this is safe to call
data = await json_or_text(r) data = await json_or_text(response)
# check if we have rate limit header information # check if we have rate limit header information
remaining = r.headers.get('X-Ratelimit-Remaining') remaining = response.headers.get('X-Ratelimit-Remaining')
if remaining == '0' and r.status != 429: if remaining == '0' and response.status != 429:
# we've depleted our current bucket # we've depleted our current bucket
delta = utils._parse_ratelimit_header(r, use_clock=self.use_clock) delta = utils._parse_ratelimit_header(response, use_clock=self.use_clock)
log.debug('A rate limit bucket has been exhausted (bucket: %s, retry: %s).', bucket, delta) log.debug('A rate limit bucket has been exhausted (bucket: %s, retry: %s).', bucket, delta)
maybe_lock.defer() maybe_lock.defer()
self.loop.call_later(delta, lock.release) self.loop.call_later(delta, lock.release)
# the request was successful so just return the text/json # the request was successful so just return the text/json
if 300 > r.status >= 200: if 300 > response.status >= 200:
log.debug('%s %s has received %s', method, url, data) log.debug('%s %s has received %s', method, url, data)
return data return data
# we are being rate limited # we are being rate limited
if r.status == 429: if response.status == 429:
if not r.headers.get('Via'): if not response.headers.get('Via') or isinstance(data, str):
# Banned by Cloudflare more than likely. # Banned by Cloudflare more than likely.
raise HTTPException(r, data) raise HTTPException(response, data)
fmt = 'We are being rate limited. Retrying in %.2f seconds. Handled under the bucket "%s"' fmt = 'We are being rate limited. Retrying in %.2f seconds. Handled under the bucket "%s"'
# sleep a bit # sleep a bit
retry_after: float = data['retry_after'] # type: ignore retry_after: float = data['retry_after']
log.warning(fmt, retry_after, bucket) log.warning(fmt, retry_after, bucket)
# check if it's a global rate limit # check if it's a global rate limit
@ -318,19 +322,19 @@ class HTTPClient:
continue continue
# we've received a 500 or 502, unconditional retry # we've received a 500 or 502, unconditional retry
if r.status in {500, 502}: if response.status in {500, 502}:
await asyncio.sleep(1 + tries * 2) await asyncio.sleep(1 + tries * 2)
continue continue
# the usual error cases # the usual error cases
if r.status == 403: if response.status == 403:
raise Forbidden(r, data) raise Forbidden(response, data)
elif r.status == 404: elif response.status == 404:
raise NotFound(r, data) raise NotFound(response, data)
elif r.status == 503: elif response.status == 503:
raise DiscordServerError(r, data) raise DiscordServerError(response, data)
else: else:
raise HTTPException(r, data) raise HTTPException(response, data)
# This is handling exceptions from the request # This is handling exceptions from the request
except OSError as e: except OSError as e:
@ -340,11 +344,14 @@ class HTTPClient:
continue continue
raise raise
# We've run out of retries, raise. if response is not None:
if r.status >= 500: # We've run out of retries, raise.
raise DiscordServerError(r, data) if response.status >= 500:
raise DiscordServerError(response, data)
raise HTTPException(r, data) raise HTTPException(response, data)
raise RuntimeError('Unreachable code in HTTP handling')
async def get_from_cdn(self, url: str) -> bytes: async def get_from_cdn(self, url: str) -> bytes:
async with self.__session.get(url) as resp: async with self.__session.get(url) as resp:
@ -375,7 +382,7 @@ class HTTPClient:
data = await self.request(Route('GET', '/users/@me')) data = await self.request(Route('GET', '/users/@me'))
except HTTPException as exc: except HTTPException as exc:
self.token = old_token self.token = old_token
if exc.response.status == 401: if exc.status == 401:
raise LoginFailure('Improper token has been passed.') from exc raise LoginFailure('Improper token has been passed.') from exc
raise raise
@ -597,7 +604,7 @@ class HTTPClient:
emoji=emoji, emoji=emoji,
) )
params = { params: Dict[str, Any] = {
'limit': limit, 'limit': limit,
} }
if after: if after:

Loading…
Cancel
Save