[V3] Update code standards (black code format pass) (#1650)

* ran black: code formatter against `redbot/` with `-l 99`

* badge
This commit is contained in:
Michael H
2018-05-14 15:33:24 -04:00
committed by Will
parent e7476edd68
commit b88b5a2601
90 changed files with 3629 additions and 3223 deletions

View File

@@ -1,4 +1,4 @@
__all__ = ['TYPE_CHECKING', 'NewType', 'safe_delete']
__all__ = ["TYPE_CHECKING", "NewType", "safe_delete"]
from pathlib import Path
import os
@@ -12,6 +12,7 @@ except ImportError:
try:
from typing import NewType
except ImportError:
def NewType(name, tp):
return type(name, (tp,), {})

View File

@@ -3,7 +3,7 @@ from typing import Tuple, List
from collections import namedtuple
Interval = Tuple[timedelta, int]
AntiSpamInterval = namedtuple('AntiSpamInterval', ['period', 'frequency'])
AntiSpamInterval = namedtuple("AntiSpamInterval", ["period", "frequency"])
class AntiSpam:
@@ -26,21 +26,18 @@ class AntiSpam:
(timedelta(seconds=5), 3),
(timedelta(minutes=1), 5),
(timedelta(hours=1), 10),
(timedelta(days=1), 24)
(timedelta(days=1), 24),
]
def __init__(self, intervals: List[Interval]):
self.__event_timestamps = []
_itvs = intervals if intervals else self.default_intervals
self.__intervals = [
AntiSpamInterval(*x) for x in _itvs
]
self.__intervals = [AntiSpamInterval(*x) for x in _itvs]
self.__discard_after = max([x.period for x in self.__intervals])
def __interval_check(self, interval: AntiSpamInterval):
return len(
[t for t in self.__event_timestamps
if (t + interval.period) > datetime.utcnow()]
[t for t in self.__event_timestamps if (t + interval.period) > datetime.utcnow()]
) >= interval.frequency
@property
@@ -57,6 +54,5 @@ class AntiSpam:
"""
self.__event_timestamps.append(datetime.utcnow())
self.__event_timestamps = [
t for t in self.__event_timestamps
if t + self.__discard_after > datetime.utcnow()
t for t in self.__event_timestamps if t + self.__discard_after > datetime.utcnow()
]

View File

@@ -1,6 +1,7 @@
import itertools
from typing import Sequence, Iterator
def error(text: str) -> str:
"""Get text prefixed with an error emoji.
@@ -66,7 +67,7 @@ def bold(text: str) -> str:
return "**{}**".format(text)
def box(text: str, lang: str="") -> str:
def box(text: str, lang: str = "") -> str:
"""Get the given text in a code block.
Parameters
@@ -120,7 +121,7 @@ def italics(text: str) -> str:
return "*{}*".format(text)
def bordered(*columns: Sequence[str], ascii_border: bool=False) -> str:
def bordered(*columns: Sequence[str], ascii_border: bool = False) -> str:
"""Get two blocks of text in a borders.
Note
@@ -141,18 +142,18 @@ def bordered(*columns: Sequence[str], ascii_border: bool=False) -> str:
"""
borders = {
'TL': '-' if ascii_border else '', # Top-left
'TR': '-' if ascii_border else '', # Top-right
'BL': '-' if ascii_border else '', # Bottom-left
'BR': '-' if ascii_border else '', # Bottom-right
'HZ': '-' if ascii_border else '', # Horizontal
'VT': '|' if ascii_border else '', # Vertical
"TL": "-" if ascii_border else "", # Top-left
"TR": "-" if ascii_border else "", # Top-right
"BL": "-" if ascii_border else "", # Bottom-left
"BR": "-" if ascii_border else "", # Bottom-right
"HZ": "-" if ascii_border else "", # Horizontal
"VT": "|" if ascii_border else "", # Vertical
}
sep = ' ' * 4 # Separator between boxes
sep = " " * 4 # Separator between boxes
widths = tuple(max(len(row) for row in column) + 9 for column in columns) # width of each col
colsdone = [False] * len(columns) # whether or not each column is done
lines = [sep.join('{TL}' + '{HZ}'*width + '{TR}' for width in widths)]
lines = [sep.join("{TL}" + "{HZ}" * width + "{TR}" for width in widths)]
for line in itertools.zip_longest(*columns):
row = []
@@ -162,36 +163,38 @@ def bordered(*columns: Sequence[str], ascii_border: bool=False) -> str:
if column is None:
if not done:
# bottom border of column
column = '{HZ}' * width
row.append('{BL}' + column + '{BR}')
column = "{HZ}" * width
row.append("{BL}" + column + "{BR}")
colsdone[colidx] = True # mark column as done
else:
# leave empty
row.append(' ' * (width + 2))
row.append(" " * (width + 2))
else:
column += ' ' * (width - len(column)) # append padded spaces
row.append('{VT}' + column + '{VT}')
column += " " * (width - len(column)) # append padded spaces
row.append("{VT}" + column + "{VT}")
lines.append(sep.join(row))
final_row = []
for width, done in zip(widths, colsdone):
if not done:
final_row.append('{BL}' + '{HZ}' * width + '{BR}')
final_row.append("{BL}" + "{HZ}" * width + "{BR}")
else:
final_row.append(' ' * (width + 2))
final_row.append(" " * (width + 2))
lines.append(sep.join(final_row))
return "\n".join(lines).format(**borders)
def pagify(text: str,
delims: Sequence[str]=["\n"],
*,
priority: bool=False,
escape_mass_mentions: bool=True,
shorten_by: int=8,
page_length: int=2000) -> Iterator[str]:
def pagify(
text: str,
delims: Sequence[str] = ["\n"],
*,
priority: bool = False,
escape_mass_mentions: bool = True,
shorten_by: int = 8,
page_length: int = 2000
) -> Iterator[str]:
"""Generate multiple pages from the given text.
Note
@@ -232,10 +235,10 @@ def pagify(text: str,
while len(in_text) > page_length:
this_page_len = page_length
if escape_mass_mentions:
this_page_len -= (in_text.count("@here", 0, page_length) +
in_text.count("@everyone", 0, page_length))
closest_delim = (in_text.rfind(d, 1, this_page_len)
for d in delims)
this_page_len -= (
in_text.count("@here", 0, page_length) + in_text.count("@everyone", 0, page_length)
)
closest_delim = (in_text.rfind(d, 1, this_page_len) for d in delims)
if priority:
closest_delim = next((x for x in closest_delim if x > 0), -1)
else:
@@ -290,8 +293,7 @@ def underline(text: str) -> str:
return "__{}__".format(text)
def escape(text: str, *, mass_mentions: bool=False,
formatting: bool=False) -> str:
def escape(text: str, *, mass_mentions: bool = False, formatting: bool = False) -> str:
"""Get text with all mass mentions or markdown escaped.
Parameters
@@ -313,8 +315,7 @@ def escape(text: str, *, mass_mentions: bool=False,
text = text.replace("@everyone", "@\u200beveryone")
text = text.replace("@here", "@\u200bhere")
if formatting:
text = (text.replace("`", "\\`")
.replace("*", "\\*")
.replace("_", "\\_")
.replace("~", "\\~"))
text = (
text.replace("`", "\\`").replace("*", "\\*").replace("_", "\\_").replace("~", "\\~")
)
return text

View File

@@ -28,7 +28,7 @@ class DataConverter:
The file isn't valid JSON
"""
try:
with file_path.open(mode='r', encoding='utf-8') as f:
with file_path.open(mode="r", encoding="utf-8") as f:
data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
raise

View File

@@ -10,10 +10,14 @@ import discord
from redbot.core import commands
async def menu(ctx: commands.Context, pages: list,
controls: dict,
message: discord.Message=None, page: int=0,
timeout: float=30.0):
async def menu(
ctx: commands.Context,
pages: list,
controls: dict,
message: discord.Message = None,
page: int = 0,
timeout: float = 30.0,
):
"""
An emoji-based menu
@@ -48,8 +52,10 @@ async def menu(ctx: commands.Context, pages: list,
RuntimeError
If either of the notes above are violated
"""
if not all(isinstance(x, discord.Embed) for x in pages) and\
not all(isinstance(x, str) for x in pages):
if (
not all(isinstance(x, discord.Embed) for x in pages)
and not all(isinstance(x, str) for x in pages)
):
raise RuntimeError("All pages must be of the same type")
for key, value in controls.items():
if not asyncio.iscoroutinefunction(value):
@@ -70,15 +76,10 @@ async def menu(ctx: commands.Context, pages: list,
await message.edit(content=current_page)
def react_check(r, u):
return u == ctx.author and r.message.id == message.id and \
str(r.emoji) in controls.keys()
return u == ctx.author and r.message.id == message.id and str(r.emoji) in controls.keys()
try:
react, user = await ctx.bot.wait_for(
"reaction_add",
check=react_check,
timeout=timeout
)
react, user = await ctx.bot.wait_for("reaction_add", check=react_check, timeout=timeout)
except asyncio.TimeoutError:
try:
await message.clear_reactions()
@@ -87,14 +88,18 @@ async def menu(ctx: commands.Context, pages: list,
await message.remove_reaction(key, ctx.bot.user)
return None
return await controls[react.emoji](ctx, pages, controls,
message, page,
timeout, react.emoji)
return await controls[react.emoji](ctx, pages, controls, message, page, timeout, react.emoji)
async def next_page(ctx: commands.Context, pages: list,
controls: dict, message: discord.Message, page: int,
timeout: float, emoji: str):
async def next_page(
ctx: commands.Context,
pages: list,
controls: dict,
message: discord.Message,
page: int,
timeout: float,
emoji: str,
):
perms = message.channel.permissions_for(ctx.guild.me)
if perms.manage_messages: # Can manage messages, so remove react
try:
@@ -105,13 +110,18 @@ async def next_page(ctx: commands.Context, pages: list,
page = 0 # Loop around to the first item
else:
page = page + 1
return await menu(ctx, pages, controls, message=message,
page=page, timeout=timeout)
return await menu(ctx, pages, controls, message=message, page=page, timeout=timeout)
async def prev_page(ctx: commands.Context, pages: list,
controls: dict, message: discord.Message, page: int,
timeout: float, emoji: str):
async def prev_page(
ctx: commands.Context,
pages: list,
controls: dict,
message: discord.Message,
page: int,
timeout: float,
emoji: str,
):
perms = message.channel.permissions_for(ctx.guild.me)
if perms.manage_messages: # Can manage messages, so remove react
try:
@@ -122,20 +132,21 @@ async def prev_page(ctx: commands.Context, pages: list,
next_page = len(pages) - 1 # Loop around to the last item
else:
next_page = page - 1
return await menu(ctx, pages, controls, message=message,
page=next_page, timeout=timeout)
return await menu(ctx, pages, controls, message=message, page=next_page, timeout=timeout)
async def close_menu(ctx: commands.Context, pages: list,
controls: dict, message: discord.Message, page: int,
timeout: float, emoji: str):
async def close_menu(
ctx: commands.Context,
pages: list,
controls: dict,
message: discord.Message,
page: int,
timeout: float,
emoji: str,
):
if message:
await message.delete()
return None
DEFAULT_CONTROLS = {
"": prev_page,
"": close_menu,
"": next_page
}
DEFAULT_CONTROLS = {"": prev_page, "": close_menu, "": next_page}

View File

@@ -8,8 +8,7 @@ from redbot.core import Config
from redbot.core.bot import Red
async def mass_purge(messages: List[discord.Message],
channel: discord.TextChannel):
async def mass_purge(messages: List[discord.Message], channel: discord.TextChannel):
"""Bulk delete messages from a channel.
If more than 100 messages are supplied, the bot will delete 100 messages at
@@ -80,24 +79,23 @@ def get_audit_reason(author: discord.Member, reason: str = None):
The formatted audit log reason.
"""
return \
"Action requested by {} (ID {}). Reason: {}".format(author, author.id, reason) if reason else \
"Action requested by {} (ID {}).".format(author, author.id)
return "Action requested by {} (ID {}). Reason: {}".format(
author, author.id, reason
) if reason else "Action requested by {} (ID {}).".format(
author, author.id
)
async def is_allowed_by_hierarchy(bot: Red,
settings: Config,
guild: discord.Guild,
mod: discord.Member,
user: discord.Member):
async def is_allowed_by_hierarchy(
bot: Red, settings: Config, guild: discord.Guild, mod: discord.Member, user: discord.Member
):
if not await settings.guild(guild).respect_hierarchy():
return True
is_special = mod == guild.owner or await bot.is_owner(mod)
return mod.top_role.position > user.top_role.position or is_special
async def is_mod_or_superior(
bot: Red, obj: Union[discord.Message, discord.Member, discord.Role]):
async def is_mod_or_superior(bot: Red, obj: Union[discord.Message, discord.Member, discord.Role]):
"""Check if an object has mod or superior permissions.
If a message is passed, its author's permissions are checked. If a role is
@@ -129,7 +127,7 @@ async def is_mod_or_superior(
elif isinstance(obj, discord.Role):
pass
else:
raise TypeError('Only messages, members or roles may be passed')
raise TypeError("Only messages, members or roles may be passed")
server = obj.guild
admin_role_id = await bot.db.guild(server).admin_role()
@@ -168,26 +166,27 @@ def strfdelta(delta: timedelta):
"""
s = []
if delta.days:
ds = '%i day' % delta.days
ds = "%i day" % delta.days
if delta.days > 1:
ds += 's'
ds += "s"
s.append(ds)
hrs, rem = divmod(delta.seconds, 60*60)
hrs, rem = divmod(delta.seconds, 60 * 60)
if hrs:
hs = '%i hr' % hrs
hs = "%i hr" % hrs
if hrs > 1:
hs += 's'
hs += "s"
s.append(hs)
mins, secs = divmod(rem, 60)
if mins:
s.append('%i min' % mins)
s.append("%i min" % mins)
if secs:
s.append('%i sec' % secs)
return ' '.join(s)
s.append("%i sec" % secs)
return " ".join(s)
async def is_admin_or_superior(
bot: Red, obj: Union[discord.Message, discord.Member, discord.Role]):
bot: Red, obj: Union[discord.Message, discord.Member, discord.Role]
):
"""Same as `is_mod_or_superior` except for admin permissions.
If a message is passed, its author's permissions are checked. If a role is
@@ -219,7 +218,7 @@ async def is_admin_or_superior(
elif isinstance(obj, discord.Role):
pass
else:
raise TypeError('Only messages, members or roles may be passed')
raise TypeError("Only messages, members or roles may be passed")
server = obj.guild
admin_role_id = await bot.db.guild(server).admin_role()

View File

@@ -16,10 +16,7 @@ class TunnelMeta(type):
"""
def __call__(cls, *args, **kwargs):
lockout_tuple = (
(kwargs.get('sender'), kwargs.get('origin')),
kwargs.get('recipient')
)
lockout_tuple = ((kwargs.get("sender"), kwargs.get("origin")), kwargs.get("recipient"))
if lockout_tuple in _instances:
return _instances[lockout_tuple]
@@ -30,13 +27,8 @@ class TunnelMeta(type):
while True:
try:
if not (
any(
lockout_tuple[0] == x[0]
for x in _instances.keys()
) or any(
lockout_tuple[1] == x[1]
for x in _instances.keys()
)
any(lockout_tuple[0] == x[0] for x in _instances.keys())
or any(lockout_tuple[1] == x[1] for x in _instances.keys())
):
# if this isn't temporarily stored, the weakref dict
# will discard this before the return statement,
@@ -70,10 +62,9 @@ class Tunnel(metaclass=TunnelMeta):
The user on the other end of the tunnel
"""
def __init__(self, *,
sender: discord.Member,
origin: discord.TextChannel,
recipient: discord.User):
def __init__(
self, *, sender: discord.Member, origin: discord.TextChannel, recipient: discord.User
):
self.sender = sender
self.origin = origin
self.recipient = recipient
@@ -81,11 +72,8 @@ class Tunnel(metaclass=TunnelMeta):
async def react_close(self, *, uid: int, message: str):
send_to = self.origin if uid == self.sender.id else self.sender
closer = next(filter(
lambda x: x.id == uid, (self.sender, self.recipient)), None)
await send_to.send(
message.format(closer=closer)
)
closer = next(filter(lambda x: x.id == uid, (self.sender, self.recipient)), None)
await send_to.send(message.format(closer=closer))
@property
def members(self):
@@ -97,8 +85,8 @@ class Tunnel(metaclass=TunnelMeta):
@staticmethod
async def message_forwarder(
*, destination: discord.abc.Messageable,
content: str=None, embed=None, files=[]) -> List[discord.Message]:
*, destination: discord.abc.Messageable, content: str = None, embed=None, files=[]
) -> List[discord.Message]:
"""
This does the actual sending, use this instead of a full tunnel
if you are using command initiated reactions instead of persistent
@@ -131,18 +119,13 @@ class Tunnel(metaclass=TunnelMeta):
files = files if files else None
if content:
for page in pagify(content):
rets.append(
await destination.send(
page, files=files, embed=embed)
)
rets.append(await destination.send(page, files=files, embed=embed))
if files:
del files
if embed:
del embed
elif embed or files:
rets.append(
await destination.send(files=files, embed=embed)
)
rets.append(await destination.send(files=files, embed=embed))
return rets
@staticmethod
@@ -172,15 +155,12 @@ class Tunnel(metaclass=TunnelMeta):
size += sys.getsizeof(_fp)
if size > max_size:
return []
files.append(
discord.File(_fp, filename=a.filename)
)
files.append(discord.File(_fp, filename=a.filename))
return files
async def communicate(self, *,
message: discord.Message,
topic: str=None,
skip_message_content: bool=False):
async def communicate(
self, *, message: discord.Message, topic: str = None, skip_message_content: bool = False
):
"""
Forwards a message.
@@ -208,18 +188,15 @@ class Tunnel(metaclass=TunnelMeta):
the bot can't upload at the origin channel
or can't add reactions there.
"""
if message.channel == self.origin \
and message.author == self.sender:
if message.channel == self.origin and message.author == self.sender:
send_to = self.recipient
elif message.author == self.recipient \
and isinstance(message.channel, discord.DMChannel):
elif message.author == self.recipient and isinstance(message.channel, discord.DMChannel):
send_to = self.origin
else:
return None
if not skip_message_content:
content = "\n".join((topic, message.content)) if topic \
else message.content
content = "\n".join((topic, message.content)) if topic else message.content
else:
content = topic
@@ -234,11 +211,7 @@ class Tunnel(metaclass=TunnelMeta):
else:
attach = []
rets = await self.message_forwarder(
destination=send_to,
content=content,
files=attach
)
rets = await self.message_forwarder(destination=send_to, content=content, files=attach)
await message.add_reaction("\N{WHITE HEAVY CHECK MARK}")
await message.add_reaction("\N{NEGATIVE SQUARED CROSS MARK}")