Drop support for Python 3.4 and make minimum version 3.5.2.

This commit is contained in:
Rapptz
2018-06-10 18:09:14 -04:00
parent 7eb918b19e
commit f25091efe1
35 changed files with 626 additions and 1069 deletions

View File

@ -48,7 +48,6 @@ import sys, re
import signal
from collections import namedtuple
PY35 = sys.version_info >= (3, 5)
log = logging.getLogger(__name__)
AppInfo = namedtuple('AppInfo', 'id name description icon owner')
@ -139,12 +138,10 @@ class Client:
# internals
@asyncio.coroutine
def _syncer(self, guilds):
yield from self.ws.request_sync(guilds)
async def _syncer(self, guilds):
await self.ws.request_sync(guilds)
@asyncio.coroutine
def _chunker(self, guild):
async def _chunker(self, guild):
try:
guild_id = guild.id
except AttributeError:
@ -159,7 +156,7 @@ class Client:
}
}
yield from self.ws.send_as_json(payload)
await self.ws.send_as_json(payload)
def handle_ready(self):
self._ready.set()
@ -218,15 +215,14 @@ class Client:
""":obj:`bool`: Specifies if the client's internal cache is ready for use."""
return self._ready.is_set()
@asyncio.coroutine
def _run_event(self, coro, event_name, *args, **kwargs):
async def _run_event(self, coro, event_name, *args, **kwargs):
try:
yield from coro(*args, **kwargs)
await coro(*args, **kwargs)
except asyncio.CancelledError:
pass
except Exception:
try:
yield from self.on_error(event_name, *args, **kwargs)
await self.on_error(event_name, *args, **kwargs)
except asyncio.CancelledError:
pass
@ -276,10 +272,9 @@ class Client:
except AttributeError:
pass
else:
compat.create_task(self._run_event(coro, method, *args, **kwargs), loop=self.loop)
asyncio.ensure_future(self._run_event(coro, method, *args, **kwargs), loop=self.loop)
@asyncio.coroutine
def on_error(self, event_method, *args, **kwargs):
async def on_error(self, event_method, *args, **kwargs):
"""|coro|
The default error handler provided by the client.
@ -291,8 +286,7 @@ class Client:
print('Ignoring exception in {}'.format(event_method), file=sys.stderr)
traceback.print_exc()
@asyncio.coroutine
def request_offline_members(self, *guilds):
async def request_offline_members(self, *guilds):
"""|coro|
Requests previously offline members from the guild to be filled up
@ -318,12 +312,11 @@ class Client:
if any(not g.large or g.unavailable for g in guilds):
raise InvalidArgument('An unavailable or non-large guild was passed.')
yield from self._connection.request_offline_members(guilds)
await self._connection.request_offline_members(guilds)
# login state management
@asyncio.coroutine
def login(self, token, *, bot=True):
async def login(self, token, *, bot=True):
"""|coro|
Logs in the client with the specified credentials.
@ -350,34 +343,31 @@ class Client:
"""
log.info('logging in using static token')
yield from self.http.static_login(token, bot=bot)
await self.http.static_login(token, bot=bot)
self._connection.is_bot = bot
@asyncio.coroutine
def logout(self):
async def logout(self):
"""|coro|
Logs out of Discord and closes all connections.
"""
yield from self.close()
await self.close()
@asyncio.coroutine
def _connect(self):
async def _connect(self):
coro = DiscordWebSocket.from_client(self, shard_id=self.shard_id)
self.ws = yield from asyncio.wait_for(coro, timeout=180.0, loop=self.loop)
self.ws = await asyncio.wait_for(coro, timeout=180.0, loop=self.loop)
while True:
try:
yield from self.ws.poll_event()
await self.ws.poll_event()
except ResumeWebSocket as e:
log.info('Got a request to RESUME the websocket.')
coro = DiscordWebSocket.from_client(self, shard_id=self.shard_id,
session=self.ws.session_id,
sequence=self.ws.sequence,
resume=True)
self.ws = yield from asyncio.wait_for(coro, timeout=180.0, loop=self.loop)
self.ws = await asyncio.wait_for(coro, timeout=180.0, loop=self.loop)
@asyncio.coroutine
def connect(self, *, reconnect=True):
async def connect(self, *, reconnect=True):
"""|coro|
Creates a websocket connection and lets the websocket listen
@ -405,7 +395,7 @@ class Client:
backoff = ExponentialBackoff()
while not self.is_closed():
try:
yield from self._connect()
await self._connect()
except (OSError,
HTTPException,
GatewayNotFound,
@ -416,7 +406,7 @@ class Client:
websockets.WebSocketProtocolError) as e:
if not reconnect:
yield from self.close()
await self.close()
if isinstance(e, ConnectionClosed) and e.code == 1000:
# clean close, don't re-raise this
return
@ -431,15 +421,14 @@ class Client:
# regardless and rely on is_closed instead
if isinstance(e, ConnectionClosed):
if e.code != 1000:
yield from self.close()
await self.close()
raise
retry = backoff.delay()
log.exception("Attempting a reconnect in %.2fs", retry)
yield from asyncio.sleep(retry, loop=self.loop)
await asyncio.sleep(retry, loop=self.loop)
@asyncio.coroutine
def close(self):
async def close(self):
"""|coro|
Closes the connection to discord.
@ -451,16 +440,16 @@ class Client:
for voice in self.voice_clients:
try:
yield from voice.disconnect()
await voice.disconnect()
except:
# if an error happens during disconnects, disregard it.
pass
if self.ws is not None and self.ws.open:
yield from self.ws.close()
await self.ws.close()
yield from self.http.close()
await self.http.close()
self._ready.clear()
def clear(self):
@ -475,8 +464,7 @@ class Client:
self._connection.clear()
self.http.recreate()
@asyncio.coroutine
def start(self, *args, **kwargs):
async def start(self, *args, **kwargs):
"""|coro|
A shorthand coroutine for :meth:`login` + :meth:`connect`.
@ -484,8 +472,8 @@ class Client:
bot = kwargs.pop('bot', True)
reconnect = kwargs.pop('reconnect', True)
yield from self.login(*args, bot=bot)
yield from self.connect(reconnect=reconnect)
await self.login(*args, bot=bot)
await self.connect(reconnect=reconnect)
def _do_cleanup(self):
log.info('Cleaning up event loop.')
@ -493,7 +481,7 @@ class Client:
if loop.is_closed():
return # we're already cleaning up
task = compat.create_task(self.close(), loop=loop)
task = asyncio.ensure_future(self.close(), loop=loop)
def _silence_gathered(fut):
try:
@ -558,7 +546,7 @@ class Client:
loop.add_signal_handler(signal.SIGINT, self._do_cleanup)
loop.add_signal_handler(signal.SIGTERM, self._do_cleanup)
task = compat.create_task(self.start(*args, **kwargs), loop=loop)
task = asyncio.ensure_future(self.start(*args, **kwargs), loop=loop)
def stop_loop_on_finish(fut):
loop.stop()
@ -661,13 +649,12 @@ class Client:
# listeners/waiters
@asyncio.coroutine
def wait_until_ready(self):
async def wait_until_ready(self):
"""|coro|
Waits until the client's internal cache is all ready.
"""
yield from self._ready.wait()
await self._ready.wait()
def wait_for(self, event, *, check=None, timeout=None):
"""|coro|
@ -751,7 +738,7 @@ class Client:
:ref:`event reference <discord-api-events>`.
"""
future = compat.create_future(self.loop)
future = self.loop.create_future()
if check is None:
def _check(*args):
return True
@ -782,8 +769,7 @@ class Client:
Using the basic :meth:`event` decorator: ::
@client.event
@asyncio.coroutine
def on_ready():
async def on_ready():
print('Ready!')
Saving characters by using the :meth:`async_event` decorator: ::
@ -808,8 +794,7 @@ class Client:
return self.event(coro)
@asyncio.coroutine
def change_presence(self, *, activity=None, status=None, afk=False):
async def change_presence(self, *, activity=None, status=None, afk=False):
"""|coro|
Changes the client's presence.
@ -851,7 +836,7 @@ class Client:
status_enum = status
status = str(status)
yield from self.ws.change_presence(activity=activity, status=status, afk=afk)
await self.ws.change_presence(activity=activity, status=status, afk=afk)
for guild in self._connection.guilds:
me = guild.me
@ -863,8 +848,7 @@ class Client:
# Guild stuff
@asyncio.coroutine
def create_guild(self, name, region=None, icon=None):
async def create_guild(self, name, region=None, icon=None):
"""|coro|
Creates a :class:`Guild`.
@ -903,13 +887,12 @@ class Client:
else:
region = region.value
data = yield from self.http.create_guild(name, region, icon)
data = await self.http.create_guild(name, region, icon)
return Guild(data=data, state=self._connection)
# Invite management
@asyncio.coroutine
def get_invite(self, url):
async def get_invite(self, url):
"""|coro|
Gets an :class:`Invite` from a discord.gg URL or ID.
@ -939,11 +922,10 @@ class Client:
"""
invite_id = self._resolve_invite(url)
data = yield from self.http.get_invite(invite_id)
data = await self.http.get_invite(invite_id)
return Invite.from_incomplete(state=self._connection, data=data)
@asyncio.coroutine
def delete_invite(self, invite):
async def delete_invite(self, invite):
"""|coro|
Revokes an :class:`Invite`, URL, or ID to an invite.
@ -967,12 +949,11 @@ class Client:
"""
invite_id = self._resolve_invite(invite)
yield from self.http.delete_invite(invite_id)
await self.http.delete_invite(invite_id)
# Miscellaneous stuff
@asyncio.coroutine
def application_info(self):
async def application_info(self):
"""|coro|
Retrieve's the bot's application information.
@ -987,13 +968,12 @@ class Client:
HTTPException
Retrieving the information failed somehow.
"""
data = yield from self.http.application_info()
data = await self.http.application_info()
return AppInfo(id=int(data['id']), name=data['name'],
description=data['description'], icon=data['icon'],
owner=User(state=self._connection, data=data['owner']))
@asyncio.coroutine
def get_user_info(self, user_id):
async def get_user_info(self, user_id):
"""|coro|
Retrieves a :class:`User` based on their ID. This can only
@ -1018,11 +998,10 @@ class Client:
HTTPException
Fetching the user failed.
"""
data = yield from self.http.get_user_info(user_id)
data = await self.http.get_user_info(user_id)
return User(state=self._connection, data=data)
@asyncio.coroutine
def get_user_profile(self, user_id):
async def get_user_profile(self, user_id):
"""|coro|
Gets an arbitrary user's profile. This can only be used by non-bot accounts.
@ -1046,7 +1025,7 @@ class Client:
"""
state = self._connection
data = yield from self.http.get_user_profile(user_id)
data = await self.http.get_user_profile(user_id)
def transform(d):
return state._get_guild(int(d['id']))
@ -1060,8 +1039,7 @@ class Client:
user=User(data=user, state=state),
connected_accounts=data['connected_accounts'])
@asyncio.coroutine
def get_webhook_info(self, webhook_id):
async def get_webhook_info(self, webhook_id):
"""|coro|
Retrieves a :class:`Webhook` with the specified ID.
@ -1080,5 +1058,5 @@ class Client:
:class:`Webhook`
The webhook you requested.
"""
data = yield from self.http.get_webhook(webhook_id)
data = await self.http.get_webhook(webhook_id)
return Webhook.from_state(data, state=self._connection)