aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar mbaruh <[email protected]>2023-03-10 16:43:27 +0200
committerGravatar mbaruh <[email protected]>2023-03-10 16:45:00 +0200
commita05ab5b71f595b2e36df87ac9ea2350fe8cf0bfb (patch)
tree97b92982801477062678da516bb0cc76a9cf69b5
parentReduce long timeout to 28d instead of denying it (diff)
parentMerge pull request #2326 from python-discord/snekbox-files (diff)
Merge branch 'main' into mbaruh/timeout
-rw-r--r--.github/workflows/lint-test.yml5
-rw-r--r--.github/workflows/status_embed.yaml2
-rw-r--r--.gitignore1
-rw-r--r--bot/constants.py1101
-rw-r--r--bot/exts/backend/branding/_cog.py2
-rw-r--r--bot/exts/backend/config_verifier.py4
-rw-r--r--bot/exts/filters/antispam.py16
-rw-r--r--bot/exts/fun/duck_pond.py2
-rw-r--r--bot/exts/help_channels/_channel.py4
-rw-r--r--bot/exts/help_channels/_cog.py4
-rw-r--r--bot/exts/help_channels/_stats.py2
-rw-r--r--bot/exts/info/codeblock/_cog.py2
-rw-r--r--bot/exts/info/doc/_markdown.py12
-rw-r--r--bot/exts/info/stats.py11
-rw-r--r--bot/exts/info/tags.py1
-rw-r--r--bot/exts/moderation/incidents.py6
-rw-r--r--bot/exts/moderation/watchchannels/_watchchannel.py2
-rw-r--r--bot/exts/moderation/watchchannels/bigbrother.py4
-rw-r--r--bot/exts/recruitment/talentpool/_review.py2
-rw-r--r--bot/exts/utils/snekbox/__init__.py12
-rw-r--r--bot/exts/utils/snekbox/_cog.py (renamed from bot/exts/utils/snekbox.py)373
-rw-r--r--bot/exts/utils/snekbox/_eval.py183
-rw-r--r--bot/exts/utils/snekbox/_io.py102
-rw-r--r--botstrap.py165
-rw-r--r--config-default.yml560
-rw-r--r--docker-compose.yml7
-rw-r--r--poetry.lock1377
-rw-r--r--pyproject.toml45
-rw-r--r--tests/bot/exts/info/doc/test_parsing.py23
-rw-r--r--tests/bot/exts/utils/snekbox/__init__.py0
-rw-r--r--tests/bot/exts/utils/snekbox/test_io.py34
-rw-r--r--tests/bot/exts/utils/snekbox/test_snekbox.py (renamed from tests/bot/exts/utils/test_snekbox.py)266
32 files changed, 2323 insertions, 2007 deletions
diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml
index 051ca2265..af1d703c0 100644
--- a/.github/workflows/lint-test.yml
+++ b/.github/workflows/lint-test.yml
@@ -16,7 +16,8 @@ jobs:
MIT License;
Mozilla Public License 2.0 (MPL 2.0);
Public Domain;
- Python Software Foundation License
+ Python Software Foundation License;
+ The Unlicense (Unlicense)
# Dummy values for required bot environment variables
BOT_API_KEY: foo
@@ -83,7 +84,7 @@ jobs:
- name: Upload a Build Artifact
if: always() && steps.prepare-artifact.outcome == 'success'
continue-on-error: true
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v3
with:
name: pull-request-payload
path: pull_request_payload.json
diff --git a/.github/workflows/status_embed.yaml b/.github/workflows/status_embed.yaml
index 1923965ab..60bdaf770 100644
--- a/.github/workflows/status_embed.yaml
+++ b/.github/workflows/status_embed.yaml
@@ -58,7 +58,7 @@ jobs:
# more information and we can fine tune when we actually want
# to send an embed.
- name: GitHub Actions Status Embed for Discord
- uses: SebastiaanZ/[email protected]
+ uses: SebastiaanZ/[email protected]
with:
# Our GitHub Actions webhook
webhook_id: '784184528997842985'
diff --git a/.gitignore b/.gitignore
index 6691dbea1..65a9af431 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,3 +123,4 @@ TEST-**.xml
# Mac OS .DS_Store, which is a file that stores custom attributes of its containing folder
.DS_Store
+*.env*
diff --git a/bot/constants.py b/bot/constants.py
index 7e8e7591a..983853e00 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -1,703 +1,721 @@
"""
-Loads bot configuration from YAML files.
-By default, this simply loads the default
-configuration located at `config-default.yml`.
-If a file called `config.yml` is found in the
-project directory, the default configuration
-is recursively updated with any settings from
-the custom configuration. Any settings left
-out in the custom user configuration will stay
-their default values from `config-default.yml`.
+Loads bot configuration from environment variables
+and `.env` files. By default, this simply loads the
+default configuration defined thanks to the `default`
+keyword argument in each instance of the `Field` class
+If two files called `.env` and `.env.server` are found
+in the project directory, the values will be loaded
+from both of them, thus overlooking the predefined defaults.
+Any settings left out in the custom user configuration
+will default to the values passed to the `default` kwarg.
"""
import os
-from collections.abc import Mapping
from enum import Enum
-from pathlib import Path
-from typing import Dict, List, Optional
+from typing import Optional
-import yaml
+from pydantic import BaseModel, BaseSettings, root_validator
-try:
- import dotenv
- dotenv.load_dotenv()
-except ModuleNotFoundError:
- pass
+class EnvConfig(BaseSettings):
+ class Config:
+ env_file = ".env", ".env.server",
+ env_file_encoding = 'utf-8'
+ env_nested_delimiter = '__'
-def _env_var_constructor(loader, node):
- """
- Implements a custom YAML tag for loading optional environment
- variables. If the environment variable is set, returns the
- value of it. Otherwise, returns `None`.
- Example usage in the YAML configuration:
+class _Miscellaneous(EnvConfig):
+ debug = True
+ file_logs = False
- # Optional app configuration. Set `MY_APP_KEY` in the environment to use it.
- application:
- key: !ENV 'MY_APP_KEY'
- """
- default = None
+Miscellaneous = _Miscellaneous()
- # Check if the node is a plain string value
- if node.id == 'scalar':
- value = loader.construct_scalar(node)
- key = str(value)
- else:
- # The node value is a list
- value = loader.construct_sequence(node)
- if len(value) >= 2:
- # If we have at least two values, then we have both a key and a default value
- default = value[1]
- key = value[0]
- else:
- # Otherwise, we just have a key
- key = value[0]
+FILE_LOGS = Miscellaneous.file_logs
+DEBUG_MODE = Miscellaneous.debug
- return os.getenv(key, default)
+class _Bot(EnvConfig):
+ EnvConfig.Config.env_prefix = "bot_"
-def _join_var_constructor(loader, node):
- """
- Implements a custom YAML tag for concatenating other tags in
- the document to strings. This allows for a much more DRY configuration
- file.
- """
+ prefix = "!"
+ sentry_dsn = ""
+ token = ""
+ trace_loggers = "*"
- fields = loader.construct_sequence(node)
- return "".join(str(x) for x in fields)
+Bot = _Bot()
-yaml.SafeLoader.add_constructor("!ENV", _env_var_constructor)
-yaml.SafeLoader.add_constructor("!JOIN", _join_var_constructor)
-# Pointing old tag to !ENV constructor to avoid breaking existing configs
-yaml.SafeLoader.add_constructor("!REQUIRED_ENV", _env_var_constructor)
+class _Channels(EnvConfig):
+ EnvConfig.Config.env_prefix = "channels_"
+ announcements = 354619224620138496
+ changelog = 748238795236704388
+ mailing_lists = 704372456592506880
+ python_events = 729674110270963822
+ python_news = 704372456592506880
+ reddit = 458224812528238616
-with open("config-default.yml", encoding="UTF-8") as f:
- _CONFIG_YAML = yaml.safe_load(f)
+ dev_contrib = 635950537262759947
+ dev_core = 411200599653351425
+ dev_log = 622895325144940554
+ meta = 429409067623251969
+ python_general = 267624335836053506
-def _recursive_update(original, new):
- """
- Helper method which implements a recursive `dict.update`
- method, used for updating the original configuration with
- configuration specified by the user.
- """
+ python_help = 1035199133436354600
- for key, value in original.items():
- if key not in new:
- continue
+ attachment_log = 649243850006855680
+ filter_log = 1014943924185473094
+ message_log = 467752170159079424
+ mod_log = 282638479504965634
+ nomination_voting_archive = 833371042046148738
+ user_log = 528976905546760203
+ voice_log = 640292421988646961
- if isinstance(value, Mapping):
- if not any(isinstance(subvalue, Mapping) for subvalue in value.values()):
- original[key].update(new[key])
- _recursive_update(original[key], new[key])
- else:
- original[key] = new[key]
+ off_topic_0 = 291284109232308226
+ off_topic_1 = 463035241142026251
+ off_topic_2 = 463035268514185226
+ bot_commands = 267659945086812160
+ discord_bots = 343944376055103488
+ esoteric = 470884583684964352
+ voice_gate = 764802555427029012
+ code_jam_planning = 490217981872177157
-if Path("config.yml").exists():
- print("Found `config.yml` file, loading constants from it.")
- with open("config.yml", encoding="UTF-8") as f:
- user_config = yaml.safe_load(f)
- _recursive_update(_CONFIG_YAML, user_config)
+ # Staff
+ admins = 365960823622991872
+ admin_spam = 563594791770914816
+ defcon = 464469101889454091
+ helpers = 385474242440986624
+ incidents = 714214212200562749
+ incidents_archive = 720668923636351037
+ mod_alerts = 473092532147060736
+ mod_meta = 775412552795947058
+ mods = 305126844661760000
+ nominations = 822920136150745168
+ nomination_voting = 822853512709931008
+ organisation = 551789653284356126
+ # Staff announcement channels
+ admin_announcements = 749736155569848370
+ mod_announcements = 372115205867700225
+ staff_announcements = 464033278631084042
+ staff_info = 396684402404622347
+ staff_lounge = 464905259261755392
-def check_required_keys(keys):
- """
- Verifies that keys that are set to be required are present in the
- loaded configuration.
- """
- for key_path in keys:
- lookup = _CONFIG_YAML
- try:
- for key in key_path.split('.'):
- lookup = lookup[key]
- if lookup is None:
- raise KeyError(key)
- except KeyError:
- raise KeyError(
- f"A configuration for `{key_path}` is required, but was not found. "
- "Please set it in `config.yml` or setup an environment variable and try again."
- )
-
-
-try:
- required_keys = _CONFIG_YAML['config']['required_keys']
-except KeyError:
- pass
-else:
- check_required_keys(required_keys)
-
-
-class YAMLGetter(type):
+ # Voice Channels
+ admins_voice = 500734494840717332
+ code_help_voice_0 = 751592231726481530
+ code_help_voice_1 = 764232549840846858
+ general_voice_0 = 751591688538947646
+ general_voice_1 = 799641437645701151
+ staff_voice = 412375055910043655
+
+ black_formatter = 846434317021741086
+
+ # Voice Chat
+ code_help_chat_0 = 755154969761677312
+ code_help_chat_1 = 766330079135268884
+ staff_voice_chat = 541638762007101470
+ voice_chat_0 = 412357430186344448
+ voice_chat_1 = 799647045886541885
+
+ big_brother = 468507907357409333
+ duck_pond = 637820308341915648
+ roles = 851270062434156586
+
+
+Channels = _Channels()
+
+
+class _Roles(EnvConfig):
+
+ EnvConfig.Config.env_prefix = "roles_"
+
+ # Self-assignable roles, see the Subscribe cog
+ advent_of_code = 518565788744024082
+ announcements = 463658397560995840
+ lovefest = 542431903886606399
+ pyweek_announcements = 897568414044938310
+ revival_of_code = 988801794668908655
+ legacy_help_channels_access = 1074780483776417964
+
+ contributors = 295488872404484098
+ help_cooldown = 699189276025421825
+ muted = 277914926603829249 # TODO remove when no longer relevant.
+ partners = 323426753857191936
+ python_community = 458226413825294336
+ voice_verified = 764802720779337729
+
+ # Streaming
+ video = 764245844798079016
+
+ # Staff
+ admins = 267628507062992896
+ core_developers = 587606783669829632
+ code_jam_event_team = 787816728474288181
+ devops = 409416496733880320
+ domain_leads = 807415650778742785
+ events_lead = 778361735739998228
+ helpers = 267630620367257601
+ moderators = 831776746206265384
+ mod_team = 267629731250176001
+ owners = 267627879762755584
+ project_leads = 815701647526330398
+
+ # Code Jam
+ jammers = 737249140966162473
+
+ # Patreon
+ patreon_tier_1 = 505040943800516611
+ patreon_tier_2 = 743399725914390631
+ patreon_tier_3 = 743400204367036520
+
+
+Roles = _Roles()
+
+
+class _Categories(EnvConfig):
+ EnvConfig.Config.env_prefix = "categories_"
+
+ logs = 468520609152892958
+ moderators = 749736277464842262
+ modmail = 714494672835444826
+ appeals = 890331800025563216
+ appeals_2 = 895417395261341766
+ voice = 356013253765234688
+
+ # 2021 Summer Code Jam
+ summer_code_jam = 861692638540857384
+
+
+Categories = _Categories()
+
+
+class _Guild(EnvConfig):
+ EnvConfig.Config.env_prefix = "guild_"
+
+ id = 267624335836053506
+ invite = "https://discord.gg/python"
+
+ moderation_categories = [
+ Categories.moderators,
+ Categories.modmail,
+ Categories.logs,
+ Categories.appeals,
+ Categories.appeals_2
+ ]
+ moderation_channels = [Channels.admins, Channels.admin_spam, Channels.mods]
+ modlog_blacklist = [
+ Channels.attachment_log,
+ Channels.message_log,
+ Channels.mod_log,
+ Channels.staff_voice,
+ Channels.filter_log
+ ]
+ reminder_whitelist = [Channels.bot_commands, Channels.dev_contrib, Channels.black_formatter]
+ moderation_roles = [Roles.admins, Roles.mod_team, Roles.moderators, Roles.owners]
+ staff_roles = [Roles.admins, Roles.helpers, Roles.mod_team, Roles.owners]
+
+
+Guild = _Guild()
+
+
+class Event(Enum):
"""
- Implements a custom metaclass used for accessing
- configuration data by simply accessing class attributes.
- Supports getting configuration from up to two levels
- of nested configuration through `section` and `subsection`.
-
- `section` specifies the YAML configuration section (or "key")
- in which the configuration lives, and must be set.
-
- `subsection` is an optional attribute specifying the section
- within the section from which configuration should be loaded.
-
- Example Usage:
-
- # config.yml
- bot:
- prefixes:
- direct_message: ''
- guild: '!'
-
- # config.py
- class Prefixes(metaclass=YAMLGetter):
- section = "bot"
- subsection = "prefixes"
-
- # Usage in Python code
- from config import Prefixes
- def get_prefix(bot, message):
- if isinstance(message.channel, PrivateChannel):
- return Prefixes.direct_message
- return Prefixes.guild
+ Event names. This does not include every event (for example, raw
+ events aren't here), but only events used in ModLog for now.
"""
- subsection = None
+ guild_channel_create = "guild_channel_create"
+ guild_channel_delete = "guild_channel_delete"
+ guild_channel_update = "guild_channel_update"
+ guild_role_create = "guild_role_create"
+ guild_role_delete = "guild_role_delete"
+ guild_role_update = "guild_role_update"
+ guild_update = "guild_update"
+
+ member_join = "member_join"
+ member_remove = "member_remove"
+ member_ban = "member_ban"
+ member_unban = "member_unban"
+ member_update = "member_update"
- def __getattr__(cls, name):
- name = name.lower()
+ message_delete = "message_delete"
+ message_edit = "message_edit"
- try:
- if cls.subsection is not None:
- return _CONFIG_YAML[cls.section][cls.subsection][name]
- return _CONFIG_YAML[cls.section][name]
- except KeyError as e:
- dotted_path = '.'.join(
- (cls.section, cls.subsection, name)
- if cls.subsection is not None else (cls.section, name)
- )
- print(f"Tried accessing configuration variable at `{dotted_path}`, but it could not be found.")
- raise AttributeError(repr(name)) from e
+ voice_state_update = "voice_state_update"
- def __getitem__(cls, name):
- return cls.__getattr__(name)
- def __iter__(cls):
- """Return generator of key: value pairs of current constants class' config values."""
- for name in cls.__annotations__:
- yield name, getattr(cls, name)
+class ThreadArchiveTimes(Enum):
+ HOUR = 60
+ DAY = 1440
+ THREE_DAY = 4320
+ WEEK = 10080
-# Dataclasses
-class Bot(metaclass=YAMLGetter):
- section = "bot"
+class Webhook(BaseModel):
+ id: int
+ channel: int
- prefix: str
- sentry_dsn: Optional[str]
- token: str
- trace_loggers: Optional[str]
+class _Webhooks(EnvConfig):
+ EnvConfig.Config.env_prefix = "webhooks_"
-class Redis(metaclass=YAMLGetter):
- section = "bot"
- subsection = "redis"
+ big_brother: Webhook = Webhook(id=569133704568373283, channel=Channels.big_brother)
+ dev_log: Webhook = Webhook(id=680501655111729222, channel=Channels.dev_log)
+ duck_pond: Webhook = Webhook(id=637821475327311927, channel=Channels.duck_pond)
+ incidents: Webhook = Webhook(id=816650601844572212, channel=Channels.incidents)
+ incidents_archive: Webhook = Webhook(id=720671599790915702, channel=Channels.incidents_archive)
+ python_news: Webhook = Webhook(id=704381182279942324, channel=Channels.python_news)
- host: str
- password: Optional[str]
- port: int
- use_fakeredis: bool # If this is True, Bot will use fakeredis.aioredis
+Webhooks = _Webhooks()
-class Filter(metaclass=YAMLGetter):
- section = "filter"
- filter_domains: bool
- filter_everyone_ping: bool
- filter_invites: bool
- filter_zalgo: bool
- watch_regex: bool
- watch_rich_embeds: bool
+class _BigBrother(EnvConfig):
+ EnvConfig.Config.env_prefix = "big_brother_"
- # Notifications are not expected for "watchlist" type filters
+ header_message_limit = 15
+ log_delay = 15
- notify_user_domains: bool
- notify_user_everyone_ping: bool
- notify_user_invites: bool
- notify_user_zalgo: bool
- offensive_msg_delete_days: int
- ping_everyone: bool
+BigBrother = _BigBrother()
- channel_whitelist: List[int]
- role_whitelist: List[int]
+class _CodeBlock(EnvConfig):
+ EnvConfig.Config.env_prefix = "code_block_"
-class Cooldowns(metaclass=YAMLGetter):
- section = "bot"
- subsection = "cooldowns"
+ # The channels in which code blocks will be detected. They are not subject to a cooldown.
+ channel_whitelist: list[int] = [Channels.bot_commands]
+ # The channels which will be affected by a cooldown. These channels are also whitelisted.
+ cooldown_channels: list[int] = [Channels.python_general]
- tags: int
+ cooldown_seconds = 300
+ minimum_lines = 4
-class Colours(metaclass=YAMLGetter):
- section = "style"
- subsection = "colours"
+CodeBlock = _CodeBlock()
- blue: int
- bright_green: int
- orange: int
- pink: int
- purple: int
- soft_green: int
- soft_orange: int
- soft_red: int
- white: int
- yellow: int
+class _Colours(EnvConfig):
+ EnvConfig.Config.env_prefix = "colours_"
-class DuckPond(metaclass=YAMLGetter):
- section = "duck_pond"
+ blue = 0x3775a8
+ bright_green = 0x01d277
+ orange = 0xe67e22
+ pink = 0xcf84e0
+ purple = 0xb734eb
+ soft_green = 0x68c290
+ soft_orange = 0xf9cb54
+ soft_red = 0xcd6d6d
+ white = 0xfffffe
+ yellow = 0xffd241
- threshold: int
- channel_blacklist: List[int]
+ @root_validator(pre=True)
+ def parse_hex_values(cls, values):
+ for key, value in values.items():
+ values[key] = int(value, 16)
+ return values
-class Emojis(metaclass=YAMLGetter):
- section = "style"
- subsection = "emojis"
+Colours = _Colours()
- badge_bug_hunter: str
- badge_bug_hunter_level_2: str
- badge_early_supporter: str
- badge_hypesquad: str
- badge_hypesquad_balance: str
- badge_hypesquad_bravery: str
- badge_hypesquad_brilliance: str
- badge_partner: str
- badge_staff: str
- badge_verified_bot_developer: str
- verified_bot: str
- bot: str
- defcon_shutdown: str # noqa: E704
- defcon_unshutdown: str # noqa: E704
- defcon_update: str # noqa: E704
+class _Free(EnvConfig):
+ EnvConfig.Config.env_prefix = "free_"
- failmail: str
+ activity_timeout = 600
+ cooldown_per = 60.0
+ cooldown_rate = 1
- incident_actioned: str
- incident_investigating: str
- incident_unactioned: str
- status_dnd: str
- status_idle: str
- status_offline: str
- status_online: str
+Free = _Free()
- ducky_dave: str
- trashcan: str
+class Rule(BaseModel):
+ interval: int
+ max: int
- bullet: str
- check_mark: str
- cross_mark: str
- new: str
- pencil: str
- ok_hand: str
+# Some help in choosing an appropriate name for this is appreciated
+class ExtendedRule(Rule):
+ max_consecutive: int
-class Icons(metaclass=YAMLGetter):
- section = "style"
- subsection = "icons"
+class Rules(BaseModel):
+ attachments: Rule = Rule(interval=10, max=6)
+ burst: Rule = Rule(interval=10, max=7)
+ chars: Rule = Rule(interval=5, max=4_200)
+ discord_emojis: Rule = Rule(interval=10, max=20)
+ duplicates: Rule = Rule(interval=10, max=3)
+ links: Rule = Rule(interval=10, max=10)
+ mentions: Rule = Rule(interval=10, max=5)
+ newlines: ExtendedRule = ExtendedRule(interval=10, max=100, max_consecutive=10)
+ role_mentions: Rule = Rule(interval=10, max=3)
- crown_blurple: str
- crown_green: str
- crown_red: str
- defcon_denied: str # noqa: E704
- defcon_shutdown: str # noqa: E704
- defcon_unshutdown: str # noqa: E704
- defcon_update: str # noqa: E704
+class _AntiSpam(EnvConfig):
+ EnvConfig.Config.env_prefix = 'anti_spam_'
- filtering: str
+ cache_size = 100
- green_checkmark: str
- green_questionmark: str
- guild_update: str
+ clean_offending = True
+ ping_everyone = True
- hash_blurple: str
- hash_green: str
- hash_red: str
+ remove_timeout_after = 600
+ rules = Rules()
- message_bulk_delete: str
- message_delete: str
- message_edit: str
- pencil: str
+AntiSpam = _AntiSpam()
- questionmark: str
- remind_blurple: str
- remind_green: str
- remind_red: str
+class _HelpChannels(EnvConfig):
+ EnvConfig.Config.env_prefix = "help_channels_"
- sign_in: str
- sign_out: str
+ enable = True
+ idle_minutes = 30
+ deleted_idle_minutes = 5
+ # Roles which are allowed to use the command which makes channels dormant
+ cmd_whitelist: list[int] = [Roles.helpers]
- superstarify: str
- unsuperstarify: str
- token_removed: str
+HelpChannels = _HelpChannels()
- user_ban: str
- user_timeout: str
- user_unban: str
- user_untimeout: str
- user_update: str
- user_verified: str
- user_warn: str
- voice_state_blue: str
- voice_state_green: str
- voice_state_red: str
+class _RedirectOutput(EnvConfig):
+ EnvConfig.Config.env_prefix = "redirect_output_"
+ delete_delay = 15
+ delete_invocation = True
-class CleanMessages(metaclass=YAMLGetter):
- section = "bot"
- subsection = "clean"
- message_limit: int
+RedirectOutput = _RedirectOutput()
-class Stats(metaclass=YAMLGetter):
- section = "bot"
- subsection = "stats"
+class _DuckPond(EnvConfig):
+ EnvConfig.Config.env_prefix = "duck_pond_"
- presence_update_timeout: int
- statsd_host: str
+ threshold = 7
+ channel_blacklist: list[str] = [
+ Channels.announcements,
+ Channels.python_news,
+ Channels.python_events,
+ Channels.mailing_lists,
+ Channels.reddit,
+ Channels.duck_pond,
+ Channels.changelog,
+ Channels.staff_announcements,
+ Channels.mod_announcements,
+ Channels.admin_announcements,
+ Channels.staff_info
+ ]
-class Categories(metaclass=YAMLGetter):
- section = "guild"
- subsection = "categories"
- moderators: int
- modmail: int
- voice: int
+DuckPond = _DuckPond()
- # 2021 Summer Code Jam
- summer_code_jam: int
-
-
-class Channels(metaclass=YAMLGetter):
- section = "guild"
- subsection = "channels"
-
- announcements: int
- change_log: int
- mailing_lists: int
- python_events: int
- python_news: int
- reddit: int
-
- dev_contrib: int
- dev_core: int
- dev_log: int
-
- meta: int
- python_general: int
-
- help_system_forum: int
-
- attachment_log: int
- filter_log: int
- message_log: int
- mod_log: int
- nomination_archive: int
- user_log: int
- voice_log: int
-
- off_topic_0: int
- off_topic_1: int
- off_topic_2: int
-
- bot_commands: int
- discord_bots: int
- esoteric: int
- voice_gate: int
- code_jam_planning: int
-
- admins: int
- admin_spam: int
- defcon: int
- helpers: int
- incidents: int
- incidents_archive: int
- mod_alerts: int
- mod_meta: int
- mods: int
- nominations: int
- nomination_voting: int
- organisation: int
-
- admin_announcements: int
- mod_announcements: int
- staff_announcements: int
-
- admins_voice: int
- code_help_voice_0: int
- code_help_voice_1: int
- general_voice_0: int
- general_voice_1: int
- staff_voice: int
-
- code_help_chat_0: int
- code_help_chat_1: int
- staff_voice_chat: int
- voice_chat_0: int
- voice_chat_1: int
-
- big_brother_logs: int
-
- roles: int
-
-
-class Webhooks(metaclass=YAMLGetter):
- section = "guild"
- subsection = "webhooks"
-
- big_brother: int
- dev_log: int
- duck_pond: int
- incidents: int
- incidents_archive: int
-
-
-class Roles(metaclass=YAMLGetter):
- section = "guild"
- subsection = "roles"
- # Self-assignable roles, see the Subscribe cog
- advent_of_code: int
- announcements: int
- lovefest: int
- pyweek_announcements: int
- revival_of_code: int
- legacy_help_channels_access: int
-
- contributors: int
- help_cooldown: int
- muted: int # TODO remove when no longer relevant.
- partners: int
- python_community: int
- sprinters: int
- voice_verified: int
- video: int
-
- admins: int
- core_developers: int
- code_jam_event_team: int
- devops: int
- domain_leads: int
- events_lead: int
- helpers: int
- moderators: int
- mod_team: int
- owners: int
- project_leads: int
-
- jammers: int
-
- patreon_tier_1: int
- patreon_tier_2: int
- patreon_tier_3: int
-
-
-class Guild(metaclass=YAMLGetter):
- section = "guild"
+class _PythonNews(EnvConfig):
+ EnvConfig.Config.env_prefix = "python_news_"
- id: int
- invite: str # Discord invite, gets embedded in chat
+ channel: int = Webhooks.python_news.channel
+ webhook: int = Webhooks.python_news.id
+ mail_lists = ['python-ideas', 'python-announce-list', 'pypi-announce', 'python-dev']
+
+
+PythonNews = _PythonNews()
+
+
+class _VoiceGate(EnvConfig):
+ EnvConfig.Config.env_prefix = "voice_gate_"
+
+ bot_message_delete_delay = 10
+ minimum_activity_blocks = 3
+ minimum_days_member = 3
+ minimum_messages = 50
+ voice_ping_delete_delay = 60
+
+
+VoiceGate = _VoiceGate()
+
+
+class _Branding(EnvConfig):
+ EnvConfig.Config.env_prefix = "branding_"
+
+ cycle_frequency = 3
- moderation_categories: List[int]
- moderation_channels: List[int]
- modlog_blacklist: List[int]
- reminder_whitelist: List[int]
- moderation_roles: List[int]
- staff_roles: List[int]
+Branding = _Branding()
-class Keys(metaclass=YAMLGetter):
- section = "keys"
- github: Optional[str]
- site_api: Optional[str]
+class _VideoPermission(EnvConfig):
+ EnvConfig.Config.env_prefix = "video_permission_"
+ default_permission_duration = 5
-class URLs(metaclass=YAMLGetter):
- section = "urls"
+
+VideoPermission = _VideoPermission()
+
+
+class _Redis(EnvConfig):
+ EnvConfig.Config.env_prefix = "redis_"
+
+ host = "redis.default.svc.cluster.local"
+ password = ""
+ port = 6379
+ use_fakeredis = False # If this is True, Bot will use fakeredis.aioredis
+
+
+Redis = _Redis()
+
+
+class _CleanMessages(EnvConfig):
+ EnvConfig.Config.env_prefix = "clean_"
+
+ message_limit = 10_000
+
+
+CleanMessages = _CleanMessages()
+
+
+class _Stats(EnvConfig):
+ EnvConfig.Config.env_prefix = "stats_"
+
+ presence_update_timeout = 30
+ statsd_host = "graphite.default.svc.cluster.local"
+
+
+Stats = _Stats()
+
+
+class _Cooldowns(EnvConfig):
+ EnvConfig.Config.env_prefix = "cooldowns_"
+
+ tags = 60
+
+
+Cooldowns = _Cooldowns()
+
+
+class _Metabase(EnvConfig):
+ EnvConfig.Config.env_prefix = "metabase_"
+
+ username = ""
+ password = ""
+ base_url = "http://metabase.default.svc.cluster.local"
+ public_url = "https://metabase.pythondiscord.com"
+ max_session_age = 20_160
+
+
+Metabase = _Metabase()
+
+
+class _BaseURLs(EnvConfig):
+ EnvConfig.Config.env_prefix = "urls_"
# Snekbox endpoints
- snekbox_eval_api: str
- snekbox_311_eval_api: str
+ snekbox_eval_api = "http://snekbox-310.default.svc.cluster.local/eval"
+ snekbox_311_eval_api = "http://snekbox.default.svc.cluster.local/eval"
- # Discord API endpoints
- discord_api: str
- discord_invite_api: str
+ # Discord API
+ discord_api = "https://discordapp.com/api/v7/"
# Misc endpoints
- bot_avatar: str
- github_bot_repo: str
+ bot_avatar = "https://raw.githubusercontent.com/python-discord/branding/main/logos/logo_circle/logo_circle.png"
+
+ github_bot_repo = "https://github.com/python-discord/bot"
+
+ # Site
+ site = "pythondiscord.com"
+ site_schema = "https://"
+ site_api = "site.default.svc.cluster.local/api"
+ site_api_schema = "http://"
+
+
+BaseURLs = _BaseURLs()
+
+
+class _URLs(_BaseURLs):
+
+ # Discord API endpoints
+ discord_invite_api: str = "".join([BaseURLs.discord_api, "invites"])
# Base site vars
- connect_max_retries: int
- connect_cooldown: int
- site: str
- site_api: str
- site_schema: str
- site_api_schema: str
+ connect_max_retries = 3
+ connect_cooldown = 5
+
+ site_staff: str = "".join([BaseURLs.site_schema, BaseURLs.site, "/staff"])
+ site_paste = "".join(["paste.", BaseURLs.site])
# Site endpoints
- site_logs_view: str
- paste_service: str
+ site_logs_view: str = "".join([BaseURLs.site_schema, BaseURLs.site, "/staff/bot/logs"])
+ paste_service: str = "".join([BaseURLs.site_schema, "paste.", BaseURLs.site, "/{key}"])
-class Metabase(metaclass=YAMLGetter):
- section = "metabase"
+URLs = _URLs()
- username: Optional[str]
- password: Optional[str]
- base_url: str
- public_url: str
- max_session_age: int
+class _Emojis(EnvConfig):
+ EnvConfig.Config.env_prefix = "emojis_"
-class AntiSpam(metaclass=YAMLGetter):
- section = 'anti_spam'
+ badge_bug_hunter = "<:bug_hunter_lvl1:743882896372269137>"
+ badge_bug_hunter_level_2 = "<:bug_hunter_lvl2:743882896611344505>"
+ badge_early_supporter = "<:early_supporter:743882896909140058>"
+ badge_hypesquad = "<:hypesquad_events:743882896892362873>"
+ badge_hypesquad_balance = "<:hypesquad_balance:743882896460480625>"
+ badge_hypesquad_bravery = "<:hypesquad_bravery:743882896745693335>"
+ badge_hypesquad_brilliance = "<:hypesquad_brilliance:743882896938631248>"
+ badge_partner = "<:partner:748666453242413136>"
+ badge_staff = "<:discord_staff:743882896498098226>"
+ badge_verified_bot_developer = "<:verified_bot_dev:743882897299210310>"
+ verified_bot = "<:verified_bot:811645219220750347>"
+ bot = "<:bot:812712599464443914>"
- cache_size: int
+ defcon_shutdown = "<:defcondisabled:470326273952972810>" # noqa: E704
+ defcon_unshutdown = "<:defconenabled:470326274213150730>" # noqa: E704
+ defcon_update = "<:defconsettingsupdated:470326274082996224>" # noqa: E704
- clean_offending: bool
- ping_everyone: bool
+ failmail = "<:failmail:633660039931887616>"
+ failed_file = "<:failed_file:1073298441968562226>"
- punishment: Dict[str, Dict[str, int]]
- rules: Dict[str, Dict[str, int]]
+ incident_actioned = "<:incident_actioned:714221559279255583>"
+ incident_investigating = "<:incident_investigating:714224190928191551>"
+ incident_unactioned = "<:incident_unactioned:714223099645526026>"
+ status_dnd = "<:status_dnd:470326272082313216>"
+ status_idle = "<:status_idle:470326266625785866>"
+ status_offline = "<:status_offline:470326266537705472>"
+ status_online = "<:status_online:470326272351010816>"
-class BigBrother(metaclass=YAMLGetter):
- section = 'big_brother'
+ ducky_dave = "<:ducky_dave:742058418692423772>"
- header_message_limit: int
- log_delay: int
+ trashcan = "<:trashcan:637136429717389331>"
+ bullet = "\u2022"
+ check_mark = "\u2705"
+ cross_mark = "\u274C"
+ new = "\U0001F195"
+ pencil = "\u270F"
-class CodeBlock(metaclass=YAMLGetter):
- section = 'code_block'
+ ok_hand = ":ok_hand:"
- channel_whitelist: List[int]
- cooldown_channels: List[int]
- cooldown_seconds: int
- minimum_lines: int
+Emojis = _Emojis()
-class Free(metaclass=YAMLGetter):
- section = 'free'
- activity_timeout: int
- cooldown_per: float
- cooldown_rate: int
+class _Icons(EnvConfig):
+ EnvConfig.Config.env_prefix = "icons_"
+ crown_blurple = "https://cdn.discordapp.com/emojis/469964153289965568.png"
+ crown_green = "https://cdn.discordapp.com/emojis/469964154719961088.png"
+ crown_red = "https://cdn.discordapp.com/emojis/469964154879344640.png"
-class HelpChannels(metaclass=YAMLGetter):
- section = 'help_channels'
+ defcon_denied = "https://cdn.discordapp.com/emojis/472475292078964738.png" # noqa: E704
+ defcon_shutdown = "https://cdn.discordapp.com/emojis/470326273952972810.png" # noqa: E704
+ defcon_unshutdown = "https://cdn.discordapp.com/emojis/470326274213150730.png" # noqa: E704
+ defcon_update = "https://cdn.discordapp.com/emojis/472472638342561793.png" # noqa: E704
- enable: bool
- idle_minutes: int
- deleted_idle_minutes: int
- cmd_whitelist: List[int]
+ filtering = "https://cdn.discordapp.com/emojis/472472638594482195.png"
+ green_checkmark = "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-checkmark-dist.png"
+ green_questionmark = "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-question-mark-dist.png"
+ guild_update = "https://cdn.discordapp.com/emojis/469954765141442561.png"
-class RedirectOutput(metaclass=YAMLGetter):
- section = 'redirect_output'
+ hash_blurple = "https://cdn.discordapp.com/emojis/469950142942806017.png"
+ hash_green = "https://cdn.discordapp.com/emojis/469950144918585344.png"
+ hash_red = "https://cdn.discordapp.com/emojis/469950145413251072.png"
- delete_delay: int
- delete_invocation: bool
+ message_bulk_delete = "https://cdn.discordapp.com/emojis/469952898994929668.png"
+ message_delete = "https://cdn.discordapp.com/emojis/472472641320648704.png"
+ message_edit = "https://cdn.discordapp.com/emojis/472472638976163870.png"
+ pencil = "https://cdn.discordapp.com/emojis/470326272401211415.png"
-class PythonNews(metaclass=YAMLGetter):
- section = 'python_news'
+ questionmark = "https://cdn.discordapp.com/emojis/512367613339369475.png"
- channel: int
- webhook: int
- mail_lists: List[str]
+ remind_blurple = "https://cdn.discordapp.com/emojis/477907609215827968.png"
+ remind_green = "https://cdn.discordapp.com/emojis/477907607785570310.png"
+ remind_red = "https://cdn.discordapp.com/emojis/477907608057937930.png"
+ sign_in = "https://cdn.discordapp.com/emojis/469952898181234698.png"
+ sign_out = "https://cdn.discordapp.com/emojis/469952898089091082.png"
-class VoiceGate(metaclass=YAMLGetter):
- section = "voice_gate"
+ superstarify = "https://cdn.discordapp.com/emojis/636288153044516874.png"
+ unsuperstarify = "https://cdn.discordapp.com/emojis/636288201258172446.png"
- bot_message_delete_delay: int
- minimum_activity_blocks: int
- minimum_days_member: int
- minimum_messages: int
- voice_ping_delete_delay: int
+ token_removed = "https://cdn.discordapp.com/emojis/470326273298792469.png"
+ user_ban = "https://cdn.discordapp.com/emojis/469952898026045441.png"
+ user_timeout = "https://cdn.discordapp.com/emojis/472472640100106250.png"
+ user_unban = "https://cdn.discordapp.com/emojis/469952898692808704.png"
+ user_untimeout = "https://cdn.discordapp.com/emojis/472472639206719508.png"
+ user_update = "https://cdn.discordapp.com/emojis/469952898684551168.png"
+ user_verified = "https://cdn.discordapp.com/emojis/470326274519334936.png"
+ user_warn = "https://cdn.discordapp.com/emojis/470326274238447633.png"
-class Branding(metaclass=YAMLGetter):
- section = "branding"
+ voice_state_blue = "https://cdn.discordapp.com/emojis/656899769662439456.png"
+ voice_state_green = "https://cdn.discordapp.com/emojis/656899770094452754.png"
+ voice_state_red = "https://cdn.discordapp.com/emojis/656899769905709076.png"
- cycle_frequency: int
+Icons = _Icons()
-class Event(Enum):
- """
- Event names. This does not include every event (for example, raw
- events aren't here), but only events used in ModLog for now.
- """
- guild_channel_create = "guild_channel_create"
- guild_channel_delete = "guild_channel_delete"
- guild_channel_update = "guild_channel_update"
- guild_role_create = "guild_role_create"
- guild_role_delete = "guild_role_delete"
- guild_role_update = "guild_role_update"
- guild_update = "guild_update"
+class _Filter(EnvConfig):
+ EnvConfig.Config.env_prefix = "filters_"
- member_join = "member_join"
- member_remove = "member_remove"
- member_ban = "member_ban"
- member_unban = "member_unban"
- member_update = "member_update"
+ filter_domains = True
+ filter_everyone_ping = True
+ filter_invites = True
+ filter_zalgo = False
+ watch_regex = True
+ watch_rich_embeds = True
- message_delete = "message_delete"
- message_edit = "message_edit"
+ # Notifications are not expected for "watchlist" type filters
- voice_state_update = "voice_state_update"
+ notify_user_domains = False
+ notify_user_everyone_ping = True
+ notify_user_invites = True
+ notify_user_zalgo = False
+ offensive_msg_delete_days = 7
+ ping_everyone = True
-class VideoPermission(metaclass=YAMLGetter):
- section = "video_permission"
+ channel_whitelist = [
+ Channels.admins,
+ Channels.big_brother,
+ Channels.dev_log,
+ Channels.message_log,
+ Channels.mod_log,
+ Channels.staff_lounge
+ ]
+ role_whitelist = [
+ Roles.admins,
+ Roles.helpers,
+ Roles.moderators,
+ Roles.owners,
+ Roles.python_community,
+ Roles.partners
+ ]
- default_permission_duration: int
+Filter = _Filter()
-class ThreadArchiveTimes(Enum):
- HOUR = 60
- DAY = 1440
- THREE_DAY = 4320
- WEEK = 10080
+class _Keys(EnvConfig):
+
+ EnvConfig.Config.env_prefix = "api_keys_"
+
+ github = ""
+ site_api = ""
+
+
+Keys = _Keys()
-# Debug mode
-DEBUG_MODE: bool = _CONFIG_YAML["debug"] == "true"
-FILE_LOGS: bool = _CONFIG_YAML["file_logs"].lower() == "true"
-# Paths
BOT_DIR = os.path.dirname(__file__)
PROJECT_ROOT = os.path.abspath(os.path.join(BOT_DIR, os.pardir))
@@ -715,6 +733,7 @@ MODERATION_CATEGORIES = Guild.moderation_categories
# Git SHA for Sentry
GIT_SHA = os.environ.get("GIT_SHA", "development")
+
# Bot replies
NEGATIVE_REPLIES = [
"Noooooo!!",
diff --git a/bot/exts/backend/branding/_cog.py b/bot/exts/backend/branding/_cog.py
index ff2704835..94429c172 100644
--- a/bot/exts/backend/branding/_cog.py
+++ b/bot/exts/backend/branding/_cog.py
@@ -325,7 +325,7 @@ class Branding(commands.Cog):
# Notify guild of new event ~ this reads the information that we cached above.
if event_changed and not event.meta.is_fallback:
- await self.send_info_embed(Channels.change_log, is_notification=True)
+ await self.send_info_embed(Channels.changelog, is_notification=True)
else:
log.trace("Omitting #changelog notification. Event has not changed, or new event is fallback.")
diff --git a/bot/exts/backend/config_verifier.py b/bot/exts/backend/config_verifier.py
index 97c8869a1..84ae5ca92 100644
--- a/bot/exts/backend/config_verifier.py
+++ b/bot/exts/backend/config_verifier.py
@@ -24,12 +24,12 @@ class ConfigVerifier(Cog):
server_channel_ids = {channel.id for channel in server.channels}
invalid_channels = [
- channel_name for channel_name, channel_id in constants.Channels
+ (channel_name, channel_id) for channel_name, channel_id in constants.Channels
if channel_id not in server_channel_ids
]
if invalid_channels:
- log.warning(f"Configured channels do not exist in server: {', '.join(invalid_channels)}.")
+ log.warning(f"Configured channels do not exist in server: {invalid_channels}.")
async def setup(bot: Bot) -> None:
diff --git a/bot/exts/filters/antispam.py b/bot/exts/filters/antispam.py
index 5473889f3..70a9c00b8 100644
--- a/bot/exts/filters/antispam.py
+++ b/bot/exts/filters/antispam.py
@@ -8,7 +8,7 @@ from operator import attrgetter, itemgetter
from typing import Dict, Iterable, List, Set
import arrow
-from discord import Colour, Member, Message, MessageType, NotFound, Object, TextChannel
+from discord import Colour, Member, Message, MessageType, NotFound, TextChannel
from discord.ext.commands import Cog
from pydis_core.utils import scheduling
@@ -41,6 +41,8 @@ RULE_FUNCTION_MAPPING = {
'role_mentions': rules.apply_role_mentions,
}
+ANTI_SPAM_RULES = AntiSpamConfig.rules.dict()
+
@dataclass
class DeletionContext:
@@ -121,15 +123,13 @@ class AntiSpam(Cog):
def __init__(self, bot: Bot, validation_errors: Dict[str, str]) -> None:
self.bot = bot
self.validation_errors = validation_errors
- role_id = AntiSpamConfig.punishment['role_id']
- self.muted_role = Object(role_id)
self.expiration_date_converter = Duration()
self.message_deletion_queue = dict()
# Fetch the rule configuration with the highest rule interval.
max_interval_config = max(
- AntiSpamConfig.rules.values(),
+ ANTI_SPAM_RULES.values(),
key=itemgetter('interval')
)
self.max_interval = max_interval_config['interval']
@@ -178,8 +178,7 @@ class AntiSpam(Cog):
earliest_relevant_at = arrow.utcnow() - timedelta(seconds=self.max_interval)
relevant_messages = list(takewhile(lambda msg: msg.created_at > earliest_relevant_at, self.cache))
- for rule_name in AntiSpamConfig.rules:
- rule_config = AntiSpamConfig.rules[rule_name]
+ for rule_name, rule_config in ANTI_SPAM_RULES.items():
rule_function = RULE_FUNCTION_MAPPING[rule_name]
# Create a list of messages that were sent in the interval that the rule cares about.
@@ -229,7 +228,7 @@ class AntiSpam(Cog):
async def punish(self, msg: Message, member: Member, reason: str) -> None:
"""Punishes the given member for triggering an antispam rule."""
if not member.is_timed_out():
- remove_timeout_after = AntiSpamConfig.punishment['remove_after']
+ remove_timeout_after = AntiSpamConfig.remove_timeout_after
# Get context and make sure the bot becomes the actor of infraction by patching the `author` attributes
context = await self.bot.get_context(msg)
@@ -297,10 +296,11 @@ class AntiSpam(Cog):
self.cache.update(after)
-def validate_config(rules_: Mapping = AntiSpamConfig.rules) -> Dict[str, str]:
+def validate_config(rules_: Mapping = ANTI_SPAM_RULES) -> Dict[str, str]:
"""Validates the antispam configs."""
validation_errors = {}
for name, config in rules_.items():
+ config = config
if name not in RULE_FUNCTION_MAPPING:
log.error(
f"Unrecognized antispam rule `{name}`. "
diff --git a/bot/exts/fun/duck_pond.py b/bot/exts/fun/duck_pond.py
index 1815e54f2..fee933b47 100644
--- a/bot/exts/fun/duck_pond.py
+++ b/bot/exts/fun/duck_pond.py
@@ -21,7 +21,7 @@ class DuckPond(Cog):
def __init__(self, bot: Bot):
self.bot = bot
- self.webhook_id = constants.Webhooks.duck_pond
+ self.webhook_id = constants.Webhooks.duck_pond.id
self.webhook = None
self.ducked_messages = []
self.relay_lock = None
diff --git a/bot/exts/help_channels/_channel.py b/bot/exts/help_channels/_channel.py
index f64162006..670a10446 100644
--- a/bot/exts/help_channels/_channel.py
+++ b/bot/exts/help_channels/_channel.py
@@ -30,7 +30,7 @@ NEW_POST_ICON_URL = f"{BRANDING_REPO_RAW_URL}/main/icons/checkmark/green-checkma
CLOSED_POST_MSG = f"""
This help channel has been closed and it's no longer possible to send messages here. \
-If your question wasn't answered, feel free to create a new post in <#{constants.Channels.help_system_forum}>. \
+If your question wasn't answered, feel free to create a new post in <#{constants.Channels.python_help}>. \
To maximize your chances of getting a response, check out this guide on [asking good questions]({ASKING_GUIDE_URL}).
"""
CLOSED_POST_ICON_URL = f"{BRANDING_REPO_RAW_URL}/main/icons/zzz/zzz-dist.png"
@@ -39,7 +39,7 @@ CLOSED_POST_ICON_URL = f"{BRANDING_REPO_RAW_URL}/main/icons/zzz/zzz-dist.png"
def is_help_forum_post(channel: discord.abc.GuildChannel) -> bool:
"""Return True if `channel` is a post in the help forum."""
log.trace(f"Checking if #{channel} is a help channel.")
- return getattr(channel, "parent_id", None) == constants.Channels.help_system_forum
+ return getattr(channel, "parent_id", None) == constants.Channels.python_help
async def _close_help_post(closed_post: discord.Thread, closing_reason: _stats.ClosingReason) -> None:
diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py
index bc6bd0303..29d238a5c 100644
--- a/bot/exts/help_channels/_cog.py
+++ b/bot/exts/help_channels/_cog.py
@@ -39,9 +39,9 @@ class HelpForum(commands.Cog):
async def cog_load(self) -> None:
"""Archive all idle open posts, schedule check for later for active open posts."""
log.trace("Initialising help forum cog.")
- self.help_forum_channel = self.bot.get_channel(constants.Channels.help_system_forum)
+ self.help_forum_channel = self.bot.get_channel(constants.Channels.python_help)
if not isinstance(self.help_forum_channel, discord.ForumChannel):
- raise TypeError("Channels.help_system_forum is not a forum channel!")
+ raise TypeError("Channels.python_help is not a forum channel!")
for post in self.help_forum_channel.threads:
await _channel.maybe_archive_idle_post(post, self.scheduler, has_task=False)
diff --git a/bot/exts/help_channels/_stats.py b/bot/exts/help_channels/_stats.py
index 1075b439e..6ca40139b 100644
--- a/bot/exts/help_channels/_stats.py
+++ b/bot/exts/help_channels/_stats.py
@@ -22,7 +22,7 @@ class ClosingReason(Enum):
def report_post_count() -> None:
"""Report post count stats of the help forum."""
- help_forum = bot.instance.get_channel(constants.Channels.help_system_forum)
+ help_forum = bot.instance.get_channel(constants.Channels.python_help)
bot.instance.stats.gauge("help.total.in_use", len(help_forum.threads))
diff --git a/bot/exts/info/codeblock/_cog.py b/bot/exts/info/codeblock/_cog.py
index a431175fd..073a91a53 100644
--- a/bot/exts/info/codeblock/_cog.py
+++ b/bot/exts/info/codeblock/_cog.py
@@ -50,7 +50,7 @@ class CodeBlockCog(Cog, name="Code Block"):
The cog only detects messages in whitelisted channels. Channels may also have a cooldown on the
instructions being sent. Note all help channels are also whitelisted with cooldowns enabled.
- For configurable parameters, see the `code_block` section in config-default.py.
+ For configurable parameters, see the `_CodeBlock` class in constants.py.
"""
def __init__(self, bot: Bot):
diff --git a/bot/exts/info/doc/_markdown.py b/bot/exts/info/doc/_markdown.py
index 1b7d8232b..315adda66 100644
--- a/bot/exts/info/doc/_markdown.py
+++ b/bot/exts/info/doc/_markdown.py
@@ -1,10 +1,14 @@
+import re
from urllib.parse import urljoin
+import markdownify
from bs4.element import PageElement
-from markdownify import MarkdownConverter
+# See https://github.com/matthewwithanm/python-markdownify/issues/31
+markdownify.whitespace_re = re.compile(r"[\r\n\s\t ]+")
-class DocMarkdownConverter(MarkdownConverter):
+
+class DocMarkdownConverter(markdownify.MarkdownConverter):
"""Subclass markdownify's MarkdownCoverter to provide custom conversion methods."""
def __init__(self, *, page_url: str, **options):
@@ -56,3 +60,7 @@ class DocMarkdownConverter(MarkdownConverter):
if parent is not None and parent.name == "li":
return f"{text}\n"
return super().convert_p(el, text, convert_as_inline)
+
+ def convert_hr(self, el: PageElement, text: str, convert_as_inline: bool) -> str:
+ """Ignore `hr` tag."""
+ return ""
diff --git a/bot/exts/info/stats.py b/bot/exts/info/stats.py
index d4001a7bb..65a4b5b6c 100644
--- a/bot/exts/info/stats.py
+++ b/bot/exts/info/stats.py
@@ -41,12 +41,11 @@ class Stats(Cog):
# of them for interesting statistics to be drawn out of this.
return
- reformatted_name = message.channel.name.replace('-', '_')
-
- if CHANNEL_NAME_OVERRIDES.get(message.channel.id):
- reformatted_name = CHANNEL_NAME_OVERRIDES.get(message.channel.id)
-
- reformatted_name = "".join(char for char in reformatted_name if char in ALLOWED_CHARS)
+ channel = message.channel
+ if hasattr(channel, 'parent') and channel.parent:
+ channel = channel.parent
+ reformatted_name = CHANNEL_NAME_OVERRIDES.get(channel.id, channel.name)
+ reformatted_name = "".join(char if char in ALLOWED_CHARS else '_' for char in reformatted_name)
stat_name = f"channels.{reformatted_name}"
self.bot.stats.incr(stat_name)
diff --git a/bot/exts/info/tags.py b/bot/exts/info/tags.py
index 309f22cad..27dfa1913 100644
--- a/bot/exts/info/tags.py
+++ b/bot/exts/info/tags.py
@@ -317,6 +317,7 @@ class Tags(Cog):
return True
@app_commands.command(name="tag")
+ @app_commands.guild_only()
async def get_command(self, interaction: Interaction, *, name: Optional[str]) -> bool:
"""
If a single argument matching a group name is given, list all accessible tags from that group
diff --git a/bot/exts/moderation/incidents.py b/bot/exts/moderation/incidents.py
index ce83ca3fe..3e06cc215 100644
--- a/bot/exts/moderation/incidents.py
+++ b/bot/exts/moderation/incidents.py
@@ -329,9 +329,9 @@ class Incidents(Cog):
await self.bot.wait_until_guild_available()
try:
- self.incidents_webhook = await self.bot.fetch_webhook(Webhooks.incidents)
+ self.incidents_webhook = await self.bot.fetch_webhook(Webhooks.incidents.id)
except discord.HTTPException:
- log.error(f"Failed to fetch incidents webhook with id `{Webhooks.incidents}`.")
+ log.error(f"Failed to fetch incidents webhook with id `{Webhooks.incidents.id}`.")
async def crawl_incidents(self) -> None:
"""
@@ -389,7 +389,7 @@ class Incidents(Cog):
embed, attachment_file = await make_embed(incident, outcome, actioned_by)
try:
- webhook = await self.bot.fetch_webhook(Webhooks.incidents_archive)
+ webhook = await self.bot.fetch_webhook(Webhooks.incidents_archive.id)
await webhook.send(
embed=embed,
username=sub_clyde(incident.author.display_name),
diff --git a/bot/exts/moderation/watchchannels/_watchchannel.py b/bot/exts/moderation/watchchannels/_watchchannel.py
index 2871eb5de..bc70a8c1d 100644
--- a/bot/exts/moderation/watchchannels/_watchchannel.py
+++ b/bot/exts/moderation/watchchannels/_watchchannel.py
@@ -53,7 +53,7 @@ class WatchChannel(metaclass=CogABCMeta):
) -> None:
self.bot = bot
- self.destination = destination # E.g., Channels.big_brother_logs
+ self.destination = destination # E.g., Channels.big_brother
self.webhook_id = webhook_id # E.g., Webhooks.big_brother
self.api_endpoint = api_endpoint # E.g., 'bot/infractions'
self.api_default_params = api_default_params # E.g., {'active': 'true', 'type': 'watch'}
diff --git a/bot/exts/moderation/watchchannels/bigbrother.py b/bot/exts/moderation/watchchannels/bigbrother.py
index 4a746edff..fe652cc5b 100644
--- a/bot/exts/moderation/watchchannels/bigbrother.py
+++ b/bot/exts/moderation/watchchannels/bigbrother.py
@@ -19,8 +19,8 @@ class BigBrother(WatchChannel, Cog, name="Big Brother"):
def __init__(self, bot: Bot) -> None:
super().__init__(
bot,
- destination=Channels.big_brother_logs,
- webhook_id=Webhooks.big_brother,
+ destination=Channels.big_brother,
+ webhook_id=Webhooks.big_brother.id,
api_endpoint='bot/infractions',
api_default_params={'active': 'true', 'type': 'watch', 'ordering': '-inserted_at', 'limit': 10_000},
logger=log
diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py
index b9e76b60f..efc9c7a4d 100644
--- a/bot/exts/recruitment/talentpool/_review.py
+++ b/bot/exts/recruitment/talentpool/_review.py
@@ -292,7 +292,7 @@ class Reviewer:
else:
embed_title = f"Vote for `{user_id}`"
- channel = self.bot.get_channel(Channels.nomination_archive)
+ channel = self.bot.get_channel(Channels.nomination_voting_archive)
for number, part in enumerate(
textwrap.wrap(embed_content, width=MAX_EMBED_SIZE, replace_whitespace=False, placeholder="")
):
diff --git a/bot/exts/utils/snekbox/__init__.py b/bot/exts/utils/snekbox/__init__.py
new file mode 100644
index 000000000..cd1d3b059
--- /dev/null
+++ b/bot/exts/utils/snekbox/__init__.py
@@ -0,0 +1,12 @@
+from bot.bot import Bot
+from bot.exts.utils.snekbox._cog import CodeblockConverter, Snekbox
+from bot.exts.utils.snekbox._eval import EvalJob, EvalResult
+
+__all__ = ("CodeblockConverter", "Snekbox", "EvalJob", "EvalResult")
+
+
+async def setup(bot: Bot) -> None:
+ """Load the Snekbox cog."""
+ # Defer import to reduce side effects from importing the codeblock package.
+ from bot.exts.utils.snekbox._cog import Snekbox
+ await bot.add_cog(Snekbox(bot))
diff --git a/bot/exts/utils/snekbox.py b/bot/exts/utils/snekbox/_cog.py
index ddcbe01fa..b48fcf592 100644
--- a/bot/exts/utils/snekbox.py
+++ b/bot/exts/utils/snekbox/_cog.py
@@ -1,11 +1,12 @@
+from __future__ import annotations
+
import asyncio
import contextlib
import re
from functools import partial
from operator import attrgetter
-from signal import Signals
from textwrap import dedent
-from typing import Literal, Optional, Tuple
+from typing import Literal, NamedTuple, Optional, TYPE_CHECKING
from discord import AllowedMentions, HTTPException, Interaction, Message, NotFound, Reaction, User, enums, ui
from discord.ext.commands import Cog, Command, Context, Converter, command, guild_only
@@ -13,14 +14,21 @@ from pydis_core.utils import interactions
from pydis_core.utils.regex import FORMATTED_CODE_REGEX, RAW_CODE_REGEX
from bot.bot import Bot
-from bot.constants import Channels, MODERATION_ROLES, Roles, URLs
+from bot.constants import Channels, Emojis, Filter, MODERATION_ROLES, Roles, URLs
from bot.decorators import redirect_output
+from bot.exts.events.code_jams._channels import CATEGORY_NAME as JAM_CATEGORY_NAME
+from bot.exts.filters.antimalware import TXT_LIKE_FILES
from bot.exts.help_channels._channel import is_help_forum_post
+from bot.exts.utils.snekbox._eval import EvalJob, EvalResult
+from bot.exts.utils.snekbox._io import FileAttachment
from bot.log import get_logger
from bot.utils import send_to_paste_service
from bot.utils.lock import LockedResourceError, lock_arg
from bot.utils.services import PasteTooLongError, PasteUploadError
+if TYPE_CHECKING:
+ from bot.exts.filters.filtering import Filtering
+
log = get_logger(__name__)
ESCAPE_REGEX = re.compile("[`\u202E\u200B]{3,}")
@@ -68,17 +76,23 @@ if not hasattr(sys, "_setup_finished"):
"""
MAX_PASTE_LENGTH = 10_000
+# Max to display in a codeblock before sending to a paste service
+# This also applies to text files
+MAX_OUTPUT_BLOCK_LINES = 10
+MAX_OUTPUT_BLOCK_CHARS = 1000
# The Snekbox commands' whitelists and blacklists.
NO_SNEKBOX_CHANNELS = (Channels.python_general,)
NO_SNEKBOX_CATEGORIES = ()
SNEKBOX_ROLES = (Roles.helpers, Roles.moderators, Roles.admins, Roles.owners, Roles.python_community, Roles.partners)
-SIGKILL = 9
-
REDO_EMOJI = '\U0001f501' # :repeat:
REDO_TIMEOUT = 30
+PythonVersion = Literal["3.10", "3.11"]
+
+FilteredFiles = NamedTuple("FilteredFiles", [("allowed", list[FileAttachment]), ("blocked", list[FileAttachment])])
+
class CodeblockConverter(Converter):
"""Attempts to extract code from a codeblock, if provided."""
@@ -122,21 +136,17 @@ class PythonVersionSwitcherButton(ui.Button):
def __init__(
self,
- job_name: str,
- version_to_switch_to: Literal["3.10", "3.11"],
- snekbox_cog: "Snekbox",
+ version_to_switch_to: PythonVersion,
+ snekbox_cog: Snekbox,
ctx: Context,
- code: str,
- args: Optional[list[str]] = None
+ job: EvalJob,
) -> None:
self.version_to_switch_to = version_to_switch_to
super().__init__(label=f"Run in {self.version_to_switch_to}", style=enums.ButtonStyle.primary)
self.snekbox_cog = snekbox_cog
self.ctx = ctx
- self.job_name = job_name
- self.code = code
- self.args = args
+ self.job = job
async def callback(self, interaction: Interaction) -> None:
"""
@@ -149,13 +159,11 @@ class PythonVersionSwitcherButton(ui.Button):
await interaction.response.defer()
with contextlib.suppress(NotFound):
- # Suppress this delete to cover the case where a user re-runs code and very quickly clicks the button.
+ # Suppress delete to cover the case where a user re-runs code and very quickly clicks the button.
# The log arg on send_job will stop the actual job from running.
await interaction.message.delete()
- await self.snekbox_cog.run_job(
- self.job_name, self.ctx, self.version_to_switch_to, self.code, args=self.args
- )
+ await self.snekbox_cog.run_job(self.ctx, self.job.as_version(self.version_to_switch_to))
class Snekbox(Cog):
@@ -167,13 +175,12 @@ class Snekbox(Cog):
def build_python_version_switcher_view(
self,
- job_name: str,
- current_python_version: Literal["3.10", "3.11"],
+ current_python_version: PythonVersion,
ctx: Context,
- code: str,
- args: Optional[list[str]] = None
- ) -> None:
+ job: EvalJob,
+ ) -> interactions.ViewWithUserAndRoleCheck:
"""Return a view that allows the user to change what version of Python their code is run on."""
+ alt_python_version: PythonVersion
if current_python_version == "3.10":
alt_python_version = "3.11"
else:
@@ -183,33 +190,25 @@ class Snekbox(Cog):
allowed_users=(ctx.author.id,),
allowed_roles=MODERATION_ROLES,
)
- view.add_item(PythonVersionSwitcherButton(job_name, alt_python_version, self, ctx, code, args))
+ view.add_item(PythonVersionSwitcherButton(alt_python_version, self, ctx, job))
view.add_item(interactions.DeleteMessageButton())
return view
- async def post_job(
- self,
- code: str,
- python_version: Literal["3.10", "3.11"],
- *,
- args: Optional[list[str]] = None
- ) -> dict:
+ async def post_job(self, job: EvalJob) -> EvalResult:
"""Send a POST request to the Snekbox API to evaluate code and return the results."""
- if python_version == "3.10":
+ if job.version == "3.10":
url = URLs.snekbox_eval_api
else:
url = URLs.snekbox_311_eval_api
- data = {"input": code}
-
- if args is not None:
- data["args"] = args
+ data = job.to_dict()
async with self.bot.http_session.post(url, json=data, raise_for_status=True) as resp:
- return await resp.json()
+ return EvalResult.from_dict(await resp.json())
- async def upload_output(self, output: str) -> Optional[str]:
+ @staticmethod
+ async def upload_output(output: str) -> Optional[str]:
"""Upload the job's output to a paste service and return a URL to it if successful."""
log.trace("Uploading full output to paste service...")
@@ -221,59 +220,27 @@ class Snekbox(Cog):
return "unable to upload"
@staticmethod
- def prepare_timeit_input(codeblocks: list[str]) -> tuple[str, list[str]]:
+ def prepare_timeit_input(codeblocks: list[str]) -> list[str]:
"""
- Join the codeblocks into a single string, then return the code and the arguments in a tuple.
+ Join the codeblocks into a single string, then return the arguments in a list.
If there are multiple codeblocks, insert the first one into the wrapped setup code.
"""
args = ["-m", "timeit"]
- setup = ""
- if len(codeblocks) > 1:
- setup = codeblocks.pop(0)
-
+ setup_code = codeblocks.pop(0) if len(codeblocks) > 1 else ""
code = "\n".join(codeblocks)
- args.extend(["-s", TIMEIT_SETUP_WRAPPER.format(setup=setup)])
-
- return code, args
-
- @staticmethod
- def get_results_message(results: dict, job_name: str, python_version: Literal["3.10", "3.11"]) -> Tuple[str, str]:
- """Return a user-friendly message and error corresponding to the process's return code."""
- stdout, returncode = results["stdout"], results["returncode"]
- msg = f"Your {python_version} {job_name} job has completed with return code {returncode}"
- error = ""
-
- if returncode is None:
- msg = f"Your {python_version} {job_name} job has failed"
- error = stdout.strip()
- elif returncode == 128 + SIGKILL:
- msg = f"Your {python_version} {job_name} job timed out or ran out of memory"
- elif returncode == 255:
- msg = f"Your {python_version} {job_name} job has failed"
- error = "A fatal NsJail error occurred"
- else:
- # Try to append signal's name if one exists
- try:
- name = Signals(returncode - 128).name
- msg = f"{msg} ({name})"
- except ValueError:
- pass
-
- return msg, error
+ args.extend(["-s", TIMEIT_SETUP_WRAPPER.format(setup=setup_code), code])
+ return args
- @staticmethod
- def get_status_emoji(results: dict) -> str:
- """Return an emoji corresponding to the status code or lack of output in result."""
- if not results["stdout"].strip(): # No output
- return ":warning:"
- elif results["returncode"] == 0: # No error
- return ":white_check_mark:"
- else: # Exception
- return ":x:"
-
- async def format_output(self, output: str) -> Tuple[str, Optional[str]]:
+ async def format_output(
+ self,
+ output: str,
+ max_lines: int = MAX_OUTPUT_BLOCK_LINES,
+ max_chars: int = MAX_OUTPUT_BLOCK_CHARS,
+ line_nums: bool = True,
+ output_default: str = "[No output]",
+ ) -> tuple[str, str | None]:
"""
Format the output and return a tuple of the formatted output and a URL to the full output.
@@ -295,96 +262,182 @@ class Snekbox(Cog):
return "Code block escape attempt detected; will not output result", paste_link
truncated = False
- lines = output.count("\n")
+ lines = output.splitlines()
- if lines > 0:
- output = [f"{i:03d} | {line}" for i, line in enumerate(output.split('\n'), 1)]
- output = output[:11] # Limiting to only 11 lines
- output = "\n".join(output)
+ if len(lines) > 1:
+ if line_nums:
+ lines = [f"{i:03d} | {line}" for i, line in enumerate(lines, 1)]
+ lines = lines[:max_lines+1] # Limiting to max+1 lines
+ output = "\n".join(lines)
- if lines > 10:
+ if len(lines) > max_lines:
truncated = True
- if len(output) >= 1000:
- output = f"{output[:1000]}\n... (truncated - too long, too many lines)"
+ if len(output) >= max_chars:
+ output = f"{output[:max_chars]}\n... (truncated - too long, too many lines)"
else:
output = f"{output}\n... (truncated - too many lines)"
- elif len(output) >= 1000:
+ elif len(output) >= max_chars:
truncated = True
- output = f"{output[:1000]}\n... (truncated - too long)"
+ output = f"{output[:max_chars]}\n... (truncated - too long)"
if truncated:
paste_link = await self.upload_output(original_output)
- output = output or "[No output]"
+ if output_default and not output:
+ output = output_default
return output, paste_link
+ def get_extensions_whitelist(self) -> set[str]:
+ """Return a set of whitelisted file extensions."""
+ return set(self.bot.filter_list_cache['FILE_FORMAT.True'].keys()) | TXT_LIKE_FILES
+
+ def _filter_files(self, ctx: Context, files: list[FileAttachment]) -> FilteredFiles:
+ """Filter to restrict files to allowed extensions. Return a named tuple of allowed and blocked files lists."""
+ # Check if user is staff, if is, return
+ # Since we only care that roles exist to iterate over, check for the attr rather than a User/Member instance
+ if hasattr(ctx.author, "roles") and any(role.id in Filter.role_whitelist for role in ctx.author.roles):
+ return FilteredFiles(files, [])
+ # Ignore code jam channels
+ if getattr(ctx.channel, "category", None) and ctx.channel.category.name == JAM_CATEGORY_NAME:
+ return FilteredFiles(files, [])
+
+ # Get whitelisted extensions
+ whitelist = self.get_extensions_whitelist()
+
+ # Filter files into allowed and blocked
+ blocked = []
+ allowed = []
+ for file in files:
+ if file.suffix in whitelist:
+ allowed.append(file)
+ else:
+ blocked.append(file)
+
+ if blocked:
+ blocked_str = ", ".join(f.suffix for f in blocked)
+ log.info(
+ f"User '{ctx.author}' ({ctx.author.id}) uploaded blacklisted file(s) in eval: {blocked_str}",
+ extra={"attachment_list": [f.path for f in files]}
+ )
+
+ return FilteredFiles(allowed, blocked)
+
@lock_arg("snekbox.send_job", "ctx", attrgetter("author.id"), raise_error=True)
- async def send_job(
- self,
- ctx: Context,
- python_version: Literal["3.10", "3.11"],
- code: str,
- *,
- args: Optional[list[str]] = None,
- job_name: str
- ) -> Message:
+ async def send_job(self, ctx: Context, job: EvalJob) -> Message:
"""
Evaluate code, format it, and send the output to the corresponding channel.
Return the bot response.
"""
async with ctx.typing():
- results = await self.post_job(code, python_version, args=args)
- msg, error = self.get_results_message(results, job_name, python_version)
+ result = await self.post_job(job)
+ msg = result.get_message(job)
+ error = result.error_message
if error:
output, paste_link = error, None
else:
log.trace("Formatting output...")
- output, paste_link = await self.format_output(results["stdout"])
+ output, paste_link = await self.format_output(result.stdout)
- warning_message = ""
+ msg = f"{ctx.author.mention} {result.status_emoji} {msg}.\n"
# This is done to make sure the last line of output contains the error
# and the error is not manually printed by the author with a syntax error.
- if results["stdout"].rstrip().endswith("EOFError: EOF when reading a line") and results["returncode"] == 1:
- warning_message += ":warning: Note: `input` is not supported by the bot :warning:\n\n"
+ if result.stdout.rstrip().endswith("EOFError: EOF when reading a line") and result.returncode == 1:
+ msg += ":warning: Note: `input` is not supported by the bot :warning:\n\n"
+
+ # Skip output if it's empty and there are file uploads
+ if result.stdout or not result.has_files:
+ msg += f"\n```\n{output}\n```"
- icon = self.get_status_emoji(results)
- msg = f"{ctx.author.mention} {icon} {msg}.\n\n{warning_message}```\n{output}\n```"
if paste_link:
- msg = f"{msg}\nFull output: {paste_link}"
+ msg += f"\nFull output: {paste_link}"
+
+ # Additional files error message after output
+ if files_error := result.files_error_message:
+ msg += f"\n{files_error}"
# Collect stats of job fails + successes
- if icon == ":x:":
+ if result.returncode != 0:
self.bot.stats.incr("snekbox.python.fail")
else:
self.bot.stats.incr("snekbox.python.success")
- filter_cog = self.bot.get_cog("Filtering")
- filter_triggered = False
- if filter_cog:
- filter_triggered = await filter_cog.filter_snekbox_output(msg, ctx.message)
- if filter_triggered:
- response = await ctx.send("Attempt to circumvent filter detected. Moderator team has been alerted.")
- else:
- allowed_mentions = AllowedMentions(everyone=False, roles=False, users=[ctx.author])
- view = self.build_python_version_switcher_view(job_name, python_version, ctx, code, args)
- response = await ctx.send(msg, allowed_mentions=allowed_mentions, view=view)
- view.message = response
-
- log.info(f"{ctx.author}'s {job_name} job had a return code of {results['returncode']}")
+ # Filter file extensions
+ allowed, blocked = self._filter_files(ctx, result.files)
+ # Also scan failed files for blocked extensions
+ failed_files = [FileAttachment(name, b"") for name in result.failed_files]
+ blocked.extend(self._filter_files(ctx, failed_files).blocked)
+ # Add notice if any files were blocked
+ if blocked:
+ blocked_sorted = sorted(set(f.suffix for f in blocked))
+ # Only no extension
+ if len(blocked_sorted) == 1 and blocked_sorted[0] == "":
+ blocked_msg = "Files with no extension can't be uploaded."
+ # Both
+ elif "" in blocked_sorted:
+ blocked_str = ", ".join(ext for ext in blocked_sorted if ext)
+ blocked_msg = (
+ f"Files with no extension or disallowed extensions can't be uploaded: **{blocked_str}**"
+ )
+ else:
+ blocked_str = ", ".join(blocked_sorted)
+ blocked_msg = f"Files with disallowed extensions can't be uploaded: **{blocked_str}**"
+
+ msg += f"\n{Emojis.failed_file} {blocked_msg}"
+
+ # Split text files
+ text_files = [f for f in allowed if f.suffix in TXT_LIKE_FILES]
+ # Inline until budget, then upload to paste service
+ # Budget is shared with stdout, so subtract what we've already used
+ budget_lines = MAX_OUTPUT_BLOCK_LINES - (output.count("\n") + 1)
+ budget_chars = MAX_OUTPUT_BLOCK_CHARS - len(output)
+ for file in text_files:
+ file_text = file.content.decode("utf-8", errors="replace") or "[Empty]"
+ # Override to always allow 1 line and <= 50 chars, since this is less than a link
+ if len(file_text) <= 50 and not file_text.count("\n"):
+ msg += f"\n`{file.name}`\n```\n{file_text}\n```"
+ # otherwise, use budget
+ else:
+ format_text, link_text = await self.format_output(
+ file_text,
+ budget_lines,
+ budget_chars,
+ line_nums=False,
+ output_default="[Empty]"
+ )
+ # With any link, use it (don't use budget)
+ if link_text:
+ msg += f"\n`{file.name}`\n{link_text}"
+ else:
+ msg += f"\n`{file.name}`\n```\n{format_text}\n```"
+ budget_lines -= format_text.count("\n") + 1
+ budget_chars -= len(file_text)
+
+ filter_cog: Filtering | None = self.bot.get_cog("Filtering")
+ if filter_cog and (await filter_cog.filter_snekbox_output(msg, ctx.message)):
+ return await ctx.send("Attempt to circumvent filter detected. Moderator team has been alerted.")
+
+ # Upload remaining non-text files
+ files = [f.to_file() for f in allowed if f not in text_files]
+ allowed_mentions = AllowedMentions(everyone=False, roles=False, users=[ctx.author])
+ view = self.build_python_version_switcher_view(job.version, ctx, job)
+ response = await ctx.send(msg, allowed_mentions=allowed_mentions, view=view, files=files)
+ view.message = response
+
+ log.info(f"{ctx.author}'s {job.name} job had a return code of {result.returncode}")
return response
async def continue_job(
self, ctx: Context, response: Message, job_name: str
- ) -> tuple[Optional[str], Optional[list[str]]]:
+ ) -> EvalJob | None:
"""
Check if the job's session should continue.
- If the code is to be re-evaluated, return the new code, and the args if the command is the timeit command.
- Otherwise return (None, None) if the job's session should be terminated.
+ If the code is to be re-evaluated, return the new EvalJob.
+ Otherwise, return None if the job's session should be terminated.
"""
_predicate_message_edit = partial(predicate_message_edit, ctx)
_predicate_emoji_reaction = partial(predicate_emoji_reaction, ctx)
@@ -406,7 +459,7 @@ class Snekbox(Cog):
# Ensure the response that's about to be edited is still the most recent.
# This could have already been updated via a button press to switch to an alt Python version.
if self.jobs[ctx.message.id] != response.id:
- return None, None
+ return None
code = await self.get_code(new_message, ctx.command)
with contextlib.suppress(HTTPException):
@@ -414,21 +467,21 @@ class Snekbox(Cog):
await response.delete()
if code is None:
- return None, None
+ return None
except asyncio.TimeoutError:
with contextlib.suppress(HTTPException):
await ctx.message.clear_reaction(REDO_EMOJI)
- return None, None
+ return None
codeblocks = await CodeblockConverter.convert(ctx, code)
if job_name == "timeit":
- return self.prepare_timeit_input(codeblocks)
+ return EvalJob(self.prepare_timeit_input(codeblocks))
else:
- return "\n".join(codeblocks), None
+ return EvalJob.from_code("\n".join(codeblocks))
- return None, None
+ return None
async def get_code(self, message: Message, command: Command) -> Optional[str]:
"""
@@ -452,12 +505,8 @@ class Snekbox(Cog):
async def run_job(
self,
- job_name: str,
ctx: Context,
- python_version: Literal["3.10", "3.11"],
- code: str,
- *,
- args: Optional[list[str]] = None,
+ job: EvalJob,
) -> None:
"""Handles checks, stats and re-evaluation of a snekbox job."""
if Roles.helpers in (role.id for role in ctx.author.roles):
@@ -472,11 +521,11 @@ class Snekbox(Cog):
else:
self.bot.stats.incr("snekbox_usages.channels.topical")
- log.info(f"Received code from {ctx.author} for evaluation:\n{code}")
+ log.info(f"Received code from {ctx.author} for evaluation:\n{job}")
while True:
try:
- response = await self.send_job(ctx, python_version, code, args=args, job_name=job_name)
+ response = await self.send_job(ctx, job)
except LockedResourceError:
await ctx.send(
f"{ctx.author.mention} You've already got a job running - "
@@ -489,10 +538,10 @@ class Snekbox(Cog):
# This can happen when a button is pressed and then original code is edited and re-run.
self.jobs[ctx.message.id] = response.id
- code, args = await self.continue_job(ctx, response, job_name)
- if not code:
+ job = await self.continue_job(ctx, response, job.name)
+ if not job:
break
- log.info(f"Re-evaluating code from message {ctx.message.id}:\n{code}")
+ log.info(f"Re-evaluating code from message {ctx.message.id}:\n{job}")
@command(name="eval", aliases=("e",), usage="[python_version] <code, ...>")
@guild_only()
@@ -506,29 +555,32 @@ class Snekbox(Cog):
async def eval_command(
self,
ctx: Context,
- python_version: Optional[Literal["3.10", "3.11"]],
+ python_version: PythonVersion | None,
*,
code: CodeblockConverter
) -> None:
"""
Run Python code and get the results.
- This command supports multiple lines of code, including code wrapped inside a formatted code
- block. Code can be re-evaluated by editing the original message within 10 seconds and
+ This command supports multiple lines of code, including formatted code blocks.
+ Code can be re-evaluated by editing the original message within 10 seconds and
clicking the reaction that subsequently appears.
+ The starting working directory `/home`, is a writeable temporary file system.
+ Files created, excluding names with leading underscores, will be uploaded in the response.
+
If multiple codeblocks are in a message, all of them will be joined and evaluated,
- ignoring the text outside of them.
+ ignoring the text outside them.
- By default your code is run on Python's 3.11 beta release, to assist with testing. If you
- run into issues related to this Python version, you can request the bot to use Python
- 3.10 by specifying the `python_version` arg and setting it to `3.10`.
+ By default, your code is run on Python 3.11. A `python_version` arg of `3.10` can also be specified.
We've done our best to make this sandboxed, but do let us know if you manage to find an
issue with it!
"""
+ code: list[str]
python_version = python_version or "3.11"
- await self.run_job("eval", ctx, python_version, "\n".join(code))
+ job = EvalJob.from_code("\n".join(code)).as_version(python_version)
+ await self.run_job(ctx, job)
@command(name="timeit", aliases=("ti",), usage="[python_version] [setup_code] <code, ...>")
@guild_only()
@@ -542,7 +594,7 @@ class Snekbox(Cog):
async def timeit_command(
self,
ctx: Context,
- python_version: Optional[Literal["3.10", "3.11"]],
+ python_version: PythonVersion | None,
*,
code: CodeblockConverter
) -> None:
@@ -556,17 +608,17 @@ class Snekbox(Cog):
If multiple formatted codeblocks are provided, the first one will be the setup code, which will
not be timed. The remaining codeblocks will be joined together and timed.
- By default your code is run on Python's 3.11 beta release, to assist with testing. If you
- run into issues related to this Python version, you can request the bot to use Python
- 3.10 by specifying the `python_version` arg and setting it to `3.10`.
+ By default, your code is run on Python 3.11. A `python_version` arg of `3.10` can also be specified.
We've done our best to make this sandboxed, but do let us know if you manage to find an
issue with it!
"""
+ code: list[str]
python_version = python_version or "3.11"
- code, args = self.prepare_timeit_input(code)
+ args = self.prepare_timeit_input(code)
+ job = EvalJob(args, version=python_version, name="timeit")
- await self.run_job("timeit", ctx, python_version, code=code, args=args)
+ await self.run_job(ctx, job)
def predicate_message_edit(ctx: Context, old_msg: Message, new_msg: Message) -> bool:
@@ -577,8 +629,3 @@ def predicate_message_edit(ctx: Context, old_msg: Message, new_msg: Message) ->
def predicate_emoji_reaction(ctx: Context, reaction: Reaction, user: User) -> bool:
"""Return True if the reaction REDO_EMOJI was added by the context message author on this message."""
return reaction.message.id == ctx.message.id and user.id == ctx.author.id and str(reaction) == REDO_EMOJI
-
-
-async def setup(bot: Bot) -> None:
- """Load the Snekbox cog."""
- await bot.add_cog(Snekbox(bot))
diff --git a/bot/exts/utils/snekbox/_eval.py b/bot/exts/utils/snekbox/_eval.py
new file mode 100644
index 000000000..2f61b5924
--- /dev/null
+++ b/bot/exts/utils/snekbox/_eval.py
@@ -0,0 +1,183 @@
+from __future__ import annotations
+
+import contextlib
+from dataclasses import dataclass, field
+from signal import Signals
+from typing import TYPE_CHECKING
+
+from discord.utils import escape_markdown, escape_mentions
+
+from bot.constants import Emojis
+from bot.exts.utils.snekbox._io import FILE_COUNT_LIMIT, FILE_SIZE_LIMIT, FileAttachment, sizeof_fmt
+from bot.log import get_logger
+
+if TYPE_CHECKING:
+ from bot.exts.utils.snekbox._cog import PythonVersion
+
+log = get_logger(__name__)
+
+SIGKILL = 9
+
+
+@dataclass(frozen=True)
+class EvalJob:
+ """Job to be evaluated by snekbox."""
+
+ args: list[str]
+ files: list[FileAttachment] = field(default_factory=list)
+ name: str = "eval"
+ version: PythonVersion = "3.11"
+
+ @classmethod
+ def from_code(cls, code: str, path: str = "main.py") -> EvalJob:
+ """Create an EvalJob from a code string."""
+ return cls(
+ args=[path],
+ files=[FileAttachment(path, code.encode())],
+ )
+
+ def as_version(self, version: PythonVersion) -> EvalJob:
+ """Return a copy of the job with a different Python version."""
+ return EvalJob(
+ args=self.args,
+ files=self.files,
+ name=self.name,
+ version=version,
+ )
+
+ def to_dict(self) -> dict[str, list[str | dict[str, str]]]:
+ """Convert the job to a dict."""
+ return {
+ "args": self.args,
+ "files": [file.to_dict() for file in self.files],
+ }
+
+
+@dataclass(frozen=True)
+class EvalResult:
+ """The result of an eval job."""
+
+ stdout: str
+ returncode: int | None
+ files: list[FileAttachment] = field(default_factory=list)
+ failed_files: list[str] = field(default_factory=list)
+
+ @property
+ def has_output(self) -> bool:
+ """True if the result has any output (stdout, files, or failed files)."""
+ return bool(self.stdout.strip() or self.files or self.failed_files)
+
+ @property
+ def has_files(self) -> bool:
+ """True if the result has any files or failed files."""
+ return bool(self.files or self.failed_files)
+
+ @property
+ def status_emoji(self) -> str:
+ """Return an emoji corresponding to the status code or lack of output in result."""
+ if not self.has_output:
+ return ":warning:"
+ elif self.returncode == 0: # No error
+ return ":white_check_mark:"
+ else: # Exception
+ return ":x:"
+
+ @property
+ def error_message(self) -> str:
+ """Return an error message corresponding to the process's return code."""
+ error = ""
+ if self.returncode is None:
+ error = self.stdout.strip()
+ elif self.returncode == 255:
+ error = "A fatal NsJail error occurred"
+ return error
+
+ @property
+ def files_error_message(self) -> str:
+ """Return an error message corresponding to the failed files."""
+ if not self.failed_files:
+ return ""
+
+ failed_files = f"({self.get_failed_files_str()})"
+
+ n_failed = len(self.failed_files)
+ s_upload = "uploads" if n_failed > 1 else "upload"
+
+ msg = f"{Emojis.failed_file} {n_failed} file {s_upload} {failed_files} failed"
+
+ # Exceeded file count limit
+ if (n_failed + len(self.files)) > FILE_COUNT_LIMIT:
+ s_it = "they" if n_failed > 1 else "it"
+ msg += f" as {s_it} exceeded the {FILE_COUNT_LIMIT} file limit."
+ # Exceeded file size limit
+ else:
+ s_each_file = "each file's" if n_failed > 1 else "its file"
+ msg += f" because {s_each_file} size exceeds {sizeof_fmt(FILE_SIZE_LIMIT)}."
+
+ return msg
+
+ def get_failed_files_str(self, char_max: int = 85) -> str:
+ """
+ Return a string containing the names of failed files, truncated char_max.
+
+ Will truncate on whole file names if less than 3 characters remaining.
+ """
+ names = []
+ for file in self.failed_files:
+ # Only attempt to truncate name if more than 3 chars remaining
+ if char_max < 3:
+ names.append("...")
+ break
+
+ if len(file) > char_max:
+ names.append(file[:char_max] + "...")
+ break
+ char_max -= len(file)
+ names.append(file)
+
+ text = ", ".join(names)
+ # Since the file names are provided by user
+ text = escape_markdown(text)
+ text = escape_mentions(text)
+ return text
+
+ def get_message(self, job: EvalJob) -> str:
+ """Return a user-friendly message corresponding to the process's return code."""
+ msg = f"Your {job.version} {job.name} job"
+
+ if self.returncode is None:
+ msg += " has failed"
+ elif self.returncode == 128 + SIGKILL:
+ msg += " timed out or ran out of memory"
+ elif self.returncode == 255:
+ msg += " has failed"
+ else:
+ msg += f" has completed with return code {self.returncode}"
+ # Try to append signal's name if one exists
+ with contextlib.suppress(ValueError):
+ name = Signals(self.returncode - 128).name
+ msg += f" ({name})"
+
+ return msg
+
+ @classmethod
+ def from_dict(cls, data: dict[str, str | int | list[dict[str, str]]]) -> EvalResult:
+ """Create an EvalResult from a dict."""
+ res = cls(
+ stdout=data["stdout"],
+ returncode=data["returncode"],
+ )
+
+ files = iter(data["files"])
+ for i, file in enumerate(files):
+ # Limit to FILE_COUNT_LIMIT files
+ if i >= FILE_COUNT_LIMIT:
+ res.failed_files.extend(file["path"] for file in files)
+ break
+ try:
+ res.files.append(FileAttachment.from_dict(file))
+ except ValueError as e:
+ log.info(f"Failed to parse file from snekbox response: {e}")
+ res.failed_files.append(file["path"])
+
+ return res
diff --git a/bot/exts/utils/snekbox/_io.py b/bot/exts/utils/snekbox/_io.py
new file mode 100644
index 000000000..9be396335
--- /dev/null
+++ b/bot/exts/utils/snekbox/_io.py
@@ -0,0 +1,102 @@
+"""I/O File protocols for snekbox."""
+from __future__ import annotations
+
+from base64 import b64decode, b64encode
+from dataclasses import dataclass
+from io import BytesIO
+from pathlib import PurePosixPath
+
+import regex
+from discord import File
+
+# Note discord bot upload limit is 8 MiB per file,
+# or 50 MiB for lvl 2 boosted servers
+FILE_SIZE_LIMIT = 8 * 1024 * 1024
+
+# Discord currently has a 10-file limit per message
+FILE_COUNT_LIMIT = 10
+
+
+# ANSI escape sequences
+RE_ANSI = regex.compile(r"\\u.*\[(.*?)m")
+# Characters with a leading backslash
+RE_BACKSLASH = regex.compile(r"\\.")
+# Discord disallowed file name characters
+RE_DISCORD_FILE_NAME_DISALLOWED = regex.compile(r"[^a-zA-Z0-9._-]+")
+
+
+def sizeof_fmt(num: int | float, suffix: str = "B") -> str:
+ """Return a human-readable file size."""
+ num = float(num)
+ for unit in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"):
+ if abs(num) < 1024:
+ num_str = f"{int(num)}" if num.is_integer() else f"{num:3.1f}"
+ return f"{num_str} {unit}{suffix}"
+ num /= 1024
+ num_str = f"{int(num)}" if num.is_integer() else f"{num:3.1f}"
+ return f"{num_str} Yi{suffix}"
+
+
+def normalize_discord_file_name(name: str) -> str:
+ """Return a normalized valid discord file name."""
+ # Discord file names only allow A-Z, a-z, 0-9, underscores, dashes, and dots
+ # https://discord.com/developers/docs/reference#uploading-files
+ # Server will remove any other characters, but we'll get a 400 error for \ escaped chars
+ name = RE_ANSI.sub("_", name)
+ name = RE_BACKSLASH.sub("_", name)
+ # Replace any disallowed character with an underscore
+ name = RE_DISCORD_FILE_NAME_DISALLOWED.sub("_", name)
+ return name
+
+
+@dataclass(frozen=True)
+class FileAttachment:
+ """File Attachment from Snekbox eval."""
+
+ path: str
+ content: bytes
+
+ def __repr__(self) -> str:
+ """Return the content as a string."""
+ content = f"{self.content[:10]}..." if len(self.content) > 10 else self.content
+ return f"FileAttachment(path={self.path!r}, content={content})"
+
+ @property
+ def suffix(self) -> str:
+ """Return the file suffix."""
+ return PurePosixPath(self.path).suffix
+
+ @property
+ def name(self) -> str:
+ """Return the file name."""
+ return PurePosixPath(self.path).name
+
+ @classmethod
+ def from_dict(cls, data: dict, size_limit: int = FILE_SIZE_LIMIT) -> FileAttachment:
+ """Create a FileAttachment from a dict response."""
+ size = data.get("size")
+ if (size and size > size_limit) or (len(data["content"]) > size_limit):
+ raise ValueError("File size exceeds limit")
+
+ content = b64decode(data["content"])
+
+ if len(content) > size_limit:
+ raise ValueError("File size exceeds limit")
+
+ return cls(data["path"], content)
+
+ def to_dict(self) -> dict[str, str]:
+ """Convert the attachment to a json dict."""
+ content = self.content
+ if isinstance(content, str):
+ content = content.encode("utf-8")
+
+ return {
+ "path": self.path,
+ "content": b64encode(content).decode("ascii"),
+ }
+
+ def to_file(self) -> File:
+ """Convert to a discord.File."""
+ name = normalize_discord_file_name(self.name)
+ return File(BytesIO(self.content), filename=name)
diff --git a/botstrap.py b/botstrap.py
new file mode 100644
index 000000000..28486bd36
--- /dev/null
+++ b/botstrap.py
@@ -0,0 +1,165 @@
+import os
+import re
+from pathlib import Path
+
+from dotenv import load_dotenv
+from httpx import Client, HTTPStatusError, Response
+
+from bot.constants import Webhooks, _Categories, _Channels, _Roles
+from bot.log import get_logger
+
+load_dotenv()
+log = get_logger("Config Bootstrapper")
+
+env_file_path = Path(".env.server")
+BOT_TOKEN = os.getenv("BOT_TOKEN", None)
+GUILD_ID = os.getenv("GUILD_ID", None)
+
+
+if not BOT_TOKEN:
+ message = (
+ "Couldn't find BOT_TOKEN in the environment variables."
+ "Make sure to add it to the `.env` file likewise: `BOT_TOKEN=value_of_your_bot_token`"
+ )
+ log.warning(message)
+ raise ValueError(message)
+
+if not GUILD_ID:
+ message = (
+ "Couldn't find GUILD_ID in the environment variables."
+ "Make sure to add it to the `.env` file likewise: `GUILD_ID=value_of_your_discord_server_id`"
+ )
+ log.warning(message)
+ raise ValueError(message)
+
+
+class DiscordClient(Client):
+ """An HTTP client to communicate with Discord's APIs."""
+
+ def __init__(self):
+ super().__init__(
+ base_url="https://discord.com/api/v10",
+ headers={"Authorization": f"Bot {BOT_TOKEN}"},
+ event_hooks={"response": [self._raise_for_status]}
+ )
+
+ @staticmethod
+ def _raise_for_status(response: Response) -> None:
+ response.raise_for_status()
+
+
+def get_all_roles(guild_id: int | str, client: DiscordClient) -> dict:
+ """Fetches all the roles in a guild."""
+ result = {}
+
+ response = client.get(f"guilds/{guild_id}/roles")
+ roles = response.json()
+
+ for role in roles:
+ name = "_".join(part.lower() for part in role["name"].split(" ")).replace("-", "_")
+ result[name] = role["id"]
+
+ return result
+
+
+def get_all_channels_and_categories(
+ guild_id: int | str,
+ client: DiscordClient
+) -> tuple[dict[str, str], dict[str, str]]:
+ """Fetches all the text channels & categories in a guild."""
+ off_topic_channel_name_regex = r"ot\d{1}(_.*)+"
+ off_topic_count = 0
+ channels = {} # could be text channels only as well
+ categories = {}
+
+ response = client.get(f"guilds/{guild_id}/channels")
+ server_channels = response.json()
+
+ for channel in server_channels:
+ channel_type = channel["type"]
+ name = "_".join(part.lower() for part in channel["name"].split(" ")).replace("-", "_")
+ if re.match(off_topic_channel_name_regex, name):
+ name = f"off_topic_{off_topic_count}"
+ off_topic_count += 1
+
+ if channel_type == 4:
+ categories[name] = channel["id"]
+ else:
+ channels[name] = channel["id"]
+
+ return channels, categories
+
+
+def webhook_exists(webhook_id_: int, client: DiscordClient) -> bool:
+ """A predicate that indicates whether a webhook exists already or not."""
+ try:
+ client.get(f"webhooks/{webhook_id_}")
+ return True
+ except HTTPStatusError:
+ return False
+
+
+def create_webhook(name: str, channel_id_: int, client: DiscordClient) -> str:
+ """Creates a new webhook for a particular channel."""
+ payload = {"name": name}
+
+ response = client.post(f"channels/{channel_id_}/webhooks", json=payload)
+ new_webhook = response.json()
+ return new_webhook["id"]
+
+
+with DiscordClient() as discord_client:
+ config_str = "#Roles\n"
+
+ all_roles = get_all_roles(guild_id=GUILD_ID, client=discord_client)
+
+ for role_name in _Roles.__fields__:
+
+ role_id = all_roles.get(role_name, None)
+ if not role_id:
+ log.warning(f"Couldn't find the role {role_name} in the guild, PyDis' default values will be used.")
+ continue
+
+ config_str += f"roles_{role_name}={role_id}\n"
+
+ all_channels, all_categories = get_all_channels_and_categories(guild_id=GUILD_ID, client=discord_client)
+
+ config_str += "\n#Channels\n"
+
+ for channel_name in _Channels.__fields__:
+ channel_id = all_channels.get(channel_name, None)
+ if not channel_id:
+ log.warning(
+ f"Couldn't find the channel {channel_name} in the guild, PyDis' default values will be used."
+ )
+ continue
+
+ config_str += f"channels_{channel_name}={channel_id}\n"
+
+ config_str += "\n#Categories\n"
+
+ for category_name in _Categories.__fields__:
+ category_id = all_categories.get(category_name, None)
+ if not category_id:
+ log.warning(
+ f"Couldn't find the category {category_name} in the guild, PyDis' default values will be used."
+ )
+ continue
+
+ config_str += f"categories_{category_name}={category_id}\n"
+
+ env_file_path.write_text(config_str)
+
+ config_str += "\n#Webhooks\n"
+
+ for webhook_name, webhook_model in Webhooks:
+ webhook = webhook_exists(webhook_model.id, client=discord_client)
+ if not webhook:
+ webhook_channel_id = int(all_channels[webhook_name])
+ webhook_id = create_webhook(webhook_name, webhook_channel_id, client=discord_client)
+ else:
+ webhook_id = webhook_model.id
+ config_str += f"webhooks_{webhook_name}__id={webhook_id}\n"
+ config_str += f"webhooks_{webhook_name}__channel={all_channels[webhook_name]}\n"
+
+ env_file_path.write_text(config_str)
diff --git a/config-default.yml b/config-default.yml
deleted file mode 100644
index 9088fae34..000000000
--- a/config-default.yml
+++ /dev/null
@@ -1,560 +0,0 @@
-debug: !ENV ["BOT_DEBUG", "true"]
-file_logs: !ENV ["FILE_LOGS", "false"]
-
-
-bot:
- prefix: "!"
- sentry_dsn: !ENV "BOT_SENTRY_DSN"
- token: !ENV "BOT_TOKEN"
- trace_loggers: !ENV "BOT_TRACE_LOGGERS"
-
- clean:
- # Maximum number of messages to traverse for clean commands
- message_limit: 10000
-
- cooldowns:
- # Per channel, per tag.
- tags: 60
-
- redis:
- host: "redis.default.svc.cluster.local"
- password: !ENV "REDIS_PASSWORD"
- port: 6379
- use_fakeredis: false
-
- stats:
- presence_update_timeout: 300
- statsd_host: "graphite.default.svc.cluster.local"
-
-
-style:
- colours:
- blue: 0x3775a8
- bright_green: 0x01d277
- orange: 0xe67e22
- pink: 0xcf84e0
- purple: 0xb734eb
- soft_green: 0x68c290
- soft_orange: 0xf9cb54
- soft_red: 0xcd6d6d
- white: 0xfffffe
- yellow: 0xffd241
-
- emojis:
- badge_bug_hunter: "<:bug_hunter_lvl1:743882896372269137>"
- badge_bug_hunter_level_2: "<:bug_hunter_lvl2:743882896611344505>"
- badge_early_supporter: "<:early_supporter:743882896909140058>"
- badge_hypesquad: "<:hypesquad_events:743882896892362873>"
- badge_hypesquad_balance: "<:hypesquad_balance:743882896460480625>"
- badge_hypesquad_bravery: "<:hypesquad_bravery:743882896745693335>"
- badge_hypesquad_brilliance: "<:hypesquad_brilliance:743882896938631248>"
- badge_partner: "<:partner:748666453242413136>"
- badge_staff: "<:discord_staff:743882896498098226>"
- badge_verified_bot_developer: "<:verified_bot_dev:743882897299210310>"
- bot: "<:bot:812712599464443914>"
- verified_bot: "<:verified_bot:811645219220750347>"
-
- defcon_shutdown: "<:defcondisabled:470326273952972810>"
- defcon_unshutdown: "<:defconenabled:470326274213150730>"
- defcon_update: "<:defconsettingsupdated:470326274082996224>"
-
- failmail: "<:failmail:633660039931887616>"
-
- incident_actioned: "<:incident_actioned:714221559279255583>"
- incident_investigating: "<:incident_investigating:714224190928191551>"
- incident_unactioned: "<:incident_unactioned:714223099645526026>"
-
- status_dnd: "<:status_dnd:470326272082313216>"
- status_idle: "<:status_idle:470326266625785866>"
- status_offline: "<:status_offline:470326266537705472>"
- status_online: "<:status_online:470326272351010816>"
-
- ducky_dave: "<:ducky_dave:742058418692423772>"
-
- trashcan: "<:trashcan:637136429717389331>"
-
- bullet: "\u2022"
- check_mark: "\u2705"
- cross_mark: "\u274C"
- new: "\U0001F195"
- pencil: "\u270F"
-
- ok_hand: ":ok_hand:"
-
- icons:
- crown_blurple: "https://cdn.discordapp.com/emojis/469964153289965568.png"
- crown_green: "https://cdn.discordapp.com/emojis/469964154719961088.png"
- crown_red: "https://cdn.discordapp.com/emojis/469964154879344640.png"
-
- defcon_denied: "https://cdn.discordapp.com/emojis/472475292078964738.png"
- defcon_shutdown: "https://cdn.discordapp.com/emojis/470326273952972810.png"
- defcon_unshutdown: "https://cdn.discordapp.com/emojis/470326274213150730.png"
- defcon_update: "https://cdn.discordapp.com/emojis/472472638342561793.png"
-
- filtering: "https://cdn.discordapp.com/emojis/472472638594482195.png"
-
- green_checkmark: "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-checkmark-dist.png"
- green_questionmark: "https://raw.githubusercontent.com/python-discord/branding/main/icons/checkmark/green-question-mark-dist.png"
- guild_update: "https://cdn.discordapp.com/emojis/469954765141442561.png"
-
- hash_blurple: "https://cdn.discordapp.com/emojis/469950142942806017.png"
- hash_green: "https://cdn.discordapp.com/emojis/469950144918585344.png"
- hash_red: "https://cdn.discordapp.com/emojis/469950145413251072.png"
-
- message_bulk_delete: "https://cdn.discordapp.com/emojis/469952898994929668.png"
- message_delete: "https://cdn.discordapp.com/emojis/472472641320648704.png"
- message_edit: "https://cdn.discordapp.com/emojis/472472638976163870.png"
-
- pencil: "https://cdn.discordapp.com/emojis/470326272401211415.png"
-
- questionmark: "https://cdn.discordapp.com/emojis/512367613339369475.png"
-
- remind_blurple: "https://cdn.discordapp.com/emojis/477907609215827968.png"
- remind_green: "https://cdn.discordapp.com/emojis/477907607785570310.png"
- remind_red: "https://cdn.discordapp.com/emojis/477907608057937930.png"
-
- sign_in: "https://cdn.discordapp.com/emojis/469952898181234698.png"
- sign_out: "https://cdn.discordapp.com/emojis/469952898089091082.png"
-
- superstarify: "https://cdn.discordapp.com/emojis/636288153044516874.png"
- unsuperstarify: "https://cdn.discordapp.com/emojis/636288201258172446.png"
-
- token_removed: "https://cdn.discordapp.com/emojis/470326273298792469.png"
-
- user_ban: "https://cdn.discordapp.com/emojis/469952898026045441.png"
- user_timeout: "https://cdn.discordapp.com/emojis/472472640100106250.png"
- user_unban: "https://cdn.discordapp.com/emojis/469952898692808704.png"
- user_untimeout: "https://cdn.discordapp.com/emojis/472472639206719508.png"
- user_update: "https://cdn.discordapp.com/emojis/469952898684551168.png"
- user_verified: "https://cdn.discordapp.com/emojis/470326274519334936.png"
- user_warn: "https://cdn.discordapp.com/emojis/470326274238447633.png"
-
- voice_state_blue: "https://cdn.discordapp.com/emojis/656899769662439456.png"
- voice_state_green: "https://cdn.discordapp.com/emojis/656899770094452754.png"
- voice_state_red: "https://cdn.discordapp.com/emojis/656899769905709076.png"
-
-
-guild:
- id: 267624335836053506
- invite: "https://discord.gg/python"
-
- categories:
- logs: &LOGS 468520609152892958
- moderators: &MODS_CATEGORY 749736277464842262
- modmail: &MODMAIL 714494672835444826
- appeals: &APPEALS 890331800025563216
- appeals2: &APPEALS2 895417395261341766
- voice: 356013253765234688
- summer_code_jam: 861692638540857384
-
- channels:
- # Public announcement and news channels
- announcements: &ANNOUNCEMENTS 354619224620138496
- change_log: &CHANGE_LOG 748238795236704388
- mailing_lists: &MAILING_LISTS 704372456592506880
- python_events: &PYEVENTS_CHANNEL 729674110270963822
- python_news: &PYNEWS_CHANNEL 704372456592506880
- reddit: &REDDIT_CHANNEL 458224812528238616
-
- # Development
- dev_contrib: &DEV_CONTRIB 635950537262759947
- dev_core: &DEV_CORE 411200599653351425
- dev_voting: &DEV_CORE_VOTING 839162966519447552
- dev_log: &DEV_LOG 622895325144940554
-
- # Discussion
- meta: 429409067623251969
- python_general: &PY_GENERAL 267624335836053506
-
- # Python Help
- help_system_forum: 1035199133436354600
-
- # Topical
- discord_bots: 343944376055103488
-
- # Logs
- attachment_log: &ATTACH_LOG 649243850006855680
- filter_log: &FILTER_LOG 1014943924185473094
- message_log: &MESSAGE_LOG 467752170159079424
- mod_log: &MOD_LOG 282638479504965634
- nomination_archive: 833371042046148738
- user_log: 528976905546760203
- voice_log: 640292421988646961
-
- # Open Source Projects
- black_formatter: &BLACK_FORMATTER 846434317021741086
-
- # Off-topic
- off_topic_0: 291284109232308226
- off_topic_1: 463035241142026251
- off_topic_2: 463035268514185226
-
- # Special
- bot_commands: &BOT_CMD 267659945086812160
- esoteric: 470884583684964352
- voice_gate: 764802555427029012
- code_jam_planning: 490217981872177157
-
- # Staff
- admins: &ADMINS 365960823622991872
- admin_spam: &ADMIN_SPAM 563594791770914816
- defcon: &DEFCON 464469101889454091
- duck_pond: &DUCK_POND 637820308341915648
- helpers: &HELPERS 385474242440986624
- incidents: 714214212200562749
- incidents_archive: 720668923636351037
- mod_alerts: 473092532147060736
- mods: &MODS 305126844661760000
- mod_meta: 775412552795947058
- nominations: 822920136150745168
- nomination_voting: 822853512709931008
- organisation: &ORGANISATION 551789653284356126
- staff_lounge: &STAFF_LOUNGE 464905259261755392
- staff_info: &STAFF_INFO 396684402404622347
-
- # Staff announcement channels
- admin_announcements: &ADMIN_ANNOUNCEMENTS 749736155569848370
- mod_announcements: &MOD_ANNOUNCEMENTS 372115205867700225
- staff_announcements: &STAFF_ANNOUNCEMENTS 464033278631084042
-
- # Voice Channels
- admins_voice: &ADMINS_VOICE 500734494840717332
- code_help_voice_0: 751592231726481530
- code_help_voice_1: 764232549840846858
- general_voice_0: 751591688538947646
- general_voice_1: 799641437645701151
- staff_voice: &STAFF_VOICE 412375055910043655
-
- # Voice Chat
- code_help_chat_0: 755154969761677312
- code_help_chat_1: 766330079135268884
- staff_voice_chat: 541638762007101470
- voice_chat_0: 412357430186344448
- voice_chat_1: 799647045886541885
-
- # Watch
- big_brother_logs: &BB_LOGS 468507907357409333
-
- # Information
- roles: 851270062434156586
-
- moderation_categories:
- - *MODS_CATEGORY
- - *MODMAIL
- - *LOGS
- - *APPEALS
- - *APPEALS2
-
- moderation_channels:
- - *ADMINS
- - *ADMIN_SPAM
- - *MODS
-
- # Modlog cog explicitly ignores events which occur in these channels.
- # This is on top of implicitly ignoring events in channels that the mod team cannot view.
- modlog_blacklist:
- - *ATTACH_LOG
- - *MESSAGE_LOG
- - *MOD_LOG
- - *STAFF_VOICE
- - *FILTER_LOG
-
- reminder_whitelist:
- - *BOT_CMD
- - *DEV_CONTRIB
- - *BLACK_FORMATTER
-
- roles:
- # Self-assignable roles, see the Subscribe cog
- advent_of_code: 518565788744024082
- announcements: 463658397560995840
- lovefest: 542431903886606399
- pyweek_announcements: 897568414044938310
- revival_of_code: 988801794668908655
- legacy_help_channels_access: 1074780483776417964
-
- contributors: 295488872404484098
- help_cooldown: 699189276025421825
- muted: &MUTED_ROLE 277914926603829249 # TODO remove when no longer relevant.
- partners: &PY_PARTNER_ROLE 323426753857191936
- python_community: &PY_COMMUNITY_ROLE 458226413825294336
- sprinters: &SPRINTERS 758422482289426471
- voice_verified: 764802720779337729
-
- # Staff
- admins: &ADMINS_ROLE 267628507062992896
- core_developers: 587606783669829632
- code_jam_event_team: 787816728474288181
- devops: 409416496733880320
- domain_leads: 807415650778742785
- events_lead: 778361735739998228
- helpers: &HELPERS_ROLE 267630620367257601
- moderators: &MODS_ROLE 831776746206265384
- mod_team: &MOD_TEAM_ROLE 267629731250176001
- owners: &OWNERS_ROLE 267627879762755584
- project_leads: 815701647526330398
-
- # Code Jam
- jammers: 737249140966162473
-
- # Streaming
- video: 764245844798079016
-
- # Patreon
- patreon_tier_1: 505040943800516611
- patreon_tier_2: 743399725914390631
- patreon_tier_3: 743400204367036520
-
- moderation_roles:
- - *ADMINS_ROLE
- - *MOD_TEAM_ROLE
- - *MODS_ROLE
- - *OWNERS_ROLE
-
- staff_roles:
- - *ADMINS_ROLE
- - *HELPERS_ROLE
- - *MOD_TEAM_ROLE
- - *OWNERS_ROLE
-
- webhooks:
- big_brother: 569133704568373283
- dev_log: 680501655111729222
- duck_pond: 637821475327311927
- incidents: 816650601844572212
- incidents_archive: 720671599790915702
- python_news: &PYNEWS_WEBHOOK 704381182279942324
-
-
-filter:
- # What do we filter?
- filter_domains: true
- filter_everyone_ping: true
- filter_invites: true
- filter_zalgo: false
- watch_regex: true
- watch_rich_embeds: true
-
- # Notify user on filter?
- # Notifications are not expected for "watchlist" type filters
- notify_user_domains: false
- notify_user_everyone_ping: true
- notify_user_invites: true
- notify_user_zalgo: false
-
- # Filter configuration
- offensive_msg_delete_days: 7 # How many days before deleting an offensive message?
- ping_everyone: true
-
- # Censor doesn't apply to these
- channel_whitelist:
- - *ADMINS
- - *BB_LOGS
- - *DEV_LOG
- - *MESSAGE_LOG
- - *MOD_LOG
- - *STAFF_LOUNGE
-
- role_whitelist:
- - *ADMINS_ROLE
- - *HELPERS_ROLE
- - *MODS_ROLE
- - *OWNERS_ROLE
- - *PY_COMMUNITY_ROLE
- - *SPRINTERS
- - *PY_PARTNER_ROLE
-
-
-keys:
- github: !ENV "GITHUB_API_KEY"
- site_api: !ENV "BOT_API_KEY"
-
-
-urls:
- # PyDis site vars
- connect_max_retries: 3
- connect_cooldown: 5
- site: &DOMAIN "pythondiscord.com"
- site_api: &API "site.default.svc.cluster.local/api"
- site_api_schema: "http://"
- site_paste: &PASTE !JOIN ["paste.", *DOMAIN]
- site_schema: &SCHEMA "https://"
- site_staff: &STAFF !JOIN [*SCHEMA, *DOMAIN, "/staff"]
-
- paste_service: !JOIN [*SCHEMA, *PASTE, "/{key}"]
- site_logs_view: !JOIN [*STAFF, "/bot/logs"]
-
- # Snekbox
- snekbox_eval_api: !ENV ["SNEKBOX_EVAL_API", "http://snekbox.default.svc.cluster.local/eval"]
- snekbox_311_eval_api: !ENV ["SNEKBOX_311_EVAL_API", "http://snekbox-311.default.svc.cluster.local/eval"]
-
- # Discord API URLs
- discord_api: &DISCORD_API "https://discordapp.com/api/v7/"
- discord_invite_api: !JOIN [*DISCORD_API, "invites"]
-
- # Misc URLsw
- bot_avatar: "https://raw.githubusercontent.com/python-discord/branding/main/logos/logo_circle/logo_circle.png"
- github_bot_repo: "https://github.com/python-discord/bot"
-
-
-anti_spam:
- cache_size: 100
-
- # Clean messages that violate a rule.
- clean_offending: true
- ping_everyone: true
-
- punishment:
- remove_after: 600
- role_id: *MUTED_ROLE
-
- rules:
- attachments:
- interval: 10
- max: 6
-
- burst:
- interval: 10
- max: 7
-
- # Burst shared it (temporarily) disabled to prevent
- # the bug that triggers multiple infractions/DMs per
- # user. It also tends to catch a lot of innocent users
- # now that we're so big.
- # burst_shared:
- # interval: 10
- # max: 20
-
- chars:
- interval: 5
- max: 4_200
-
- discord_emojis:
- interval: 10
- max: 20
-
- duplicates:
- interval: 10
- max: 3
-
- links:
- interval: 10
- max: 10
-
- mentions:
- interval: 10
- max: 5
-
- newlines:
- interval: 10
- max: 100
- max_consecutive: 10
-
- role_mentions:
- interval: 10
- max: 3
-
-
-metabase:
- username: !ENV "METABASE_USERNAME"
- password: !ENV "METABASE_PASSWORD"
- base_url: "http://metabase.default.svc.cluster.local"
- public_url: "https://metabase.pythondiscord.com"
- # 14 days, see https://www.metabase.com/docs/latest/operations-guide/environment-variables.html#max_session_age
- max_session_age: 20160
-
-
-big_brother:
- header_message_limit: 15
- log_delay: 15
-
-
-code_block:
- # The channels in which code blocks will be detected. They are not subject to a cooldown.
- channel_whitelist:
- - *BOT_CMD
-
- # The channels which will be affected by a cooldown. These channels are also whitelisted.
- cooldown_channels:
- - *PY_GENERAL
-
- # Sending instructions triggers a cooldown on a per-channel basis.
- # More instruction messages will not be sent in the same channel until the cooldown has elapsed.
- cooldown_seconds: 300
-
- # The minimum amount of lines a message or code block must have for instructions to be sent.
- minimum_lines: 4
-
-
-free:
- # Seconds to elapse for a channel
- # to be considered inactive.
- activity_timeout: 600
- cooldown_per: 60.0
- cooldown_rate: 1
-
-
-help_channels:
- enable: true
-
- # Allowed duration of inactivity before archiving a help post
- idle_minutes: 30
-
- # Allowed duration of inactivity when post is empty (due to deleted messages)
- # before archiving a help post
- deleted_idle_minutes: 5
-
- # Roles which are allowed to use the command which makes channels dormant
- cmd_whitelist:
- - *HELPERS_ROLE
-
-redirect_output:
- delete_delay: 15
- delete_invocation: true
-
-
-duck_pond:
- threshold: 7
- channel_blacklist:
- - *ANNOUNCEMENTS
- - *PYNEWS_CHANNEL
- - *PYEVENTS_CHANNEL
- - *MAILING_LISTS
- - *REDDIT_CHANNEL
- - *DUCK_POND
- - *CHANGE_LOG
- - *STAFF_ANNOUNCEMENTS
- - *MOD_ANNOUNCEMENTS
- - *ADMIN_ANNOUNCEMENTS
- - *STAFF_INFO
-
-
-python_news:
- channel: *PYNEWS_CHANNEL
- webhook: *PYNEWS_WEBHOOK
-
- mail_lists:
- - 'python-ideas'
- - 'python-announce-list'
- - 'pypi-announce'
- - 'python-dev'
-
-
-voice_gate:
- bot_message_delete_delay: 10 # Seconds before deleting bot's response in Voice Gate
- minimum_activity_blocks: 3 # Number of 10 minute blocks during which a user must have been active
- minimum_days_member: 3 # How many days the user must have been a member for
- minimum_messages: 50 # How many messages a user must have to be eligible for voice
- voice_ping_delete_delay: 60 # Seconds before deleting the bot's ping to user in Voice Gate
-
-
-branding:
- cycle_frequency: 3 # How many days bot wait before refreshing server icon
-
-
-config:
- required_keys: ['bot.token']
-
-
-video_permission:
- default_permission_duration: 5 # Default duration for stream command in minutes
diff --git a/docker-compose.yml b/docker-compose.yml
index bc53c482b..694f44507 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -112,4 +112,9 @@ services:
env_file:
- .env
environment:
- BOT_API_KEY: badbot13m0n8f570f942013fc818f234916ca531
+ API_KEYS_SITE_API: "badbot13m0n8f570f942013fc818f234916ca531"
+ URLS_SITE_API: "web:8000/api"
+ URLS_SNEKBOX_EVAL_API: "http://snekbox/eval"
+ URLS_SNEKBOX_311_EVAL_API: "http://snekbox-311/eval"
+ REDIS_HOST: "redis"
+ STATS_STATSD_HOST: "http://localhost"
diff --git a/poetry.lock b/poetry.lock
index 8a718ab82..5f90384ab 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -140,6 +140,27 @@ files = [
frozenlist = ">=1.1.0"
[[package]]
+name = "anyio"
+version = "3.6.2"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+category = "dev"
+optional = false
+python-versions = ">=3.6.2"
+files = [
+ {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"},
+ {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"},
+]
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+
+[package.extras]
+doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
+trio = ["trio (>=0.16,<0.22)"]
+
+[[package]]
name = "arrow"
version = "1.2.3"
description = "Better dates & times for Python"
@@ -187,32 +208,33 @@ files = [
[[package]]
name = "attrs"
-version = "22.1.0"
+version = "22.2.0"
description = "Classes Without Boilerplate"
category = "main"
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.6"
files = [
- {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
- {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
+ {file = "attrs-22.2.0-py3-none-any.whl", hash = "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836"},
+ {file = "attrs-22.2.0.tar.gz", hash = "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99"},
]
[package.extras]
-dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
-docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
-tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"]
-tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"]
+cov = ["attrs[tests]", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"]
+dev = ["attrs[docs,tests]"]
+docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope.interface"]
+tests = ["attrs[tests-no-zope]", "zope.interface"]
+tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy (>=0.971,<0.990)", "mypy (>=0.971,<0.990)", "pympler", "pympler", "pytest (>=4.3.0)", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-mypy-plugins", "pytest-xdist[psutil]", "pytest-xdist[psutil]"]
[[package]]
name = "beautifulsoup4"
-version = "4.11.1"
+version = "4.11.2"
description = "Screen-scraping library"
category = "main"
optional = false
python-versions = ">=3.6.0"
files = [
- {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"},
- {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"},
+ {file = "beautifulsoup4-4.11.2-py3-none-any.whl", hash = "sha256:0e79446b10b3ecb499c1556f7e228a53e64a2bfcebd455f370d8927cb5b59e39"},
+ {file = "beautifulsoup4-4.11.2.tar.gz", hash = "sha256:bc4bdda6717de5a2987436fb8d72f45dc90dd856bdfd512a1314ce90349a0106"},
]
[package.dependencies]
@@ -370,62 +392,63 @@ cron = ["capturer (>=2.4)"]
[[package]]
name = "coverage"
-version = "6.5.0"
+version = "7.2.1"
description = "Code coverage measurement for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53"},
- {file = "coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660"},
- {file = "coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4"},
- {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04"},
- {file = "coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0"},
- {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae"},
- {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466"},
- {file = "coverage-6.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a"},
- {file = "coverage-6.5.0-cp310-cp310-win32.whl", hash = "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32"},
- {file = "coverage-6.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e"},
- {file = "coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795"},
- {file = "coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75"},
- {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b"},
- {file = "coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91"},
- {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4"},
- {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa"},
- {file = "coverage-6.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b"},
- {file = "coverage-6.5.0-cp311-cp311-win32.whl", hash = "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578"},
- {file = "coverage-6.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b"},
- {file = "coverage-6.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d"},
- {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3"},
- {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef"},
- {file = "coverage-6.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79"},
- {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d"},
- {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c"},
- {file = "coverage-6.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f"},
- {file = "coverage-6.5.0-cp37-cp37m-win32.whl", hash = "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b"},
- {file = "coverage-6.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2"},
- {file = "coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c"},
- {file = "coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba"},
- {file = "coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e"},
- {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398"},
- {file = "coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b"},
- {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b"},
- {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f"},
- {file = "coverage-6.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e"},
- {file = "coverage-6.5.0-cp38-cp38-win32.whl", hash = "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d"},
- {file = "coverage-6.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6"},
- {file = "coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745"},
- {file = "coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc"},
- {file = "coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe"},
- {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf"},
- {file = "coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5"},
- {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62"},
- {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518"},
- {file = "coverage-6.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f"},
- {file = "coverage-6.5.0-cp39-cp39-win32.whl", hash = "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72"},
- {file = "coverage-6.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987"},
- {file = "coverage-6.5.0-pp36.pp37.pp38-none-any.whl", hash = "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a"},
- {file = "coverage-6.5.0.tar.gz", hash = "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84"},
+ {file = "coverage-7.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:49567ec91fc5e0b15356da07a2feabb421d62f52a9fff4b1ec40e9e19772f5f8"},
+ {file = "coverage-7.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2ef6cae70168815ed91388948b5f4fcc69681480a0061114db737f957719f03"},
+ {file = "coverage-7.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3004765bca3acd9e015794e5c2f0c9a05587f5e698127ff95e9cfba0d3f29339"},
+ {file = "coverage-7.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cca7c0b7f5881dfe0291ef09ba7bb1582cb92ab0aeffd8afb00c700bf692415a"},
+ {file = "coverage-7.2.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2167d116309f564af56f9aa5e75ef710ef871c5f9b313a83050035097b56820"},
+ {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cb5f152fb14857cbe7f3e8c9a5d98979c4c66319a33cad6e617f0067c9accdc4"},
+ {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:87dc37f16fb5e3a28429e094145bf7c1753e32bb50f662722e378c5851f7fdc6"},
+ {file = "coverage-7.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e191a63a05851f8bce77bc875e75457f9b01d42843f8bd7feed2fc26bbe60833"},
+ {file = "coverage-7.2.1-cp310-cp310-win32.whl", hash = "sha256:e3ea04b23b114572b98a88c85379e9e9ae031272ba1fb9b532aa934c621626d4"},
+ {file = "coverage-7.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:0cf557827be7eca1c38a2480484d706693e7bb1929e129785fe59ec155a59de6"},
+ {file = "coverage-7.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:570c21a29493b350f591a4b04c158ce1601e8d18bdcd21db136fbb135d75efa6"},
+ {file = "coverage-7.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e872b082b32065ac2834149dc0adc2a2e6d8203080501e1e3c3c77851b466f9"},
+ {file = "coverage-7.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fac6343bae03b176e9b58104a9810df3cdccd5cfed19f99adfa807ffbf43cf9b"},
+ {file = "coverage-7.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abacd0a738e71b20e224861bc87e819ef46fedba2fb01bc1af83dfd122e9c319"},
+ {file = "coverage-7.2.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9256d4c60c4bbfec92721b51579c50f9e5062c21c12bec56b55292464873508"},
+ {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:80559eaf6c15ce3da10edb7977a1548b393db36cbc6cf417633eca05d84dd1ed"},
+ {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bd7e628f6c3ec4e7d2d24ec0e50aae4e5ae95ea644e849d92ae4805650b4c4e"},
+ {file = "coverage-7.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09643fb0df8e29f7417adc3f40aaf379d071ee8f0350ab290517c7004f05360b"},
+ {file = "coverage-7.2.1-cp311-cp311-win32.whl", hash = "sha256:1b7fb13850ecb29b62a447ac3516c777b0e7a09ecb0f4bb6718a8654c87dfc80"},
+ {file = "coverage-7.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:617a94ada56bbfe547aa8d1b1a2b8299e2ec1ba14aac1d4b26a9f7d6158e1273"},
+ {file = "coverage-7.2.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8649371570551d2fd7dee22cfbf0b61f1747cdfb2b7587bb551e4beaaa44cb97"},
+ {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d2b9b5e70a21474c105a133ba227c61bc95f2ac3b66861143ce39a5ea4b3f84"},
+ {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae82c988954722fa07ec5045c57b6d55bc1a0890defb57cf4a712ced65b26ddd"},
+ {file = "coverage-7.2.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:861cc85dfbf55a7a768443d90a07e0ac5207704a9f97a8eb753292a7fcbdfcfc"},
+ {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0339dc3237c0d31c3b574f19c57985fcbe494280153bbcad33f2cdf469f4ac3e"},
+ {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5928b85416a388dd557ddc006425b0c37e8468bd1c3dc118c1a3de42f59e2a54"},
+ {file = "coverage-7.2.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8d3843ca645f62c426c3d272902b9de90558e9886f15ddf5efe757b12dd376f5"},
+ {file = "coverage-7.2.1-cp37-cp37m-win32.whl", hash = "sha256:6a034480e9ebd4e83d1aa0453fd78986414b5d237aea89a8fdc35d330aa13bae"},
+ {file = "coverage-7.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6fce673f79a0e017a4dc35e18dc7bb90bf6d307c67a11ad5e61ca8d42b87cbff"},
+ {file = "coverage-7.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f099da6958ddfa2ed84bddea7515cb248583292e16bb9231d151cd528eab657"},
+ {file = "coverage-7.2.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:97a3189e019d27e914ecf5c5247ea9f13261d22c3bb0cfcfd2a9b179bb36f8b1"},
+ {file = "coverage-7.2.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a81dbcf6c6c877986083d00b834ac1e84b375220207a059ad45d12f6e518a4e3"},
+ {file = "coverage-7.2.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d2c3dde4c0b9be4b02067185136b7ee4681978228ad5ec1278fa74f5ca3e99"},
+ {file = "coverage-7.2.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a209d512d157379cc9ab697cbdbb4cfd18daa3e7eebaa84c3d20b6af0037384"},
+ {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f3d07edb912a978915576a776756069dede66d012baa503022d3a0adba1b6afa"},
+ {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8dca3c1706670297851bca1acff9618455122246bdae623be31eca744ade05ec"},
+ {file = "coverage-7.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b1991a6d64231a3e5bbe3099fb0dd7c9aeaa4275ad0e0aeff4cb9ef885c62ba2"},
+ {file = "coverage-7.2.1-cp38-cp38-win32.whl", hash = "sha256:22c308bc508372576ffa3d2dbc4824bb70d28eeb4fcd79d4d1aed663a06630d0"},
+ {file = "coverage-7.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:b0c0d46de5dd97f6c2d1b560bf0fcf0215658097b604f1840365296302a9d1fb"},
+ {file = "coverage-7.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4dd34a935de268a133e4741827ae951283a28c0125ddcdbcbba41c4b98f2dfef"},
+ {file = "coverage-7.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f8318ed0f3c376cfad8d3520f496946977abde080439d6689d7799791457454"},
+ {file = "coverage-7.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:834c2172edff5a08d78e2f53cf5e7164aacabeb66b369f76e7bb367ca4e2d993"},
+ {file = "coverage-7.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4d70c853f0546855f027890b77854508bdb4d6a81242a9d804482e667fff6e6"},
+ {file = "coverage-7.2.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a6450da4c7afc4534305b2b7d8650131e130610cea448ff240b6ab73d7eab63"},
+ {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:99f4dd81b2bb8fc67c3da68b1f5ee1650aca06faa585cbc6818dbf67893c6d58"},
+ {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bdd3f2f285ddcf2e75174248b2406189261a79e7fedee2ceeadc76219b6faa0e"},
+ {file = "coverage-7.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f29351393eb05e6326f044a7b45ed8e38cb4dcc38570d12791f271399dc41431"},
+ {file = "coverage-7.2.1-cp39-cp39-win32.whl", hash = "sha256:e2b50ebc2b6121edf352336d503357321b9d8738bb7a72d06fc56153fd3f4cd8"},
+ {file = "coverage-7.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:bd5a12239c0006252244f94863f1c518ac256160cd316ea5c47fb1a11b25889a"},
+ {file = "coverage-7.2.1-pp37.pp38.pp39-none-any.whl", hash = "sha256:436313d129db7cf5b4ac355dd2bd3f7c7e5294af077b090b85de75f8458b8616"},
+ {file = "coverage-7.2.1.tar.gz", hash = "sha256:c77f2a9093ccf329dd523a9b2b3c854c20d2a3d968b6def3b820272ca6732242"},
]
[package.dependencies]
@@ -436,21 +459,22 @@ toml = ["tomli"]
[[package]]
name = "deepdiff"
-version = "6.2.1"
+version = "6.2.3"
description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "deepdiff-6.2.1-py3-none-any.whl", hash = "sha256:8ba27c185f9197b78c316ce7bb0c743d25d14f7cdb8ec3b340437dbc93dcbff2"},
- {file = "deepdiff-6.2.1.tar.gz", hash = "sha256:3fe134dde5b3922ff8c51fc1e95a972e659c853797231b836a5ccf15532fd516"},
+ {file = "deepdiff-6.2.3-py3-none-any.whl", hash = "sha256:d83b06e043447d6770860a635abecb46e849b0494c43ced2ecafda7628c7ce72"},
+ {file = "deepdiff-6.2.3.tar.gz", hash = "sha256:a02aaa8171351eba675cff5f795ec7a90987f86ad5449553308d4e18df57dc3d"},
]
[package.dependencies]
ordered-set = ">=4.0.2,<4.2.0"
+orjson = "*"
[package.extras]
-cli = ["clevercsv (==0.7.4)", "click (==8.1.3)", "pyyaml (==6.0)", "toml (==0.10.2)"]
+cli = ["click (==8.1.3)", "pyyaml (==6.0)"]
[[package]]
name = "discord-py"
@@ -501,14 +525,14 @@ dev = ["coverage", "coveralls", "pytest"]
[[package]]
name = "exceptiongroup"
-version = "1.0.4"
+version = "1.1.0"
description = "Backport of PEP 654 (exception groups)"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"},
- {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"},
+ {file = "exceptiongroup-1.1.0-py3-none-any.whl", hash = "sha256:327cbda3da756e2de031a3107b81ab7b3770a602c4d16ca618298c526f4bec1e"},
+ {file = "exceptiongroup-1.1.0.tar.gz", hash = "sha256:bcb67d800a4497e1b404c2dd44fca47d3b7a5e5433dbab67f96c1a685cdfdf23"},
]
[package.extras]
@@ -531,24 +555,24 @@ testing = ["pre-commit"]
[[package]]
name = "fakeredis"
-version = "2.0.0"
+version = "2.10.0"
description = "Fake implementation of redis API for testing purposes."
category = "main"
optional = false
-python-versions = ">=3.7,<4.0"
+python-versions = ">=3.8,<4.0"
files = [
- {file = "fakeredis-2.0.0-py3-none-any.whl", hash = "sha256:fb3186cbbe4c549f922b0f08eb84b09c0e51ecf8efbed3572d20544254f93a97"},
- {file = "fakeredis-2.0.0.tar.gz", hash = "sha256:6d1dc2417921b7ce56a80877afa390d6335a3154146f201a86e3a14417bdc79e"},
+ {file = "fakeredis-2.10.0-py3-none-any.whl", hash = "sha256:7e66c96793688703a1da41256323ddaa1b3a2cab4ef793866839a937bb273915"},
+ {file = "fakeredis-2.10.0.tar.gz", hash = "sha256:722644759bba4ad61fa38f0bb34939b7657f166ba35892f747e282407a196845"},
]
[package.dependencies]
-lupa = {version = ">=1.13,<2.0", optional = true, markers = "extra == \"lua\""}
-redis = "<4.5"
-sortedcontainers = ">=2.4.0,<3.0.0"
+lupa = {version = ">=1.14,<2.0", optional = true, markers = "extra == \"lua\""}
+redis = ">=4,<5"
+sortedcontainers = ">=2.4,<3.0"
[package.extras]
-aioredis = ["aioredis (>=2.0.1,<3.0.0)"]
-lua = ["lupa (>=1.13,<2.0)"]
+json = ["jsonpath-ng (>=1.5,<2.0)"]
+lua = ["lupa (>=1.14,<2.0)"]
[[package]]
name = "feedparser"
@@ -567,19 +591,19 @@ sgmllib3k = "*"
[[package]]
name = "filelock"
-version = "3.8.0"
+version = "3.9.0"
description = "A platform independent file lock."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
- {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"},
+ {file = "filelock-3.9.0-py3-none-any.whl", hash = "sha256:f58d535af89bb9ad5cd4df046f741f8553a418c01a7856bf0d173bbc9f6bd16d"},
+ {file = "filelock-3.9.0.tar.gz", hash = "sha256:7b319f24340b51f55a2bf7a12ac0755a9b03e718311dac567a0f4f7fabd2f5de"},
]
[package.extras]
-docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"]
-testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"]
+docs = ["furo (>=2022.12.7)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.5)"]
+testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-timeout (>=2.1)"]
[[package]]
name = "flake8"
@@ -600,30 +624,30 @@ pyflakes = ">=3.0.0,<3.1.0"
[[package]]
name = "flake8-annotations"
-version = "2.9.1"
+version = "3.0.0"
description = "Flake8 Type Annotation Checks"
category = "dev"
optional = false
-python-versions = ">=3.7,<4.0"
+python-versions = ">=3.8.1,<4.0.0"
files = [
- {file = "flake8-annotations-2.9.1.tar.gz", hash = "sha256:11f09efb99ae63c8f9d6b492b75fe147fbc323179fddfe00b2e56eefeca42f57"},
- {file = "flake8_annotations-2.9.1-py3-none-any.whl", hash = "sha256:a4385158a7a9fc8af1d8820a2f4c8d03387997006a83f5f8bfe5bc6085bdf88a"},
+ {file = "flake8_annotations-3.0.0-py3-none-any.whl", hash = "sha256:ea927d31016515e9aa6e256651d74baeeee6fa4ad3f8383715ec5c0460a4c225"},
+ {file = "flake8_annotations-3.0.0.tar.gz", hash = "sha256:88c8b35a0db10b9a92be69ed3f81494509a18db1c3162551e57bc0fc35fab065"},
]
[package.dependencies]
attrs = ">=21.4"
-flake8 = ">=3.7"
+flake8 = ">=5.0"
[[package]]
name = "flake8-bugbear"
-version = "22.10.27"
+version = "23.2.13"
description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "flake8-bugbear-22.10.27.tar.gz", hash = "sha256:a6708608965c9e0de5fff13904fed82e0ba21ac929fe4896459226a797e11cd5"},
- {file = "flake8_bugbear-22.10.27-py3-none-any.whl", hash = "sha256:6ad0ab754507319060695e2f2be80e6d8977cfcea082293089a9226276bd825d"},
+ {file = "flake8-bugbear-23.2.13.tar.gz", hash = "sha256:39259814a83f33c8409417ee12dd4050c9c0bb4c8707c12fc18ae62b2f3ddee1"},
+ {file = "flake8_bugbear-23.2.13-py3-none-any.whl", hash = "sha256:f136bd0ca2684f101168bba2310dec541e11aa6b252260c17dcf58d18069a740"},
]
[package.dependencies]
@@ -631,18 +655,18 @@ attrs = ">=19.2.0"
flake8 = ">=3.0.0"
[package.extras]
-dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "tox"]
+dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit", "pytest", "tox"]
[[package]]
name = "flake8-docstrings"
-version = "1.6.0"
+version = "1.7.0"
description = "Extension for flake8 which uses pydocstyle to check docstrings"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
files = [
- {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"},
- {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"},
+ {file = "flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75"},
+ {file = "flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af"},
]
[package.dependencies]
@@ -651,19 +675,19 @@ pydocstyle = ">=2.1"
[[package]]
name = "flake8-isort"
-version = "5.0.3"
+version = "6.0.0"
description = "flake8 plugin that integrates isort ."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "flake8-isort-5.0.3.tar.gz", hash = "sha256:0951398c343c67f4933407adbbfb495d4df7c038650c5d05753a006efcfeb390"},
- {file = "flake8_isort-5.0.3-py3-none-any.whl", hash = "sha256:8c4ab431d87780d0c8336e9614e50ef11201bc848ef64ca017532dec39d4bf49"},
+ {file = "flake8-isort-6.0.0.tar.gz", hash = "sha256:537f453a660d7e903f602ecfa36136b140de279df58d02eb1b6a0c84e83c528c"},
+ {file = "flake8_isort-6.0.0-py3-none-any.whl", hash = "sha256:aa0cac02a62c7739e370ce6b9c31743edac904bae4b157274511fc8a19c75bbc"},
]
[package.dependencies]
flake8 = "*"
-isort = ">=4.3.5,<6"
+isort = ">=5.0.0,<6"
[package.extras]
test = ["pytest"]
@@ -797,6 +821,64 @@ files = [
]
[[package]]
+name = "h11"
+version = "0.14.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
+ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
+]
+
+[[package]]
+name = "httpcore"
+version = "0.16.3"
+description = "A minimal low-level HTTP client."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"},
+ {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"},
+]
+
+[package.dependencies]
+anyio = ">=3.0,<5.0"
+certifi = "*"
+h11 = ">=0.13,<0.15"
+sniffio = ">=1.0.0,<2.0.0"
+
+[package.extras]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (>=1.0.0,<2.0.0)"]
+
+[[package]]
+name = "httpx"
+version = "0.23.3"
+description = "The next generation HTTP client."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"},
+ {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"},
+]
+
+[package.dependencies]
+certifi = "*"
+httpcore = ">=0.15.0,<0.17.0"
+rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (>=1.0.0,<2.0.0)"]
+
+[[package]]
name = "humanfriendly"
version = "10.0"
description = "Human friendly output for text interfaces using Python"
@@ -813,14 +895,14 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve
[[package]]
name = "identify"
-version = "2.5.9"
+version = "2.5.18"
description = "File identification library for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "identify-2.5.9-py2.py3-none-any.whl", hash = "sha256:a390fb696e164dbddb047a0db26e57972ae52fbd037ae68797e5ae2f4492485d"},
- {file = "identify-2.5.9.tar.gz", hash = "sha256:906036344ca769539610436e40a684e170c3648b552194980bb7b617a8daeb9f"},
+ {file = "identify-2.5.18-py2.py3-none-any.whl", hash = "sha256:93aac7ecf2f6abf879b8f29a8002d3c6de7086b8c28d88e1ad15045a15ab63f9"},
+ {file = "identify-2.5.18.tar.gz", hash = "sha256:89e144fa560cc4cffb6ef2ab5e9fb18ed9f9b3cb054384bab4b95c12f6c309fe"},
]
[package.extras]
@@ -840,31 +922,31 @@ files = [
[[package]]
name = "iniconfig"
-version = "1.1.1"
-description = "iniconfig: brain-dead simple config-ini parsing"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
category = "dev"
optional = false
-python-versions = "*"
+python-versions = ">=3.7"
files = [
- {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
- {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "isort"
-version = "5.10.1"
+version = "5.12.0"
description = "A Python utility / library to sort Python imports."
category = "dev"
optional = false
-python-versions = ">=3.6.1,<4.0"
+python-versions = ">=3.8.0"
files = [
- {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
- {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
+ {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
+ {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
]
[package.extras]
-colors = ["colorama (>=0.4.3,<0.5.0)"]
-pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
+colors = ["colorama (>=0.4.3)"]
+pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
plugins = ["setuptools"]
requirements-deprecated-finder = ["pip-api", "pipreqs"]
@@ -955,82 +1037,89 @@ files = [
[[package]]
name = "lxml"
-version = "4.9.1"
+version = "4.9.2"
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
files = [
- {file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"},
- {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"},
- {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc"},
- {file = "lxml-4.9.1-cp27-cp27m-win32.whl", hash = "sha256:86e92728ef3fc842c50a5cb1d5ba2bc66db7da08a7af53fb3da79e202d1b2cd3"},
- {file = "lxml-4.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4cfbe42c686f33944e12f45a27d25a492cc0e43e1dc1da5d6a87cbcaf2e95627"},
- {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dad7b164905d3e534883281c050180afcf1e230c3d4a54e8038aa5cfcf312b84"},
- {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a614e4afed58c14254e67862456d212c4dcceebab2eaa44d627c2ca04bf86837"},
- {file = "lxml-4.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f9ced82717c7ec65a67667bb05865ffe38af0e835cdd78728f1209c8fffe0cad"},
- {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:d9fc0bf3ff86c17348dfc5d322f627d78273eba545db865c3cd14b3f19e57fa5"},
- {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e5f66bdf0976ec667fc4594d2812a00b07ed14d1b44259d19a41ae3fff99f2b8"},
- {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fe17d10b97fdf58155f858606bddb4e037b805a60ae023c009f760d8361a4eb8"},
- {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8caf4d16b31961e964c62194ea3e26a0e9561cdf72eecb1781458b67ec83423d"},
- {file = "lxml-4.9.1-cp310-cp310-win32.whl", hash = "sha256:4780677767dd52b99f0af1f123bc2c22873d30b474aa0e2fc3fe5e02217687c7"},
- {file = "lxml-4.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a188cd292c4d2fcd78d04f863b789ef43aa129b233d7c9004de08693728b"},
- {file = "lxml-4.9.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:be9eb06489bc975c38706902cbc6888f39e946b81383abc2838d186f0e8b6a9d"},
- {file = "lxml-4.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f1be258c4d3dc609e654a1dc59d37b17d7fef05df912c01fc2e15eb43a9735f3"},
- {file = "lxml-4.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:927a9dd016d6033bc12e0bf5dee1dde140235fc8d0d51099353c76081c03dc29"},
- {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9232b09f5efee6a495a99ae6824881940d6447debe272ea400c02e3b68aad85d"},
- {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:04da965dfebb5dac2619cb90fcf93efdb35b3c6994fea58a157a834f2f94b318"},
- {file = "lxml-4.9.1-cp35-cp35m-win32.whl", hash = "sha256:4d5bae0a37af799207140652a700f21a85946f107a199bcb06720b13a4f1f0b7"},
- {file = "lxml-4.9.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4878e667ebabe9b65e785ac8da4d48886fe81193a84bbe49f12acff8f7a383a4"},
- {file = "lxml-4.9.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:1355755b62c28950f9ce123c7a41460ed9743c699905cbe664a5bcc5c9c7c7fb"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:bcaa1c495ce623966d9fc8a187da80082334236a2a1c7e141763ffaf7a405067"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eafc048ea3f1b3c136c71a86db393be36b5b3d9c87b1c25204e7d397cee9536"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:13c90064b224e10c14dcdf8086688d3f0e612db53766e7478d7754703295c7c8"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206a51077773c6c5d2ce1991327cda719063a47adc02bd703c56a662cdb6c58b"},
- {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e8f0c9d65da595cfe91713bc1222af9ecabd37971762cb830dea2fc3b3bb2acf"},
- {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0a4d179c9a941eb80c3a63cdb495e539e064f8054230844dcf2fcb812b71d3"},
- {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:830c88747dce8a3e7525defa68afd742b4580df6aa2fdd6f0855481e3994d391"},
- {file = "lxml-4.9.1-cp36-cp36m-win32.whl", hash = "sha256:1e1cf47774373777936c5aabad489fef7b1c087dcd1f426b621fda9dcc12994e"},
- {file = "lxml-4.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:5974895115737a74a00b321e339b9c3f45c20275d226398ae79ac008d908bff7"},
- {file = "lxml-4.9.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1423631e3d51008871299525b541413c9b6c6423593e89f9c4cfbe8460afc0a2"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:2aaf6a0a6465d39b5ca69688fce82d20088c1838534982996ec46633dc7ad6cc"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:9f36de4cd0c262dd9927886cc2305aa3f2210db437aa4fed3fb4940b8bf4592c"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae06c1e4bc60ee076292e582a7512f304abdf6c70db59b56745cca1684f875a4"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:57e4d637258703d14171b54203fd6822fda218c6c2658a7d30816b10995f29f3"},
- {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d279033bf614953c3fc4a0aa9ac33a21e8044ca72d4fa8b9273fe75359d5cca"},
- {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a60f90bba4c37962cbf210f0188ecca87daafdf60271f4c6948606e4dabf8785"},
- {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2264f341dd81e41f3fffecec6e446aa2121e0b8d026fb5130e02de1402785"},
- {file = "lxml-4.9.1-cp37-cp37m-win32.whl", hash = "sha256:27e590352c76156f50f538dbcebd1925317a0f70540f7dc8c97d2931c595783a"},
- {file = "lxml-4.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:eea5d6443b093e1545ad0210e6cf27f920482bfcf5c77cdc8596aec73523bb7e"},
- {file = "lxml-4.9.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f05251bbc2145349b8d0b77c0d4e5f3b228418807b1ee27cefb11f69ed3d233b"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:487c8e61d7acc50b8be82bda8c8d21d20e133c3cbf41bd8ad7eb1aaeb3f07c97"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d1a92d8e90b286d491e5626af53afef2ba04da33e82e30744795c71880eaa21"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b570da8cd0012f4af9fa76a5635cd31f707473e65a5a335b186069d5c7121ff2"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ef87fca280fb15342726bd5f980f6faf8b84a5287fcc2d4962ea8af88b35130"},
- {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:93e414e3206779ef41e5ff2448067213febf260ba747fc65389a3ddaa3fb8715"},
- {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6653071f4f9bac46fbc30f3c7838b0e9063ee335908c5d61fb7a4a86c8fd2036"},
- {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:32a73c53783becdb7eaf75a2a1525ea8e49379fb7248c3eeefb9412123536387"},
- {file = "lxml-4.9.1-cp38-cp38-win32.whl", hash = "sha256:1a7c59c6ffd6ef5db362b798f350e24ab2cfa5700d53ac6681918f314a4d3b94"},
- {file = "lxml-4.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:1436cf0063bba7888e43f1ba8d58824f085410ea2025befe81150aceb123e345"},
- {file = "lxml-4.9.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:4beea0f31491bc086991b97517b9683e5cfb369205dac0148ef685ac12a20a67"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:41fb58868b816c202e8881fd0f179a4644ce6e7cbbb248ef0283a34b73ec73bb"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bd34f6d1810d9354dc7e35158aa6cc33456be7706df4420819af6ed966e85448"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:edffbe3c510d8f4bf8640e02ca019e48a9b72357318383ca60e3330c23aaffc7"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d949f53ad4fc7cf02c44d6678e7ff05ec5f5552b235b9e136bd52e9bf730b91"},
- {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:079b68f197c796e42aa80b1f739f058dcee796dc725cc9a1be0cdb08fc45b000"},
- {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9c3a88d20e4fe4a2a4a84bf439a5ac9c9aba400b85244c63a1ab7088f85d9d25"},
- {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4e285b5f2bf321fc0857b491b5028c5f276ec0c873b985d58d7748ece1d770dd"},
- {file = "lxml-4.9.1-cp39-cp39-win32.whl", hash = "sha256:ef72013e20dd5ba86a8ae1aed7f56f31d3374189aa8b433e7b12ad182c0d2dfb"},
- {file = "lxml-4.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:10d2017f9150248563bb579cd0d07c61c58da85c922b780060dcc9a3aa9f432d"},
- {file = "lxml-4.9.1-pp37-pypy37_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538747a9d7827ce3e16a8fdd201a99e661c7dee3c96c885d8ecba3c35d1032c"},
- {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0645e934e940107e2fdbe7c5b6fb8ec6232444260752598bc4d09511bd056c0b"},
- {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6daa662aba22ef3258934105be2dd9afa5bb45748f4f702a3b39a5bf53a1f4dc"},
- {file = "lxml-4.9.1-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:603a464c2e67d8a546ddaa206d98e3246e5db05594b97db844c2f0a1af37cf5b"},
- {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c4b2e0559b68455c085fb0f6178e9752c4be3bba104d6e881eb5573b399d1eb2"},
- {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0f3f0059891d3254c7b5fb935330d6db38d6519ecd238ca4fce93c234b4a0f73"},
- {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c852b1530083a620cb0de5f3cd6826f19862bafeaf77586f1aef326e49d95f0c"},
- {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"},
- {file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"},
+ {file = "lxml-4.9.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2"},
+ {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892"},
+ {file = "lxml-4.9.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a"},
+ {file = "lxml-4.9.2-cp27-cp27m-win32.whl", hash = "sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de"},
+ {file = "lxml-4.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3"},
+ {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50"},
+ {file = "lxml-4.9.2-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975"},
+ {file = "lxml-4.9.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c"},
+ {file = "lxml-4.9.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a"},
+ {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4"},
+ {file = "lxml-4.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4"},
+ {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7"},
+ {file = "lxml-4.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184"},
+ {file = "lxml-4.9.2-cp310-cp310-win32.whl", hash = "sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda"},
+ {file = "lxml-4.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab"},
+ {file = "lxml-4.9.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9"},
+ {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf"},
+ {file = "lxml-4.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380"},
+ {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92"},
+ {file = "lxml-4.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1"},
+ {file = "lxml-4.9.2-cp311-cp311-win32.whl", hash = "sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33"},
+ {file = "lxml-4.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd"},
+ {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0"},
+ {file = "lxml-4.9.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e"},
+ {file = "lxml-4.9.2-cp35-cp35m-win32.whl", hash = "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df"},
+ {file = "lxml-4.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5"},
+ {file = "lxml-4.9.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53"},
+ {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7"},
+ {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe"},
+ {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c"},
+ {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1"},
+ {file = "lxml-4.9.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e"},
+ {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74"},
+ {file = "lxml-4.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38"},
+ {file = "lxml-4.9.2-cp36-cp36m-win32.whl", hash = "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5"},
+ {file = "lxml-4.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3"},
+ {file = "lxml-4.9.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03"},
+ {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941"},
+ {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726"},
+ {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b"},
+ {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894"},
+ {file = "lxml-4.9.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45"},
+ {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e"},
+ {file = "lxml-4.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b"},
+ {file = "lxml-4.9.2-cp37-cp37m-win32.whl", hash = "sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe"},
+ {file = "lxml-4.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9"},
+ {file = "lxml-4.9.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8"},
+ {file = "lxml-4.9.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24"},
+ {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889"},
+ {file = "lxml-4.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f"},
+ {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03"},
+ {file = "lxml-4.9.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c"},
+ {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f"},
+ {file = "lxml-4.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457"},
+ {file = "lxml-4.9.2-cp38-cp38-win32.whl", hash = "sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b"},
+ {file = "lxml-4.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7"},
+ {file = "lxml-4.9.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1"},
+ {file = "lxml-4.9.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140"},
+ {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4"},
+ {file = "lxml-4.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf"},
+ {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947"},
+ {file = "lxml-4.9.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5"},
+ {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5"},
+ {file = "lxml-4.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2"},
+ {file = "lxml-4.9.2-cp39-cp39-win32.whl", hash = "sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1"},
+ {file = "lxml-4.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f"},
+ {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c"},
+ {file = "lxml-4.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a"},
+ {file = "lxml-4.9.2-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419"},
+ {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05"},
+ {file = "lxml-4.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f"},
+ {file = "lxml-4.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9"},
+ {file = "lxml-4.9.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5"},
+ {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746"},
+ {file = "lxml-4.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7"},
+ {file = "lxml-4.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409"},
+ {file = "lxml-4.9.2.tar.gz", hash = "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67"},
]
[package.extras]
@@ -1041,19 +1130,19 @@ source = ["Cython (>=0.29.7)"]
[[package]]
name = "markdownify"
-version = "0.6.1"
+version = "0.11.6"
description = "Convert HTML to markdown."
category = "main"
optional = false
python-versions = "*"
files = [
- {file = "markdownify-0.6.1-py3-none-any.whl", hash = "sha256:7489fd5c601536996a376c4afbcd1dd034db7690af807120681461e82fbc0acc"},
- {file = "markdownify-0.6.1.tar.gz", hash = "sha256:31d7c13ac2ada8bfc7535a25fee6622ca720e1b5f2d4a9cbc429d167c21f886d"},
+ {file = "markdownify-0.11.6-py3-none-any.whl", hash = "sha256:ba35fe289d5e9073bcd7d2cad629278fe25f1a93741fcdc0bfb4f009076d8324"},
+ {file = "markdownify-0.11.6.tar.gz", hash = "sha256:009b240e0c9f4c8eaf1d085625dcd4011e12f0f8cec55dedf9ea6f7655e49bfe"},
]
[package.dependencies]
-beautifulsoup4 = "*"
-six = "*"
+beautifulsoup4 = ">=4.9,<5"
+six = ">=1.15,<2"
[[package]]
name = "mccabe"
@@ -1069,14 +1158,14 @@ files = [
[[package]]
name = "more-itertools"
-version = "9.0.0"
+version = "9.1.0"
description = "More routines for operating on iterables, beyond itertools"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "more-itertools-9.0.0.tar.gz", hash = "sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab"},
- {file = "more_itertools-9.0.0-py3-none-any.whl", hash = "sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41"},
+ {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"},
+ {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"},
]
[[package]]
@@ -1093,71 +1182,86 @@ files = [
[[package]]
name = "multidict"
-version = "6.0.2"
+version = "6.0.4"
description = "multidict implementation"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"},
- {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"},
- {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"},
- {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"},
- {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"},
- {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"},
- {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"},
- {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"},
- {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"},
- {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"},
- {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"},
- {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"},
- {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"},
- {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"},
- {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"},
- {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"},
- {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"},
- {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"},
- {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"},
- {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"},
- {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"},
- {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"},
- {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"},
- {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"},
- {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"},
- {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"},
- {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"},
- {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"},
- {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"},
- {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"},
- {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"},
- {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"},
- {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"},
- {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"},
- {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"},
- {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"},
- {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"},
- {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"},
- {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"},
- {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"},
- {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"},
- {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"},
- {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"},
- {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"},
- {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"},
- {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"},
- {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"},
- {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"},
- {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"},
- {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"},
- {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"},
- {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"},
- {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"},
- {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"},
- {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"},
- {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"},
- {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"},
- {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"},
- {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"},
+ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
+ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
+ {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"},
+ {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"},
+ {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"},
+ {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"},
+ {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"},
+ {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"},
+ {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"},
+ {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"},
+ {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"},
+ {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"},
+ {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"},
+ {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"},
+ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
]
[[package]]
@@ -1191,68 +1295,119 @@ files = [
dev = ["black", "mypy", "pytest"]
[[package]]
+name = "orjson"
+version = "3.8.7"
+description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "orjson-3.8.7-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:f98c82850b7b4b7e27785ca43706fa86c893cdb88d54576bbb9b0d9c1070e421"},
+ {file = "orjson-3.8.7-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:1dee503c6c1a0659c5b46f5f39d9ca9d3657b11ca8bb4af8506086df416887d9"},
+ {file = "orjson-3.8.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc4fa83831f42ce5c938f8cefc2e175fa1df6f661fdeaba3badf26d2b8cfcf73"},
+ {file = "orjson-3.8.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e432c6c9c8b97ad825276d5795286f7cc9689f377a97e3b7ecf14918413303f"},
+ {file = "orjson-3.8.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee519964a5a0efb9633f38b1129fd242807c5c57162844efeeaab1c8de080051"},
+ {file = "orjson-3.8.7-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:109b539ce5bf60a121454d008fa67c3b67e5a3249e47d277012645922cf74bd0"},
+ {file = "orjson-3.8.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ad4d441fbde4133af6fee37f67dbf23181b9c537ecc317346ec8c3b4c8ec7705"},
+ {file = "orjson-3.8.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89dc786419e1ce2588345f58dd6a434e6728bce66b94989644234bcdbe39b603"},
+ {file = "orjson-3.8.7-cp310-none-win_amd64.whl", hash = "sha256:697abde7350fb8076d44bcb6b4ab3ce415ae2b5a9bb91efc460e5ab0d96bb5d3"},
+ {file = "orjson-3.8.7-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:1c19f47b35b9966a3abadf341b18ee4a860431bf2b00fd8d58906d51cf78aa70"},
+ {file = "orjson-3.8.7-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:3ffaabb380cd0ee187b4fc362516df6bf739808130b1339445c7d8878fca36e7"},
+ {file = "orjson-3.8.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d88837002c5a8af970745b8e0ca1b0fdb06aafbe7f1279e110d338ea19f3d23"},
+ {file = "orjson-3.8.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff60187d1b7e0bfab376b6002b08c560b7de06c87cf3a8ac639ecf58f84c5f3b"},
+ {file = "orjson-3.8.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0110970aed35dec293f30ed1e09f8604afd5d15c5ef83de7f6c427619b3ba47b"},
+ {file = "orjson-3.8.7-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:51b275475d4e36118b65ad56f9764056a09d985c5d72e64579bf8816f1356a5e"},
+ {file = "orjson-3.8.7-cp311-none-win_amd64.whl", hash = "sha256:63144d27735f3b60f079f247ac9a289d80dfe49a7f03880dfa0c0ba64d6491d5"},
+ {file = "orjson-3.8.7-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:a16273d77db746bb1789a2bbfded81148a60743fd6f9d5185e02d92e3732fa18"},
+ {file = "orjson-3.8.7-cp37-cp37m-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:5bb32259ea22cc9dd47a6fdc4b8f9f1e2f798fcf56c7c1122a7df0f4c5d33bf3"},
+ {file = "orjson-3.8.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad02e9102d4ba67db30a136e631e32aeebd1dce26c9f5942a457b02df131c5d0"},
+ {file = "orjson-3.8.7-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dbcfcec2b7ac52deb7be3685b551addc28ee8fa454ef41f8b714df6ba0e32a27"},
+ {file = "orjson-3.8.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a0e5504a5fc86083cc210c6946e8d61e13fe9f1d7a7bf81b42f7050a49d4fb"},
+ {file = "orjson-3.8.7-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:7bd4fd37adb03b1f2a1012d43c9f95973a02164e131dfe3ff804d7e180af5653"},
+ {file = "orjson-3.8.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:188ed9f9a781333ad802af54c55d5a48991e292239aef41bd663b6e314377eb8"},
+ {file = "orjson-3.8.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:cc52f58c688cb10afd810280e450f56fbcb27f52c053463e625c8335c95db0dc"},
+ {file = "orjson-3.8.7-cp37-none-win_amd64.whl", hash = "sha256:403c8c84ac8a02c40613b0493b74d5256379e65196d39399edbf2ed3169cbeb5"},
+ {file = "orjson-3.8.7-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:7d6ac5f8a2a17095cd927c4d52abbb38af45918e0d3abd60fb50cfd49d71ae24"},
+ {file = "orjson-3.8.7-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:0295a7bfd713fa89231fd0822c995c31fc2343c59a1d13aa1b8b6651335654f5"},
+ {file = "orjson-3.8.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feb32aaaa34cf2f891eb793ad320d4bb6731328496ae59b6c9eb1b620c42b529"},
+ {file = "orjson-3.8.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7a3ab1a473894e609b6f1d763838c6689ba2b97620c256a32c4d9f10595ac179"},
+ {file = "orjson-3.8.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e8c430d82b532c5ab95634e034bbf6ca7432ffe175a3e63eadd493e00b3a555"},
+ {file = "orjson-3.8.7-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:366cc75f7e09106f9dac95a675aef413367b284f25507d21e55bd7f45f445e80"},
+ {file = "orjson-3.8.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:84d154d07e8b17d97e990d5d710b719a031738eb1687d8a05b9089f0564ff3e0"},
+ {file = "orjson-3.8.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06180014afcfdc167ca984b312218aa62ce20093965c437c5f9166764cb65ef7"},
+ {file = "orjson-3.8.7-cp38-none-win_amd64.whl", hash = "sha256:41244431ba13f2e6ef22b52c5cf0202d17954489f4a3c0505bd28d0e805c3546"},
+ {file = "orjson-3.8.7-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:b20f29fa8371b8023f1791df035a2c3ccbd98baa429ac3114fc104768f7db6f8"},
+ {file = "orjson-3.8.7-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:226bfc1da2f21ee74918cee2873ea9a0fec1a8830e533cb287d192d593e99d02"},
+ {file = "orjson-3.8.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75c11023ac29e29fd3e75038d0e8dd93f9ea24d7b9a5e871967a8921a88df24"},
+ {file = "orjson-3.8.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:78604d3acfd7cd502f6381eea0c42281fe2b74755b334074ab3ebc0224100be1"},
+ {file = "orjson-3.8.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7129a6847f0494aa1427167486ef6aea2e835ba05f6c627df522692ee228f65"},
+ {file = "orjson-3.8.7-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1a1a8f4980059f48483782c608145b0f74538c266e01c183d9bcd9f8b71dbada"},
+ {file = "orjson-3.8.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d60304172a33705ce4bd25a6261ab84bed2dab0b3d3b79672ea16c7648af4832"},
+ {file = "orjson-3.8.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4f733062d84389c32c0492e5a4929056fac217034a94523debe0430bcc602cda"},
+ {file = "orjson-3.8.7-cp39-none-win_amd64.whl", hash = "sha256:010e2970ec9e826c332819e0da4b14b29b19641da0f1a6af4cec91629ef9b988"},
+ {file = "orjson-3.8.7.tar.gz", hash = "sha256:8460c8810652dba59c38c80d27c325b5092d189308d8d4f3e688dbd8d4f3b2dc"},
+]
+
+[[package]]
name = "packaging"
-version = "21.3"
+version = "23.0"
description = "Core utilities for Python packages"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
- {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
+ {file = "packaging-23.0-py3-none-any.whl", hash = "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2"},
+ {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
]
-[package.dependencies]
-pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
-
[[package]]
name = "pep8-naming"
-version = "0.13.2"
+version = "0.13.3"
description = "Check PEP-8 naming conventions, plugin for flake8"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pep8-naming-0.13.2.tar.gz", hash = "sha256:93eef62f525fd12a6f8c98f4dcc17fa70baae2f37fa1f73bec00e3e44392fa48"},
- {file = "pep8_naming-0.13.2-py3-none-any.whl", hash = "sha256:59e29e55c478db69cffbe14ab24b5bd2cd615c0413edf790d47d3fb7ba9a4e23"},
+ {file = "pep8-naming-0.13.3.tar.gz", hash = "sha256:1705f046dfcd851378aac3be1cd1551c7c1e5ff363bacad707d43007877fa971"},
+ {file = "pep8_naming-0.13.3-py3-none-any.whl", hash = "sha256:1a86b8c71a03337c97181917e2b472f0f5e4ccb06844a0d6f0a33522549e7a80"},
]
[package.dependencies]
-flake8 = ">=3.9.1"
+flake8 = ">=5.0.0"
[[package]]
name = "pip-licenses"
-version = "4.0.1"
+version = "4.1.0"
description = "Dump the software license list of Python packages installed with pip."
category = "dev"
optional = false
python-versions = "~=3.8"
files = [
- {file = "pip-licenses-4.0.1.tar.gz", hash = "sha256:05a180f5610b262e2d56eea99f04e380db7080e79655abf1c916125f39fe207d"},
- {file = "pip_licenses-4.0.1-py3-none-any.whl", hash = "sha256:5896c18b7897e38fdd7be9a9ea0de02d6ff3264b7411967d6b679019ddc31878"},
+ {file = "pip-licenses-4.1.0.tar.gz", hash = "sha256:6905abcc8b3ca45548a7a33b24abbee0e7a6eb451463acc676eaa879f1130598"},
+ {file = "pip_licenses-4.1.0-py3-none-any.whl", hash = "sha256:0aef43fa605aaed17342f74c325b5b1a15be034c9b12bcc97ff290de1a8bc0af"},
]
[package.dependencies]
prettytable = ">=2.3.0"
[package.extras]
-test = ["docutils", "pytest-cov", "pytest-pycodestyle", "pytest-runner"]
+test = ["docutils", "mypy", "pytest-cov", "pytest-pycodestyle", "pytest-runner"]
[[package]]
name = "platformdirs"
-version = "2.5.4"
+version = "3.1.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"},
- {file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"},
+ {file = "platformdirs-3.1.0-py3-none-any.whl", hash = "sha256:13b08a53ed71021350c9e300d4ea8668438fb0046ab3937ac9a29913a1a1350a"},
+ {file = "platformdirs-3.1.0.tar.gz", hash = "sha256:accc3665857288317f32c7bebb5a8e482ba717b474f3fc1d18ca7f9214be0cef"},
]
[package.extras]
-docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"]
-test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
+docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
[[package]]
name = "pluggy"
@@ -1272,14 +1427,14 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pre-commit"
-version = "2.20.0"
+version = "3.1.1"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"},
- {file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"},
+ {file = "pre_commit-3.1.1-py2.py3-none-any.whl", hash = "sha256:b80254e60668e1dd1f5c03a1c9e0413941d61f568a57d745add265945f65bfe8"},
+ {file = "pre_commit-3.1.1.tar.gz", hash = "sha256:d63e6537f9252d99f65755ae5b79c989b462d511ebbc481b561db6a297e1e865"},
]
[package.dependencies]
@@ -1287,19 +1442,18 @@ cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
-toml = "*"
-virtualenv = ">=20.0.8"
+virtualenv = ">=20.10.0"
[[package]]
name = "prettytable"
-version = "3.5.0"
+version = "3.6.0"
description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "prettytable-3.5.0-py3-none-any.whl", hash = "sha256:fe391c3b545800028edf5dbb6a5360893feb398367fcc1cf8d7a5b29ce5c59a1"},
- {file = "prettytable-3.5.0.tar.gz", hash = "sha256:52f682ba4efe29dccb38ff0fe5bac8a23007d0780ff92a8b85af64bc4fc74d72"},
+ {file = "prettytable-3.6.0-py3-none-any.whl", hash = "sha256:3b767129491767a3a5108e6f305cbaa650f8020a7db5dfe994a2df7ef7bad0fe"},
+ {file = "prettytable-3.6.0.tar.gz", hash = "sha256:2e0026af955b4ea67b22122f310b90eae890738c08cb0458693a49b6221530ac"},
]
[package.dependencies]
@@ -1337,61 +1491,64 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
[[package]]
name = "pycares"
-version = "4.2.2"
+version = "4.3.0"
description = "Python interface for c-ares"
category = "main"
optional = false
python-versions = "*"
files = [
- {file = "pycares-4.2.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5dc6418e87729105d93162155793002b3fa95490e2f2df33afec08b0b0d44989"},
- {file = "pycares-4.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9481ee42df7e34c9ef7b2f045e534062b980b2c971677868df9f17730b147ceb"},
- {file = "pycares-4.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e029e594c27a0066cdb89dfc5bba28ba94e2b27b0ca7aceb94f9aea06812cd"},
- {file = "pycares-4.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0eb203ceedcf7f9865ed3abb6128dfbb3498c5e76342e3c820c4274cc0c8e873"},
- {file = "pycares-4.2.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4a01ba75e8a2947fc0b954850f8db9d52166634a206056febef2f833c8cfa1e"},
- {file = "pycares-4.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:064543e222e3587a92bccae704fcc5f4ce1ba1ce66aac96483c9cf504d554a67"},
- {file = "pycares-4.2.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a5a28f1d041aa2102bd2512e7361671e4ef46bc927e95b6932ed95cc45273480"},
- {file = "pycares-4.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:650b16f025bd3dad6a331e63bb8c9d55994c1b5d0d289ebe03c0bc16edad114f"},
- {file = "pycares-4.2.2-cp310-cp310-win32.whl", hash = "sha256:f8b76c13275b319b850e28bb9b3f5815de7521b1e0a581453d1acf10011bafef"},
- {file = "pycares-4.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:bcfcafbb376376c9cca6d37a8497dfd6dbd82333bf37627067b34dcaf5039612"},
- {file = "pycares-4.2.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ae5accd693c6910bbd1a99d1f4551a9e99decd65d792a80f10c27b8fcc32b497"},
- {file = "pycares-4.2.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f1901b309cb5cf7ade5822d74b904f55c49369e4ff9328818e554d4c34b4714"},
- {file = "pycares-4.2.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bc61edb98aff9cb4b2e07c25383100b81459a676ca0b0bd5fe77226eb1f850e"},
- {file = "pycares-4.2.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:241155687db7b45cb4ef84a18755ebc78c3ad624fd2578b48ea52ac16a4c8d9f"},
- {file = "pycares-4.2.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:27a21184ba35fff12eec36375d5b064516a0c3401dbf66a7eded7da34c5ca282"},
- {file = "pycares-4.2.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8a376e637ecd79db62761ca40cda080b9383a07d6dedbc799dd1a31e053862d9"},
- {file = "pycares-4.2.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6c411610be8de17cd5257845ebba5104b8e6356c62e66768728985a2ac0e9d1c"},
- {file = "pycares-4.2.2-cp36-cp36m-win32.whl", hash = "sha256:6a5af6443a1cefb36ddca47af37e29cae94a734c6c7cea3eb94e5de5cc2a4f1a"},
- {file = "pycares-4.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a01ab41405dd4dd8449f9323b2dac25e1d856ef02d85c8aedea0130b65275b2a"},
- {file = "pycares-4.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9a2053b34163d13d6d135248c65e71cefce3f25b3611677a1428ec7a57bae856"},
- {file = "pycares-4.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8064eaae5084e5155008b8f9d089055a432ff2115960273fc570f55dccedf666"},
- {file = "pycares-4.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc045040c094068d5de28e61a6fd0babe8522e8f61829839b893f7aff928173b"},
- {file = "pycares-4.2.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:135a356d52773f02d7babd2b38ad64493418363274972cc786fdce847875ca03"},
- {file = "pycares-4.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:512fb2c04c28e0e5a7de0b61624ab9c15d2df52db113f63a0aba6c6f1174b92f"},
- {file = "pycares-4.2.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eb374525c6231920509612f197ca47bdaa6ec9a0728aa199ba536dc0c25bb55"},
- {file = "pycares-4.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:47c6e18bbe6f2f4ce42fbdfa4ab2602268590f76110f06af60d02f964b72fada"},
- {file = "pycares-4.2.2-cp37-cp37m-win32.whl", hash = "sha256:a2c7fb5d3cb633e3f23344194da9b5caa54eb40da34dbe4465f0ebcede2e1e1a"},
- {file = "pycares-4.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:90f374fae2af5eb728841b4c2a0c8038a6889ba2a5a421e4c4e4e0f15bcc5912"},
- {file = "pycares-4.2.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c0a7e0f9371c47cf028e2389f11385e906ba2797900419509adfa86587a2ac"},
- {file = "pycares-4.2.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0fb3944af9492cfde6e1167780c9b8a701a56cf7d3fb29086cfb906b8261648f"},
- {file = "pycares-4.2.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7466315e76ab0ca4dc1354f3d7cb53f6d99d365b3778d9849e52643270beb6f2"},
- {file = "pycares-4.2.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f58398bd9fa99cc2dd79f7fecddc85837ccb452d673168037ea603b15aa11b"},
- {file = "pycares-4.2.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47eae9809826cea5c0eb08eec9da584dd6330e51c075c2f6963ca2067555cd07"},
- {file = "pycares-4.2.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6cbd4df536d2c32d2d74b854db25f1d15cc61cdd182b03206afbd7ccbe7b8f11"},
- {file = "pycares-4.2.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3e4519bc51b744331c968eef0bd0071ca9c3e5863b8b8c1d99540ab8bfb04235"},
- {file = "pycares-4.2.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5e2af8ca3bc49894a87d2b5629b993c22b0e602ecb7fd2fad660ebb9be584829"},
- {file = "pycares-4.2.2-cp38-cp38-win32.whl", hash = "sha256:f6b5360e2278fae1e79479a4b56198fc7faf46ab350da18756c4de789835dbcd"},
- {file = "pycares-4.2.2-cp38-cp38-win_amd64.whl", hash = "sha256:4304e5f0c10281abcee3c2547140a6b280c70866f2828956c9bcb2de6cffa211"},
- {file = "pycares-4.2.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9155e95cbe26b4b57ca691e9d8bfb5a002c7ce14ac02ddfcfe7849d4d349badb"},
- {file = "pycares-4.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:612a20514685a3d999dd0a99eede9da851be11171d599b211fac287eee452ff1"},
- {file = "pycares-4.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:075d4bdde10590a2d0456eab20028aab997207e45469d30dd01a4a65caa7f8da"},
- {file = "pycares-4.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6eebdf34477c9bfb00497f8e58a674fd22b348bd928d19d29c84e8923554e1"},
- {file = "pycares-4.2.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55d39f2c38d1285d1ae248b9d2d965b161dcf51a4b6eacf97ff056da6f09dd30"},
- {file = "pycares-4.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:64261640fd52910e7960f30888abeca4e6a7a91371d351ccebc70ac1625ca74e"},
- {file = "pycares-4.2.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:72184b1510866c9bc97a6daca7d8218a6954c4a78640197f0840e604ba1182f9"},
- {file = "pycares-4.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:02fdf5ce48b21da6eafc5cb4508d344a0d48ac1a31e8df178f7c2fb548fcbc14"},
- {file = "pycares-4.2.2-cp39-cp39-win32.whl", hash = "sha256:fe8e0f8ed7fd795868bfc2211e345963174a9f4d1e2125753e1715a60441c8a0"},
- {file = "pycares-4.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:bb09c084de909206e6db1f014d4c6d662c7df04194df31f4831088d426afe8f1"},
- {file = "pycares-4.2.2.tar.gz", hash = "sha256:e1f57a8004370080694bd6fb969a1ffc9171a59c6824d54f791c1b2e4d298385"},
+ {file = "pycares-4.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:19c9cdd3322d422931982939773e453e491dfc5c0b2e23d7266959315c7a0824"},
+ {file = "pycares-4.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9e56e9cdf46a092970dc4b75bbabddea9f480be5eeadc3fcae3eb5c6807c4136"},
+ {file = "pycares-4.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c75a6241c79b935048272cb77df498da64b8defc8c4b29fdf9870e43ba4cbb4"},
+ {file = "pycares-4.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24d8654fac3742791b8bef59d1fbb3e19ae6a5c48876a6d98659f7c66ee546c4"},
+ {file = "pycares-4.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebf50b049a245880f1aa16a6f72c4408e0a65b49ea1d3bf13383a44a2cabd2bf"},
+ {file = "pycares-4.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:84daf560962763c0359fd79c750ef480f0fda40c08b57765088dbe362e8dc452"},
+ {file = "pycares-4.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:978d10da7ee74b9979c494afa8b646411119ad0186a29c7f13c72bb4295630c6"},
+ {file = "pycares-4.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c5b9d7fe52eb3d243f5ead58d5c0011884226d961df8360a34618c38c7515"},
+ {file = "pycares-4.3.0-cp310-cp310-win32.whl", hash = "sha256:da7c7089ae617317d2cbe38baefd3821387b3bfef7b3ee5b797b871cb1257974"},
+ {file = "pycares-4.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:7106dc683db30e1d851283b7b9df7a5ea4964d6bdd000d918d91d4b1f9bed329"},
+ {file = "pycares-4.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4e7a24ecef0b1933f2a3fdbf328d1b529a76cda113f8364fa0742e5b3bd76566"},
+ {file = "pycares-4.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7abccc2aa4771c06994e4d9ed596453061e2b8846f887d9c98a64ccdaf4790a"},
+ {file = "pycares-4.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:531fed46c5ed798a914c3207be4ae7b297c4d09e4183d3cf8fd9ee59a55d5080"},
+ {file = "pycares-4.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9335175af0c64a1e0ba67bdd349eb62d4eea0ad02c235ccdf0d535fd20f323"},
+ {file = "pycares-4.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5f0e95535027d2dcd51e780410632b0d3ed7e9e5ceb25dc0fe937f2c2960079"},
+ {file = "pycares-4.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3692179ce5fb96908ba342e1e5303608d0c976f0d5d4619fa9d3d6d9d5a9a1b4"},
+ {file = "pycares-4.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c4cb6cc7fe8e0606d30b60367f59fe26d1472e88555d61e202db70dea5c8edb"},
+ {file = "pycares-4.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3215445396c74103e2054e6b349d9e85883ceda2006d0039fc2d58c9b11818a2"},
+ {file = "pycares-4.3.0-cp311-cp311-win32.whl", hash = "sha256:6a0c0c3a0adf490bba9dbb37dbd07ec81e4a6584f095036ac34f06a633710ffe"},
+ {file = "pycares-4.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:995cb37cc39bd40ca87bb16555a0f7724f3be30d9f9059a4caab2fde45b1b903"},
+ {file = "pycares-4.3.0-cp36-cp36m-win32.whl", hash = "sha256:4c9187be72449c975c11daa1d94d7ddcc494f8a4c37a6c18f977cd7024a531d9"},
+ {file = "pycares-4.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d7405ba10a2903a58b8b0faedcb54994c9ee002ad01963587fabf93e7e479783"},
+ {file = "pycares-4.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:40aaa12081495f879f11f4cfc95edfec1ea14711188563102f9e33fe98728fac"},
+ {file = "pycares-4.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4972cac24b66c5997f3a3e2cb608e408066d80103d443e36d626a88a287b9ae7"},
+ {file = "pycares-4.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35886dba7aa5b73affca8729aeb5a1f5e94d3d9a764adb1b7e75bafca44eeca5"},
+ {file = "pycares-4.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5cea6e1f3be016f155d60f27f16c1074d58b4d6e123228fdbc3326d076016af8"},
+ {file = "pycares-4.3.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3a9fd2665b053afb39226ac6f8137a60910ca7729358456df2fb94866f4297de"},
+ {file = "pycares-4.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e8e9195f869120e44e0aa0a6098bb5c19947f4753054365891f592e6f9eab3ef"},
+ {file = "pycares-4.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:674486ecf2afb25ee219171b07cdaba481a1aaa2dabb155779c7be9ded03eaa9"},
+ {file = "pycares-4.3.0-cp37-cp37m-win32.whl", hash = "sha256:1b6cd3161851499b6894d1e23bfd633e7b775472f5af35ae35409c4a47a2d45e"},
+ {file = "pycares-4.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:710120c97b9afdba443564350c3f5f72fd9aae74d95b73dc062ca8ac3d7f36d7"},
+ {file = "pycares-4.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9103649bd29d84bc6bcfaf09def9c0592bbc766018fad19d76d09989608b915d"},
+ {file = "pycares-4.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c072dbaf73cb5434279578dc35322867d8d5df053e14fdcdcc589994ba4804ae"},
+ {file = "pycares-4.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008531733f9c7a976b59c7760a3672b191159fd69ae76c01ca051f20b5e44164"},
+ {file = "pycares-4.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2aae02d97d77dcff840ab55f86cb8b99bf644acbca17e1edb7048408b9782088"},
+ {file = "pycares-4.3.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:257953ae6d400a934fd9193aeb20990ac84a78648bdf5978e998bd007a4045cd"},
+ {file = "pycares-4.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c28d481efae26936ec08cb6beea305f4b145503b152cf2c4dc68cc4ad9644f0e"},
+ {file = "pycares-4.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:976249b39037dbfb709ccf7e1c40d2785905a0065536385d501b94570cfed96d"},
+ {file = "pycares-4.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:98568c30cfab6b327d94ae1acdf85bbba4cffd415980804985d34ca07e6f4791"},
+ {file = "pycares-4.3.0-cp38-cp38-win32.whl", hash = "sha256:a2f3c4f49f43162f7e684419d9834c2c8ec165e54cb8dc47aa9dc0c2132701c0"},
+ {file = "pycares-4.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:1730ef93e33e4682fbbf0e7fb19df2ed9822779d17de8ea6e20d5b0d71c1d2be"},
+ {file = "pycares-4.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a26b3f1684557025da26ce65d076619890c82b95e38cc7284ce51c3539a1ce8"},
+ {file = "pycares-4.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86112cce01655b9f63c5e53b74722084e88e784a7a8ad138d373440337c591c9"},
+ {file = "pycares-4.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01465a191dc78e923884bb45cd63c7e012623e520cf7ed67e542413ee334804"},
+ {file = "pycares-4.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9fd5d6012f3ee8c8038cbfe16e988bbd17b2f21eea86650874bf63757ee6161"},
+ {file = "pycares-4.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa36b8ea91eae20b5c7205f3e6654423f066af24a1df02b274770a96cbcafaa7"},
+ {file = "pycares-4.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:61019151130557c1788cae52e4f2f388a7520c9d92574f3a0d61c974c6740db0"},
+ {file = "pycares-4.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:231962bb46274c52632469a1e686fab065dbd106dbef586de4f7fb101e297587"},
+ {file = "pycares-4.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6c979512fa51c7ccef5204fe10ed4e5c44c2bce5f335fe98a3e423f1672bd7d4"},
+ {file = "pycares-4.3.0-cp39-cp39-win32.whl", hash = "sha256:655cf0df862ce3847a60e1a106dafa2ba2c14e6636bac49e874347acdc7312dc"},
+ {file = "pycares-4.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:36f2251ad0f99a5ce13df45c94c3161d9734c9e9fa2b9b4cc163b853ca170dc5"},
+ {file = "pycares-4.3.0.tar.gz", hash = "sha256:c542696f6dac978e9d99192384745a65f80a7d9450501151e4a7563e06010d45"},
]
[package.dependencies]
@@ -1426,52 +1583,53 @@ files = [
[[package]]
name = "pydantic"
-version = "1.10.2"
+version = "1.10.5"
description = "Data validation and settings management using python type hints"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pydantic-1.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd"},
- {file = "pydantic-1.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98"},
- {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912"},
- {file = "pydantic-1.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559"},
- {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"},
- {file = "pydantic-1.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c"},
- {file = "pydantic-1.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644"},
- {file = "pydantic-1.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f"},
- {file = "pydantic-1.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a"},
- {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525"},
- {file = "pydantic-1.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283"},
- {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42"},
- {file = "pydantic-1.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52"},
- {file = "pydantic-1.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c"},
- {file = "pydantic-1.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5"},
- {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c"},
- {file = "pydantic-1.10.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254"},
- {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5"},
- {file = "pydantic-1.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d"},
- {file = "pydantic-1.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2"},
- {file = "pydantic-1.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13"},
- {file = "pydantic-1.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116"},
- {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624"},
- {file = "pydantic-1.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1"},
- {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9"},
- {file = "pydantic-1.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965"},
- {file = "pydantic-1.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e"},
- {file = "pydantic-1.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488"},
- {file = "pydantic-1.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41"},
- {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b"},
- {file = "pydantic-1.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe"},
- {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d"},
- {file = "pydantic-1.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda"},
- {file = "pydantic-1.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6"},
- {file = "pydantic-1.10.2-py3-none-any.whl", hash = "sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709"},
- {file = "pydantic-1.10.2.tar.gz", hash = "sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410"},
+ {file = "pydantic-1.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5920824fe1e21cbb3e38cf0f3dd24857c8959801d1031ce1fac1d50857a03bfb"},
+ {file = "pydantic-1.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3bb99cf9655b377db1a9e47fa4479e3330ea96f4123c6c8200e482704bf1eda2"},
+ {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2185a3b3d98ab4506a3f6707569802d2d92c3a7ba3a9a35683a7709ea6c2aaa2"},
+ {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f582cac9d11c227c652d3ce8ee223d94eb06f4228b52a8adaafa9fa62e73d5c9"},
+ {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c9e5b778b6842f135902e2d82624008c6a79710207e28e86966cd136c621bfee"},
+ {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72ef3783be8cbdef6bca034606a5de3862be6b72415dc5cb1fb8ddbac110049a"},
+ {file = "pydantic-1.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:45edea10b75d3da43cfda12f3792833a3fa70b6eee4db1ed6aed528cef17c74e"},
+ {file = "pydantic-1.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63200cd8af1af2c07964546b7bc8f217e8bda9d0a2ef0ee0c797b36353914984"},
+ {file = "pydantic-1.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:305d0376c516b0dfa1dbefeae8c21042b57b496892d721905a6ec6b79494a66d"},
+ {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd326aff5d6c36f05735c7c9b3d5b0e933b4ca52ad0b6e4b38038d82703d35b"},
+ {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bb0452d7b8516178c969d305d9630a3c9b8cf16fcf4713261c9ebd465af0d73"},
+ {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9a9d9155e2a9f38b2eb9374c88f02fd4d6851ae17b65ee786a87d032f87008f8"},
+ {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f836444b4c5ece128b23ec36a446c9ab7f9b0f7981d0d27e13a7c366ee163f8a"},
+ {file = "pydantic-1.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:8481dca324e1c7b715ce091a698b181054d22072e848b6fc7895cd86f79b4449"},
+ {file = "pydantic-1.10.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87f831e81ea0589cd18257f84386bf30154c5f4bed373b7b75e5cb0b5d53ea87"},
+ {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce1612e98c6326f10888df951a26ec1a577d8df49ddcaea87773bfbe23ba5cc"},
+ {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58e41dd1e977531ac6073b11baac8c013f3cd8706a01d3dc74e86955be8b2c0c"},
+ {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6a4b0aab29061262065bbdede617ef99cc5914d1bf0ddc8bcd8e3d7928d85bd6"},
+ {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:36e44a4de37b8aecffa81c081dbfe42c4d2bf9f6dff34d03dce157ec65eb0f15"},
+ {file = "pydantic-1.10.5-cp37-cp37m-win_amd64.whl", hash = "sha256:261f357f0aecda005934e413dfd7aa4077004a174dafe414a8325e6098a8e419"},
+ {file = "pydantic-1.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b429f7c457aebb7fbe7cd69c418d1cd7c6fdc4d3c8697f45af78b8d5a7955760"},
+ {file = "pydantic-1.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:663d2dd78596c5fa3eb996bc3f34b8c2a592648ad10008f98d1348be7ae212fb"},
+ {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51782fd81f09edcf265823c3bf43ff36d00db246eca39ee765ef58dc8421a642"},
+ {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c428c0f64a86661fb4873495c4fac430ec7a7cef2b8c1c28f3d1a7277f9ea5ab"},
+ {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:76c930ad0746c70f0368c4596020b736ab65b473c1f9b3872310a835d852eb19"},
+ {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3257bd714de9db2102b742570a56bf7978e90441193acac109b1f500290f5718"},
+ {file = "pydantic-1.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:f5bee6c523d13944a1fdc6f0525bc86dbbd94372f17b83fa6331aabacc8fd08e"},
+ {file = "pydantic-1.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:532e97c35719f137ee5405bd3eeddc5c06eb91a032bc755a44e34a712420daf3"},
+ {file = "pydantic-1.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ca9075ab3de9e48b75fa8ccb897c34ccc1519177ad8841d99f7fd74cf43be5bf"},
+ {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd46a0e6296346c477e59a954da57beaf9c538da37b9df482e50f836e4a7d4bb"},
+ {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3353072625ea2a9a6c81ad01b91e5c07fa70deb06368c71307529abf70d23325"},
+ {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3f9d9b2be177c3cb6027cd67fbf323586417868c06c3c85d0d101703136e6b31"},
+ {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b473d00ccd5c2061fd896ac127b7755baad233f8d996ea288af14ae09f8e0d1e"},
+ {file = "pydantic-1.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:5f3bc8f103b56a8c88021d481410874b1f13edf6e838da607dcb57ecff9b4594"},
+ {file = "pydantic-1.10.5-py3-none-any.whl", hash = "sha256:7c5b94d598c90f2f46b3a983ffb46ab806a67099d118ae0da7ef21a2a4033b28"},
+ {file = "pydantic-1.10.5.tar.gz", hash = "sha256:9e337ac83686645a46db0e825acceea8e02fca4062483f40e9ae178e8bd1103a"},
]
[package.dependencies]
-typing-extensions = ">=4.1.0"
+python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""}
+typing-extensions = ">=4.2.0"
[package.extras]
dotenv = ["python-dotenv (>=0.10.4)"]
@@ -1500,21 +1658,21 @@ async-rediscache = ["async-rediscache[fakeredis] (==1.0.0rc2)"]
[[package]]
name = "pydocstyle"
-version = "6.1.1"
+version = "6.3.0"
description = "Python docstring style checker"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
- {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"},
- {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"},
+ {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"},
+ {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"},
]
[package.dependencies]
-snowballstemmer = "*"
+snowballstemmer = ">=2.2.0"
[package.extras]
-toml = ["toml"]
+toml = ["tomli (>=1.2.3)"]
[[package]]
name = "pyflakes"
@@ -1529,21 +1687,6 @@ files = [
]
[[package]]
-name = "pyparsing"
-version = "3.0.9"
-description = "pyparsing module - Classes and methods to define and execute parsing grammars"
-category = "dev"
-optional = false
-python-versions = ">=3.6.8"
-files = [
- {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
- {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
-]
-
-[package.extras]
-diagrams = ["jinja2", "railroad-diagrams"]
-
-[[package]]
name = "pyreadline3"
version = "3.4.1"
description = "A python implementation of GNU readline."
@@ -1557,14 +1700,14 @@ files = [
[[package]]
name = "pytest"
-version = "7.2.0"
+version = "7.2.2"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"},
- {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
+ {file = "pytest-7.2.2-py3-none-any.whl", hash = "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e"},
+ {file = "pytest-7.2.2.tar.gz", hash = "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4"},
]
[package.dependencies]
@@ -1600,29 +1743,30 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale
[[package]]
name = "pytest-subtests"
-version = "0.9.0"
+version = "0.10.0"
description = "unittest subTest() support and subtests fixture"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-subtests-0.9.0.tar.gz", hash = "sha256:c0317cd5f6a5eb3e957e89dbe4fc3322a9afddba2db8414355ed2a2cb91a844e"},
- {file = "pytest_subtests-0.9.0-py3-none-any.whl", hash = "sha256:f5f616b92c13405909d210569d6d3914db6fe156333ff5426534f97d5b447861"},
+ {file = "pytest-subtests-0.10.0.tar.gz", hash = "sha256:d9961a67c1791e8c1e32dce7a70ed1e54f3b1e641087f2094f2d37087ab7fb17"},
+ {file = "pytest_subtests-0.10.0-py3-none-any.whl", hash = "sha256:03a50a14f7981cd03090e8ca94205d783e290266d828728210c3d79f4d00c46f"},
]
[package.dependencies]
+attrs = ">=19.2.0"
pytest = ">=7.0"
[[package]]
name = "pytest-xdist"
-version = "3.0.2"
-description = "pytest xdist plugin for distributed testing and loop-on-failing modes"
+version = "3.2.0"
+description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "pytest-xdist-3.0.2.tar.gz", hash = "sha256:688da9b814370e891ba5de650c9327d1a9d861721a524eb917e620eec3e90291"},
- {file = "pytest_xdist-3.0.2-py3-none-any.whl", hash = "sha256:9feb9a18e1790696ea23e1434fa73b325ed4998b0e9fcb221f16fd1945e6df1b"},
+ {file = "pytest-xdist-3.2.0.tar.gz", hash = "sha256:fa10f95a2564cd91652f2d132725183c3b590d9fdcdec09d3677386ecf4c1ce9"},
+ {file = "pytest_xdist-3.2.0-py3-none-any.whl", hash = "sha256:336098e3bbd8193276867cc87db8b22903c3927665dff9d1ac8684c02f597b68"},
]
[package.dependencies]
@@ -1651,14 +1795,14 @@ six = ">=1.5"
[[package]]
name = "python-dotenv"
-version = "0.21.0"
+version = "1.0.0"
description = "Read key-value pairs from a .env file and set them as environment variables"
-category = "dev"
+category = "main"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "python-dotenv-0.21.0.tar.gz", hash = "sha256:b77d08274639e3d34145dfa6c7008e66df0f04b7be7a75fd0d5292c191d79045"},
- {file = "python_dotenv-0.21.0-py3-none-any.whl", hash = "sha256:1684eb44636dd462b66c3ee016599815514527ad99965de77f43e0944634a7e5"},
+ {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
+ {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
]
[package.extras]
@@ -1735,101 +1879,101 @@ files = [
[[package]]
name = "rapidfuzz"
-version = "2.13.2"
+version = "2.13.7"
description = "rapid fuzzy string matching"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "rapidfuzz-2.13.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91c049f7591d9e9f8bcc3c556c0c4b448223f564ad04511a8719d28f5d38daed"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:26e4b7f7941b92546a9b06ed75b40b5d7ceace8f3074d06cb3369349388d700d"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba2a8fbd21079093118c40e8e80068750c1619a5988e54220ea0929de48e7d65"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de707808f1997574014d9ba87c2d9f8a619688d615520e3dce958bf4398514c7"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba3f47a5b82de7304ae08e2a111ccc90a6ea06ecc3f25d7870d08be0973c94cb"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a181b6ef9b480b56b29bdc58dc50c198e93d33398d2f8e57da05cbddb095bd9e"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1e569953a2abe945f116a6c22b71e8fc02d7c27068af2af40990115f25c93e4"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:026f6ecd8948e168a89fc015ef34b6bcb200f30ac33f1480554d722181b38bea"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daf5e4f6b048c225a494c941a21463a0d397c39a080db8fece9b3136297ed240"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e39ae60598ed533f513db6d0370755685666024ab187a144fc688dd16cfa2d33"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e8d71f1611431c445ced872b303cd61f215551a11df0c7171e5993bed84867d5"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:f5d07dca69bf5a9f1e1cd5756ded6c197a27e8d8f2d8a3d99565add37a3bd1ec"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ac95981911559c842e1e4532e2f89ca255531db1d87257e5e69cd8c0c0d585fc"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-win32.whl", hash = "sha256:b4162b96d0908cb0ca218513eab559e9a77c8a1d9705c9133813634d9db27f4f"},
- {file = "rapidfuzz-2.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:84fd3cfc1cb872019e60a3844b1deedb176de0b9ded11bf30147137ac65185f5"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a599cc5cec196c0776faf65b74ac957354bd036f878905a16be9e20884870d02"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dbad2b7dad98b854a468d2c6a0b11464f68ce841428aded2f24f201a17a144eb"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ad78fb90540dc752b532345065146371acd3804a917c31fdd8a337951da9def2"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed0f99e0037b7f9f7117493e8723851c9eece4629906b2d5da21d3ef124149a2"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9abdffc590ef08d27dfd14d32e571f4a0f5f797f433f00c5faf4cf56ab62792a"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:352c920e166e838bc560014885ba979df656938fcc29a12c73ff06dc76b150d8"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c40acbadc965e72f1b44b3c665a59ec78a5e959757e52520bf73687c84ce6854"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a4053d5b62cedec83ff67d55e50da35f7736bed0a3b2af51fa6143f5fef3785"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0c324d82871fe50471f7ba38a21c3e68167e868f541f57ac0ef23c053bbef6e6"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cb4bd75518838b141dab8fe663de988c4d08502999068dc0b3949d43bd86ace6"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4b785ffbc16795fca27c9e899993df7721d886249061689c48dbfe60fa7d02a1"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1f363bf95d79dbafa8eac17697965e02e74da6f21b231b3fb808b2185bfed337"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f7cfc25d8143a7570f5e4c9da072a1e1c335d81a6926eb10c1fd3f637fa3c022"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-win32.whl", hash = "sha256:580f32cda7f911fef8266c7d811e580c18734cd12308d099b9975b914f33fcaf"},
- {file = "rapidfuzz-2.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:98be3e873c8f9d90a982891b2b061521ba4e5e49552ba2d3c1b0806dd5677f88"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:de8ec700127b645b0e2e28e694a2bba6dcb6a305ef080ad312f3086d47fb6973"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0ec73e6d3ad9442cfb5b94c137cf4241fff2860d81a9ee8be8c3d987bb400c0"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da5b7f35fc824cff36a2baa62486d5b427bf0fd7714c19704b5a7df82c2950b4"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f186b3a32d78af7a805584a7e1c2fdf6f6fd62939936e4f3df869158c147a55"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68f2e23eec59fc77bef164157889a2f7fb9800c47d615c58ee3809e2be3c8509"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4291a8c02d32aa6ebdffe63cf91abc2846383de95ae04a275f036c4e7a27f9ba"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a2eeee09ff716c8ff75942c1b93f0bca129590499f1127cbeb1b5cefbdc0c3d5"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2345656b30d7e18d18a4df5b765e4059111860a69bf3a36608a7d625e92567e6"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1e1dd1a328464dd2ae70f0e31ec403593fbb1b254bab7ac9f0cd08ba71c797d0"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:54fe1835f96c1033cdb7e4677497e784704c81d028c962d2222239ded93d978b"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6b68b6a12411cfacca16ace22d42ae8e9946315d79f49c6c97089789c235e795"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-win32.whl", hash = "sha256:9a740ddd3f7725c80e500f16b1b02b83a58b47164c0f3ddd9379208629c8c4b5"},
- {file = "rapidfuzz-2.13.2-cp37-cp37m-win_amd64.whl", hash = "sha256:378554acdcf8370cc5c777b1312921a2a670f68888e999ea1305599c55b67f5d"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa96955f2878116239db55506fe825f574651a8893d07a83de7b3c76a2f0386e"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b4df886481ca27a6d53d30a73625fb86dd308cf7d6d99d32e0dfbfcc8e8a75b9"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c66f3b8e93cdc3063ffd7224cad84951834d9434ffd27fa3fabad2e942ddab7"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d6d5ab0f12f2d7ae6aad77af67ae6253b6c1d54c320484f1acd2fce38b39ac2"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f0574d5d97722cfaf51b7dd667c8c836fa9fdf5a7d8158a787b98ee2788f6c5"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:83ff31d33c1391a0a6b23273b7f839dc8f7b5fb75ddca59ce4f334b83ca822bb"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94d8c65f48665f82064bea8a48ff185409a309ba396f5aec3a846831cbe36e6d"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c065a83883af2a9a0303b6c06844a700af0db97ff6dc894324f656ad8efe405"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:773c60a5368a361253efea194552ff9ed6879756f6feb71b61b514723f8cb726"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:12ece1a4d024297afa4b76d2ce71c2c65fc7eaa487a9ae9f6e17c160253cfd23"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2b491f2fac36718247070c3343f53aadbbe8684f3e0cf3b6cce1bd099e1d05cb"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:31370273787dca851e2df6f32f1ec8c61f86e9bbeb1cc42787020b6dfff952fd"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:47b5b227dc0bd53530dda55f344e1b24087fa99bb1bd7fceb6f5a2b1e2831ad4"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-win32.whl", hash = "sha256:8f09a16ae84b1decb9df9a7e393ec84a0b2a11da6356c3eedcf86da8cabe3071"},
- {file = "rapidfuzz-2.13.2-cp38-cp38-win_amd64.whl", hash = "sha256:e038e187270cbb987cf7c5d4b574fce7a32bc3d9593e9346d129874a7dc08dc3"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:aee5dce78e157e503269121ad6f886acab4b1ab3e3956bcdf0549d54596eab57"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80073e897af0669f496d23899583b5c2f0decc2ec06aa7c36a3b8fb16eda5e0e"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ce40c2a68fe28e05a4f66229c11885ef928086fbcd2eff086decdacfe5254da9"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd268701bf930bbb2d12f6f7f75c681e16fee646ea1663d258e825bf919ca7a1"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5d93e77881497f76e77056feea4c375732d27151151273d6e4cb8a1defbf17a"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b27c3e2b1789a635b9df1d74838ae032dc2dbc596ece5d89f9de2c37ba0a6dfe"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e49f412fe58c793af61b04fb5536534dfc95000b6c2bf0bfa42fcf7eb1453d42"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27bbdee91718019e251d315c6e9b03aa5b7663b90e4228ac1ddb0a567ff3634b"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b51d45cb9ed81669206e338413ba224c06a8900ab0cc9106f4750ac73dc687bb"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3479a2fd88504cc41eb707650e81fd7ce864f2418fee24f7224775b539536b39"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7adb4327453c1550f51d6ba13d718a84091f82230c1d0daca6db628e57d0fa5a"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3a4e87aae287d757d9c5b045c819c985b02b38dea3f75630cc24d53826e640be"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:13e175b1643306558a3d7604789c4a8c217a64406fe82bf1a9e52efb5dea53ae"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-win32.whl", hash = "sha256:fb896fafa206db4d55f4412135c3ae28fbc56b8afc476970d0c5f29d2ce50948"},
- {file = "rapidfuzz-2.13.2-cp39-cp39-win_amd64.whl", hash = "sha256:37a9a8f5737b8e429291148be67d2dd8ba779a69a87ad95d2785bb3d80fd1df7"},
- {file = "rapidfuzz-2.13.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d6cb51a8459e7160366c6c7b31e8f9a671f7d617591c0ad305f2697707061da2"},
- {file = "rapidfuzz-2.13.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:343fe1fcbbf55c994b22962bfb46f6b6903faeac5a2671b2f0fa5e3664de3e66"},
- {file = "rapidfuzz-2.13.2-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d9d081cd8e0110661c8a3e728d7b491a903bb54d34de40b17d19144563bd5f6"},
- {file = "rapidfuzz-2.13.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f93a6740fef239a8aca6521cc1891d448664115b53528a3dd7f95c1781a5fa6"},
- {file = "rapidfuzz-2.13.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:deaf26cc23cfbf90650993108de888533635b981a7157a0234b4753527ac6e5c"},
- {file = "rapidfuzz-2.13.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b6a0617ba60f81a8df3b9ddca09f591a0a0c8269402169825fcd50daa03e5c25"},
- {file = "rapidfuzz-2.13.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bee1065d55edfeabdb98211bb673cb44a8b118cded42d743f7d59c07b05a80d"},
- {file = "rapidfuzz-2.13.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e5afd5477332ceeb960e2002d5bb0b04ad00b40037a0ab1de9916041badcf00"},
- {file = "rapidfuzz-2.13.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eead76c172ba08d49ea621016cf84031fff1ee33d7db751d7003e491e55e66af"},
- {file = "rapidfuzz-2.13.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:83b1e8aca6c3fad058d8a2b7653b7496df0c4aca903d589bb0e4184868290767"},
- {file = "rapidfuzz-2.13.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:41610c3a9be4febcbcac2b69b2f45d0da33e39d1194e5ffa3dd3a104d5a67a70"},
- {file = "rapidfuzz-2.13.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aacc4eb58d6bccf6ec571619bee35861d4103961b9873d9b0829d347ca8a63e"},
- {file = "rapidfuzz-2.13.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:791d90aa1c68b5485f6340a8dc485aba7e9bcb729572449174ded0692e7e7ad0"},
- {file = "rapidfuzz-2.13.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d4f94b408c9f9218d61e8af55e43c8102f813eea2cf82de10906b032ddcb9aa"},
- {file = "rapidfuzz-2.13.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ac6a8a34f858f3862798383f51012788df6be823e2874fa426667a4da94ded7e"},
- {file = "rapidfuzz-2.13.2.tar.gz", hash = "sha256:1c67007161655c59e13bba130a2db29d7c9e5c81bcecb8846a3dd7386065eb24"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b75dd0928ce8e216f88660ab3d5c5ffe990f4dd682fd1709dba29d5dafdde6de"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:24d3fea10680d085fd0a4d76e581bfb2b1074e66e78fd5964d4559e1fcd2a2d4"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8109e0324d21993d5b2d111742bf5958f3516bf8c59f297c5d1cc25a2342eb66"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f705652360d520c2de52bee11100c92f59b3e3daca308ebb150cbc58aecdad"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7496e8779905b02abc0ab4ba2a848e802ab99a6e20756ffc967a0de4900bd3da"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:24eb6b843492bdc63c79ee4b2f104059b7a2201fef17f25177f585d3be03405a"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:467c1505362823a5af12b10234cb1c4771ccf124c00e3fc9a43696512bd52293"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53dcae85956853b787c27c1cb06f18bb450e22cf57a4ad3444cf03b8ff31724a"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:46b9b8aa09998bc48dd800854e8d9b74bc534d7922c1d6e1bbf783e7fa6ac29c"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1fbad8fb28d98980f5bff33c7842efef0315d42f0cd59082108482a7e6b61410"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:43fb8cb030f888c3f076d40d428ed5eb4331f5dd6cf1796cfa39c67bf0f0fc1e"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:b6bad92de071cbffa2acd4239c1779f66851b60ffbbda0e4f4e8a2e9b17e7eef"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d00df2e4a81ffa56a6b1ec4d2bc29afdcb7f565e0b8cd3092fece2290c4c7a79"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-win32.whl", hash = "sha256:2c836f0f2d33d4614c3fbaf9a1eb5407c0fe23f8876f47fd15b90f78daa64c34"},
+ {file = "rapidfuzz-2.13.7-cp310-cp310-win_amd64.whl", hash = "sha256:c36fd260084bb636b9400bb92016c6bd81fd80e59ed47f2466f85eda1fc9f782"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b34e8c0e492949ecdd5da46a1cfc856a342e2f0389b379b1a45a3cdcd3176a6e"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:875d51b3497439a72e2d76183e1cb5468f3f979ab2ddfc1d1f7dde3b1ecfb42f"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ae33a72336059213996fe4baca4e0e4860913905c2efb7c991eab33b95a98a0a"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5585189b3d90d81ccd62d4f18530d5ac8972021f0aaaa1ffc6af387ff1dce75"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42085d4b154a8232767de8296ac39c8af5bccee6b823b0507de35f51c9cbc2d7"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:585206112c294e335d84de5d5f179c0f932837752d7420e3de21db7fdc476278"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f891b98f8bc6c9d521785816085e9657212621e93f223917fb8e32f318b2957e"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08590905a95ccfa43f4df353dcc5d28c15d70664299c64abcad8721d89adce4f"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b5dd713a1734574c2850c566ac4286594bacbc2d60b9170b795bee4b68656625"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:988f8f6abfba7ee79449f8b50687c174733b079521c3cc121d65ad2d38831846"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b3210869161a864f3831635bb13d24f4708c0aa7208ef5baac1ac4d46e9b4208"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f6fe570e20e293eb50491ae14ddeef71a6a7e5f59d7e791393ffa99b13f1f8c2"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6120f2995f5154057454c5de99d86b4ef3b38397899b5da1265467e8980b2f60"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-win32.whl", hash = "sha256:b20141fa6cee041917801de0bab503447196d372d4c7ee9a03721b0a8edf5337"},
+ {file = "rapidfuzz-2.13.7-cp311-cp311-win_amd64.whl", hash = "sha256:ec55a81ac2b0f41b8d6fb29aad16e55417036c7563bad5568686931aa4ff08f7"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7d005e058d86f2a968a8d28ca6f2052fab1f124a39035aa0523261d6baf21e1f"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe59a0c21a032024edb0c8e43f5dee5623fef0b65a1e3c1281836d9ce199af3b"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfc04f7647c29fb48da7a04082c34cdb16f878d3c6d098d62d5715c0ad3000c"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68a89bb06d5a331511961f4d3fa7606f8e21237467ba9997cae6f67a1c2c2b9e"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:effe182767d102cb65dfbbf74192237dbd22d4191928d59415aa7d7c861d8c88"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25b4cedf2aa19fb7212894ce5f5219010cce611b60350e9a0a4d492122e7b351"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3a9bd02e1679c0fd2ecf69b72d0652dbe2a9844eaf04a36ddf4adfbd70010e95"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5e2b3d020219baa75f82a4e24b7c8adcb598c62f0e54e763c39361a9e5bad510"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:cf62dacb3f9234f3fddd74e178e6d25c68f2067fde765f1d95f87b1381248f58"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:fa263135b892686e11d5b84f6a1892523123a00b7e5882eff4fbdabb38667347"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa4c598ed77f74ec973247ca776341200b0f93ec3883e34c222907ce72cb92a4"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-win32.whl", hash = "sha256:c2523f8180ebd9796c18d809e9a19075a1060b1a170fde3799e83db940c1b6d5"},
+ {file = "rapidfuzz-2.13.7-cp37-cp37m-win_amd64.whl", hash = "sha256:5ada0a14c67452358c1ee52ad14b80517a87b944897aaec3e875279371a9cb96"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ca8a23097c1f50e0fdb4de9e427537ca122a18df2eead06ed39c3a0bef6d9d3a"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9be02162af0376d64b840f2fc8ee3366794fc149f1e06d095a6a1d42447d97c5"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af4f7c3c904ca709493eb66ca9080b44190c38e9ecb3b48b96d38825d5672559"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f50d1227e6e2a0e3ae1fb1c9a2e1c59577d3051af72c7cab2bcc430cb5e18da"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c71d9d512b76f05fa00282227c2ae884abb60e09f08b5ca3132b7e7431ac7f0d"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b52ac2626945cd21a2487aeefed794c14ee31514c8ae69b7599170418211e6f6"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca00fafd2756bc9649bf80f1cf72c647dce38635f0695d7ce804bc0f759aa756"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d248a109699ce9992304e79c1f8735c82cc4c1386cd8e27027329c0549f248a2"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c88adbcb933f6b8612f6c593384bf824e562bb35fc8a0f55fac690ab5b3486e5"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c8601a66fbfc0052bb7860d2eacd303fcde3c14e87fdde409eceff516d659e77"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:27be9c63215d302ede7d654142a2e21f0d34ea6acba512a4ae4cfd52bbaa5b59"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3dcffe1f3cbda0dc32133a2ae2255526561ca594f15f9644384549037b355245"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8450d15f7765482e86ef9be2ad1a05683cd826f59ad236ef7b9fb606464a56aa"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-win32.whl", hash = "sha256:460853983ab88f873173e27cc601c5276d469388e6ad6e08c4fd57b2a86f1064"},
+ {file = "rapidfuzz-2.13.7-cp38-cp38-win_amd64.whl", hash = "sha256:424f82c35dbe4f83bdc3b490d7d696a1dc6423b3d911460f5493b7ffae999fd2"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c3fbe449d869ea4d0909fc9d862007fb39a584fb0b73349a6aab336f0d90eaed"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:16080c05a63d6042643ae9b6cfec1aefd3e61cef53d0abe0df3069b9d4b72077"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dbcf5371ea704759fcce772c66a07647751d1f5dbdec7818331c9b31ae996c77"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114810491efb25464016fd554fdf1e20d390309cecef62587494fc474d4b926f"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99a84ab9ac9a823e7e93b4414f86344052a5f3e23b23aa365cda01393ad895bd"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:81642a24798851b118f82884205fc1bd9ff70b655c04018c467824b6ecc1fabc"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3741cb0bf9794783028e8b0cf23dab917fa5e37a6093b94c4c2f805f8e36b9f"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:759a3361711586a29bc753d3d1bdb862983bd9b9f37fbd7f6216c24f7c972554"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1333fb3d603d6b1040e365dca4892ba72c7e896df77a54eae27dc07db90906e3"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:916bc2e6cf492c77ad6deb7bcd088f0ce9c607aaeabc543edeb703e1fbc43e31"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:23524635840500ce6f4d25005c9529a97621689c85d2f727c52eed1782839a6a"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:ebe303cd9839af69dd1f7942acaa80b1ba90bacef2e7ded9347fbed4f1654672"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fe56659ccadbee97908132135de4b875543353351e0c92e736b7c57aee298b5a"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-win32.whl", hash = "sha256:3f11a7eff7bc6301cd6a5d43f309e22a815af07e1f08eeb2182892fca04c86cb"},
+ {file = "rapidfuzz-2.13.7-cp39-cp39-win_amd64.whl", hash = "sha256:e8914dad106dacb0775718e54bf15e528055c4e92fb2677842996f2d52da5069"},
+ {file = "rapidfuzz-2.13.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f7930adf84301797c3f09c94b9c5a9ed90a9e8b8ed19b41d2384937e0f9f5bd"},
+ {file = "rapidfuzz-2.13.7-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c31022d9970177f6affc6d5dd757ed22e44a10890212032fabab903fdee3bfe7"},
+ {file = "rapidfuzz-2.13.7-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f42b82f268689f429def9ecfb86fa65ceea0eaf3fed408b570fe113311bf5ce7"},
+ {file = "rapidfuzz-2.13.7-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b477b43ced896301665183a5e0faec0f5aea2373005648da8bdcb3c4b73f280"},
+ {file = "rapidfuzz-2.13.7-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d63def9bbc6b35aef4d76dc740301a4185867e8870cbb8719ec9de672212fca8"},
+ {file = "rapidfuzz-2.13.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c66546e30addb04a16cd864f10f5821272a1bfe6462ee5605613b4f1cb6f7b48"},
+ {file = "rapidfuzz-2.13.7-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f799d1d6c33d81e983d3682571cc7d993ae7ff772c19b3aabb767039c33f6d1e"},
+ {file = "rapidfuzz-2.13.7-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82f20c0060ffdaadaf642b88ab0aa52365b56dffae812e188e5bdb998043588"},
+ {file = "rapidfuzz-2.13.7-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:042644133244bfa7b20de635d500eb9f46af7097f3d90b1724f94866f17cb55e"},
+ {file = "rapidfuzz-2.13.7-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75c45dcd595f8178412367e302fd022860ea025dc4a78b197b35428081ed33d5"},
+ {file = "rapidfuzz-2.13.7-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3d8b081988d0a49c486e4e845a547565fee7c6e7ad8be57ff29c3d7c14c6894c"},
+ {file = "rapidfuzz-2.13.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16ffad751f43ab61001187b3fb4a9447ec2d1aedeff7c5bac86d3b95f9980cc3"},
+ {file = "rapidfuzz-2.13.7-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:020858dd89b60ce38811cd6e37875c4c3c8d7fcd8bc20a0ad2ed1f464b34dc4e"},
+ {file = "rapidfuzz-2.13.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cda1e2f66bb4ba7261a0f4c2d052d5d909798fca557cbff68f8a79a87d66a18f"},
+ {file = "rapidfuzz-2.13.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b6389c50d8d214c9cd11a77f6d501529cb23279a9c9cafe519a3a4b503b5f72a"},
+ {file = "rapidfuzz-2.13.7.tar.gz", hash = "sha256:8d3e252d4127c79b4d7c2ae47271636cbaca905c8bb46d80c7930ab906cf4b5c"},
]
[package.extras]
@@ -1837,14 +1981,14 @@ full = ["numpy"]
[[package]]
name = "redis"
-version = "4.4.2"
+version = "4.5.1"
description = "Python client for Redis database and key-value store"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "redis-4.4.2-py3-none-any.whl", hash = "sha256:e6206448e2f8a432871d07d432c13ed6c2abcf6b74edb436c99752b1371be387"},
- {file = "redis-4.4.2.tar.gz", hash = "sha256:a010f6cb7378065040a02839c3f75c7e0fb37a87116fb4a95be82a95552776c7"},
+ {file = "redis-4.5.1-py3-none-any.whl", hash = "sha256:5deb072d26e67d2be1712603bfb7947ec3431fb0eec9c578994052e33035af6d"},
+ {file = "redis-4.5.1.tar.gz", hash = "sha256:1eec3741cda408d3a5f84b78d089c8b8d895f21b3b050988351e925faf202864"},
]
[package.dependencies]
@@ -1954,19 +2098,19 @@ files = [
[[package]]
name = "requests"
-version = "2.28.1"
+version = "2.28.2"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=3.7, <4"
files = [
- {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
- {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
+ {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"},
+ {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"},
]
[package.dependencies]
certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<3"
+charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<1.27"
@@ -1991,6 +2135,24 @@ requests = ">=1.0.0"
six = "*"
[[package]]
+name = "rfc3986"
+version = "1.5.0"
+description = "Validating URI References per RFC 3986"
+category = "dev"
+optional = false
+python-versions = "*"
+files = [
+ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
+ {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
+]
+
+[package.dependencies]
+idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
+
+[package.extras]
+idna2008 = ["idna"]
+
+[[package]]
name = "sentry-sdk"
version = "1.16.0"
description = "Python client for Sentry (https://sentry.io)"
@@ -2033,18 +2195,18 @@ tornado = ["tornado (>=5)"]
[[package]]
name = "setuptools"
-version = "65.6.3"
+version = "67.5.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
- {file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"},
- {file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"},
+ {file = "setuptools-67.5.1-py3-none-any.whl", hash = "sha256:1c39d42bda4cb89f7fdcad52b6762e3c309ec8f8715b27c684176b7d71283242"},
+ {file = "setuptools-67.5.1.tar.gz", hash = "sha256:15136a251127da2d2e77ac7a1bc231eb504654f7e3346d93613a13f2e2787535"},
]
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
@@ -2072,6 +2234,18 @@ files = [
]
[[package]]
+name = "sniffio"
+version = "1.3.0"
+description = "Sniff out which async library your code is running under"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
+ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
+]
+
+[[package]]
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
@@ -2097,14 +2271,14 @@ files = [
[[package]]
name = "soupsieve"
-version = "2.3.2.post1"
+version = "2.4"
description = "A modern CSS selector implementation for Beautiful Soup."
category = "main"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"},
- {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"},
+ {file = "soupsieve-2.4-py3-none-any.whl", hash = "sha256:49e5368c2cda80ee7e84da9dbe3e110b70a4575f196efb74e51b94549d921955"},
+ {file = "soupsieve-2.4.tar.gz", hash = "sha256:e28dba9ca6c7c00173e34e4ba57448f0688bb681b7c5e8bf4971daafc093d69a"},
]
[[package]]
@@ -2156,18 +2330,6 @@ requests = ">=2.1.0"
requests-file = ">=1.4"
[[package]]
-name = "toml"
-version = "0.10.2"
-description = "Python Library for Tom's Obvious, Minimal Language"
-category = "dev"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
- {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-
-[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
@@ -2181,26 +2343,26 @@ files = [
[[package]]
name = "typing-extensions"
-version = "4.4.0"
+version = "4.5.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
- {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"},
+ {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"},
+ {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"},
]
[[package]]
name = "urllib3"
-version = "1.26.13"
+version = "1.26.14"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
- {file = "urllib3-1.26.13-py2.py3-none-any.whl", hash = "sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc"},
- {file = "urllib3-1.26.13.tar.gz", hash = "sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8"},
+ {file = "urllib3-1.26.14-py2.py3-none-any.whl", hash = "sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1"},
+ {file = "urllib3-1.26.14.tar.gz", hash = "sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72"},
]
[package.extras]
@@ -2210,104 +2372,119 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "virtualenv"
-version = "20.16.7"
+version = "20.20.0"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "virtualenv-20.16.7-py3-none-any.whl", hash = "sha256:efd66b00386fdb7dbe4822d172303f40cd05e50e01740b19ea42425cbe653e29"},
- {file = "virtualenv-20.16.7.tar.gz", hash = "sha256:8691e3ff9387f743e00f6bb20f70121f5e4f596cae754531f2b3b3a1b1ac696e"},
+ {file = "virtualenv-20.20.0-py3-none-any.whl", hash = "sha256:3c22fa5a7c7aa106ced59934d2c20a2ecb7f49b4130b8bf444178a16b880fa45"},
+ {file = "virtualenv-20.20.0.tar.gz", hash = "sha256:a8a4b8ca1e28f864b7514a253f98c1d62b64e31e77325ba279248c65fb4fcef4"},
]
[package.dependencies]
distlib = ">=0.3.6,<1"
filelock = ">=3.4.1,<4"
-platformdirs = ">=2.4,<3"
+platformdirs = ">=2.4,<4"
[package.extras]
-docs = ["proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-argparse (>=0.3.2)", "sphinx-rtd-theme (>=1)", "towncrier (>=22.8)"]
-testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"]
+docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"]
+test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23)", "pytest (>=7.2.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"]
[[package]]
name = "wcwidth"
-version = "0.2.5"
+version = "0.2.6"
description = "Measures the displayed width of unicode strings in a terminal"
category = "dev"
optional = false
python-versions = "*"
files = [
- {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
- {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+ {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
+ {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
]
[[package]]
name = "yarl"
-version = "1.8.1"
+version = "1.8.2"
description = "Yet another URL library"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:abc06b97407868ef38f3d172762f4069323de52f2b70d133d096a48d72215d28"},
- {file = "yarl-1.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:07b21e274de4c637f3e3b7104694e53260b5fc10d51fb3ec5fed1da8e0f754e3"},
- {file = "yarl-1.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9de955d98e02fab288c7718662afb33aab64212ecb368c5dc866d9a57bf48880"},
- {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ec362167e2c9fd178f82f252b6d97669d7245695dc057ee182118042026da40"},
- {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20df6ff4089bc86e4a66e3b1380460f864df3dd9dccaf88d6b3385d24405893b"},
- {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5999c4662631cb798496535afbd837a102859568adc67d75d2045e31ec3ac497"},
- {file = "yarl-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed19b74e81b10b592084a5ad1e70f845f0aacb57577018d31de064e71ffa267a"},
- {file = "yarl-1.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e4808f996ca39a6463f45182e2af2fae55e2560be586d447ce8016f389f626f"},
- {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2d800b9c2eaf0684c08be5f50e52bfa2aa920e7163c2ea43f4f431e829b4f0fd"},
- {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6628d750041550c5d9da50bb40b5cf28a2e63b9388bac10fedd4f19236ef4957"},
- {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f5af52738e225fcc526ae64071b7e5342abe03f42e0e8918227b38c9aa711e28"},
- {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:76577f13333b4fe345c3704811ac7509b31499132ff0181f25ee26619de2c843"},
- {file = "yarl-1.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c03f456522d1ec815893d85fccb5def01ffaa74c1b16ff30f8aaa03eb21e453"},
- {file = "yarl-1.8.1-cp310-cp310-win32.whl", hash = "sha256:ea30a42dc94d42f2ba4d0f7c0ffb4f4f9baa1b23045910c0c32df9c9902cb272"},
- {file = "yarl-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:9130ddf1ae9978abe63808b6b60a897e41fccb834408cde79522feb37fb72fb0"},
- {file = "yarl-1.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0ab5a138211c1c366404d912824bdcf5545ccba5b3ff52c42c4af4cbdc2c5035"},
- {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0fb2cb4204ddb456a8e32381f9a90000429489a25f64e817e6ff94879d432fc"},
- {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85cba594433915d5c9a0d14b24cfba0339f57a2fff203a5d4fd070e593307d0b"},
- {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ca7e596c55bd675432b11320b4eacc62310c2145d6801a1f8e9ad160685a231"},
- {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f77539733e0ec2475ddcd4e26777d08996f8cd55d2aef82ec4d3896687abda"},
- {file = "yarl-1.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29e256649f42771829974e742061c3501cc50cf16e63f91ed8d1bf98242e5507"},
- {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7fce6cbc6c170ede0221cc8c91b285f7f3c8b9fe28283b51885ff621bbe0f8ee"},
- {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:59ddd85a1214862ce7c7c66457f05543b6a275b70a65de366030d56159a979f0"},
- {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:12768232751689c1a89b0376a96a32bc7633c08da45ad985d0c49ede691f5c0d"},
- {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:b19255dde4b4f4c32e012038f2c169bb72e7f081552bea4641cab4d88bc409dd"},
- {file = "yarl-1.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6c8148e0b52bf9535c40c48faebb00cb294ee577ca069d21bd5c48d302a83780"},
- {file = "yarl-1.8.1-cp37-cp37m-win32.whl", hash = "sha256:de839c3a1826a909fdbfe05f6fe2167c4ab033f1133757b5936efe2f84904c07"},
- {file = "yarl-1.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:dd032e8422a52e5a4860e062eb84ac94ea08861d334a4bcaf142a63ce8ad4802"},
- {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:19cd801d6f983918a3f3a39f3a45b553c015c5aac92ccd1fac619bd74beece4a"},
- {file = "yarl-1.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6347f1a58e658b97b0a0d1ff7658a03cb79bdbda0331603bed24dd7054a6dea1"},
- {file = "yarl-1.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c0da7e44d0c9108d8b98469338705e07f4bb7dab96dbd8fa4e91b337db42548"},
- {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5587bba41399854703212b87071c6d8638fa6e61656385875f8c6dff92b2e461"},
- {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31a9a04ecccd6b03e2b0e12e82131f1488dea5555a13a4d32f064e22a6003cfe"},
- {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205904cffd69ae972a1707a1bd3ea7cded594b1d773a0ce66714edf17833cdae"},
- {file = "yarl-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea513a25976d21733bff523e0ca836ef1679630ef4ad22d46987d04b372d57fc"},
- {file = "yarl-1.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0b51530877d3ad7a8d47b2fff0c8df3b8f3b8deddf057379ba50b13df2a5eae"},
- {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d2b8f245dad9e331540c350285910b20dd913dc86d4ee410c11d48523c4fd546"},
- {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ab2a60d57ca88e1d4ca34a10e9fb4ab2ac5ad315543351de3a612bbb0560bead"},
- {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:449c957ffc6bc2309e1fbe67ab7d2c1efca89d3f4912baeb8ead207bb3cc1cd4"},
- {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a165442348c211b5dea67c0206fc61366212d7082ba8118c8c5c1c853ea4d82e"},
- {file = "yarl-1.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b3ded839a5c5608eec8b6f9ae9a62cb22cd037ea97c627f38ae0841a48f09eae"},
- {file = "yarl-1.8.1-cp38-cp38-win32.whl", hash = "sha256:c1445a0c562ed561d06d8cbc5c8916c6008a31c60bc3655cdd2de1d3bf5174a0"},
- {file = "yarl-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:56c11efb0a89700987d05597b08a1efcd78d74c52febe530126785e1b1a285f4"},
- {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e80ed5a9939ceb6fda42811542f31c8602be336b1fb977bccb012e83da7e4936"},
- {file = "yarl-1.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6afb336e23a793cd3b6476c30f030a0d4c7539cd81649683b5e0c1b0ab0bf350"},
- {file = "yarl-1.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c322cbaa4ed78a8aac89b2174a6df398faf50e5fc12c4c191c40c59d5e28357"},
- {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fae37373155f5ef9b403ab48af5136ae9851151f7aacd9926251ab26b953118b"},
- {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5395da939ffa959974577eff2cbfc24b004a2fb6c346918f39966a5786874e54"},
- {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:076eede537ab978b605f41db79a56cad2e7efeea2aa6e0fa8f05a26c24a034fb"},
- {file = "yarl-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d1a50e461615747dd93c099f297c1994d472b0f4d2db8a64e55b1edf704ec1c"},
- {file = "yarl-1.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7de89c8456525650ffa2bb56a3eee6af891e98f498babd43ae307bd42dca98f6"},
- {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4a88510731cd8d4befaba5fbd734a7dd914de5ab8132a5b3dde0bbd6c9476c64"},
- {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2d93a049d29df172f48bcb09acf9226318e712ce67374f893b460b42cc1380ae"},
- {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:21ac44b763e0eec15746a3d440f5e09ad2ecc8b5f6dcd3ea8cb4773d6d4703e3"},
- {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d0272228fabe78ce00a3365ffffd6f643f57a91043e119c289aaba202f4095b0"},
- {file = "yarl-1.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99449cd5366fe4608e7226c6cae80873296dfa0cde45d9b498fefa1de315a09e"},
- {file = "yarl-1.8.1-cp39-cp39-win32.whl", hash = "sha256:8b0af1cf36b93cee99a31a545fe91d08223e64390c5ecc5e94c39511832a4bb6"},
- {file = "yarl-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:de49d77e968de6626ba7ef4472323f9d2e5a56c1d85b7c0e2a190b2173d3b9be"},
- {file = "yarl-1.8.1.tar.gz", hash = "sha256:af887845b8c2e060eb5605ff72b6f2dd2aab7a761379373fd89d314f4752abbf"},
+ {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bb81f753c815f6b8e2ddd2eef3c855cf7da193b82396ac013c661aaa6cc6b0a5"},
+ {file = "yarl-1.8.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:47d49ac96156f0928f002e2424299b2c91d9db73e08c4cd6742923a086f1c863"},
+ {file = "yarl-1.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc056e35fa6fba63248d93ff6e672c096f95f7836938241ebc8260e062832fe"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58a3c13d1c3005dbbac5c9f0d3210b60220a65a999b1833aa46bd6677c69b08e"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10b08293cda921157f1e7c2790999d903b3fd28cd5c208cf8826b3b508026996"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de986979bbd87272fe557e0a8fcb66fd40ae2ddfe28a8b1ce4eae22681728fef"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c4fcfa71e2c6a3cb568cf81aadc12768b9995323186a10827beccf5fa23d4f8"},
+ {file = "yarl-1.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae4d7ff1049f36accde9e1ef7301912a751e5bae0a9d142459646114c70ecba6"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf071f797aec5b96abfc735ab97da9fd8f8768b43ce2abd85356a3127909d146"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:74dece2bfc60f0f70907c34b857ee98f2c6dd0f75185db133770cd67300d505f"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:df60a94d332158b444301c7f569659c926168e4d4aad2cfbf4bce0e8fb8be826"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:63243b21c6e28ec2375f932a10ce7eda65139b5b854c0f6b82ed945ba526bff3"},
+ {file = "yarl-1.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cfa2bbca929aa742b5084fd4663dd4b87c191c844326fcb21c3afd2d11497f80"},
+ {file = "yarl-1.8.2-cp310-cp310-win32.whl", hash = "sha256:b05df9ea7496df11b710081bd90ecc3a3db6adb4fee36f6a411e7bc91a18aa42"},
+ {file = "yarl-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:24ad1d10c9db1953291f56b5fe76203977f1ed05f82d09ec97acb623a7976574"},
+ {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2a1fca9588f360036242f379bfea2b8b44cae2721859b1c56d033adfd5893634"},
+ {file = "yarl-1.8.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f37db05c6051eff17bc832914fe46869f8849de5b92dc4a3466cd63095d23dfd"},
+ {file = "yarl-1.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77e913b846a6b9c5f767b14dc1e759e5aff05502fe73079f6f4176359d832581"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0978f29222e649c351b173da2b9b4665ad1feb8d1daa9d971eb90df08702668a"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388a45dc77198b2460eac0aca1efd6a7c09e976ee768b0d5109173e521a19daf"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2305517e332a862ef75be8fad3606ea10108662bc6fe08509d5ca99503ac2aee"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42430ff511571940d51e75cf42f1e4dbdded477e71c1b7a17f4da76c1da8ea76"},
+ {file = "yarl-1.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3150078118f62371375e1e69b13b48288e44f6691c1069340081c3fd12c94d5b"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c15163b6125db87c8f53c98baa5e785782078fbd2dbeaa04c6141935eb6dab7a"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d04acba75c72e6eb90745447d69f84e6c9056390f7a9724605ca9c56b4afcc6"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e7fd20d6576c10306dea2d6a5765f46f0ac5d6f53436217913e952d19237efc4"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:75c16b2a900b3536dfc7014905a128a2bea8fb01f9ee26d2d7d8db0a08e7cb2c"},
+ {file = "yarl-1.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6d88056a04860a98341a0cf53e950e3ac9f4e51d1b6f61a53b0609df342cc8b2"},
+ {file = "yarl-1.8.2-cp311-cp311-win32.whl", hash = "sha256:fb742dcdd5eec9f26b61224c23baea46c9055cf16f62475e11b9b15dfd5c117b"},
+ {file = "yarl-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:8c46d3d89902c393a1d1e243ac847e0442d0196bbd81aecc94fcebbc2fd5857c"},
+ {file = "yarl-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ceff9722e0df2e0a9e8a79c610842004fa54e5b309fe6d218e47cd52f791d7ef"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f6b4aca43b602ba0f1459de647af954769919c4714706be36af670a5f44c9c1"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1684a9bd9077e922300ecd48003ddae7a7474e0412bea38d4631443a91d61077"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebb78745273e51b9832ef90c0898501006670d6e059f2cdb0e999494eb1450c2"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3adeef150d528ded2a8e734ebf9ae2e658f4c49bf413f5f157a470e17a4a2e89"},
+ {file = "yarl-1.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57a7c87927a468e5a1dc60c17caf9597161d66457a34273ab1760219953f7f4c"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:efff27bd8cbe1f9bd127e7894942ccc20c857aa8b5a0327874f30201e5ce83d0"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a783cd344113cb88c5ff7ca32f1f16532a6f2142185147822187913eb989f739"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:705227dccbe96ab02c7cb2c43e1228e2826e7ead880bb19ec94ef279e9555b5b"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:34c09b43bd538bf6c4b891ecce94b6fa4f1f10663a8d4ca589a079a5018f6ed7"},
+ {file = "yarl-1.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a48f4f7fea9a51098b02209d90297ac324241bf37ff6be6d2b0149ab2bd51b37"},
+ {file = "yarl-1.8.2-cp37-cp37m-win32.whl", hash = "sha256:0414fd91ce0b763d4eadb4456795b307a71524dbacd015c657bb2a39db2eab89"},
+ {file = "yarl-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d881d152ae0007809c2c02e22aa534e702f12071e6b285e90945aa3c376463c5"},
+ {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5df5e3d04101c1e5c3b1d69710b0574171cc02fddc4b23d1b2813e75f35a30b1"},
+ {file = "yarl-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7a66c506ec67eb3159eea5096acd05f5e788ceec7b96087d30c7d2865a243918"},
+ {file = "yarl-1.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2b4fa2606adf392051d990c3b3877d768771adc3faf2e117b9de7eb977741229"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e21fb44e1eff06dd6ef971d4bdc611807d6bd3691223d9c01a18cec3677939e"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93202666046d9edadfe9f2e7bf5e0782ea0d497b6d63da322e541665d65a044e"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc77086ce244453e074e445104f0ecb27530d6fd3a46698e33f6c38951d5a0f1"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dd68a92cab699a233641f5929a40f02a4ede8c009068ca8aa1fe87b8c20ae3"},
+ {file = "yarl-1.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b372aad2b5f81db66ee7ec085cbad72c4da660d994e8e590c997e9b01e44901"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e6f3515aafe0209dd17fb9bdd3b4e892963370b3de781f53e1746a521fb39fc0"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dfef7350ee369197106805e193d420b75467b6cceac646ea5ed3049fcc950a05"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:728be34f70a190566d20aa13dc1f01dc44b6aa74580e10a3fb159691bc76909d"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ff205b58dc2929191f68162633d5e10e8044398d7a45265f90a0f1d51f85f72c"},
+ {file = "yarl-1.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baf211dcad448a87a0d9047dc8282d7de59473ade7d7fdf22150b1d23859f946"},
+ {file = "yarl-1.8.2-cp38-cp38-win32.whl", hash = "sha256:272b4f1599f1b621bf2aabe4e5b54f39a933971f4e7c9aa311d6d7dc06965165"},
+ {file = "yarl-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:326dd1d3caf910cd26a26ccbfb84c03b608ba32499b5d6eeb09252c920bcbe4f"},
+ {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f8ca8ad414c85bbc50f49c0a106f951613dfa5f948ab69c10ce9b128d368baf8"},
+ {file = "yarl-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:418857f837347e8aaef682679f41e36c24250097f9e2f315d39bae3a99a34cbf"},
+ {file = "yarl-1.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ae0eec05ab49e91a78700761777f284c2df119376e391db42c38ab46fd662b77"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:009a028127e0a1755c38b03244c0bea9d5565630db9c4cf9572496e947137a87"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3edac5d74bb3209c418805bda77f973117836e1de7c000e9755e572c1f7850d0"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da65c3f263729e47351261351b8679c6429151ef9649bba08ef2528ff2c423b2"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef8fb25e52663a1c85d608f6dd72e19bd390e2ecaf29c17fb08f730226e3a08"},
+ {file = "yarl-1.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcd7bb1e5c45274af9a1dd7494d3c52b2be5e6bd8d7e49c612705fd45420b12d"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44ceac0450e648de86da8e42674f9b7077d763ea80c8ceb9d1c3e41f0f0a9951"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:97209cc91189b48e7cfe777237c04af8e7cc51eb369004e061809bcdf4e55220"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:48dd18adcf98ea9cd721a25313aef49d70d413a999d7d89df44f469edfb38a06"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e59399dda559688461762800d7fb34d9e8a6a7444fd76ec33220a926c8be1516"},
+ {file = "yarl-1.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d617c241c8c3ad5c4e78a08429fa49e4b04bedfc507b34b4d8dceb83b4af3588"},
+ {file = "yarl-1.8.2-cp39-cp39-win32.whl", hash = "sha256:cb6d48d80a41f68de41212f3dfd1a9d9898d7841c8f7ce6696cf2fd9cb57ef83"},
+ {file = "yarl-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:6604711362f2dbf7160df21c416f81fac0de6dbcf0b5445a2ef25478ecc4c778"},
+ {file = "yarl-1.8.2.tar.gz", hash = "sha256:49d43402c6e3013ad0978602bf6bf5328535c48d192304b91b97a3c6790b1562"},
]
[package.dependencies]
@@ -2317,4 +2494,4 @@ multidict = ">=4.0"
[metadata]
lock-version = "2.0"
python-versions = "3.10.*"
-content-hash = "68bfdf2115a5242df097155a2660a1c0276cf25b4785bdb761580bd35b77383c"
+content-hash = "e8487c5583b62d24dd7dda6b45b5bd8d6405d2e5ba55fef3c23a08ac965f2ab6"
diff --git a/pyproject.toml b/pyproject.toml
index 71981e8d0..6c8344fc9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,52 +10,46 @@ python = "3.10.*"
# See https://bot-core.pythondiscord.com/ for docs.
pydis_core = { version = "9.5.1", extras = ["async-rediscache"] }
-redis = "4.4.2"
-fakeredis = { version = "2.0.0", extras = ["lua"] }
aiohttp = "3.8.3"
arrow = "1.2.3"
-beautifulsoup4 = "4.11.1"
+beautifulsoup4 = "4.11.2"
colorama = { version = "0.4.6", markers = "sys_platform == 'win32'" }
coloredlogs = "15.0.1"
-deepdiff = "6.2.1"
+deepdiff = "6.2.3"
emoji = "2.2.0"
feedparser = "6.0.10"
-lxml = "4.9.1"
-
-# Must be kept on this version unless doc command output is fixed
-# See https://github.com/python-discord/bot/pull/2156
-markdownify = "0.6.1"
-
-more-itertools = "9.0.0"
+lxml = "4.9.2"
+markdownify = "0.11.6"
+more-itertools = "9.1.0"
python-dateutil = "2.8.2"
python-frontmatter = "1.0.0"
pyyaml = "6.0"
-rapidfuzz = "2.13.2"
+rapidfuzz = "2.13.7"
regex = "2022.10.31"
sentry-sdk = "1.16.0"
tldextract = "3.4.0"
-pydantic = "1.10.2"
+pydantic = { version = "1.10.5", extras = ["dotenv"]}
[tool.poetry.dev-dependencies]
-coverage = "6.5.0"
+coverage = "7.2.1"
flake8 = "6.0.0"
-flake8-annotations = "2.9.1"
-flake8-bugbear = "22.10.27"
-flake8-docstrings = "1.6.0"
+flake8-annotations = "3.0.0"
+flake8-bugbear = "23.2.13"
+flake8-docstrings = "1.7.0"
flake8-string-format = "0.3.0"
flake8-tidy-imports = "4.8.0"
flake8-todo = "0.7"
-flake8-isort = "5.0.3"
-pep8-naming = "0.13.2"
-pre-commit = "2.20.0"
-pip-licenses = "4.0.1"
-pytest = "7.2.0"
+flake8-isort = "6.0.0"
+pep8-naming = "0.13.3"
+pre-commit = "3.1.1"
+pip-licenses = "4.1.0"
+pytest = "7.2.2"
pytest-cov = "4.0.0"
-python-dotenv = "0.21.0"
-pytest-xdist = "3.0.2"
-pytest-subtests = "0.9.0"
+pytest-subtests = "0.10.0"
+pytest-xdist = "3.2.0"
taskipy = "1.10.3"
+httpx = "0.23.3"
[build-system]
@@ -64,6 +58,7 @@ build-backend = "poetry.core.masonry.api"
[tool.taskipy.tasks]
start = "python -m bot"
+configure = "python -m botstrap"
lint = "pre-commit run --all-files"
precommit = "pre-commit install"
build = "docker build -t ghcr.io/python-discord/bot:latest -f Dockerfile ."
diff --git a/tests/bot/exts/info/doc/test_parsing.py b/tests/bot/exts/info/doc/test_parsing.py
index 1663d8491..d2105a53c 100644
--- a/tests/bot/exts/info/doc/test_parsing.py
+++ b/tests/bot/exts/info/doc/test_parsing.py
@@ -1,6 +1,7 @@
from unittest import TestCase
from bot.exts.info.doc import _parsing as parsing
+from bot.exts.info.doc._markdown import DocMarkdownConverter
class SignatureSplitter(TestCase):
@@ -64,3 +65,25 @@ class SignatureSplitter(TestCase):
for input_string, expected_output in test_cases:
with self.subTest(input_string=input_string):
self.assertEqual(list(parsing._split_parameters(input_string)), expected_output)
+
+
+class MarkdownConverterTest(TestCase):
+ def test_hr_removed(self):
+ test_cases = (
+ ('<hr class="docutils"/>', ""),
+ ("<hr>", ""),
+ )
+ self._run_tests(test_cases)
+
+ def test_whitespace_removed(self):
+ test_cases = (
+ ("lines\nof\ntext", "lines of text"),
+ ("lines\n\nof\n\ntext", "lines of text"),
+ )
+ self._run_tests(test_cases)
+
+ def _run_tests(self, test_cases: tuple[tuple[str, str], ...]):
+ for input_string, expected_output in test_cases:
+ with self.subTest(input_string=input_string):
+ d = DocMarkdownConverter(page_url="https://example.com")
+ self.assertEqual(d.convert(input_string), expected_output)
diff --git a/tests/bot/exts/utils/snekbox/__init__.py b/tests/bot/exts/utils/snekbox/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/bot/exts/utils/snekbox/__init__.py
diff --git a/tests/bot/exts/utils/snekbox/test_io.py b/tests/bot/exts/utils/snekbox/test_io.py
new file mode 100644
index 000000000..bcf1162b8
--- /dev/null
+++ b/tests/bot/exts/utils/snekbox/test_io.py
@@ -0,0 +1,34 @@
+from unittest import TestCase
+
+# noinspection PyProtectedMember
+from bot.exts.utils.snekbox import _io
+
+
+class SnekboxIOTests(TestCase):
+ # noinspection SpellCheckingInspection
+ def test_normalize_file_name(self):
+ """Invalid file names should be normalized."""
+ cases = [
+ # ANSI escape sequences -> underscore
+ (r"\u001b[31mText", "_Text"),
+ # (Multiple consecutive should be collapsed to one underscore)
+ (r"a\u001b[35m\u001b[37mb", "a_b"),
+ # Backslash escaped chars -> underscore
+ (r"\n", "_"),
+ (r"\r", "_"),
+ (r"A\0\tB", "A__B"),
+ # Any other disallowed chars -> underscore
+ (r"\\.txt", "_.txt"),
+ (r"A!@#$%^&*B, C()[]{}+=D.txt", "A_B_C_D.txt"), # noqa: P103
+ (" ", "_"),
+ # Normal file names should be unchanged
+ ("legal_file-name.txt", "legal_file-name.txt"),
+ ("_-.", "_-."),
+ ]
+ for name, expected in cases:
+ with self.subTest(name=name, expected=expected):
+ # Test function directly
+ self.assertEqual(_io.normalize_discord_file_name(name), expected)
+ # Test FileAttachment.to_file()
+ obj = _io.FileAttachment(name, b"")
+ self.assertEqual(obj.to_file().filename, expected)
diff --git a/tests/bot/exts/utils/test_snekbox.py b/tests/bot/exts/utils/snekbox/test_snekbox.py
index b1f32c210..9dcf7fd8c 100644
--- a/tests/bot/exts/utils/test_snekbox.py
+++ b/tests/bot/exts/utils/snekbox/test_snekbox.py
@@ -1,5 +1,6 @@
import asyncio
import unittest
+from base64 import b64encode
from unittest.mock import AsyncMock, MagicMock, Mock, call, create_autospec, patch
from discord import AllowedMentions
@@ -8,7 +9,8 @@ from discord.ext import commands
from bot import constants
from bot.errors import LockedResourceError
from bot.exts.utils import snekbox
-from bot.exts.utils.snekbox import Snekbox
+from bot.exts.utils.snekbox import EvalJob, EvalResult, Snekbox
+from bot.exts.utils.snekbox._io import FileAttachment
from tests.helpers import MockBot, MockContext, MockMember, MockMessage, MockReaction, MockUser
@@ -17,34 +19,55 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
"""Add mocked bot and cog to the instance."""
self.bot = MockBot()
self.cog = Snekbox(bot=self.bot)
+ self.job = EvalJob.from_code("import random")
+
+ @staticmethod
+ def code_args(code: str) -> tuple[EvalJob]:
+ """Converts code to a tuple of arguments expected."""
+ return EvalJob.from_code(code),
async def test_post_job(self):
"""Post the eval code to the URLs.snekbox_eval_api endpoint."""
resp = MagicMock()
- resp.json = AsyncMock(return_value="return")
+ resp.json = AsyncMock(return_value={"stdout": "Hi", "returncode": 137, "files": []})
context_manager = MagicMock()
context_manager.__aenter__.return_value = resp
self.bot.http_session.post.return_value = context_manager
- self.assertEqual(await self.cog.post_job("import random", "3.10"), "return")
+ job = EvalJob.from_code("import random").as_version("3.10")
+ self.assertEqual(await self.cog.post_job(job), EvalResult("Hi", 137))
+
+ expected = {
+ "args": ["main.py"],
+ "files": [
+ {
+ "path": "main.py",
+ "content": b64encode("import random".encode()).decode()
+ }
+ ]
+ }
self.bot.http_session.post.assert_called_with(
constants.URLs.snekbox_eval_api,
- json={"input": "import random"},
+ json=expected,
raise_for_status=True
)
resp.json.assert_awaited_once()
async def test_upload_output_reject_too_long(self):
"""Reject output longer than MAX_PASTE_LENGTH."""
- result = await self.cog.upload_output("-" * (snekbox.MAX_PASTE_LENGTH + 1))
+ result = await self.cog.upload_output("-" * (snekbox._cog.MAX_PASTE_LENGTH + 1))
self.assertEqual(result, "too long to upload")
- @patch("bot.exts.utils.snekbox.send_to_paste_service")
+ @patch("bot.exts.utils.snekbox._cog.send_to_paste_service")
async def test_upload_output(self, mock_paste_util):
"""Upload the eval output to the URLs.paste_service.format(key="documents") endpoint."""
await self.cog.upload_output("Test output.")
- mock_paste_util.assert_called_once_with("Test output.", extension="txt", max_length=snekbox.MAX_PASTE_LENGTH)
+ mock_paste_util.assert_called_once_with(
+ "Test output.",
+ extension="txt",
+ max_length=snekbox._cog.MAX_PASTE_LENGTH
+ )
async def test_codeblock_converter(self):
ctx = MockContext()
@@ -76,40 +99,94 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
(['x = 1', 'print(x)', 'print("Some other code.")'], 'x = 1', 'three blocks of code')
)
- for case, setup_code, testname in cases:
- setup = snekbox.TIMEIT_SETUP_WRAPPER.format(setup=setup_code)
- expected = ('\n'.join(case[1:] if setup_code else case), [*base_args, setup])
- with self.subTest(msg=f'Test with {testname} and expected return {expected}'):
+ for case, setup_code, test_name in cases:
+ setup = snekbox._cog.TIMEIT_SETUP_WRAPPER.format(setup=setup_code)
+ expected = [*base_args, setup, '\n'.join(case[1:] if setup_code else case)]
+ with self.subTest(msg=f'Test with {test_name} and expected return {expected}'):
self.assertEqual(self.cog.prepare_timeit_input(case), expected)
- def test_get_results_message(self):
- """Return error and message according to the eval result."""
+ def test_eval_result_message(self):
+ """EvalResult.get_message(), should return message."""
cases = (
- ('ERROR', None, ('Your 3.11 eval job has failed', 'ERROR')),
- ('', 128 + snekbox.SIGKILL, ('Your 3.11 eval job timed out or ran out of memory', '')),
- ('', 255, ('Your 3.11 eval job has failed', 'A fatal NsJail error occurred'))
+ ('ERROR', None, ('Your 3.11 eval job has failed', 'ERROR', '')),
+ ('', 128 + snekbox._eval.SIGKILL, ('Your 3.11 eval job timed out or ran out of memory', '', '')),
+ ('', 255, ('Your 3.11 eval job has failed', 'A fatal NsJail error occurred', ''))
)
for stdout, returncode, expected in cases:
+ exp_msg, exp_err, exp_files_err = expected
with self.subTest(stdout=stdout, returncode=returncode, expected=expected):
- actual = self.cog.get_results_message({'stdout': stdout, 'returncode': returncode}, 'eval', '3.11')
- self.assertEqual(actual, expected)
-
- @patch('bot.exts.utils.snekbox.Signals', side_effect=ValueError)
- def test_get_results_message_invalid_signal(self, mock_signals: Mock):
+ result = EvalResult(stdout=stdout, returncode=returncode)
+ job = EvalJob([])
+ # Check all 3 message types
+ msg = result.get_message(job)
+ self.assertEqual(msg, exp_msg)
+ error = result.error_message
+ self.assertEqual(error, exp_err)
+ files_error = result.files_error_message
+ self.assertEqual(files_error, exp_files_err)
+
+ @patch("bot.exts.utils.snekbox._eval.FILE_COUNT_LIMIT", 2)
+ def test_eval_result_files_error_message(self):
+ """EvalResult.files_error_message, should return files error message."""
+ cases = [
+ ([], ["abc"], (
+ "1 file upload (abc) failed because its file size exceeds 8 MiB."
+ )),
+ ([], ["file1.bin", "f2.bin"], (
+ "2 file uploads (file1.bin, f2.bin) failed because each file's size exceeds 8 MiB."
+ )),
+ (["a", "b"], ["c"], (
+ "1 file upload (c) failed as it exceeded the 2 file limit."
+ )),
+ (["a"], ["b", "c"], (
+ "2 file uploads (b, c) failed as they exceeded the 2 file limit."
+ )),
+ ]
+ for files, failed_files, expected_msg in cases:
+ with self.subTest(files=files, failed_files=failed_files, expected_msg=expected_msg):
+ result = EvalResult("", 0, files, failed_files)
+ msg = result.files_error_message
+ self.assertIn(expected_msg, msg)
+
+ @patch("bot.exts.utils.snekbox._eval.FILE_COUNT_LIMIT", 2)
+ def test_eval_result_files_error_str(self):
+ """EvalResult.files_error_message, should return files error message."""
+ cases = [
+ # Normal
+ (["x.ini"], "x.ini"),
+ (["123456", "879"], "123456, 879"),
+ # Break on whole name if less than 3 characters remaining
+ (["12345678", "9"], "12345678, ..."),
+ # Otherwise break on max chars
+ (["123", "345", "67890000"], "123, 345, 6789..."),
+ (["abcdefg1234567"], "abcdefg123..."),
+ ]
+ for failed_files, expected in cases:
+ with self.subTest(failed_files=failed_files, expected=expected):
+ result = EvalResult("", 0, [], failed_files)
+ msg = result.get_failed_files_str(char_max=10)
+ self.assertEqual(msg, expected)
+
+ @patch('bot.exts.utils.snekbox._eval.Signals', side_effect=ValueError)
+ def test_eval_result_message_invalid_signal(self, _mock_signals: Mock):
+ result = EvalResult(stdout="", returncode=127)
self.assertEqual(
- self.cog.get_results_message({'stdout': '', 'returncode': 127}, 'eval', '3.11'),
- ('Your 3.11 eval job has completed with return code 127', '')
+ result.get_message(EvalJob([], version="3.10")),
+ "Your 3.10 eval job has completed with return code 127"
)
+ self.assertEqual(result.error_message, "")
+ self.assertEqual(result.files_error_message, "")
- @patch('bot.exts.utils.snekbox.Signals')
- def test_get_results_message_valid_signal(self, mock_signals: Mock):
- mock_signals.return_value.name = 'SIGTEST'
+ @patch('bot.exts.utils.snekbox._eval.Signals')
+ def test_eval_result_message_valid_signal(self, mock_signals: Mock):
+ mock_signals.return_value.name = "SIGTEST"
+ result = EvalResult(stdout="", returncode=127)
self.assertEqual(
- self.cog.get_results_message({'stdout': '', 'returncode': 127}, 'eval', '3.11'),
- ('Your 3.11 eval job has completed with return code 127 (SIGTEST)', '')
+ result.get_message(EvalJob([], version="3.11")),
+ "Your 3.11 eval job has completed with return code 127 (SIGTEST)"
)
- def test_get_status_emoji(self):
+ def test_eval_result_status_emoji(self):
"""Return emoji according to the eval result."""
cases = (
(' ', -1, ':warning:'),
@@ -118,8 +195,8 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
)
for stdout, returncode, expected in cases:
with self.subTest(stdout=stdout, returncode=returncode, expected=expected):
- actual = self.cog.get_status_emoji({'stdout': stdout, 'returncode': returncode})
- self.assertEqual(actual, expected)
+ result = EvalResult(stdout=stdout, returncode=returncode)
+ self.assertEqual(result.status_emoji, expected)
async def test_format_output(self):
"""Test output formatting."""
@@ -178,10 +255,11 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
ctx.command = MagicMock()
self.cog.send_job = AsyncMock(return_value=response)
- self.cog.continue_job = AsyncMock(return_value=(None, None))
+ self.cog.continue_job = AsyncMock(return_value=None)
await self.cog.eval_command(self.cog, ctx=ctx, python_version='3.11', code=['MyAwesomeCode'])
- self.cog.send_job.assert_called_once_with(ctx, '3.11', 'MyAwesomeCode', args=None, job_name='eval')
+ job = EvalJob.from_code("MyAwesomeCode")
+ self.cog.send_job.assert_called_once_with(ctx, job)
self.cog.continue_job.assert_called_once_with(ctx, response, 'eval')
async def test_eval_command_evaluate_twice(self):
@@ -191,13 +269,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
ctx.command = MagicMock()
self.cog.send_job = AsyncMock(return_value=response)
self.cog.continue_job = AsyncMock()
- self.cog.continue_job.side_effect = (('MyAwesomeFormattedCode', None), (None, None))
+ self.cog.continue_job.side_effect = (EvalJob.from_code('MyAwesomeFormattedCode'), None)
await self.cog.eval_command(self.cog, ctx=ctx, python_version='3.11', code=['MyAwesomeCode'])
- self.cog.send_job.assert_called_with(
- ctx, '3.11', 'MyAwesomeFormattedCode', args=None, job_name='eval'
- )
- self.cog.continue_job.assert_called_with(ctx, response, 'eval')
+
+ expected_job = EvalJob.from_code("MyAwesomeFormattedCode")
+ self.cog.send_job.assert_called_with(ctx, expected_job)
+ self.cog.continue_job.assert_called_with(ctx, response, "eval")
async def test_eval_command_reject_two_eval_at_the_same_time(self):
"""Test if the eval command rejects an eval if the author already have a running eval."""
@@ -212,8 +290,8 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
self.cog.post_job = AsyncMock(side_effect=delay_with_side_effect)
with self.assertRaises(LockedResourceError):
await asyncio.gather(
- self.cog.send_job(ctx, '3.11', 'MyAwesomeCode', job_name='eval'),
- self.cog.send_job(ctx, '3.11', 'MyAwesomeCode', job_name='eval'),
+ self.cog.send_job(ctx, EvalJob.from_code("MyAwesomeCode")),
+ self.cog.send_job(ctx, EvalJob.from_code("MyAwesomeCode")),
)
async def test_send_job(self):
@@ -223,30 +301,31 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
ctx.send = AsyncMock()
ctx.author = MockUser(mention='@LemonLemonishBeard#0042')
- self.cog.post_job = AsyncMock(return_value={'stdout': '', 'returncode': 0})
- self.cog.get_results_message = MagicMock(return_value=('Return code 0', ''))
- self.cog.get_status_emoji = MagicMock(return_value=':yay!:')
+ eval_result = EvalResult("", 0)
+ self.cog.post_job = AsyncMock(return_value=eval_result)
self.cog.format_output = AsyncMock(return_value=('[No output]', None))
+ self.cog.upload_output = AsyncMock() # Should not be called
mocked_filter_cog = MagicMock()
mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=False)
self.bot.get_cog.return_value = mocked_filter_cog
- await self.cog.send_job(ctx, '3.11', 'MyAwesomeCode', job_name='eval')
+ job = EvalJob.from_code('MyAwesomeCode')
+ await self.cog.send_job(ctx, job),
ctx.send.assert_called_once()
self.assertEqual(
ctx.send.call_args.args[0],
- '@LemonLemonishBeard#0042 :yay!: Return code 0.\n\n```\n[No output]\n```'
+ '@LemonLemonishBeard#0042 :warning: Your 3.11 eval job has completed '
+ 'with return code 0.\n\n```\n[No output]\n```'
)
allowed_mentions = ctx.send.call_args.kwargs['allowed_mentions']
expected_allowed_mentions = AllowedMentions(everyone=False, roles=False, users=[ctx.author])
self.assertEqual(allowed_mentions.to_dict(), expected_allowed_mentions.to_dict())
- self.cog.post_job.assert_called_once_with('MyAwesomeCode', '3.11', args=None)
- self.cog.get_status_emoji.assert_called_once_with({'stdout': '', 'returncode': 0})
- self.cog.get_results_message.assert_called_once_with({'stdout': '', 'returncode': 0}, 'eval', '3.11')
+ self.cog.post_job.assert_called_once_with(job)
self.cog.format_output.assert_called_once_with('')
+ self.cog.upload_output.assert_not_called()
async def test_send_job_with_paste_link(self):
"""Test the send_job function with a too long output that generate a paste link."""
@@ -255,29 +334,26 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
ctx.send = AsyncMock()
ctx.author.mention = '@LemonLemonishBeard#0042'
- self.cog.post_job = AsyncMock(return_value={'stdout': 'Way too long beard', 'returncode': 0})
- self.cog.get_results_message = MagicMock(return_value=('Return code 0', ''))
- self.cog.get_status_emoji = MagicMock(return_value=':yay!:')
+ eval_result = EvalResult("Way too long beard", 0)
+ self.cog.post_job = AsyncMock(return_value=eval_result)
self.cog.format_output = AsyncMock(return_value=('Way too long beard', 'lookatmybeard.com'))
mocked_filter_cog = MagicMock()
mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=False)
self.bot.get_cog.return_value = mocked_filter_cog
- await self.cog.send_job(ctx, '3.11', 'MyAwesomeCode', job_name='eval')
+ job = EvalJob.from_code("MyAwesomeCode").as_version("3.11")
+ await self.cog.send_job(ctx, job),
ctx.send.assert_called_once()
self.assertEqual(
ctx.send.call_args.args[0],
- '@LemonLemonishBeard#0042 :yay!: Return code 0.'
+ '@LemonLemonishBeard#0042 :white_check_mark: Your 3.11 eval job '
+ 'has completed with return code 0.'
'\n\n```\nWay too long beard\n```\nFull output: lookatmybeard.com'
)
- self.cog.post_job.assert_called_once_with('MyAwesomeCode', '3.11', args=None)
- self.cog.get_status_emoji.assert_called_once_with({'stdout': 'Way too long beard', 'returncode': 0})
- self.cog.get_results_message.assert_called_once_with(
- {'stdout': 'Way too long beard', 'returncode': 0}, 'eval', '3.11'
- )
+ self.cog.post_job.assert_called_once_with(job)
self.cog.format_output.assert_called_once_with('Way too long beard')
async def test_send_job_with_non_zero_eval(self):
@@ -286,29 +362,57 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
ctx.message = MockMessage()
ctx.send = AsyncMock()
ctx.author.mention = '@LemonLemonishBeard#0042'
- self.cog.post_job = AsyncMock(return_value={'stdout': 'ERROR', 'returncode': 127})
- self.cog.get_results_message = MagicMock(return_value=('Return code 127', 'Beard got stuck in the eval'))
- self.cog.get_status_emoji = MagicMock(return_value=':nope!:')
- self.cog.format_output = AsyncMock() # This function isn't called
+
+ eval_result = EvalResult("ERROR", 127)
+ self.cog.post_job = AsyncMock(return_value=eval_result)
+ self.cog.upload_output = AsyncMock() # This function isn't called
mocked_filter_cog = MagicMock()
mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=False)
self.bot.get_cog.return_value = mocked_filter_cog
- await self.cog.send_job(ctx, '3.11', 'MyAwesomeCode', job_name='eval')
+ job = EvalJob.from_code("MyAwesomeCode").as_version("3.11")
+ await self.cog.send_job(ctx, job),
ctx.send.assert_called_once()
self.assertEqual(
ctx.send.call_args.args[0],
- '@LemonLemonishBeard#0042 :nope!: Return code 127.\n\n```\nBeard got stuck in the eval\n```'
+ '@LemonLemonishBeard#0042 :x: Your 3.11 eval job has completed with return code 127.'
+ '\n\n```\nERROR\n```'
+ )
+
+ self.cog.post_job.assert_called_once_with(job)
+ self.cog.upload_output.assert_not_called()
+
+ async def test_send_job_with_disallowed_file_ext(self):
+ """Test send_job with disallowed file extensions."""
+ ctx = MockContext()
+ ctx.message = MockMessage()
+ ctx.send = AsyncMock()
+ ctx.author.mention = "@user#7700"
+
+ eval_result = EvalResult("", 0, files=[FileAttachment("test.disallowed", b"test")])
+ self.cog.post_job = AsyncMock(return_value=eval_result)
+ self.cog.upload_output = AsyncMock() # This function isn't called
+
+ mocked_filter_cog = MagicMock()
+ mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=False)
+ self.bot.get_cog.return_value = mocked_filter_cog
+
+ job = EvalJob.from_code("MyAwesomeCode").as_version("3.11")
+ await self.cog.send_job(ctx, job),
+
+ ctx.send.assert_called_once()
+ res = ctx.send.call_args.args[0]
+ self.assertTrue(
+ res.startswith("@user#7700 :white_check_mark: Your 3.11 eval job has completed with return code 0.")
)
+ self.assertIn("Files with disallowed extensions can't be uploaded: **.disallowed**", res)
- self.cog.post_job.assert_called_once_with('MyAwesomeCode', '3.11', args=None)
- self.cog.get_status_emoji.assert_called_once_with({'stdout': 'ERROR', 'returncode': 127})
- self.cog.get_results_message.assert_called_once_with({'stdout': 'ERROR', 'returncode': 127}, 'eval', '3.11')
- self.cog.format_output.assert_not_called()
+ self.cog.post_job.assert_called_once_with(job)
+ self.cog.upload_output.assert_not_called()
- @patch("bot.exts.utils.snekbox.partial")
+ @patch("bot.exts.utils.snekbox._cog.partial")
async def test_continue_job_does_continue(self, partial_mock):
"""Test that the continue_job function does continue if required conditions are met."""
ctx = MockContext(
@@ -328,19 +432,19 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
actual = await self.cog.continue_job(ctx, response, self.cog.eval_command)
self.cog.get_code.assert_awaited_once_with(new_msg, ctx.command)
- self.assertEqual(actual, (expected, None))
+ self.assertEqual(actual, EvalJob.from_code(expected))
self.bot.wait_for.assert_has_awaits(
(
call(
'message_edit',
- check=partial_mock(snekbox.predicate_message_edit, ctx),
- timeout=snekbox.REDO_TIMEOUT,
+ check=partial_mock(snekbox._cog.predicate_message_edit, ctx),
+ timeout=snekbox._cog.REDO_TIMEOUT,
),
- call('reaction_add', check=partial_mock(snekbox.predicate_emoji_reaction, ctx), timeout=10)
+ call('reaction_add', check=partial_mock(snekbox._cog.predicate_emoji_reaction, ctx), timeout=10)
)
)
- ctx.message.add_reaction.assert_called_once_with(snekbox.REDO_EMOJI)
- ctx.message.clear_reaction.assert_called_once_with(snekbox.REDO_EMOJI)
+ ctx.message.add_reaction.assert_called_once_with(snekbox._cog.REDO_EMOJI)
+ ctx.message.clear_reaction.assert_called_once_with(snekbox._cog.REDO_EMOJI)
response.delete.assert_called_once()
async def test_continue_job_does_not_continue(self):
@@ -348,8 +452,8 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
self.bot.wait_for.side_effect = asyncio.TimeoutError
actual = await self.cog.continue_job(ctx, MockMessage(), self.cog.eval_command)
- self.assertEqual(actual, (None, None))
- ctx.message.clear_reaction.assert_called_once_with(snekbox.REDO_EMOJI)
+ self.assertEqual(actual, None)
+ ctx.message.clear_reaction.assert_called_once_with(snekbox._cog.REDO_EMOJI)
async def test_get_code(self):
"""Should return 1st arg (or None) if eval cmd in message, otherwise return full content."""
@@ -391,18 +495,18 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
for ctx_msg, new_msg, expected, testname in cases:
with self.subTest(msg=f'Messages with {testname} return {expected}'):
ctx = MockContext(message=ctx_msg)
- actual = snekbox.predicate_message_edit(ctx, ctx_msg, new_msg)
+ actual = snekbox._cog.predicate_message_edit(ctx, ctx_msg, new_msg)
self.assertEqual(actual, expected)
def test_predicate_emoji_reaction(self):
"""Test the predicate_emoji_reaction function."""
valid_reaction = MockReaction(message=MockMessage(id=1))
- valid_reaction.__str__.return_value = snekbox.REDO_EMOJI
+ valid_reaction.__str__.return_value = snekbox._cog.REDO_EMOJI
valid_ctx = MockContext(message=MockMessage(id=1), author=MockUser(id=2))
valid_user = MockUser(id=2)
invalid_reaction_id = MockReaction(message=MockMessage(id=42))
- invalid_reaction_id.__str__.return_value = snekbox.REDO_EMOJI
+ invalid_reaction_id.__str__.return_value = snekbox._cog.REDO_EMOJI
invalid_user_id = MockUser(id=42)
invalid_reaction_str = MockReaction(message=MockMessage(id=1))
invalid_reaction_str.__str__.return_value = ':longbeard:'
@@ -415,7 +519,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
)
for reaction, user, expected, testname in cases:
with self.subTest(msg=f'Test with {testname} and expected return {expected}'):
- actual = snekbox.predicate_emoji_reaction(valid_ctx, reaction, user)
+ actual = snekbox._cog.predicate_emoji_reaction(valid_ctx, reaction, user)
self.assertEqual(actual, expected)