diff options
| author | 2022-06-03 15:01:16 +0400 | |
|---|---|---|
| committer | 2022-06-03 15:01:16 +0400 | |
| commit | b8b83deaec2c84be73f0684976d175c7ded11df9 (patch) | |
| tree | 459fd6c7a76ba699a6997e7d67b091d0636a9d7d | |
| parent | Merge branch 'main' into multiple-banners (diff) | |
| parent | Merge pull request #2179 from Autonymic/docs/clean-help (diff) | |
Merge branch 'main' into multiple-banners
| -rw-r--r-- | bot/exts/backend/error_handler.py | 34 | ||||
| -rw-r--r-- | bot/exts/filters/filtering.py | 3 | ||||
| -rw-r--r-- | bot/exts/info/doc/_cog.py | 1 | ||||
| -rw-r--r-- | bot/exts/info/doc/_redis_cache.py | 40 | ||||
| -rw-r--r-- | bot/exts/info/tags.py | 6 | ||||
| -rw-r--r-- | bot/exts/moderation/clean.py | 57 | ||||
| -rw-r--r-- | bot/exts/moderation/modlog.py | 47 | ||||
| -rw-r--r-- | bot/exts/utils/thread_bumper.py | 39 | ||||
| -rw-r--r-- | poetry.lock | 257 | ||||
| -rw-r--r-- | pyproject.toml | 4 | ||||
| -rw-r--r-- | tests/bot/exts/backend/test_error_handler.py | 9 | 
11 files changed, 300 insertions, 197 deletions
| diff --git a/bot/exts/backend/error_handler.py b/bot/exts/backend/error_handler.py index 5391a7f15..761991488 100644 --- a/bot/exts/backend/error_handler.py +++ b/bot/exts/backend/error_handler.py @@ -1,3 +1,4 @@ +import copy  import difflib  from botcore.site_api import ResponseCodeError @@ -65,6 +66,8 @@ class ErrorHandler(Cog):          if isinstance(e, errors.CommandNotFound) and not getattr(ctx, "invoked_from_error_handler", False):              if await self.try_silence(ctx):                  return +            if await self.try_run_eval(ctx): +                return              await self.try_get_tag(ctx)  # Try to look for a tag with the command's name          elif isinstance(e, errors.UserInputError):              log.debug(debug_message) @@ -179,6 +182,30 @@ class ErrorHandler(Cog):          if not any(role.id in MODERATION_ROLES for role in ctx.author.roles):              await self.send_command_suggestion(ctx, ctx.invoked_with) +    async def try_run_eval(self, ctx: Context) -> bool: +        """ +        Attempt to run eval command with backticks directly after command. + +        For example: !eval```print("hi")``` + +        Return True if command was invoked, else False +        """ +        msg = copy.copy(ctx.message) + +        command, sep, end = msg.content.partition("```") +        msg.content = command + " " + sep + end +        new_ctx = await self.bot.get_context(msg) + +        eval_command = self.bot.get_command("eval") +        if eval_command is None or new_ctx.command != eval_command: +            return False + +        log.debug("Running fixed eval command.") +        new_ctx.invoked_from_error_handler = True +        await self.bot.invoke(new_ctx) + +        return True +      async def send_command_suggestion(self, ctx: Context, command_name: str) -> None:          """Sends user similar commands if any can be found."""          # No similar tag found, or tag on cooldown - @@ -284,8 +311,11 @@ class ErrorHandler(Cog):              await ctx.send("There does not seem to be anything matching your query.")              ctx.bot.stats.incr("errors.api_error_404")          elif e.status == 400: -            content = await e.response.json() -            log.debug(f"API responded with 400 for command {ctx.command}: %r.", content) +            log.error( +                "API responded with 400 for command %s: %r.", +                ctx.command, +                e.response_json or e.response_text, +            )              await ctx.send("According to the API, your request is malformed.")              ctx.bot.stats.incr("errors.api_error_400")          elif 500 <= e.status < 600: diff --git a/bot/exts/filters/filtering.py b/bot/exts/filters/filtering.py index 21c155902..70f59c1ee 100644 --- a/bot/exts/filters/filtering.py +++ b/bot/exts/filters/filtering.py @@ -1,6 +1,7 @@  import asyncio  import re  import unicodedata +import urllib.parse  from datetime import timedelta  from typing import Any, Dict, List, Mapping, NamedTuple, Optional, Tuple, Union @@ -565,6 +566,7 @@ class Filtering(Cog):          If any are detected, a dictionary of invite data is returned, with a key per invite.          If none are detected, False is returned. +        If we are unable to process an invite, True is returned.          Attempts to catch some of common ways to try to cheat the system.          """ @@ -577,6 +579,7 @@ class Filtering(Cog):          invites = [m.group("invite") for m in DISCORD_INVITE.finditer(text)]          invite_data = dict()          for invite in invites: +            invite = urllib.parse.quote_plus(invite.rstrip("/"))              if invite in invite_data:                  continue diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py index dece44063..cbc329a06 100644 --- a/bot/exts/info/doc/_cog.py +++ b/bot/exts/info/doc/_cog.py @@ -464,5 +464,4 @@ class DocCog(commands.Cog):      async def cog_unload(self) -> None:          """Clear scheduled inventories, queued symbols and cleanup task on cog unload."""          self.inventory_scheduler.cancel_all() -        self.init_refresh_task.cancel()          await self.item_fetcher.clear() diff --git a/bot/exts/info/doc/_redis_cache.py b/bot/exts/info/doc/_redis_cache.py index 107f2344f..8e08e7ae4 100644 --- a/bot/exts/info/doc/_redis_cache.py +++ b/bot/exts/info/doc/_redis_cache.py @@ -1,22 +1,28 @@  from __future__ import annotations  import datetime +import fnmatch +import time  from typing import Optional, TYPE_CHECKING  from async_rediscache.types.base import RedisObject, namespace_lock +from bot.log import get_logger +  if TYPE_CHECKING:      from ._cog import DocItem  WEEK_SECONDS = datetime.timedelta(weeks=1).total_seconds() +log = get_logger(__name__) +  class DocRedisCache(RedisObject):      """Interface for redis functionality needed by the Doc cog."""      def __init__(self, *args, **kwargs):          super().__init__(*args, **kwargs) -        self._set_expires = set() +        self._set_expires = dict[str, float]()      @namespace_lock      async def set(self, item: DocItem, value: str) -> None: @@ -29,16 +35,30 @@ class DocRedisCache(RedisObject):          needs_expire = False          with await self._get_pool_connection() as connection: -            if redis_key not in self._set_expires: +            set_expire = self._set_expires.get(redis_key) +            if set_expire is None:                  # An expire is only set if the key didn't exist before. -                # If this is the first time setting values for this key check if it exists and add it to -                # `_set_expires` to prevent redundant checks for subsequent uses with items from the same page. -                self._set_expires.add(redis_key) -                needs_expire = not await connection.exists(redis_key) +                ttl = await connection.ttl(redis_key) +                log.debug(f"Checked TTL for `{redis_key}`.") + +                if ttl == -1: +                    log.warning(f"Key `{redis_key}` had no expire set.") +                if ttl < 0:  # not set or didn't exist +                    needs_expire = True +                else: +                    log.debug(f"Key `{redis_key}` has a {ttl} TTL.") +                    self._set_expires[redis_key] = time.monotonic() + ttl - .1  # we need this to expire before redis + +            elif time.monotonic() > set_expire: +                # If we got here the key expired in redis and we can be sure it doesn't exist. +                needs_expire = True +                log.debug(f"Key `{redis_key}` expired in internal key cache.")              await connection.hset(redis_key, item.symbol_id, value)              if needs_expire: +                self._set_expires[redis_key] = time.monotonic() + WEEK_SECONDS                  await connection.expire(redis_key, WEEK_SECONDS) +                log.info(f"Set {redis_key} to expire in a week.")      @namespace_lock      async def get(self, item: DocItem) -> Optional[str]: @@ -49,12 +69,18 @@ class DocRedisCache(RedisObject):      @namespace_lock      async def delete(self, package: str) -> bool:          """Remove all values for `package`; return True if at least one key was deleted, False otherwise.""" +        pattern = f"{self.namespace}:{package}:*" +          with await self._get_pool_connection() as connection:              package_keys = [ -                package_key async for package_key in connection.iscan(match=f"{self.namespace}:{package}:*") +                package_key async for package_key in connection.iscan(match=pattern)              ]              if package_keys:                  await connection.delete(*package_keys) +                log.info(f"Deleted keys from redis: {package_keys}.") +                self._set_expires = { +                    key: expire for key, expire in self._set_expires.items() if not fnmatch.fnmatchcase(key, pattern) +                }                  return True              return False diff --git a/bot/exts/info/tags.py b/bot/exts/info/tags.py index e89ffafb1..5d7467caf 100644 --- a/bot/exts/info/tags.py +++ b/bot/exts/info/tags.py @@ -278,7 +278,11 @@ class Tags(Cog):          if tag is None and tag_identifier.group is not None:              # Try exact match with only the name -            tag = self.tags.get(TagIdentifier(None, tag_identifier.group)) +            name_only_identifier = TagIdentifier(None, tag_identifier.group) +            tag = self.tags.get(name_only_identifier) +            if tag: +                # Ensure the correct tag information is sent to statsd +                tag_identifier = name_only_identifier          if tag is None and len(filtered_tags) == 1:              tag_identifier = filtered_tags[0][0] diff --git a/bot/exts/moderation/clean.py b/bot/exts/moderation/clean.py index 0f14f515e..39eff9757 100644 --- a/bot/exts/moderation/clean.py +++ b/bot/exts/moderation/clean.py @@ -7,7 +7,7 @@ from datetime import datetime  from itertools import takewhile  from typing import Callable, Iterable, Literal, Optional, TYPE_CHECKING, Union -from discord import Colour, Message, NotFound, TextChannel, User, errors +from discord import Colour, Message, NotFound, TextChannel, Thread, User, errors  from discord.ext.commands import Cog, Context, Converter, Greedy, group, has_any_role  from discord.ext.commands.converter import TextChannelConverter  from discord.ext.commands.errors import BadArgument @@ -130,8 +130,8 @@ class Clean(Cog):          else:              if channels == "*":                  channels = { -                    channel for channel in ctx.guild.channels -                    if isinstance(channel, TextChannel) +                    channel for channel in ctx.guild.channels + ctx.guild.threads +                    if isinstance(channel, (TextChannel, Thread))                      # Assume that non-public channels are not needed to optimize for speed.                      and channel.permissions_for(ctx.guild.default_role).view_channel                  } @@ -443,7 +443,7 @@ class Clean(Cog):          if log_url and is_mod_channel(ctx.channel):              try:                  await ctx.reply(success_message) -            except errors.NotFound: +            except errors.HTTPException:                  await ctx.send(success_message)          elif log_url:              if mods := self.bot.get_channel(Channels.mods): @@ -486,34 +486,40 @@ class Clean(Cog):          await self._clean_messages(ctx, channels, bots_only, users, regex, first_limit, second_limit) -    @clean_group.command(name="user", aliases=["users"]) -    async def clean_user( +    @clean_group.command(name="users", aliases=["user"]) +    async def clean_users(          self,          ctx: Context, -        user: User, +        users: Greedy[User],          message_or_time: CleanLimit,          *,          channels: CleanChannels = None      ) -> None:          """ -        Delete messages posted by the provided user, stop cleaning after reaching `message_or_time`. +        Delete messages posted by the provided users, stop cleaning after reaching `message_or_time`.          `message_or_time` can be either a message to stop at (exclusive), a timedelta for max message age, or an ISO          datetime. -        If a message is specified, `channels` cannot be specified. +        If a message is specified the cleanup will be limited to the channel the message is in. + +        If a timedelta or an ISO datetime is specified, `channels` can be specified to clean across multiple channels. +        An asterisk can also be used to designate cleanup across all channels.          """ -        await self._clean_messages(ctx, users=[user], channels=channels, first_limit=message_or_time) +        await self._clean_messages(ctx, users=users, channels=channels, first_limit=message_or_time)      @clean_group.command(name="bots", aliases=["bot"])      async def clean_bots(self, ctx: Context, message_or_time: CleanLimit, *, channels: CleanChannels = None) -> None:          """ -        Delete all messages posted by a bot, stop cleaning after traversing `traverse` messages. +        Delete all messages posted by a bot, stop cleaning after reaching `message_or_time`.          `message_or_time` can be either a message to stop at (exclusive), a timedelta for max message age, or an ISO          datetime. -        If a message is specified, `channels` cannot be specified. +        If a message is specified the cleanup will be limited to the channel the message is in. + +        If a timedelta or an ISO datetime is specified, `channels` can be specified to clean across multiple channels. +        An asterisk can also be used to designate cleanup across all channels.          """          await self._clean_messages(ctx, bots_only=True, channels=channels, first_limit=message_or_time) @@ -531,11 +537,19 @@ class Clean(Cog):          `message_or_time` can be either a message to stop at (exclusive), a timedelta for max message age, or an ISO          datetime. -        If a message is specified, `channels` cannot be specified. -        The pattern must be provided enclosed in backticks. -        If the pattern contains spaces, it still needs to be enclosed in double quotes on top of that. -        For example: `[0-9]` +        If a message is specified the cleanup will be limited to the channel the message is in. + +        If a timedelta or an ISO datetime is specified, `channels` can be specified to clean across multiple channels. +        An asterisk can also be used to designate cleanup across all channels. + +        The `regex` pattern must be provided enclosed in backticks. + +        For example: \\`[0-9]\\`. + +        If the `regex` pattern contains spaces, it still needs to be enclosed in double quotes on top of that. + +        For example: "\\`[0-9]\\`".          """          await self._clean_messages(ctx, regex=regex, channels=channels, first_limit=message_or_time) @@ -550,7 +564,11 @@ class Clean(Cog):          Delete all messages until a certain limit.          A limit can be either a message, and ISO date-time string, or a time delta. -        If a message is specified, `channel` cannot be specified. + +        If a message is specified the cleanup will be limited to the channel the message is in. + +        If a timedelta or an ISO datetime is specified, `channels` can be specified to clean across multiple channels. +        An asterisk can also be used to designate cleanup across all channels.          """          await self._clean_messages(              ctx, @@ -573,7 +591,10 @@ class Clean(Cog):          A limit can be either a message, and ISO date-time string, or a time delta.          If two messages are specified, they both must be in the same channel. -        If a message is specified, `channel` cannot be specified. +        The cleanup will be limited to the channel the messages are in. + +        If two timedeltas or ISO datetimes are specified, `channels` can be specified to clean across multiple channels. +        An asterisk can also be used to designate cleanup across all channels.          """          await self._clean_messages(              ctx, diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py index 80f68e442..67991730e 100644 --- a/bot/exts/moderation/modlog.py +++ b/bot/exts/moderation/modlog.py @@ -6,12 +6,14 @@ from datetime import datetime, timezone  from itertools import zip_longest  import discord +from botcore.site_api import ResponseCodeError  from dateutil.relativedelta import relativedelta  from deepdiff import DeepDiff  from discord import Colour, Message, Thread  from discord.abc import GuildChannel  from discord.ext.commands import Cog, Context  from discord.utils import escape_markdown, format_dt, snowflake_time +from sentry_sdk import add_breadcrumb  from bot.bot import Bot  from bot.constants import Categories, Channels, Colours, Emojis, Event, Guild as GuildConstant, Icons, Roles, URLs @@ -53,24 +55,35 @@ class ModLog(Cog, name="ModLog"):          if attachments is None:              attachments = [] -        response = await self.bot.api_client.post( -            'bot/deleted-messages', -            json={ -                'actor': actor_id, -                'creation': datetime.now(timezone.utc).isoformat(), -                'deletedmessage_set': [ -                    { -                        'id': message.id, -                        'author': message.author.id, -                        'channel_id': message.channel.id, -                        'content': message.content.replace("\0", ""),  # Null chars cause 400. -                        'embeds': [embed.to_dict() for embed in message.embeds], -                        'attachments': attachment, -                    } -                    for message, attachment in zip_longest(messages, attachments, fillvalue=[]) -                ] +        deletedmessage_set = [ +            { +                "id": message.id, +                "author": message.author.id, +                "channel_id": message.channel.id, +                "content": message.content.replace("\0", ""),  # Null chars cause 400. +                "embeds": [embed.to_dict() for embed in message.embeds], +                "attachments": attachment,              } -        ) +            for message, attachment in zip_longest(messages, attachments, fillvalue=[]) +        ] + +        try: +            response = await self.bot.api_client.post( +                "bot/deleted-messages", +                json={ +                    "actor": actor_id, +                    "creation": datetime.now(timezone.utc).isoformat(), +                    "deletedmessage_set": deletedmessage_set, +                } +            ) +        except ResponseCodeError as e: +            add_breadcrumb( +                category="api_error", +                message=str(e), +                level="error", +                data=deletedmessage_set, +            ) +            raise          return f"{URLs.site_logs_view}/{response['id']}" diff --git a/bot/exts/utils/thread_bumper.py b/bot/exts/utils/thread_bumper.py index 743919d4e..a2f208484 100644 --- a/bot/exts/utils/thread_bumper.py +++ b/bot/exts/utils/thread_bumper.py @@ -1,7 +1,6 @@  import typing as t  import discord -from async_rediscache import RedisCache  from botcore.site_api import ResponseCodeError  from discord.ext import commands @@ -18,12 +17,26 @@ THREAD_BUMP_ENDPOINT = "bot/bumped-threads"  class ThreadBumper(commands.Cog):      """Cog that allow users to add the current thread to a list that get reopened on archive.""" -    # RedisCache[discord.Thread.id, "sentinel"] -    threads_to_bump = RedisCache() -      def __init__(self, bot: Bot):          self.bot = bot +    async def thread_exists_in_site(self, thread_id: int) -> bool: +        """Return whether the given thread_id exists in the site api's bump list.""" +        # If the thread exists, site returns a 204 with no content. +        # Due to this, `api_client.request()` cannot be used, as it always attempts to decode the response as json. +        # Instead, call the site manually using the api_client's session, to use the auth token logic in the wrapper. + +        async with self.bot.api_client.session.get( +            f"{self.bot.api_client._url_for(THREAD_BUMP_ENDPOINT)}/{thread_id}" +        ) as response: +            if response.status == 204: +                return True +            elif response.status == 404: +                return False +            else: +                # A status other than 204/404 is undefined behaviour from site. Raise error for investigation. +                raise ResponseCodeError(response, response.text()) +      async def unarchive_threads_not_manually_archived(self, threads: list[discord.Thread]) -> None:          """          Iterate through and unarchive any threads that weren't manually archived recently. @@ -89,12 +102,7 @@ class ThreadBumper(commands.Cog):              else:                  raise commands.BadArgument("You must provide a thread, or run this command within a thread.") -        try: -            await self.bot.api_client.get(f"{THREAD_BUMP_ENDPOINT}/{thread.id}") -        except ResponseCodeError as e: -            if e.status != 404: -                raise -        else: +        if await self.thread_exists_in_site(thread.id):              raise commands.BadArgument("This thread is already in the bump list.")          await self.bot.api_client.post(THREAD_BUMP_ENDPOINT, data={"thread_id": thread.id}) @@ -109,9 +117,7 @@ class ThreadBumper(commands.Cog):              else:                  raise commands.BadArgument("You must provide a thread, or run this command within a thread.") -        try: -            await self.bot.api_client.get(f"{THREAD_BUMP_ENDPOINT}/{thread.id}") -        except ResponseCodeError: +        if not await self.thread_exists_in_site(thread.id):              raise commands.BadArgument("This thread is not in the bump list.")          await self.bot.api_client.delete(f"{THREAD_BUMP_ENDPOINT}/{thread.id}") @@ -137,12 +143,7 @@ class ThreadBumper(commands.Cog):          if not after.archived:              return -        try: -            await self.bot.api_client.get(f"{THREAD_BUMP_ENDPOINT}/{after.id}") -        except ResponseCodeError as e: -            if e.status != 404: -                raise -        else: +        if await self.thread_exists_in_site(after.id):              await self.unarchive_threads_not_manually_archived([after])      async def cog_check(self, ctx: commands.Context) -> bool: diff --git a/poetry.lock b/poetry.lock index 8281394e6..7e74cecdd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -141,14 +141,14 @@ async-rediscache = ["async-rediscache[fakeredis] (==0.2.0)"]  [package.source]  type = "url" -url = "https://github.com/python-discord/bot-core/archive/refs/tags/v6.4.0.zip" +url = "https://github.com/python-discord/bot-core/archive/refs/tags/v7.0.0.zip"  [[package]]  name = "certifi" -version = "2021.10.8" +version = "2022.5.18.1"  description = "Python package for providing Mozilla's CA Bundle."  category = "main"  optional = false -python-versions = "*" +python-versions = ">=3.6"  [[package]]  name = "cffi" @@ -297,16 +297,16 @@ testing = ["pre-commit"]  [[package]]  name = "fakeredis" -version = "1.7.1" +version = "1.7.5"  description = "Fake implementation of redis API for testing purposes."  category = "main"  optional = false -python-versions = ">=3.5" +python-versions = ">=3.7"  [package.dependencies]  lupa = {version = "*", optional = true, markers = "extra == \"lua\""}  packaging = "*" -redis = "<4.2.0" +redis = "<=4.3.1"  six = ">=1.12"  sortedcontainers = "*" @@ -327,7 +327,7 @@ sgmllib3k = "*"  [[package]]  name = "filelock" -version = "3.6.0" +version = "3.7.0"  description = "A platform independent file lock."  category = "main"  optional = false @@ -478,7 +478,7 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve  [[package]]  name = "identify" -version = "2.4.12" +version = "2.5.1"  description = "File identification library for Python"  category = "dev"  optional = false @@ -549,15 +549,15 @@ source = ["Cython (>=0.29.7)"]  [[package]]  name = "markdownify" -version = "0.10.3" +version = "0.6.1"  description = "Convert HTML to markdown."  category = "main"  optional = false  python-versions = "*"  [package.dependencies] -beautifulsoup4 = ">=4.9,<5" -six = ">=1.15,<2" +beautifulsoup4 = "*" +six = "*"  [[package]]  name = "mccabe" @@ -686,14 +686,14 @@ virtualenv = ">=20.0.8"  [[package]]  name = "psutil" -version = "5.9.0" +version = "5.9.1"  description = "Cross-platform lib for process and system monitoring in Python."  category = "dev"  optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"  [package.extras] -test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] +test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"]  [[package]]  name = "ptable" @@ -765,7 +765,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"  [[package]]  name = "pyparsing" -version = "3.0.8" +version = "3.0.9"  description = "pyparsing module - Classes and methods to define and execute parsing grammars"  category = "main"  optional = false @@ -909,13 +909,14 @@ full = ["numpy"]  [[package]]  name = "redis" -version = "4.1.4" +version = "4.3.1"  description = "Python client for Redis database and key-value store"  category = "main"  optional = false  python-versions = ">=3.6"  [package.dependencies] +async-timeout = ">=4.0.2"  deprecated = ">=1.2.3"  packaging = ">=20.4" @@ -1129,7 +1130,7 @@ testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)",  [[package]]  name = "wrapt" -version = "1.14.0" +version = "1.14.1"  description = "Module for decorators, wrappers and monkey patching."  category = "main"  optional = false @@ -1150,7 +1151,7 @@ multidict = ">=4.0"  [metadata]  lock-version = "1.1"  python-versions = "3.9.*" -content-hash = "a07f619c75f8133982984eb506ad350144829f10c704421f09b3dbe72cd037d8" +content-hash = "953529931f133865df736f9a6f96f59c64336963ef9e6ce6c959e6bd8c73792c"  [metadata.files]  aiodns = [ @@ -1265,8 +1266,8 @@ beautifulsoup4 = [  ]  bot-core = []  certifi = [ -    {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, -    {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +    {file = "certifi-2022.5.18.1-py3-none-any.whl", hash = "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a"}, +    {file = "certifi-2022.5.18.1.tar.gz", hash = "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7"},  ]  cffi = [      {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, @@ -1400,16 +1401,16 @@ execnet = [      {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},  ]  fakeredis = [ -    {file = "fakeredis-1.7.1-py3-none-any.whl", hash = "sha256:be3668e50f6b57d5fc4abfd27f9f655bed07a2c5aecfc8b15d0aad59f997c1ba"}, -    {file = "fakeredis-1.7.1.tar.gz", hash = "sha256:7c2c4ba1b42e0a75337c54b777bf0671056b4569650e3ff927e4b9b385afc8ec"}, +    {file = "fakeredis-1.7.5-py3-none-any.whl", hash = "sha256:c4ca2be686e7e7637756ccc7dcad8472a5e4866b065431107d7a4b7a250d4e6f"}, +    {file = "fakeredis-1.7.5.tar.gz", hash = "sha256:49375c630981dd4045d9a92e2709fcd4476c91f927e0228493eefa625e705133"},  ]  feedparser = [      {file = "feedparser-6.0.8-py3-none-any.whl", hash = "sha256:1b7f57841d9cf85074deb316ed2c795091a238adb79846bc46dccdaf80f9c59a"},      {file = "feedparser-6.0.8.tar.gz", hash = "sha256:5ce0410a05ab248c8c7cfca3a0ea2203968ee9ff4486067379af4827a59f9661"},  ]  filelock = [ -    {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, -    {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, +    {file = "filelock-3.7.0-py3-none-any.whl", hash = "sha256:c7b5fdb219b398a5b28c8e4c1893ef5f98ece6a38c6ab2c22e26ec161556fed6"}, +    {file = "filelock-3.7.0.tar.gz", hash = "sha256:b795f1b42a61bbf8ec7113c341dad679d772567b936fbd1bf43c9a238e673e20"},  ]  flake8 = [      {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, @@ -1555,8 +1556,8 @@ humanfriendly = [      {file = "humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc"},  ]  identify = [ -    {file = "identify-2.4.12-py2.py3-none-any.whl", hash = "sha256:5f06b14366bd1facb88b00540a1de05b69b310cbc2654db3c7e07fa3a4339323"}, -    {file = "identify-2.4.12.tar.gz", hash = "sha256:3f3244a559290e7d3deb9e9adc7b33594c1bc85a9dd82e0f1be519bf12a1ec17"}, +    {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, +    {file = "identify-2.5.1.tar.gz", hash = "sha256:3d11b16f3fe19f52039fb7e39c9c884b21cb1b586988114fbe42671f03de3e82"},  ]  idna = [      {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, @@ -1784,8 +1785,8 @@ lxml = [      {file = "lxml-4.8.0.tar.gz", hash = "sha256:f63f62fc60e6228a4ca9abae28228f35e1bd3ce675013d1dfb828688d50c6e23"},  ]  markdownify = [ -    {file = "markdownify-0.10.3-py3-none-any.whl", hash = "sha256:edad0ad3896ec7460d05537ad804bbb3614877c6cd0df27b56dee218236d9ce2"}, -    {file = "markdownify-0.10.3.tar.gz", hash = "sha256:782e310390cd5e4bde7543ceb644598c78b9824ee9f8d7ef9f9f4f8782e46974"}, +    {file = "markdownify-0.6.1-py3-none-any.whl", hash = "sha256:7489fd5c601536996a376c4afbcd1dd034db7690af807120681461e82fbc0acc"}, +    {file = "markdownify-0.6.1.tar.gz", hash = "sha256:31d7c13ac2ada8bfc7535a25fee6622ca720e1b5f2d4a9cbc429d167c21f886d"},  ]  mccabe = [      {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, @@ -1892,38 +1893,38 @@ pre-commit = [      {file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"},  ]  psutil = [ -    {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"}, -    {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"}, -    {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"}, -    {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"}, -    {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"}, -    {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"}, -    {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"}, -    {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"}, -    {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"}, -    {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"}, -    {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"}, -    {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"}, -    {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"}, -    {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"}, -    {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"}, -    {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"}, -    {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"}, -    {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"}, -    {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"}, -    {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"}, -    {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"}, -    {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"}, -    {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"}, -    {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"}, -    {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"}, -    {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"}, -    {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"}, -    {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"}, -    {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"}, -    {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"}, -    {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"}, -    {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"}, +    {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, +    {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, +    {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, +    {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, +    {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, +    {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, +    {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, +    {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, +    {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, +    {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, +    {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, +    {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, +    {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, +    {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, +    {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, +    {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, +    {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, +    {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, +    {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, +    {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, +    {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, +    {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, +    {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, +    {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, +    {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, +    {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, +    {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, +    {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, +    {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, +    {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, +    {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, +    {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"},  ]  ptable = [      {file = "PTable-0.9.2.tar.gz", hash = "sha256:aa7fc151cb40f2dabcd2275ba6f7fd0ff8577a86be3365cd3fb297cbe09cc292"}, @@ -1982,8 +1983,8 @@ pyflakes = [      {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},  ]  pyparsing = [ -    {file = "pyparsing-3.0.8-py3-none-any.whl", hash = "sha256:ef7b523f6356f763771559412c0d7134753f037822dad1b16945b7b846f7ad06"}, -    {file = "pyparsing-3.0.8.tar.gz", hash = "sha256:7bf433498c016c4314268d95df76c81b842a4cb2b276fa3312cfb1e1d85f6954"}, +    {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, +    {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},  ]  pyreadline3 = [      {file = "pyreadline3-3.4.1-py3-none-any.whl", hash = "sha256:b0efb6516fd4fb07b45949053826a62fa4cb353db5be2bbb4a7aa1fdd1e345fb"}, @@ -2098,8 +2099,8 @@ rapidfuzz = [      {file = "rapidfuzz-2.0.7.tar.gz", hash = "sha256:93bf42784fd74ebf1a8e89ca1596e9bea7f3ac4a61b825ecc6eb2d9893ad6844"},  ]  redis = [ -    {file = "redis-4.1.4-py3-none-any.whl", hash = "sha256:04629f8e42be942c4f7d1812f2094568f04c612865ad19ad3ace3005da70631a"}, -    {file = "redis-4.1.4.tar.gz", hash = "sha256:1d9a0cdf89fdd93f84261733e24f55a7bbd413a9b219fdaf56e3e728ca9a2306"}, +    {file = "redis-4.3.1-py3-none-any.whl", hash = "sha256:84316970995a7adb907a56754d2b92d88fc2d252963dc5ac34c88f0f1a22c25d"}, +    {file = "redis-4.3.1.tar.gz", hash = "sha256:94b617b4cd296e94991146f66fc5559756fbefe9493604f0312e4d3298ac63e9"},  ]  regex = [      {file = "regex-2022.3.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:42eb13b93765c6698a5ab3bcd318d8c39bb42e5fa8a7fcf7d8d98923f3babdb1"}, @@ -2241,70 +2242,70 @@ virtualenv = [      {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"},  ]  wrapt = [ -    {file = "wrapt-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:5a9a1889cc01ed2ed5f34574c90745fab1dd06ec2eee663e8ebeefe363e8efd7"}, -    {file = "wrapt-1.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:9a3ff5fb015f6feb78340143584d9f8a0b91b6293d6b5cf4295b3e95d179b88c"}, -    {file = "wrapt-1.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4b847029e2d5e11fd536c9ac3136ddc3f54bc9488a75ef7d040a3900406a91eb"}, -    {file = "wrapt-1.14.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:9a5a544861b21e0e7575b6023adebe7a8c6321127bb1d238eb40d99803a0e8bd"}, -    {file = "wrapt-1.14.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:88236b90dda77f0394f878324cfbae05ae6fde8a84d548cfe73a75278d760291"}, -    {file = "wrapt-1.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f0408e2dbad9e82b4c960274214af533f856a199c9274bd4aff55d4634dedc33"}, -    {file = "wrapt-1.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9d8c68c4145041b4eeae96239802cfdfd9ef927754a5be3f50505f09f309d8c6"}, -    {file = "wrapt-1.14.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:22626dca56fd7f55a0733e604f1027277eb0f4f3d95ff28f15d27ac25a45f71b"}, -    {file = "wrapt-1.14.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:65bf3eb34721bf18b5a021a1ad7aa05947a1767d1aa272b725728014475ea7d5"}, -    {file = "wrapt-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09d16ae7a13cff43660155383a2372b4aa09109c7127aa3f24c3cf99b891c330"}, -    {file = "wrapt-1.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:debaf04f813ada978d7d16c7dfa16f3c9c2ec9adf4656efdc4defdf841fc2f0c"}, -    {file = "wrapt-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748df39ed634851350efa87690c2237a678ed794fe9ede3f0d79f071ee042561"}, -    {file = "wrapt-1.14.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1807054aa7b61ad8d8103b3b30c9764de2e9d0c0978e9d3fc337e4e74bf25faa"}, -    {file = "wrapt-1.14.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763a73ab377390e2af26042f685a26787c402390f682443727b847e9496e4a2a"}, -    {file = "wrapt-1.14.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8529b07b49b2d89d6917cfa157d3ea1dfb4d319d51e23030664a827fe5fd2131"}, -    {file = "wrapt-1.14.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:68aeefac31c1f73949662ba8affaf9950b9938b712fb9d428fa2a07e40ee57f8"}, -    {file = "wrapt-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59d7d92cee84a547d91267f0fea381c363121d70fe90b12cd88241bd9b0e1763"}, -    {file = "wrapt-1.14.0-cp310-cp310-win32.whl", hash = "sha256:3a88254881e8a8c4784ecc9cb2249ff757fd94b911d5df9a5984961b96113fff"}, -    {file = "wrapt-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:9a242871b3d8eecc56d350e5e03ea1854de47b17f040446da0e47dc3e0b9ad4d"}, -    {file = "wrapt-1.14.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a65bffd24409454b889af33b6c49d0d9bcd1a219b972fba975ac935f17bdf627"}, -    {file = "wrapt-1.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9d9fcd06c952efa4b6b95f3d788a819b7f33d11bea377be6b8980c95e7d10775"}, -    {file = "wrapt-1.14.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:db6a0ddc1282ceb9032e41853e659c9b638789be38e5b8ad7498caac00231c23"}, -    {file = "wrapt-1.14.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:14e7e2c5f5fca67e9a6d5f753d21f138398cad2b1159913ec9e9a67745f09ba3"}, -    {file = "wrapt-1.14.0-cp35-cp35m-win32.whl", hash = "sha256:6d9810d4f697d58fd66039ab959e6d37e63ab377008ef1d63904df25956c7db0"}, -    {file = "wrapt-1.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:d808a5a5411982a09fef6b49aac62986274ab050e9d3e9817ad65b2791ed1425"}, -    {file = "wrapt-1.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b77159d9862374da213f741af0c361720200ab7ad21b9f12556e0eb95912cd48"}, -    {file = "wrapt-1.14.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36a76a7527df8583112b24adc01748cd51a2d14e905b337a6fefa8b96fc708fb"}, -    {file = "wrapt-1.14.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0057b5435a65b933cbf5d859cd4956624df37b8bf0917c71756e4b3d9958b9e"}, -    {file = "wrapt-1.14.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a0a4ca02752ced5f37498827e49c414d694ad7cf451ee850e3ff160f2bee9d3"}, -    {file = "wrapt-1.14.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8c6be72eac3c14baa473620e04f74186c5d8f45d80f8f2b4eda6e1d18af808e8"}, -    {file = "wrapt-1.14.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:21b1106bff6ece8cb203ef45b4f5778d7226c941c83aaaa1e1f0f4f32cc148cd"}, -    {file = "wrapt-1.14.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:493da1f8b1bb8a623c16552fb4a1e164c0200447eb83d3f68b44315ead3f9036"}, -    {file = "wrapt-1.14.0-cp36-cp36m-win32.whl", hash = "sha256:89ba3d548ee1e6291a20f3c7380c92f71e358ce8b9e48161401e087e0bc740f8"}, -    {file = "wrapt-1.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:729d5e96566f44fccac6c4447ec2332636b4fe273f03da128fff8d5559782b06"}, -    {file = "wrapt-1.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:891c353e95bb11abb548ca95c8b98050f3620a7378332eb90d6acdef35b401d4"}, -    {file = "wrapt-1.14.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23f96134a3aa24cc50614920cc087e22f87439053d886e474638c68c8d15dc80"}, -    {file = "wrapt-1.14.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6807bcee549a8cb2f38f73f469703a1d8d5d990815c3004f21ddb68a567385ce"}, -    {file = "wrapt-1.14.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6915682f9a9bc4cf2908e83caf5895a685da1fbd20b6d485dafb8e218a338279"}, -    {file = "wrapt-1.14.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f2f3bc7cd9c9fcd39143f11342eb5963317bd54ecc98e3650ca22704b69d9653"}, -    {file = "wrapt-1.14.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3a71dbd792cc7a3d772ef8cd08d3048593f13d6f40a11f3427c000cf0a5b36a0"}, -    {file = "wrapt-1.14.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5a0898a640559dec00f3614ffb11d97a2666ee9a2a6bad1259c9facd01a1d4d9"}, -    {file = "wrapt-1.14.0-cp37-cp37m-win32.whl", hash = "sha256:167e4793dc987f77fd476862d32fa404d42b71f6a85d3b38cbce711dba5e6b68"}, -    {file = "wrapt-1.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d066ffc5ed0be00cd0352c95800a519cf9e4b5dd34a028d301bdc7177c72daf3"}, -    {file = "wrapt-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d9bdfa74d369256e4218000a629978590fd7cb6cf6893251dad13d051090436d"}, -    {file = "wrapt-1.14.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2498762814dd7dd2a1d0248eda2afbc3dd9c11537bc8200a4b21789b6df6cd38"}, -    {file = "wrapt-1.14.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f24ca7953f2643d59a9c87d6e272d8adddd4a53bb62b9208f36db408d7aafc7"}, -    {file = "wrapt-1.14.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b835b86bd5a1bdbe257d610eecab07bf685b1af2a7563093e0e69180c1d4af1"}, -    {file = "wrapt-1.14.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b21650fa6907e523869e0396c5bd591cc326e5c1dd594dcdccac089561cacfb8"}, -    {file = "wrapt-1.14.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:354d9fc6b1e44750e2a67b4b108841f5f5ea08853453ecbf44c81fdc2e0d50bd"}, -    {file = "wrapt-1.14.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1f83e9c21cd5275991076b2ba1cd35418af3504667affb4745b48937e214bafe"}, -    {file = "wrapt-1.14.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:61e1a064906ccba038aa3c4a5a82f6199749efbbb3cef0804ae5c37f550eded0"}, -    {file = "wrapt-1.14.0-cp38-cp38-win32.whl", hash = "sha256:28c659878f684365d53cf59dc9a1929ea2eecd7ac65da762be8b1ba193f7e84f"}, -    {file = "wrapt-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:b0ed6ad6c9640671689c2dbe6244680fe8b897c08fd1fab2228429b66c518e5e"}, -    {file = "wrapt-1.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3f7e671fb19734c872566e57ce7fc235fa953d7c181bb4ef138e17d607dc8a1"}, -    {file = "wrapt-1.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87fa943e8bbe40c8c1ba4086971a6fefbf75e9991217c55ed1bcb2f1985bd3d4"}, -    {file = "wrapt-1.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4775a574e9d84e0212f5b18886cace049a42e13e12009bb0491562a48bb2b758"}, -    {file = "wrapt-1.14.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d57677238a0c5411c76097b8b93bdebb02eb845814c90f0b01727527a179e4d"}, -    {file = "wrapt-1.14.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00108411e0f34c52ce16f81f1d308a571df7784932cc7491d1e94be2ee93374b"}, -    {file = "wrapt-1.14.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d332eecf307fca852d02b63f35a7872de32d5ba8b4ec32da82f45df986b39ff6"}, -    {file = "wrapt-1.14.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:01f799def9b96a8ec1ef6b9c1bbaf2bbc859b87545efbecc4a78faea13d0e3a0"}, -    {file = "wrapt-1.14.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47045ed35481e857918ae78b54891fac0c1d197f22c95778e66302668309336c"}, -    {file = "wrapt-1.14.0-cp39-cp39-win32.whl", hash = "sha256:2eca15d6b947cfff51ed76b2d60fd172c6ecd418ddab1c5126032d27f74bc350"}, -    {file = "wrapt-1.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:bb36fbb48b22985d13a6b496ea5fb9bb2a076fea943831643836c9f6febbcfdc"}, -    {file = "wrapt-1.14.0.tar.gz", hash = "sha256:8323a43bd9c91f62bb7d4be74cc9ff10090e7ef820e27bfe8815c57e68261311"}, +    {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, +    {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, +    {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, +    {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, +    {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, +    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, +    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, +    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, +    {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, +    {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, +    {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, +    {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, +    {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, +    {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, +    {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, +    {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, +    {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, +    {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, +    {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, +    {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, +    {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, +    {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, +    {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, +    {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, +    {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, +    {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, +    {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, +    {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, +    {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, +    {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, +    {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, +    {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, +    {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, +    {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, +    {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, +    {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, +    {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, +    {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, +    {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, +    {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, +    {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, +    {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, +    {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, +    {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, +    {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, +    {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, +    {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, +    {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, +    {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, +    {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, +    {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, +    {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, +    {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, +    {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, +    {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, +    {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, +    {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, +    {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, +    {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, +    {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, +    {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, +    {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, +    {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, +    {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"},  ]  yarl = [      {file = "yarl-1.7.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2a8508f7350512434e41065684076f640ecce176d262a7d54f0da41d99c5a95"}, diff --git a/pyproject.toml b/pyproject.toml index 402d05f70..2d6adb9c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ python = "3.9.*"  "discord.py" = {url = "https://github.com/Rapptz/discord.py/archive/5a06fa5f3e28d2b7191722e1a84c541560008aea.zip"}  # See https://bot-core.pythondiscord.com/ for docs. -bot-core = {url = "https://github.com/python-discord/bot-core/archive/refs/tags/v6.4.0.zip", extras = ["async-rediscache"]} +bot-core = {url = "https://github.com/python-discord/bot-core/archive/refs/tags/v7.0.0.zip", extras = ["async-rediscache"]}  aiodns = "3.0.0"  aiohttp = "3.8.1" @@ -25,7 +25,7 @@ emoji = "1.7.0"  feedparser = "6.0.8"  rapidfuzz = "2.0.7"  lxml = "4.8.0" -markdownify = "0.10.3" +markdownify = "0.6.1"  more_itertools = "8.12.0"  python-dateutil = "2.8.2"  python-frontmatter = "1.0.0" diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py index 193f1d822..0a58126e7 100644 --- a/tests/bot/exts/backend/test_error_handler.py +++ b/tests/bot/exts/backend/test_error_handler.py @@ -48,6 +48,7 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):          cog = ErrorHandler(self.bot)          cog.try_silence = AsyncMock()          cog.try_get_tag = AsyncMock() +        cog.try_run_eval = AsyncMock(return_value=False)          for case in test_cases:              with self.subTest(try_silence_return=case["try_silence_return"], try_get_tag=case["called_try_get_tag"]): @@ -76,6 +77,7 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):          cog = ErrorHandler(self.bot)          cog.try_silence = AsyncMock()          cog.try_get_tag = AsyncMock() +        cog.try_run_eval = AsyncMock()          error = errors.CommandNotFound() @@ -83,6 +85,7 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):          cog.try_silence.assert_not_awaited()          cog.try_get_tag.assert_not_awaited() +        cog.try_run_eval.assert_not_awaited()          self.ctx.send.assert_not_awaited()      async def test_error_handler_user_input_error(self): @@ -477,11 +480,11 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase):      @patch("bot.exts.backend.error_handler.log")      async def test_handle_api_error(self, log_mock): -        """Should `ctx.send` on HTTP error codes, `log.debug|warning` depends on code.""" +        """Should `ctx.send` on HTTP error codes, and log at correct level."""          test_cases = (              {                  "error": ResponseCodeError(AsyncMock(status=400)), -                "log_level": "debug" +                "log_level": "error"              },              {                  "error": ResponseCodeError(AsyncMock(status=404)), @@ -505,6 +508,8 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase):                  self.ctx.send.assert_awaited_once()                  if case["log_level"] == "warning":                      log_mock.warning.assert_called_once() +                elif case["log_level"] == "error": +                    log_mock.error.assert_called_once()                  else:                      log_mock.debug.assert_called_once() | 
