aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar mbaruh <[email protected]>2024-03-17 23:14:26 +0200
committerGravatar mbaruh <[email protected]>2024-03-17 23:14:26 +0200
commitd4d8e6d0af376e32112512a3726df8f1449deb01 (patch)
treee3f4c4f53ca17c6129b586e167dfa83e1c2cf51d
parentMake the moderator the author of the compban for phishing attempts (diff)
parentMerge pull request #2953 from python-discord/dependabot/pip/pytest-8.1.1 (diff)
Merge branch 'main' into phishing_button
-rw-r--r--.github/workflows/build-deploy.yml19
-rw-r--r--.github/workflows/lint-test.yml8
-rw-r--r--.github/workflows/main.yml2
-rw-r--r--.github/workflows/sentry_release.yml2
-rw-r--r--.github/workflows/status_embed.yaml2
-rw-r--r--.gitignore1
-rw-r--r--.pre-commit-config.yaml5
-rw-r--r--bot/bot.py22
-rw-r--r--bot/constants.py534
-rw-r--r--bot/converters.py48
-rw-r--r--bot/exts/backend/branding/_cog.py5
-rw-r--r--bot/exts/backend/error_handler.py58
-rw-r--r--bot/exts/backend/logging.py2
-rw-r--r--bot/exts/filtering/_filter_lists/antispam.py2
-rw-r--r--bot/exts/filtering/_filter_lists/extension.py8
-rw-r--r--bot/exts/filtering/_filter_lists/filter_list.py2
-rw-r--r--bot/exts/filtering/_filter_lists/invite.py6
-rw-r--r--bot/exts/filtering/_filters/filter.py4
-rw-r--r--bot/exts/filtering/_filters/unique/rich_embed.py2
-rw-r--r--bot/exts/filtering/_settings.py2
-rw-r--r--bot/exts/filtering/_settings_types/actions/infraction_and_notification.py16
-rw-r--r--bot/exts/filtering/_settings_types/actions/ping.py4
-rw-r--r--bot/exts/filtering/_settings_types/settings_entry.py2
-rw-r--r--bot/exts/filtering/_settings_types/validations/bypass_roles.py25
-rw-r--r--bot/exts/filtering/_settings_types/validations/channel_scope.py34
-rw-r--r--bot/exts/filtering/_ui/filter.py18
-rw-r--r--bot/exts/filtering/_ui/filter_list.py11
-rw-r--r--bot/exts/filtering/_ui/search.py9
-rw-r--r--bot/exts/filtering/_ui/ui.py6
-rw-r--r--bot/exts/filtering/_utils.py30
-rw-r--r--bot/exts/filtering/filtering.py37
-rw-r--r--bot/exts/fun/duck_pond.py15
-rw-r--r--bot/exts/fun/off_topic_names.py6
-rw-r--r--bot/exts/help_channels/_channel.py29
-rw-r--r--bot/exts/help_channels/_cog.py3
-rw-r--r--bot/exts/info/code_snippets.py3
-rw-r--r--bot/exts/info/codeblock/_cog.py2
-rw-r--r--bot/exts/info/doc/_batch_parser.py3
-rw-r--r--bot/exts/info/doc/_cog.py4
-rw-r--r--bot/exts/info/doc/_markdown.py2
-rw-r--r--bot/exts/info/information.py41
-rw-r--r--bot/exts/info/patreon.py8
-rw-r--r--bot/exts/info/pep.py14
-rw-r--r--bot/exts/info/pypi.py17
-rw-r--r--bot/exts/info/python_news.py131
-rw-r--r--bot/exts/info/source.py4
-rw-r--r--bot/exts/info/subscribe.py4
-rw-r--r--bot/exts/moderation/clean.py22
-rw-r--r--bot/exts/moderation/defcon.py13
-rw-r--r--bot/exts/moderation/dm_relay.py12
-rw-r--r--bot/exts/moderation/incidents.py8
-rw-r--r--bot/exts/moderation/infraction/_scheduler.py10
-rw-r--r--bot/exts/moderation/infraction/_utils.py34
-rw-r--r--bot/exts/moderation/infraction/infractions.py2
-rw-r--r--bot/exts/moderation/infraction/management.py18
-rw-r--r--bot/exts/moderation/infraction/superstarify.py2
-rw-r--r--bot/exts/moderation/metabase.py15
-rw-r--r--bot/exts/moderation/modlog.py216
-rw-r--r--bot/exts/moderation/modpings.py2
-rw-r--r--bot/exts/moderation/silence.py4
-rw-r--r--bot/exts/moderation/stream.py12
-rw-r--r--bot/exts/moderation/voice_gate.py290
-rw-r--r--bot/exts/moderation/watchchannels/_watchchannel.py30
-rw-r--r--bot/exts/recruitment/talentpool/_api.py12
-rw-r--r--bot/exts/recruitment/talentpool/_cog.py12
-rw-r--r--bot/exts/recruitment/talentpool/_review.py6
-rw-r--r--bot/exts/utils/internal.py15
-rw-r--r--bot/exts/utils/reminders.py94
-rw-r--r--bot/exts/utils/snekbox/_cog.py37
-rw-r--r--bot/exts/utils/snekbox/_eval.py2
-rw-r--r--bot/exts/utils/thread_bumper.py4
-rw-r--r--bot/log.py59
-rw-r--r--bot/pagination.py372
-rw-r--r--bot/resources/tags/args-kwargs.md8
-rw-r--r--bot/resources/tags/blocking.md10
-rw-r--r--bot/resources/tags/class.md2
-rw-r--r--bot/resources/tags/codeblock.md2
-rw-r--r--bot/resources/tags/contribute.md6
-rw-r--r--bot/resources/tags/decorators.md4
-rw-r--r--bot/resources/tags/dotenv.md10
-rw-r--r--bot/resources/tags/environments.md10
-rw-r--r--bot/resources/tags/equals-true.md24
-rw-r--r--bot/resources/tags/foo.md4
-rw-r--r--bot/resources/tags/if-name-main.md6
-rw-r--r--bot/resources/tags/in-place.md36
-rw-r--r--bot/resources/tags/mutable-default-args.md6
-rw-r--r--bot/resources/tags/names.md14
-rw-r--r--bot/resources/tags/nomodule.md4
-rw-r--r--bot/resources/tags/off-topic-names.md6
-rw-r--r--bot/resources/tags/open.md16
-rw-r--r--bot/resources/tags/paste.md2
-rw-r--r--bot/resources/tags/pathlib.md24
-rw-r--r--bot/resources/tags/pep8.md4
-rw-r--r--bot/resources/tags/positional-keyword.md6
-rw-r--r--bot/resources/tags/precedence.md2
-rw-r--r--bot/resources/tags/quotes.md4
-rw-r--r--bot/resources/tags/range-len.md4
-rw-r--r--bot/resources/tags/regex.md4
-rw-r--r--bot/resources/tags/relative-path.md4
-rw-r--r--bot/resources/tags/repl.md25
-rw-r--r--bot/resources/tags/return.md6
-rw-r--r--bot/resources/tags/round.md8
-rw-r--r--bot/resources/tags/scope.md2
-rw-r--r--bot/resources/tags/seek.md4
-rw-r--r--bot/resources/tags/sql-fstring.md4
-rw-r--r--bot/resources/tags/star-imports.md10
-rw-r--r--bot/resources/tags/underscore.md14
-rw-r--r--bot/resources/tags/windows-path.md2
-rw-r--r--bot/utils/__init__.py4
-rw-r--r--bot/utils/caching.py42
-rw-r--r--bot/utils/channel.py15
-rw-r--r--bot/utils/checks.py12
-rw-r--r--bot/utils/members.py48
-rw-r--r--bot/utils/messages.py18
-rw-r--r--bot/utils/modlog.py69
-rw-r--r--bot/utils/services.py83
-rw-r--r--botstrap.py6
-rw-r--r--poetry.lock2496
-rw-r--r--pyproject.toml77
-rw-r--r--tests/_autospec.py2
-rw-r--r--tests/bot/exts/backend/test_error_handler.py36
-rw-r--r--tests/bot/exts/backend/test_logging.py5
-rw-r--r--tests/bot/exts/filtering/test_settings_entries.py8
-rw-r--r--tests/bot/exts/moderation/infraction/test_utils.py10
-rw-r--r--tests/bot/exts/moderation/test_incidents.py22
-rw-r--r--tests/bot/exts/moderation/test_modlog.py4
-rw-r--r--tests/bot/exts/moderation/test_silence.py9
-rw-r--r--tests/bot/exts/test_cogs.py3
-rw-r--r--tests/bot/exts/utils/snekbox/test_snekbox.py45
-rw-r--r--tests/bot/test_pagination.py46
-rw-r--r--tests/bot/utils/test_services.py90
-rw-r--r--tests/helpers.py10
-rw-r--r--tests/test_helpers.py1
133 files changed, 2830 insertions, 3143 deletions
diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml
index f38686426..1dc00881a 100644
--- a/.github/workflows/build-deploy.yml
+++ b/.github/workflows/build-deploy.yml
@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# The current version (v2) of Docker's build-push action uses
# buildx, which comes with BuildKit features that help us speed
@@ -26,10 +26,10 @@ jobs:
# See https://github.com/docker/build-push-action
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
- name: Login to Github Container Registry
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -40,7 +40,7 @@ jobs:
# and with the short SHA of the commit.
- name: Build and push
- uses: docker/build-push-action@v4
+ uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
@@ -61,14 +61,15 @@ jobs:
environment: production
steps:
- name: Checkout Kubernetes repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
- repository: python-discord/kubernetes
+ repository: python-discord/infra
+ path: infra
- - uses: azure/setup-kubectl@v3
+ - uses: azure/setup-kubectl@v4
- name: Authenticate with Kubernetes
- uses: azure/k8s-set-context@v3
+ uses: azure/k8s-set-context@v4
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBECONFIG }}
@@ -77,5 +78,5 @@ jobs:
uses: azure/k8s-deploy@v4
with:
manifests: |
- namespaces/default/bot/deployment.yaml
+ infra/kubernetes/namespaces/default/bot/deployment.yaml
images: 'ghcr.io/python-discord/bot:${{ inputs.sha-tag }}'
diff --git a/.github/workflows/lint-test.yml b/.github/workflows/lint-test.yml
index 5e1f55584..bec7d13ea 100644
--- a/.github/workflows/lint-test.yml
+++ b/.github/workflows/lint-test.yml
@@ -29,10 +29,10 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install Python Dependencies
- uses: HassanAbouelela/actions/setup-python@setup-python_v1.4.1
+ uses: HassanAbouelela/actions/setup-python@setup-python_v1.4.2
with:
python_version: '3.11'
@@ -50,7 +50,7 @@ jobs:
# Run `ruff` using github formatting to enable automatic inline annotations.
- name: Run ruff
- run: "ruff check --format=github ."
+ run: "ruff check --output-format=github ."
- name: Run tests and generate coverage report
run: pytest -n auto --cov --disable-warnings -q
@@ -72,7 +72,7 @@ jobs:
- name: Upload a Build Artifact
if: always() && steps.prepare-artifact.outcome == 'success'
continue-on-error: true
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: pull-request-payload
path: pull_request_payload.json
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a512f289d..6f471c931 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -13,10 +13,10 @@ concurrency:
jobs:
+
lint-test:
uses: ./.github/workflows/lint-test.yml
-
generate-sha-tag:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
diff --git a/.github/workflows/sentry_release.yml b/.github/workflows/sentry_release.yml
index cdc8f37d5..265c71111 100644
--- a/.github/workflows/sentry_release.yml
+++ b/.github/workflows/sentry_release.yml
@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Create a Sentry.io release
uses: tclindner/[email protected]
diff --git a/.github/workflows/status_embed.yaml b/.github/workflows/status_embed.yaml
index 60bdaf770..8c99bb1b2 100644
--- a/.github/workflows/status_embed.yaml
+++ b/.github/workflows/status_embed.yaml
@@ -43,7 +43,7 @@ jobs:
curl -s -H "Authorization: token $GITHUB_TOKEN" ${{ github.event.workflow_run.artifacts_url }} > artifacts.json
DOWNLOAD_URL=$(cat artifacts.json | jq -r '.artifacts[] | select(.name == "pull-request-payload") | .archive_download_url')
[ -z "$DOWNLOAD_URL" ] && exit 1
- wget --quiet --header="Authorization: token $GITHUB_TOKEN" -O pull_request_payload.zip $DOWNLOAD_URL || exit 2
+ curl -sSL -H "Authorization: token $GITHUB_TOKEN" -o pull_request_payload.zip $DOWNLOAD_URL || exit 2
unzip -p pull_request_payload.zip > pull_request_payload.json
[ -s pull_request_payload.json ] || exit 3
echo "::set-output name=pr_author_login::$(jq -r '.user.login // empty' pull_request_payload.json)"
diff --git a/.gitignore b/.gitignore
index 65a9af431..630f83870 100644
--- a/.gitignore
+++ b/.gitignore
@@ -117,6 +117,7 @@ log.*
*config.yml
docker-compose.override.yml
metricity-config.toml
+pyrightconfig.json
# xmlrunner unittest XML reports
TEST-**.xml
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 728dbe0b2..bd37fe19c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -8,11 +8,6 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- - repo: https://github.com/pycqa/isort
- rev: 5.12.0
- hooks:
- - id: isort
- name: isort (python)
- repo: local
hooks:
- id: ruff
diff --git a/bot/bot.py b/bot/bot.py
index 2321232de..84caf1873 100644
--- a/bot/bot.py
+++ b/bot/bot.py
@@ -1,8 +1,11 @@
import asyncio
+import contextlib
+from sys import exception
import aiohttp
+from discord.errors import Forbidden
from pydis_core import BotBase
-from pydis_core.utils import scheduling
+from pydis_core.utils.error_handling import handle_forbidden_from_block
from sentry_sdk import push_scope
from bot import constants, exts
@@ -45,13 +48,22 @@ class Bot(BotBase):
async def setup_hook(self) -> None:
"""Default async initialisation method for discord.py."""
await super().setup_hook()
-
- # This is not awaited to avoid a deadlock with any cogs that have
- # wait_until_guild_available in their cog_load method.
- scheduling.create_task(self.load_extensions(exts))
+ await self.load_extensions(exts)
async def on_error(self, event: str, *args, **kwargs) -> None:
"""Log errors raised in event listeners rather than printing them to stderr."""
+ e_val = exception()
+
+ if isinstance(e_val, Forbidden):
+ message = args[0] if event == "on_message" else args[1] if event == "on_message_edit" else None
+
+ with contextlib.suppress(Forbidden):
+ # Attempt to handle the error. This reraises the error if's not due to a block,
+ # in which case the error is suppressed and handled normally. Otherwise, it was
+ # handled so return.
+ await handle_forbidden_from_block(e_val, message)
+ return
+
self.stats.incr(f"errors.event.{event}")
with push_scope() as scope:
diff --git a/bot/constants.py b/bot/constants.py
index e1d8a1e72..0250d0e31 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -8,23 +8,23 @@ By default, the values defined in the classes are used, these can be overridden
import os
from enum import Enum
-from pydantic import BaseModel, BaseSettings, root_validator
+from pydantic import BaseModel
+from pydantic_settings import BaseSettings
-class EnvConfig(BaseSettings):
+class EnvConfig(
+ BaseSettings,
+ env_file=(".env.server", ".env"),
+ env_file_encoding = "utf-8",
+ env_nested_delimiter = "__",
+ extra="ignore",
+):
"""Our default configuration for models that should load from .env files."""
- class Config:
- """Specify what .env files to load, and how to load them."""
-
- env_file = ".env.server", ".env",
- env_file_encoding = "utf-8"
- env_nested_delimiter = "__"
-
class _Miscellaneous(EnvConfig):
- debug = True
- file_logs = False
+ debug: bool = True
+ file_logs: bool = False
Miscellaneous = _Miscellaneous()
@@ -34,192 +34,185 @@ FILE_LOGS = Miscellaneous.file_logs
DEBUG_MODE = Miscellaneous.debug
-class _Bot(EnvConfig):
- EnvConfig.Config.env_prefix = "bot_"
+class _Bot(EnvConfig, env_prefix="bot_"):
- prefix = "!"
- sentry_dsn = ""
- token = ""
- trace_loggers = "*"
+ prefix: str = "!"
+ sentry_dsn: str = ""
+ token: str
+ trace_loggers: str = "*"
Bot = _Bot()
-class _Channels(EnvConfig):
- EnvConfig.Config.env_prefix = "channels_"
+class _Channels(EnvConfig, env_prefix="channels_"):
- announcements = 354619224620138496
- changelog = 748238795236704388
- mailing_lists = 704372456592506880
- python_events = 729674110270963822
- python_news = 704372456592506880
- reddit = 458224812528238616
+ announcements: int = 354619224620138496
+ changelog: int = 748238795236704388
+ mailing_lists: int = 704372456592506880
+ python_events: int = 729674110270963822
+ python_news: int = 704372456592506880
+ reddit: int = 458224812528238616
- dev_contrib = 635950537262759947
- dev_core = 411200599653351425
- dev_log = 622895325144940554
+ dev_contrib: int = 635950537262759947
+ dev_core: int = 411200599653351425
+ dev_log: int = 622895325144940554
- meta = 429409067623251969
- python_general = 267624335836053506
+ meta: int = 429409067623251969
+ python_general: int = 267624335836053506
- python_help = 1035199133436354600
+ python_help: int = 1035199133436354600
- attachment_log = 649243850006855680
- filter_log = 1014943924185473094
- message_log = 467752170159079424
- mod_log = 282638479504965634
- nomination_voting_archive = 833371042046148738
- user_log = 528976905546760203
- voice_log = 640292421988646961
+ attachment_log: int = 649243850006855680
+ filter_log: int = 1014943924185473094
+ message_log: int = 467752170159079424
+ mod_log: int = 282638479504965634
+ nomination_voting_archive: int = 833371042046148738
+ user_log: int = 528976905546760203
+ voice_log: int = 640292421988646961
- off_topic_0 = 291284109232308226
- off_topic_1 = 463035241142026251
- off_topic_2 = 463035268514185226
+ off_topic_0: int = 291284109232308226
+ off_topic_1: int = 463035241142026251
+ off_topic_2: int = 463035268514185226
- bot_commands = 267659945086812160
- discord_bots = 343944376055103488
- esoteric = 470884583684964352
- voice_gate = 764802555427029012
- code_jam_planning = 490217981872177157
+ bot_commands: int = 267659945086812160
+ discord_bots: int = 343944376055103488
+ esoteric: int = 470884583684964352
+ voice_gate: int = 764802555427029012
+ code_jam_planning: int = 490217981872177157
# 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_discussion = 798959130634747914
- nomination_voting = 822853512709931008
- organisation = 551789653284356126
+ admins: int = 365960823622991872
+ admin_spam: int = 563594791770914816
+ defcon: int = 464469101889454091
+ helpers: int = 385474242440986624
+ incidents: int = 714214212200562749
+ incidents_archive: int = 720668923636351037
+ mod_alerts: int = 473092532147060736
+ mod_meta: int = 775412552795947058
+ mods: int = 305126844661760000
+ nominations: int = 822920136150745168
+ nomination_discussion: int = 798959130634747914
+ nomination_voting: int = 822853512709931008
+ organisation: int = 551789653284356126
# Staff announcement channels
- admin_announcements = 749736155569848370
- mod_announcements = 372115205867700225
- staff_announcements = 464033278631084042
- staff_info = 396684402404622347
- staff_lounge = 464905259261755392
+ admin_announcements: int = 749736155569848370
+ mod_announcements: int = 372115205867700225
+ staff_announcements: int = 464033278631084042
+ staff_info: int = 396684402404622347
+ staff_lounge: int = 464905259261755392
# 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
+ admins_voice: int = 500734494840717332
+ code_help_voice_0: int = 751592231726481530
+ code_help_voice_1: int = 764232549840846858
+ general_voice_0: int = 751591688538947646
+ general_voice_1: int = 799641437645701151
+ staff_voice: int = 412375055910043655
- black_formatter = 846434317021741086
+ black_formatter: int = 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
+ code_help_chat_0: int = 755154969761677312
+ code_help_chat_1: int = 766330079135268884
+ staff_voice_chat: int = 541638762007101470
+ voice_chat_0: int = 412357430186344448
+ voice_chat_1: int = 799647045886541885
- big_brother = 468507907357409333
- duck_pond = 637820308341915648
- roles = 851270062434156586
+ big_brother: int = 468507907357409333
+ duck_pond: int = 637820308341915648
+ roles: int = 851270062434156586
- rules = 693837295685730335
+ rules: int = 693837295685730335
Channels = _Channels()
-class _Roles(EnvConfig):
-
- EnvConfig.Config.env_prefix = "roles_"
+class _Roles(EnvConfig, 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
- partners = 323426753857191936
- python_community = 458226413825294336
- voice_verified = 764802720779337729
+ advent_of_code: int = 518565788744024082
+ announcements: int = 463658397560995840
+ lovefest: int = 542431903886606399
+ pyweek_announcements: int = 897568414044938310
+ revival_of_code: int = 988801794668908655
+ legacy_help_channels_access: int = 1074780483776417964
+
+ contributors: int = 295488872404484098
+ partners: int = 323426753857191936
+ python_community: int = 458226413825294336
+ voice_verified: int = 764802720779337729
# Streaming
- video = 764245844798079016
+ video: int = 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
+ admins: int = 267628507062992896
+ core_developers: int = 587606783669829632
+ code_jam_event_team: int = 787816728474288181
+ devops: int = 409416496733880320
+ domain_leads: int = 807415650778742785
+ events_lead: int = 778361735739998228
+ helpers: int = 267630620367257601
+ moderators: int = 831776746206265384
+ mod_team: int = 267629731250176001
+ owners: int = 267627879762755584
+ project_leads: int = 815701647526330398
# Code Jam
- jammers = 737249140966162473
+ jammers: int = 737249140966162473
# Patreon
- patreon_tier_1 = 505040943800516611
- patreon_tier_2 = 743399725914390631
- patreon_tier_3 = 743400204367036520
+ patreon_tier_1: int = 505040943800516611
+ patreon_tier_2: int = 743399725914390631
+ patreon_tier_3: int = 743400204367036520
Roles = _Roles()
-class _Categories(EnvConfig):
- EnvConfig.Config.env_prefix = "categories_"
+class _Categories(EnvConfig, env_prefix="categories_"):
- logs = 468520609152892958
- moderators = 749736277464842262
- modmail = 714494672835444826
- appeals = 890331800025563216
- appeals_2 = 895417395261341766
- voice = 356013253765234688
+ logs: int = 468520609152892958
+ moderators: int = 749736277464842262
+ modmail: int = 714494672835444826
+ appeals: int = 890331800025563216
+ appeals_2: int = 895417395261341766
+ voice: int = 356013253765234688
# 2021 Summer Code Jam
- summer_code_jam = 861692638540857384
- python_help_system = 691405807388196926
+ summer_code_jam: int = 861692638540857384
+ python_help_system: int = 691405807388196926
Categories = _Categories()
-class _Guild(EnvConfig):
- EnvConfig.Config.env_prefix = "guild_"
+class _Guild(EnvConfig, env_prefix="guild_"):
- id = 267624335836053506
- invite = "https://discord.gg/python"
+ id: int = 267624335836053506
+ invite: str = "https://discord.gg/python"
- moderation_categories = [
+ moderation_categories: tuple[int, ...] = (
Categories.moderators,
Categories.modmail,
Categories.logs,
Categories.appeals,
Categories.appeals_2
- ]
- moderation_channels = [Channels.admins, Channels.admin_spam, Channels.mods]
- modlog_blacklist = [
+ )
+ moderation_channels: tuple[int, ...] = (Channels.admins, Channels.admin_spam, Channels.mods)
+ modlog_blacklist: tuple[int, ...] = (
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]
+ )
+ reminder_whitelist: tuple[int, ...] = (Channels.bot_commands, Channels.dev_contrib, Channels.black_formatter)
+ moderation_roles: tuple[int, ...] = (Roles.admins, Roles.mod_team, Roles.moderators, Roles.owners)
+ staff_roles: tuple[int, ...] = (Roles.admins, Roles.helpers, Roles.mod_team, Roles.owners)
Guild = _Guild()
@@ -268,8 +261,7 @@ class Webhook(BaseModel):
channel: int
-class _Webhooks(EnvConfig):
- EnvConfig.Config.env_prefix = "webhooks_"
+class _Webhooks(EnvConfig, env_prefix="webhooks_"):
big_brother: Webhook = Webhook(id=569133704568373283, channel=Channels.big_brother)
dev_log: Webhook = Webhook(id=680501655111729222, channel=Channels.dev_log)
@@ -282,85 +274,55 @@ class _Webhooks(EnvConfig):
Webhooks = _Webhooks()
-class _BigBrother(EnvConfig):
- EnvConfig.Config.env_prefix = "big_brother_"
+class _BigBrother(EnvConfig, env_prefix="big_brother_"):
- header_message_limit = 15
- log_delay = 15
+ header_message_limit: int = 15
+ log_delay: int = 15
BigBrother = _BigBrother()
-class _CodeBlock(EnvConfig):
- EnvConfig.Config.env_prefix = "code_block_"
+class _CodeBlock(EnvConfig, env_prefix="code_block_"):
# The channels in which code blocks will be detected. They are not subject to a cooldown.
- channel_whitelist: list[int] = [Channels.bot_commands]
+ channel_whitelist: tuple[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]
+ cooldown_channels: tuple[int, ...] = (Channels.python_general,)
- cooldown_seconds = 300
- minimum_lines = 4
+ cooldown_seconds: int = 300
+ minimum_lines: int = 4
CodeBlock = _CodeBlock()
-class _Colours(EnvConfig):
- EnvConfig.Config.env_prefix = "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
-
- @root_validator(pre=True)
- def parse_hex_values(cls, values: dict) -> dict: # noqa: N805
- """Convert hex strings to ints."""
- for key, value in values.items():
- values[key] = int(value, 16)
- return values
+class _HelpChannels(EnvConfig, env_prefix="help_channels_"):
-
-Colours = _Colours()
-
-
-class _HelpChannels(EnvConfig):
- EnvConfig.Config.env_prefix = "help_channels_"
-
- enable = True
- idle_minutes = 60
- deleted_idle_minutes = 5
+ enable: bool = True
+ idle_minutes: int = 60
+ deleted_idle_minutes: int = 5
# Roles which are allowed to use the command which makes channels dormant
- cmd_whitelist = Guild.moderation_roles
+ cmd_whitelist: tuple[int, ...] = Guild.moderation_roles
HelpChannels = _HelpChannels()
-class _RedirectOutput(EnvConfig):
- EnvConfig.Config.env_prefix = "redirect_output_"
+class _RedirectOutput(EnvConfig, env_prefix="redirect_output_"):
- delete_delay = 15
- delete_invocation = True
+ delete_delay: int = 15
+ delete_invocation: bool = True
RedirectOutput = _RedirectOutput()
-class _DuckPond(EnvConfig):
- EnvConfig.Config.env_prefix = "duck_pond_"
+class _DuckPond(EnvConfig, env_prefix="duck_pond_"):
- threshold = 7
+ threshold: int = 7
- channel_blacklist: list[int] = [
+ channel_blacklist: tuple[int, ...] = (
Channels.announcements,
Channels.python_news,
Channels.python_events,
@@ -371,125 +333,113 @@ class _DuckPond(EnvConfig):
Channels.staff_announcements,
Channels.mod_announcements,
Channels.admin_announcements,
- Channels.staff_info
- ]
+ Channels.staff_info,
+ )
DuckPond = _DuckPond()
-class _PythonNews(EnvConfig):
- EnvConfig.Config.env_prefix = "python_news_"
+class _PythonNews(EnvConfig, env_prefix="python_news_"):
channel: int = Webhooks.python_news.channel
webhook: int = Webhooks.python_news.id
- mail_lists = ["python-ideas", "python-announce-list", "pypi-announce", "python-dev"]
+ mail_lists: tuple[str, ...] = ("python-ideas", "python-announce-list", "pypi-announce", "python-dev")
PythonNews = _PythonNews()
-class _VoiceGate(EnvConfig):
- EnvConfig.Config.env_prefix = "voice_gate_"
+class _VoiceGate(EnvConfig, 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
+ delete_after_delay: int = 60
+ minimum_activity_blocks: int = 3
+ minimum_days_member: int = 3
+ minimum_messages: int = 50
VoiceGate = _VoiceGate()
-class _Branding(EnvConfig):
- EnvConfig.Config.env_prefix = "branding_"
+class _Branding(EnvConfig, env_prefix="branding_"):
- cycle_frequency = 3
+ cycle_frequency: int = 3
Branding = _Branding()
-class _VideoPermission(EnvConfig):
- EnvConfig.Config.env_prefix = "video_permission_"
+class _VideoPermission(EnvConfig, env_prefix="video_permission_"):
- default_permission_duration = 5
+ default_permission_duration: int = 5
VideoPermission = _VideoPermission()
-class _Redis(EnvConfig):
- EnvConfig.Config.env_prefix = "redis_"
+class _Redis(EnvConfig, env_prefix="redis_"):
- host = "redis.default.svc.cluster.local"
- password = ""
- port = 6379
- use_fakeredis = False # If this is True, Bot will use fakeredis.aioredis
+ host: str = "redis.default.svc.cluster.local"
+ password: str = ""
+ port: int = 6379
+ use_fakeredis: bool = False # If this is True, Bot will use fakeredis.aioredis
Redis = _Redis()
-class _CleanMessages(EnvConfig):
- EnvConfig.Config.env_prefix = "clean_"
+class _CleanMessages(EnvConfig, env_prefix="clean_"):
- message_limit = 10_000
+ message_limit: int = 10_000
CleanMessages = _CleanMessages()
-class _Stats(EnvConfig):
- EnvConfig.Config.env_prefix = "stats_"
+class _Stats(EnvConfig, env_prefix="stats_"):
- presence_update_timeout = 30
- statsd_host = "graphite.default.svc.cluster.local"
+ presence_update_timeout: int = 30
+ statsd_host: str = "graphite.default.svc.cluster.local"
Stats = _Stats()
-class _Cooldowns(EnvConfig):
- EnvConfig.Config.env_prefix = "cooldowns_"
+class _Cooldowns(EnvConfig, env_prefix="cooldowns_"):
- tags = 60
+ tags: int = 60
Cooldowns = _Cooldowns()
-class _Metabase(EnvConfig):
- EnvConfig.Config.env_prefix = "metabase_"
+class _Metabase(EnvConfig, env_prefix="metabase_"):
- username = ""
- password = ""
- base_url = "http://metabase.default.svc.cluster.local"
- public_url = "https://metabase.pythondiscord.com"
- max_session_age = 20_160
+ username: str = ""
+ password: str = ""
+ base_url: str = "http://metabase.default.svc.cluster.local"
+ public_url: str = "https://metabase.pythondiscord.com"
+ max_session_age: int = 20_160
Metabase = _Metabase()
-class _BaseURLs(EnvConfig):
- EnvConfig.Config.env_prefix = "urls_"
+class _BaseURLs(EnvConfig, env_prefix="urls_"):
# Snekbox endpoints
- snekbox_eval_api = "http://snekbox.default.svc.cluster.local/eval"
+ snekbox_eval_api: str = "http://snekbox.default.svc.cluster.local/eval"
# Discord API
- discord_api = "https://discordapp.com/api/v7/"
+ discord_api: str = "https://discordapp.com/api/v7/"
# Misc endpoints
- 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"
+ bot_avatar: str = "https://raw.githubusercontent.com/python-discord/branding/main/logos/logo_circle/logo_circle.png"
+ github_bot_repo: str = "https://github.com/python-discord/bot"
# Site
- site_api = "http://site.default.svc.cluster.local/api"
- site_paste = "https://paste.pythondiscord.com"
+ site_api: str = "http://site.default.svc.cluster.local/api"
+ paste_url: str = "https://paste.pythondiscord.com"
BaseURLs = _BaseURLs()
@@ -501,69 +451,67 @@ class _URLs(_BaseURLs):
discord_invite_api: str = "".join([BaseURLs.discord_api, "invites"])
# Base site vars
- connect_max_retries = 3
- connect_cooldown = 5
+ connect_max_retries: int = 3
+ connect_cooldown: int = 5
- paste_service: str = "".join([BaseURLs.site_paste, "/{key}"])
site_logs_view: str = "https://pythondiscord.com/staff/bot/logs"
URLs = _URLs()
-class _Emojis(EnvConfig):
- EnvConfig.Config.env_prefix = "emojis_"
+class _Emojis(EnvConfig, env_prefix="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>"
- badge_discord_certified_moderator = "<:discord_certified_moderator:1114130029547364434>"
- badge_bot_http_interactions = "<:bot_http_interactions:1114130379754975283>"
- badge_active_developer = "<:active_developer:1114130031036338176>"
- verified_bot = "<:verified_bot:811645219220750347>"
- bot = "<:bot:812712599464443914>"
+ badge_bug_hunter: str = "<:bug_hunter_lvl1:743882896372269137>"
+ badge_bug_hunter_level_2: str = "<:bug_hunter_lvl2:743882896611344505>"
+ badge_early_supporter: str = "<:early_supporter:743882896909140058>"
+ badge_hypesquad: str = "<:hypesquad_events:743882896892362873>"
+ badge_hypesquad_balance: str = "<:hypesquad_balance:743882896460480625>"
+ badge_hypesquad_bravery: str = "<:hypesquad_bravery:743882896745693335>"
+ badge_hypesquad_brilliance: str = "<:hypesquad_brilliance:743882896938631248>"
+ badge_partner: str = "<:partner:748666453242413136>"
+ badge_staff: str = "<:discord_staff:743882896498098226>"
+ badge_verified_bot_developer: str = "<:verified_bot_dev:743882897299210310>"
+ badge_discord_certified_moderator: str = "<:discord_certified_moderator:1114130029547364434>"
+ badge_bot_http_interactions: str = "<:bot_http_interactions:1114130379754975283>"
+ badge_active_developer: str = "<:active_developer:1114130031036338176>"
+ verified_bot: str = "<:verified_bot:811645219220750347>"
+ bot: str = "<:bot:812712599464443914>"
- defcon_shutdown = "<:defcondisabled:470326273952972810>"
- defcon_unshutdown = "<:defconenabled:470326274213150730>"
- defcon_update = "<:defconsettingsupdated:470326274082996224>"
+ defcon_shutdown: str = "<:defcondisabled:470326273952972810>"
+ defcon_unshutdown: str = "<:defconenabled:470326274213150730>"
+ defcon_update: str = "<:defconsettingsupdated:470326274082996224>"
- failmail = "<:failmail:633660039931887616>"
- failed_file = "<:failed_file:1073298441968562226>"
+ failmail: str = "<:failmail:633660039931887616>"
+ failed_file: str = "<:failed_file:1073298441968562226>"
- incident_actioned = "<:incident_actioned:714221559279255583>"
- incident_investigating = "<:incident_investigating:714224190928191551>"
- incident_unactioned = "<:incident_unactioned:714223099645526026>"
+ incident_actioned: str = "<:incident_actioned:714221559279255583>"
+ incident_investigating: str = "<:incident_investigating:714224190928191551>"
+ incident_unactioned: str = "<:incident_unactioned:714223099645526026>"
- status_dnd = "<:status_dnd:470326272082313216>"
- status_idle = "<:status_idle:470326266625785866>"
- status_offline = "<:status_offline:470326266537705472>"
- status_online = "<:status_online:470326272351010816>"
+ status_dnd: str = "<:status_dnd:470326272082313216>"
+ status_idle: str = "<:status_idle:470326266625785866>"
+ status_offline: str = "<:status_offline:470326266537705472>"
+ status_online: str = "<:status_online:470326272351010816>"
- ducky_dave = "<:ducky_dave:742058418692423772>"
+ ducky_dave: str = "<:ducky_dave:742058418692423772>"
- trashcan = "<:trashcan:637136429717389331>"
+ trashcan: str = "<:trashcan:637136429717389331>"
- bullet = "\u2022"
- check_mark = "\u2705"
- cross_mark = "\u274C"
- new = "\U0001F195"
- pencil = "\u270F"
+ bullet: str = "\u2022"
+ check_mark: str = "\u2705"
+ cross_mark: str = "\u274C"
+ new: str = "\U0001F195"
+ pencil: str = "\u270F"
- ok_hand = ":ok_hand:"
+ ok_hand: str = ":ok_hand:"
Emojis = _Emojis()
-class _Icons(EnvConfig):
- EnvConfig.Config.env_prefix = "icons_"
+class Icons:
+ """URLs to commonly used icons."""
crown_blurple = "https://cdn.discordapp.com/emojis/469964153289965568.png"
crown_green = "https://cdn.discordapp.com/emojis/469964154719961088.png"
@@ -617,15 +565,25 @@ class _Icons(EnvConfig):
voice_state_red = "https://cdn.discordapp.com/emojis/656899769905709076.png"
-Icons = _Icons()
+class Colours:
+ """Colour codes, mostly used to set discord.Embed colours."""
+ blue: int = 0x3775a8
+ bright_green: int = 0x01d277
+ orange: int = 0xe67e22
+ pink: int = 0xcf84e0
+ purple: int = 0xb734eb
+ soft_green: int = 0x68c290
+ soft_orange: int = 0xf9cb54
+ soft_red: int = 0xcd6d6d
+ white: int = 0xfffffe
+ yellow: int = 0xffd241
-class _Keys(EnvConfig):
- EnvConfig.Config.env_prefix = "api_keys_"
+class _Keys(EnvConfig, env_prefix="api_keys_"):
- github = ""
- site_api = ""
+ github: str = ""
+ site_api: str = ""
Keys = _Keys()
@@ -637,7 +595,7 @@ PROJECT_ROOT = os.path.abspath(os.path.join(BOT_DIR, os.pardir))
# Default role combinations
MODERATION_ROLES = Guild.moderation_roles
STAFF_ROLES = Guild.staff_roles
-STAFF_PARTNERS_COMMUNITY_ROLES = STAFF_ROLES + [Roles.partners, Roles.python_community]
+STAFF_PARTNERS_COMMUNITY_ROLES = STAFF_ROLES + (Roles.partners, Roles.python_community)
# Channel combinations
MODERATION_CHANNELS = Guild.moderation_channels
@@ -650,7 +608,7 @@ GIT_SHA = os.environ.get("GIT_SHA", "development")
# Bot replies
-NEGATIVE_REPLIES = [
+NEGATIVE_REPLIES = (
"Noooooo!!",
"Nope.",
"I'm sorry Dave, I'm afraid I can't do that.",
@@ -668,9 +626,9 @@ NEGATIVE_REPLIES = [
"NEGATORY.",
"Nuh-uh.",
"Not in my house!",
-]
+)
-POSITIVE_REPLIES = [
+POSITIVE_REPLIES = (
"Yep.",
"Absolutely!",
"Can do!",
@@ -688,9 +646,9 @@ POSITIVE_REPLIES = [
"Of course!",
"Aye aye, cap'n!",
"I'll allow it.",
-]
+)
-ERROR_REPLIES = [
+ERROR_REPLIES = (
"Please don't do that.",
"You have to stop.",
"Do you mind?",
@@ -701,4 +659,4 @@ ERROR_REPLIES = [
"Are you trying to kill me?",
"Noooooo!!",
"I can't believe you've done this",
-]
+)
diff --git a/bot/converters.py b/bot/converters.py
index 3c8ea44d7..34a764567 100644
--- a/bot/converters.py
+++ b/bot/converters.py
@@ -423,23 +423,19 @@ class HushDurationConverter(Converter):
def _is_an_unambiguous_user_argument(argument: str) -> bool:
- """Check if the provided argument is a user mention, user id, or username (name#discrim)."""
- has_id_or_mention = bool(IDConverter()._get_id_match(argument) or RE_USER_MENTION.match(argument))
+ """Check if the provided argument is a user mention or user id."""
+ user_id = IDConverter._get_id_match(argument)
+ user_mention = RE_USER_MENTION.match(argument)
- # Check to see if the author passed a username (a discriminator exists)
- argument = argument.removeprefix("@")
- has_username = len(argument) > 5 and argument[-5] == "#"
+ return bool(user_id or user_mention)
- return has_id_or_mention or has_username
-
-AMBIGUOUS_ARGUMENT_MSG = ("`{argument}` is not a User mention, a User ID or a Username in the format"
- " `name#discriminator`.")
+AMBIGUOUS_ARGUMENT_MSG = "`{argument}` is not a User mention or a User ID."
class UnambiguousUser(UserConverter):
"""
- Converts to a `discord.User`, but only if a mention, userID or a username (name#discrim) is provided.
+ Converts to a `discord.User`, but only if a mention or userID is provided.
Unlike the default `UserConverter`, it doesn't allow conversion from a name.
This is useful in cases where that lookup strategy would lead to too much ambiguity.
@@ -454,7 +450,7 @@ class UnambiguousUser(UserConverter):
class UnambiguousMember(MemberConverter):
"""
- Converts to a `discord.Member`, but only if a mention, userID or a username (name#discrim) is provided.
+ Converts to a `discord.Member`, but only if a mention or userID is provided.
Unlike the default `MemberConverter`, it doesn't allow conversion from a name or nickname.
This is useful in cases where that lookup strategy would lead to too much ambiguity.
@@ -504,23 +500,23 @@ class Infraction(Converter):
if t.TYPE_CHECKING:
- ValidDiscordServerInvite = dict # noqa: F811
+ ValidDiscordServerInvite = dict
ValidFilterListType = str
- Extension = str # noqa: F811
- PackageName = str # noqa: F811
- ValidURL = str # noqa: F811
- Inventory = tuple[str, _inventory_parser.InventoryDict] # noqa: F811
- Snowflake = int # noqa: F811
- SourceConverter = SourceType # noqa: F811
+ Extension = str
+ PackageName = str
+ ValidURL = str
+ Inventory = tuple[str, _inventory_parser.InventoryDict]
+ Snowflake = int
+ SourceConverter = SourceType
DurationDelta = relativedelta
- Duration = datetime # noqa: F811
- Age = datetime # noqa: F811
- OffTopicName = str # noqa: F811
- ISODateTime = datetime # noqa: F811
- HushDurationConverter = int # noqa: F811
- UnambiguousUser = discord.User # noqa: F811
- UnambiguousMember = discord.Member # noqa: F811
- Infraction = dict | None # noqa: F811
+ Duration = datetime
+ Age = datetime
+ OffTopicName = str
+ ISODateTime = datetime
+ HushDurationConverter = int
+ UnambiguousUser = discord.User
+ UnambiguousMember = discord.Member
+ Infraction = dict | None
Expiry = Duration | ISODateTime
DurationOrExpiry = DurationDelta | ISODateTime
diff --git a/bot/exts/backend/branding/_cog.py b/bot/exts/backend/branding/_cog.py
index 5d194ec3e..433f152b4 100644
--- a/bot/exts/backend/branding/_cog.py
+++ b/bot/exts/backend/branding/_cog.py
@@ -7,7 +7,6 @@ from datetime import timedelta
from enum import Enum
from operator import attrgetter
-import async_timeout
import discord
from arrow import Arrow
from async_rediscache import RedisCache
@@ -158,12 +157,12 @@ class Branding(commands.Cog):
timeout = 10 # Seconds.
try:
- with async_timeout.timeout(timeout): # Raise after `timeout` seconds.
+ async with asyncio.timeout(timeout): # Raise after `timeout` seconds.
await pydis.edit(**{asset_type.value: file})
except discord.HTTPException:
log.exception("Asset upload to Discord failed.")
return False
- except asyncio.TimeoutError:
+ except TimeoutError:
log.error(f"Asset upload to Discord timed out after {timeout} seconds.")
return False
else:
diff --git a/bot/exts/backend/error_handler.py b/bot/exts/backend/error_handler.py
index 0c8938918..faa39db5d 100644
--- a/bot/exts/backend/error_handler.py
+++ b/bot/exts/backend/error_handler.py
@@ -1,9 +1,10 @@
import copy
import difflib
-from discord import Embed, Member
+from discord import Embed, Forbidden, Member
from discord.ext.commands import ChannelNotFound, Cog, Context, TextChannelConverter, VoiceChannelConverter, errors
from pydis_core.site_api import ResponseCodeError
+from pydis_core.utils.error_handling import handle_forbidden_from_block
from sentry_sdk import push_scope
from bot.bot import Bot
@@ -64,11 +65,24 @@ class ErrorHandler(Cog):
)
if isinstance(e, errors.CommandNotFound) and not getattr(ctx, "invoked_from_error_handler", False):
- if await self.try_silence(ctx):
- return
- if await self.try_run_fixed_codeblock(ctx):
- return
- await self.try_get_tag(ctx) # Try to look for a tag with the command's name
+ # We might not invoke a command from the error handler, but it's easier and safer to ensure
+ # this is always set rather than trying to get it exact, and shouldn't cause any issues.
+ ctx.invoked_from_error_handler = True
+
+ # All errors from attempting to execute these commands should be handled by the error handler.
+ # We wrap non CommandErrors in CommandInvokeError to mirror the behaviour of normal commands.
+ try:
+ if await self.try_silence(ctx):
+ return
+ if await self.try_run_fixed_codeblock(ctx):
+ return
+ await self.try_get_tag(ctx)
+ except Exception as err:
+ log.info("Re-handling error raised by command in error handler")
+ if isinstance(err, errors.CommandError):
+ await self.on_command_error(ctx, err)
+ else:
+ await self.on_command_error(ctx, errors.CommandInvokeError(err))
elif isinstance(e, errors.UserInputError):
log.debug(debug_message)
await self.handle_user_input_error(ctx, e)
@@ -85,6 +99,11 @@ class ErrorHandler(Cog):
await ctx.send(f"{e.original} Please wait for it to finish and try again later.")
elif isinstance(e.original, InvalidInfractedUserError):
await ctx.send(f"Cannot infract that user. {e.original.reason}")
+ elif isinstance(e.original, Forbidden):
+ try:
+ await handle_forbidden_from_block(e.original, ctx.message)
+ except Forbidden:
+ await self.handle_unexpected_error(ctx, e.original)
else:
await self.handle_unexpected_error(ctx, e.original)
elif isinstance(e, errors.ConversionError):
@@ -123,7 +142,6 @@ class ErrorHandler(Cog):
command = ctx.invoked_with.lower()
args = ctx.message.content.lower().split(" ")
- ctx.invoked_from_error_handler = True
try:
if not await silence_command.can_run(ctx):
@@ -160,13 +178,7 @@ class ErrorHandler(Cog):
return False
async def try_get_tag(self, ctx: Context) -> None:
- """
- Attempt to display a tag by interpreting the command name as a tag name.
-
- The invocation of tags get respects its checks. Any CommandErrors raised will be handled
- by `on_command_error`, but the `invoked_from_error_handler` attribute will be added to
- the context to prevent infinite recursion in the case of a CommandNotFound exception.
- """
+ """Attempt to display a tag by interpreting the command name as a tag name."""
tags_cog = self.bot.get_cog("Tags")
if not tags_cog:
log.debug("Not attempting to parse message as a tag as could not find `Tags` cog.")
@@ -177,23 +189,19 @@ class ErrorHandler(Cog):
if not maybe_tag_name or not isinstance(ctx.author, Member):
return
- ctx.invoked_from_error_handler = True
try:
if not await self.bot.can_run(ctx):
log.debug("Cancelling attempt to fall back to a tag due to failed checks.")
return
+ except errors.CommandError:
+ log.debug("Cancelling attempt to fall back to a tag due to failed checks.")
+ return
- if await tags_get_command(ctx, maybe_tag_name):
- return
+ if await tags_get_command(ctx, maybe_tag_name):
+ return
- if not any(role.id in MODERATION_ROLES for role in ctx.author.roles):
- await self.send_command_suggestion(ctx, maybe_tag_name)
- except Exception as err:
- log.debug("Error while attempting to invoke tag fallback.")
- if isinstance(err, errors.CommandError):
- await self.on_command_error(ctx, err)
- else:
- await self.on_command_error(ctx, errors.CommandInvokeError(err))
+ if not any(role.id in MODERATION_ROLES for role in ctx.author.roles):
+ await self.send_command_suggestion(ctx, maybe_tag_name)
async def try_run_fixed_codeblock(self, ctx: Context) -> bool:
"""
diff --git a/bot/exts/backend/logging.py b/bot/exts/backend/logging.py
index eba9f3c74..331a7835e 100644
--- a/bot/exts/backend/logging.py
+++ b/bot/exts/backend/logging.py
@@ -15,7 +15,7 @@ class Logging(Cog):
def __init__(self, bot: Bot):
self.bot = bot
- scheduling.create_task(self.startup_greeting(), event_loop=self.bot.loop)
+ scheduling.create_task(self.startup_greeting())
async def startup_greeting(self) -> None:
"""Announce our presence to the configured devlog channel."""
diff --git a/bot/exts/filtering/_filter_lists/antispam.py b/bot/exts/filtering/_filter_lists/antispam.py
index 22f35f40e..f27412e1a 100644
--- a/bot/exts/filtering/_filter_lists/antispam.py
+++ b/bot/exts/filtering/_filter_lists/antispam.py
@@ -93,7 +93,7 @@ class AntispamList(UniquesListBase):
current_actions.pop("ping", None)
current_actions.pop("send_alert", None)
- new_infraction = current_actions[InfractionAndNotification.name].copy()
+ new_infraction = current_actions[InfractionAndNotification.name].model_copy()
# Smaller infraction value => higher in hierarchy.
if not current_infraction or new_infraction.infraction_type.value < current_infraction.value:
# Pick the first triggered filter for the reason, there's no good way to decide between them.
diff --git a/bot/exts/filtering/_filter_lists/extension.py b/bot/exts/filtering/_filter_lists/extension.py
index 84bd68aaa..d656bc6d2 100644
--- a/bot/exts/filtering/_filter_lists/extension.py
+++ b/bot/exts/filtering/_filter_lists/extension.py
@@ -4,7 +4,7 @@ import typing
from os.path import splitext
import bot
-from bot.constants import BaseURLs, Channels
+from bot.constants import Channels
from bot.exts.filtering._filter_context import Event, FilterContext
from bot.exts.filtering._filter_lists.filter_list import FilterList, ListType
from bot.exts.filtering._filters.extension import ExtensionFilter
@@ -14,16 +14,16 @@ from bot.exts.filtering._settings import ActionSettings
if typing.TYPE_CHECKING:
from bot.exts.filtering.filtering import Filtering
-
+PASTE_URL = "https://paste.pythondiscord.com"
PY_EMBED_DESCRIPTION = (
"It looks like you tried to attach a Python file - "
- f"please use a code-pasting service such as {BaseURLs.site_paste}"
+ f"please use a code-pasting service such as {PASTE_URL}"
)
TXT_LIKE_FILES = {".txt", ".csv", ".json"}
TXT_EMBED_DESCRIPTION = (
"You either uploaded a `{blocked_extension}` file or entered a message that was too long. "
- f"Please use our [paste bin]({BaseURLs.site_paste}) instead."
+ f"Please use our [paste bin]({PASTE_URL}) instead."
)
DISALLOWED_EMBED_DESCRIPTION = (
diff --git a/bot/exts/filtering/_filter_lists/filter_list.py b/bot/exts/filtering/_filter_lists/filter_list.py
index e5b6b2a65..2cc54e8fb 100644
--- a/bot/exts/filtering/_filter_lists/filter_list.py
+++ b/bot/exts/filtering/_filter_lists/filter_list.py
@@ -107,7 +107,7 @@ class AtomicList:
if ctx.event == Event.MESSAGE_EDIT and ctx.message and self.list_type == ListType.DENY:
previously_triggered = ctx.message_cache.get_message_metadata(ctx.message.id)
# The message might not be cached.
- if previously_triggered:
+ if previously_triggered and self in previously_triggered:
ignore_filters = previously_triggered[self]
# This updates the cache. Some filters are ignored, but they're necessary if there's another edit.
previously_triggered[self] = relevant_filters
diff --git a/bot/exts/filtering/_filter_lists/invite.py b/bot/exts/filtering/_filter_lists/invite.py
index 63310eb93..b43e1bb7c 100644
--- a/bot/exts/filtering/_filter_lists/invite.py
+++ b/bot/exts/filtering/_filter_lists/invite.py
@@ -20,9 +20,9 @@ if typing.TYPE_CHECKING:
REFINED_INVITE_CODE = re.compile(
- r"(?P<invite>[a-zA-Z0-9/]+)" # The supposedly real invite code.
- r"(?:[^a-zA-Z0-9/].*)?" # Ignoring anything that may come after an invalid character.
- r"$" # Up until the end of the string.
+ r"(?P<invite>[a-zA-Z0-9/_-]+)" # The supposedly real invite code.
+ r"(?:[^a-zA-Z0-9/].*)?" # Ignoring anything that may come after an invalid character.
+ r"$" # Up until the end of the string.
)
diff --git a/bot/exts/filtering/_filters/filter.py b/bot/exts/filtering/_filters/filter.py
index 6745e4f66..3f201cfde 100644
--- a/bot/exts/filtering/_filters/filter.py
+++ b/bot/exts/filtering/_filters/filter.py
@@ -31,7 +31,7 @@ class Filter(FieldRequiring):
self.updated_at = arrow.get(filter_data["updated_at"])
self.actions, self.validations = create_settings(filter_data["settings"], defaults=defaults)
if self.extra_fields_type:
- self.extra_fields = self.extra_fields_type.parse_obj(filter_data["additional_settings"])
+ self.extra_fields = self.extra_fields_type.model_validate(filter_data["additional_settings"])
else:
self.extra_fields = None
@@ -46,7 +46,7 @@ class Filter(FieldRequiring):
filter_settings = {}
if self.extra_fields:
- filter_settings = self.extra_fields.dict(exclude_unset=True)
+ filter_settings = self.extra_fields.model_dump(exclude_unset=True)
return settings, filter_settings
diff --git a/bot/exts/filtering/_filters/unique/rich_embed.py b/bot/exts/filtering/_filters/unique/rich_embed.py
index cfed18e64..cbcc90272 100644
--- a/bot/exts/filtering/_filters/unique/rich_embed.py
+++ b/bot/exts/filtering/_filters/unique/rich_embed.py
@@ -8,7 +8,7 @@ from bot.utils.helpers import remove_subdomain_from_url
log = get_logger(__name__)
-URL_RE = re.compile(r"(https?://\S+)", flags=re.IGNORECASE)
+URL_RE = re.compile(r"(https?://[^\)\s]+)", flags=re.IGNORECASE)
class RichEmbedFilter(UniqueFilter):
diff --git a/bot/exts/filtering/_settings.py b/bot/exts/filtering/_settings.py
index 766a5ea10..7005dd2d1 100644
--- a/bot/exts/filtering/_settings.py
+++ b/bot/exts/filtering/_settings.py
@@ -227,5 +227,5 @@ class Defaults(NamedTuple):
"""Return a dict representation of the stored fields across all entries."""
dict_ = {}
for settings in self:
- dict_ = reduce(operator.or_, (entry.dict() for entry in settings.values()), dict_)
+ dict_ = reduce(operator.or_, (entry.model_dump() for entry in settings.values()), dict_)
return dict_
diff --git a/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py b/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py
index f12538294..359aa7bc3 100644
--- a/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py
+++ b/bot/exts/filtering/_settings_types/actions/infraction_and_notification.py
@@ -6,7 +6,7 @@ import discord.abc
from dateutil.relativedelta import relativedelta
from discord import Colour, Embed, Member, User
from discord.errors import Forbidden
-from pydantic import validator
+from pydantic import field_validator
from pydis_core.utils.logging import get_logger
from pydis_core.utils.members import get_or_fetch_member
@@ -151,7 +151,7 @@ class InfractionAndNotification(ActionEntry):
infraction_duration: InfractionDuration
infraction_channel: int
- @validator("infraction_type", pre=True)
+ @field_validator("infraction_type", mode="before")
@classmethod
def convert_infraction_name(cls, infr_type: str | Infraction) -> Infraction:
"""Convert the string to an Infraction by name."""
@@ -221,14 +221,14 @@ class InfractionAndNotification(ActionEntry):
"""
# Lower number -> higher in the hierarchy
if self.infraction_type is None:
- return other.copy()
+ return other.model_copy()
if other.infraction_type is None:
- return self.copy()
+ return self.model_copy()
if self.infraction_type.value < other.infraction_type.value:
- result = self.copy()
+ result = self.model_copy()
elif self.infraction_type.value > other.infraction_type.value:
- result = other.copy()
+ result = other.model_copy()
other = self
else:
now = arrow.utcnow().datetime
@@ -236,9 +236,9 @@ class InfractionAndNotification(ActionEntry):
other.infraction_duration is not None
and now + self.infraction_duration.value > now + other.infraction_duration.value
):
- result = self.copy()
+ result = self.model_copy()
else:
- result = other.copy()
+ result = other.model_copy()
other = self
# If the winner has no message but the loser does, copy the message to the winner.
diff --git a/bot/exts/filtering/_settings_types/actions/ping.py b/bot/exts/filtering/_settings_types/actions/ping.py
index fa3e2224f..4b38a19f3 100644
--- a/bot/exts/filtering/_settings_types/actions/ping.py
+++ b/bot/exts/filtering/_settings_types/actions/ping.py
@@ -1,6 +1,6 @@
from typing import ClassVar, Self
-from pydantic import validator
+from pydantic import field_validator
from bot.exts.filtering._filter_context import FilterContext
from bot.exts.filtering._settings_types.settings_entry import ActionEntry
@@ -25,7 +25,7 @@ class Ping(ActionEntry):
guild_pings: set[str]
dm_pings: set[str]
- @validator("*", pre=True)
+ @field_validator("*", mode="before")
@classmethod
def init_sequence_if_none(cls, pings: list[str] | None) -> list[str]:
"""Initialize an empty sequence if the value is None."""
diff --git a/bot/exts/filtering/_settings_types/settings_entry.py b/bot/exts/filtering/_settings_types/settings_entry.py
index a3a2cbe1a..1cb9d9f2a 100644
--- a/bot/exts/filtering/_settings_types/settings_entry.py
+++ b/bot/exts/filtering/_settings_types/settings_entry.py
@@ -28,7 +28,7 @@ class SettingsEntry(BaseModel, FieldRequiring):
def __init__(self, defaults: SettingsEntry | None = None, /, **data):
overrides = set()
if defaults:
- defaults_dict = defaults.dict()
+ defaults_dict = defaults.model_dump()
for field_name, field_value in list(data.items()):
if field_value is None:
data[field_name] = defaults_dict[field_name]
diff --git a/bot/exts/filtering/_settings_types/validations/bypass_roles.py b/bot/exts/filtering/_settings_types/validations/bypass_roles.py
index 50fb1e650..0ddb2fdb5 100644
--- a/bot/exts/filtering/_settings_types/validations/bypass_roles.py
+++ b/bot/exts/filtering/_settings_types/validations/bypass_roles.py
@@ -1,6 +1,8 @@
-from typing import ClassVar, Union
+from collections.abc import Sequence
+from typing import ClassVar
from discord import Member
+from pydantic import field_validator
from bot.exts.filtering._filter_context import FilterContext
from bot.exts.filtering._settings_types.settings_entry import ValidationEntry
@@ -12,7 +14,26 @@ class RoleBypass(ValidationEntry):
name: ClassVar[str] = "bypass_roles"
description: ClassVar[str] = "A list of role IDs or role names. Users with these roles will not trigger the filter."
- bypass_roles: set[Union[int, str]] # noqa: UP007
+ bypass_roles: set[int | str]
+
+ @field_validator("bypass_roles", mode="before")
+ @classmethod
+ def init_if_bypass_roles_none(cls, bypass_roles: Sequence[int | str] | None) -> Sequence[int | str]:
+ """
+ Initialize an empty sequence if the value is None.
+
+ This also coerces each element of bypass_roles to an int, if possible.
+ """
+ if bypass_roles is None:
+ return []
+
+ def _coerce_to_int(input: int | str) -> int | str:
+ try:
+ return int(input)
+ except ValueError:
+ return input
+
+ return map(_coerce_to_int, bypass_roles)
def triggers_on(self, ctx: FilterContext) -> bool:
"""Return whether the filter should be triggered on this user given their roles."""
diff --git a/bot/exts/filtering/_settings_types/validations/channel_scope.py b/bot/exts/filtering/_settings_types/validations/channel_scope.py
index 1880ae27e..3d31131af 100644
--- a/bot/exts/filtering/_settings_types/validations/channel_scope.py
+++ b/bot/exts/filtering/_settings_types/validations/channel_scope.py
@@ -1,6 +1,7 @@
-from typing import ClassVar, Union
+from collections.abc import Sequence
+from typing import ClassVar
-from pydantic import validator
+from pydantic import field_validator
from bot.exts.filtering._filter_context import FilterContext
from bot.exts.filtering._settings_types.settings_entry import ValidationEntry
@@ -29,20 +30,29 @@ class ChannelScope(ValidationEntry):
)
}
- # NOTE: Don't change this to use the new 3.10 union syntax unless you ensure Pydantic type validation and coercion
- # work properly. At the time of writing this code there's a difference.
- disabled_channels: set[Union[int, str]] # noqa: UP007
- disabled_categories: set[Union[int, str]] # noqa: UP007
- enabled_channels: set[Union[int, str]] # noqa: UP007
- enabled_categories: set[Union[int, str]] # noqa: UP007
+ disabled_channels: set[int | str]
+ disabled_categories: set[int | str]
+ enabled_channels: set[int | str]
+ enabled_categories: set[int | str]
- @validator("*", pre=True)
+ @field_validator("*", mode="before")
@classmethod
- def init_if_sequence_none(cls, sequence: list[str] | None) -> list[str]:
- """Initialize an empty sequence if the value is None."""
+ def init_if_sequence_none(cls, sequence: Sequence[int | str] | None) -> Sequence[int | str]:
+ """
+ Initialize an empty sequence if the value is None.
+
+ This also coerces each element of sequence to an int, if possible.
+ """
if sequence is None:
return []
- return sequence
+
+ def _coerce_to_int(input: int | str) -> int | str:
+ try:
+ return int(input)
+ except ValueError:
+ return input
+
+ return map(_coerce_to_int, sequence)
def triggers_on(self, ctx: FilterContext) -> bool:
"""
diff --git a/bot/exts/filtering/_ui/filter.py b/bot/exts/filtering/_ui/filter.py
index 4300de19c..d15a3cacb 100644
--- a/bot/exts/filtering/_ui/filter.py
+++ b/bot/exts/filtering/_ui/filter.py
@@ -13,8 +13,16 @@ from pydis_core.site_api import ResponseCodeError
from bot.exts.filtering._filter_lists.filter_list import FilterList, ListType
from bot.exts.filtering._filters.filter import Filter
from bot.exts.filtering._ui.ui import (
- COMPONENT_TIMEOUT, CustomCallbackSelect, EditBaseView, MAX_EMBED_DESCRIPTION, MISSING, SETTINGS_DELIMITER,
- SINGLE_SETTING_PATTERN, format_response_error, parse_value, populate_embed_from_dict
+ COMPONENT_TIMEOUT,
+ CustomCallbackSelect,
+ EditBaseView,
+ MAX_EMBED_DESCRIPTION,
+ MISSING,
+ SETTINGS_DELIMITER,
+ SINGLE_SETTING_PATTERN,
+ format_response_error,
+ parse_value,
+ populate_embed_from_dict,
)
from bot.exts.filtering._utils import repr_equals, to_serializable
from bot.log import get_logger
@@ -34,7 +42,7 @@ def build_filter_repr_dict(
default_setting_values = {}
for settings_group in filter_list[list_type].defaults:
for _, setting in settings_group.items():
- default_setting_values.update(to_serializable(setting.dict(), ui_repr=True))
+ default_setting_values.update(to_serializable(setting.model_dump(), ui_repr=True))
# Add overrides. It's done in this way to preserve field order, since the filter won't have all settings.
total_values = {}
@@ -47,7 +55,7 @@ def build_filter_repr_dict(
# Add the filter-specific settings.
if filter_type.extra_fields_type:
# This iterates over the default values of the extra fields model.
- for name, value in filter_type.extra_fields_type().dict().items():
+ for name, value in filter_type.extra_fields_type().model_dump().items():
if name not in extra_fields_overrides or repr_equals(extra_fields_overrides[name], value):
total_values[f"{filter_type.name}/{name}"] = value
else:
@@ -287,7 +295,7 @@ class FilterEditView(EditBaseView):
if "/" in setting_name:
filter_name, setting_name = setting_name.split("/", maxsplit=1)
dict_to_edit = self.filter_settings_overrides
- default_value = self.filter_type.extra_fields_type().dict()[setting_name]
+ default_value = self.filter_type.extra_fields_type().model_dump()[setting_name]
else:
dict_to_edit = self.settings_overrides
default_value = self.filter_list[self.list_type].default(setting_name)
diff --git a/bot/exts/filtering/_ui/filter_list.py b/bot/exts/filtering/_ui/filter_list.py
index 062975ad7..a06e8a71e 100644
--- a/bot/exts/filtering/_ui/filter_list.py
+++ b/bot/exts/filtering/_ui/filter_list.py
@@ -10,8 +10,13 @@ from pydis_core.site_api import ResponseCodeError
from bot.exts.filtering._filter_lists import FilterList, ListType
from bot.exts.filtering._ui.ui import (
- CustomCallbackSelect, EditBaseView, MISSING, SETTINGS_DELIMITER, format_response_error, parse_value,
- populate_embed_from_dict
+ CustomCallbackSelect,
+ EditBaseView,
+ MISSING,
+ SETTINGS_DELIMITER,
+ format_response_error,
+ parse_value,
+ populate_embed_from_dict,
)
from bot.exts.filtering._utils import repr_equals, to_serializable
@@ -50,7 +55,7 @@ def build_filterlist_repr_dict(filter_list: FilterList, list_type: ListType, new
default_setting_values = {}
for settings_group in filter_list[list_type].defaults:
for _, setting in settings_group.items():
- default_setting_values.update(to_serializable(setting.dict(), ui_repr=True))
+ default_setting_values.update(to_serializable(setting.model_dump(), ui_repr=True))
# Add new values. It's done in this way to preserve field order, since the new_values won't have all settings.
total_values = {}
diff --git a/bot/exts/filtering/_ui/search.py b/bot/exts/filtering/_ui/search.py
index ccb47cdd2..d26fd9929 100644
--- a/bot/exts/filtering/_ui/search.py
+++ b/bot/exts/filtering/_ui/search.py
@@ -12,8 +12,13 @@ from bot.exts.filtering._filters.filter import Filter
from bot.exts.filtering._settings_types.settings_entry import SettingsEntry
from bot.exts.filtering._ui.filter import filter_overrides_for_ui
from bot.exts.filtering._ui.ui import (
- COMPONENT_TIMEOUT, CustomCallbackSelect, EditBaseView, MISSING, SETTINGS_DELIMITER, parse_value,
- populate_embed_from_dict
+ COMPONENT_TIMEOUT,
+ CustomCallbackSelect,
+ EditBaseView,
+ MISSING,
+ SETTINGS_DELIMITER,
+ parse_value,
+ populate_embed_from_dict,
)
diff --git a/bot/exts/filtering/_ui/ui.py b/bot/exts/filtering/_ui/ui.py
index ec5227198..711006222 100644
--- a/bot/exts/filtering/_ui/ui.py
+++ b/bot/exts/filtering/_ui/ui.py
@@ -156,13 +156,13 @@ def format_response_error(e: ResponseCodeError) -> Embed:
"""Format the response error into an embed."""
description = ""
if isinstance(e.response_json, list):
- description = "\n".join(f"• {error}" for error in e.response_json)
+ description = "\n".join(f"- {error}" for error in e.response_json)
elif isinstance(e.response_json, dict):
if "non_field_errors" in e.response_json:
non_field_errors = e.response_json.pop("non_field_errors")
- description += "\n".join(f"• {error}" for error in non_field_errors) + "\n"
+ description += "\n".join(f"- {error}" for error in non_field_errors) + "\n"
for field, errors in e.response_json.items():
- description += "\n".join(f"• {field} - {error}" for error in errors) + "\n"
+ description += "\n".join(f"- {field} - {error}" for error in errors) + "\n"
description = description.strip()
if len(description) > MAX_EMBED_DESCRIPTION:
diff --git a/bot/exts/filtering/_utils.py b/bot/exts/filtering/_utils.py
index e109a47ee..a12d09875 100644
--- a/bot/exts/filtering/_utils.py
+++ b/bot/exts/filtering/_utils.py
@@ -5,9 +5,10 @@ import importlib.util
import inspect
import pkgutil
import types
+import warnings
from abc import ABC, abstractmethod
from collections import defaultdict
-from collections.abc import Iterable
+from collections.abc import Callable, Iterable
from dataclasses import dataclass
from functools import cache
from typing import Any, Self, TypeVar, Union, get_args, get_origin
@@ -15,6 +16,8 @@ from typing import Any, Self, TypeVar, Union, get_args, get_origin
import discord
import regex
from discord.ext.commands import Command
+from pydantic import PydanticDeprecatedSince20
+from pydantic_core import core_schema
import bot
from bot.bot import Bot
@@ -180,11 +183,16 @@ class FieldRequiring(ABC):
# If a new attribute with the value MUST_SET_UNIQUE was defined in an abstract class, record it.
if inspect.isabstract(cls):
- for attribute in dir(cls):
- if getattr(cls, attribute, None) is FieldRequiring.MUST_SET_UNIQUE:
- if not inherited(attribute):
- # A new attribute with the value MUST_SET_UNIQUE.
- FieldRequiring.__unique_attributes[cls][attribute] = set()
+ with warnings.catch_warnings():
+ # The code below will raise a warning about the use the __fields__ attr on a pydantic model
+ # This will continue to be warned about until removed in pydantic 3.0
+ # This warning is a false-positive as only the custom MUST_SET_UNIQUE attr is used here
+ warnings.simplefilter("ignore", category=PydanticDeprecatedSince20)
+ for attribute in dir(cls):
+ if getattr(cls, attribute, None) is FieldRequiring.MUST_SET_UNIQUE:
+ if not inherited(attribute):
+ # A new attribute with the value MUST_SET_UNIQUE.
+ FieldRequiring.__unique_attributes[cls][attribute] = set()
return
for attribute in dir(cls):
@@ -252,12 +260,16 @@ class CustomIOField:
self.value = self.process_value(value)
@classmethod
- def __get_validators__(cls):
+ def __get_pydantic_core_schema__(
+ cls,
+ _source: type[Any],
+ _handler: Callable[[Any], core_schema.CoreSchema],
+ ) -> core_schema.CoreSchema:
"""Boilerplate for Pydantic."""
- yield cls.validate
+ return core_schema.with_info_plain_validator_function(cls.validate)
@classmethod
- def validate(cls, v: Any) -> Self:
+ def validate(cls, v: Any, _info: core_schema.ValidationInfo) -> Self:
"""Takes the given value and returns a class instance with that value."""
if isinstance(v, CustomIOField):
return cls(v.value)
diff --git a/bot/exts/filtering/filtering.py b/bot/exts/filtering/filtering.py
index d69bd9644..844f2942e 100644
--- a/bot/exts/filtering/filtering.py
+++ b/bot/exts/filtering/filtering.py
@@ -18,12 +18,13 @@ from discord.ext import commands, tasks
from discord.ext.commands import BadArgument, Cog, Context, command, has_any_role
from pydis_core.site_api import ResponseCodeError
from pydis_core.utils import scheduling
+from pydis_core.utils.paste_service import PasteFile, PasteTooLongError, PasteUploadError, send_to_paste_service
import bot
import bot.exts.filtering._ui.filter as filters_ui
from bot import constants
from bot.bot import Bot
-from bot.constants import Channels, Guild, MODERATION_ROLES, Roles
+from bot.constants import BaseURLs, Channels, Guild, MODERATION_ROLES, Roles
from bot.exts.backend.branding._repository import HEADERS, PARAMS
from bot.exts.filtering._filter_context import Event, FilterContext
from bot.exts.filtering._filter_lists import FilterList, ListType, ListTypeConverter, filter_list_types
@@ -32,12 +33,19 @@ from bot.exts.filtering._filters.filter import Filter, UniqueFilter
from bot.exts.filtering._settings import ActionSettings
from bot.exts.filtering._settings_types.actions.infraction_and_notification import Infraction
from bot.exts.filtering._ui.filter import (
- build_filter_repr_dict, description_and_settings_converter, filter_overrides_for_ui, populate_embed_from_dict
+ build_filter_repr_dict,
+ description_and_settings_converter,
+ filter_overrides_for_ui,
+ populate_embed_from_dict,
)
from bot.exts.filtering._ui.filter_list import FilterListAddView, FilterListEditView, settings_converter
from bot.exts.filtering._ui.search import SearchEditView, search_criteria_converter
from bot.exts.filtering._ui.ui import (
- AlertView, ArgumentCompletionView, DeleteConfirmationView, build_mod_alert, format_response_error
+ AlertView,
+ ArgumentCompletionView,
+ DeleteConfirmationView,
+ build_mod_alert,
+ format_response_error,
)
from bot.exts.filtering._utils import past_tense, repr_equals, starting_value, to_serializable
from bot.exts.moderation.infraction.infractions import COMP_BAN_DURATION, COMP_BAN_REASON
@@ -47,7 +55,6 @@ from bot.pagination import LinePaginator
from bot.utils.channel import is_mod_channel
from bot.utils.lock import lock_arg
from bot.utils.message_cache import MessageCache
-from bot.utils.services import PasteTooLongError, PasteUploadError, send_to_paste_service
log = get_logger(__name__)
@@ -181,7 +188,7 @@ class Filtering(Cog):
extra_fields_type,
type_hints[field_name]
)
- for field_name in extra_fields_type.__fields__
+ for field_name in extra_fields_type.model_fields
}
async def schedule_offending_messages_deletion(self) -> None:
@@ -754,7 +761,7 @@ class Filtering(Cog):
setting_values = {}
for settings_group in filter_list[list_type].defaults:
for _, setting in settings_group.items():
- setting_values.update(to_serializable(setting.dict(), ui_repr=True))
+ setting_values.update(to_serializable(setting.model_dump(), ui_repr=True))
embed = Embed(colour=Colour.blue())
populate_embed_from_dict(embed, setting_values)
@@ -1239,7 +1246,13 @@ class Filtering(Cog):
for current_settings in (filter_.actions, filter_.validations):
if current_settings:
for setting_entry in current_settings.values():
- settings.update({setting: None for setting in setting_entry.dict() if setting not in settings})
+ settings.update(
+ {
+ setting: None
+ for setting in setting_entry.model_dump()
+ if setting not in settings
+ }
+ )
# Even though the list ID remains unchanged, it still needs to be provided for correct serializer validation.
list_id = filter_list[list_type].id
@@ -1295,7 +1308,7 @@ class Filtering(Cog):
if not (differ_by_default <= override_matches): # The overrides didn't cover for the default mismatches.
return False
- filter_settings = filter_.extra_fields.dict() if filter_.extra_fields else {}
+ filter_settings = filter_.extra_fields.model_dump() if filter_.extra_fields else {}
# If the dict changes then some fields were not the same.
return (filter_settings | filter_settings_query) == filter_settings
@@ -1451,8 +1464,14 @@ class Filtering(Cog):
if e.code != 50035: # Content too long
raise
report = discord.utils.remove_markdown(report)
+ file = PasteFile(content=report, lexer="text")
try:
- paste_resp = await send_to_paste_service(report, extension="txt")
+ resp = await send_to_paste_service(
+ files=[file],
+ http_session=self.bot.http_session,
+ paste_url=BaseURLs.paste_url,
+ )
+ paste_resp = resp.link
except (ValueError, PasteTooLongError, PasteUploadError):
paste_resp = ":warning: Failed to upload report to paste service"
file_buffer = io.StringIO(report)
diff --git a/bot/exts/fun/duck_pond.py b/bot/exts/fun/duck_pond.py
index 63a455131..2abe72678 100644
--- a/bot/exts/fun/duck_pond.py
+++ b/bot/exts/fun/duck_pond.py
@@ -25,15 +25,6 @@ class DuckPond(Cog):
self.ducked_messages = []
self.relay_lock = None
- async def cog_load(self) -> None:
- """Fetches the webhook object, so we can post to it."""
- await self.bot.wait_until_guild_available()
-
- try:
- self.webhook = await self.bot.fetch_webhook(self.webhook_id)
- except discord.HTTPException:
- log.exception(f"Failed to fetch webhook with id `{self.webhook_id}`")
-
@staticmethod
def is_staff(member: MemberOrUser) -> bool:
"""Check if a specific member or user is staff."""
@@ -74,6 +65,12 @@ class DuckPond(Cog):
async def relay_message(self, message: Message) -> None:
"""Relays the message's content and attachments to the duck pond channel."""
+ if not self.webhook:
+ await self.bot.wait_until_guild_available()
+ # Fetching this can fail if using an invalid webhook id.
+ # Letting this error bubble up is fine as it will cause an error log and sentry event.
+ self.webhook = await self.bot.fetch_webhook(self.webhook_id)
+
if message.clean_content:
await send_webhook(
webhook=self.webhook,
diff --git a/bot/exts/fun/off_topic_names.py b/bot/exts/fun/off_topic_names.py
index 120e3b4a4..21dfaee20 100644
--- a/bot/exts/fun/off_topic_names.py
+++ b/bot/exts/fun/off_topic_names.py
@@ -81,7 +81,7 @@ class OffTopicNames(Cog):
async def list_ot_names(self, ctx: Context, active: bool = True) -> None:
"""Send an embed containing active/deactivated off-topic channel names."""
result = await self.bot.api_client.get("bot/off-topic-channel-names", params={"active": json.dumps(active)})
- lines = sorted(f"• {name}" for name in result)
+ lines = sorted(f"- {name}" for name in result)
embed = Embed(
title=f"{'Active' if active else 'Deactivated'} off-topic names (`{len(result)}` total)",
colour=Colour.blue()
@@ -207,7 +207,7 @@ class OffTopicNames(Cog):
try:
await asyncio.wait_for(rename_channel(), 3)
- except asyncio.TimeoutError:
+ except TimeoutError:
# Channel rename endpoint rate limited. The task was cancelled by asyncio.
btn_yes = Button(label="Yes", style=ButtonStyle.success)
btn_no = Button(label="No", style=ButtonStyle.danger)
@@ -286,7 +286,7 @@ class OffTopicNames(Cog):
close_matches = difflib.get_close_matches(query, result.keys(), n=10, cutoff=0.70)
# Send Results
- lines = sorted(f"• {result[name]}" for name in in_matches.union(close_matches))
+ lines = sorted(f"- {result[name]}" for name in in_matches.union(close_matches))
embed = Embed(
title="Query results",
colour=Colour.blue()
diff --git a/bot/exts/help_channels/_channel.py b/bot/exts/help_channels/_channel.py
index 759288266..fe7da789b 100644
--- a/bot/exts/help_channels/_channel.py
+++ b/bot/exts/help_channels/_channel.py
@@ -4,7 +4,7 @@ from datetime import timedelta
import arrow
import discord
-from pydis_core.utils import members, scheduling
+from pydis_core.utils import scheduling
import bot
from bot import constants
@@ -19,9 +19,9 @@ POST_TITLE = "Python help channel"
NEW_POST_MSG = """
**Remember to:**
-• **Ask** your Python question, not if you can ask or if there's an expert who can help.
-• **Show** a code sample as text (rather than a screenshot) and the error message, if you've got one.
-• **Explain** what you expect to happen and what actually happens.
+- **Ask** your Python question, not if you can ask or if there's an expert who can help.
+- **Show** a code sample as text (rather than a screenshot) and the error message, if you've got one.
+- **Explain** what you expect to happen and what actually happens.
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
"""
@@ -69,19 +69,6 @@ async def _close_help_post(closed_post: discord.Thread, closing_reason: _stats.C
_stats.report_post_count()
await _stats.report_complete_session(closed_post, closing_reason)
- poster = closed_post.owner
- cooldown_role = closed_post.guild.get_role(constants.Roles.help_cooldown)
-
- if poster is None:
- # We can't include the owner ID/name here since the thread only contains None
- log.info(
- f"Failed to remove cooldown role for owner of post ({closed_post.id}). "
- f"The user is likely no longer on the server."
- )
- return
-
- await members.handle_role_change(poster, poster.remove_roles, cooldown_role)
-
async def send_opened_post_message(post: discord.Thread) -> None:
"""Send the opener message in the new help post."""
@@ -159,9 +146,6 @@ async def help_post_opened(opened_post: discord.Thread, *, reopen: bool = False)
await send_opened_post_message(opened_post)
- cooldown_role = opened_post.guild.get_role(constants.Roles.help_cooldown)
- await members.handle_role_change(opened_post.owner, opened_post.owner.add_roles, cooldown_role)
-
async def help_post_closed(closed_post: discord.Thread) -> None:
"""Apply archive logic to a manually closed help forum post."""
@@ -188,10 +172,7 @@ async def help_post_deleted(deleted_post_event: discord.RawThreadDeleteEvent) ->
cached_post = deleted_post_event.thread
if cached_post and not cached_post.archived:
# If the post is in the bot's cache, and it was not archived before deleting,
- # report a complete session and remove the cooldown.
- poster = cached_post.owner
- cooldown_role = cached_post.guild.get_role(constants.Roles.help_cooldown)
- await members.handle_role_change(poster, poster.remove_roles, cooldown_role)
+ # report a complete session.
await _stats.report_complete_session(cached_post, _stats.ClosingReason.DELETED)
diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py
index 2d2bc27aa..2e7735148 100644
--- a/bot/exts/help_channels/_cog.py
+++ b/bot/exts/help_channels/_cog.py
@@ -8,6 +8,7 @@ from bot import constants
from bot.bot import Bot
from bot.exts.help_channels import _caches, _channel, _message
from bot.log import get_logger
+from bot.utils.checks import has_any_role_check
log = get_logger(__name__)
@@ -108,7 +109,7 @@ class HelpForum(commands.Cog):
# Silently fail in channels other than help posts
return
- if not await commands.has_any_role(constants.Roles.helpers).predicate(ctx):
+ if not await has_any_role_check(ctx, constants.Roles.helpers):
# Silently fail for non-helpers
return
diff --git a/bot/exts/info/code_snippets.py b/bot/exts/info/code_snippets.py
index dc5c2258a..a44b0c475 100644
--- a/bot/exts/info/code_snippets.py
+++ b/bot/exts/info/code_snippets.py
@@ -210,6 +210,9 @@ class CodeSnippets(Cog):
if not is_valid_language:
language = ""
+ if language == "pyi":
+ language = "py"
+
# Adds a label showing the file path to the snippet
if start_line == end_line:
ret = f"`{file_path}` line {start_line}\n"
diff --git a/bot/exts/info/codeblock/_cog.py b/bot/exts/info/codeblock/_cog.py
index 24d956b9f..6f095a2d0 100644
--- a/bot/exts/info/codeblock/_cog.py
+++ b/bot/exts/info/codeblock/_cog.py
@@ -114,7 +114,7 @@ class CodeBlockCog(Cog, name="Code Block"):
bot_message = await message.channel.send(f"Hey {message.author.mention}!", embed=embed)
self.codeblock_message_ids[message.id] = bot_message.id
- scheduling.create_task(wait_for_deletion(bot_message, (message.author.id,)), event_loop=self.bot.loop)
+ scheduling.create_task(wait_for_deletion(bot_message, (message.author.id,)))
# Increase amount of codeblock correction in stats
self.bot.stats.incr("codeblock_corrections")
diff --git a/bot/exts/info/doc/_batch_parser.py b/bot/exts/info/doc/_batch_parser.py
index 79bfb5482..4dfed205a 100644
--- a/bot/exts/info/doc/_batch_parser.py
+++ b/bot/exts/info/doc/_batch_parser.py
@@ -30,8 +30,7 @@ class StaleInventoryNotifier:
def __init__(self):
self._init_task = scheduling.create_task(
self._init_channel(),
- name="StaleInventoryNotifier channel init",
- event_loop=bot.instance.loop,
+ name="StaleInventoryNotifier channel init"
)
self._warned_urls = set()
diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py
index c82f40557..105f05108 100644
--- a/bot/exts/info/doc/_cog.py
+++ b/bot/exts/info/doc/_cog.py
@@ -233,7 +233,7 @@ class DocCog(commands.Cog):
"""
doc_item = self.doc_symbols.get(symbol_name)
if doc_item is None and " " in symbol_name:
- symbol_name = symbol_name.split(" ", maxsplit=1)[0]
+ symbol_name = symbol_name.split(maxsplit=1)[0]
doc_item = self.doc_symbols.get(symbol_name)
return symbol_name, doc_item
@@ -325,7 +325,7 @@ class DocCog(commands.Cog):
colour=discord.Colour.blue()
)
- lines = sorted(f"• [`{name}`]({url})" for name, url in self.base_urls.items())
+ lines = sorted(f"- [`{name}`]({url})" for name, url in self.base_urls.items())
if self.base_urls:
await LinePaginator.paginate(lines, ctx, inventory_embed, max_size=400, empty=False)
diff --git a/bot/exts/info/doc/_markdown.py b/bot/exts/info/doc/_markdown.py
index 315adda66..f3d769070 100644
--- a/bot/exts/info/doc/_markdown.py
+++ b/bot/exts/info/doc/_markdown.py
@@ -49,6 +49,8 @@ class DocMarkdownConverter(markdownify.MarkdownConverter):
def convert_a(self, el: PageElement, text: str, convert_as_inline: bool) -> str:
"""Resolve relative URLs to `self.page_url`."""
el["href"] = urljoin(self.page_url, el["href"])
+ # Discord doesn't handle titles properly, showing links with them as raw text.
+ el["title"] = None
return super().convert_a(el, text, convert_as_inline)
def convert_p(self, el: PageElement, text: str, convert_as_inline: bool) -> str:
diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py
index fe39d86fc..ef00f1235 100644
--- a/bot/exts/info/information.py
+++ b/bot/exts/info/information.py
@@ -11,6 +11,7 @@ from discord import AllowedMentions, Colour, Embed, Guild, Message, Role
from discord.ext.commands import BucketType, Cog, Context, Paginator, command, group, has_any_role
from discord.utils import escape_markdown
from pydis_core.site_api import ResponseCodeError
+from pydis_core.utils.members import get_or_fetch_member
from bot import constants
from bot.bot import Bot
@@ -22,7 +23,7 @@ from bot.pagination import LinePaginator
from bot.utils import time
from bot.utils.channel import is_mod_channel, is_staff_channel
from bot.utils.checks import cooldown_with_role_bypass, has_no_roles_check, in_whitelist_check
-from bot.utils.members import get_or_fetch_member
+from bot.utils.messages import send_denial
log = get_logger(__name__)
@@ -238,7 +239,7 @@ class Information(Cog):
await ctx.send(embed=embed)
@command(name="user", aliases=["user_info", "member", "member_info", "u"])
- async def user_info(self, ctx: Context, user_or_message: MemberOrUser | Message = None) -> None: # noqa: RUF013
+ async def user_info(self, ctx: Context, user_or_message: MemberOrUser | Message = None) -> None:
"""Returns info about a user."""
if passed_as_message := isinstance(user_or_message, Message):
user = user_or_message.author
@@ -420,7 +421,7 @@ class Information(Cog):
return "Nominations", "\n".join(output)
- async def user_messages(self, user: MemberOrUser) -> tuple[bool | str, tuple[str, str]]:
+ async def user_messages(self, user: MemberOrUser) -> tuple[str, str]:
"""
Gets the amount of messages for `member`.
@@ -435,15 +436,21 @@ class Information(Cog):
if e.status == 404:
activity_output = "No activity"
else:
- activity_output.append(f"{user_activity['total_messages']:,}" or "No messages")
- activity_output.append(f"{user_activity['activity_blocks']:,}" or "No activity")
+ total_message_text = (
+ f"{user_activity['total_messages']:,}" if user_activity["total_messages"] else "No messages"
+ )
+ activity_blocks_text = (
+ f"{user_activity['activity_blocks']:,}" if user_activity["activity_blocks"] else "No activity"
+ )
+ activity_output.append(total_message_text)
+ activity_output.append(activity_blocks_text)
activity_output = "\n".join(
f"{name}: {metric}"
for name, metric in zip(["Messages", "Activity blocks"], activity_output, strict=True)
)
- return ("Activity", activity_output)
+ return "Activity", activity_output
def format_fields(self, mapping: Mapping[str, Any], field_width: int | None = None) -> str:
"""Format a mapping to be readable to a human."""
@@ -524,13 +531,31 @@ class Information(Cog):
@cooldown_with_role_bypass(2, 60 * 3, BucketType.member, bypass_roles=constants.STAFF_PARTNERS_COMMUNITY_ROLES)
@group(invoke_without_command=True)
@in_whitelist(channels=(constants.Channels.bot_commands,), roles=constants.STAFF_PARTNERS_COMMUNITY_ROLES)
- async def raw(self, ctx: Context, message: Message) -> None:
+ async def raw(self, ctx: Context, message: Message | None = None) -> None:
"""Shows information about the raw API response."""
+ if message is None:
+ if (reference := ctx.message.reference) and isinstance(reference.resolved, Message):
+ message = reference.resolved
+ else:
+ await send_denial(
+ ctx, "Missing message argument. Please provide a message ID/link or reply to a message."
+ )
+ return
+
await self.send_raw_content(ctx, message)
@raw.command()
- async def json(self, ctx: Context, message: Message) -> None:
+ async def json(self, ctx: Context, message: Message | None = None) -> None:
"""Shows information about the raw API response in a copy-pasteable Python format."""
+ if message is None:
+ if (reference := ctx.message.reference) and isinstance(reference.resolved, Message):
+ message = reference.resolved
+ else:
+ await send_denial(
+ ctx, "Missing message argument. Please provide a message ID/link or reply to a message."
+ )
+ return
+
await self.send_raw_content(ctx, message, json=True)
async def _set_rules_command_help(self) -> None:
diff --git a/bot/exts/info/patreon.py b/bot/exts/info/patreon.py
index e3c8c07d1..1c8c443c8 100644
--- a/bot/exts/info/patreon.py
+++ b/bot/exts/info/patreon.py
@@ -3,13 +3,13 @@ import datetime
import arrow
import discord
from discord.ext import commands, tasks
+from pydis_core.utils.channel import get_or_fetch_channel
from bot import constants
from bot.bot import Bot
from bot.constants import Channels, Guild, Roles, STAFF_PARTNERS_COMMUNITY_ROLES
from bot.decorators import in_whitelist
from bot.log import get_logger
-from bot.utils.channel import get_or_fetch_channel
log = get_logger(__name__)
@@ -64,7 +64,7 @@ class Patreon(commands.Cog):
f":tada: {after.mention} just became a **tier {new_patreon_tier}** patron!\n"
"Support us on Patreon: https://pydis.com/patreon"
)
- channel = await get_or_fetch_channel(Channels.meta)
+ channel = await get_or_fetch_channel(self.bot, Channels.meta)
await channel.send(message)
async def send_current_supporters(self, channel: discord.abc.Messageable, automatic: bool = False) -> None:
@@ -77,7 +77,7 @@ class Patreon(commands.Cog):
# Filter out any members where this is not their highest tier.
patrons = [member for member in role.members if get_patreon_tier(member) == tier]
- patron_names = [f"• {patron}" for patron in patrons]
+ patron_names = [f"- {patron}" for patron in patrons]
embed = discord.Embed(
title=role.name,
@@ -120,7 +120,7 @@ class Patreon(commands.Cog):
"""A loop running daily to see if it's the first of the month. If so call `self.send_current_supporters()`."""
now = arrow.utcnow()
if now.day == 1:
- meta_channel = await get_or_fetch_channel(Channels.meta)
+ meta_channel = await get_or_fetch_channel(self.bot, Channels.meta)
await self.send_current_supporters(meta_channel, automatic=True)
diff --git a/bot/exts/info/pep.py b/bot/exts/info/pep.py
index 6f50769d0..2b552dc4f 100644
--- a/bot/exts/info/pep.py
+++ b/bot/exts/info/pep.py
@@ -4,17 +4,17 @@ from io import StringIO
from discord import Colour, Embed
from discord.ext.commands import Cog, Context, command
+from pydis_core.utils.caching import AsyncCache
from bot.bot import Bot
from bot.constants import Keys
from bot.log import get_logger
-from bot.utils.caching import AsyncCache
log = get_logger(__name__)
ICON_URL = "https://www.python.org/static/opengraph-icon-200x200.png"
BASE_PEP_URL = "https://peps.python.org/pep-"
-PEPS_LISTING_API_URL = "https://api.github.com/repos/python/peps/contents?ref=main"
+PEPS_LISTING_API_URL = "https://api.github.com/repos/python/peps/contents/peps?ref=main"
pep_cache = AsyncCache()
@@ -29,12 +29,8 @@ class PythonEnhancementProposals(Cog):
def __init__(self, bot: Bot):
self.bot = bot
self.peps: dict[int, str] = {}
- # To avoid situations where we don't have last datetime, set this to now.
- self.last_refreshed_peps: datetime = datetime.now(tz=UTC)
-
- async def cog_load(self) -> None:
- """Carry out cog asynchronous initialisation."""
- await self.refresh_peps_urls()
+ # Ensure peps are refreshed the first time this is checked
+ self.last_refreshed_peps: datetime = datetime.min.replace(tzinfo=UTC)
async def refresh_peps_urls(self) -> None:
"""Refresh PEP URLs listing in every 3 hours."""
@@ -104,7 +100,7 @@ class PythonEnhancementProposals(Cog):
# Assemble the embed
pep_embed = Embed(
title=f"**PEP {pep_nr} - {title}**",
- description=f"[Link]({BASE_PEP_URL}{pep_nr:04})",
+ url=f"{BASE_PEP_URL}{pep_nr:04}",
)
pep_embed.set_thumbnail(url=ICON_URL)
diff --git a/bot/exts/info/pypi.py b/bot/exts/info/pypi.py
index bac7d2389..e924b7a41 100644
--- a/bot/exts/info/pypi.py
+++ b/bot/exts/info/pypi.py
@@ -1,7 +1,9 @@
import itertools
import random
import re
+import typing
from contextlib import suppress
+from datetime import datetime
from discord import Embed, NotFound
from discord.ext.commands import Cog, Context, command
@@ -10,6 +12,7 @@ from discord.utils import escape_markdown
from bot.bot import Bot
from bot.constants import Colours, NEGATIVE_REPLIES, RedirectOutput
from bot.log import get_logger
+from bot.utils import time
from bot.utils.messages import wait_for_deletion
URL = "https://pypi.org/pypi/{package}/json"
@@ -22,6 +25,16 @@ INVALID_INPUT_DELETE_DELAY = RedirectOutput.delete_delay
log = get_logger(__name__)
+def _get_latest_distribution_timestamp(data: dict[str, typing.Any]) -> datetime | None:
+ """Get upload date of last distribution, or `None` if no distributions were found."""
+ if not data["urls"]:
+ return None
+
+ try:
+ return time.discord_timestamp(data["urls"][-1]["upload_time_iso_8601"], time.TimestampFormats.DATE)
+ except KeyError:
+ log.trace("KeyError trying to fetch upload time: data['urls'][-1]['upload_time_iso_8601']")
+ return None
class PyPi(Cog):
"""Cog for getting information about PyPi packages."""
@@ -63,6 +76,10 @@ class PyPi(Cog):
else:
embed.description = "No summary provided."
+ upload_date = _get_latest_distribution_timestamp(response_json)
+ if upload_date:
+ embed.description += f"\n\nReleased on {upload_date}."
+
error = False
else:
diff --git a/bot/exts/info/python_news.py b/bot/exts/info/python_news.py
index 76ebf2731..e06c82bb8 100644
--- a/bot/exts/info/python_news.py
+++ b/bot/exts/info/python_news.py
@@ -7,6 +7,7 @@ import feedparser
from bs4 import BeautifulSoup
from discord.ext.commands import Cog
from discord.ext.tasks import loop
+from pydis_core.site_api import ResponseCodeError
from bot import constants
from bot.bot import Bot
@@ -40,48 +41,32 @@ class PythonNews(Cog):
self.bot = bot
self.webhook_names = {}
self.webhook: discord.Webhook | None = None
+ self.seen_items: dict[str, set[str]] = {}
- async def cog_load(self) -> None:
- """Carry out cog asynchronous initialisation."""
- await self.get_webhook_names()
- await self.get_webhook_and_channel()
+ async def cog_unload(self) -> None:
+ """Stop news posting tasks on cog unload."""
+ self.fetch_new_media.cancel()
- async def start_tasks(self) -> None:
- """Start the tasks for fetching new PEPs and mailing list messages."""
- self.fetch_new_media.start()
+ async def get_webhooks(self) -> None:
+ """Get webhook author names from maillist API."""
+ async with self.bot.http_session.get("https://mail.python.org/archives/api/lists") as resp:
+ lists = await resp.json()
+
+ for mail in lists:
+ mailing_list_name = mail["name"].split("@")[0]
+ if mailing_list_name in constants.PythonNews.mail_lists:
+ self.webhook_names[mailing_list_name] = mail["display_name"]
+ self.webhook = await self.bot.fetch_webhook(constants.PythonNews.webhook)
@loop(minutes=20)
async def fetch_new_media(self) -> None:
"""Fetch new mailing list messages and then new PEPs."""
- await self.post_maillist_news()
- await self.post_pep_news()
-
- async def sync_maillists(self) -> None:
- """Sync currently in-use maillists with API."""
- # Wait until guild is available to avoid running before everything is ready
await self.bot.wait_until_guild_available()
+ if not self.webhook:
+ await self.get_webhooks()
- response = await self.bot.api_client.get("bot/bot-settings/news")
- for mail in constants.PythonNews.mail_lists:
- if mail not in response["data"]:
- response["data"][mail] = []
-
- # Because we are handling PEPs differently, we don't include it to mail lists
- if "pep" not in response["data"]:
- response["data"]["pep"] = []
-
- await self.bot.api_client.put("bot/bot-settings/news", json=response)
-
- async def get_webhook_names(self) -> None:
- """Get webhook author names from maillist API."""
- await self.bot.wait_until_guild_available()
-
- async with self.bot.http_session.get("https://mail.python.org/archives/api/lists") as resp:
- lists = await resp.json()
-
- for mail in lists:
- if mail["name"].split("@")[0] in constants.PythonNews.mail_lists:
- self.webhook_names[mail["name"].split("@")[0]] = mail["display_name"]
+ await self.post_maillist_news()
+ await self.post_pep_news()
@staticmethod
def escape_markdown(content: str) -> str:
@@ -93,16 +78,10 @@ class PythonNews(Cog):
async def post_pep_news(self) -> None:
"""Fetch new PEPs and when they don't have announcement in #python-news, create it."""
- # Wait until everything is ready and http_session available
- await self.bot.wait_until_guild_available()
- await self.sync_maillists()
-
async with self.bot.http_session.get(PEPS_RSS_URL) as resp:
data = feedparser.parse(await resp.text("utf-8"))
- news_listing = await self.bot.api_client.get("bot/bot-settings/news")
- payload = news_listing.copy()
- pep_numbers = news_listing["data"]["pep"]
+ pep_numbers = self.seen_items["pep"]
# Reverse entries to send oldest first
data["entries"].reverse()
@@ -139,26 +118,19 @@ class PythonNews(Cog):
avatar_url=AVATAR_URL,
wait=True,
)
- payload["data"]["pep"].append(pep_nr)
-
- # Increase overall PEP new stat
- self.bot.stats.incr("python_news.posted.pep")
-
- if msg.channel.is_news():
+ pep_successfully_added = await self.add_item_to_mail_list("pep", pep_nr)
+ if pep_successfully_added and msg.channel.is_news():
log.trace("Publishing PEP announcement because it was in a news channel")
await msg.publish()
- # Apply new sent news to DB to avoid duplicate sending
- await self.bot.api_client.put("bot/bot-settings/news", json=payload)
-
async def post_maillist_news(self) -> None:
"""Send new maillist threads to #python-news that is listed in configuration."""
- await self.bot.wait_until_guild_available()
- await self.sync_maillists()
- existing_news = await self.bot.api_client.get("bot/bot-settings/news")
- payload = existing_news.copy()
-
for maillist in constants.PythonNews.mail_lists:
+ if maillist not in self.seen_items:
+ # If for some reason we have a mailing list that isn't tracked.
+ log.warning("Mailing list %s doesn't exist in the database", maillist)
+ continue
+
async with self.bot.http_session.get(RECENT_THREADS_TEMPLATE.format(name=maillist)) as resp:
recents = BeautifulSoup(await resp.text(), features="lxml")
@@ -183,8 +155,9 @@ class PythonNews(Cog):
log.warning(f"Invalid datetime from Thread email: {email_information['date']}")
continue
+ thread_id = thread_information["thread_id"]
if (
- thread_information["thread_id"] in existing_news["data"][maillist]
+ thread_id in self.seen_items[maillist]
or "Re: " in thread_information["subject"]
or new_date.date() < datetime.now(tz=UTC).date()
):
@@ -216,16 +189,30 @@ class PythonNews(Cog):
avatar_url=AVATAR_URL,
wait=True,
)
- payload["data"][maillist].append(thread_information["thread_id"])
-
- # Increase this specific maillist counter in stats
- self.bot.stats.incr(f"python_news.posted.{maillist.replace('-', '_')}")
-
- if msg.channel.is_news():
+ thread_successfully_added = await self.add_item_to_mail_list(maillist, thread_id)
+ if thread_successfully_added and msg.channel.is_news():
log.trace("Publishing mailing list message because it was in a news channel")
await msg.publish()
- await self.bot.api_client.put("bot/bot-settings/news", json=payload)
+ async def add_item_to_mail_list(self, mail_list: str, item_identifier: str) -> bool:
+ """Adds a new item to a particular mailing_list."""
+ try:
+ await self.bot.api_client.post(f"bot/mailing-lists/{mail_list}/seen-items", json=item_identifier)
+ self.seen_items[mail_list].add(item_identifier)
+ # Increase this specific mailing list counter in stats
+ self.bot.stats.incr(f"python_news.posted.{mail_list.replace('-', '_')}")
+ return True
+
+ except ResponseCodeError as e:
+ non_field_errors = e.response_json.get("non_field_errors", [])
+ if non_field_errors != ["Seen item already known."]:
+ raise e
+ log.trace(
+ "Item %s has already been seen in the following mailing list: %s",
+ item_identifier,
+ mail_list
+ )
+ return False
async def get_thread_and_first_mail(self, maillist: str, thread_identifier: str) -> tuple[t.Any, t.Any]:
"""Get mail thread and first mail from mail.python.org based on `maillist` and `thread_identifier`."""
@@ -238,16 +225,18 @@ class PythonNews(Cog):
email_information = await resp.json()
return thread_information, email_information
- async def get_webhook_and_channel(self) -> None:
- """Storage #python-news channel Webhook and `TextChannel` to `News.webhook` and `channel`."""
- await self.bot.wait_until_guild_available()
- self.webhook = await self.bot.fetch_webhook(constants.PythonNews.webhook)
+ async def cog_load(self) -> None:
+ """Load all existing seen items from db and create any missing mailing lists."""
+ response = await self.bot.api_client.get("bot/mailing-lists")
+ for mailing_list in response:
+ self.seen_items[mailing_list["name"]] = set(mailing_list["seen_items"])
- await self.start_tasks()
+ for mailing_list in ("pep", *constants.PythonNews.mail_lists):
+ if mailing_list not in self.seen_items:
+ await self.bot.api_client.post("bot/mailing-lists", json={"name": mailing_list})
+ self.seen_items[mailing_list] = set()
- async def cog_unload(self) -> None:
- """Stop news posting tasks on cog unload."""
- self.fetch_new_media.cancel()
+ self.fetch_new_media.start()
async def setup(bot: Bot) -> None:
diff --git a/bot/exts/info/source.py b/bot/exts/info/source.py
index a0b2d5535..1c3387e53 100644
--- a/bot/exts/info/source.py
+++ b/bot/exts/info/source.py
@@ -23,7 +23,7 @@ class BotSource(commands.Cog):
self,
ctx: commands.Context,
*,
- source_item: SourceConverter = None, # noqa: RUF013
+ source_item: SourceConverter = None,
) -> None:
"""Display information and a GitHub link to the source code of a command, tag, or cog."""
if not source_item:
@@ -69,7 +69,7 @@ class BotSource(commands.Cog):
# Handle tag file location differently than others to avoid errors in some cases
if not first_line_no:
- file_location = Path(filename).relative_to("bot/")
+ file_location = Path(filename)
else:
file_location = Path(filename).relative_to(Path.cwd()).as_posix()
diff --git a/bot/exts/info/subscribe.py b/bot/exts/info/subscribe.py
index 4f57f7c0f..048884587 100644
--- a/bot/exts/info/subscribe.py
+++ b/bot/exts/info/subscribe.py
@@ -5,12 +5,12 @@ import discord
from discord.ext import commands
from discord.interactions import Interaction
from pydis_core.utils import members
+from pydis_core.utils.channel import get_or_fetch_channel
from bot import constants
from bot.bot import Bot
from bot.decorators import redirect_output
from bot.log import get_logger
-from bot.utils.channel import get_or_fetch_channel
@dataclass(frozen=True)
@@ -198,7 +198,7 @@ class Subscribe(commands.Cog):
If the initial message isn't found, a new one will be created.
This message will always be needed to attach the persistent view to it
"""
- roles_channel: discord.TextChannel = await get_or_fetch_channel(constants.Channels.roles)
+ roles_channel: discord.TextChannel = await get_or_fetch_channel(self.bot, constants.Channels.roles)
async for message in roles_channel.history(limit=30):
if message.content == self.SELF_ASSIGNABLE_ROLES_MESSAGE:
diff --git a/bot/exts/moderation/clean.py b/bot/exts/moderation/clean.py
index 731c52300..a7605460d 100644
--- a/bot/exts/moderation/clean.py
+++ b/bot/exts/moderation/clean.py
@@ -21,6 +21,7 @@ from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
from bot.utils.channel import is_mod_channel
from bot.utils.messages import upload_log
+from bot.utils.modlog import send_log_message
log = get_logger(__name__)
@@ -60,8 +61,8 @@ class Regex(Converter):
if TYPE_CHECKING: # Used to allow method resolution in IDEs like in converters.py.
- CleanChannels = Literal["*"] | list[TextChannel] # noqa: F811
- Regex = re.Pattern # noqa: F811
+ CleanChannels = Literal["*"] | list[TextChannel]
+ Regex = re.Pattern
class Clean(Cog):
@@ -367,7 +368,8 @@ class Clean(Cog):
f"A log of the deleted messages can be found [here]({log_url})."
)
- await self.mod_log.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url=Icons.message_bulk_delete,
colour=Colour(Colours.soft_red),
title="Bulk message delete",
@@ -464,13 +466,13 @@ class Clean(Cog):
async def clean_group(
self,
ctx: Context,
- users: Greedy[User] = None, # noqa: RUF013
+ users: Greedy[User] = None,
first_limit: CleanLimit | None = None,
second_limit: CleanLimit | None = None,
regex: Regex | None = None,
bots_only: bool | None = False,
*,
- channels: CleanChannels = None # noqa: RUF013 "Optional" with discord.py silently ignores incorrect input.
+ channels: CleanChannels = None # "Optional" with discord.py silently ignores incorrect input.
) -> None:
"""
Commands for cleaning messages in channels.
@@ -501,7 +503,7 @@ class Clean(Cog):
users: Greedy[User],
message_or_time: CleanLimit,
*,
- channels: CleanChannels = None # noqa: RUF013
+ channels: CleanChannels = None
) -> None:
"""
Delete messages posted by the provided users, stop cleaning after reaching `message_or_time`.
@@ -522,7 +524,7 @@ class Clean(Cog):
ctx: Context,
message_or_time: CleanLimit,
*,
- channels: CleanChannels = None, # noqa: RUF013
+ channels: CleanChannels = None,
) -> None:
"""
Delete all messages posted by a bot, stop cleaning after reaching `message_or_time`.
@@ -544,7 +546,7 @@ class Clean(Cog):
regex: Regex,
message_or_time: CleanLimit,
*,
- channels: CleanChannels = None # noqa: RUF013
+ channels: CleanChannels = None
) -> None:
"""
Delete all messages that match a certain regex, stop cleaning after reaching `message_or_time`.
@@ -572,7 +574,7 @@ class Clean(Cog):
self,
ctx: Context,
until: CleanLimit,
- channel: TextChannel = None # noqa: RUF013
+ channel: TextChannel = None
) -> None:
"""
Delete all messages until a certain limit.
@@ -598,7 +600,7 @@ class Clean(Cog):
ctx: Context,
first_limit: CleanLimit,
second_limit: CleanLimit,
- channel: TextChannel = None # noqa: RUF013
+ channel: TextChannel = None
) -> None:
"""
Delete all messages within range.
diff --git a/bot/exts/moderation/defcon.py b/bot/exts/moderation/defcon.py
index 3c16f8e0e..3196c4a1a 100644
--- a/bot/exts/moderation/defcon.py
+++ b/bot/exts/moderation/defcon.py
@@ -21,6 +21,7 @@ from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
from bot.utils import time
from bot.utils.messages import format_user
+from bot.utils.modlog import send_log_message
log = get_logger(__name__)
@@ -136,9 +137,13 @@ class Defcon(Cog):
if not message_sent:
message = f"{message}\n\nUnable to send rejection message via DM; they probably have DMs disabled."
- await (await self.get_mod_log()).send_log_message(
- Icons.defcon_denied, Colours.soft_red, "Entry denied",
- message, member.display_avatar.url
+ await send_log_message(
+ self.bot,
+ Icons.defcon_denied,
+ Colours.soft_red,
+ "Entry denied",
+ message,
+ thumbnail=member.display_avatar.url
)
@group(name="defcon", aliases=("dc",), invoke_without_command=True)
@@ -304,7 +309,7 @@ class Defcon(Cog):
)
status_msg = f"DEFCON {action.name.lower()}"
- await (await self.get_mod_log()).send_log_message(info.icon, info.color, status_msg, log_msg)
+ await send_log_message(self.bot, info.icon, info.color, status_msg, log_msg)
def _update_notifier(self) -> None:
"""Start or stop the notifier according to the DEFCON status."""
diff --git a/bot/exts/moderation/dm_relay.py b/bot/exts/moderation/dm_relay.py
index bf0b96a58..03b18e46a 100644
--- a/bot/exts/moderation/dm_relay.py
+++ b/bot/exts/moderation/dm_relay.py
@@ -1,11 +1,11 @@
import discord
from discord.ext.commands import Cog, Context, command, has_any_role
+from pydis_core.utils.paste_service import PasteFile, PasteTooLongError, PasteUploadError, send_to_paste_service
from bot.bot import Bot
-from bot.constants import Emojis, MODERATION_ROLES
+from bot.constants import BaseURLs, Emojis, MODERATION_ROLES
from bot.log import get_logger
from bot.utils.channel import is_mod_channel
-from bot.utils.services import PasteTooLongError, PasteUploadError, send_to_paste_service
log = get_logger(__name__)
@@ -53,8 +53,14 @@ class DMRelay(Cog):
f"User: {user} ({user.id})\n"
f"Channel ID: {user.dm_channel.id}\n\n"
)
+ file = PasteFile(content=metadata + output, lexer="text")
try:
- message = await send_to_paste_service(metadata + output, extension="txt")
+ resp = await send_to_paste_service(
+ files=[file],
+ http_session=self.bot.http_session,
+ paste_url=BaseURLs.paste_url,
+ )
+ message = resp.link
except PasteTooLongError:
message = f"{Emojis.cross_mark} Too long to upload to paste service."
except PasteUploadError:
diff --git a/bot/exts/moderation/incidents.py b/bot/exts/moderation/incidents.py
index 7fb8cac5a..9c11ad96c 100644
--- a/bot/exts/moderation/incidents.py
+++ b/bot/exts/moderation/incidents.py
@@ -319,10 +319,10 @@ class Incidents(Cog):
self.bot = bot
self.incidents_webhook = None
- scheduling.create_task(self.fetch_webhook(), event_loop=self.bot.loop)
+ scheduling.create_task(self.fetch_webhook())
self.event_lock = asyncio.Lock()
- self.crawl_task = scheduling.create_task(self.crawl_incidents(), event_loop=self.bot.loop)
+ self.crawl_task = scheduling.create_task(self.crawl_incidents())
async def fetch_webhook(self) -> None:
"""Fetch the incidents webhook object, so we can post message link embeds to it."""
@@ -416,7 +416,7 @@ class Incidents(Cog):
return payload.message_id == incident.id
coroutine = self.bot.wait_for("raw_message_delete", check=check, timeout=timeout)
- return scheduling.create_task(coroutine, event_loop=self.bot.loop)
+ return scheduling.create_task(coroutine)
async def process_event(self, reaction: str, incident: discord.Message, member: discord.Member) -> None:
"""
@@ -478,7 +478,7 @@ class Incidents(Cog):
log.trace(f"Awaiting deletion confirmation: {timeout=} seconds")
try:
await confirmation_task
- except asyncio.TimeoutError:
+ except TimeoutError:
log.info(f"Did not receive incident deletion confirmation within {timeout} seconds!")
else:
log.trace("Deletion was confirmed")
diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py
index a079f775e..aed6210a2 100644
--- a/bot/exts/moderation/infraction/_scheduler.py
+++ b/bot/exts/moderation/infraction/_scheduler.py
@@ -20,6 +20,7 @@ from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
from bot.utils import messages, time
from bot.utils.channel import is_mod_channel
+from bot.utils.modlog import send_log_message
log = get_logger(__name__)
@@ -270,7 +271,8 @@ class InfractionScheduler:
# Send a log message to the mod log.
# Don't use ctx.message.author for the actor; antispam only patches ctx.author.
log.trace(f"Sending apply mod log for infraction #{id_}.")
- await self.mod_log.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url=icon,
colour=Colours.soft_red,
title=f"Infraction {log_title}: {' '.join(infr_type.split('_'))}",
@@ -369,7 +371,8 @@ class InfractionScheduler:
log_text["Reason"] = log_text.pop("Reason")
# Send a log message to the mod log.
- await self.mod_log.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url=_utils.INFRACTION_ICONS[infr_type][1],
colour=Colours.soft_green,
title=f"Infraction {log_title}: {' '.join(infr_type.split('_'))}",
@@ -507,7 +510,8 @@ class InfractionScheduler:
log_text["Reason"] = log_text.pop("Reason")
log.trace(f"Sending deactivation mod log for infraction #{id_}.")
- await self.mod_log.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url=_utils.INFRACTION_ICONS[type_][1],
colour=Colours.soft_green,
title=f"Infraction {log_title}: {type_}",
diff --git a/bot/exts/moderation/infraction/_utils.py b/bot/exts/moderation/infraction/_utils.py
index 1bd5c5d5f..baeb971e4 100644
--- a/bot/exts/moderation/infraction/_utils.py
+++ b/bot/exts/moderation/infraction/_utils.py
@@ -39,17 +39,26 @@ INFRACTION_APPEAL_MODMAIL_FOOTER = (
"\nIf you would like to discuss or appeal this infraction, "
f"send a message to the ModMail bot (<@{MODMAIL_ACCOUNT_ID}>)."
)
+INFRACTION_MODMAIL_FOOTER = (
+ "\nIf you would like to discuss this infraction, "
+ f"send a message to the ModMail bot (<@{MODMAIL_ACCOUNT_ID}>)."
+)
INFRACTION_AUTHOR_NAME = "Infraction information"
LONGEST_EXTRAS = max(len(INFRACTION_APPEAL_SERVER_FOOTER), len(INFRACTION_APPEAL_MODMAIL_FOOTER))
-INFRACTION_DESCRIPTION_TEMPLATE = (
+INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE = (
"**Type:** {type}\n"
"**Duration:** {duration}\n"
"**Expires:** {expires}\n"
"**Reason:** {reason}\n"
)
+INFRACTION_DESCRIPTION_WARNING_TEMPLATE = (
+ "**Type:** Warning\n"
+ "**Reason:** {reason}\n"
+)
+
async def post_user(ctx: Context, user: MemberOrUser) -> dict | None:
"""
@@ -213,18 +222,27 @@ async def notify_infraction(
if reason is None:
reason = infraction["reason"]
- text = INFRACTION_DESCRIPTION_TEMPLATE.format(
- type=infr_type.title(),
- expires=expires_at,
- duration=duration,
- reason=reason or "No reason provided."
- )
+ if infraction["type"] == "warning":
+ text = INFRACTION_DESCRIPTION_WARNING_TEMPLATE.format(
+ reason=reason or "No reason provided."
+ )
+ else:
+ text = INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format(
+ type=infr_type.title(),
+ expires=expires_at,
+ duration=duration,
+ reason=reason or "No reason provided."
+ )
# For case when other fields than reason is too long and this reach limit, then force-shorten string
if len(text) > 4096 - LONGEST_EXTRAS:
text = f"{text[:4093-LONGEST_EXTRAS]}..."
- text += INFRACTION_APPEAL_SERVER_FOOTER if infraction["type"] == "ban" else INFRACTION_APPEAL_MODMAIL_FOOTER
+ text += (
+ INFRACTION_APPEAL_SERVER_FOOTER if infraction["type"] == "ban"
+ else INFRACTION_MODMAIL_FOOTER if infraction["type"] == "warning"
+ else INFRACTION_APPEAL_MODMAIL_FOOTER
+ )
embed = discord.Embed(
description=text,
diff --git a/bot/exts/moderation/infraction/infractions.py b/bot/exts/moderation/infraction/infractions.py
index 15d80cd58..6af2571de 100644
--- a/bot/exts/moderation/infraction/infractions.py
+++ b/bot/exts/moderation/infraction/infractions.py
@@ -8,6 +8,7 @@ from dateutil.relativedelta import relativedelta
from discord import Member
from discord.ext import commands
from discord.ext.commands import Context, command
+from pydis_core.utils.members import get_or_fetch_member
from bot import constants
from bot.bot import Bot
@@ -18,7 +19,6 @@ from bot.exts.moderation.infraction import _utils
from bot.exts.moderation.infraction._scheduler import InfractionScheduler
from bot.log import get_logger
from bot.utils.channel import is_mod_channel
-from bot.utils.members import get_or_fetch_member
from bot.utils.messages import format_user
log = get_logger(__name__)
diff --git a/bot/exts/moderation/infraction/management.py b/bot/exts/moderation/infraction/management.py
index 9fc0722ce..93959042b 100644
--- a/bot/exts/moderation/infraction/management.py
+++ b/bot/exts/moderation/infraction/management.py
@@ -6,6 +6,7 @@ import discord
from discord.ext import commands
from discord.ext.commands import Context
from discord.utils import escape_markdown
+from pydis_core.utils.members import get_or_fetch_member
from bot import constants
from bot.bot import Bot
@@ -15,12 +16,11 @@ from bot.decorators import ensure_future_timestamp
from bot.errors import InvalidInfractionError
from bot.exts.moderation.infraction import _utils
from bot.exts.moderation.infraction.infractions import Infractions
-from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
from bot.pagination import LinePaginator
from bot.utils import messages, time
from bot.utils.channel import is_in_category, is_mod_channel
-from bot.utils.members import get_or_fetch_member
+from bot.utils.modlog import send_log_message
from bot.utils.time import unpack_duration
log = get_logger(__name__)
@@ -58,17 +58,12 @@ class ModManagement(commands.Cog):
command.help += f"\n{SYMBOLS_GUIDE}"
@property
- def mod_log(self) -> ModLog:
- """Get currently loaded ModLog cog instance."""
- return self.bot.get_cog("ModLog")
-
- @property
def infractions_cog(self) -> Infractions:
"""Get currently loaded Infractions cog instance."""
return self.bot.get_cog("Infractions")
@commands.group(name="infraction", aliases=("infr", "infractions", "inf", "i"), invoke_without_command=True)
- async def infraction_group(self, ctx: Context, infraction: Infraction = None) -> None: # noqa: RUF013
+ async def infraction_group(self, ctx: Context, infraction: Infraction = None) -> None:
"""
Infraction management commands.
@@ -260,7 +255,8 @@ class ModManagement(commands.Cog):
else:
jump_url = f"[Click here.]({ctx.message.jump_url})"
- await self.mod_log.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url=constants.Icons.pencil,
colour=discord.Colour.og_blurple(),
title="Infraction edited",
@@ -299,8 +295,8 @@ class ModManagement(commands.Cog):
user_str = escape_markdown(str(user))
else:
if infraction_list:
- user = infraction_list[0]["user"]
- user_str = escape_markdown(user["name"]) + f"#{user['discriminator']:04}"
+ user_data = infraction_list[0]["user"]
+ user_str = escape_markdown(user_data["name"]) + f"#{user_data['discriminator']:04}"
else:
user_str = str(user.id)
diff --git a/bot/exts/moderation/infraction/superstarify.py b/bot/exts/moderation/infraction/superstarify.py
index 7de4d08b0..006334755 100644
--- a/bot/exts/moderation/infraction/superstarify.py
+++ b/bot/exts/moderation/infraction/superstarify.py
@@ -6,6 +6,7 @@ from pathlib import Path
from discord import Embed, Member
from discord.ext.commands import Cog, Context, command, has_any_role
from discord.utils import escape_markdown
+from pydis_core.utils.members import get_or_fetch_member
from bot import constants
from bot.bot import Bot
@@ -15,7 +16,6 @@ from bot.exts.moderation.infraction import _utils
from bot.exts.moderation.infraction._scheduler import InfractionScheduler
from bot.log import get_logger
from bot.utils import time
-from bot.utils.members import get_or_fetch_member
from bot.utils.messages import format_user
log = get_logger(__name__)
diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py
index f5483325f..cfb4f5f71 100644
--- a/bot/exts/moderation/metabase.py
+++ b/bot/exts/moderation/metabase.py
@@ -9,14 +9,13 @@ from aiohttp.client_exceptions import ClientResponseError
from arrow import Arrow
from async_rediscache import RedisCache
from discord.ext.commands import Cog, Context, group, has_any_role
+from pydis_core.utils.paste_service import PasteFile, PasteTooLongError, PasteUploadError, send_to_paste_service
from pydis_core.utils.scheduling import Scheduler
from bot.bot import Bot
-from bot.constants import Metabase as MetabaseConfig, Roles
+from bot.constants import BaseURLs, Metabase as MetabaseConfig, Roles
from bot.log import get_logger
-from bot.utils import send_to_paste_service
from bot.utils.channel import is_mod_channel
-from bot.utils.services import PasteTooLongError, PasteUploadError
log = get_logger(__name__)
@@ -129,6 +128,7 @@ class Metabase(Cog):
async with self.bot.http_session.post(url, headers=self.headers, raise_for_status=True) as resp:
if extension == "csv":
+ extension = "text" # paste site doesn't support csv as a lexer
out = await resp.text(encoding="utf-8")
# Save the output for use with int e
self.exports[question_id] = list(csv.DictReader(StringIO(out)))
@@ -141,14 +141,19 @@ class Metabase(Cog):
# Format it nicely for human eyes
out = json.dumps(out, indent=4, sort_keys=True)
+ file = PasteFile(content=out, lexer=extension)
try:
- paste_link = await send_to_paste_service(out, extension=extension)
+ resp = await send_to_paste_service(
+ files=[file],
+ http_session=self.bot.http_session,
+ paste_url=BaseURLs.paste_url,
+ )
except PasteTooLongError:
message = f":x: {ctx.author.mention} Too long to upload to paste service."
except PasteUploadError:
message = f":x: {ctx.author.mention} Failed to upload to paste service."
else:
- message = f":+1: {ctx.author.mention} Here's your link: {paste_link}"
+ message = f":+1: {ctx.author.mention} Here's your link: {resp.link}"
await ctx.send(
f"{message}\nYou can also access this data within internal eval by doing: "
diff --git a/bot/exts/moderation/modlog.py b/bot/exts/moderation/modlog.py
index b349f4d5d..74bdc9db9 100644
--- a/bot/exts/moderation/modlog.py
+++ b/bot/exts/moderation/modlog.py
@@ -8,14 +8,16 @@ from dateutil.relativedelta import relativedelta
from deepdiff import DeepDiff
from discord import Colour, Message, Thread
from discord.abc import GuildChannel
-from discord.ext.commands import Cog, Context
+from discord.ext.commands import Cog
from discord.utils import escape_markdown, format_dt, snowflake_time
+from pydis_core.utils.channel import get_or_fetch_channel
from bot.bot import Bot
from bot.constants import Channels, Colours, Emojis, Event, Guild as GuildConstant, Icons, Roles
from bot.log import get_logger
from bot.utils import time
from bot.utils.messages import format_user, upload_log
+from bot.utils.modlog import send_log_message
log = get_logger(__name__)
@@ -47,63 +49,6 @@ class ModLog(Cog, name="ModLog"):
if item not in self._ignored[event]:
self._ignored[event].append(item)
- async def send_log_message(
- self,
- icon_url: str | None,
- colour: discord.Colour | int,
- title: str | None,
- text: str,
- thumbnail: str | discord.Asset | None = None,
- channel_id: int = Channels.mod_log,
- ping_everyone: bool = False,
- files: list[discord.File] | None = None,
- content: str | None = None,
- additional_embeds: list[discord.Embed] | None = None,
- timestamp_override: datetime | None = None,
- footer: str | None = None,
- ) -> Context:
- """Generate log embed and send to logging channel."""
- await self.bot.wait_until_guild_available()
- # Truncate string directly here to avoid removing newlines
- embed = discord.Embed(
- description=text[:4093] + "..." if len(text) > 4096 else text
- )
-
- if title and icon_url:
- embed.set_author(name=title, icon_url=icon_url)
-
- embed.colour = colour
- embed.timestamp = timestamp_override or datetime.now(tz=UTC)
-
- if footer:
- embed.set_footer(text=footer)
-
- if thumbnail:
- embed.set_thumbnail(url=thumbnail)
-
- if ping_everyone:
- if content:
- content = f"<@&{Roles.moderators}> {content}"
- else:
- content = f"<@&{Roles.moderators}>"
-
- # Truncate content to 2000 characters and append an ellipsis.
- if content and len(content) > 2000:
- content = content[:2000 - 3] + "..."
-
- channel = self.bot.get_channel(channel_id)
- log_message = await channel.send(
- content=content,
- embed=embed,
- files=files
- )
-
- if additional_embeds:
- for additional_embed in additional_embeds:
- await channel.send(embed=additional_embed)
-
- return await self.bot.get_context(log_message) # Optionally return for use with antispam
-
@Cog.listener()
async def on_guild_channel_create(self, channel: GUILD_CHANNEL) -> None:
"""Log channel create event to mod log."""
@@ -128,7 +73,7 @@ class ModLog(Cog, name="ModLog"):
else:
message = f"{channel.name} (`{channel.id}`)"
- await self.send_log_message(Icons.hash_green, Colours.soft_green, title, message)
+ await send_log_message(self.bot, Icons.hash_green, Colours.soft_green, title, message)
@Cog.listener()
async def on_guild_channel_delete(self, channel: GUILD_CHANNEL) -> None:
@@ -148,9 +93,12 @@ class ModLog(Cog, name="ModLog"):
else:
message = f"{channel.name} (`{channel.id}`)"
- await self.send_log_message(
- Icons.hash_red, Colours.soft_red,
- title, message
+ await send_log_message(
+ self.bot,
+ Icons.hash_red,
+ Colours.soft_red,
+ title,
+ message
)
@Cog.listener()
@@ -211,9 +159,12 @@ class ModLog(Cog, name="ModLog"):
else:
message = f"**#{after.name}** (`{after.id}`)\n{message}"
- await self.send_log_message(
- Icons.hash_blurple, Colour.og_blurple(),
- "Channel updated", message
+ await send_log_message(
+ self.bot,
+ Icons.hash_blurple,
+ Colour.og_blurple(),
+ "Channel updated",
+ message
)
@Cog.listener()
@@ -222,9 +173,12 @@ class ModLog(Cog, name="ModLog"):
if role.guild.id != GuildConstant.id:
return
- await self.send_log_message(
- Icons.crown_green, Colours.soft_green,
- "Role created", f"`{role.id}`"
+ await send_log_message(
+ self.bot,
+ Icons.crown_green,
+ Colours.soft_green,
+ "Role created",
+ f"`{role.id}`"
)
@Cog.listener()
@@ -233,9 +187,12 @@ class ModLog(Cog, name="ModLog"):
if role.guild.id != GuildConstant.id:
return
- await self.send_log_message(
- Icons.crown_red, Colours.soft_red,
- "Role removed", f"{role.name} (`{role.id}`)"
+ await send_log_message(
+ self.bot,
+ Icons.crown_red,
+ Colours.soft_red,
+ "Role removed",
+ f"{role.name} (`{role.id}`)"
)
@Cog.listener()
@@ -286,9 +243,12 @@ class ModLog(Cog, name="ModLog"):
message = f"**{after.name}** (`{after.id}`)\n{message}"
- await self.send_log_message(
- Icons.crown_blurple, Colour.og_blurple(),
- "Role updated", message
+ await send_log_message(
+ self.bot,
+ Icons.crown_blurple,
+ Colour.og_blurple(),
+ "Role updated",
+ message
)
@Cog.listener()
@@ -336,9 +296,12 @@ class ModLog(Cog, name="ModLog"):
message = f"**{after.name}** (`{after.id}`)\n{message}"
- await self.send_log_message(
- Icons.guild_update, Colour.og_blurple(),
- "Guild updated", message,
+ await send_log_message(
+ self.bot,
+ Icons.guild_update,
+ Colour.og_blurple(),
+ "Guild updated",
+ message,
thumbnail=after.icon.with_static_format("png")
)
@@ -352,9 +315,12 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.member_ban].remove(member.id)
return
- await self.send_log_message(
- Icons.user_ban, Colours.soft_red,
- "User banned", format_user(member),
+ await send_log_message(
+ self.bot,
+ Icons.user_ban,
+ Colours.soft_red,
+ "User banned",
+ format_user(member),
thumbnail=member.display_avatar.url,
channel_id=Channels.user_log
)
@@ -373,9 +339,12 @@ class ModLog(Cog, name="ModLog"):
if difference.days < 1 and difference.months < 1 and difference.years < 1: # New user account!
message = f"{Emojis.new} {message}"
- await self.send_log_message(
- Icons.sign_in, Colours.soft_green,
- "User joined", message,
+ await send_log_message(
+ self.bot,
+ Icons.sign_in,
+ Colours.soft_green,
+ "User joined",
+ message,
thumbnail=member.display_avatar.url,
channel_id=Channels.user_log
)
@@ -390,9 +359,12 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.member_remove].remove(member.id)
return
- await self.send_log_message(
- Icons.sign_out, Colours.soft_red,
- "User left", format_user(member),
+ await send_log_message(
+ self.bot,
+ Icons.sign_out,
+ Colours.soft_red,
+ "User left",
+ format_user(member),
thumbnail=member.display_avatar.url,
channel_id=Channels.user_log
)
@@ -407,9 +379,12 @@ class ModLog(Cog, name="ModLog"):
self._ignored[Event.member_unban].remove(member.id)
return
- await self.send_log_message(
- Icons.user_unban, Colour.og_blurple(),
- "User unbanned", format_user(member),
+ await send_log_message(
+ self.bot,
+ Icons.user_unban,
+ Colour.og_blurple(),
+ "User unbanned",
+ format_user(member),
thumbnail=member.display_avatar.url,
channel_id=Channels.mod_log
)
@@ -471,7 +446,8 @@ class ModLog(Cog, name="ModLog"):
message = f"{format_user(after)}\n{message}"
- await self.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url=Icons.user_update,
colour=Colour.og_blurple(),
title="Member updated",
@@ -565,8 +541,10 @@ class ModLog(Cog, name="ModLog"):
response += f"{content}"
- await self.send_log_message(
- Icons.message_delete, Colours.soft_red,
+ await send_log_message(
+ self.bot,
+ Icons.message_delete,
+ Colours.soft_red,
"Message deleted",
response,
channel_id=Channels.message_log
@@ -606,8 +584,10 @@ class ModLog(Cog, name="ModLog"):
"This message was not cached, so the message content cannot be displayed."
)
- await self.send_log_message(
- Icons.message_delete, Colours.soft_red,
+ await send_log_message(
+ self.bot,
+ Icons.message_delete,
+ Colours.soft_red,
"Message deleted",
response,
channel_id=Channels.message_log
@@ -687,9 +667,15 @@ class ModLog(Cog, name="ModLog"):
timestamp = msg_before.created_at
footer = None
- await self.send_log_message(
- Icons.message_edit, Colour.og_blurple(), "Message edited", response,
- channel_id=Channels.message_log, timestamp_override=timestamp, footer=footer
+ await send_log_message(
+ self.bot,
+ Icons.message_edit,
+ Colour.og_blurple(),
+ "Message edited",
+ response,
+ channel_id=Channels.message_log,
+ timestamp_override=timestamp,
+ footer=footer
)
@Cog.listener()
@@ -700,9 +686,9 @@ class ModLog(Cog, name="ModLog"):
await self.bot.wait_until_guild_available()
try:
- channel = self.bot.get_channel(int(event.data["channel_id"]))
+ channel = await get_or_fetch_channel(self.bot, int(event.data["channel_id"]))
message = await channel.fetch_message(event.message_id)
- except discord.NotFound: # Was deleted before we got the event
+ except discord.NotFound: # Channel/message was deleted before we got the event
return
if self.is_message_blacklisted(message):
@@ -734,14 +720,22 @@ class ModLog(Cog, name="ModLog"):
f"{message.clean_content}"
)
- await self.send_log_message(
- Icons.message_edit, Colour.og_blurple(), "Message edited (Before)",
- before_response, channel_id=Channels.message_log
+ await send_log_message(
+ self.bot,
+ Icons.message_edit,
+ Colour.og_blurple(),
+ "Message edited (Before)",
+ before_response,
+ channel_id=Channels.message_log
)
- await self.send_log_message(
- Icons.message_edit, Colour.og_blurple(), "Message edited (After)",
- after_response, channel_id=Channels.message_log
+ await send_log_message(
+ self.bot,
+ Icons.message_edit,
+ Colour.og_blurple(),
+ "Message edited (After)",
+ after_response,
+ channel_id=Channels.message_log
)
@Cog.listener()
@@ -752,7 +746,8 @@ class ModLog(Cog, name="ModLog"):
return
if before.name != after.name:
- await self.send_log_message(
+ await send_log_message(
+ self.bot,
Icons.hash_blurple,
Colour.og_blurple(),
"Thread name edited",
@@ -774,7 +769,8 @@ class ModLog(Cog, name="ModLog"):
else:
return
- await self.send_log_message(
+ await send_log_message(
+ self.bot,
icon,
colour,
f"Thread {action}",
@@ -792,7 +788,8 @@ class ModLog(Cog, name="ModLog"):
log.trace("Ignoring deletion of thread %s (%d)", thread.mention, thread.id)
return
- await self.send_log_message(
+ await send_log_message(
+ self.bot,
Icons.hash_red,
Colours.soft_red,
"Thread deleted",
@@ -868,7 +865,8 @@ class ModLog(Cog, name="ModLog"):
message = "\n".join(f"{Emojis.bullet} {item}" for item in sorted(changes))
message = f"{format_user(member)}\n{message}"
- await self.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url=icon,
colour=colour,
title="Voice state updated",
diff --git a/bot/exts/moderation/modpings.py b/bot/exts/moderation/modpings.py
index ca05f2c25..002bc4cfe 100644
--- a/bot/exts/moderation/modpings.py
+++ b/bot/exts/moderation/modpings.py
@@ -6,13 +6,13 @@ from async_rediscache import RedisCache
from dateutil.parser import isoparse, parse as dateutil_parse
from discord import Member
from discord.ext.commands import Cog, Context, group, has_any_role
+from pydis_core.utils.members import get_or_fetch_member
from pydis_core.utils.scheduling import Scheduler
from bot.bot import Bot
from bot.constants import Emojis, Guild, MODERATION_ROLES, Roles
from bot.converters import Expiry
from bot.log import get_logger
-from bot.utils.members import get_or_fetch_member
from bot.utils.time import TimestampFormats, discord_timestamp
log = get_logger(__name__)
diff --git a/bot/exts/moderation/silence.py b/bot/exts/moderation/silence.py
index a7151aaa1..2852598ca 100644
--- a/bot/exts/moderation/silence.py
+++ b/bot/exts/moderation/silence.py
@@ -158,7 +158,7 @@ class Silence(commands.Cog):
async def silence(
self,
ctx: Context,
- duration_or_channel: TextOrVoiceChannel | HushDurationConverter = None, # noqa: RUF013
+ duration_or_channel: TextOrVoiceChannel | HushDurationConverter = None,
duration: HushDurationConverter = 10,
*,
kick: bool = False
@@ -269,7 +269,7 @@ class Silence(commands.Cog):
await self.unsilence_timestamps.set(channel.id, unsilence_time.timestamp())
@commands.command(aliases=("unhush",))
- async def unsilence(self, ctx: Context, *, channel: TextOrVoiceChannel = None) -> None: # noqa: RUF013
+ async def unsilence(self, ctx: Context, *, channel: TextOrVoiceChannel = None) -> None:
"""
Unsilence the given channel if given, else the current one.
diff --git a/bot/exts/moderation/stream.py b/bot/exts/moderation/stream.py
index ddfdd0a45..6ffae1e6e 100644
--- a/bot/exts/moderation/stream.py
+++ b/bot/exts/moderation/stream.py
@@ -7,16 +7,22 @@ from arrow import Arrow
from async_rediscache import RedisCache
from discord.ext import commands
from pydis_core.utils import scheduling
+from pydis_core.utils.members import get_or_fetch_member
from bot.bot import Bot
from bot.constants import (
- Colours, Emojis, Guild, MODERATION_ROLES, Roles, STAFF_PARTNERS_COMMUNITY_ROLES, VideoPermission
+ Colours,
+ Emojis,
+ Guild,
+ MODERATION_ROLES,
+ Roles,
+ STAFF_PARTNERS_COMMUNITY_ROLES,
+ VideoPermission,
)
from bot.converters import Expiry
from bot.log import get_logger
from bot.pagination import LinePaginator
from bot.utils import time
-from bot.utils.members import get_or_fetch_member
log = get_logger(__name__)
@@ -89,7 +95,7 @@ class Stream(commands.Cog):
self,
ctx: commands.Context,
member: discord.Member,
- duration: Expiry = None, # noqa: RUF013
+ duration: Expiry = None,
) -> None:
"""
Temporarily grant streaming permissions to a member for a given duration.
diff --git a/bot/exts/moderation/voice_gate.py b/bot/exts/moderation/voice_gate.py
index 50d3188bd..4244cce03 100644
--- a/bot/exts/moderation/voice_gate.py
+++ b/bot/exts/moderation/voice_gate.py
@@ -1,18 +1,15 @@
-import asyncio
-from contextlib import suppress
from datetime import timedelta
import arrow
import discord
from async_rediscache import RedisCache
-from discord import Colour, Member, VoiceState
-from discord.ext.commands import Cog, Context, command
+from discord import Colour, Member, TextChannel, VoiceState
+from discord.ext.commands import Cog, Context, command, has_any_role
from pydis_core.site_api import ResponseCodeError
+from pydis_core.utils.channel import get_or_fetch_channel
from bot.bot import Bot
-from bot.constants import Bot as BotConfig, Channels, MODERATION_ROLES, Roles, VoiceGate as GateConf
-from bot.decorators import has_no_roles, in_whitelist
-from bot.exts.moderation.modlog import ModLog
+from bot.constants import Channels, MODERATION_ROLES, Roles, VoiceGate as GateConf
from bot.log import get_logger
from bot.utils.checks import InWhitelistCheckFailure
@@ -37,137 +34,63 @@ MESSAGE_FIELD_MAP = {
VOICE_PING = (
"Wondering why you can't talk in the voice channels? "
- f"Use the `{BotConfig.prefix}voiceverify` command in here to verify. "
+ "Click the Voice Verify button above to verify. "
"If you don't yet qualify, you'll be told why!"
)
-VOICE_PING_DM = (
- "Wondering why you can't talk in the voice channels? "
- f"Use the `{BotConfig.prefix}voiceverify` command in "
- "{channel_mention} to verify. If you don't yet qualify, you'll be told why!"
-)
-
-class VoiceGate(Cog):
- """Voice channels verification management."""
-
- # RedisCache[discord.User.id | discord.Member.id, discord.Message.id | int]
- # The cache's keys are the IDs of members who are verified or have joined a voice channel
- # The cache's values are either the message ID of the ping message or 0 (NO_MSG) if no message is present
- redis_cache = RedisCache()
+class VoiceVerificationView(discord.ui.View):
+ """Persistent view to add a Voice Verify button."""
def __init__(self, bot: Bot) -> None:
+ super().__init__(timeout=None)
self.bot = bot
- @property
- def mod_log(self) -> ModLog:
- """Get the currently loaded ModLog cog instance."""
- return self.bot.get_cog("ModLog")
-
- @redis_cache.atomic_transaction # Fully process each call until starting the next
- async def _delete_ping(self, member_id: int) -> None:
- """
- If `redis_cache` holds a message ID for `member_id`, delete the message.
-
- If the message was deleted, the value under the `member_id` key is then set to `NO_MSG`.
- When `member_id` is not in the cache, or has a value of `NO_MSG` already, this function
- does nothing.
- """
- if message_id := await self.redis_cache.get(member_id):
- log.trace(f"Removing voice gate reminder message for user: {member_id}")
- with suppress(discord.NotFound):
- await self.bot.http.delete_message(Channels.voice_gate, message_id)
- await self.redis_cache.set(member_id, NO_MSG)
- else:
- log.trace(f"Voice gate reminder message for user {member_id} was already removed")
-
- @redis_cache.atomic_transaction
- async def _ping_newcomer(self, member: discord.Member) -> tuple:
- """
- See if `member` should be sent a voice verification notification, and send it if so.
-
- Returns (False, None) if the notification was not sent. This happens when:
- * The `member` has already received the notification
- * The `member` is already voice-verified
-
- Otherwise, the notification message ID is stored in `redis_cache` and return (True, channel).
- channel is either [discord.TextChannel, discord.DMChannel].
- """
- if await self.redis_cache.contains(member.id):
- log.trace("User already in cache. Ignore.")
- return False, None
-
- log.trace("User not in cache and is in a voice channel.")
- verified = any(Roles.voice_verified == role.id for role in member.roles)
- if verified:
- log.trace("User is verified, add to the cache and ignore.")
- await self.redis_cache.set(member.id, NO_MSG)
- return False, None
-
- log.trace("User is unverified. Send ping.")
-
- await self.bot.wait_until_guild_available()
- voice_verification_channel = self.bot.get_channel(Channels.voice_gate)
-
- try:
- message = await member.send(VOICE_PING_DM.format(channel_mention=voice_verification_channel.mention))
- except discord.Forbidden:
- log.trace("DM failed for Voice ping message. Sending in channel.")
- message = await voice_verification_channel.send(f"Hello, {member.mention}! {VOICE_PING}")
-
- await self.redis_cache.set(member.id, message.id)
- return True, message.channel
-
- @command(aliases=("voiceverify", "voice-verify",))
- @has_no_roles(Roles.voice_verified)
- @in_whitelist(channels=(Channels.voice_gate,), redirect=None)
- async def voice_verify(self, ctx: Context, *_) -> None:
- """
- Apply to be able to use voice within the Discord server.
-
- In order to use voice you must meet all three of the following criteria:
- - You must have over a certain number of messages within the Discord server
- - You must have accepted our rules over a certain number of days ago
- - You must not be actively banned from using our voice channels
- - You must have been active for over a certain number of 10-minute blocks
- """
- await self._delete_ping(ctx.author.id) # If user has received a ping in voice_verification, delete the message
+ @discord.ui.button(label="Voice Verify", style=discord.ButtonStyle.primary, custom_id="voice_verify_button",)
+ async def voice_button(self, interaction: discord.Interaction, button: discord.ui.Button) -> None:
+ """A button that checks to see if the user qualifies for voice verification and verifies them if they do."""
+ if interaction.user.get_role(Roles.voice_verified):
+ await interaction.response.send_message((
+ "You have already verified! "
+ "If you have received this message in error, "
+ "please send a message to the ModMail bot."),
+ ephemeral=True,
+ delete_after=GateConf.delete_after_delay,
+ )
+ return
try:
- data = await self.bot.api_client.get(f"bot/users/{ctx.author.id}/metricity_data")
- except ResponseCodeError as e:
- if e.status == 404:
- embed = discord.Embed(
- title="Not found",
- description=(
- "We were unable to find user data for you. "
- "Please try again shortly, "
- "if this problem persists please contact the server staff through Modmail."
- ),
- color=Colour.red()
+ data = await self.bot.api_client.get(
+ f"bot/users/{interaction.user.id}/metricity_data",
+ raise_for_status=True
+ )
+ except ResponseCodeError as err:
+ if err.response.status == 404:
+ await interaction.response.send_message((
+ "We were unable to find user data for you. "
+ "Please try again shortly. "
+ "If this problem persists, please contact the server staff through ModMail."),
+ ephemeral=True,
+ delete_after=GateConf.delete_after_delay,
)
- log.info(f"Unable to find Metricity data about {ctx.author} ({ctx.author.id})")
+ log.info("Unable to find Metricity data about %s (%s)", interaction.user, interaction.user.id)
else:
- embed = discord.Embed(
- title="Unexpected response",
- description=(
- "We encountered an error while attempting to find data for your user. "
- "Please try again and let us know if the problem persists."
- ),
- color=Colour.red()
+ await interaction.response.send_message((
+ "We encountered an error while attempting to find data for your user. "
+ "Please try again and let us know if the problem persists."),
+ ephemeral=True,
+ delete_after=GateConf.delete_after_delay,
+ )
+ log.warning(
+ "Got response code %s while trying to get %s Metricity data.",
+ err.status,
+ interaction.user.id
)
- log.warning(f"Got response code {e.status} while trying to get {ctx.author.id} Metricity data.")
- try:
- await ctx.author.send(embed=embed)
- except discord.Forbidden:
- log.info("Could not send user DM. Sending in voice-verify channel and scheduling delete.")
- await ctx.send(embed=embed)
-
return
checks = {
"joined_at": (
- ctx.author.joined_at > arrow.utcnow() - timedelta(days=GateConf.minimum_days_member)
+ interaction.user.joined_at > arrow.utcnow() - timedelta(days=GateConf.minimum_days_member)
),
"total_messages": data["total_messages"] < GateConf.minimum_messages,
"voice_gate_blocked": data["voice_gate_blocked"],
@@ -175,102 +98,121 @@ class VoiceGate(Cog):
}
failed = any(checks.values())
- failed_reasons = [MESSAGE_FIELD_MAP[key] for key, value in checks.items() if value is True]
- [self.bot.stats.incr(f"voice_gate.failed.{key}") for key, value in checks.items() if value is True]
if failed:
+ failed_reasons = []
+ for key, value in checks.items():
+ if value is True:
+ failed_reasons.append(MESSAGE_FIELD_MAP[key])
+ self.bot.stats.incr(f"voice_gate.failed.{key}")
+
embed = discord.Embed(
title="Voice Gate failed",
- description=FAILED_MESSAGE.format(reasons="\n".join(f"• You {reason}." for reason in failed_reasons)),
+ description=FAILED_MESSAGE.format(reasons="\n".join(f"- You {reason}." for reason in failed_reasons)),
color=Colour.red()
)
- try:
- await ctx.author.send(embed=embed)
- await ctx.send(f"{ctx.author}, please check your DMs.")
- except discord.Forbidden:
- await ctx.channel.send(ctx.author.mention, embed=embed)
+
+ await interaction.response.send_message(
+ embed=embed,
+ ephemeral=True,
+ delete_after=GateConf.delete_after_delay,
+ )
return
embed = discord.Embed(
title="Voice gate passed",
description="You have been granted permission to use voice channels in Python Discord.",
- color=Colour.green()
+ color=Colour.green(),
)
- if ctx.author.voice:
+ # interaction.user.voice will return None if the user is not in a voice channel
+ if interaction.user.voice:
embed.description += "\n\nPlease reconnect to your voice channel to be granted your new permissions."
- try:
- await ctx.author.send(embed=embed)
- await ctx.send(f"{ctx.author}, please check your DMs.")
- except discord.Forbidden:
- await ctx.channel.send(ctx.author.mention, embed=embed)
+ await interaction.response.send_message(
+ embed=embed,
+ ephemeral=True,
+ delete_after=GateConf.delete_after_delay,
+ )
+ await interaction.user.add_roles(discord.Object(Roles.voice_verified), reason="Voice Gate passed")
+ self.bot.stats.incr("voice_gate.passed")
- # wait a little bit so those who don't get DMs see the response in-channel before losing perms to see it.
- await asyncio.sleep(3)
- await ctx.author.add_roles(discord.Object(Roles.voice_verified), reason="Voice Gate passed")
- self.bot.stats.incr("voice_gate.passed")
+class VoiceGate(Cog):
+ """Voice channels verification management."""
- @Cog.listener()
- async def on_message(self, message: discord.Message) -> None:
- """Delete all non-staff messages from voice gate channel that don't invoke voice verify command."""
- # Check is channel voice gate
- if message.channel.id != Channels.voice_gate:
- return
+ # RedisCache[discord.User.id | discord.Member.id, discord.Message.id | int]
+ # The cache's keys are the IDs of members who are verified or have joined a voice channel
+ # The cache's values are set to 0, as we only need to track which users have connected before
+ redis_cache = RedisCache()
- ctx = await self.bot.get_context(message)
- is_verify_command = ctx.command is not None and ctx.command.name == "voice_verify"
-
- # When it's a bot sent message, delete it after some time
- if message.author.bot:
- # Comparing the message with the voice ping constant
- if message.content.endswith(VOICE_PING):
- log.trace("Message is the voice verification ping. Ignore.")
- return
- with suppress(discord.NotFound):
- await message.delete(delay=GateConf.bot_message_delete_delay)
- return
-
- # Then check is member moderator+, because we don't want to delete their messages.
- if any(role.id in MODERATION_ROLES for role in message.author.roles) and is_verify_command is False:
- log.trace(f"Excluding moderator message {message.id} from deletion in #{message.channel}.")
+ def __init__(self, bot: Bot) -> None:
+ self.bot = bot
+
+ async def cog_load(self) -> None:
+ """Adds verify button to be monitored by the bot."""
+ self.bot.add_view(VoiceVerificationView(self.bot))
+
+ @redis_cache.atomic_transaction
+ async def _ping_newcomer(self, member: discord.Member) -> None:
+ """See if `member` should be sent a voice verification notification, and send it if so."""
+ log.trace("User is not verified. Checking cache.")
+ if await self.redis_cache.contains(member.id):
+ log.trace("User %s already in cache. Ignore.", member.id)
return
- with suppress(discord.NotFound):
- await message.delete()
+ log.trace("User %s is unverified and has not been pinged before. Sending ping.", member.id)
+ await self.bot.wait_until_guild_available()
+ voice_verification_channel = await get_or_fetch_channel(self.bot, Channels.voice_gate)
+
+ await voice_verification_channel.send(
+ f"Hello, {member.mention}! {VOICE_PING}",
+ delete_after=GateConf.delete_after_delay,
+ )
+
+ await self.redis_cache.set(member.id, NO_MSG)
+ log.trace("User %s added to cache to not be pinged again.", member.id)
@Cog.listener()
async def on_voice_state_update(self, member: Member, before: VoiceState, after: VoiceState) -> None:
"""Pings a user if they've never joined the voice chat before and aren't voice verified."""
if member.bot:
- log.trace("User is a bot. Ignore.")
+ log.trace("User %s is a bot. Ignore.", member.id)
+ return
+
+ if member.get_role(Roles.voice_verified):
+ log.trace("User %s already verified. Ignore", member.id)
return
# member.voice will return None if the user is not in a voice channel
if member.voice is None:
- log.trace("User not in a voice channel. Ignore.")
+ log.trace("User %s not in a voice channel. Ignore.", member.id)
return
if isinstance(after.channel, discord.StageChannel):
- log.trace("User joined a stage channel. Ignore.")
+ log.trace("User %s joined a stage channel. Ignore.", member.id)
return
# To avoid race conditions, checking if the user should receive a notification
# and sending it if appropriate is delegated to an atomic helper
- notification_sent, message_channel = await self._ping_newcomer(member)
-
- # Schedule the channel ping notification to be deleted after the configured delay, which is
- # again delegated to an atomic helper
- if notification_sent and isinstance(message_channel, discord.TextChannel):
- await asyncio.sleep(GateConf.voice_ping_delete_delay)
- await self._delete_ping(member.id)
+ await self._ping_newcomer(member)
async def cog_command_error(self, ctx: Context, error: Exception) -> None:
"""Check for & ignore any InWhitelistCheckFailure."""
if isinstance(error, InWhitelistCheckFailure):
error.handled = True
+ @command(name="prepare_voice")
+ @has_any_role(*MODERATION_ROLES)
+ async def prepare_voice_button(self, ctx: Context, channel: TextChannel | None, *, text: str) -> None:
+ """Sends a message that includes the Voice Verify button. Should only need to be run once."""
+ if channel is None:
+ await ctx.send(text, view=VoiceVerificationView(self.bot))
+ elif not channel.permissions_for(ctx.author).send_messages:
+ await ctx.send("You don't have permission to send messages to that channel.")
+ else:
+ await channel.send(text, view=VoiceVerificationView(self.bot))
+
async def setup(bot: Bot) -> None:
"""Loads the VoiceGate cog."""
diff --git a/bot/exts/moderation/watchchannels/_watchchannel.py b/bot/exts/moderation/watchchannels/_watchchannel.py
index c3f823982..71600e9df 100644
--- a/bot/exts/moderation/watchchannels/_watchchannel.py
+++ b/bot/exts/moderation/watchchannels/_watchchannel.py
@@ -11,16 +11,18 @@ from discord import Color, DMChannel, Embed, HTTPException, Message, errors
from discord.ext.commands import Cog, Context
from pydis_core.site_api import ResponseCodeError
from pydis_core.utils import scheduling
+from pydis_core.utils.channel import get_or_fetch_channel
+from pydis_core.utils.logging import CustomLogger
+from pydis_core.utils.members import get_or_fetch_member
from bot.bot import Bot
from bot.constants import BigBrother as BigBrotherConfig, Guild as GuildConfig, Icons
from bot.exts.filtering._filters.unique.discord_token import DiscordTokenFilter
from bot.exts.filtering._filters.unique.webhook import WEBHOOK_URL_RE
-from bot.exts.moderation.modlog import ModLog
-from bot.log import CustomLogger, get_logger
+from bot.log import get_logger
from bot.pagination import LinePaginator
from bot.utils import CogABCMeta, messages, time
-from bot.utils.members import get_or_fetch_member
+from bot.utils.modlog import send_log_message
log = get_logger(__name__)
@@ -71,11 +73,6 @@ class WatchChannel(metaclass=CogABCMeta):
self.disable_header = disable_header
@property
- def modlog(self) -> ModLog:
- """Provides access to the ModLog cog for alert purposes."""
- return self.bot.get_cog("ModLog")
-
- @property
def consuming_messages(self) -> bool:
"""Checks if a consumption task is currently running."""
if self._consume_task is None:
@@ -97,7 +94,7 @@ class WatchChannel(metaclass=CogABCMeta):
await self.bot.wait_until_guild_available()
try:
- self.channel = await self.bot.fetch_channel(self.destination)
+ self.channel = await get_or_fetch_channel(self.bot, self.destination)
except HTTPException:
self.log.exception(f"Failed to retrieve the text channel with id `{self.destination}`")
@@ -120,7 +117,8 @@ class WatchChannel(metaclass=CogABCMeta):
"""
)
- await self.modlog.send_log_message(
+ await send_log_message(
+ self.bot,
title=f"Error: Failed to initialize the {self.__class__.__name__} watch channel",
text=message,
ping_everyone=True,
@@ -132,7 +130,8 @@ class WatchChannel(metaclass=CogABCMeta):
return
if not await self.fetch_user_cache():
- await self.modlog.send_log_message(
+ await send_log_message(
+ self.bot,
title=f"Warning: Failed to retrieve user cache for the {self.__class__.__name__} watch channel",
text=(
"Could not retrieve the list of watched users from the API. "
@@ -168,7 +167,7 @@ class WatchChannel(metaclass=CogABCMeta):
"""Queues up messages sent by watched users."""
if msg.author.id in self.watched_users:
if not self.consuming_messages:
- self._consume_task = scheduling.create_task(self.consume_messages(), event_loop=self.bot.loop)
+ self._consume_task = scheduling.create_task(self.consume_messages())
self.log.trace(f"Received message: {msg.content} ({len(msg.attachments)} attachments)")
self.message_queue[msg.author.id][msg.channel.id].append(msg)
@@ -198,10 +197,7 @@ class WatchChannel(metaclass=CogABCMeta):
if self.message_queue:
self.log.trace("Channel queue not empty: Continuing consuming queues")
- self._consume_task = scheduling.create_task(
- self.consume_messages(delay_consumption=False),
- event_loop=self.bot.loop,
- )
+ self._consume_task = scheduling.create_task(self.consume_messages(delay_consumption=False))
else:
self.log.trace("Done consuming messages.")
@@ -354,7 +350,7 @@ class WatchChannel(metaclass=CogABCMeta):
list_data["info"] = {}
for user_id, user_data in watched_iter:
member = await get_or_fetch_member(ctx.guild, user_id)
- line = f"• `{user_id}`"
+ line = f"- `{user_id}`"
if member:
line += f" ({member.name}#{member.discriminator})"
inserted_at = user_data["inserted_at"]
diff --git a/bot/exts/recruitment/talentpool/_api.py b/bot/exts/recruitment/talentpool/_api.py
index e12111de5..f7b243209 100644
--- a/bot/exts/recruitment/talentpool/_api.py
+++ b/bot/exts/recruitment/talentpool/_api.py
@@ -1,6 +1,6 @@
from datetime import datetime
-from pydantic import BaseModel, Field, parse_obj_as
+from pydantic import BaseModel, Field, TypeAdapter
from pydis_core.site_api import APIClient
@@ -50,13 +50,13 @@ class NominationAPI:
params["user__id"] = str(user_id)
data = await self.site_api.get("bot/nominations", params=params)
- nominations = parse_obj_as(list[Nomination], data)
+ nominations = TypeAdapter(list[Nomination]).validate_python(data)
return nominations
async def get_nomination(self, nomination_id: int) -> Nomination:
"""Fetch a nomination by ID."""
data = await self.site_api.get(f"bot/nominations/{nomination_id}")
- nomination = Nomination.parse_obj(data)
+ nomination = Nomination.model_validate(data)
return nomination
async def edit_nomination(
@@ -84,7 +84,7 @@ class NominationAPI:
data["thread_id"] = thread_id
result = await self.site_api.patch(f"bot/nominations/{nomination_id}", json=data)
- return Nomination.parse_obj(result)
+ return Nomination.model_validate(result)
async def edit_nomination_entry(
self,
@@ -96,7 +96,7 @@ class NominationAPI:
"""Edit a nomination entry."""
data = {"actor": actor_id, "reason": reason}
result = await self.site_api.patch(f"bot/nominations/{nomination_id}", json=data)
- return Nomination.parse_obj(result)
+ return Nomination.model_validate(result)
async def post_nomination(
self,
@@ -111,7 +111,7 @@ class NominationAPI:
"user": user_id,
}
result = await self.site_api.post("bot/nominations", json=data)
- return Nomination.parse_obj(result)
+ return Nomination.model_validate(result)
async def get_activity(
self,
diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py
index 547de4cc9..78084899f 100644
--- a/bot/exts/recruitment/talentpool/_cog.py
+++ b/bot/exts/recruitment/talentpool/_cog.py
@@ -9,6 +9,8 @@ from discord import Color, Embed, Member, PartialMessage, RawReactionActionEvent
from discord.ext import commands, tasks
from discord.ext.commands import BadArgument, Cog, Context, group, has_any_role
from pydis_core.site_api import ResponseCodeError
+from pydis_core.utils.channel import get_or_fetch_channel
+from pydis_core.utils.members import get_or_fetch_member
from bot.bot import Bot
from bot.constants import Bot as BotConfig, Channels, Emojis, Guild, MODERATION_ROLES, Roles, STAFF_ROLES
@@ -18,8 +20,6 @@ from bot.exts.recruitment.talentpool._review import Reviewer
from bot.log import get_logger
from bot.pagination import LinePaginator
from bot.utils import time
-from bot.utils.channel import get_or_fetch_channel
-from bot.utils.members import get_or_fetch_member
AUTOREVIEW_ENABLED_KEY = "autoreview_enabled"
REASON_MAX_CHARS = 1000
@@ -146,7 +146,7 @@ class TalentPool(Cog, name="Talentpool"):
days=DAYS_UNTIL_INACTIVE
)
- nomination_discussion = await get_or_fetch_channel(Channels.nomination_discussion)
+ nomination_discussion = await get_or_fetch_channel(self.bot, Channels.nomination_discussion)
for nomination in nominations:
if messages_per_user[nomination.user_id] > 0:
continue
@@ -279,7 +279,7 @@ class TalentPool(Cog, name="Talentpool"):
return ["*None*"]
for nomination in nominations:
- line = f"• `{nomination.user_id}`"
+ line = f"- `{nomination.user_id}`"
member = await get_or_fetch_member(ctx.guild, nomination.user_id)
if member:
@@ -553,7 +553,7 @@ class TalentPool(Cog, name="Talentpool"):
async def on_member_ban(self, guild: Guild, user: MemberOrUser) -> None:
"""Remove `user` from the talent pool after they are banned."""
if await self.end_nomination(user.id, "Automatic removal: User was banned"):
- nomination_discussion = await get_or_fetch_channel(Channels.nomination_discussion)
+ nomination_discussion = await get_or_fetch_channel(self.bot, Channels.nomination_discussion)
await nomination_discussion.send(
f":warning: <@{user.id}> ({user.id})"
" was removed from the talentpool due to being banned."
@@ -614,7 +614,7 @@ class TalentPool(Cog, name="Talentpool"):
if nomination.thread_id:
try:
- thread = await get_or_fetch_channel(nomination.thread_id)
+ thread = await get_or_fetch_channel(self.bot, nomination.thread_id)
except discord.HTTPException:
thread_jump_url = "*Not found*"
else:
diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py
index 241656853..fc48809c0 100644
--- a/bot/exts/recruitment/talentpool/_review.py
+++ b/bot/exts/recruitment/talentpool/_review.py
@@ -11,14 +11,14 @@ import discord
from async_rediscache import RedisCache
from discord import Embed, Emoji, Member, Message, NotFound, PartialMessage, TextChannel
from pydis_core.site_api import ResponseCodeError
+from pydis_core.utils.channel import get_or_fetch_channel
+from pydis_core.utils.members import get_or_fetch_member
from bot.bot import Bot
from bot.constants import Channels, Colours, Emojis, Guild, Roles
from bot.exts.recruitment.talentpool._api import Nomination, NominationAPI
from bot.log import get_logger
from bot.utils import time
-from bot.utils.channel import get_or_fetch_channel
-from bot.utils.members import get_or_fetch_member
from bot.utils.messages import count_unique_users_reaction
if typing.TYPE_CHECKING:
@@ -502,7 +502,7 @@ class Reviewer:
if nomination.thread_id is None:
continue
try:
- thread = await get_or_fetch_channel(nomination.thread_id)
+ thread = await get_or_fetch_channel(self.bot, nomination.thread_id)
except discord.HTTPException:
# Nothing to do here
pass
diff --git a/bot/exts/utils/internal.py b/bot/exts/utils/internal.py
index cdaa9a61b..ea27a5f50 100644
--- a/bot/exts/utils/internal.py
+++ b/bot/exts/utils/internal.py
@@ -11,12 +11,12 @@ from typing import Any
import arrow
import discord
from discord.ext.commands import Cog, Context, group, has_any_role, is_owner
+from pydis_core.utils.paste_service import PasteFile, PasteTooLongError, PasteUploadError, send_to_paste_service
from bot.bot import Bot
-from bot.constants import DEBUG_MODE, Roles
+from bot.constants import BaseURLs, DEBUG_MODE, Roles
from bot.log import get_logger
-from bot.utils import find_nth_occurrence, send_to_paste_service
-from bot.utils.services import PasteTooLongError, PasteUploadError
+from bot.utils import find_nth_occurrence
log = get_logger(__name__)
@@ -195,14 +195,19 @@ async def func(): # (None,) -> Any
truncate_index = newline_truncate_index
if len(out) > truncate_index:
+ file = PasteFile(content=out)
try:
- paste_link = await send_to_paste_service(out, extension="py")
+ resp = await send_to_paste_service(
+ files=[file],
+ http_session=self.bot.http_session,
+ paste_url=BaseURLs.paste_url,
+ )
except PasteTooLongError:
paste_text = "too long to upload to paste service."
except PasteUploadError:
paste_text = "failed to upload contents to paste service."
else:
- paste_text = f"full contents at {paste_link}"
+ paste_text = f"full contents at {resp.link}"
await ctx.send(
f"```py\n{out[:truncate_index]}\n```"
diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py
index b6dae0b51..c79e0499c 100644
--- a/bot/exts/utils/reminders.py
+++ b/bot/exts/utils/reminders.py
@@ -6,14 +6,23 @@ from operator import itemgetter
import discord
from dateutil.parser import isoparse
+from discord import Interaction
from discord.ext.commands import Cog, Context, Greedy, group
from pydis_core.site_api import ResponseCodeError
from pydis_core.utils import scheduling
+from pydis_core.utils.members import get_or_fetch_member
from pydis_core.utils.scheduling import Scheduler
from bot.bot import Bot
from bot.constants import (
- Channels, Guild, Icons, MODERATION_ROLES, NEGATIVE_REPLIES, POSITIVE_REPLIES, Roles, STAFF_PARTNERS_COMMUNITY_ROLES
+ Channels,
+ Guild,
+ Icons,
+ MODERATION_ROLES,
+ NEGATIVE_REPLIES,
+ POSITIVE_REPLIES,
+ Roles,
+ STAFF_PARTNERS_COMMUNITY_ROLES,
)
from bot.converters import Duration, UnambiguousUser
from bot.errors import LockedResourceError
@@ -22,7 +31,6 @@ from bot.pagination import LinePaginator
from bot.utils import time
from bot.utils.checks import has_any_role_check, has_no_roles_check
from bot.utils.lock import lock_arg
-from bot.utils.members import get_or_fetch_member
from bot.utils.messages import send_denial
log = get_logger(__name__)
@@ -30,11 +38,43 @@ log = get_logger(__name__)
LOCK_NAMESPACE = "reminder"
WHITELISTED_CHANNELS = Guild.reminder_whitelist
MAXIMUM_REMINDERS = 5
+REMINDER_EDIT_CONFIRMATION_TIMEOUT = 60
Mentionable = discord.Member | discord.Role
ReminderMention = UnambiguousUser | discord.Role
+class ModifyReminderConfirmationView(discord.ui.View):
+ """A view to confirm modifying someone else's reminder by admins."""
+
+ def __init__(self, author: discord.Member):
+ super().__init__(timeout=REMINDER_EDIT_CONFIRMATION_TIMEOUT)
+ self.author = author
+ self.result: bool | None = None
+
+ async def interaction_check(self, interaction: Interaction) -> bool:
+ """Only allow interactions from the command invoker."""
+ return interaction.user.id == self.author.id
+
+ async def on_timeout(self) -> None:
+ """Default to not modifying if the user doesn't respond."""
+ self.result = False
+
+ @discord.ui.button(label="Confirm", style=discord.ButtonStyle.blurple, row=0)
+ async def confirm(self, interaction: Interaction, button: discord.ui.Button) -> None:
+ """Confirm the reminder modification."""
+ await interaction.response.edit_message(view=None)
+ self.result = True
+ self.stop()
+
+ @discord.ui.button(label="Cancel", row=0)
+ async def cancel(self, interaction: Interaction, button: discord.ui.Button) -> None:
+ """Cancel the reminder modification."""
+ await interaction.response.edit_message(view=None)
+ self.result = False
+ self.stop()
+
+
class Reminders(Cog):
"""Provide in-channel reminder functionality."""
@@ -419,7 +459,10 @@ class Reminders(Cog):
For example, to edit a reminder to expire in 3 days and 1 minute, you can do `!remind edit duration 1234 3d1M`.
"""
- await self.edit_reminder(ctx, id_, {"expiration": expiration.isoformat()})
+ formatted_time = time.discord_timestamp(expiration, time.TimestampFormats.DAY_TIME)
+ message = f"It will arrive on {formatted_time}."
+
+ await self.edit_reminder(ctx, id_, {"expiration": expiration.isoformat()}, message)
@edit_reminder_group.command(name="content", aliases=("reason",))
async def edit_reminder_content(self, ctx: Context, id_: int, *, content: str | None = None) -> None:
@@ -450,7 +493,7 @@ class Reminders(Cog):
await self.edit_reminder(ctx, id_, {"mentions": mention_ids})
@lock_arg(LOCK_NAMESPACE, "id_", raise_error=True)
- async def edit_reminder(self, ctx: Context, id_: int, payload: dict) -> None:
+ async def edit_reminder(self, ctx: Context, id_: int, payload: dict, message: str = "") -> None:
"""Edits a reminder with the given payload, then sends a confirmation message."""
if not await self._can_modify(ctx, id_):
return
@@ -459,7 +502,7 @@ class Reminders(Cog):
# Send a confirmation message to the channel
await self._send_confirmation(
ctx,
- on_success="That reminder has been edited successfully!",
+ on_success=" ".join(("That reminder has been edited successfully!", message)).rstrip(),
reminder_id=id_,
)
await self._reschedule_reminder(reminder)
@@ -520,7 +563,7 @@ class Reminders(Cog):
"""
Check whether the reminder can be modified by the ctx author.
- The check passes when the user is an admin, or if they created the reminder.
+ The check passes if the user created the reminder, or if they are an admin (with confirmation).
"""
try:
api_response = await self.bot.api_client.get(f"bot/reminders/{reminder_id}")
@@ -530,18 +573,41 @@ class Reminders(Cog):
if e.status == 404:
return False
raise e
+ owner_id = api_response["author"]
- if await has_any_role_check(ctx, Roles.admins):
+ if owner_id == ctx.author.id:
+ log.debug(f"{ctx.author} is the reminder's author and passes the check.")
return True
- if api_response["author"] != ctx.author.id:
- log.debug(f"{ctx.author} is not the reminder author and does not pass the check.")
- if send_on_denial:
- await send_denial(ctx, "You can't modify reminders of other users!")
- return False
+ if await has_any_role_check(ctx, Roles.admins):
+ log.debug(f"{ctx.author} is an admin, asking for confirmation to modify someone else's.")
- log.debug(f"{ctx.author} is the reminder author and passes the check.")
- return True
+ if ctx.command == self.delete_reminder:
+ modify_action = "delete"
+ else:
+ modify_action = "edit"
+
+ confirmation_view = ModifyReminderConfirmationView(ctx.author)
+ confirmation_message = await ctx.reply(
+ f"Are you sure you want to {modify_action} <@{owner_id}>'s reminder?",
+ view=confirmation_view,
+ )
+ view_timed_out = await confirmation_view.wait()
+ # We don't have access to the message in `on_timeout` so we have to delete the view here
+ if view_timed_out:
+ await confirmation_message.edit(view=None)
+
+ if confirmation_view.result:
+ log.debug(f"{ctx.author} has confirmed reminder modification.")
+ else:
+ await ctx.send("🚫 Operation canceled.")
+ log.debug(f"{ctx.author} has cancelled reminder modification.")
+ return confirmation_view.result
+
+ log.debug(f"{ctx.author} is not the reminder's author and thus does not pass the check.")
+ if send_on_denial:
+ await send_denial(ctx, "You can't modify reminders of other users!")
+ return False
async def setup(bot: Bot) -> None:
diff --git a/bot/exts/utils/snekbox/_cog.py b/bot/exts/utils/snekbox/_cog.py
index 9d0ced4ef..db4181d68 100644
--- a/bot/exts/utils/snekbox/_cog.py
+++ b/bot/exts/utils/snekbox/_cog.py
@@ -1,6 +1,5 @@
from __future__ import annotations
-import asyncio
import contextlib
import re
from functools import partial
@@ -10,20 +9,19 @@ from typing import Literal, NamedTuple, 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
-from pydis_core.utils import interactions
+from pydis_core.utils import interactions, paste_service
+from pydis_core.utils.paste_service import PasteFile, send_to_paste_service
from pydis_core.utils.regex import FORMATTED_CODE_REGEX, RAW_CODE_REGEX
from bot.bot import Bot
-from bot.constants import Channels, Emojis, MODERATION_ROLES, Roles, URLs
+from bot.constants import BaseURLs, Channels, Emojis, MODERATION_ROLES, Roles, URLs
from bot.decorators import redirect_output
from bot.exts.filtering._filter_lists.extension 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.filtering.filtering import Filtering
@@ -74,7 +72,6 @@ if not hasattr(sys, "_setup_finished"):
{setup}
"""
-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
@@ -88,7 +85,8 @@ SNEKBOX_ROLES = (Roles.helpers, Roles.moderators, Roles.admins, Roles.owners, Ro
REDO_EMOJI = "\U0001f501" # :repeat:
REDO_TIMEOUT = 30
-SupportedPythonVersions = Literal["3.11"]
+SupportedPythonVersions = Literal["3.12"]
+
class FilteredFiles(NamedTuple):
allowed: list[FileAttachment]
@@ -205,16 +203,21 @@ class Snekbox(Cog):
async with self.bot.http_session.post(URLs.snekbox_eval_api, json=data, raise_for_status=True) as resp:
return EvalResult.from_dict(await resp.json())
- @staticmethod
- async def upload_output(output: str) -> str | None:
+ async def upload_output(self, output: str) -> str | None:
"""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...")
+ file = PasteFile(content=output, lexer="text")
try:
- return await send_to_paste_service(output, extension="txt", max_length=MAX_PASTE_LENGTH)
- except PasteTooLongError:
+ paste_response = await send_to_paste_service(
+ files=[file],
+ http_session=self.bot.http_session,
+ paste_url=BaseURLs.paste_url,
+ )
+ return paste_response.link
+ except paste_service.PasteTooLongError:
return "too long to upload"
- except PasteUploadError:
+ except paste_service.PasteUploadError:
return "unable to upload"
@staticmethod
@@ -456,7 +459,7 @@ class Snekbox(Cog):
if code is None:
return None
- except asyncio.TimeoutError:
+ except TimeoutError:
with contextlib.suppress(HTTPException):
await ctx.message.clear_reaction(REDO_EMOJI)
return None
@@ -558,13 +561,13 @@ class Snekbox(Cog):
If multiple codeblocks are in a message, all of them will be joined and evaluated,
ignoring the text outside them.
- By default, your code is run on Python 3.11. A `python_version` arg of `3.10` can also be specified.
+ Currently only 3.12 version is supported.
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"
+ python_version = python_version or "3.12"
job = EvalJob.from_code("\n".join(code)).as_version(python_version)
await self.run_job(ctx, job)
@@ -594,13 +597,13 @@ 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 3.11. A `python_version` arg of `3.10` can also be specified.
+ Currently only 3.12 version is supported.
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"
+ python_version = python_version or "3.12"
args = self.prepare_timeit_input(code)
job = EvalJob(args, version=python_version, name="timeit")
diff --git a/bot/exts/utils/snekbox/_eval.py b/bot/exts/utils/snekbox/_eval.py
index 9eb8039ce..d3d1e7a18 100644
--- a/bot/exts/utils/snekbox/_eval.py
+++ b/bot/exts/utils/snekbox/_eval.py
@@ -26,7 +26,7 @@ class EvalJob:
args: list[str]
files: list[FileAttachment] = field(default_factory=list)
name: str = "eval"
- version: SupportedPythonVersions = "3.11"
+ version: SupportedPythonVersions = "3.12"
@classmethod
def from_code(cls, code: str, path: str = "main.py") -> EvalJob:
diff --git a/bot/exts/utils/thread_bumper.py b/bot/exts/utils/thread_bumper.py
index e6c47c1ba..e93301e0c 100644
--- a/bot/exts/utils/thread_bumper.py
+++ b/bot/exts/utils/thread_bumper.py
@@ -2,12 +2,12 @@
import discord
from discord.ext import commands
from pydis_core.site_api import ResponseCodeError
+from pydis_core.utils.channel import get_or_fetch_channel
from bot import constants
from bot.bot import Bot
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import channel
log = get_logger(__name__)
THREAD_BUMP_ENDPOINT = "bot/bumped-threads"
@@ -69,7 +69,7 @@ class ThreadBumper(commands.Cog):
threads_to_maybe_bump = []
for thread_id in await self.bot.api_client.get(THREAD_BUMP_ENDPOINT):
try:
- thread = await channel.get_or_fetch_channel(thread_id)
+ thread = await get_or_fetch_channel(self.bot, thread_id)
except discord.NotFound:
log.info("Thread %d has been deleted, removing from bumped threads.", thread_id)
await self.bot.api_client.delete(f"{THREAD_BUMP_ENDPOINT}/{thread_id}")
diff --git a/bot/log.py b/bot/log.py
index 8b18df70a..068c77189 100644
--- a/bot/log.py
+++ b/bot/log.py
@@ -1,63 +1,29 @@
import logging
import os
import sys
-from logging import Logger, handlers
+from logging import handlers
from pathlib import Path
-from typing import TYPE_CHECKING, cast
import coloredlogs
import sentry_sdk
+from pydis_core.utils import logging as core_logging
from sentry_sdk.integrations.logging import LoggingIntegration
from sentry_sdk.integrations.redis import RedisIntegration
from bot import constants
-TRACE_LEVEL = 5
-
-
-if TYPE_CHECKING:
- LoggerClass = Logger
-else:
- LoggerClass = logging.getLoggerClass()
-
-
-class CustomLogger(LoggerClass):
- """Custom implementation of the `Logger` class with an added `trace` method."""
-
- def trace(self, msg: str, *args, **kwargs) -> None:
- """
- Log 'msg % args' with severity 'TRACE'.
-
- To pass exception information, use the keyword argument exc_info with
- a true value, e.g.
-
- logger.trace("Houston, we have an %s", "interesting problem", exc_info=1)
- """
- if self.isEnabledFor(TRACE_LEVEL):
- self.log(TRACE_LEVEL, msg, *args, **kwargs)
-
-
-def get_logger(name: str | None = None) -> CustomLogger:
- """Utility to make mypy recognise that logger is of type `CustomLogger`."""
- return cast(CustomLogger, logging.getLogger(name))
+get_logger = core_logging.get_logger
def setup() -> None:
"""Set up loggers."""
- logging.TRACE = TRACE_LEVEL
- logging.addLevelName(TRACE_LEVEL, "TRACE")
- logging.setLoggerClass(CustomLogger)
-
root_log = get_logger()
- format_string = "%(asctime)s | %(name)s | %(levelname)s | %(message)s"
- log_format = logging.Formatter(format_string)
-
if constants.FILE_LOGS:
log_file = Path("logs", "bot.log")
log_file.parent.mkdir(exist_ok=True)
file_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7, encoding="utf8")
- file_handler.setFormatter(log_format)
+ file_handler.setFormatter(core_logging.log_format)
root_log.addHandler(file_handler)
if "COLOREDLOGS_LEVEL_STYLES" not in os.environ:
@@ -69,18 +35,11 @@ def setup() -> None:
}
if "COLOREDLOGS_LOG_FORMAT" not in os.environ:
- coloredlogs.DEFAULT_LOG_FORMAT = format_string
+ coloredlogs.DEFAULT_LOG_FORMAT = core_logging.log_format._fmt
- coloredlogs.install(level=TRACE_LEVEL, logger=root_log, stream=sys.stdout)
+ coloredlogs.install(level=core_logging.TRACE_LEVEL, logger=root_log, stream=sys.stdout)
root_log.setLevel(logging.DEBUG if constants.DEBUG_MODE else logging.INFO)
- get_logger("discord").setLevel(logging.WARNING)
- get_logger("websockets").setLevel(logging.WARNING)
- get_logger("chardet").setLevel(logging.WARNING)
- get_logger("async_rediscache").setLevel(logging.WARNING)
-
- # Set back to the default of INFO even if asyncio's debug mode is enabled.
- get_logger("asyncio").setLevel(logging.INFO)
_set_trace_loggers()
@@ -121,13 +80,13 @@ def _set_trace_loggers() -> None:
level_filter = constants.Bot.trace_loggers
if level_filter:
if level_filter.startswith("*"):
- get_logger().setLevel(TRACE_LEVEL)
+ get_logger().setLevel(core_logging.TRACE_LEVEL)
elif level_filter.startswith("!"):
- get_logger().setLevel(TRACE_LEVEL)
+ get_logger().setLevel(core_logging.TRACE_LEVEL)
for logger_name in level_filter.strip("!,").split(","):
get_logger(logger_name).setLevel(logging.DEBUG)
else:
for logger_name in level_filter.strip(",").split(","):
- get_logger(logger_name).setLevel(TRACE_LEVEL)
+ get_logger(logger_name).setLevel(core_logging.TRACE_LEVEL)
diff --git a/bot/pagination.py b/bot/pagination.py
index fd9dd8af6..6b2f76715 100644
--- a/bot/pagination.py
+++ b/bot/pagination.py
@@ -1,190 +1,19 @@
-import asyncio
-from contextlib import suppress
-from functools import partial
+from collections.abc import Sequence
import discord
-from discord.abc import User
-from discord.ext.commands import Context, Paginator
+from discord.ext.commands import Context
+from pydis_core.utils.pagination import LinePaginator as _LinePaginator, PaginationEmojis
-from bot import constants
-from bot.log import get_logger
-from bot.utils import messages
+from bot.constants import Emojis
-FIRST_EMOJI = "\u23EE" # [:track_previous:]
-LEFT_EMOJI = "\u2B05" # [:arrow_left:]
-RIGHT_EMOJI = "\u27A1" # [:arrow_right:]
-LAST_EMOJI = "\u23ED" # [:track_next:]
-DELETE_EMOJI = constants.Emojis.trashcan # [:trashcan:]
-PAGINATION_EMOJI = (FIRST_EMOJI, LEFT_EMOJI, RIGHT_EMOJI, LAST_EMOJI, DELETE_EMOJI)
-
-log = get_logger(__name__)
-
-
-class EmptyPaginatorEmbedError(Exception):
- """Raised when attempting to paginate with empty contents."""
-
-
-
-class LinePaginator(Paginator):
+class LinePaginator(_LinePaginator):
"""
A class that aids in paginating code blocks for Discord messages.
- Available attributes include:
- * prefix: `str`
- The prefix inserted to every page. e.g. three backticks.
- * suffix: `str`
- The suffix appended at the end of every page. e.g. three backticks.
- * max_size: `int`
- The maximum amount of codepoints allowed in a page.
- * scale_to_size: `int`
- The maximum amount of characters a single line can scale up to.
- * max_lines: `int`
- The maximum amount of lines allowed in a page.
+ See the super class's docs for more info.
"""
- def __init__(
- self,
- prefix: str = "```",
- suffix: str = "```",
- max_size: int = 4000,
- scale_to_size: int = 4000,
- max_lines: int | None = None,
- linesep: str = "\n"
- ) -> None:
- """
- This function overrides the Paginator.__init__ from inside discord.ext.commands.
-
- It overrides in order to allow us to configure the maximum number of lines per page.
- """
- # Embeds that exceed 4096 characters will result in an HTTPException
- # (Discord API limit), so we've set a limit of 4000
- if max_size > 4000:
- raise ValueError(f"max_size must be <= 4,000 characters. ({max_size} > 4000)")
-
- super().__init__(
- prefix,
- suffix,
- max_size - len(suffix),
- linesep
- )
-
- if scale_to_size < max_size:
- raise ValueError(f"scale_to_size must be >= max_size. ({scale_to_size} < {max_size})")
-
- if scale_to_size > 4000:
- raise ValueError(f"scale_to_size must be <= 4,000 characters. ({scale_to_size} > 4000)")
-
- self.scale_to_size = scale_to_size - len(suffix)
- self.max_lines = max_lines
- self._current_page = [prefix]
- self._linecount = 0
- self._count = len(prefix) + 1 # prefix + newline
- self._pages = []
-
- def add_line(self, line: str = "", *, empty: bool = False) -> None:
- """
- Adds a line to the current page.
-
- If a line on a page exceeds `max_size` characters, then `max_size` will go up to
- `scale_to_size` for a single line before creating a new page for the overflow words. If it
- is still exceeded, the excess characters are stored and placed on the next pages unti
- there are none remaining (by word boundary). The line is truncated if `scale_to_size` is
- still exceeded after attempting to continue onto the next page.
-
- In the case that the page already contains one or more lines and the new lines would cause
- `max_size` to be exceeded, a new page is created. This is done in order to make a best
- effort to avoid breaking up single lines across pages, while keeping the total length of the
- page at a reasonable size.
-
- This function overrides the `Paginator.add_line` from inside `discord.ext.commands`.
-
- It overrides in order to allow us to configure the maximum number of lines per page.
- """
- remaining_words = None
- if len(line) > (max_chars := self.max_size - len(self.prefix) - 2):
- if len(line) > self.scale_to_size:
- line, remaining_words = self._split_remaining_words(line, max_chars)
- if len(line) > self.scale_to_size:
- log.debug("Could not continue to next page, truncating line.")
- line = line[:self.scale_to_size]
-
- # Check if we should start a new page or continue the line on the current one
- if self.max_lines is not None and self._linecount >= self.max_lines:
- log.debug("max_lines exceeded, creating new page.")
- self._new_page()
- elif self._count + len(line) + 1 > self.max_size and self._linecount > 0:
- log.debug("max_size exceeded on page with lines, creating new page.")
- self._new_page()
-
- self._linecount += 1
-
- self._count += len(line) + 1
- self._current_page.append(line)
-
- if empty:
- self._current_page.append("")
- self._count += 1
-
- # Start a new page if there were any overflow words
- if remaining_words:
- self._new_page()
- self.add_line(remaining_words)
-
- def _new_page(self) -> None:
- """
- Internal: start a new page for the paginator.
-
- This closes the current page and resets the counters for the new page's line count and
- character count.
- """
- self._linecount = 0
- self._count = len(self.prefix) + 1
- self.close_page()
-
- def _split_remaining_words(self, line: str, max_chars: int) -> tuple[str, str | None]:
- """
- Internal: split a line into two strings -- reduced_words and remaining_words.
-
- reduced_words: the remaining words in `line`, after attempting to remove all words that
- exceed `max_chars` (rounding down to the nearest word boundary).
-
- remaining_words: the words in `line` which exceed `max_chars`. This value is None if
- no words could be split from `line`.
-
- If there are any remaining_words, an ellipses is appended to reduced_words and a
- continuation header is inserted before remaining_words to visually communicate the line
- continuation.
-
- Return a tuple in the format (reduced_words, remaining_words).
- """
- reduced_words = []
- remaining_words = []
-
- # "(Continued)" is used on a line by itself to indicate the continuation of last page
- continuation_header = "(Continued)\n-----------\n"
- reduced_char_count = 0
- is_full = False
-
- for word in line.split(" "):
- if not is_full:
- if len(word) + reduced_char_count <= max_chars:
- reduced_words.append(word)
- reduced_char_count += len(word) + 1
- else:
- # If reduced_words is empty, we were unable to split the words across pages
- if not reduced_words:
- return line, None
- is_full = True
- remaining_words.append(word)
- else:
- remaining_words.append(word)
-
- return (
- " ".join(reduced_words) + "..." if remaining_words else "",
- continuation_header + " ".join(remaining_words) if remaining_words else None
- )
-
@classmethod
async def paginate(
cls,
@@ -197,175 +26,36 @@ class LinePaginator(Paginator):
max_size: int = 500,
scale_to_size: int = 4000,
empty: bool = True,
- restrict_to_user: User | None = None,
+ restrict_to_user: discord.User | None = None,
timeout: int = 300,
- footer_text: str | None = None,
- url: str | None = None,
+ footer_text: str | None = None, url: str | None = None,
exception_on_empty_embed: bool = False,
reply: bool = False,
+ allowed_roles: Sequence[int] | None = None, **kwargs
) -> discord.Message | None:
"""
Use a paginator and set of reactions to provide pagination over a set of lines.
- The reactions are used to switch page, or to finish with pagination.
-
- When used, this will send a message using `ctx.send()` and apply a set of reactions to it. These reactions may
- be used to change page, or to remove pagination from the message.
-
- Pagination will also be removed automatically if no reaction is added for five minutes (300 seconds).
-
- The interaction will be limited to `restrict_to_user` (ctx.author by default) or
- to any user with a moderation role.
-
- Example:
- >>> embed = discord.Embed()
- >>> embed.set_author(name="Some Operation", url=url, icon_url=icon)
- >>> await LinePaginator.paginate([line for line in lines], ctx, embed)
- """
- paginator = cls(prefix=prefix, suffix=suffix, max_size=max_size, max_lines=max_lines,
- scale_to_size=scale_to_size)
- current_page = 0
-
- if not restrict_to_user:
- if isinstance(ctx, discord.Interaction):
- restrict_to_user = ctx.user
- else:
- restrict_to_user = ctx.author
-
- if not lines:
- if exception_on_empty_embed:
- log.exception("Pagination asked for empty lines iterable")
- raise EmptyPaginatorEmbedError("No lines to paginate")
-
- log.debug("No lines to add to paginator, adding '(nothing to display)' message")
- lines.append("*(nothing to display)*")
-
- for line in lines:
- try:
- paginator.add_line(line, empty=empty)
- except Exception:
- log.exception(f"Failed to add line to paginator: '{line}'")
- raise # Should propagate
- else:
- log.trace(f"Added line to paginator: '{line}'")
-
- log.debug(f"Paginator created with {len(paginator.pages)} pages")
-
- embed.description = paginator.pages[current_page]
-
- reference = ctx.message if reply else None
-
- if len(paginator.pages) <= 1:
- if footer_text:
- embed.set_footer(text=footer_text)
- log.trace(f"Setting embed footer to '{footer_text}'")
-
- if url:
- embed.url = url
- log.trace(f"Setting embed url to '{url}'")
-
- log.debug("There's less than two pages, so we won't paginate - sending single page on its own")
-
- if isinstance(ctx, discord.Interaction):
- return await ctx.response.send_message(embed=embed)
- return await ctx.send(embed=embed, reference=reference)
-
- if footer_text:
- embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
- else:
- embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}")
- log.trace(f"Setting embed footer to '{embed.footer.text}'")
-
- if url:
- embed.url = url
- log.trace(f"Setting embed url to '{url}'")
-
- log.debug("Sending first page to channel...")
-
- if isinstance(ctx, discord.Interaction):
- await ctx.response.send_message(embed=embed)
- message = await ctx.original_response()
- else:
- message = await ctx.send(embed=embed, reference=reference)
-
- log.debug("Adding emoji reactions to message...")
-
- for emoji in PAGINATION_EMOJI:
- # Add all the applicable emoji to the message
- log.trace(f"Adding reaction: {emoji!r}")
- await message.add_reaction(emoji)
-
- check = partial(
- messages.reaction_check,
- message_id=message.id,
- allowed_emoji=PAGINATION_EMOJI,
- allowed_users=(restrict_to_user.id,),
+ Acts as a wrapper for the super class' `paginate` method to provide the pagination emojis by default.
+
+ Consult the super class's `paginate` method for detailed information.
+ """
+ return await super().paginate(
+ pagination_emojis=PaginationEmojis(delete=Emojis.trashcan),
+ lines=lines,
+ ctx=ctx,
+ embed=embed,
+ prefix=prefix,
+ suffix=suffix,
+ max_lines=max_lines,
+ max_size=max_size,
+ scale_to_size=scale_to_size,
+ empty=empty,
+ restrict_to_user=restrict_to_user,
+ timeout=timeout,
+ footer_text=footer_text,
+ url=url,
+ exception_on_empty_embed=exception_on_empty_embed,
+ reply=reply,
+ allowed_roles=allowed_roles
)
-
- while True:
- try:
- if isinstance(ctx, discord.Interaction):
- reaction, user = await ctx.client.wait_for("reaction_add", timeout=timeout, check=check)
- else:
- reaction, user = await ctx.bot.wait_for("reaction_add", timeout=timeout, check=check)
- log.trace(f"Got reaction: {reaction}")
- except asyncio.TimeoutError:
- log.debug("Timed out waiting for a reaction")
- break # We're done, no reactions for the last 5 minutes
-
- if str(reaction.emoji) == DELETE_EMOJI:
- log.debug("Got delete reaction")
- return await message.delete()
- if reaction.emoji in PAGINATION_EMOJI:
- total_pages = len(paginator.pages)
- try:
- await message.remove_reaction(reaction.emoji, user)
- except discord.HTTPException as e:
- # Suppress if trying to act on an archived thread.
- if e.code != 50083:
- raise e
-
- if reaction.emoji == FIRST_EMOJI:
- current_page = 0
- log.debug(f"Got first page reaction - changing to page 1/{total_pages}")
- elif reaction.emoji == LAST_EMOJI:
- current_page = len(paginator.pages) - 1
- log.debug(f"Got last page reaction - changing to page {current_page + 1}/{total_pages}")
- elif reaction.emoji == LEFT_EMOJI:
- if current_page <= 0:
- log.debug("Got previous page reaction, but we're on the first page - ignoring")
- continue
-
- current_page -= 1
- log.debug(f"Got previous page reaction - changing to page {current_page + 1}/{total_pages}")
- elif reaction.emoji == RIGHT_EMOJI:
- if current_page >= len(paginator.pages) - 1:
- log.debug("Got next page reaction, but we're on the last page - ignoring")
- continue
-
- current_page += 1
- log.debug(f"Got next page reaction - changing to page {current_page + 1}/{total_pages}")
-
- embed.description = paginator.pages[current_page]
-
- if footer_text:
- embed.set_footer(text=f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})")
- else:
- embed.set_footer(text=f"Page {current_page + 1}/{len(paginator.pages)}")
-
- try:
- await message.edit(embed=embed)
- except discord.HTTPException as e:
- if e.code == 50083:
- # Trying to act on an archived thread, just ignore and abort
- break
- raise e
-
- log.debug("Ending pagination and clearing reactions.")
- with suppress(discord.NotFound):
- try:
- await message.clear_reactions()
- except discord.HTTPException as e:
- # Suppress if trying to act on an archived thread.
- if e.code != 50083:
- raise e
diff --git a/bot/resources/tags/args-kwargs.md b/bot/resources/tags/args-kwargs.md
index 67d93fc40..d9d433fab 100644
--- a/bot/resources/tags/args-kwargs.md
+++ b/bot/resources/tags/args-kwargs.md
@@ -11,9 +11,9 @@ These special parameters allow functions to take arbitrary amounts of positional
`**kwargs` will ingest an arbitrary amount of **keyword arguments**, and store it in a dictionary. There can be **no** additional parameters **after** `**kwargs` in the parameter list.
**Use cases**
-• **Decorators** (see `/tag decorators`)
-• **Inheritance** (overriding methods)
-• **Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break)
-• **Flexibility** (writing functions that behave like `dict()` or `print()`)
+- **Decorators** (see `/tag decorators`)
+- **Inheritance** (overriding methods)
+- **Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break)
+- **Flexibility** (writing functions that behave like `dict()` or `print()`)
*See* `/tag positional-keyword` *for information about positional and keyword arguments*
diff --git a/bot/resources/tags/blocking.md b/bot/resources/tags/blocking.md
index 1999e2421..4fe778e61 100644
--- a/bot/resources/tags/blocking.md
+++ b/bot/resources/tags/blocking.md
@@ -19,8 +19,8 @@ async def ping(ctx):
A blocking operation is wherever you do something without `await`ing it. This tells Python that this step must be completed before it can do anything else. Common examples of blocking operations, as simple as they may seem, include: outputting text, adding two numbers and appending an item onto a list. Most common Python libraries have an asynchronous version available to use in asynchronous contexts.
**`async` libraries**
-The standard async library - `asyncio`
-Asynchronous web requests - `aiohttp`
-Talking to PostgreSQL asynchronously - `asyncpg`
-MongoDB interactions asynchronously - `motor`
-Check out [this](https://github.com/timofurrer/awesome-asyncio) list for even more!
+- The standard async library - `asyncio`
+- Asynchronous web requests - `aiohttp`
+- Talking to PostgreSQL asynchronously - `asyncpg`
+- MongoDB interactions asynchronously - `motor`
+- Check out [this](https://github.com/timofurrer/awesome-asyncio) list for even more!
diff --git a/bot/resources/tags/class.md b/bot/resources/tags/class.md
index 5fbe43c18..1e8b3ae9b 100644
--- a/bot/resources/tags/class.md
+++ b/bot/resources/tags/class.md
@@ -4,7 +4,7 @@ embed:
---
Classes are used to create objects that have specific behavior.
-Every object in python has a class, including `list`s, `dict`ionaries and even numbers. Using a class to group code and data like this is the foundation of Object Oriented Programming. Classes allow you to expose a simple, consistent interface while hiding the more complicated details. This simplifies the rest of your program and makes it easier to separately maintain and debug each component.
+Every object in Python has a class, including `list`s, `dict`ionaries and even numbers. Using a class to group code and data like this is the foundation of Object Oriented Programming. Classes allow you to expose a simple, consistent interface while hiding the more complicated details. This simplifies the rest of your program and makes it easier to separately maintain and debug each component.
Here is an example class:
diff --git a/bot/resources/tags/codeblock.md b/bot/resources/tags/codeblock.md
index c2b77637e..c7f2990fd 100644
--- a/bot/resources/tags/codeblock.md
+++ b/bot/resources/tags/codeblock.md
@@ -1,6 +1,6 @@
---
embed:
- title: "Formatting code on discord"
+ title: "Formatting code on Discord"
---
Here's how to format Python code on Discord:
diff --git a/bot/resources/tags/contribute.md b/bot/resources/tags/contribute.md
index 57eae0a7e..b788aba21 100644
--- a/bot/resources/tags/contribute.md
+++ b/bot/resources/tags/contribute.md
@@ -5,9 +5,9 @@ embed:
Looking to contribute to Open Source Projects for the first time? Want to add a feature or fix a bug on the bots on this server? We have on-going projects that people can contribute to, even if you've never contributed to open source before!
**Projects to Contribute to**
-• [Sir Lancebot](https://github.com/python-discord/sir-lancebot) - our fun, beginner-friendly bot
-• [Python](https://github.com/python-discord/bot) - our utility & moderation bot
-• [Site](https://github.com/python-discord/site) - resources, guides, and more
+- [Sir Lancebot](https://github.com/python-discord/sir-lancebot) - our fun, beginner-friendly bot
+- [Python](https://github.com/python-discord/bot) - our utility & moderation bot
+- [Site](https://github.com/python-discord/site) - resources, guides, and more
**Where to start**
1. Read our [contribution guide](https://pythondiscord.com/pages/guides/pydis-guides/contributing/)
diff --git a/bot/resources/tags/decorators.md b/bot/resources/tags/decorators.md
index 65d221d6e..ebfb40a10 100644
--- a/bot/resources/tags/decorators.md
+++ b/bot/resources/tags/decorators.md
@@ -29,5 +29,5 @@ Finished!
```
More information:
-• [Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U)
-• [Real python article](https://realpython.com/primer-on-python-decorators/)
+- [Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U)
+- [Real python article](https://realpython.com/primer-on-python-decorators/)
diff --git a/bot/resources/tags/dotenv.md b/bot/resources/tags/dotenv.md
index 14fff3458..b1dc57fa5 100644
--- a/bot/resources/tags/dotenv.md
+++ b/bot/resources/tags/dotenv.md
@@ -6,20 +6,20 @@ embed:
Dotenv files are especially suited for storing secrets as they are a key-value store in a file, which can be easily loaded in most programming languages and ignored by version control systems like Git with a single entry in a `.gitignore` file.
-In python you can use dotenv files with the [`python-dotenv`](https://pypi.org/project/python-dotenv) module from PyPI, which can be installed with `pip install python-dotenv`. To use dotenv files you'll first need a file called `.env`, with content such as the following:
+In Python you can use dotenv files with the [`python-dotenv`](https://pypi.org/project/python-dotenv) module from PyPI, which can be installed with `pip install python-dotenv`. To use dotenv files you'll first need a file called `.env`, with content such as the following:
```
TOKEN=a00418c85bff087b49f23923efe40aa5
```
Next, in your main Python file, you need to load the environment variables from the dotenv file you just created:
```py
-from dotenv import load_dotenv()
+from dotenv import load_dotenv
-load_dotenv(".env")
+load_dotenv()
```
-The variables from the file have now been loaded into your programs environment, and you can access them using `os.getenv()` anywhere in your program, like this:
+The variables from the file have now been loaded into your program's environment, and you can access them using `os.getenv()` anywhere in your program, like this:
```py
from os import getenv
my_token = getenv("TOKEN")
```
-For further reading about tokens and secrets, please read [this explanation](https://vcokltfre.dev/tips/tokens).
+For further reading about tokens and secrets, please read [this explanation](https://tutorial.vco.sh/tips/tokens/).
diff --git a/bot/resources/tags/environments.md b/bot/resources/tags/environments.md
index d6ed06448..1c1677f4d 100644
--- a/bot/resources/tags/environments.md
+++ b/bot/resources/tags/environments.md
@@ -18,12 +18,12 @@ If Python's `sys.executable` doesn't match pip's, then they are currently using
**Why use a virtual environment?**
-• Resolve dependency issues by allowing the use of different versions of a package for different projects. For example, you could use Package A v2.7 for Project X and Package A v1.3 for Project Y.
-• Make your project self-contained and reproducible by capturing all package dependencies in a requirements file. Try running `pip freeze` to see what you currently have installed!
-• Keep your global `site-packages/` directory tidy by removing the need to install packages system-wide which you might only need for one project.
+- Resolve dependency issues by allowing the use of different versions of a package for different projects. For example, you could use Package A v2.7 for Project X and Package A v1.3 for Project Y.
+- Make your project self-contained and reproducible by capturing all package dependencies in a requirements file. Try running `pip freeze` to see what you currently have installed!
+- Keep your global `site-packages/` directory tidy by removing the need to install packages system-wide which you might only need for one project.
**Further reading:**
-• [Python Virtual Environments: A Primer](https://realpython.com/python-virtual-environments-a-primer)
-• [pyenv: Simple Python Version Management](https://github.com/pyenv/pyenv)
+- [Python Virtual Environments: A Primer](https://realpython.com/python-virtual-environments-a-primer)
+- [pyenv: Simple Python Version Management](https://github.com/pyenv/pyenv)
diff --git a/bot/resources/tags/equals-true.md b/bot/resources/tags/equals-true.md
new file mode 100644
index 000000000..d8e1a707e
--- /dev/null
+++ b/bot/resources/tags/equals-true.md
@@ -0,0 +1,24 @@
+---
+embed:
+ title: "Comparisons to `True` and `False`"
+---
+It's tempting to think that if statements always need a comparison operator like `==` or `!=`, but this isn't true.
+If you're just checking if a value is truthy or falsey, you don't need `== True` or `== False`.
+```py
+# instead of this...
+if user_input.startswith('y') == True:
+ my_func(user_input)
+
+# ...write this
+if user_input.startswith('y'):
+ my_func(user_input)
+
+# for false conditions, instead of this...
+if user_input.startswith('y') == False:
+ my_func(user_input)
+
+# ...just use `not`
+if not user_input.startswith('y'):
+ my_func(user_input)
+```
+This also applies to expressions that use `is True` or `is False`.
diff --git a/bot/resources/tags/foo.md b/bot/resources/tags/foo.md
index 0a40eb991..5605ad67e 100644
--- a/bot/resources/tags/foo.md
+++ b/bot/resources/tags/foo.md
@@ -8,5 +8,5 @@ Common examples include `foobar`, `foo`, `bar`, `baz`, and `qux`.
Python has its own metasyntactic variables, namely `spam`, `eggs`, and `bacon`. This is a reference to a [Monty Python](https://en.wikipedia.org/wiki/Monty_Python) sketch (the eponym of the language).
More information:
-• [History of foobar](https://en.wikipedia.org/wiki/Foobar)
-• [Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29)
+- [History of foobar](https://en.wikipedia.org/wiki/Foobar)
+- [Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29)
diff --git a/bot/resources/tags/if-name-main.md b/bot/resources/tags/if-name-main.md
index c2a7eb289..589c47618 100644
--- a/bot/resources/tags/if-name-main.md
+++ b/bot/resources/tags/if-name-main.md
@@ -23,6 +23,6 @@ If you run this module named `bar.py`, it will execute the code in `foo.py`. Fir
**Why would I do this?**
-• Your module is a library, but also has a special case where it can be run directly
-• Your module is a library and you want to safeguard it against people running it directly (like what `pip` does)
-• Your module is the main program, but has unit tests and the testing framework works by importing your module, and you want to avoid having your main code run during the test
+- Your module is a library, but also has a special case where it can be run directly
+- Your module is a library and you want to safeguard it against people running it directly (like what `pip` does)
+- Your module is the main program, but has unit tests and the testing framework works by importing your module, and you want to avoid having your main code run during the test
diff --git a/bot/resources/tags/in-place.md b/bot/resources/tags/in-place.md
index e2218a7df..b66c9d93a 100644
--- a/bot/resources/tags/in-place.md
+++ b/bot/resources/tags/in-place.md
@@ -1,22 +1,26 @@
---
embed:
- title: "Out of place and in place"
+ title: "In-place vs. Out-of-place operations"
---
-In programming, there are two types of operations: "out of place" operations create a new object, leaving the original object unchanged. "in place" operations modify the original object without creating a new one, and return `None` explicitly.
+In programming, there are two types of operations:
+- "In-place" operations, which modify the original object
+- "Out-of-place" operations, which returns a new object and leaves the original object unchanged
-A common example of these different concepts is seen in the use of the methods `list.sort()` and `sorted(...)`. Using `list.sort()` and attempting to access an element of the list will result in an error.
-
-```py
-# WRONG:
-
-unsorted_list = [3, 1, 2]
-sorted_list = unsorted_list.sort() # This will be None
-print(sorted_list) # Outputs None. Where did the list go?
-
-list_to_sort = [3, 1, 2]
-sorted(list_to_sort)
-print(list_to_sort) # The list still isn't sorted. Why?
+For example, the `.sort()` method of lists is in-place, so it modifies the list you call `.sort()` on:
+```python
+>>> my_list = [5, 2, 3, 1]
+>>> my_list.sort() # Returns None
+>>> my_list
+[1, 2, 3, 5]
```
-
-To avoid these errors and unexpected results, you should either use an out-of-place operation `(sorted(...))` and assign it to a variable or use an in-place operation `(list.sort())` without assignment.
+On the other hand, the `sorted()` function is out-of-place, so it returns a new list and leaves the original list unchanged:
+```python
+>>> my_list = [5, 2, 3, 1]
+>>> sorted_list = sorted(my_list)
+>>> sorted_list
+[1, 2, 3, 5]
+>>> my_list
+[5, 2, 3, 1]
+```
+In general, methods of mutable objects tend to be in-place (since it can be expensive to create a new object), whereas operations on immutable objects are always out-of-place (since they cannot be modified).
diff --git a/bot/resources/tags/mutable-default-args.md b/bot/resources/tags/mutable-default-args.md
index e314e6c43..a1a6d3d2a 100644
--- a/bot/resources/tags/mutable-default-args.md
+++ b/bot/resources/tags/mutable-default-args.md
@@ -2,7 +2,7 @@
embed:
title: "Mutable default arguments"
---
-Default arguments in python are evaluated *once* when the function is
+Default arguments in Python are evaluated *once* when the function is
**defined**, *not* each time the function is **called**. This means that if
you have a mutable default argument and mutate it, you will have
mutated that object for all future calls to the function as well.
@@ -44,7 +44,7 @@ function is **called**:
**Note**:
-• This behavior can be used intentionally to maintain state between
+- This behavior can be used intentionally to maintain state between
calls of a function (eg. when writing a caching function).
-• This behavior is not unique to mutable objects, all default
+- This behavior is not unique to mutable objects, all default
arguments are evaulated only once when the function is defined.
diff --git a/bot/resources/tags/names.md b/bot/resources/tags/names.md
index f949c861a..bc66fa099 100644
--- a/bot/resources/tags/names.md
+++ b/bot/resources/tags/names.md
@@ -26,14 +26,14 @@ y ━━ 1
```
**Names are created in multiple ways**
You might think that the only way to bind a name to an object is by using assignment, but that isn't the case. All of the following work exactly the same as assignment:
-• `import` statements
-• `class` and `def`
-• `for` loop headers
-• `as` keyword when used with `except`, `import`, and `with`
-• formal parameters in function headers
+- `import` statements
+- `class` and `def`
+- `for` loop headers
+- `as` keyword when used with `except`, `import`, and `with`
+- formal parameters in function headers
There is also `del` which has the purpose of *unbinding* a name.
**More info**
-• Please watch [Ned Batchelder's talk](https://youtu.be/_AEJHKGk9ns) on names in python for a detailed explanation with examples
-• [Official documentation](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding)
+- Please watch [Ned Batchelder's talk](https://youtu.be/_AEJHKGk9ns) on names in python for a detailed explanation with examples
+- [Official documentation](https://docs.python.org/3/reference/executionmodel.html#naming-and-binding)
diff --git a/bot/resources/tags/nomodule.md b/bot/resources/tags/nomodule.md
index 2f420e4bc..8b02b1b7a 100644
--- a/bot/resources/tags/nomodule.md
+++ b/bot/resources/tags/nomodule.md
@@ -8,8 +8,8 @@ You can read about Python environments at `/tag environments` and `/tag venv`.
Common causes of this problem include:
-• You installed your package using `pip install ...`. It could be that the `pip` command is not pointing to the environment where your code runs. For greater control, you could instead run pip as a module within the python environment you specify:
+- You installed your package using `pip install ...`. It could be that the `pip` command is not pointing to the environment where your code runs. For greater control, you could instead run pip as a module within the python environment you specify:
```
python -m pip install <your_package>
```
-• Your editor/ide is configured to create virtual environments automatically (PyCharm is configured this way by default).
+- Your editor/ide is configured to create virtual environments automatically (PyCharm is configured this way by default).
diff --git a/bot/resources/tags/off-topic-names.md b/bot/resources/tags/off-topic-names.md
index 44a16d0c2..8326f56fe 100644
--- a/bot/resources/tags/off-topic-names.md
+++ b/bot/resources/tags/off-topic-names.md
@@ -3,9 +3,9 @@ embed:
title: "Off-topic channels"
---
There are three off-topic channels:
-• <#291284109232308226>
-• <#463035241142026251>
-• <#463035268514185226>
+- <#291284109232308226>
+- <#463035241142026251>
+- <#463035268514185226>
The channel names change every night at midnight UTC and are often fun meta references to jokes or conversations that happened on the server.
diff --git a/bot/resources/tags/open.md b/bot/resources/tags/open.md
index 3947cb88d..97595b5eb 100644
--- a/bot/resources/tags/open.md
+++ b/bot/resources/tags/open.md
@@ -5,9 +5,9 @@ embed:
The built-in function `open()` is one of several ways to open files on your computer. It accepts many different parameters, so this tag will only go over two of them (`file` and `mode`). For more extensive documentation on all these parameters, consult the [official documentation](https://docs.python.org/3/library/functions.html#open). The object returned from this function is a [file object or stream](https://docs.python.org/3/glossary.html#term-file-object), for which the full documentation can be found [here](https://docs.python.org/3/library/io.html#io.TextIOBase).
See also:
-• `!tags with` for information on context managers
-• `!tags pathlib` for an alternative way of opening files
-• `!tags seek` for information on changing your position in a file
+- `!tags with` for information on context managers
+- `!tags pathlib` for an alternative way of opening files
+- `!tags seek` for information on changing your position in a file
**The `file` parameter**
@@ -21,8 +21,8 @@ See `!tags relative-path` for more information on relative paths.
This is an optional string that specifies the mode in which the file should be opened. There's not enough room to discuss them all, but listed below are some of the more confusing modes.
-`'r+'` Opens for reading and writing (file must already exist)
-`'w+'` Opens for reading and writing and truncates (can create files)
-`'x'` Creates file and opens for writing (file must **not** already exist)
-`'x+'` Creates file and opens for reading and writing (file must **not** already exist)
-`'a+'` Opens file for reading and writing at **end of file** (can create files)
+- `'r+'` Opens for reading and writing (file must already exist)
+- `'w+'` Opens for reading and writing and truncates (can create files)
+- `'x'` Creates file and opens for writing (file must **not** already exist)
+- `'x+'` Creates file and opens for reading and writing (file must **not** already exist)
+- `'a+'` Opens file for reading and writing at **end of file** (can create files)
diff --git a/bot/resources/tags/paste.md b/bot/resources/tags/paste.md
index f4c004291..79e34b3bd 100644
--- a/bot/resources/tags/paste.md
+++ b/bot/resources/tags/paste.md
@@ -5,4 +5,4 @@ embed:
If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/
-After pasting your code, **save** it by clicking the floppy disk icon in the top right, or by typing `ctrl + S`. After doing that, the URL should **change**. Copy the URL and post it here so others can see it.
+After pasting your code, **save** it by clicking the Paste! button in the bottom left, or by pressing `CTRL + S`. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.
diff --git a/bot/resources/tags/pathlib.md b/bot/resources/tags/pathlib.md
index db6c1f3eb..6e3ed85bc 100644
--- a/bot/resources/tags/pathlib.md
+++ b/bot/resources/tags/pathlib.md
@@ -6,18 +6,18 @@ Python 3 comes with a new module named `Pathlib`. Since Python 3.6, `pathlib.Pat
**Feature spotlight**:
-• Normalizes file paths for all platforms automatically
-• Has glob-like utilites (eg. `Path.glob`, `Path.rglob`) for searching files
-• Can read and write files, and close them automatically
-• Convenient syntax, utilising the `/` operator (e.g. `Path('~') / 'Documents'`)
-• Can easily pick out components of a path (eg. name, parent, stem, suffix, anchor)
-• Supports method chaining
-• Move and delete files
-• And much more
+- Normalizes file paths for all platforms automatically
+- Has glob-like utilites (eg. `Path.glob`, `Path.rglob`) for searching files
+- Can read and write files, and close them automatically
+- Convenient syntax, utilising the `/` operator (e.g. `Path('~') / 'Documents'`)
+- Can easily pick out components of a path (eg. name, parent, stem, suffix, anchor)
+- Supports method chaining
+- Move and delete files
+- And much more
**More Info**:
-• [**Why you should use pathlib** - Trey Hunner](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/)
-• [**Answering concerns about pathlib** - Trey Hunner](https://treyhunner.com/2019/01/no-really-pathlib-is-great/)
-• [**Official Documentation**](https://docs.python.org/3/library/pathlib.html)
-• [**PEP 519** - Adding a file system path protocol](https://peps.python.org/pep-0519/)
+- [**Why you should use pathlib** - Trey Hunner](https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/)
+- [**Answering concerns about pathlib** - Trey Hunner](https://treyhunner.com/2019/01/no-really-pathlib-is-great/)
+- [**Official Documentation**](https://docs.python.org/3/library/pathlib.html)
+- [**PEP 519** - Adding a file system path protocol](https://peps.python.org/pep-0519/)
diff --git a/bot/resources/tags/pep8.md b/bot/resources/tags/pep8.md
index 98c739e5e..620e2fa08 100644
--- a/bot/resources/tags/pep8.md
+++ b/bot/resources/tags/pep8.md
@@ -5,5 +5,5 @@ embed:
**PEP 8** is the official style guide for Python. It includes comprehensive guidelines for code formatting, variable naming, and making your code easy to read. Professional Python developers are usually required to follow the guidelines, and will often use code-linters like flake8 to verify that the code they're writing complies with the style guide.
More information:
-• [PEP 8 document](https://peps.python.org/pep-0008/)
-• [Our PEP 8 song!](https://www.youtube.com/watch?v=hgI0p1zf31k) :notes:
+- [PEP 8 document](https://peps.python.org/pep-0008/)
+- [Our PEP 8 song!](https://www.youtube.com/watch?v=hgI0p1zf31k) :notes:
diff --git a/bot/resources/tags/positional-keyword.md b/bot/resources/tags/positional-keyword.md
index 1325fefc2..0e1eb464e 100644
--- a/bot/resources/tags/positional-keyword.md
+++ b/bot/resources/tags/positional-keyword.md
@@ -35,6 +35,6 @@ The reverse is also true:
```
**More info**
-• [Keyword only arguments](https://peps.python.org/pep-3102/)
-• [Positional only arguments](https://peps.python.org/pep-0570/)
-• `/tag param-arg` (Parameters vs. Arguments)
+- [Keyword only arguments](https://peps.python.org/pep-3102/)
+- [Positional only arguments](https://peps.python.org/pep-0570/)
+- `/tag param-arg` (Parameters vs. Arguments)
diff --git a/bot/resources/tags/precedence.md b/bot/resources/tags/precedence.md
index a9b3c4070..8a92c644b 100644
--- a/bot/resources/tags/precedence.md
+++ b/bot/resources/tags/precedence.md
@@ -2,7 +2,7 @@
embed:
title: "Operator precedence"
---
-Operator precedence is essentially like an order of operations for python's operators.
+Operator precedence is essentially like an order of operations for Python's operators.
**Example 1** (arithmetic)
`2 * 3 + 1` is `7` because multiplication is first
diff --git a/bot/resources/tags/quotes.md b/bot/resources/tags/quotes.md
index cea6d0147..dcc21c813 100644
--- a/bot/resources/tags/quotes.md
+++ b/bot/resources/tags/quotes.md
@@ -18,5 +18,5 @@ Example:
If you need both single and double quotes inside your string, use the version that would result in the least amount of escapes. In the case of a tie, use the quotation you use the most.
**References:**
-• [pep-8 on quotes](https://peps.python.org/pep-0008/#string-quotes)
-• [convention for triple quoted strings](https://peps.python.org/pep-0257/)
+- [pep-8 on quotes](https://peps.python.org/pep-0008/#string-quotes)
+- [convention for triple quoted strings](https://peps.python.org/pep-0257/)
diff --git a/bot/resources/tags/range-len.md b/bot/resources/tags/range-len.md
index 4bd377d59..76fe9051e 100644
--- a/bot/resources/tags/range-len.md
+++ b/bot/resources/tags/range-len.md
@@ -2,12 +2,12 @@
embed:
title: "Pythonic way of iterating over ordered collections"
---
-Iterating over `range(len(...))` is a common approach to accessing each item in an ordered collection.
+Beginners often iterate over `range(len(...))` because they look like Java or C-style loops, but this is almost always a bad practice in Python.
```py
for i in range(len(my_list)):
do_something(my_list[i])
```
-The pythonic syntax is much simpler, and is guaranteed to produce elements in the same order:
+It's much simpler to iterate over the list (or other sequence) directly:
```py
for item in my_list:
do_something(item)
diff --git a/bot/resources/tags/regex.md b/bot/resources/tags/regex.md
index d8bbb38cd..3883db28c 100644
--- a/bot/resources/tags/regex.md
+++ b/bot/resources/tags/regex.md
@@ -14,5 +14,5 @@ We can use regex to pull out all the numbers in a sentence:
['18', '196', '3', '47'] # Notice the year is cut off
```
**See Also**
-• [The re docs](https://docs.python.org/3/library/re.html) - for functions that use regex
-• [regex101.com](https://regex101.com) - an interactive site for testing your regular expression
+- [The re docs](https://docs.python.org/3/library/re.html) - for functions that use regex
+- [regex101.com](https://regex101.com) - an interactive site for testing your regular expression
diff --git a/bot/resources/tags/relative-path.md b/bot/resources/tags/relative-path.md
index c9feb2e52..15f00c951 100644
--- a/bot/resources/tags/relative-path.md
+++ b/bot/resources/tags/relative-path.md
@@ -2,8 +2,8 @@
embed:
title: "Relative path"
---
-A relative path is a partial path that is relative to your current working directory. A common misconception is that your current working directory is the location of the module you're executing, **but this is not the case**. Your current working directory is actually the **directory you were in when you ran the python interpreter**. The reason for this misconception is because a common way to run your code is to navigate to the directory your module is stored, and run `python <module>.py`. Thus, in this case your current working directory will be the same as the location of the module. However, if we instead did `python path/to/<module>.py`, our current working directory would no longer be the same as the location of the module we're executing.
+A relative path is a partial path that is relative to your current working directory. A common misconception is that your current working directory is the location of the module you're executing, **but this is not the case**. Your current working directory is actually the **directory you were in when you ran the Python interpreter**. The reason for this misconception is because a common way to run your code is to navigate to the directory your module is stored, and run `python <module>.py`. Thus, in this case your current working directory will be the same as the location of the module. However, if we instead did `python path/to/<module>.py`, our current working directory would no longer be the same as the location of the module we're executing.
**Why is this important?**
-When opening files in python, relative paths won't always work since it's dependent on what directory you were in when you ran your code. A common issue people face is running their code in an IDE thinking they can open files that are in the same directory as their module, but the current working directory will be different than what they expect and so they won't find the file. The way to avoid this problem is by using absolute paths, which is the full path from your root directory to the file you want to open.
+When opening files in Python, relative paths won't always work since it's dependent on what directory you were in when you ran your code. A common issue people face is running their code in an IDE thinking they can open files that are in the same directory as their module, but the current working directory will be different than what they expect and so they won't find the file. The way to avoid this problem is by using absolute paths, which is the full path from your root directory to the file you want to open.
diff --git a/bot/resources/tags/repl.md b/bot/resources/tags/repl.md
index 7d56457d3..410d08f74 100644
--- a/bot/resources/tags/repl.md
+++ b/bot/resources/tags/repl.md
@@ -2,14 +2,19 @@
embed:
title: "Read-Eval-Print Loop (REPL)"
---
-A REPL is an interactive language shell environment. It first **reads** one or more expressions entered by the user, **evaluates** it, yields the result, and **prints** it out to the user. It will then **loop** back to the **read** step.
+A REPL is an interactive shell where you can execute individual lines of code one at a time, like so:
+```python-repl
+>>> x = 5
+>>> x + 2
+7
+>>> for i in range(3):
+... print(i)
+...
+0
+1
+2
+>>>
+```
+To enter the REPL, run `python` (`py` on Windows) in the command line without any arguments. The `>>>` or `...` at the start of some lines are prompts to enter code, and indicate that you are in the Python REPL. Any other lines show the output of the code.
-To use python's REPL, execute the interpreter with no arguments. This will drop you into the interactive interpreter shell, print out some relevant information, and then prompt you with the primary prompt `>>>`. At this point it is waiting for your input.
-
-Firstly you can start typing in some valid python expressions, pressing <return> to either bring you to the **eval** step, or prompting you with the secondary prompt `...` (or no prompt at all depending on your environment), meaning your expression isn't yet terminated and it's waiting for more input. This is useful for code that requires multiple lines like loops, functions, and classes. If you reach the secondary prompt in a clause that can have an arbitrary amount of expressions, you can terminate it by pressing <return> on a blank line. In other words, for the last expression you write in the clause, <return> must be pressed twice in a row.
-
-Alternatively, you can make use of the builtin `help()` function. `help(thing)` to get help on some `thing` object, or `help()` to start an interactive help session. This mode is extremely powerful, read the instructions when first entering the session to learn how to use it.
-
-Lastly you can run your code with the `-i` flag to execute your code normally, but be dropped into the REPL once execution is finished, giving you access to all your global variables/functions in the REPL.
-
-To **exit** either a help session, or normal REPL prompt, you must send an EOF signal to the prompt. In *nix systems, this is done with `ctrl + D`, and in windows systems it is `ctrl + Z`. You can also exit the normal REPL prompt with the dedicated functions `exit()` or `quit()`.
+Trying to execute commands for the command-line (such as `pip install xyz`) in the REPL will throw an error. To run these commands, exit the REPL first by running `exit()` and then run the original command.
diff --git a/bot/resources/tags/return.md b/bot/resources/tags/return.md
index 805a17ee1..0676bbbd9 100644
--- a/bot/resources/tags/return.md
+++ b/bot/resources/tags/return.md
@@ -29,6 +29,6 @@ None
None
```
**Things to note**
-• `print()` and `return` do **not** accomplish the same thing. `print()` will show the value, and then it will be gone.
-• A function will return `None` if it ends without a `return` statement.
-• When you want to print a value from a function, it's best to return the value and print the *function call* instead, like `print(square(5))`.
+- `print()` and `return` do **not** accomplish the same thing. `print()` will show the value, and then it will be gone.
+- A function will return `None` if it ends without a `return` statement.
+- When you want to print a value from a function, it's best to return the value and print the *function call* instead, like `print(square(5))`.
diff --git a/bot/resources/tags/round.md b/bot/resources/tags/round.md
index fc67ca2f9..aca11dec5 100644
--- a/bot/resources/tags/round.md
+++ b/bot/resources/tags/round.md
@@ -20,7 +20,7 @@ The round half up technique creates a slight bias towards the larger number. Wit
It should be noted that round half to even distorts the distribution by increasing the probability of evens relative to odds, however this is considered less important than the bias explained above.
**References:**
-• [Wikipedia article about rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even)
-• [Documentation on `round` function](https://docs.python.org/3/library/functions.html#round)
-• [`round` in what's new in python 3](https://docs.python.org/3/whatsnew/3.0.html#builtins) (4th bullet down)
-• [How to force rounding technique](https://stackoverflow.com/a/10826537/4607272)
+- [Wikipedia article about rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even)
+- [Documentation on `round` function](https://docs.python.org/3/library/functions.html#round)
+- [`round` in what's new in python 3](https://docs.python.org/3/whatsnew/3.0.html#builtins) (4th bullet down)
+- [How to force rounding technique](https://stackoverflow.com/a/10826537/4607272)
diff --git a/bot/resources/tags/scope.md b/bot/resources/tags/scope.md
index cd55acf8c..963aaaa81 100644
--- a/bot/resources/tags/scope.md
+++ b/bot/resources/tags/scope.md
@@ -2,7 +2,7 @@
embed:
title: "Scoping rules"
---
-A *scope* defines the visibility of a name within a block, where a block is a piece of python code executed as a unit. For simplicity, this would be a module, a function body, and a class definition. A name refers to text bound to an object.
+A *scope* defines the visibility of a name within a block, where a block is a piece of Python code executed as a unit. For simplicity, this would be a module, a function body, and a class definition. A name refers to text bound to an object.
*For more information about names, see `/tag names`*
diff --git a/bot/resources/tags/seek.md b/bot/resources/tags/seek.md
index 53949506e..542ec25df 100644
--- a/bot/resources/tags/seek.md
+++ b/bot/resources/tags/seek.md
@@ -20,5 +20,5 @@ Now lets do `f.seek(4, 1)`. This will move our stream position 4 bytes forward r
Finally, lets do `f.seek(-4, 2)`, moving our stream position *backwards* 4 bytes relative to the **end** of the stream. Now if we did `f.read()` to read everything after our position in the file, it would return the string `'eggs'` and also move our stream position to the end of the file.
**Note**
-• For the second argument in `seek()`, use `os.SEEK_SET`, `os.SEEK_CUR`, and `os.SEEK_END` in place of 0, 1, and 2 respectively.
-• `os.SEEK_CUR` is only usable when the file is in byte mode.
+- For the second argument in `seek()`, use `os.SEEK_SET`, `os.SEEK_CUR`, and `os.SEEK_END` in place of 0, 1, and 2 respectively.
+- `os.SEEK_CUR` is only usable when the file is in byte mode.
diff --git a/bot/resources/tags/sql-fstring.md b/bot/resources/tags/sql-fstring.md
index b57d6cb3a..30de567b2 100644
--- a/bot/resources/tags/sql-fstring.md
+++ b/bot/resources/tags/sql-fstring.md
@@ -15,5 +15,5 @@ db.execute(query, params)
Note: Different database libraries support different placeholder styles, e.g. `%s` and `$1`. Consult your library's documentation for details.
**See Also**
-• [Python sqlite3 docs](https://docs.python.org/3/library/sqlite3.html#how-to-use-placeholders-to-bind-values-in-sql-queries) - How to use placeholders to bind values in SQL queries
-• [PEP-249](https://peps.python.org/pep-0249/) - A specification of how database libraries in Python should work
+- [Python sqlite3 docs](https://docs.python.org/3/library/sqlite3.html#how-to-use-placeholders-to-bind-values-in-sql-queries) - How to use placeholders to bind values in SQL queries
+- [PEP-249](https://peps.python.org/pep-0249/) - A specification of how database libraries in Python should work
diff --git a/bot/resources/tags/star-imports.md b/bot/resources/tags/star-imports.md
index 43e29fea5..236c7b503 100644
--- a/bot/resources/tags/star-imports.md
+++ b/bot/resources/tags/star-imports.md
@@ -19,18 +19,18 @@ Example:
>>> from math import *
>>> sin(pi / 2) # uses sin from math rather than your custom sin
```
-• Potential namespace collision. Names defined from a previous import might get shadowed by a wildcard import.
-• Causes ambiguity. From the example, it is unclear which `sin` function is actually being used. From the Zen of Python **[3]**: `Explicit is better than implicit.`
-• Makes import order significant, which they shouldn't. Certain IDE's `sort import` functionality may end up breaking code due to namespace collision.
+- Potential namespace collision. Names defined from a previous import might get shadowed by a wildcard import.
+- Causes ambiguity. From the example, it is unclear which `sin` function is actually being used. From the Zen of Python **[3]**: `Explicit is better than implicit.`
+- Makes import order significant, which they shouldn't. Certain IDE's `sort import` functionality may end up breaking code due to namespace collision.
**How should you import?**
-• Import the module under the module's namespace (Only import the name of the module, and names defined in the module can be used by prefixing the module's name)
+- Import the module under the module's namespace (Only import the name of the module, and names defined in the module can be used by prefixing the module's name)
```python
>>> import math
>>> math.sin(math.pi / 2)
```
-• Explicitly import certain names from the module
+- Explicitly import certain names from the module
```python
>>> from math import sin, pi
>>> sin(pi / 2)
diff --git a/bot/resources/tags/underscore.md b/bot/resources/tags/underscore.md
index 06c525538..3dc1ede01 100644
--- a/bot/resources/tags/underscore.md
+++ b/bot/resources/tags/underscore.md
@@ -4,24 +4,24 @@ embed:
title: "Meanings of underscores in identifier names"
---
-• `__name__`: Used to implement special behaviour, such as the `+` operator for classes with the `__add__` method. [More info](https://dbader.org/blog/python-dunder-methods)
-• `_name`: Indicates that a variable is "private" and should only be used by the class or module that defines it
-• `name_`: Used to avoid naming conflicts. For example, as `class` is a keyword, you could call a variable `class_` instead
-• `__name`: Causes the name to be "mangled" if defined inside a class. [More info](https://docs.python.org/3/tutorial/classes.html#private-variables)
+- `__name__`: Used to implement special behaviour, such as the `+` operator for classes with the `__add__` method. [More info](https://dbader.org/blog/python-dunder-methods)
+- `_name`: Indicates that a variable is "private" and should only be used by the class or module that defines it
+- `name_`: Used to avoid naming conflicts. For example, as `class` is a keyword, you could call a variable `class_` instead
+- `__name`: Causes the name to be "mangled" if defined inside a class. [More info](https://docs.python.org/3/tutorial/classes.html#private-variables)
A single underscore, **`_`**, has multiple uses:
-• To indicate an unused variable, e.g. in a for loop if you don't care which iteration you are on
+- To indicate an unused variable, e.g. in a for loop if you don't care which iteration you are on
```python
for _ in range(10):
print("Hello World")
```
-• In the REPL, where the previous result is assigned to the variable `_`
+- In the REPL, where the previous result is assigned to the variable `_`
```python
>>> 1 + 1 # Evaluated and stored in `_`
2
>>> _ + 3 # Take the previous result and add 3
5
```
-• In integer literals, e.g. `x = 1_500_000` can be written instead of `x = 1500000` to improve readability
+- In integer literals, e.g. `x = 1_500_000` can be written instead of `x = 1500000` to improve readability
See also ["Reserved classes of identifiers"](https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers) in the Python docs, and [this more detailed guide](https://dbader.org/blog/meaning-of-underscores-in-python).
diff --git a/bot/resources/tags/windows-path.md b/bot/resources/tags/windows-path.md
index af28c3eb0..ee86772bb 100644
--- a/bot/resources/tags/windows-path.md
+++ b/bot/resources/tags/windows-path.md
@@ -6,7 +6,7 @@ If you have installed Python but forgot to check the `Add Python to PATH` option
If you did not uncheck the option to install the `py launcher`, then you'll instead have a `py` command which can be used in the same way. If you want to be able to access your Python installation via the `python` command, then your best option is to re-install Python (remembering to tick the `Add Python to PATH` checkbox).
-You can pass any options to the Python interpreter, e.g. to install the `[numpy](https://pypi.org/project/numpy/)` module from PyPI you can run `py -3 -m pip install numpy` or `python -m pip install numpy`.
+You can pass any options to the Python interpreter, e.g. to install the [`numpy`](https://pypi.org/project/numpy/) module from PyPI you can run `py -3 -m pip install numpy` or `python -m pip install numpy`.
You can also access different versions of Python using the version flag of the `py` command, like so:
```
diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py
index f576b7f11..f803add1c 100644
--- a/bot/utils/__init__.py
+++ b/bot/utils/__init__.py
@@ -1,12 +1,8 @@
from bot.utils.helpers import CogABCMeta, find_nth_occurrence, has_lines, pad_base64
-from bot.utils.services import PasteTooLongError, PasteUploadError, send_to_paste_service
__all__ = [
"CogABCMeta",
"find_nth_occurrence",
"has_lines",
"pad_base64",
- "send_to_paste_service",
- "PasteUploadError",
- "PasteTooLongError",
]
diff --git a/bot/utils/caching.py b/bot/utils/caching.py
deleted file mode 100644
index 2d0e077ec..000000000
--- a/bot/utils/caching.py
+++ /dev/null
@@ -1,42 +0,0 @@
-import functools
-from collections import OrderedDict
-from collections.abc import Callable
-from typing import Any
-
-
-class AsyncCache:
- """
- LRU cache implementation for coroutines.
-
- Once the cache exceeds the maximum size, keys are deleted in FIFO order.
-
- An offset may be optionally provided to be applied to the coroutine's arguments when creating the cache key.
- """
-
- def __init__(self, max_size: int = 128):
- self._cache = OrderedDict()
- self._max_size = max_size
-
- def __call__(self, arg_offset: int = 0) -> Callable:
- """Decorator for async cache."""
-
- def decorator(function: Callable) -> Callable:
- """Define the async cache decorator."""
-
- @functools.wraps(function)
- async def wrapper(*args) -> Any:
- """Decorator wrapper for the caching logic."""
- key = args[arg_offset:]
-
- if key not in self._cache:
- if len(self._cache) > self._max_size:
- self._cache.popitem(last=False)
-
- self._cache[key] = await function(*args)
- return self._cache[key]
- return wrapper
- return decorator
-
- def clear(self) -> None:
- """Clear cache instance."""
- self._cache.clear()
diff --git a/bot/utils/channel.py b/bot/utils/channel.py
index 05afbe009..b819237c4 100644
--- a/bot/utils/channel.py
+++ b/bot/utils/channel.py
@@ -44,18 +44,3 @@ def is_staff_channel(channel: discord.TextChannel) -> bool:
def is_in_category(channel: discord.TextChannel, category_id: int) -> bool:
"""Return True if `channel` is within a category with `category_id`."""
return getattr(channel, "category_id", None) == category_id
-
-
-async def get_or_fetch_channel(
- channel_id: int
-) -> discord.abc.GuildChannel | discord.abc.PrivateChannel | discord.Thread:
- """Attempt to get or fetch a channel and return it."""
- log.trace(f"Getting the channel {channel_id}.")
-
- channel = bot.instance.get_channel(channel_id)
- if not channel:
- log.debug(f"Channel {channel_id} is not in cache; fetching from API.")
- channel = await bot.instance.fetch_channel(channel_id)
-
- log.trace(f"Channel #{channel} ({channel_id}) retrieved.")
- return channel
diff --git a/bot/utils/checks.py b/bot/utils/checks.py
index d4f9c3a27..e46c8209b 100644
--- a/bot/utils/checks.py
+++ b/bot/utils/checks.py
@@ -1,8 +1,16 @@
from collections.abc import Callable, Container, Iterable
from discord.ext.commands import (
- BucketType, CheckFailure, Cog, Command, CommandOnCooldown, Context, Cooldown, CooldownMapping, NoPrivateMessage,
- has_any_role
+ BucketType,
+ CheckFailure,
+ Cog,
+ Command,
+ CommandOnCooldown,
+ Context,
+ Cooldown,
+ CooldownMapping,
+ NoPrivateMessage,
+ has_any_role,
)
from bot import constants
diff --git a/bot/utils/members.py b/bot/utils/members.py
deleted file mode 100644
index 08ee78504..000000000
--- a/bot/utils/members.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import typing as t
-
-import discord
-
-from bot.log import get_logger
-
-log = get_logger(__name__)
-
-
-async def get_or_fetch_member(guild: discord.Guild, member_id: int) -> discord.Member | None:
- """
- Attempt to get a member from cache; on failure fetch from the API.
-
- Return `None` to indicate the member could not be found.
- """
- if member := guild.get_member(member_id):
- log.trace("%s retrieved from cache.", member)
- else:
- try:
- member = await guild.fetch_member(member_id)
- except discord.errors.NotFound:
- log.trace("Failed to fetch %d from API.", member_id)
- return None
- log.trace("%s fetched from API.", member)
- return member
-
-
-async def handle_role_change(
- member: discord.Member,
- coro: t.Callable[..., t.Coroutine],
- role: discord.Role
-) -> None:
- """
- Change `member`'s cooldown role via awaiting `coro` and handle errors.
-
- `coro` is intended to be `discord.Member.add_roles` or `discord.Member.remove_roles`.
- """
- try:
- await coro(role)
- except discord.NotFound:
- log.debug(f"Failed to change role for {member} ({member.id}): member not found")
- except discord.Forbidden:
- log.debug(
- f"Forbidden to change role for {member} ({member.id}); "
- f"possibly due to role hierarchy"
- )
- except discord.HTTPException as e:
- log.error(f"Failed to change role for {member} ({member.id}): {e.status} {e.code}")
diff --git a/bot/utils/messages.py b/bot/utils/messages.py
index 175d4d4b8..57bb2c8e3 100644
--- a/bot/utils/messages.py
+++ b/bot/utils/messages.py
@@ -1,4 +1,3 @@
-import asyncio
import random
import re
from collections.abc import Callable, Iterable, Sequence
@@ -102,7 +101,7 @@ async def wait_for_deletion(
try:
try:
await bot.instance.wait_for("reaction_add", check=check, timeout=timeout)
- except asyncio.TimeoutError:
+ except TimeoutError:
await message.clear_reactions()
else:
await message.delete()
@@ -204,21 +203,6 @@ async def count_unique_users_reaction(
return len(unique_users)
-async def pin_no_system_message(message: discord.Message) -> bool:
- """Pin the given message, wait a couple of seconds and try to delete the system message."""
- await message.pin()
-
- # Make sure that we give it enough time to deliver the message
- await asyncio.sleep(2)
- # Search for the system message in the last 10 messages
- async for historical_message in message.channel.history(limit=10):
- if historical_message.type == discord.MessageType.pins_add:
- await historical_message.delete()
- return True
-
- return False
-
-
def sub_clyde(username: str | None) -> str | None:
"""
Replace "e"/"E" in any "clyde" in `username` with a Cyrillic "е"/"Е" and return the new string.
diff --git a/bot/utils/modlog.py b/bot/utils/modlog.py
new file mode 100644
index 000000000..6a432e65e
--- /dev/null
+++ b/bot/utils/modlog.py
@@ -0,0 +1,69 @@
+from datetime import UTC, datetime
+
+import discord
+
+from bot.bot import Bot
+from bot.constants import Channels, Roles
+
+
+async def send_log_message(
+ bot: Bot,
+ icon_url: str | None,
+ colour: discord.Colour | int,
+ title: str | None,
+ text: str,
+ *,
+ thumbnail: str | discord.Asset | None = None,
+ channel_id: int = Channels.mod_log,
+ ping_everyone: bool = False,
+ files: list[discord.File] | None = None,
+ content: str | None = None,
+ additional_embeds: list[discord.Embed] | None = None,
+ timestamp_override: datetime | None = None,
+ footer: str | None = None,
+) -> discord.Message:
+ """Generate log embed and send to logging channel."""
+ await bot.wait_until_guild_available()
+ # Truncate string directly here to avoid removing newlines
+ embed = discord.Embed(
+ description=text[:4093] + "..." if len(text) > 4096 else text
+ )
+
+ if title and icon_url:
+ embed.set_author(name=title, icon_url=icon_url)
+ elif title:
+ raise ValueError("title cannot be set without icon_url")
+ elif icon_url:
+ raise ValueError("icon_url cannot be set without title")
+
+ embed.colour = colour
+ embed.timestamp = timestamp_override or datetime.now(tz=UTC)
+
+ if footer:
+ embed.set_footer(text=footer)
+
+ if thumbnail:
+ embed.set_thumbnail(url=thumbnail)
+
+ if ping_everyone:
+ if content:
+ content = f"<@&{Roles.moderators}> {content}"
+ else:
+ content = f"<@&{Roles.moderators}>"
+
+ # Truncate content to 2000 characters and append an ellipsis.
+ if content and len(content) > 2000:
+ content = content[:2000 - 3] + "..."
+
+ channel = bot.get_channel(channel_id)
+ log_message = await channel.send(
+ content=content,
+ embed=embed,
+ files=files
+ )
+
+ if additional_embeds:
+ for additional_embed in additional_embeds:
+ await channel.send(embed=additional_embed)
+
+ return log_message
diff --git a/bot/utils/services.py b/bot/utils/services.py
deleted file mode 100644
index a25548510..000000000
--- a/bot/utils/services.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from aiohttp import ClientConnectorError
-
-import bot
-from bot.constants import URLs
-from bot.log import get_logger
-
-log = get_logger(__name__)
-
-FAILED_REQUEST_ATTEMPTS = 3
-MAX_PASTE_LENGTH = 100_000
-
-
-class PasteUploadError(Exception):
- """Raised when an error is encountered uploading to the paste service."""
-
-
-class PasteTooLongError(Exception):
- """Raised when content is too large to upload to the paste service."""
-
-
-async def send_to_paste_service(contents: str, *, extension: str = "", max_length: int = MAX_PASTE_LENGTH) -> str:
- """
- Upload `contents` to the paste service.
-
- Add `extension` to the output URL. Use `max_length` to limit the allowed contents length
- to lower than the maximum allowed by the paste service.
-
- Raise `ValueError` if `max_length` is greater than the maximum allowed by the paste service.
- Raise `PasteTooLongError` if `contents` is too long to upload, and `PasteUploadError` if uploading fails.
-
- Return the generated URL with the extension.
- """
- if max_length > MAX_PASTE_LENGTH:
- raise ValueError(f"`max_length` must not be greater than {MAX_PASTE_LENGTH}")
-
- extension = extension and f".{extension}"
-
- contents_size = len(contents.encode())
- if contents_size > max_length:
- log.info("Contents too large to send to paste service.")
- raise PasteTooLongError(f"Contents of size {contents_size} greater than maximum size {max_length}")
-
- log.debug(f"Sending contents of size {contents_size} bytes to paste service.")
- paste_url = URLs.paste_service.format(key="documents")
- for attempt in range(1, FAILED_REQUEST_ATTEMPTS + 1):
- try:
- async with bot.instance.http_session.post(paste_url, data=contents) as response:
- response_json = await response.json()
- except ClientConnectorError:
- log.warning(
- f"Failed to connect to paste service at url {paste_url}, "
- f"trying again ({attempt}/{FAILED_REQUEST_ATTEMPTS})."
- )
- continue
- except Exception:
- log.exception(
- f"An unexpected error has occurred during handling of the request, "
- f"trying again ({attempt}/{FAILED_REQUEST_ATTEMPTS})."
- )
- continue
-
- if "message" in response_json:
- log.warning(
- f"Paste service returned error {response_json['message']} with status code {response.status}, "
- f"trying again ({attempt}/{FAILED_REQUEST_ATTEMPTS})."
- )
- continue
- if "key" in response_json:
- log.info(f"Successfully uploaded contents to paste service behind key {response_json['key']}.")
-
- paste_link = URLs.paste_service.format(key=response_json["key"]) + extension
-
- if extension == ".py":
- return paste_link
-
- return paste_link + "?noredirect"
-
- log.warning(
- f"Got unexpected JSON response from paste service: {response_json}\n"
- f"trying again ({attempt}/{FAILED_REQUEST_ATTEMPTS})."
- )
-
- raise PasteUploadError("Failed to upload contents to paste service")
diff --git a/botstrap.py b/botstrap.py
index c57a254a3..7a9d94d8b 100644
--- a/botstrap.py
+++ b/botstrap.py
@@ -176,7 +176,7 @@ with DiscordClient(guild_id=GUILD_ID) as discord_client:
all_roles = discord_client.get_all_roles()
- for role_name in _Roles.__fields__:
+ for role_name in _Roles.model_fields:
role_id = all_roles.get(role_name, None)
if not role_id:
@@ -209,7 +209,7 @@ with DiscordClient(guild_id=GUILD_ID) as discord_client:
python_help_channel_id = discord_client.create_forum_channel(python_help_channel_name, python_help_category_id)
all_channels[PYTHON_HELP_CHANNEL_NAME] = python_help_channel_id
- for channel_name in _Channels.__fields__:
+ for channel_name in _Channels.model_fields:
channel_id = all_channels.get(channel_name, None)
if not channel_id:
log.warning(
@@ -222,7 +222,7 @@ with DiscordClient(guild_id=GUILD_ID) as discord_client:
config_str += "\n#Categories\n"
- for category_name in _Categories.__fields__:
+ for category_name in _Categories.model_fields:
category_id = all_categories.get(category_name, None)
if not category_id:
log.warning(
diff --git a/poetry.lock b/poetry.lock
index 760ce7342..66f1b1cc8 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,14 +1,14 @@
-# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "aiodns"
-version = "3.0.0"
+version = "3.1.1"
description = "Simple DNS resolver for asyncio"
optional = false
python-versions = "*"
files = [
- {file = "aiodns-3.0.0-py3-none-any.whl", hash = "sha256:2b19bc5f97e5c936638d28e665923c093d8af2bf3aa88d35c43417fa25d136a2"},
- {file = "aiodns-3.0.0.tar.gz", hash = "sha256:946bdfabe743fceeeb093c8a010f5d1645f708a241be849e17edfb0e49e08cd6"},
+ {file = "aiodns-3.1.1-py3-none-any.whl", hash = "sha256:a387b63da4ced6aad35b1dda2d09620ad608a1c7c0fb71efa07ebb4cd511928d"},
+ {file = "aiodns-3.1.1.tar.gz", hash = "sha256:1073eac48185f7a4150cad7f96a5192d6911f12b4fb894de80a088508c9b3a99"},
]
[package.dependencies]
@@ -16,111 +16,98 @@ pycares = ">=4.0.0"
[[package]]
name = "aiohttp"
-version = "3.8.4"
+version = "3.9.3"
description = "Async http client/server framework (asyncio)"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5ce45967538fb747370308d3145aa68a074bdecb4f3a300869590f725ced69c1"},
- {file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b744c33b6f14ca26b7544e8d8aadff6b765a80ad6164fb1a430bbadd593dfb1a"},
- {file = "aiohttp-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a45865451439eb320784918617ba54b7a377e3501fb70402ab84d38c2cd891b"},
- {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86d42d7cba1cec432d47ab13b6637bee393a10f664c425ea7b305d1301ca1a3"},
- {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee3c36df21b5714d49fc4580247947aa64bcbe2939d1b77b4c8dcb8f6c9faecc"},
- {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:176a64b24c0935869d5bbc4c96e82f89f643bcdf08ec947701b9dbb3c956b7dd"},
- {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c844fd628851c0bc309f3c801b3a3d58ce430b2ce5b359cd918a5a76d0b20cb5"},
- {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5393fb786a9e23e4799fec788e7e735de18052f83682ce2dfcabaf1c00c2c08e"},
- {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e4b09863aae0dc965c3ef36500d891a3ff495a2ea9ae9171e4519963c12ceefd"},
- {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:adfbc22e87365a6e564c804c58fc44ff7727deea782d175c33602737b7feadb6"},
- {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:147ae376f14b55f4f3c2b118b95be50a369b89b38a971e80a17c3fd623f280c9"},
- {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:eafb3e874816ebe2a92f5e155f17260034c8c341dad1df25672fb710627c6949"},
- {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6cc15d58053c76eacac5fa9152d7d84b8d67b3fde92709195cb984cfb3475ea"},
- {file = "aiohttp-3.8.4-cp310-cp310-win32.whl", hash = "sha256:59f029a5f6e2d679296db7bee982bb3d20c088e52a2977e3175faf31d6fb75d1"},
- {file = "aiohttp-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:fe7ba4a51f33ab275515f66b0a236bcde4fb5561498fe8f898d4e549b2e4509f"},
- {file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d8ef1a630519a26d6760bc695842579cb09e373c5f227a21b67dc3eb16cfea4"},
- {file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b3f2e06a512e94722886c0827bee9807c86a9f698fac6b3aee841fab49bbfb4"},
- {file = "aiohttp-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a80464982d41b1fbfe3154e440ba4904b71c1a53e9cd584098cd41efdb188ef"},
- {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b631e26df63e52f7cce0cce6507b7a7f1bc9b0c501fcde69742130b32e8782f"},
- {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f43255086fe25e36fd5ed8f2ee47477408a73ef00e804cb2b5cba4bf2ac7f5e"},
- {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d347a172f866cd1d93126d9b239fcbe682acb39b48ee0873c73c933dd23bd0f"},
- {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3fec6a4cb5551721cdd70473eb009d90935b4063acc5f40905d40ecfea23e05"},
- {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80a37fe8f7c1e6ce8f2d9c411676e4bc633a8462844e38f46156d07a7d401654"},
- {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d1e6a862b76f34395a985b3cd39a0d949ca80a70b6ebdea37d3ab39ceea6698a"},
- {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cd468460eefef601ece4428d3cf4562459157c0f6523db89365202c31b6daebb"},
- {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:618c901dd3aad4ace71dfa0f5e82e88b46ef57e3239fc7027773cb6d4ed53531"},
- {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:652b1bff4f15f6287550b4670546a2947f2a4575b6c6dff7760eafb22eacbf0b"},
- {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80575ba9377c5171407a06d0196b2310b679dc752d02a1fcaa2bc20b235dbf24"},
- {file = "aiohttp-3.8.4-cp311-cp311-win32.whl", hash = "sha256:bbcf1a76cf6f6dacf2c7f4d2ebd411438c275faa1dc0c68e46eb84eebd05dd7d"},
- {file = "aiohttp-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:6e74dd54f7239fcffe07913ff8b964e28b712f09846e20de78676ce2a3dc0bfc"},
- {file = "aiohttp-3.8.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:880e15bb6dad90549b43f796b391cfffd7af373f4646784795e20d92606b7a51"},
- {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb96fa6b56bb536c42d6a4a87dfca570ff8e52de2d63cabebfd6fb67049c34b6"},
- {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a6cadebe132e90cefa77e45f2d2f1a4b2ce5c6b1bfc1656c1ddafcfe4ba8131"},
- {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f352b62b45dff37b55ddd7b9c0c8672c4dd2eb9c0f9c11d395075a84e2c40f75"},
- {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ab43061a0c81198d88f39aaf90dae9a7744620978f7ef3e3708339b8ed2ef01"},
- {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9cb1565a7ad52e096a6988e2ee0397f72fe056dadf75d17fa6b5aebaea05622"},
- {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:1b3ea7edd2d24538959c1c1abf97c744d879d4e541d38305f9bd7d9b10c9ec41"},
- {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7c7837fe8037e96b6dd5cfcf47263c1620a9d332a87ec06a6ca4564e56bd0f36"},
- {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3b90467ebc3d9fa5b0f9b6489dfb2c304a1db7b9946fa92aa76a831b9d587e99"},
- {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:cab9401de3ea52b4b4c6971db5fb5c999bd4260898af972bf23de1c6b5dd9d71"},
- {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d1f9282c5f2b5e241034a009779e7b2a1aa045f667ff521e7948ea9b56e0c5ff"},
- {file = "aiohttp-3.8.4-cp36-cp36m-win32.whl", hash = "sha256:5e14f25765a578a0a634d5f0cd1e2c3f53964553a00347998dfdf96b8137f777"},
- {file = "aiohttp-3.8.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4c745b109057e7e5f1848c689ee4fb3a016c8d4d92da52b312f8a509f83aa05e"},
- {file = "aiohttp-3.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:aede4df4eeb926c8fa70de46c340a1bc2c6079e1c40ccf7b0eae1313ffd33519"},
- {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ddaae3f3d32fc2cb4c53fab020b69a05c8ab1f02e0e59665c6f7a0d3a5be54f"},
- {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4eb3b82ca349cf6fadcdc7abcc8b3a50ab74a62e9113ab7a8ebc268aad35bb9"},
- {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bcb89336efa095ea21b30f9e686763f2be4478f1b0a616969551982c4ee4c3b"},
- {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c08e8ed6fa3d477e501ec9db169bfac8140e830aa372d77e4a43084d8dd91ab"},
- {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6cd05ea06daca6ad6a4ca3ba7fe7dc5b5de063ff4daec6170ec0f9979f6c332"},
- {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7a00a9ed8d6e725b55ef98b1b35c88013245f35f68b1b12c5cd4100dddac333"},
- {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:de04b491d0e5007ee1b63a309956eaed959a49f5bb4e84b26c8f5d49de140fa9"},
- {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:40653609b3bf50611356e6b6554e3a331f6879fa7116f3959b20e3528783e699"},
- {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dbf3a08a06b3f433013c143ebd72c15cac33d2914b8ea4bea7ac2c23578815d6"},
- {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854f422ac44af92bfe172d8e73229c270dc09b96535e8a548f99c84f82dde241"},
- {file = "aiohttp-3.8.4-cp37-cp37m-win32.whl", hash = "sha256:aeb29c84bb53a84b1a81c6c09d24cf33bb8432cc5c39979021cc0f98c1292a1a"},
- {file = "aiohttp-3.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:db3fc6120bce9f446d13b1b834ea5b15341ca9ff3f335e4a951a6ead31105480"},
- {file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fabb87dd8850ef0f7fe2b366d44b77d7e6fa2ea87861ab3844da99291e81e60f"},
- {file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91f6d540163f90bbaef9387e65f18f73ffd7c79f5225ac3d3f61df7b0d01ad15"},
- {file = "aiohttp-3.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d265f09a75a79a788237d7f9054f929ced2e69eb0bb79de3798c468d8a90f945"},
- {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d89efa095ca7d442a6d0cbc755f9e08190ba40069b235c9886a8763b03785da"},
- {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dac314662f4e2aa5009977b652d9b8db7121b46c38f2073bfeed9f4049732cd"},
- {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe11310ae1e4cd560035598c3f29d86cef39a83d244c7466f95c27ae04850f10"},
- {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ddb2a2026c3f6a68c3998a6c47ab6795e4127315d2e35a09997da21865757f8"},
- {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e75b89ac3bd27d2d043b234aa7b734c38ba1b0e43f07787130a0ecac1e12228a"},
- {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6e601588f2b502c93c30cd5a45bfc665faaf37bbe835b7cfd461753068232074"},
- {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a5d794d1ae64e7753e405ba58e08fcfa73e3fad93ef9b7e31112ef3c9a0efb52"},
- {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a1f4689c9a1462f3df0a1f7e797791cd6b124ddbee2b570d34e7f38ade0e2c71"},
- {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3032dcb1c35bc330134a5b8a5d4f68c1a87252dfc6e1262c65a7e30e62298275"},
- {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8189c56eb0ddbb95bfadb8f60ea1b22fcfa659396ea36f6adcc521213cd7b44d"},
- {file = "aiohttp-3.8.4-cp38-cp38-win32.whl", hash = "sha256:33587f26dcee66efb2fff3c177547bd0449ab7edf1b73a7f5dea1e38609a0c54"},
- {file = "aiohttp-3.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:e595432ac259af2d4630008bf638873d69346372d38255774c0e286951e8b79f"},
- {file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a7bdf9e57126dc345b683c3632e8ba317c31d2a41acd5800c10640387d193ed"},
- {file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:22f6eab15b6db242499a16de87939a342f5a950ad0abaf1532038e2ce7d31567"},
- {file = "aiohttp-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7235604476a76ef249bd64cb8274ed24ccf6995c4a8b51a237005ee7a57e8643"},
- {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea9eb976ffdd79d0e893869cfe179a8f60f152d42cb64622fca418cd9b18dc2a"},
- {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92c0cea74a2a81c4c76b62ea1cac163ecb20fb3ba3a75c909b9fa71b4ad493cf"},
- {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:493f5bc2f8307286b7799c6d899d388bbaa7dfa6c4caf4f97ef7521b9cb13719"},
- {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a63f03189a6fa7c900226e3ef5ba4d3bd047e18f445e69adbd65af433add5a2"},
- {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10c8cefcff98fd9168cdd86c4da8b84baaa90bf2da2269c6161984e6737bf23e"},
- {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bca5f24726e2919de94f047739d0a4fc01372801a3672708260546aa2601bf57"},
- {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:03baa76b730e4e15a45f81dfe29a8d910314143414e528737f8589ec60cf7391"},
- {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8c29c77cc57e40f84acef9bfb904373a4e89a4e8b74e71aa8075c021ec9078c2"},
- {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:03543dcf98a6619254b409be2d22b51f21ec66272be4ebda7b04e6412e4b2e14"},
- {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17b79c2963db82086229012cff93ea55196ed31f6493bb1ccd2c62f1724324e4"},
- {file = "aiohttp-3.8.4-cp39-cp39-win32.whl", hash = "sha256:34ce9f93a4a68d1272d26030655dd1b58ff727b3ed2a33d80ec433561b03d67a"},
- {file = "aiohttp-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:41a86a69bb63bb2fc3dc9ad5ea9f10f1c9c8e282b471931be0268ddd09430b04"},
- {file = "aiohttp-3.8.4.tar.gz", hash = "sha256:bf2e1a9162c1e441bf805a1fd166e249d574ca04e03b34f97e2928769e91ab5c"},
+ {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"},
+ {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"},
+ {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"},
+ {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"},
+ {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"},
+ {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"},
+ {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"},
+ {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"},
+ {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"},
+ {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"},
+ {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"},
+ {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"},
+ {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"},
+ {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"},
+ {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"},
+ {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"},
+ {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"},
+ {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"},
+ {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"},
+ {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"},
+ {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"},
+ {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"},
+ {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"},
+ {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"},
+ {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"},
+ {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"},
+ {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"},
+ {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"},
+ {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"},
+ {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"},
+ {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"},
+ {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"},
+ {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"},
+ {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"},
+ {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"},
+ {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"},
+ {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"},
+ {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"},
+ {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"},
+ {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"},
+ {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"},
+ {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"},
+ {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"},
+ {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"},
+ {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"},
+ {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"},
+ {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"},
+ {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"},
+ {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"},
+ {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"},
+ {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"},
+ {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"},
+ {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"},
+ {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"},
+ {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"},
+ {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"},
+ {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"},
+ {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"},
+ {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"},
+ {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"},
+ {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"},
+ {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"},
+ {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"},
+ {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"},
+ {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"},
+ {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"},
+ {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"},
+ {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"},
+ {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"},
+ {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"},
+ {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"},
+ {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"},
+ {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"},
+ {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"},
+ {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"},
+ {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"},
]
[package.dependencies]
aiosignal = ">=1.1.2"
-async-timeout = ">=4.0.0a3,<5.0"
attrs = ">=17.3.0"
-charset-normalizer = ">=2.0,<4.0"
frozenlist = ">=1.1.1"
multidict = ">=4.5,<7.0"
yarl = ">=1.0,<2.0"
[package.extras]
-speedups = ["Brotli", "aiodns", "cchardet"]
+speedups = ["Brotli", "aiodns", "brotlicffi"]
[[package]]
name = "aiosignal"
@@ -137,14 +124,25 @@ files = [
frozenlist = ">=1.1.0"
[[package]]
+name = "annotated-types"
+version = "0.6.0"
+description = "Reusable constraint types to use with typing.Annotated"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"},
+ {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"},
+]
+
+[[package]]
name = "anyio"
-version = "3.7.0"
+version = "4.2.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "anyio-3.7.0-py3-none-any.whl", hash = "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"},
- {file = "anyio-3.7.0.tar.gz", hash = "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce"},
+ {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"},
+ {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"},
]
[package.dependencies]
@@ -152,23 +150,28 @@ idna = ">=2.8"
sniffio = ">=1.1"
[package.extras]
-doc = ["Sphinx (>=6.1.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme", "sphinxcontrib-jquery"]
-test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
-trio = ["trio (<0.22)"]
+doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
+trio = ["trio (>=0.23)"]
[[package]]
name = "arrow"
-version = "1.2.3"
+version = "1.3.0"
description = "Better dates & times for Python"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
files = [
- {file = "arrow-1.2.3-py3-none-any.whl", hash = "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2"},
- {file = "arrow-1.2.3.tar.gz", hash = "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1"},
+ {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"},
+ {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"},
]
[package.dependencies]
python-dateutil = ">=2.7.0"
+types-python-dateutil = ">=2.8.10"
+
+[package.extras]
+doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"]
+test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"]
[[package]]
name = "async-rediscache"
@@ -190,133 +193,125 @@ fakeredis = ["fakeredis[lua] (>=1.7.1)"]
[[package]]
name = "async-timeout"
-version = "4.0.2"
+version = "4.0.3"
description = "Timeout context manager for asyncio programs"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"},
- {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
+ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
+ {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
]
[[package]]
name = "attrs"
-version = "23.1.0"
+version = "23.2.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.7"
files = [
- {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"},
- {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"},
+ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
+ {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
]
[package.extras]
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
-dev = ["attrs[docs,tests]", "pre-commit"]
+dev = ["attrs[tests]", "pre-commit"]
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
tests = ["attrs[tests-no-zope]", "zope-interface"]
-tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
+tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
[[package]]
name = "beautifulsoup4"
-version = "4.12.2"
+version = "4.12.3"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.6.0"
files = [
- {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
- {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"},
+ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
+ {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
]
[package.dependencies]
soupsieve = ">1.2"
[package.extras]
+cchardet = ["cchardet"]
+chardet = ["chardet"]
+charset-normalizer = ["charset-normalizer"]
html5lib = ["html5lib"]
lxml = ["lxml"]
[[package]]
name = "certifi"
-version = "2023.5.7"
+version = "2023.11.17"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
- {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"},
- {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"},
+ {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
+ {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
]
[[package]]
name = "cffi"
-version = "1.15.1"
+version = "1.16.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
-python-versions = "*"
+python-versions = ">=3.8"
files = [
- {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"},
- {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"},
- {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"},
- {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"},
- {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"},
- {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"},
- {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"},
- {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"},
- {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"},
- {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"},
- {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"},
- {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"},
- {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"},
- {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"},
- {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"},
- {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"},
- {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"},
- {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"},
- {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"},
- {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"},
- {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"},
- {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"},
- {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"},
- {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"},
- {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"},
- {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"},
- {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"},
- {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"},
- {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"},
- {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"},
- {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"},
- {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
- {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
+ {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
+ {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
+ {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
+ {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
+ {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
+ {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
+ {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
+ {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
+ {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
+ {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
+ {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
+ {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
+ {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
+ {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
+ {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
]
[package.dependencies]
@@ -324,97 +319,112 @@ pycparser = "*"
[[package]]
name = "cfgv"
-version = "3.3.1"
+version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
optional = false
-python-versions = ">=3.6.1"
+python-versions = ">=3.8"
files = [
- {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
- {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
+ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
+ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
]
[[package]]
name = "charset-normalizer"
-version = "3.1.0"
+version = "3.3.2"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7.0"
files = [
- {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"},
- {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"},
- {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"},
- {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"},
- {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"},
- {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"},
- {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"},
+ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
+ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
]
[[package]]
@@ -447,71 +457,63 @@ cron = ["capturer (>=2.4)"]
[[package]]
name = "coverage"
-version = "7.2.7"
+version = "7.4.3"
description = "Code coverage measurement for Python"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"},
- {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"},
- {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"},
- {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"},
- {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"},
- {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"},
- {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"},
- {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"},
- {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"},
- {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"},
- {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"},
- {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"},
- {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"},
- {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"},
- {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"},
- {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"},
- {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"},
- {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"},
- {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"},
- {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"},
- {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"},
- {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"},
- {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"},
- {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"},
+ {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"},
+ {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"},
+ {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"},
+ {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"},
+ {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"},
+ {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"},
+ {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"},
+ {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"},
+ {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"},
+ {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"},
+ {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"},
+ {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"},
+ {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"},
+ {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"},
+ {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"},
+ {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"},
+ {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"},
+ {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"},
+ {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"},
+ {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"},
+ {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"},
+ {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"},
+ {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"},
+ {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"},
+ {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"},
+ {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"},
+ {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"},
+ {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"},
+ {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"},
+ {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"},
+ {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"},
+ {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"},
]
[package.dependencies]
@@ -522,31 +524,31 @@ toml = ["tomli"]
[[package]]
name = "deepdiff"
-version = "6.3.0"
+version = "6.7.1"
description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other."
optional = false
python-versions = ">=3.7"
files = [
- {file = "deepdiff-6.3.0-py3-none-any.whl", hash = "sha256:15838bd1cbd046ce15ed0c41e837cd04aff6b3e169c5e06fca69d7aa11615ceb"},
- {file = "deepdiff-6.3.0.tar.gz", hash = "sha256:6a3bf1e7228ac5c71ca2ec43505ca0a743ff54ec77aa08d7db22de6bc7b2b644"},
+ {file = "deepdiff-6.7.1-py3-none-any.whl", hash = "sha256:58396bb7a863cbb4ed5193f548c56f18218060362311aa1dc36397b2f25108bd"},
+ {file = "deepdiff-6.7.1.tar.gz", hash = "sha256:b367e6fa6caac1c9f500adc79ada1b5b1242c50d5f716a1a4362030197847d30"},
]
[package.dependencies]
ordered-set = ">=4.0.2,<4.2.0"
[package.extras]
-cli = ["click (==8.1.3)", "pyyaml (==6.0)"]
+cli = ["click (==8.1.3)", "pyyaml (==6.0.1)"]
optimize = ["orjson"]
[[package]]
name = "discord-py"
-version = "2.3.0"
+version = "2.3.2"
description = "A Python wrapper for the Discord API"
optional = false
python-versions = ">=3.8.0"
files = [
- {file = "discord.py-2.3.0-py3-none-any.whl", hash = "sha256:3e9498967822ad4499f8f72deb9173f942d9827d92b6e4e4e7732d24f78f300c"},
- {file = "discord.py-2.3.0.tar.gz", hash = "sha256:c71066a30f037d069218e59092505c3e8945fd175e396a80748056d989756806"},
+ {file = "discord.py-2.3.2-py3-none-any.whl", hash = "sha256:9da4679fc3cb10c64b388284700dc998663e0e57328283bbfcfc2525ec5960a6"},
+ {file = "discord.py-2.3.2.tar.gz", hash = "sha256:4560f70f2eddba7e83370ecebd237ac09fbb4980dc66507482b0c0e5b8f76b9c"},
]
[package.dependencies]
@@ -560,23 +562,24 @@ voice = ["PyNaCl (>=1.3.0,<1.6)"]
[[package]]
name = "distlib"
-version = "0.3.6"
+version = "0.3.8"
description = "Distribution utilities"
optional = false
python-versions = "*"
files = [
- {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
- {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"},
+ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
+ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
]
[[package]]
name = "emoji"
-version = "2.6.0"
+version = "2.10.1"
description = "Emoji for Python"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
- {file = "emoji-2.6.0.tar.gz", hash = "sha256:39ad95c9ba270cdfbd141d20c2ebcfc4e295d010b605de66908a098a3ba63a3c"},
+ {file = "emoji-2.10.1-py2.py3-none-any.whl", hash = "sha256:11fb369ea79d20c14efa4362c732d67126df294a7959a2c98bfd7447c12a218e"},
+ {file = "emoji-2.10.1.tar.gz", hash = "sha256:16287283518fb7141bde00198f9ffff4e1c1cb570efb68b2f1ec50975c3a581d"},
]
[package.extras]
@@ -584,47 +587,50 @@ dev = ["coverage", "coveralls", "pytest"]
[[package]]
name = "execnet"
-version = "1.9.0"
+version = "2.0.2"
description = "execnet: rapid multi-Python deployment"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.7"
files = [
- {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"},
- {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"},
+ {file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"},
+ {file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"},
]
[package.extras]
-testing = ["pre-commit"]
+testing = ["hatch", "pre-commit", "pytest", "tox"]
[[package]]
name = "fakeredis"
-version = "2.15.0"
+version = "2.21.0"
description = "Python implementation of redis API, can be used for testing purposes."
optional = false
python-versions = ">=3.7,<4.0"
files = [
- {file = "fakeredis-2.15.0-py3-none-any.whl", hash = "sha256:221edb25c0a38d0fb7c7b91f52c4e2beb6fbd3369899a8ab59328c3e432b062a"},
- {file = "fakeredis-2.15.0.tar.gz", hash = "sha256:519ab3585ac0c652f04836209b9d835f33879560c7451c2abfc59a663cfaffbc"},
+ {file = "fakeredis-2.21.0-py3-none-any.whl", hash = "sha256:dcea37c57a1aaf39bed1227aea20de052fb19dc030ccac3f8a73324b2ec90cee"},
+ {file = "fakeredis-2.21.0.tar.gz", hash = "sha256:1b3ff9c068e39c43725f2373b105228cd03e6a50fc79a5698e852b7601b1201b"},
]
[package.dependencies]
-lupa = {version = ">=1.14,<2.0", optional = true, markers = "extra == \"lua\""}
+lupa = {version = ">=1.14,<3.0", optional = true, markers = "extra == \"lua\""}
redis = ">=4"
-sortedcontainers = ">=2.4,<3.0"
+sortedcontainers = ">=2,<3"
[package.extras]
-json = ["jsonpath-ng (>=1.5,<2.0)"]
-lua = ["lupa (>=1.14,<2.0)"]
+bf = ["pyprobables (>=0.6,<0.7)"]
+cf = ["pyprobables (>=0.6,<0.7)"]
+json = ["jsonpath-ng (>=1.6,<2.0)"]
+lua = ["lupa (>=1.14,<3.0)"]
+probabilistic = ["pyprobables (>=0.6,<0.7)"]
[[package]]
name = "feedparser"
-version = "6.0.10"
+version = "6.0.11"
description = "Universal feed parser, handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds"
optional = false
python-versions = ">=3.6"
files = [
- {file = "feedparser-6.0.10-py3-none-any.whl", hash = "sha256:79c257d526d13b944e965f6095700587f27388e50ea16fd245babe4dfae7024f"},
- {file = "feedparser-6.0.10.tar.gz", hash = "sha256:27da485f4637ce7163cdeab13a80312b93b7d0c1b775bef4a47629a3110bca51"},
+ {file = "feedparser-6.0.11-py3-none-any.whl", hash = "sha256:0be7ee7b395572b19ebeb1d6aafb0028dee11169f1c934e0ed67d54992f4ad45"},
+ {file = "feedparser-6.0.11.tar.gz", hash = "sha256:c9d0407b64c6f2a065d0ebb292c2b35c01050cc0dc33757461aaabdc4c4184d5"},
]
[package.dependencies]
@@ -632,100 +638,104 @@ sgmllib3k = "*"
[[package]]
name = "filelock"
-version = "3.12.2"
+version = "3.13.1"
description = "A platform independent file lock."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"},
- {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"},
+ {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
+ {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
]
[package.extras]
-docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
-testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
+docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
+typing = ["typing-extensions (>=4.8)"]
[[package]]
name = "frozenlist"
-version = "1.3.3"
+version = "1.4.1"
description = "A list-like structure which implements collections.abc.MutableSequence"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"},
- {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"},
- {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"},
- {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"},
- {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"},
- {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"},
- {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"},
- {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"},
- {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"},
- {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"},
- {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"},
- {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"},
- {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"},
- {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"},
- {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"},
- {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"},
- {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"},
- {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"},
- {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"},
- {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"},
- {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"},
- {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"},
- {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"},
- {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"},
- {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"},
- {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"},
- {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"},
- {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"},
- {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"},
- {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"},
- {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"},
- {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"},
- {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"},
- {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"},
- {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"},
- {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"},
- {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"},
- {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"},
- {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"},
- {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"},
- {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"},
- {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"},
- {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"},
- {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"},
- {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"},
- {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"},
- {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"},
- {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"},
- {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"},
- {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"},
- {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"},
- {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"},
- {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"},
- {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"},
- {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"},
- {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"},
- {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"},
- {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"},
- {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"},
- {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"},
- {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"},
- {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"},
- {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"},
- {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"},
- {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"},
- {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"},
- {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"},
- {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"},
- {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"},
- {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"},
- {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"},
- {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"},
- {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"},
- {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"},
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"},
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"},
+ {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"},
+ {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"},
+ {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"},
+ {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"},
+ {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"},
+ {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"},
+ {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"},
+ {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"},
+ {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"},
+ {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"},
+ {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"},
+ {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"},
+ {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"},
+ {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"},
+ {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"},
+ {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"},
+ {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"},
+ {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"},
+ {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"},
+ {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"},
+ {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"},
+ {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"},
+ {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"},
+ {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"},
+ {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"},
+ {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"},
+ {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"},
+ {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"},
+ {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"},
]
[[package]]
@@ -741,39 +751,40 @@ files = [
[[package]]
name = "httpcore"
-version = "0.17.2"
+version = "1.0.2"
description = "A minimal low-level HTTP client."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "httpcore-0.17.2-py3-none-any.whl", hash = "sha256:5581b9c12379c4288fe70f43c710d16060c10080617001e6b22a3b6dbcbefd36"},
- {file = "httpcore-0.17.2.tar.gz", hash = "sha256:125f8375ab60036db632f34f4b627a9ad085048eef7cb7d2616fea0f739f98af"},
+ {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"},
+ {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"},
]
[package.dependencies]
-anyio = ">=3.0,<5.0"
certifi = "*"
h11 = ">=0.13,<0.15"
-sniffio = "==1.*"
[package.extras]
+asyncio = ["anyio (>=4.0,<5.0)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
+trio = ["trio (>=0.22.0,<0.23.0)"]
[[package]]
name = "httpx"
-version = "0.24.1"
+version = "0.27.0"
description = "The next generation HTTP client."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "httpx-0.24.1-py3-none-any.whl", hash = "sha256:06781eb9ac53cde990577af654bd990a4949de37a28bdb4a230d434f3a30b9bd"},
- {file = "httpx-0.24.1.tar.gz", hash = "sha256:5853a43053df830c20f8110c5e69fe44d035d850b2dfe795e196f00fdb774bdd"},
+ {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
+ {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
]
[package.dependencies]
+anyio = "*"
certifi = "*"
-httpcore = ">=0.15.0,<0.18.0"
+httpcore = "==1.*"
idna = "*"
sniffio = "*"
@@ -799,13 +810,13 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve
[[package]]
name = "identify"
-version = "2.5.24"
+version = "2.5.33"
description = "File identification library for Python"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"},
- {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"},
+ {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"},
+ {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"},
]
[package.extras]
@@ -813,13 +824,13 @@ license = ["ukkonen"]
[[package]]
name = "idna"
-version = "3.4"
+version = "3.6"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
- {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
- {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
+ {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
]
[[package]]
@@ -834,197 +845,187 @@ files = [
]
[[package]]
-name = "isort"
-version = "5.12.0"
-description = "A Python utility / library to sort Python imports."
-optional = false
-python-versions = ">=3.8.0"
-files = [
- {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)"]
-pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
-plugins = ["setuptools"]
-requirements-deprecated-finder = ["pip-api", "pipreqs"]
-
-[[package]]
name = "lupa"
-version = "1.14.1"
+version = "2.0"
description = "Python wrapper around Lua and LuaJIT"
optional = false
python-versions = "*"
files = [
- {file = "lupa-1.14.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:20b486cda76ff141cfb5f28df9c757224c9ed91e78c5242d402d2e9cb699d464"},
- {file = "lupa-1.14.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c685143b18c79a3a1fa25a4cc774a87b5a61c606f249bcf824d125d8accb6b2c"},
- {file = "lupa-1.14.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3865f9dbe9a84bd6a471250e52068aaf1147f206a51905fb6d93e1db9efb00ee"},
- {file = "lupa-1.14.1-cp27-cp27m-win32.whl", hash = "sha256:2dacdddd5e28c6f5fd96a46c868ec5c34b0fad1ec7235b5bbb56f06183a37f20"},
- {file = "lupa-1.14.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e754cbc6cacc9bca6ff2b39025e9659a2098420639d214054b06b466825f4470"},
- {file = "lupa-1.14.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e36f3eb70705841bce9c15e12bc6fc3b2f4f68a41ba0e4af303b22fc4d8667c"},
- {file = "lupa-1.14.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0aac06098d46729edd2d04e80b55d9d310e902f042f27521308df77cb1ba0191"},
- {file = "lupa-1.14.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:9706a192339efa1a6b7d806389572a669dd9ae2250469ff1ce13f684085af0b4"},
- {file = "lupa-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d688a35f7fe614720ed7b820cbb739b37eff577a764c2003e229c2a752201cea"},
- {file = "lupa-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:36d888bd42589ecad21a5fb957b46bc799640d18eff2fd0c47a79ffb4a1b286c"},
- {file = "lupa-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:0423acd739cf25dbdbf1e33a0aa8026f35e1edea0573db63d156f14a082d77c8"},
- {file = "lupa-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7068ae0d6a1a35ea8718ef6e103955c1ee143181bf0684604a76acc67f69de55"},
- {file = "lupa-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5fef8b755591f0466438ad0a3e92ecb21dd6bb1f05d0215139b6ff8c87b2ce65"},
- {file = "lupa-1.14.1-cp310-cp310-win32.whl", hash = "sha256:4a44e1fd0e9f4a546fbddd2e0fd913c823c9ac58a5f3160fb4f9109f633cb027"},
- {file = "lupa-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:b83100cd7b48a7ca85dda4e9a6a5e7bc3312691e7f94c6a78d1f9a48a86a7fec"},
- {file = "lupa-1.14.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:1b8bda50c61c98ff9bb41d1f4934640c323e9f1539021810016a2eae25a66c3d"},
- {file = "lupa-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa1449aa1ab46c557344867496dee324b47ede0c41643df8f392b00262d21b12"},
- {file = "lupa-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a17ebf91b3aa1c5c36661e34c9cf10e04bb4cc00076e8b966f86749647162050"},
- {file = "lupa-1.14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:b1d9cfa469e7a2ad7e9a00fea7196b0022aa52f43a2043c2e0be92122e7bcfe8"},
- {file = "lupa-1.14.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bc4f5e84aee0d567aa2e116ff6844d06086ef7404d5102807e59af5ce9daf3c0"},
- {file = "lupa-1.14.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:40cf2eb90087dfe8ee002740469f2c4c5230d5e7d10ffb676602066d2f9b1ac9"},
- {file = "lupa-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:63a27c38295aa971730795941270fff2ce65576f68ec63cb3ecb90d7a4526d03"},
- {file = "lupa-1.14.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:457330e7a5456c4415fc6d38822036bd4cff214f9d8f7906200f6b588f1b2932"},
- {file = "lupa-1.14.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d61fb507a36e18dc68f2d9e9e2ea19e1114b1a5e578a36f18e9be7a17d2931d1"},
- {file = "lupa-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:f26b73d10130ad73e07d45dfe9b7c3833e3a2aa1871a4ecf5ce2dc1abeeae74d"},
- {file = "lupa-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:297d801ba8e4e882b295c25d92f1634dde5e76d07ec6c35b13882401248c485d"},
- {file = "lupa-1.14.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:c8bddd22eaeea0ce9d302b390d8bc606f003bf6c51be68e8b007504433b91280"},
- {file = "lupa-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1661c890861cf0f7002d7a7e00f50c885577954c2d85a7173b218d3228fa3869"},
- {file = "lupa-1.14.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:2ee480d31555f00f8bf97dd949c596508bd60264cff1921a3797a03dd369e8cd"},
- {file = "lupa-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:1ff93560c2546d7627ab2f95b5e88f000705db70a3d6041ac29d050f094f2a35"},
- {file = "lupa-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:47f1459e2c98480c291ae3b70688d762f82dbb197ef121d529aa2c4e8bab1ba3"},
- {file = "lupa-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8986dba002346505ee44c78303339c97a346b883015d5cf3aaa0d76d3b952744"},
- {file = "lupa-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8912459fddf691e70f2add799a128822bae725826cfb86f69720a38bdfa42410"},
- {file = "lupa-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:9b9d1b98391959ae531bbb8df7559ac2c408fcbd33721921b6a05fd6414161e0"},
- {file = "lupa-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:61ff409040fa3a6c358b7274c10e556ba22afeb3470f8d23cd0a6bf418fb30c9"},
- {file = "lupa-1.14.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:350ba2218eea800898854b02753dc0c9cfe83db315b30c0dc10ab17493f0321a"},
- {file = "lupa-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:46dcbc0eae63899468686bb1dfc2fe4ed21fe06f69416113f039d88aab18f5dc"},
- {file = "lupa-1.14.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7ad96923e2092d8edbf0c1b274f9b522690b932ed47a70d9a0c1c329f169f107"},
- {file = "lupa-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:364b291bf2b55555c87b4bffb4db5a9619bcdb3c02e58aebde5319c3c59ec9b2"},
- {file = "lupa-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ed071efc8ee231fac1fcd6b6fce44dc6da75a352b9b78403af89a48d759743c"},
- {file = "lupa-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bce60847bebb4aa9ed3436fab3e84585e9094e15e1cb8d32e16e041c4ef65331"},
- {file = "lupa-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5fbe7f83b0007cda3b158a93726c80dfd39003a8c5c5d608f6fdf8c60c42117f"},
- {file = "lupa-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4bd789967cbb5c84470f358c7fa8fcbf7464185adbd872a6c3de9b42d29a6d26"},
- {file = "lupa-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:ca58da94a6495dda0063ba975fe2e6f722c5e84c94f09955671b279c41cfde96"},
- {file = "lupa-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:51d6965663b2be1a593beabfa10803fdbbcf0b293aa4a53ea09a23db89787d0d"},
- {file = "lupa-1.14.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d251ba009996a47231615ea6b78123c88446979ae99b5585269ec46f7a9197aa"},
- {file = "lupa-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:abe3fc103d7bd34e7028d06db557304979f13ebf9050ad0ea6c1cc3a1caea017"},
- {file = "lupa-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:4ea185c394bf7d07e9643d868e50cc94a530bb298d4bdae4915672b3809cc72b"},
- {file = "lupa-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:6aff7257b5953de620db489899406cddb22093d1124fc5b31f8900e44a9dbc2a"},
- {file = "lupa-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d6f5bfbd8fc48c27786aef8f30c84fd9197747fa0b53761e69eb968d81156cbf"},
- {file = "lupa-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dec7580b86975bc5bdf4cc54638c93daaec10143b4acc4a6c674c0f7e27dd363"},
- {file = "lupa-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:96a201537930813b34145daf337dcd934ddfaebeba6452caf8a32a418e145e82"},
- {file = "lupa-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c0efaae8e7276f4feb82cba43c3cd45c82db820c9dab3965a8f2e0cb8b0bc30b"},
- {file = "lupa-1.14.1-cp38-cp38-win32.whl", hash = "sha256:b6953854a343abdfe11aa52a2d021fadf3d77d0cd2b288b650f149b597e0d02d"},
- {file = "lupa-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:c79ced2aaf7577e3d06933cf0d323fa968e6864c498c376b0bd475ded86f01f3"},
- {file = "lupa-1.14.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:72589a21a3776c7dd4b05374780e7ecf1b49c490056077fc91486461935eaaa3"},
- {file = "lupa-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:30d356a433653b53f1fe29477faaf5e547b61953b971b010d2185a561f4ce82a"},
- {file = "lupa-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:2116eb467797d5a134b2c997dfc7974b9a84b3aa5776c17ba8578ed4f5f41a9b"},
- {file = "lupa-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:24d6c3435d38614083d197f3e7bcfe6d3d9eb02ee393d60a4ab9c719bc000162"},
- {file = "lupa-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9144ecfa5e363f03e4d1c1e678b081cd223438be08f96604fca478591c3e3b53"},
- {file = "lupa-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69be1d6c3f3ab9fc988c9a0e5801f23f68e2c8b5900a8fd3ae57d1d0e9c5539c"},
- {file = "lupa-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:77b587043d0bee9cc738e00c12718095cf808dd269b171f852bd82026c664c69"},
- {file = "lupa-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:62530cf0a9c749a3cd13ad92b31eaf178939d642b6176b46cfcd98f6c5006383"},
- {file = "lupa-1.14.1-cp39-cp39-win32.whl", hash = "sha256:d891b43b8810191eb4c42a0bc57c32f481098029aac42b176108e09ffe118cdc"},
- {file = "lupa-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:cf643bc48a152e2c572d8be7fc1de1c417a6a9648d337ffedebf00f57016b786"},
- {file = "lupa-1.14.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0ac862c6d2eb542ac70d294a8e960b9ae7f46297559733b4c25f9e3c945e522a"},
- {file = "lupa-1.14.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:0a15680f425b91ec220eb84b0ab59d24c4bee69d15b88245a6998a7d38c78ba6"},
- {file = "lupa-1.14.1-pp37-pypy37_pp73-win32.whl", hash = "sha256:8a064d72991ba53aeea9720d95f2055f7f8a1e2f35b32a35d92248b63a94bcd1"},
- {file = "lupa-1.14.1-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6d87d6c51e6c3b6326d18af83e81f4860ba0b287cda1101b1ab8562389d598f5"},
- {file = "lupa-1.14.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b3efe9d887cfdf459054308ecb716e0eb11acb9a96c3022ee4e677c1f510d244"},
- {file = "lupa-1.14.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:723fff6fcab5e7045e0fa79014729577f98082bd1fd1050f907f83a41e4c9865"},
- {file = "lupa-1.14.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:930092a27157241d07d6d09ff01d5530a9e4c0dd515228211f2902b7e88ec1f0"},
- {file = "lupa-1.14.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:7f6bc9852bdf7b16840c984a1e9f952815f7d4b3764585d20d2e062bd1128074"},
- {file = "lupa-1.14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:8f65d2007092a04616c215fea5ad05ba8f661bd0f45cde5265d27150f64d3dd8"},
- {file = "lupa-1.14.1.tar.gz", hash = "sha256:d0fd4e60ad149fe25c90530e2a0e032a42a6f0455f29ca0edb8170d6ec751c6e"},
+ {file = "lupa-2.0-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:47d3eb18511e83068a8ce476a9f7ad8642a35189e682f5a1053970ec9d98272a"},
+ {file = "lupa-2.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:32d1e7cdced4e29771dacfed68abc92da9ba2300a2929ec5782467316ea4a715"},
+ {file = "lupa-2.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d412925a73b6b848fd1076fbc392d445ff4a1ab5b5bb278e358f78768677c963"},
+ {file = "lupa-2.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:7c10d4f0fa592b798a71c0b2e273e4b899a14b3634a48cbc444917b254ddce37"},
+ {file = "lupa-2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f50a395dc3c950974ac73b2476136785c6995f611a81e14d2a7c6aa59b342abf"},
+ {file = "lupa-2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c19482a595deed90e5b8542df1ed861e2a4a9d99bd8a9ff108e3a7c66bc7c6c0"},
+ {file = "lupa-2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d85c20691dbd2db5b7c60f40e4a5ced6a35be60264a81dc08804483917b41ea9"},
+ {file = "lupa-2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43353ae1e204b1f7fb18150f7dc5357592be37431e84f799c6cf21a4b7a52dcc"},
+ {file = "lupa-2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9add3d9ba86fa2fb5604e429ca811b9fa6b4c55fe5330bd9f0fcf51f2c5bebf8"},
+ {file = "lupa-2.0-cp310-cp310-win32.whl", hash = "sha256:17fd814523b9fa268df8f0995874218a9be008dbcd1c1c7bd28207814a209491"},
+ {file = "lupa-2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5c249d83655942ebe7db99c4e981de547867a7d30ace34e61f3ccc5b7a14402c"},
+ {file = "lupa-2.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e051969dc712d7050d0f3d6c6c8ed063941a004381e84f072815350476118f81"},
+ {file = "lupa-2.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a0e45ada08e5694ab3f3c06523ec16322dfb875668ce9ff3e04a01d3e18e81"},
+ {file = "lupa-2.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f7c1cfa9dac4f1363d9620384f9881a1ec968ff825be1e9b2ecdb4cb5375fbf2"},
+ {file = "lupa-2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4649a5501f0d8e5c96c297896377e9f73d0167df139109536187c57c60be1e90"},
+ {file = "lupa-2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e980571081c93152bb04de07bbde6852462e1674349eb3eafe703f5fa81a836"},
+ {file = "lupa-2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c529e5ecf3ec5b3e57efbb9a5def5125ceb7b95f12e2c89c34535856abb1ac"},
+ {file = "lupa-2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a6847c2541f9cbdd596df821a575222f471175cd710fb967ffc51801dae58d68"},
+ {file = "lupa-2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f3f962a499f95b3a5e90de36ac396cdb59c0c46b8003fbfcc1e2d78d7edc14f8"},
+ {file = "lupa-2.0-cp311-cp311-win32.whl", hash = "sha256:fcedc43012527edb4ca2b97a6c8176dd2384a006e47549d4e73143f7982deaff"},
+ {file = "lupa-2.0-cp311-cp311-win_amd64.whl", hash = "sha256:0e66da3bc40cde8edeb4d7d8141afad67ec6a5da0ee07ce5265df7e899e0883c"},
+ {file = "lupa-2.0-cp35-cp35m-win32.whl", hash = "sha256:ab2ca1c51724b779a2531d2bef1480faae203c8917b9cc3d0a3d3acb37c1d7ad"},
+ {file = "lupa-2.0-cp35-cp35m-win_amd64.whl", hash = "sha256:3b3e02b920b61601e2d9713b1e197d8cbab0bd3709774ec6823357cd83ee7b9d"},
+ {file = "lupa-2.0-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:8214a8b0fb1277e026301f60101af323c93868eefcad69984e7285bea5c1ac3f"},
+ {file = "lupa-2.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90788d250f727720747784e67fbc50917f5ce051e24bc49661850f98b1b9ed42"},
+ {file = "lupa-2.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:019e10a56c50ba60e94ff8c3e60a9a239d6438f1dc6ac17bcf2d44d4ada8f171"},
+ {file = "lupa-2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0d5481e3af166d73da373ffda0eab1bd709b0177daa2616ce95816483942c21"},
+ {file = "lupa-2.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0432ec532513eaf5ae8961000baf56d550fed4a7b91c0a9759b6f17c1dafc8af"},
+ {file = "lupa-2.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7563c4a015f51eb36d92874c0448bb8df504041d894e61e6c9cb9e6613132470"},
+ {file = "lupa-2.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:46b77e4a545d5ba00d17432853b26b50299129047d4f999c007fb9b6db3cfdd6"},
+ {file = "lupa-2.0-cp36-cp36m-win32.whl", hash = "sha256:2c11eafd262ff47ccb0bf9c28126dde21d3d01205cf6f5b5c2c4dbf04b99f5e9"},
+ {file = "lupa-2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a91eacc06ac89a2134c6b0f35ac65c45e18c984baf24b03d0f5187071074a597"},
+ {file = "lupa-2.0-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:a97e647ac11ca5131a73628ee063233378c03100f0f408c77f9b45cb358619ab"},
+ {file = "lupa-2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02ed2848a33dfe43013c5a86d2c155a9669d3c438a847a4e3816b7f1bf17cec6"},
+ {file = "lupa-2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f576699ca59f3f76127d70210a0ba20e7def93ab1a7e3587d55dd4b770775788"},
+ {file = "lupa-2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:769d7747056380ca4fb7923b7031b5732c1b9b9d0d160324cc88a32d7c98127c"},
+ {file = "lupa-2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29c46d79273a72c010a2949d41336bbb5ebafd09e2c2a4342d2f2f4238d378c8"},
+ {file = "lupa-2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a3dbf85baf66f0a8b862293c3cd61430d2d379652e3db3e5f979b16db7e374b"},
+ {file = "lupa-2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:033a14fe291ef532db11c3f3b65b364b5b3b3d3b6146aa7f7412f8f4d89471ce"},
+ {file = "lupa-2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:793bddad1a36eb7c8c04775867942cf2adfe09d482311791022c4ab4802169b4"},
+ {file = "lupa-2.0-cp37-cp37m-win32.whl", hash = "sha256:dd9af8e86b3c811ce74f11a12f275c873bd38f40de6ce76b7ddc3664e113a98e"},
+ {file = "lupa-2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6e9ece8e7e4399473e1f9a4733445d93148c3205e1b87c158894287f3213bf6b"},
+ {file = "lupa-2.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:1be2e1015d8481511852ae0f9f05f3722715d7aadb48207480eb50edc45a7510"},
+ {file = "lupa-2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7caa1ce59fe1cefd845093d1354244c59d286fcc1196a15297fb189a5bb749c6"},
+ {file = "lupa-2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f04c7a8d4e5b50a570681b990ff3be09bce5efbd91a521442c0ebfc36e0ce422"},
+ {file = "lupa-2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8368f0d5131f47da60f7cea4a5932418ca0bcd12c22fcf700f36af93fdf2a6a"},
+ {file = "lupa-2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d225e06748aca078a02529054c6678ba3e5b7cc2080b5be30e33ede9eac5efb2"},
+ {file = "lupa-2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:974de113c63e35668fbbbff656fef718e586abed3fc875eae4fece279a1e8a11"},
+ {file = "lupa-2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c953b9430751e792b721dd2265af1759251cdac0ade5642f25e16a6174bcc58"},
+ {file = "lupa-2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65d5971eb8c060eb3c9218c25181001e25982dfdf88e0b284447f837a4318a5f"},
+ {file = "lupa-2.0-cp38-cp38-win32.whl", hash = "sha256:eece0bc316c2b050e8c3596320e124c8ccea2a7872e593193d30eecab7f0acf6"},
+ {file = "lupa-2.0-cp38-cp38-win_amd64.whl", hash = "sha256:06792b86f9410bd26936728e7f903e2eee76642cbf51e435622637a3d752a2ea"},
+ {file = "lupa-2.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:8f3e6ea86053ec0c9945ae313fba8ba06dc4ccc397369709bba956dd48db95a7"},
+ {file = "lupa-2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:201fc894d257132e90e42ce9396c5b45aa5f5bdc4cd4dfc8076c8476f04dd44b"},
+ {file = "lupa-2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b3f6837c1e2fd7c66100828953063dfe8a1d283bc48e1144d621b35bf19ce79f"},
+ {file = "lupa-2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:becb01602dc6d5439101e1ac5877b25e35817b1bd131b9af709a5a181e6b8026"},
+ {file = "lupa-2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3d34870912bf7501d2a9e7dc75319e55f836fd8412b783afa44c5bfb72be0867"},
+ {file = "lupa-2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d02d4af2682169b8aa744e7eae59d1e05f9b0071a59fb140852dae9b5c8d86c"},
+ {file = "lupa-2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:282126096ba71c1926f28da59cd1cf6913b7e9e7020d577b42dc52ca3c359e93"},
+ {file = "lupa-2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c7ec361e05d932c5355825982613077ac8cb5b63d95022d571290d8ca667188"},
+ {file = "lupa-2.0-cp39-cp39-win32.whl", hash = "sha256:e361efe6c8a667fa221d42b7fa2beb7fada86e901a0f0e1e17c7c7927d66b2ff"},
+ {file = "lupa-2.0-cp39-cp39-win_amd64.whl", hash = "sha256:c0be42065ad39219eaf890c224cc7cc140ed72691b97b0905dd7a89abebdf474"},
+ {file = "lupa-2.0-pp37-pypy37_pp73-macosx_11_0_x86_64.whl", hash = "sha256:dea916b28ee38c904ece3a26986b6943a073666c038ae6b6d6d131668da20f59"},
+ {file = "lupa-2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:345032ef77bd474d288ea2c4ddd14b552b93d60a40a9b0daf0a82bc078625982"},
+ {file = "lupa-2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:9fa9d5013a06aa09392f1d02d9724a9856f4f4111794ca9be17a016c83c6546a"},
+ {file = "lupa-2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:71e517327bff75cc5e60fe105da7da6621a75ba05a5050869e33b4bdbe838288"},
+ {file = "lupa-2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e00664780836b353113804f8e0f860322abf5ef723d615ba6f49d9e78874944"},
+ {file = "lupa-2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a5843fbfb22b70ea13ec624d43c818b396ff1f62d9bd84f9ed10e3fef06ccf0"},
+ {file = "lupa-2.0-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5396ebb51753a8243a18080e2efa9f085bac5d43185d5a1dd9a3679ff7fb09c5"},
+ {file = "lupa-2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:404bda126a34eef839e29fc94fd65c1092b53301b2d0abc9388f02cc5ba87ac9"},
+ {file = "lupa-2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:fb5efacbb5dd568d44f4f31a4764a52eefb78288f0445da016652fe7143cdde3"},
+ {file = "lupa-2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7762c6780fe7ab64d64f8658ab54d79cb5d3d0fbdcc76290f5fc19b41fc01ad5"},
+ {file = "lupa-2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0068d75f0df5f2fb85230b1df7a05305645ee28ef89551997eb09009c70d7f8a"},
+ {file = "lupa-2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:690c0654b92c6de0893c004d0a46d5d5b5fd76e9017dda328a2435afdf3c55a0"},
+ {file = "lupa-2.0-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:9b7c9799a45e6fff8c38395d370b318b8ce6841710c2082f180ea7d189f7d229"},
+ {file = "lupa-2.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:200544d259a054c5d0c6696499d0c66ccd924d42efb41b09b19c2af9771f5c31"},
+ {file = "lupa-2.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:682860cd6ed84e0ffdaf84c82c21b192858261964b3ed126bc54d52cc8a480b4"},
+ {file = "lupa-2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb4426cddefb48683068e94ed4748710507bbd3f0a4d71574535443c75a16e36"},
+ {file = "lupa-2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88495333e79937cdf7edac35ec36aca41d50134dbb23f2f1684a1685a4295433"},
+ {file = "lupa-2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c776290a06b03e8dd5ca061d9fefde13be37fb25700c56bb513343262ea1729"},
+ {file = "lupa-2.0.tar.gz", hash = "sha256:ad3fef486be7adddd349fe9a9c393789061312cf98ebc533b489be34f484cb79"},
]
[[package]]
name = "lxml"
-version = "4.9.2"
+version = "5.1.0"
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
-files = [
- {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"},
+python-versions = ">=3.6"
+files = [
+ {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:704f5572ff473a5f897745abebc6df40f22d4133c1e0a1f124e4f2bd3330ff7e"},
+ {file = "lxml-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d3c0f8567ffe7502d969c2c1b809892dc793b5d0665f602aad19895f8d508da"},
+ {file = "lxml-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5fcfbebdb0c5d8d18b84118842f31965d59ee3e66996ac842e21f957eb76138c"},
+ {file = "lxml-5.1.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f37c6d7106a9d6f0708d4e164b707037b7380fcd0b04c5bd9cae1fb46a856fb"},
+ {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2befa20a13f1a75c751f47e00929fb3433d67eb9923c2c0b364de449121f447c"},
+ {file = "lxml-5.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22b7ee4c35f374e2c20337a95502057964d7e35b996b1c667b5c65c567d2252a"},
+ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:bf8443781533b8d37b295016a4b53c1494fa9a03573c09ca5104550c138d5c05"},
+ {file = "lxml-5.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82bddf0e72cb2af3cbba7cec1d2fd11fda0de6be8f4492223d4a268713ef2147"},
+ {file = "lxml-5.1.0-cp310-cp310-win32.whl", hash = "sha256:b66aa6357b265670bb574f050ffceefb98549c721cf28351b748be1ef9577d93"},
+ {file = "lxml-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:4946e7f59b7b6a9e27bef34422f645e9a368cb2be11bf1ef3cafc39a1f6ba68d"},
+ {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:14deca1460b4b0f6b01f1ddc9557704e8b365f55c63070463f6c18619ebf964f"},
+ {file = "lxml-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed8c3d2cd329bf779b7ed38db176738f3f8be637bb395ce9629fc76f78afe3d4"},
+ {file = "lxml-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:436a943c2900bb98123b06437cdd30580a61340fbdb7b28aaf345a459c19046a"},
+ {file = "lxml-5.1.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acb6b2f96f60f70e7f34efe0c3ea34ca63f19ca63ce90019c6cbca6b676e81fa"},
+ {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af8920ce4a55ff41167ddbc20077f5698c2e710ad3353d32a07d3264f3a2021e"},
+ {file = "lxml-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cfced4a069003d8913408e10ca8ed092c49a7f6cefee9bb74b6b3e860683b45"},
+ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9e5ac3437746189a9b4121db2a7b86056ac8786b12e88838696899328fc44bb2"},
+ {file = "lxml-5.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204"},
+ {file = "lxml-5.1.0-cp311-cp311-win32.whl", hash = "sha256:bc64d1b1dab08f679fb89c368f4c05693f58a9faf744c4d390d7ed1d8223869b"},
+ {file = "lxml-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a5ab722ae5a873d8dcee1f5f45ddd93c34210aed44ff2dc643b5025981908cda"},
+ {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9aa543980ab1fbf1720969af1d99095a548ea42e00361e727c58a40832439114"},
+ {file = "lxml-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6f11b77ec0979f7e4dc5ae081325a2946f1fe424148d3945f943ceaede98adb8"},
+ {file = "lxml-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a36c506e5f8aeb40680491d39ed94670487ce6614b9d27cabe45d94cd5d63e1e"},
+ {file = "lxml-5.1.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a"},
+ {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16dd953fb719f0ffc5bc067428fc9e88f599e15723a85618c45847c96f11f431"},
+ {file = "lxml-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16018f7099245157564d7148165132c70adb272fb5a17c048ba70d9cc542a1a1"},
+ {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:82cd34f1081ae4ea2ede3d52f71b7be313756e99b4b5f829f89b12da552d3aa3"},
+ {file = "lxml-5.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:19a1bc898ae9f06bccb7c3e1dfd73897ecbbd2c96afe9095a6026016e5ca97b8"},
+ {file = "lxml-5.1.0-cp312-cp312-win32.whl", hash = "sha256:13521a321a25c641b9ea127ef478b580b5ec82aa2e9fc076c86169d161798b01"},
+ {file = "lxml-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:1ad17c20e3666c035db502c78b86e58ff6b5991906e55bdbef94977700c72623"},
+ {file = "lxml-5.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:24ef5a4631c0b6cceaf2dbca21687e29725b7c4e171f33a8f8ce23c12558ded1"},
+ {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d2900b7f5318bc7ad8631d3d40190b95ef2aa8cc59473b73b294e4a55e9f30f"},
+ {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:601f4a75797d7a770daed8b42b97cd1bb1ba18bd51a9382077a6a247a12aa38d"},
+ {file = "lxml-5.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4b68c961b5cc402cbd99cca5eb2547e46ce77260eb705f4d117fd9c3f932b95"},
+ {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:afd825e30f8d1f521713a5669b63657bcfe5980a916c95855060048b88e1adb7"},
+ {file = "lxml-5.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:262bc5f512a66b527d026518507e78c2f9c2bd9eb5c8aeeb9f0eb43fcb69dc67"},
+ {file = "lxml-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:e856c1c7255c739434489ec9c8aa9cdf5179785d10ff20add308b5d673bed5cd"},
+ {file = "lxml-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:c7257171bb8d4432fe9d6fdde4d55fdbe663a63636a17f7f9aaba9bcb3153ad7"},
+ {file = "lxml-5.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b9e240ae0ba96477682aa87899d94ddec1cc7926f9df29b1dd57b39e797d5ab5"},
+ {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a96f02ba1bcd330807fc060ed91d1f7a20853da6dd449e5da4b09bfcc08fdcf5"},
+ {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3898ae2b58eeafedfe99e542a17859017d72d7f6a63de0f04f99c2cb125936"},
+ {file = "lxml-5.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61c5a7edbd7c695e54fca029ceb351fc45cd8860119a0f83e48be44e1c464862"},
+ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3aeca824b38ca78d9ee2ab82bd9883083d0492d9d17df065ba3b94e88e4d7ee6"},
+ {file = "lxml-5.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8f52fe6859b9db71ee609b0c0a70fea5f1e71c3462ecf144ca800d3f434f0764"},
+ {file = "lxml-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:d42e3a3fc18acc88b838efded0e6ec3edf3e328a58c68fbd36a7263a874906c8"},
+ {file = "lxml-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:eac68f96539b32fce2c9b47eb7c25bb2582bdaf1bbb360d25f564ee9e04c542b"},
+ {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ae15347a88cf8af0949a9872b57a320d2605ae069bcdf047677318bc0bba45b1"},
+ {file = "lxml-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c26aab6ea9c54d3bed716b8851c8bfc40cb249b8e9880e250d1eddde9f709bf5"},
+ {file = "lxml-5.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:342e95bddec3a698ac24378d61996b3ee5ba9acfeb253986002ac53c9a5f6f84"},
+ {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:725e171e0b99a66ec8605ac77fa12239dbe061482ac854d25720e2294652eeaa"},
+ {file = "lxml-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d184e0d5c918cff04cdde9dbdf9600e960161d773666958c9d7b565ccc60c45"},
+ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:98f3f020a2b736566c707c8e034945c02aa94e124c24f77ca097c446f81b01f1"},
+ {file = "lxml-5.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d48fc57e7c1e3df57be5ae8614bab6d4e7b60f65c5457915c26892c41afc59e"},
+ {file = "lxml-5.1.0-cp38-cp38-win32.whl", hash = "sha256:7ec465e6549ed97e9f1e5ed51c657c9ede767bc1c11552f7f4d022c4df4a977a"},
+ {file = "lxml-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:b21b4031b53d25b0858d4e124f2f9131ffc1530431c6d1321805c90da78388d1"},
+ {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:52427a7eadc98f9e62cb1368a5079ae826f94f05755d2d567d93ee1bc3ceb354"},
+ {file = "lxml-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6a2a2c724d97c1eb8cf966b16ca2915566a4904b9aad2ed9a09c748ffe14f969"},
+ {file = "lxml-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:843b9c835580d52828d8f69ea4302537337a21e6b4f1ec711a52241ba4a824f3"},
+ {file = "lxml-5.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9b99f564659cfa704a2dd82d0684207b1aadf7d02d33e54845f9fc78e06b7581"},
+ {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f8b0c78e7aac24979ef09b7f50da871c2de2def043d468c4b41f512d831e912"},
+ {file = "lxml-5.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bcf86dfc8ff3e992fed847c077bd875d9e0ba2fa25d859c3a0f0f76f07f0c8d"},
+ {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:49a9b4af45e8b925e1cd6f3b15bbba2c81e7dba6dce170c677c9cda547411e14"},
+ {file = "lxml-5.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:280f3edf15c2a967d923bcfb1f8f15337ad36f93525828b40a0f9d6c2ad24890"},
+ {file = "lxml-5.1.0-cp39-cp39-win32.whl", hash = "sha256:ed7326563024b6e91fef6b6c7a1a2ff0a71b97793ac33dbbcf38f6005e51ff6e"},
+ {file = "lxml-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8d7b4beebb178e9183138f552238f7e6613162a42164233e2bda00cb3afac58f"},
+ {file = "lxml-5.1.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9bd0ae7cc2b85320abd5e0abad5ccee5564ed5f0cc90245d2f9a8ef330a8deae"},
+ {file = "lxml-5.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8c1d679df4361408b628f42b26a5d62bd3e9ba7f0c0e7969f925021554755aa"},
+ {file = "lxml-5.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ad3a8ce9e8a767131061a22cd28fdffa3cd2dc193f399ff7b81777f3520e372"},
+ {file = "lxml-5.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:304128394c9c22b6569eba2a6d98392b56fbdfbad58f83ea702530be80d0f9df"},
+ {file = "lxml-5.1.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d74fcaf87132ffc0447b3c685a9f862ffb5b43e70ea6beec2fb8057d5d2a1fea"},
+ {file = "lxml-5.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:8cf5877f7ed384dabfdcc37922c3191bf27e55b498fecece9fd5c2c7aaa34c33"},
+ {file = "lxml-5.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:877efb968c3d7eb2dad540b6cabf2f1d3c0fbf4b2d309a3c141f79c7e0061324"},
+ {file = "lxml-5.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f14a4fb1c1c402a22e6a341a24c1341b4a3def81b41cd354386dcb795f83897"},
+ {file = "lxml-5.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:25663d6e99659544ee8fe1b89b1a8c0aaa5e34b103fab124b17fa958c4a324a6"},
+ {file = "lxml-5.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b9f19df998761babaa7f09e6bc169294eefafd6149aaa272081cbddc7ba4ca3"},
+ {file = "lxml-5.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e53d7e6a98b64fe54775d23a7c669763451340c3d44ad5e3a3b48a1efbdc96f"},
+ {file = "lxml-5.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c3cd1fc1dc7c376c54440aeaaa0dcc803d2126732ff5c6b68ccd619f2e64be4f"},
+ {file = "lxml-5.1.0.tar.gz", hash = "sha256:3eea6ed6e6c918e468e693c41ef07f3c3acc310b70ddd9cc72d9ef84bc9564ca"},
]
[package.extras]
cssselect = ["cssselect (>=0.7)"]
html5 = ["html5lib"]
htmlsoup = ["BeautifulSoup4"]
-source = ["Cython (>=0.29.7)"]
+source = ["Cython (>=3.0.7)"]
[[package]]
name = "markdownify"
@@ -1043,24 +1044,24 @@ six = ">=1.15,<2"
[[package]]
name = "more-itertools"
-version = "9.1.0"
+version = "10.2.0"
description = "More routines for operating on iterables, beyond itertools"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"},
- {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"},
+ {file = "more-itertools-10.2.0.tar.gz", hash = "sha256:8fccb480c43d3e99a00087634c06dd02b0d50fbf088b380de5a41a015ec239e1"},
+ {file = "more_itertools-10.2.0-py3-none-any.whl", hash = "sha256:686b06abe565edfab151cb8fd385a05651e1fdf8f0a14191e4439283421f8684"},
]
[[package]]
name = "mslex"
-version = "0.3.0"
+version = "1.1.0"
description = "shlex for windows"
optional = false
python-versions = ">=3.5"
files = [
- {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"},
- {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"},
+ {file = "mslex-1.1.0-py2.py3-none-any.whl", hash = "sha256:8826f4bb8d8c63402203d921dc8c2df0c7fec0d9c91d020ddf02fc9d0dce81bd"},
+ {file = "mslex-1.1.0.tar.gz", hash = "sha256:7fe305fbdc9721283875e0b737fdb344374b761338a7f41af91875de278568e4"},
]
[[package]]
@@ -1176,24 +1177,24 @@ dev = ["black", "mypy", "pytest"]
[[package]]
name = "packaging"
-version = "23.1"
+version = "23.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
files = [
- {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
- {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
+ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
+ {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
]
[[package]]
name = "pip-licenses"
-version = "4.3.2"
+version = "4.3.4"
description = "Dump the software license list of Python packages installed with pip."
optional = false
python-versions = "~=3.8"
files = [
- {file = "pip-licenses-4.3.2.tar.gz", hash = "sha256:27ce33be185a009f3128ea59fe4c1490f4f4da5eb53ed951b87ef3c621b583f9"},
- {file = "pip_licenses-4.3.2-py3-none-any.whl", hash = "sha256:3fede933c47e1f4bc5e91d7cfd1d9b9d4e37a3c03b2875352b12bbad41294cd6"},
+ {file = "pip-licenses-4.3.4.tar.gz", hash = "sha256:9c6c9c3252b976d08735bdffb0eb4c5eaa50dfd46f5e075532c0248ffe94fed1"},
+ {file = "pip_licenses-4.3.4-py3-none-any.whl", hash = "sha256:85706ec30781076eb611fed3934f27a1f18437d3211f747567cd3c4e943fce1b"},
]
[package.dependencies]
@@ -1204,28 +1205,28 @@ test = ["docutils", "mypy", "pytest-cov", "pytest-pycodestyle", "pytest-runner"]
[[package]]
name = "platformdirs"
-version = "3.8.0"
+version = "4.2.0"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "platformdirs-3.8.0-py3-none-any.whl", hash = "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e"},
- {file = "platformdirs-3.8.0.tar.gz", hash = "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc"},
+ {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
+ {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
]
[package.extras]
-docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
+docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
[[package]]
name = "pluggy"
-version = "1.2.0"
+version = "1.4.0"
description = "plugin and hook calling mechanisms for python"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"},
- {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"},
+ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
+ {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
]
[package.extras]
@@ -1234,13 +1235,13 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pre-commit"
-version = "3.3.3"
+version = "3.6.2"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
files = [
- {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"},
- {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"},
+ {file = "pre_commit-3.6.2-py2.py3-none-any.whl", hash = "sha256:ba637c2d7a670c10daedc059f5c49b5bd0aadbccfcd7ec15592cf9665117532c"},
+ {file = "pre_commit-3.6.2.tar.gz", hash = "sha256:c3ef34f463045c88658c5b99f38c1e297abdcc0ff13f98d3370055fbbfabc67e"},
]
[package.dependencies]
@@ -1252,13 +1253,13 @@ virtualenv = ">=20.10.0"
[[package]]
name = "prettytable"
-version = "3.8.0"
+version = "3.9.0"
description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format"
optional = false
python-versions = ">=3.8"
files = [
- {file = "prettytable-3.8.0-py3-none-any.whl", hash = "sha256:03481bca25ae0c28958c8cd6ac5165c159ce89f7ccde04d5c899b24b68bb13b7"},
- {file = "prettytable-3.8.0.tar.gz", hash = "sha256:031eae6a9102017e8c7c7906460d150b7ed78b20fd1d8c8be4edaf88556c07ce"},
+ {file = "prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8"},
+ {file = "prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34"},
]
[package.dependencies]
@@ -1269,25 +1270,27 @@ tests = ["pytest", "pytest-cov", "pytest-lazy-fixture"]
[[package]]
name = "psutil"
-version = "5.9.5"
+version = "5.9.8"
description = "Cross-platform lib for process and system monitoring in Python."
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "psutil-5.9.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:be8929ce4313f9f8146caad4272f6abb8bf99fc6cf59344a3167ecd74f4f203f"},
- {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ab8ed1a1d77c95453db1ae00a3f9c50227ebd955437bcf2a574ba8adbf6a74d5"},
- {file = "psutil-5.9.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:4aef137f3345082a3d3232187aeb4ac4ef959ba3d7c10c33dd73763fbc063da4"},
- {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ea8518d152174e1249c4f2a1c89e3e6065941df2fa13a1ab45327716a23c2b48"},
- {file = "psutil-5.9.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:acf2aef9391710afded549ff602b5887d7a2349831ae4c26be7c807c0a39fac4"},
- {file = "psutil-5.9.5-cp27-none-win32.whl", hash = "sha256:5b9b8cb93f507e8dbaf22af6a2fd0ccbe8244bf30b1baad6b3954e935157ae3f"},
- {file = "psutil-5.9.5-cp27-none-win_amd64.whl", hash = "sha256:8c5f7c5a052d1d567db4ddd231a9d27a74e8e4a9c3f44b1032762bd7b9fdcd42"},
- {file = "psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217"},
- {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da"},
- {file = "psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4"},
- {file = "psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d"},
- {file = "psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9"},
- {file = "psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30"},
- {file = "psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c"},
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
+files = [
+ {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"},
+ {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"},
+ {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"},
+ {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"},
+ {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"},
+ {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"},
+ {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"},
+ {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"},
+ {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"},
+ {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"},
+ {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"},
+ {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"},
+ {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"},
+ {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"},
+ {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"},
+ {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"},
]
[package.extras]
@@ -1295,63 +1298,62 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
[[package]]
name = "pycares"
-version = "4.3.0"
+version = "4.4.0"
description = "Python interface for c-ares"
optional = false
-python-versions = "*"
+python-versions = ">=3.8"
files = [
- {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"},
+ {file = "pycares-4.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:24da119850841d16996713d9c3374ca28a21deee056d609fbbed29065d17e1f6"},
+ {file = "pycares-4.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8f64cb58729689d4d0e78f0bfb4c25ce2f851d0274c0273ac751795c04b8798a"},
+ {file = "pycares-4.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d33e2a1120887e89075f7f814ec144f66a6ce06a54f5722ccefc62fbeda83cff"},
+ {file = "pycares-4.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c680fef1b502ee680f8f0b95a41af4ec2c234e50e16c0af5bbda31999d3584bd"},
+ {file = "pycares-4.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fff16b09042ba077f7b8aa5868d1d22456f0002574d0ba43462b10a009331677"},
+ {file = "pycares-4.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:229a1675eb33bc9afb1fc463e73ee334950ccc485bc83a43f6ae5839fb4d5fa3"},
+ {file = "pycares-4.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3aebc73e5ad70464f998f77f2da2063aa617cbd8d3e8174dd7c5b4518f967153"},
+ {file = "pycares-4.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef64649eba56448f65e26546d85c860709844d2fc22ef14d324fe0b27f761a9"},
+ {file = "pycares-4.4.0-cp310-cp310-win32.whl", hash = "sha256:4afc2644423f4eef97857a9fd61be9758ce5e336b4b0bd3d591238bb4b8b03e0"},
+ {file = "pycares-4.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5ed4e04af4012f875b78219d34434a6d08a67175150ac1b79eb70ab585d4ba8c"},
+ {file = "pycares-4.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bce8db2fc6f3174bd39b81405210b9b88d7b607d33e56a970c34a0c190da0490"},
+ {file = "pycares-4.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9a0303428d013ccf5c51de59c83f9127aba6200adb7fd4be57eddb432a1edd2a"},
+ {file = "pycares-4.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afb91792f1556f97be7f7acb57dc7756d89c5a87bd8b90363a77dbf9ea653817"},
+ {file = "pycares-4.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b61579cecf1f4d616e5ea31a6e423a16680ab0d3a24a2ffe7bb1d4ee162477ff"},
+ {file = "pycares-4.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7af06968cbf6851566e806bf3e72825b0e6671832a2cbe840be1d2d65350710"},
+ {file = "pycares-4.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ceb12974367b0a68a05d52f4162b29f575d241bd53de155efe632bf2c943c7f6"},
+ {file = "pycares-4.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2eeec144bcf6a7b6f2d74d6e70cbba7886a84dd373c886f06cb137a07de4954c"},
+ {file = "pycares-4.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e3a6f7cfdfd11eb5493d6d632e582408c8f3b429f295f8799c584c108b28db6f"},
+ {file = "pycares-4.4.0-cp311-cp311-win32.whl", hash = "sha256:34736a2ffaa9c08ca9c707011a2d7b69074bbf82d645d8138bba771479b2362f"},
+ {file = "pycares-4.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:eb66c30eb11e877976b7ead13632082a8621df648c408b8e15cdb91a452dd502"},
+ {file = "pycares-4.4.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fd644505a8cfd7f6584d33a9066d4e3d47700f050ef1490230c962de5dfb28c6"},
+ {file = "pycares-4.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52084961262232ec04bd75f5043aed7e5d8d9695e542ff691dfef0110209f2d4"},
+ {file = "pycares-4.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0c5368206057884cde18602580083aeaad9b860e2eac14fd253543158ce1e93"},
+ {file = "pycares-4.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:112a4979c695b1c86f6782163d7dec58d57a3b9510536dcf4826550f9053dd9a"},
+ {file = "pycares-4.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d186dafccdaa3409194c0f94db93c1a5d191145a275f19da6591f9499b8e7b8"},
+ {file = "pycares-4.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:64965dc19c578a683ea73487a215a8897276224e004d50eeb21f0bc7a0b63c88"},
+ {file = "pycares-4.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ed2a38e34bec6f2586435f6ff0bc5fe11d14bebd7ed492cf739a424e81681540"},
+ {file = "pycares-4.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:94d6962db81541eb0396d2f0dfcbb18cdb8c8b251d165efc2d974ae652c547d4"},
+ {file = "pycares-4.4.0-cp312-cp312-win32.whl", hash = "sha256:1168a48a834813aa80f412be2df4abaf630528a58d15c704857448b20b1675c0"},
+ {file = "pycares-4.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:db24c4e7fea4a052c6e869cbf387dd85d53b9736cfe1ef5d8d568d1ca925e977"},
+ {file = "pycares-4.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:21a5a0468861ec7df7befa69050f952da13db5427ae41ffe4713bc96291d1d95"},
+ {file = "pycares-4.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:22c00bf659a9fa44d7b405cf1cd69b68b9d37537899898d8cbe5dffa4016b273"},
+ {file = "pycares-4.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23aa3993a352491a47fcf17867f61472f32f874df4adcbb486294bd9fbe8abee"},
+ {file = "pycares-4.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:813d661cbe2e37d87da2d16b7110a6860e93ddb11735c6919c8a3545c7b9c8d8"},
+ {file = "pycares-4.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:77cf5a2fd5583c670de41a7f4a7b46e5cbabe7180d8029f728571f4d2e864084"},
+ {file = "pycares-4.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3eaa6681c0a3e3f3868c77aca14b7760fed35fdfda2fe587e15c701950e7bc69"},
+ {file = "pycares-4.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ad58e284a658a8a6a84af2e0b62f2f961f303cedfe551854d7bd40c3cbb61912"},
+ {file = "pycares-4.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bfb89ca9e3d0a9b5332deeb666b2ede9d3469107742158f4aeda5ce032d003f4"},
+ {file = "pycares-4.4.0-cp38-cp38-win32.whl", hash = "sha256:f36bdc1562142e3695555d2f4ac0cb69af165eddcefa98efc1c79495b533481f"},
+ {file = "pycares-4.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:902461a92b6a80fd5041a2ec5235680c7cc35e43615639ec2a40e63fca2dfb51"},
+ {file = "pycares-4.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7bddc6adba8f699728f7fc1c9ce8cef359817ad78e2ed52b9502cb5f8dc7f741"},
+ {file = "pycares-4.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cb49d5805cd347c404f928c5ae7c35e86ba0c58ffa701dbe905365e77ce7d641"},
+ {file = "pycares-4.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56cf3349fa3a2e67ed387a7974c11d233734636fe19facfcda261b411af14d80"},
+ {file = "pycares-4.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bf2eaa83a5987e48fa63302f0fe7ce3275cfda87b34d40fef9ce703fb3ac002"},
+ {file = "pycares-4.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82bba2ab77eb5addbf9758d514d9bdef3c1bfe7d1649a47bd9a0d55a23ef478b"},
+ {file = "pycares-4.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c6a8bde63106f162fca736e842a916853cad3c8d9d137e11c9ffa37efa818b02"},
+ {file = "pycares-4.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5f646eec041db6ffdbcaf3e0756fb92018f7af3266138c756bb09d2b5baadec"},
+ {file = "pycares-4.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9dc04c54c6ea615210c1b9e803d0e2d2255f87a3d5d119b6482c8f0dfa15b26b"},
+ {file = "pycares-4.4.0-cp39-cp39-win32.whl", hash = "sha256:97892cced5794d721fb4ff8765764aa4ea48fe8b2c3820677505b96b83d4ef47"},
+ {file = "pycares-4.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:917f08f0b5d9324e9a34211e68d27447c552b50ab967044776bbab7e42a553a2"},
+ {file = "pycares-4.4.0.tar.gz", hash = "sha256:f47579d508f2f56eddd16ce72045782ad3b1b3b678098699e2b6a1b30733e1c2"},
]
[package.dependencies]
@@ -1373,73 +1375,176 @@ files = [
[[package]]
name = "pydantic"
-version = "1.10.9"
-description = "Data validation and settings management using python type hints"
+version = "2.5.2"
+description = "Data validation using Python type hints"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"},
+ {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"},
+]
+
+[package.dependencies]
+annotated-types = ">=0.4.0"
+pydantic-core = "2.14.5"
+typing-extensions = ">=4.6.1"
+
+[package.extras]
+email = ["email-validator (>=2.0.0)"]
+
+[[package]]
+name = "pydantic-core"
+version = "2.14.5"
+description = ""
optional = false
python-versions = ">=3.7"
files = [
- {file = "pydantic-1.10.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e692dec4a40bfb40ca530e07805b1208c1de071a18d26af4a2a0d79015b352ca"},
- {file = "pydantic-1.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c52eb595db83e189419bf337b59154bdcca642ee4b2a09e5d7797e41ace783f"},
- {file = "pydantic-1.10.9-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:939328fd539b8d0edf244327398a667b6b140afd3bf7e347cf9813c736211896"},
- {file = "pydantic-1.10.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b48d3d634bca23b172f47f2335c617d3fcb4b3ba18481c96b7943a4c634f5c8d"},
- {file = "pydantic-1.10.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f0b7628fb8efe60fe66fd4adadd7ad2304014770cdc1f4934db41fe46cc8825f"},
- {file = "pydantic-1.10.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e1aa5c2410769ca28aa9a7841b80d9d9a1c5f223928ca8bec7e7c9a34d26b1d4"},
- {file = "pydantic-1.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:eec39224b2b2e861259d6f3c8b6290d4e0fbdce147adb797484a42278a1a486f"},
- {file = "pydantic-1.10.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d111a21bbbfd85c17248130deac02bbd9b5e20b303338e0dbe0faa78330e37e0"},
- {file = "pydantic-1.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e9aec8627a1a6823fc62fb96480abe3eb10168fd0d859ee3d3b395105ae19a7"},
- {file = "pydantic-1.10.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07293ab08e7b4d3c9d7de4949a0ea571f11e4557d19ea24dd3ae0c524c0c334d"},
- {file = "pydantic-1.10.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ee829b86ce984261d99ff2fd6e88f2230068d96c2a582f29583ed602ef3fc2c"},
- {file = "pydantic-1.10.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4b466a23009ff5cdd7076eb56aca537c745ca491293cc38e72bf1e0e00de5b91"},
- {file = "pydantic-1.10.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7847ca62e581e6088d9000f3c497267868ca2fa89432714e21a4fb33a04d52e8"},
- {file = "pydantic-1.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:7845b31959468bc5b78d7b95ec52fe5be32b55d0d09983a877cca6aedc51068f"},
- {file = "pydantic-1.10.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:517a681919bf880ce1dac7e5bc0c3af1e58ba118fd774da2ffcd93c5f96eaece"},
- {file = "pydantic-1.10.9-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67195274fd27780f15c4c372f4ba9a5c02dad6d50647b917b6a92bf00b3d301a"},
- {file = "pydantic-1.10.9-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2196c06484da2b3fded1ab6dbe182bdabeb09f6318b7fdc412609ee2b564c49a"},
- {file = "pydantic-1.10.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6257bb45ad78abacda13f15bde5886efd6bf549dd71085e64b8dcf9919c38b60"},
- {file = "pydantic-1.10.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3283b574b01e8dbc982080d8287c968489d25329a463b29a90d4157de4f2baaf"},
- {file = "pydantic-1.10.9-cp37-cp37m-win_amd64.whl", hash = "sha256:5f8bbaf4013b9a50e8100333cc4e3fa2f81214033e05ac5aa44fa24a98670a29"},
- {file = "pydantic-1.10.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9cd67fb763248cbe38f0593cd8611bfe4b8ad82acb3bdf2b0898c23415a1f82"},
- {file = "pydantic-1.10.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f50e1764ce9353be67267e7fd0da08349397c7db17a562ad036aa7c8f4adfdb6"},
- {file = "pydantic-1.10.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73ef93e5e1d3c8e83f1ff2e7fdd026d9e063c7e089394869a6e2985696693766"},
- {file = "pydantic-1.10.9-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:128d9453d92e6e81e881dd7e2484e08d8b164da5507f62d06ceecf84bf2e21d3"},
- {file = "pydantic-1.10.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ad428e92ab68798d9326bb3e5515bc927444a3d71a93b4a2ca02a8a5d795c572"},
- {file = "pydantic-1.10.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fab81a92f42d6d525dd47ced310b0c3e10c416bbfae5d59523e63ea22f82b31e"},
- {file = "pydantic-1.10.9-cp38-cp38-win_amd64.whl", hash = "sha256:963671eda0b6ba6926d8fc759e3e10335e1dc1b71ff2a43ed2efd6996634dafb"},
- {file = "pydantic-1.10.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:970b1bdc6243ef663ba5c7e36ac9ab1f2bfecb8ad297c9824b542d41a750b298"},
- {file = "pydantic-1.10.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7e1d5290044f620f80cf1c969c542a5468f3656de47b41aa78100c5baa2b8276"},
- {file = "pydantic-1.10.9-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83fcff3c7df7adff880622a98022626f4f6dbce6639a88a15a3ce0f96466cb60"},
- {file = "pydantic-1.10.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0da48717dc9495d3a8f215e0d012599db6b8092db02acac5e0d58a65248ec5bc"},
- {file = "pydantic-1.10.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0a2aabdc73c2a5960e87c3ffebca6ccde88665616d1fd6d3db3178ef427b267a"},
- {file = "pydantic-1.10.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9863b9420d99dfa9c064042304868e8ba08e89081428a1c471858aa2af6f57c4"},
- {file = "pydantic-1.10.9-cp39-cp39-win_amd64.whl", hash = "sha256:e7c9900b43ac14110efa977be3da28931ffc74c27e96ee89fbcaaf0b0fe338e1"},
- {file = "pydantic-1.10.9-py3-none-any.whl", hash = "sha256:6cafde02f6699ce4ff643417d1a9223716ec25e228ddc3b436fe7e2d25a1f305"},
- {file = "pydantic-1.10.9.tar.gz", hash = "sha256:95c70da2cd3b6ddf3b9645ecaa8d98f3d80c606624b6d245558d202cd23ea3be"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"},
+ {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"},
+ {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"},
+ {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"},
+ {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"},
+ {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"},
+ {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"},
+ {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"},
+ {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"},
+ {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"},
+ {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"},
+ {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"},
+ {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"},
+ {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"},
+ {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"},
+ {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"},
+ {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"},
+ {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"},
+ {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"},
+ {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"},
+ {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"},
+ {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"},
+ {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"},
+ {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"},
+ {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"},
+ {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"},
+ {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"},
+ {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"},
+ {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"},
+ {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"},
+ {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"},
]
[package.dependencies]
-python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""}
-typing-extensions = ">=4.2.0"
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+
+[[package]]
+name = "pydantic-settings"
+version = "2.2.1"
+description = "Settings management using Pydantic"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pydantic_settings-2.2.1-py3-none-any.whl", hash = "sha256:0235391d26db4d2190cb9b31051c4b46882d28a51533f97440867f012d4da091"},
+ {file = "pydantic_settings-2.2.1.tar.gz", hash = "sha256:00b9f6a5e95553590434c0fa01ead0b216c3e10bc54ae02e37f359948643c5ed"},
+]
+
+[package.dependencies]
+pydantic = ">=2.3.0"
+python-dotenv = ">=0.21.0"
[package.extras]
-dotenv = ["python-dotenv (>=0.10.4)"]
-email = ["email-validator (>=1.0.3)"]
+toml = ["tomli (>=2.0.1)"]
+yaml = ["pyyaml (>=6.0.1)"]
[[package]]
name = "pydis-core"
-version = "9.9.1"
+version = "10.7.0"
description = "PyDis core provides core functionality and utility to the bots of the Python Discord community."
optional = false
python-versions = ">=3.10.dev0,<3.12.dev0"
files = [
- {file = "pydis_core-9.9.1-py3-none-any.whl", hash = "sha256:456c81b226c4b49611139c5502dc69f04ca5ba46088d17d9f75176e989f77ebc"},
- {file = "pydis_core-9.9.1.tar.gz", hash = "sha256:80b97eae4397e46eea90e88eccdec993a6c1859668150d32af08e80e15cefb2c"},
+ {file = "pydis_core-10.7.0-py3-none-any.whl", hash = "sha256:bd53daf83d4a073177d92591f3747884e5b9abd67d34fdf49766d5a9ebfd63f4"},
+ {file = "pydis_core-10.7.0.tar.gz", hash = "sha256:e48d2ea429427229ee5d5bc9fc303688957f431a444db0609bb31eb46c8168dd"},
]
[package.dependencies]
-aiodns = "3.0.0"
+aiodns = ">=3.1,<4.0"
async-rediscache = {version = "1.0.0rc2", extras = ["fakeredis"], optional = true, markers = "extra == \"async-rediscache\""}
-"discord.py" = "2.3.0"
-statsd = "4.0.1"
+"discord.py" = ">=2.3.2,<2.4.0"
+pydantic = ">=1.7.4,<3.0.0"
+statsd = ">=4.0,<5.0"
[package.extras]
async-rediscache = ["async-rediscache[fakeredis] (==1.0.0rc2)"]
@@ -1457,23 +1562,23 @@ files = [
[[package]]
name = "pytest"
-version = "7.4.0"
+version = "8.1.1"
description = "pytest: simple powerful testing with Python"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"},
- {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"},
+ {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"},
+ {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
-pluggy = ">=0.12,<2.0"
+pluggy = ">=1.4,<2.0"
[package.extras]
-testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[[package]]
name = "pytest-cov"
@@ -1495,13 +1600,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale
[[package]]
name = "pytest-subtests"
-version = "0.11.0"
+version = "0.12.1"
description = "unittest subTest() support and subtests fixture"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-subtests-0.11.0.tar.gz", hash = "sha256:51865c88457545f51fb72011942f0a3c6901ee9e24cbfb6d1b9dc1348bafbe37"},
- {file = "pytest_subtests-0.11.0-py3-none-any.whl", hash = "sha256:453389984952eec85ab0ce0c4f026337153df79587048271c7fd0f49119c07e4"},
+ {file = "pytest-subtests-0.12.1.tar.gz", hash = "sha256:d6605dcb88647e0b7c1889d027f8ef1c17d7a2c60927ebfdc09c7b0d8120476d"},
+ {file = "pytest_subtests-0.12.1-py3-none-any.whl", hash = "sha256:100d9f7eb966fc98efba7026c802812ae327e8b5b37181fb260a2ea93226495c"},
]
[package.dependencies]
@@ -1510,13 +1615,13 @@ pytest = ">=7.0"
[[package]]
name = "pytest-xdist"
-version = "3.3.1"
+version = "3.5.0"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"},
- {file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"},
+ {file = "pytest-xdist-3.5.0.tar.gz", hash = "sha256:cbb36f3d67e0c478baa57fa4edc8843887e0f6cfc42d677530a36d7472b32d8a"},
+ {file = "pytest_xdist-3.5.0-py3-none-any.whl", hash = "sha256:d075629c7e00b611df89f490a5063944bee7a4362a5ff11c7cc7824a03dfce24"},
]
[package.dependencies]
@@ -1530,13 +1635,13 @@ testing = ["filelock"]
[[package]]
name = "python-dateutil"
-version = "2.8.2"
+version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
- {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
- {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
+ {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
@@ -1544,13 +1649,13 @@ six = ">=1.5"
[[package]]
name = "python-dotenv"
-version = "1.0.0"
+version = "1.0.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
python-versions = ">=3.8"
files = [
- {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"},
- {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"},
+ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
+ {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
]
[package.extras]
@@ -1558,13 +1663,13 @@ cli = ["click (>=5.0)"]
[[package]]
name = "python-frontmatter"
-version = "1.0.0"
+version = "1.1.0"
description = "Parse and manage posts with YAML (or other) frontmatter"
optional = false
python-versions = "*"
files = [
- {file = "python-frontmatter-1.0.0.tar.gz", hash = "sha256:e98152e977225ddafea6f01f40b4b0f1de175766322004c826ca99842d19a7cd"},
- {file = "python_frontmatter-1.0.0-py3-none-any.whl", hash = "sha256:766ae75f1b301ffc5fe3494339147e0fd80bc3deff3d7590a93991978b579b08"},
+ {file = "python-frontmatter-1.1.0.tar.gz", hash = "sha256:7118d2bd56af9149625745c58c9b51fb67e8d1294a0c76796dafdc72c36e5f6d"},
+ {file = "python_frontmatter-1.1.0-py3-none-any.whl", hash = "sha256:335465556358d9d0e6c98bbeb69b1c969f2a4a21360587b9873bfc3b213407c1"},
]
[package.dependencies]
@@ -1572,156 +1677,165 @@ PyYAML = "*"
[package.extras]
docs = ["sphinx"]
-test = ["pyaml", "pytest", "toml"]
+test = ["mypy", "pyaml", "pytest", "toml", "types-PyYAML", "types-toml"]
[[package]]
name = "pyyaml"
-version = "6.0"
+version = "6.0.1"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.6"
files = [
- {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
- {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
- {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
- {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
- {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
- {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
- {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
- {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
- {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
- {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
- {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
- {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
- {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
- {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
- {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
- {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
- {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
- {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
- {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
- {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
- {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
- {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
- {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
- {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
- {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
- {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
- {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
- {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
- {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
- {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
- {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
- {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
- {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
- {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
- {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
- {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
- {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
- {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
- {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
- {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
+ {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
+ {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
+ {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
+ {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
+ {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
+ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
[[package]]
name = "rapidfuzz"
-version = "3.1.1"
+version = "3.6.1"
description = "rapid fuzzy string matching"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "rapidfuzz-3.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:17e4cbe6632aae7c35101c4b7c498e83f6eacf61be0def4ff98167df30dc69ca"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:167dbce2da6bb5b73d43e53434c5a9d7d1214b658b315420e44044782f4c482b"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdee4f4d04761ce167538adbefa01a64e7cab949d89aa09df39ef0d5e859fb2a"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e77ed7d0bd8d9be530c462c921904ada8d3417671eed749784c5a315af334d"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdd2ab5ab56fcaf839a9f58caa8756dbfeba0b3dc187850b763d0a1e6ee9c97a"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0843c53d54d5b7d6122d8f1d7574d8c91a7aacc5c316f74d6e33d98aec82949d"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3e953dcef0302eeb4fe8c7c4907e50d175199fc07da05ad6bd1d8d141ff138"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec5523d5c08c639cd4e301d42f3ad7c6fb061a1f1cd6b5b627e59af345edfed7"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b4995792e106c3f1ab6f56dd6089918b065888e2e55a71e3fea8d0f66bf30989"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cdbf9a76ea47f14026daaed43a2c2150ab0e9a4d5396909f028380f33e61c522"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f25d1975e846d07990cf946a5927a932aa7cccd308ae9979b03a58ff1cd80087"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e0755f5ac6c3d1dc2505eb2e6eaf5508ff17b42c084406714fbabf2d50d098b6"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:de784bbe06d32e66617cd20766c37aae2438902d54b3fa608d2e0a929ca705f4"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-win32.whl", hash = "sha256:ef6c38040d868dcc0132fad377aafeb5b2da71354759e77f41ae599316df2dee"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:7c74fde444bcd13ef3a803c578b28f33b4f9edf368f46ca3de57fda456065967"},
- {file = "rapidfuzz-3.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:e549da8d68ad4ee385c918ea8b9efeda875df9edf6c6b48df927bd061c00bfef"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:58ca539cc6ce385d650138a9b1908b05622c2dd08a23d5aea4890523ef3774d5"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91946c496e6f380939dbea14ff6ce6de87480445c09d03964f5374101462594b"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f2024f83a9300440e845b441e71726471f7567021c1d80796ca02e71c5f0dc2"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17b017f9e1b88dfd6d9b03170ef8e86477de0d9d37fbfcbe72ca070cacbe1b65"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e6772eb7cc4429f1eae5a9b41e5b0b1af8f0d50727c6e338d9ad5bceee01da5a"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c089ce856919e03f4dd8f9168d60ac580d30cd0451fd60dcdef73010eca68973"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f2cd9a3760080876fc59edb26926e51d6db44dea65e85f1eb04aa5f58c3bc41"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f32791ee045a7b3d6a56208a55d996d5f7a32fdb688f5c5ee899cb7589539eb"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:68d910048b36613701ea671de68f701e2c1ba2839295238def840ff1fc1b15f4"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6f767d4823002e65c06ea273f952fda2b88775e1c2d508564f04d32cdd7f65b2"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:10313075642a9f1f948d356f4f0803ae28a496d7967b466b9cae1a4be8aa4df3"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1465ea085154378e69bf4bc5e27bdac5c94684416882ace31865232adc9239a2"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:53e3c588e7ea158fa80095dd0ff53f49e2ede9a8d71a3a5b964ca045d845a9b9"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-win32.whl", hash = "sha256:cb08db5c122fea4196483b82f7596e50ef9cab1770f7696c197bf0815ac4dd17"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:b7c65112c87568274d399ad7a62902cef17801c2bd047b162e79e43758b3ce27"},
- {file = "rapidfuzz-3.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:ea3e46a534de97a6cad2018cb950492a0fcacad380e35440ce3c1c8fef96a261"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a8bb256b34fcad4f3fa00be6b57fe35bcb54f031911195929145c67d9738ffec"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51f21f37aec6bc117e9083181ddc3cbbcbf56b6506492b128d8e836d3545ca80"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a371846f45ed9d24927a8d5222884536c1e171543396b36250fafb2e848bc92"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:25eea5c8006b6c8747ca204675c9e939f3c4d27167fb43b2aa211443d34f9abd"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:db5e71e5a810d2f1163c914e01b3ba241409a98286ac4850ff26076115ae401b"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c07e16ab38e717931319cff1340debbf2ef940a1cda4eb70e323079b62df306"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aadc5a8b9859737a8f87831215b7fab0c04afeb960bb987c528421a4e6dfb8b6"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0de229cb613be060580c71c1674acbde57921c7ed33d7a726e071a2562924113"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b1bf8aba99b267aad0a01dfb44ee39803676007724abcfb72129c350476b2341"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d3264e4a02e4148e30078104fb0c1b6c8eb166ddc5ebe843a22433f58f87dc47"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:712331c1c70c79a219c2ac233b4e25e75ffad51042840d147d5e94519c7d8a1a"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:6ede2d42ad55bd4e7a3394e98c5f58ddace78775493391732d32be61268a4116"},
- {file = "rapidfuzz-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:32a5c47b5153f25eb512dbb91f9850225d2dcfb3404a1c48406726c7732b0726"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:51bb8f7aa4fe45618e75cdccf08491c752a7f137ffbf7d3afd1809791ac8c326"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:788fb03c5acb5b48f5f918f4cbb5dc072498becf018c64e7e27d6b76e63e68b8"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dc7f25e20781c8d42e813516ee4ff9043ecce4a8e25fc94ee6732a83d81c1c99"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4a751f216fd1222a4a8c7ceff5180872a156202c3bdca1b337e5a5b09298dfd"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83b48b789f2da1688882cba595c40179194ab15ec17ea1d4c9de9ee239649904"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a6f5cd9f1282da49b8d0747c40f3fea2d64ab5e4c2cc2295baf87ff7a0d062"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5fe8054c244bf63be2380efc275edd86da3a706460d42911dc3ff914f3260a5"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4d509e9aa011e1be5e4da7c5062dc4fc3688714687110536925980b3d03ac6"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ccc1b5b467766110085c80bb9311d233fccc8ed1ce965aebba3125e1bab04cba"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7e181411958d04d5b437a0981e87815e8f1b1909f5ae0e339246d3bc464f53e7"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:c53cf36cdb10819b7154fefdbffbef442ba567d9c1ca74a7e76fd759ace45e6c"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:851b44130393139cb336aa54c681d595d75a3160b7be330f3acc0c3b9dabce70"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49d900da023eeb3bfbe9feee126312eb9fd0458129aa5a581e4d8d8bf4483d14"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-win32.whl", hash = "sha256:6c0e96821029c46847df4ff266ea283a2b6163a4f76a4567f9986934e9c4410c"},
- {file = "rapidfuzz-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7af18372f576e36e93f4662bdf64043ac23dfa02d7f768d7e7e1d0211bb9cb35"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8b966344ed4122a71ab8ccdca2954db1ce0d8049cb9bcac58db07558f9d9ec32"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a293370448f2e46fdc6e086ac99923015bdc53973a65d3df35aefc685e1a5809"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:351d253fdee62d6d0e80c75f0505accc1ce8cc73a50779c60986ef21c92f20f9"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e951c874a0e5b375b2af9b5f264eefc679c0685c166ee0641e703ef0795509b"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4019def8a18bc867ac61f08a542bf474a7a9b3f662f5d5cd169c9135866562f5"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:086a2d84c2e497e3ab160ccf164e319bca874d9383d008fcadf91ede8ac7997f"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d4da453fbd8793ebb11bed396f8a4b9041d6227bf055903447305dd7942312f"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f56af1d46fbeaaa0dc50901c2dc439c7a455cfdac2f1acf6cffeb65ae82c48"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7726f67e4a0b2b4392f03aa62e16b12a697156c6735df27b21bd3ab561b01659"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d72916d27fb88741bfb576b0b0639354ca00f5e91046171c985262c68a86bbb5"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8c85bb6946fb02231d1e60ab45c36ecee04ecf7f725e094f5beee798b6b7d36d"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:fb7049dff52cded65184a3d2ff45cfd226bff7314f49a8f4b83f943eea9181a7"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:408007b4bc5a0a0cb9bfcdcc8cffa9b71fec6ee53ccdf9c26b57539f7e264ab5"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-win32.whl", hash = "sha256:9dc7154889937ca5a004d17f62b4798e0af52f69c38eb3112dbdb52b006d4419"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:16c506bac2e0a6f6581b334a7802c2f0d8343ec1d77e5cf9452c33d6219abef8"},
- {file = "rapidfuzz-3.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:5e11e11880951e767342b56627ab2dc9d3ef90e2605b656e9b5e6e0beadaaf0f"},
- {file = "rapidfuzz-3.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a8b8f32463781e4703965c9cf7a609a19a74478f332e0d62cd9d0e7a9db91321"},
- {file = "rapidfuzz-3.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b408ac3c7f8c3414bfd5c6044ca4bb385b390bcf5eae3ad884cef48628c131ae"},
- {file = "rapidfuzz-3.1.1-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ff1a517de2b1e80ddf1a3037a6ebca9925154c1af70751518d50d5c332e1ec8"},
- {file = "rapidfuzz-3.1.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e23665be5918f979180130babedab9317fbb34cdae237c7defad7e86bc684e"},
- {file = "rapidfuzz-3.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:15260263a0c7bffac934a53b6622d77e06e10929ee4d2e62ac6f70c13988f351"},
- {file = "rapidfuzz-3.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f7acc5c9c7cf567372de5b6c817f93db508e7b9bd7f29bd6187df8d2cc60ced5"},
- {file = "rapidfuzz-3.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79f5a3ab7ff6c46336f38690f0564bc7689cefa180257ed9078c42f75b10c9d2"},
- {file = "rapidfuzz-3.1.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:362e366e79fcc9a8866b41f20ef4d2987a06f8b134096e659594c059aa8a6d88"},
- {file = "rapidfuzz-3.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:819d9317c3d86b508d87ab1bca5867f3abc18b902c822bc57366ccc6330a030b"},
- {file = "rapidfuzz-3.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4a64ddfb7084b678da7778c1263aee2baae5a2ca55ec5589a022defc38103eb1"},
- {file = "rapidfuzz-3.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8243bb4bb4db7c3501932ced6a978b284e19c3619b6802455e47bfd0905adb81"},
- {file = "rapidfuzz-3.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39c7d0dbd77a7f28ff85a1dff2afb2ed73e5cd81cca3f654450ed339a271c0ab"},
- {file = "rapidfuzz-3.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4afab735bb0ac3ec9bafcc35376ed336d26af6140c4d81e4c869e77df77ecd5"},
- {file = "rapidfuzz-3.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69d503a7641b5a63aa53c7aca0b857d38f48cd7bae39f8563679b324e3d2d47a"},
- {file = "rapidfuzz-3.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ef3ad80458e47723812976a2ea1282ff207ad20e6cb19da1917f76699bd5aaa5"},
- {file = "rapidfuzz-3.1.1.tar.gz", hash = "sha256:a06a08be3cb7d7df7993dd16e84aaf59bd5a7ff98a9f1b3e893d18b273a71c64"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ac434fc71edda30d45db4a92ba5e7a42c7405e1a54cb4ec01d03cc668c6dcd40"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2a791168e119cfddf4b5a40470620c872812042f0621e6a293983a2d52372db0"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a2f3e9df346145c2be94e4d9eeffb82fab0cbfee85bd4a06810e834fe7c03fa"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23de71e7f05518b0bbeef55d67b5dbce3bcd3e2c81e7e533051a2e9401354eb0"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d056e342989248d2bdd67f1955bb7c3b0ecfa239d8f67a8dfe6477b30872c607"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01835d02acd5d95c1071e1da1bb27fe213c84a013b899aba96380ca9962364bc"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f712e0bb5fea327e92aec8a937afd07ba8de4c529735d82e4c4124c10d5a0"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96cd19934f76a1264e8ecfed9d9f5291fde04ecb667faef5f33bdbfd95fe2d1f"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e06c4242a1354cf9d48ee01f6f4e6e19c511d50bb1e8d7d20bcadbb83a2aea90"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d73dcfe789d37c6c8b108bf1e203e027714a239e50ad55572ced3c004424ed3b"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e98ff000e2619e7cfe552d086815671ed09b6899408c2c1b5103658261f6f3"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:08b6fb47dd889c69fbc0b915d782aaed43e025df6979b6b7f92084ba55edd526"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a1788ebb5f5b655a15777e654ea433d198f593230277e74d51a2a1e29a986283"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-win32.whl", hash = "sha256:c65f92881753aa1098c77818e2b04a95048f30edbe9c3094dc3707d67df4598b"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:4243a9c35667a349788461aae6471efde8d8800175b7db5148a6ab929628047f"},
+ {file = "rapidfuzz-3.6.1-cp310-cp310-win_arm64.whl", hash = "sha256:f59d19078cc332dbdf3b7b210852ba1f5db8c0a2cd8cc4c0ed84cc00c76e6802"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fbc07e2e4ac696497c5f66ec35c21ddab3fc7a406640bffed64c26ab2f7ce6d6"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cced1a8852652813f30fb5d4b8f9b237112a0bbaeebb0f4cc3611502556764"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82300e5f8945d601c2daaaac139d5524d7c1fdf719aa799a9439927739917460"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edf97c321fd641fea2793abce0e48fa4f91f3c202092672f8b5b4e781960b891"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7420e801b00dee4a344ae2ee10e837d603461eb180e41d063699fb7efe08faf0"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:060bd7277dc794279fa95522af355034a29c90b42adcb7aa1da358fc839cdb11"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7e3375e4f2bfec77f907680328e4cd16cc64e137c84b1886d547ab340ba6928"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a490cd645ef9d8524090551016f05f052e416c8adb2d8b85d35c9baa9d0428ab"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2e03038bfa66d2d7cffa05d81c2f18fd6acbb25e7e3c068d52bb7469e07ff382"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2b19795b26b979c845dba407fe79d66975d520947b74a8ab6cee1d22686f7967"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:064c1d66c40b3a0f488db1f319a6e75616b2e5fe5430a59f93a9a5e40a656d15"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3c772d04fb0ebeece3109d91f6122b1503023086a9591a0b63d6ee7326bd73d9"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:841eafba6913c4dfd53045835545ba01a41e9644e60920c65b89c8f7e60c00a9"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-win32.whl", hash = "sha256:266dd630f12696ea7119f31d8b8e4959ef45ee2cbedae54417d71ae6f47b9848"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:d79aec8aeee02ab55d0ddb33cea3ecd7b69813a48e423c966a26d7aab025cdfe"},
+ {file = "rapidfuzz-3.6.1-cp311-cp311-win_arm64.whl", hash = "sha256:484759b5dbc5559e76fefaa9170147d1254468f555fd9649aea3bad46162a88b"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b2ef4c0fd3256e357b70591ffb9e8ed1d439fb1f481ba03016e751a55261d7c1"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:588c4b20fa2fae79d60a4e438cf7133d6773915df3cc0a7f1351da19eb90f720"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7142ee354e9c06e29a2636b9bbcb592bb00600a88f02aa5e70e4f230347b373e"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1dfc557c0454ad22382373ec1b7df530b4bbd974335efe97a04caec936f2956a"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03f73b381bdeccb331a12c3c60f1e41943931461cdb52987f2ecf46bfc22f50d"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b0ccc2ec1781c7e5370d96aef0573dd1f97335343e4982bdb3a44c133e27786"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da3e8c9f7e64bb17faefda085ff6862ecb3ad8b79b0f618a6cf4452028aa2222"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fde9b14302a31af7bdafbf5cfbb100201ba21519be2b9dedcf4f1048e4fbe65d"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1a23eee225dfb21c07f25c9fcf23eb055d0056b48e740fe241cbb4b22284379"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e49b9575d16c56c696bc7b06a06bf0c3d4ef01e89137b3ddd4e2ce709af9fe06"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:0a9fc714b8c290261669f22808913aad49553b686115ad0ee999d1cb3df0cd66"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a3ee4f8f076aa92184e80308fc1a079ac356b99c39408fa422bbd00145be9854"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f056ba42fd2f32e06b2c2ba2443594873cfccc0c90c8b6327904fc2ddf6d5799"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-win32.whl", hash = "sha256:5d82b9651e3d34b23e4e8e201ecd3477c2baa17b638979deeabbb585bcb8ba74"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:dad55a514868dae4543ca48c4e1fc0fac704ead038dafedf8f1fc0cc263746c1"},
+ {file = "rapidfuzz-3.6.1-cp312-cp312-win_arm64.whl", hash = "sha256:3c84294f4470fcabd7830795d754d808133329e0a81d62fcc2e65886164be83b"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e19d519386e9db4a5335a4b29f25b8183a1c3f78cecb4c9c3112e7f86470e37f"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:01eb03cd880a294d1bf1a583fdd00b87169b9cc9c9f52587411506658c864d73"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:be368573255f8fbb0125a78330a1a40c65e9ba3c5ad129a426ff4289099bfb41"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3e5af946f419c30f5cb98b69d40997fe8580efe78fc83c2f0f25b60d0e56efb"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f382f7ffe384ce34345e1c0b2065451267d3453cadde78946fbd99a59f0cc23c"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be156f51f3a4f369e758505ed4ae64ea88900dcb2f89d5aabb5752676d3f3d7e"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1936d134b6c513fbe934aeb668b0fee1ffd4729a3c9d8d373f3e404fbb0ce8a0"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ff8eaf4a9399eb2bebd838f16e2d1ded0955230283b07376d68947bbc2d33d"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ae598a172e3a95df3383634589660d6b170cc1336fe7578115c584a99e0ba64d"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cd4ba4c18b149da11e7f1b3584813159f189dc20833709de5f3df8b1342a9759"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:0402f1629e91a4b2e4aee68043a30191e5e1b7cd2aa8dacf50b1a1bcf6b7d3ab"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:1e12319c6b304cd4c32d5db00b7a1e36bdc66179c44c5707f6faa5a889a317c0"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0bbfae35ce4de4c574b386c43c78a0be176eeddfdae148cb2136f4605bebab89"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-win32.whl", hash = "sha256:7fec74c234d3097612ea80f2a80c60720eec34947066d33d34dc07a3092e8105"},
+ {file = "rapidfuzz-3.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:a553cc1a80d97459d587529cc43a4c7c5ecf835f572b671107692fe9eddf3e24"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:757dfd7392ec6346bd004f8826afb3bf01d18a723c97cbe9958c733ab1a51791"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2963f4a3f763870a16ee076796be31a4a0958fbae133dbc43fc55c3968564cf5"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d2f0274595cc5b2b929c80d4e71b35041104b577e118cf789b3fe0a77b37a4c5"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f211e366e026de110a4246801d43a907cd1a10948082f47e8a4e6da76fef52"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a59472b43879012b90989603aa5a6937a869a72723b1bf2ff1a0d1edee2cc8e6"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a03863714fa6936f90caa7b4b50ea59ea32bb498cc91f74dc25485b3f8fccfe9"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd95b6b7bfb1584f806db89e1e0c8dbb9d25a30a4683880c195cc7f197eaf0c"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7183157edf0c982c0b8592686535c8b3e107f13904b36d85219c77be5cefd0d8"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ad9d74ef7c619b5b0577e909582a1928d93e07d271af18ba43e428dc3512c2a1"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b53137d81e770c82189e07a8f32722d9e4260f13a0aec9914029206ead38cac3"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:49b9ed2472394d306d5dc967a7de48b0aab599016aa4477127b20c2ed982dbf9"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:dec307b57ec2d5054d77d03ee4f654afcd2c18aee00c48014cb70bfed79597d6"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4381023fa1ff32fd5076f5d8321249a9aa62128eb3f21d7ee6a55373e672b261"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-win32.whl", hash = "sha256:8d7a072f10ee57c8413c8ab9593086d42aaff6ee65df4aa6663eecdb7c398dca"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:ebcfb5bfd0a733514352cfc94224faad8791e576a80ffe2fd40b2177bf0e7198"},
+ {file = "rapidfuzz-3.6.1-cp39-cp39-win_arm64.whl", hash = "sha256:1c47d592e447738744905c18dda47ed155620204714e6df20eb1941bb1ba315e"},
+ {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:eef8b346ab331bec12bbc83ac75641249e6167fab3d84d8f5ca37fd8e6c7a08c"},
+ {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53251e256017e2b87f7000aee0353ba42392c442ae0bafd0f6b948593d3f68c6"},
+ {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6dede83a6b903e3ebcd7e8137e7ff46907ce9316e9d7e7f917d7e7cdc570ee05"},
+ {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4da90e4c2b444d0a171d7444ea10152e07e95972bb40b834a13bdd6de1110c"},
+ {file = "rapidfuzz-3.6.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:ca3dfcf74f2b6962f411c33dd95b0adf3901266e770da6281bc96bb5a8b20de9"},
+ {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bcc957c0a8bde8007f1a8a413a632a1a409890f31f73fe764ef4eac55f59ca87"},
+ {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c9a50bea7a8537442834f9bc6b7d29d8729a5b6379df17c31b6ab4df948c2"},
+ {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c23ceaea27e790ddd35ef88b84cf9d721806ca366199a76fd47cfc0457a81b"},
+ {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b155e67fff215c09f130555002e42f7517d0ea72cbd58050abb83cb7c880cec"},
+ {file = "rapidfuzz-3.6.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3028ee8ecc48250607fa8a0adce37b56275ec3b1acaccd84aee1f68487c8557b"},
+ {file = "rapidfuzz-3.6.1.tar.gz", hash = "sha256:35660bee3ce1204872574fa041c7ad7ec5175b3053a4cb6e181463fc07013de7"},
]
[package.extras]
@@ -1729,13 +1843,13 @@ full = ["numpy"]
[[package]]
name = "redis"
-version = "4.5.5"
+version = "4.6.0"
description = "Python client for Redis database and key-value store"
optional = false
python-versions = ">=3.7"
files = [
- {file = "redis-4.5.5-py3-none-any.whl", hash = "sha256:77929bc7f5dab9adf3acba2d3bb7d7658f1e0c2f1cafe7eb36434e751c471119"},
- {file = "redis-4.5.5.tar.gz", hash = "sha256:dc87a0bdef6c8bfe1ef1e1c40be7034390c2ae02d92dcd0c7ca1729443899880"},
+ {file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"},
+ {file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"},
]
[package.dependencies]
@@ -1747,99 +1861,104 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
[[package]]
name = "regex"
-version = "2023.6.3"
+version = "2023.12.25"
description = "Alternative regular expression module, to replace re."
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.7"
files = [
- {file = "regex-2023.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:824bf3ac11001849aec3fa1d69abcb67aac3e150a933963fb12bda5151fe1bfd"},
- {file = "regex-2023.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05ed27acdf4465c95826962528f9e8d41dbf9b1aa8531a387dee6ed215a3e9ef"},
- {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b49c764f88a79160fa64f9a7b425620e87c9f46095ef9c9920542ab2495c8bc"},
- {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e3f1316c2293e5469f8f09dc2d76efb6c3982d3da91ba95061a7e69489a14ef"},
- {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43e1dd9d12df9004246bacb79a0e5886b3b6071b32e41f83b0acbf293f820ee8"},
- {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4959e8bcbfda5146477d21c3a8ad81b185cd252f3d0d6e4724a5ef11c012fb06"},
- {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af4dd387354dc83a3bff67127a124c21116feb0d2ef536805c454721c5d7993d"},
- {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2239d95d8e243658b8dbb36b12bd10c33ad6e6933a54d36ff053713f129aa536"},
- {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:890e5a11c97cf0d0c550eb661b937a1e45431ffa79803b942a057c4fb12a2da2"},
- {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a8105e9af3b029f243ab11ad47c19b566482c150c754e4c717900a798806b222"},
- {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:25be746a8ec7bc7b082783216de8e9473803706723b3f6bef34b3d0ed03d57e2"},
- {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3676f1dd082be28b1266c93f618ee07741b704ab7b68501a173ce7d8d0d0ca18"},
- {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:10cb847aeb1728412c666ab2e2000ba6f174f25b2bdc7292e7dd71b16db07568"},
- {file = "regex-2023.6.3-cp310-cp310-win32.whl", hash = "sha256:dbbbfce33cd98f97f6bffb17801b0576e653f4fdb1d399b2ea89638bc8d08ae1"},
- {file = "regex-2023.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:c5f8037000eb21e4823aa485149f2299eb589f8d1fe4b448036d230c3f4e68e0"},
- {file = "regex-2023.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c123f662be8ec5ab4ea72ea300359023a5d1df095b7ead76fedcd8babbedf969"},
- {file = "regex-2023.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9edcbad1f8a407e450fbac88d89e04e0b99a08473f666a3f3de0fd292badb6aa"},
- {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcba6dae7de533c876255317c11f3abe4907ba7d9aa15d13e3d9710d4315ec0e"},
- {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29cdd471ebf9e0f2fb3cac165efedc3c58db841d83a518b082077e612d3ee5df"},
- {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12b74fbbf6cbbf9dbce20eb9b5879469e97aeeaa874145517563cca4029db65c"},
- {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c29ca1bd61b16b67be247be87390ef1d1ef702800f91fbd1991f5c4421ebae8"},
- {file = "regex-2023.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77f09bc4b55d4bf7cc5eba785d87001d6757b7c9eec237fe2af57aba1a071d9"},
- {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ea353ecb6ab5f7e7d2f4372b1e779796ebd7b37352d290096978fea83c4dba0c"},
- {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:10590510780b7541969287512d1b43f19f965c2ece6c9b1c00fc367b29d8dce7"},
- {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e2fbd6236aae3b7f9d514312cdb58e6494ee1c76a9948adde6eba33eb1c4264f"},
- {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:6b2675068c8b56f6bfd5a2bda55b8accbb96c02fd563704732fd1c95e2083461"},
- {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74419d2b50ecb98360cfaa2974da8689cb3b45b9deff0dcf489c0d333bcc1477"},
- {file = "regex-2023.6.3-cp311-cp311-win32.whl", hash = "sha256:fb5ec16523dc573a4b277663a2b5a364e2099902d3944c9419a40ebd56a118f9"},
- {file = "regex-2023.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:09e4a1a6acc39294a36b7338819b10baceb227f7f7dbbea0506d419b5a1dd8af"},
- {file = "regex-2023.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0654bca0cdf28a5956c83839162692725159f4cda8d63e0911a2c0dc76166525"},
- {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:463b6a3ceb5ca952e66550a4532cef94c9a0c80dc156c4cc343041951aec1697"},
- {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87b2a5bb5e78ee0ad1de71c664d6eb536dc3947a46a69182a90f4410f5e3f7dd"},
- {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6343c6928282c1f6a9db41f5fd551662310e8774c0e5ebccb767002fcf663ca9"},
- {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6192d5af2ccd2a38877bfef086d35e6659566a335b1492786ff254c168b1693"},
- {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74390d18c75054947e4194019077e243c06fbb62e541d8817a0fa822ea310c14"},
- {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:742e19a90d9bb2f4a6cf2862b8b06dea5e09b96c9f2df1779e53432d7275331f"},
- {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8abbc5d54ea0ee80e37fef009e3cec5dafd722ed3c829126253d3e22f3846f1e"},
- {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c2b867c17a7a7ae44c43ebbeb1b5ff406b3e8d5b3e14662683e5e66e6cc868d3"},
- {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d831c2f8ff278179705ca59f7e8524069c1a989e716a1874d6d1aab6119d91d1"},
- {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ee2d1a9a253b1729bb2de27d41f696ae893507c7db224436abe83ee25356f5c1"},
- {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:61474f0b41fe1a80e8dfa70f70ea1e047387b7cd01c85ec88fa44f5d7561d787"},
- {file = "regex-2023.6.3-cp36-cp36m-win32.whl", hash = "sha256:0b71e63226e393b534105fcbdd8740410dc6b0854c2bfa39bbda6b0d40e59a54"},
- {file = "regex-2023.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bbb02fd4462f37060122e5acacec78e49c0fbb303c30dd49c7f493cf21fc5b27"},
- {file = "regex-2023.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b862c2b9d5ae38a68b92e215b93f98d4c5e9454fa36aae4450f61dd33ff48487"},
- {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976d7a304b59ede34ca2921305b57356694f9e6879db323fd90a80f865d355a3"},
- {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:83320a09188e0e6c39088355d423aa9d056ad57a0b6c6381b300ec1a04ec3d16"},
- {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9427a399501818a7564f8c90eced1e9e20709ece36be701f394ada99890ea4b3"},
- {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178bbc1b2ec40eaca599d13c092079bf529679bf0371c602edaa555e10b41c3"},
- {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:837328d14cde912af625d5f303ec29f7e28cdab588674897baafaf505341f2fc"},
- {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d44dc13229905ae96dd2ae2dd7cebf824ee92bc52e8cf03dcead37d926da019"},
- {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d54af539295392611e7efbe94e827311eb8b29668e2b3f4cadcfe6f46df9c777"},
- {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7117d10690c38a622e54c432dfbbd3cbd92f09401d622902c32f6d377e2300ee"},
- {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bb60b503ec8a6e4e3e03a681072fa3a5adcbfa5479fa2d898ae2b4a8e24c4591"},
- {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:65ba8603753cec91c71de423a943ba506363b0e5c3fdb913ef8f9caa14b2c7e0"},
- {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:271f0bdba3c70b58e6f500b205d10a36fb4b58bd06ac61381b68de66442efddb"},
- {file = "regex-2023.6.3-cp37-cp37m-win32.whl", hash = "sha256:9beb322958aaca059f34975b0df135181f2e5d7a13b84d3e0e45434749cb20f7"},
- {file = "regex-2023.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fea75c3710d4f31389eed3c02f62d0b66a9da282521075061ce875eb5300cf23"},
- {file = "regex-2023.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f56fcb7ff7bf7404becdfc60b1e81a6d0561807051fd2f1860b0d0348156a07"},
- {file = "regex-2023.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d2da3abc88711bce7557412310dfa50327d5769a31d1c894b58eb256459dc289"},
- {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a99b50300df5add73d307cf66abea093304a07eb017bce94f01e795090dea87c"},
- {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5708089ed5b40a7b2dc561e0c8baa9535b77771b64a8330b684823cfd5116036"},
- {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:687ea9d78a4b1cf82f8479cab23678aff723108df3edeac098e5b2498879f4a7"},
- {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d3850beab9f527f06ccc94b446c864059c57651b3f911fddb8d9d3ec1d1b25d"},
- {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8915cc96abeb8983cea1df3c939e3c6e1ac778340c17732eb63bb96247b91d2"},
- {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:841d6e0e5663d4c7b4c8099c9997be748677d46cbf43f9f471150e560791f7ff"},
- {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9edce5281f965cf135e19840f4d93d55b3835122aa76ccacfd389e880ba4cf82"},
- {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b956231ebdc45f5b7a2e1f90f66a12be9610ce775fe1b1d50414aac1e9206c06"},
- {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:36efeba71c6539d23c4643be88295ce8c82c88bbd7c65e8a24081d2ca123da3f"},
- {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:cf67ca618b4fd34aee78740bea954d7c69fdda419eb208c2c0c7060bb822d747"},
- {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b4598b1897837067a57b08147a68ac026c1e73b31ef6e36deeeb1fa60b2933c9"},
- {file = "regex-2023.6.3-cp38-cp38-win32.whl", hash = "sha256:f415f802fbcafed5dcc694c13b1292f07fe0befdb94aa8a52905bd115ff41e88"},
- {file = "regex-2023.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:d4f03bb71d482f979bda92e1427f3ec9b220e62a7dd337af0aa6b47bf4498f72"},
- {file = "regex-2023.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccf91346b7bd20c790310c4147eee6ed495a54ddb6737162a36ce9dbef3e4751"},
- {file = "regex-2023.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b28f5024a3a041009eb4c333863d7894d191215b39576535c6734cd88b0fcb68"},
- {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0bb18053dfcfed432cc3ac632b5e5e5c5b7e55fb3f8090e867bfd9b054dbcbf"},
- {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5bfb3004f2144a084a16ce19ca56b8ac46e6fd0651f54269fc9e230edb5e4a"},
- {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c6b48d0fa50d8f4df3daf451be7f9689c2bde1a52b1225c5926e3f54b6a9ed1"},
- {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:051da80e6eeb6e239e394ae60704d2b566aa6a7aed6f2890a7967307267a5dc6"},
- {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4c3b7fa4cdaa69268748665a1a6ff70c014d39bb69c50fda64b396c9116cf77"},
- {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:457b6cce21bee41ac292d6753d5e94dcbc5c9e3e3a834da285b0bde7aa4a11e9"},
- {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aad51907d74fc183033ad796dd4c2e080d1adcc4fd3c0fd4fd499f30c03011cd"},
- {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0385e73da22363778ef2324950e08b689abdf0b108a7d8decb403ad7f5191938"},
- {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c6a57b742133830eec44d9b2290daf5cbe0a2f1d6acee1b3c7b1c7b2f3606df7"},
- {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3e5219bf9e75993d73ab3d25985c857c77e614525fac9ae02b1bebd92f7cecac"},
- {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e5087a3c59eef624a4591ef9eaa6e9a8d8a94c779dade95d27c0bc24650261cd"},
- {file = "regex-2023.6.3-cp39-cp39-win32.whl", hash = "sha256:20326216cc2afe69b6e98528160b225d72f85ab080cbdf0b11528cbbaba2248f"},
- {file = "regex-2023.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:bdff5eab10e59cf26bc479f565e25ed71a7d041d1ded04ccf9aee1d9f208487a"},
- {file = "regex-2023.6.3.tar.gz", hash = "sha256:72d1a25bf36d2050ceb35b517afe13864865268dfb45910e2e17a84be6cbfeb0"},
+ {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"},
+ {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"},
+ {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"},
+ {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"},
+ {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"},
+ {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"},
+ {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"},
+ {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"},
+ {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"},
+ {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"},
+ {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"},
+ {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"},
+ {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"},
+ {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"},
+ {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"},
+ {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"},
+ {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"},
+ {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"},
+ {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"},
+ {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"},
+ {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"},
+ {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"},
+ {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"},
+ {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"},
+ {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"},
+ {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"},
+ {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"},
+ {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"},
+ {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"},
+ {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"},
+ {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"},
+ {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"},
+ {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"},
+ {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"},
+ {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"},
+ {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"},
+ {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"},
+ {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"},
+ {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"},
+ {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"},
+ {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"},
+ {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"},
+ {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"},
+ {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"},
+ {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"},
+ {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"},
+ {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"},
+ {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"},
+ {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"},
+ {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"},
+ {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"},
+ {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"},
+ {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"},
+ {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"},
+ {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"},
+ {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"},
+ {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"},
+ {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"},
+ {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"},
+ {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"},
+ {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"},
+ {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"},
+ {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"},
+ {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"},
+ {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"},
+ {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"},
+ {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"},
+ {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"},
+ {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"},
+ {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"},
+ {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"},
+ {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"},
+ {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"},
+ {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"},
+ {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"},
+ {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"},
+ {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"},
+ {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"},
+ {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"},
+ {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"},
+ {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"},
+ {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"},
+ {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"},
+ {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"},
+ {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"},
+ {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"},
+ {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"},
+ {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"},
+ {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"},
+ {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"},
+ {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"},
+ {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"},
+ {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"},
]
[[package]]
@@ -1865,54 +1984,53 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "requests-file"
-version = "1.5.1"
+version = "2.0.0"
description = "File transport adapter for Requests"
optional = false
python-versions = "*"
files = [
- {file = "requests-file-1.5.1.tar.gz", hash = "sha256:07d74208d3389d01c38ab89ef403af0cfec63957d53a0081d8eca738d0247d8e"},
- {file = "requests_file-1.5.1-py2.py3-none-any.whl", hash = "sha256:dfe5dae75c12481f68ba353183c53a65e6044c923e64c24b2209f6c7570ca953"},
+ {file = "requests-file-2.0.0.tar.gz", hash = "sha256:20c5931629c558fda566cacc10cfe2cd502433e628f568c34c80d96a0cc95972"},
+ {file = "requests_file-2.0.0-py2.py3-none-any.whl", hash = "sha256:3e493d390adb44aa102ebea827a48717336d5268968c370eaf19abaf5cae13bf"},
]
[package.dependencies]
requests = ">=1.0.0"
-six = "*"
[[package]]
name = "ruff"
-version = "0.0.275"
-description = "An extremely fast Python linter, written in Rust."
+version = "0.3.2"
+description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.0.275-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:5e6554a072e7ce81eb6f0bec1cebd3dcb0e358652c0f4900d7d630d61691e914"},
- {file = "ruff-0.0.275-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:1cc599022fe5ffb143a965b8d659eb64161ab8ab4433d208777eab018a1aab67"},
- {file = "ruff-0.0.275-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5206fc1cd8c1c1deadd2e6360c0dbcd690f1c845da588ca9d32e4a764a402c60"},
- {file = "ruff-0.0.275-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c4e6468da26f77b90cae35319d310999f471a8c352998e9b39937a23750149e"},
- {file = "ruff-0.0.275-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0dbdea02942131dbc15dd45f431d152224f15e1dd1859fcd0c0487b658f60f1a"},
- {file = "ruff-0.0.275-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:22efd9f41af27ef8fb9779462c46c35c89134d33e326c889971e10b2eaf50c63"},
- {file = "ruff-0.0.275-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c09662112cfa22d7467a19252a546291fd0eae4f423e52b75a7a2000a1894db"},
- {file = "ruff-0.0.275-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80043726662144876a381efaab88841c88e8df8baa69559f96b22d4fa216bef1"},
- {file = "ruff-0.0.275-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5859ee543b01b7eb67835dfd505faa8bb7cc1550f0295c92c1401b45b42be399"},
- {file = "ruff-0.0.275-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c8ace4d40a57b5ea3c16555f25a6b16bc5d8b2779ae1912ce2633543d4e9b1da"},
- {file = "ruff-0.0.275-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8347fc16aa185aae275906c4ac5b770e00c896b6a0acd5ba521f158801911998"},
- {file = "ruff-0.0.275-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ec43658c64bfda44fd84bbea9da8c7a3b34f65448192d1c4dd63e9f4e7abfdd4"},
- {file = "ruff-0.0.275-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:508b13f7ca37274cceaba4fb3ea5da6ca192356323d92acf39462337c33ad14e"},
- {file = "ruff-0.0.275-py3-none-win32.whl", hash = "sha256:6afb1c4422f24f361e877937e2a44b3f8176774a476f5e33845ebfe887dd5ec2"},
- {file = "ruff-0.0.275-py3-none-win_amd64.whl", hash = "sha256:d9b264d78621bf7b698b6755d4913ab52c19bd28bee1a16001f954d64c1a1220"},
- {file = "ruff-0.0.275-py3-none-win_arm64.whl", hash = "sha256:a19ce3bea71023eee5f0f089dde4a4272d088d5ac0b675867e074983238ccc65"},
- {file = "ruff-0.0.275.tar.gz", hash = "sha256:a63a0b645da699ae5c758fce19188e901b3033ec54d862d93fcd042addf7f38d"},
+ {file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:77f2612752e25f730da7421ca5e3147b213dca4f9a0f7e0b534e9562c5441f01"},
+ {file = "ruff-0.3.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9966b964b2dd1107797be9ca7195002b874424d1d5472097701ae8f43eadef5d"},
+ {file = "ruff-0.3.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b83d17ff166aa0659d1e1deaf9f2f14cbe387293a906de09bc4860717eb2e2da"},
+ {file = "ruff-0.3.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb875c6cc87b3703aeda85f01c9aebdce3d217aeaca3c2e52e38077383f7268a"},
+ {file = "ruff-0.3.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be75e468a6a86426430373d81c041b7605137a28f7014a72d2fc749e47f572aa"},
+ {file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:967978ac2d4506255e2f52afe70dda023fc602b283e97685c8447d036863a302"},
+ {file = "ruff-0.3.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1231eacd4510f73222940727ac927bc5d07667a86b0cbe822024dd00343e77e9"},
+ {file = "ruff-0.3.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c6d613b19e9a8021be2ee1d0e27710208d1603b56f47203d0abbde906929a9b"},
+ {file = "ruff-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8439338a6303585d27b66b4626cbde89bb3e50fa3cae86ce52c1db7449330a7"},
+ {file = "ruff-0.3.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:de8b480d8379620cbb5ea466a9e53bb467d2fb07c7eca54a4aa8576483c35d36"},
+ {file = "ruff-0.3.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b74c3de9103bd35df2bb05d8b2899bf2dbe4efda6474ea9681280648ec4d237d"},
+ {file = "ruff-0.3.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f380be9fc15a99765c9cf316b40b9da1f6ad2ab9639e551703e581a5e6da6745"},
+ {file = "ruff-0.3.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0ac06a3759c3ab9ef86bbeca665d31ad3aa9a4b1c17684aadb7e61c10baa0df4"},
+ {file = "ruff-0.3.2-py3-none-win32.whl", hash = "sha256:9bd640a8f7dd07a0b6901fcebccedadeb1a705a50350fb86b4003b805c81385a"},
+ {file = "ruff-0.3.2-py3-none-win_amd64.whl", hash = "sha256:0c1bdd9920cab5707c26c8b3bf33a064a4ca7842d91a99ec0634fec68f9f4037"},
+ {file = "ruff-0.3.2-py3-none-win_arm64.whl", hash = "sha256:5f65103b1d76e0d600cabd577b04179ff592064eaa451a70a81085930e907d0b"},
+ {file = "ruff-0.3.2.tar.gz", hash = "sha256:fa78ec9418eb1ca3db392811df3376b46471ae93792a81af2d1cbb0e5dcb5142"},
]
[[package]]
name = "sentry-sdk"
-version = "1.26.0"
+version = "1.42.0"
description = "Python client for Sentry (https://sentry.io)"
optional = false
python-versions = "*"
files = [
- {file = "sentry-sdk-1.26.0.tar.gz", hash = "sha256:760e4fb6d01c994110507133e08ecd4bdf4d75ee4be77f296a3579796cf73134"},
- {file = "sentry_sdk-1.26.0-py2.py3-none-any.whl", hash = "sha256:0c9f858337ec3781cf4851972ef42bba8c9828aea116b0dbed8f38c5f9a1896c"},
+ {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"},
+ {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"},
]
[package.dependencies]
@@ -1922,10 +2040,12 @@ urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""}
[package.extras]
aiohttp = ["aiohttp (>=3.5)"]
arq = ["arq (>=0.23)"]
+asyncpg = ["asyncpg (>=0.23)"]
beam = ["apache-beam (>=2.12)"]
bottle = ["bottle (>=0.12.13)"]
celery = ["celery (>=3)"]
chalice = ["chalice (>=1.16.0)"]
+clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
django = ["django (>=1.8)"]
falcon = ["falcon (>=1.4)"]
fastapi = ["fastapi (>=0.79.0)"]
@@ -1934,7 +2054,9 @@ grpcio = ["grpcio (>=1.21.1)"]
httpx = ["httpx (>=0.16.0)"]
huey = ["huey (>=2)"]
loguru = ["loguru (>=0.5)"]
+openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
+opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
pure-eval = ["asttokens", "executing", "pure-eval"]
pymongo = ["pymongo (>=3.1)"]
pyspark = ["pyspark (>=2.4.4)"]
@@ -1948,19 +2070,19 @@ tornado = ["tornado (>=5)"]
[[package]]
name = "setuptools"
-version = "68.0.0"
+version = "69.0.3"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"},
- {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"},
+ {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"},
+ {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"},
]
[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-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "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-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "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"]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "sgmllib3k"
@@ -2007,13 +2129,13 @@ files = [
[[package]]
name = "soupsieve"
-version = "2.4.1"
+version = "2.5"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"},
- {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"},
+ {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
+ {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
]
[[package]]
@@ -2029,30 +2151,30 @@ files = [
[[package]]
name = "taskipy"
-version = "1.11.0"
+version = "1.12.2"
description = "tasks runner for python projects"
optional = false
python-versions = ">=3.6,<4.0"
files = [
- {file = "taskipy-1.11.0-py3-none-any.whl", hash = "sha256:4e40cd41747a54bc8a9b3c21057c25cac645309c2d8ac897bdc1e7235e9c900e"},
- {file = "taskipy-1.11.0.tar.gz", hash = "sha256:521e8b3b65dc1ff9bb036cae989dbe5aec1626a61cf4744e5c0d0d2450c7fcb4"},
+ {file = "taskipy-1.12.2-py3-none-any.whl", hash = "sha256:ffdbb0bb0db54c0ec5c424610a3a087eea22706d4d1f6e3e8b4f12ebba05f98f"},
+ {file = "taskipy-1.12.2.tar.gz", hash = "sha256:eadfdc20d6bb94d8018eda32f1dbf584cf4aa6cffb71ba5cc2de20d344f8c4fb"},
]
[package.dependencies]
colorama = ">=0.4.4,<0.5.0"
-mslex = {version = ">=0.3.0,<0.4.0", markers = "sys_platform == \"win32\""}
+mslex = {version = ">=1.1.0,<2.0.0", markers = "sys_platform == \"win32\""}
psutil = ">=5.7.2,<6.0.0"
tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version >= \"3.7\" and python_version < \"4.0\""}
[[package]]
name = "tldextract"
-version = "3.4.4"
+version = "5.1.1"
description = "Accurately separates a URL's subdomain, domain, and public suffix, using the Public Suffix List (PSL). By default, this includes the public ICANN TLDs and their exceptions. You can optionally support the Public Suffix List's private domains as well."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "tldextract-3.4.4-py3-none-any.whl", hash = "sha256:581e7dbefc90e7bb857bb6f768d25c811a3c5f0892ed56a9a2999ddb7b1b70c2"},
- {file = "tldextract-3.4.4.tar.gz", hash = "sha256:5fe3210c577463545191d45ad522d3d5e78d55218ce97215e82004dcae1e1234"},
+ {file = "tldextract-5.1.1-py3-none-any.whl", hash = "sha256:b9c4510a8766d377033b6bace7e9f1f17a891383ced3c5d50c150f181e9e1cc2"},
+ {file = "tldextract-5.1.1.tar.gz", hash = "sha256:9b6dbf803cb5636397f0203d48541c0da8ba53babaf0e8a6feda2d88746813d4"},
]
[package.dependencies]
@@ -2061,6 +2183,9 @@ idna = "*"
requests = ">=2.1.0"
requests-file = ">=1.4"
+[package.extras]
+testing = ["black", "mypy", "pytest", "pytest-gitignore", "pytest-mock", "responses", "ruff", "tox", "types-filelock", "types-requests"]
+
[[package]]
name = "tomli"
version = "2.0.1"
@@ -2073,145 +2198,172 @@ files = [
]
[[package]]
+name = "types-python-dateutil"
+version = "2.8.19.20240106"
+description = "Typing stubs for python-dateutil"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "types-python-dateutil-2.8.19.20240106.tar.gz", hash = "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f"},
+ {file = "types_python_dateutil-2.8.19.20240106-py3-none-any.whl", hash = "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2"},
+]
+
+[[package]]
name = "typing-extensions"
-version = "4.6.3"
-description = "Backported and Experimental Type Hints for Python 3.7+"
+version = "4.9.0"
+description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"},
- {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"},
+ {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
+ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
]
[[package]]
name = "urllib3"
-version = "2.0.3"
+version = "2.2.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"},
- {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"},
+ {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"},
+ {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
-secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
+h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "virtualenv"
-version = "20.23.1"
+version = "20.25.0"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.7"
files = [
- {file = "virtualenv-20.23.1-py3-none-any.whl", hash = "sha256:34da10f14fea9be20e0fd7f04aba9732f84e593dac291b757ce42e3368a39419"},
- {file = "virtualenv-20.23.1.tar.gz", hash = "sha256:8ff19a38c1021c742148edc4f81cb43d7f8c6816d2ede2ab72af5b84c749ade1"},
+ {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"},
+ {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"},
]
[package.dependencies]
-distlib = ">=0.3.6,<1"
-filelock = ">=3.12,<4"
-platformdirs = ">=3.5.1,<4"
+distlib = ">=0.3.7,<1"
+filelock = ">=3.12.2,<4"
+platformdirs = ">=3.9.1,<5"
[package.extras]
-docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
+test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
[[package]]
name = "wcwidth"
-version = "0.2.6"
+version = "0.2.13"
description = "Measures the displayed width of unicode strings in a terminal"
optional = false
python-versions = "*"
files = [
- {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
- {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
+ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
+ {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
]
[[package]]
name = "yarl"
-version = "1.9.2"
+version = "1.9.4"
description = "Yet another URL library"
optional = false
python-versions = ">=3.7"
files = [
- {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"},
- {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"},
- {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"},
- {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"},
- {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"},
- {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"},
- {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"},
- {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"},
- {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"},
- {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"},
- {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"},
- {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"},
- {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"},
- {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"},
- {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"},
- {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"},
- {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"},
- {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"},
- {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"},
- {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"},
- {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"},
- {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"},
- {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"},
- {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"},
- {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"},
- {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"},
- {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"},
- {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"},
- {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"},
- {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"},
- {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"},
- {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"},
- {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"},
- {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"},
- {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"},
- {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"},
- {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"},
- {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"},
- {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"},
- {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"},
- {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"},
- {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"},
- {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"},
- {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"},
- {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"},
- {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"},
- {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"},
- {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"},
- {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"},
- {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"},
- {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"},
- {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"},
- {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"},
- {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"},
- {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"},
- {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"},
- {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"},
- {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"},
- {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"},
- {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"},
- {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"},
- {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"},
- {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"},
- {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"},
- {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"},
- {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"},
- {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"},
- {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"},
- {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"},
- {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"},
- {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"},
- {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"},
- {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"},
- {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"},
+ {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"},
+ {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"},
+ {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"},
+ {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"},
+ {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"},
+ {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"},
+ {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"},
+ {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"},
+ {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"},
+ {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"},
+ {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"},
+ {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"},
+ {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"},
+ {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"},
+ {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"},
+ {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"},
+ {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"},
+ {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"},
+ {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"},
+ {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"},
+ {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"},
+ {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"},
+ {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"},
+ {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"},
+ {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"},
+ {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"},
+ {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"},
+ {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"},
+ {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"},
+ {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"},
+ {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"},
+ {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"},
+ {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"},
+ {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"},
+ {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"},
+ {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"},
+ {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"},
+ {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"},
+ {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"},
+ {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"},
+ {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"},
+ {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"},
+ {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"},
+ {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"},
+ {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"},
+ {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"},
+ {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"},
+ {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"},
+ {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"},
+ {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"},
+ {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"},
+ {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"},
+ {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"},
+ {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"},
+ {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"},
+ {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"},
+ {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"},
+ {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"},
]
[package.dependencies]
@@ -2221,4 +2373,4 @@ multidict = ">=4.0"
[metadata]
lock-version = "2.0"
python-versions = "3.11.*"
-content-hash = "7e56572b0f4642adcefaf6c0a3d75afbbda1a0a35d773e945c849fad6e08908a"
+content-hash = "45cf21ebda878de3b12821aab29e1bcfed6dfae606b6535d3ee6d15abfea9c80"
diff --git a/pyproject.toml b/pyproject.toml
index 9fce23896..6bc742246 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,40 +9,39 @@ license = "MIT"
python = "3.11.*"
# See https://bot-core.pythondiscord.com/ for docs.
-pydis_core = { version = "9.9.1", extras = ["async-rediscache"] }
+pydis_core = { version = "10.7.0", extras = ["async-rediscache"] }
-aiohttp = "3.8.4"
-arrow = "1.2.3"
-beautifulsoup4 = "4.12.2"
+aiohttp = "3.9.3"
+arrow = "1.3.0"
+beautifulsoup4 = "4.12.3"
colorama = { version = "0.4.6", markers = "sys_platform == 'win32'" }
coloredlogs = "15.0.1"
-deepdiff = "6.3.0"
-emoji = "2.6.0"
-feedparser = "6.0.10"
-lxml = "4.9.2"
+deepdiff = "6.7.1"
+emoji = "2.10.1"
+feedparser = "6.0.11"
+lxml = "5.1.0"
markdownify = "0.11.6"
-more-itertools = "9.1.0"
-python-dateutil = "2.8.2"
-python-frontmatter = "1.0.0"
-pyyaml = "6.0"
-rapidfuzz = "3.1.1"
-regex = "2023.6.3"
-sentry-sdk = "1.26.0"
-tldextract = "3.4.4"
-pydantic = { version = "1.10.9", extras = ["dotenv"]}
+more-itertools = "10.2.0"
+python-dateutil = "2.9.0.post0"
+python-frontmatter = "1.1.0"
+rapidfuzz = "3.6.1"
+regex = "2023.12.25"
+sentry-sdk = "1.42.0"
+tldextract = "5.1.1"
+pydantic = "2.5.2"
+pydantic-settings = "2.2.1"
[tool.poetry.dev-dependencies]
-coverage = "7.2.7"
-httpx = "0.24.1"
-isort = "5.12.0"
-pre-commit = "3.3.3"
-pip-licenses = "4.3.2"
-pytest = "7.4.0"
+coverage = "7.4.3"
+httpx = "0.27.0"
+pre-commit = "3.6.2"
+pip-licenses = "4.3.4"
+pytest = "8.1.1"
pytest-cov = "4.1.0"
-pytest-subtests = "0.11.0"
-pytest-xdist = "3.3.1"
-ruff = "0.0.275"
-taskipy = "1.11.0"
+pytest-subtests = "0.12.1"
+pytest-xdist = "3.5.0"
+ruff = "0.3.2"
+taskipy = "1.12.2"
[build-system]
@@ -61,24 +60,19 @@ retest = "pytest -n auto --lf"
test-cov = "pytest -n auto --cov-report= --cov"
html = "coverage html"
report = "coverage report"
-isort = "isort ."
[tool.coverage.run]
branch = true
source_pkgs = ["bot"]
source = ["tests"]
-[tool.isort]
-multi_line_output = 6
-order_by_type = false
-case_sensitive = true
-combine_as_imports = true
-line_length = 120
-atomic = true
-
[tool.ruff]
target-version = "py311"
extend-exclude = [".cache"]
+line-length = 120
+
+[tool.ruff.lint]
+select = ["ANN", "B", "C4", "D", "DTZ", "E", "F", "I", "ISC", "INT", "N", "PGH", "PIE", "Q", "RET", "RSE", "RUF", "S", "SIM", "T20", "TID", "UP", "W"]
ignore = [
"ANN002", "ANN003", "ANN101", "ANN102", "ANN204", "ANN206", "ANN401",
"B904",
@@ -87,14 +81,17 @@ ignore = [
"D400", "D401", "D402", "D404", "D405", "D406", "D407", "D408", "D409", "D410", "D411", "D412", "D413", "D414", "D416", "D417",
"E731",
"RET504",
- "RUF005", "RUF012",
+ "RUF005", "RUF012", "RUF015",
"S311",
"SIM102", "SIM108",
]
-line-length = 120
-select = ["ANN", "B", "C4", "D", "DTZ", "E", "F", "ISC", "INT", "N", "PGH", "PIE", "Q", "RET", "RSE", "RUF", "S", "SIM", "T20", "TID", "UP", "W"]
-[tool.ruff.per-file-ignores]
+[tool.ruff.lint.isort]
+order-by-type = false
+case-sensitive = true
+combine-as-imports = true
+
+[tool.ruff.lint.per-file-ignores]
"tests/*" = ["ANN", "D"]
[tool.pytest.ini_options]
diff --git a/tests/_autospec.py b/tests/_autospec.py
index 6f990a580..a9cd59dc0 100644
--- a/tests/_autospec.py
+++ b/tests/_autospec.py
@@ -51,7 +51,7 @@ def autospec(target, *attributes: str, pass_mocks: bool = True, **patch_kwargs)
# Import the target if it's a string.
# This is to support both object and string targets like patch.multiple.
- if type(target) is str:
+ if isinstance(target, str):
target = pkgutil.resolve_name(target)
def decorator(func):
diff --git a/tests/bot/exts/backend/test_error_handler.py b/tests/bot/exts/backend/test_error_handler.py
index 0ba2fcf11..9670d42a0 100644
--- a/tests/bot/exts/backend/test_error_handler.py
+++ b/tests/bot/exts/backend/test_error_handler.py
@@ -1,5 +1,5 @@
import unittest
-from unittest.mock import AsyncMock, MagicMock, call, patch
+from unittest.mock import AsyncMock, MagicMock, Mock, call, patch
from discord.ext.commands import errors
from pydis_core.site_api import ResponseCodeError
@@ -54,12 +54,15 @@ class ErrorHandlerTests(unittest.IsolatedAsyncioTestCase):
self.ctx.reset_mock()
self.cog.try_silence.reset_mock(return_value=True)
self.cog.try_get_tag.reset_mock()
+ self.ctx.invoked_from_error_handler = False
self.cog.try_silence.return_value = case["try_silence_return"]
self.ctx.channel.id = 1234
self.assertIsNone(await self.cog.on_command_error(self.ctx, error))
+ self.assertTrue(self.ctx.invoked_from_error_handler)
+
if case["try_silence_return"]:
self.cog.try_get_tag.assert_not_awaited()
self.cog.try_silence.assert_awaited_once()
@@ -203,13 +206,6 @@ class TrySilenceTests(unittest.IsolatedAsyncioTestCase):
self.ctx = MockContext(bot=self.bot, guild=self.guild)
self.cog = error_handler.ErrorHandler(self.bot)
- async def test_try_silence_context_invoked_from_error_handler(self):
- """Should set `Context.invoked_from_error_handler` to `True`."""
- self.ctx.invoked_with = "foo"
- await self.cog.try_silence(self.ctx)
- self.assertTrue(hasattr(self.ctx, "invoked_from_error_handler"))
- self.assertTrue(self.ctx.invoked_from_error_handler)
-
async def test_try_silence_get_command(self):
"""Should call `get_command` with `silence`."""
self.ctx.invoked_with = "foo"
@@ -342,26 +338,12 @@ class TryGetTagTests(unittest.IsolatedAsyncioTestCase):
await self.cog.try_get_tag(self.ctx)
self.bot.get_cog.assert_called_once_with("Tags")
- async def test_try_get_tag_invoked_from_error_handler(self):
- """`self.ctx` should have `invoked_from_error_handler` `True`."""
- self.ctx.invoked_from_error_handler = False
- await self.cog.try_get_tag(self.ctx)
- self.assertTrue(self.ctx.invoked_from_error_handler)
-
async def test_try_get_tag_no_permissions(self):
"""Test how to handle checks failing."""
self.bot.can_run = AsyncMock(return_value=False)
self.ctx.invoked_with = "foo"
self.assertIsNone(await self.cog.try_get_tag(self.ctx))
- async def test_try_get_tag_command_error(self):
- """Should call `on_command_error` when `CommandError` raised."""
- err = errors.CommandError()
- self.bot.can_run = AsyncMock(side_effect=err)
- self.cog.on_command_error = AsyncMock()
- self.assertIsNone(await self.cog.try_get_tag(self.ctx))
- self.cog.on_command_error.assert_awaited_once_with(self.ctx, err)
-
async def test_dont_call_suggestion_tag_sent(self):
"""Should never call command suggestion if tag is already sent."""
self.ctx.message = MagicMock(content="foo")
@@ -375,7 +357,7 @@ class TryGetTagTests(unittest.IsolatedAsyncioTestCase):
async def test_dont_call_suggestion_if_user_mod(self):
"""Should not call command suggestion if user is a mod."""
self.ctx.invoked_with = "foo"
- self.ctx.invoke = AsyncMock(return_value=False)
+ self.tag.get_command_ctx = AsyncMock(return_value=False)
self.ctx.author.roles = [MockRole(id=1234)]
self.cog.send_command_suggestion = AsyncMock()
@@ -521,6 +503,10 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase):
self.ctx.reset_mock()
log_mock.reset_mock()
push_scope_mock.reset_mock()
+ scope_mock = Mock()
+
+ # Mock `with push_scope_mock() as scope:`
+ push_scope_mock.return_value.__enter__.return_value = scope_mock
self.ctx.guild = case
await self.cog.handle_unexpected_error(self.ctx, errors.CommandError())
@@ -544,8 +530,8 @@ class IndividualErrorHandlerTests(unittest.IsolatedAsyncioTestCase):
)
set_extra_calls.append(call("jump_to", url))
- push_scope_mock.set_tag.has_calls(set_tag_calls)
- push_scope_mock.set_extra.has_calls(set_extra_calls)
+ scope_mock.set_tag.assert_has_calls(set_tag_calls)
+ scope_mock.set_extra.assert_has_calls(set_extra_calls)
class ErrorHandlerSetupTests(unittest.IsolatedAsyncioTestCase):
diff --git a/tests/bot/exts/backend/test_logging.py b/tests/bot/exts/backend/test_logging.py
index 466f207d9..3a41220ad 100644
--- a/tests/bot/exts/backend/test_logging.py
+++ b/tests/bot/exts/backend/test_logging.py
@@ -3,7 +3,7 @@ from unittest.mock import patch
from bot import constants
from bot.exts.backend.logging import Logging
-from tests.helpers import MockBot, MockTextChannel
+from tests.helpers import MockBot, MockTextChannel, no_create_task
class LoggingTests(unittest.IsolatedAsyncioTestCase):
@@ -11,7 +11,8 @@ class LoggingTests(unittest.IsolatedAsyncioTestCase):
def setUp(self):
self.bot = MockBot()
- self.cog = Logging(self.bot)
+ with no_create_task():
+ self.cog = Logging(self.bot)
self.dev_log = MockTextChannel(id=1234, name="dev-log")
@patch("bot.exts.backend.logging.DEBUG_MODE", False)
diff --git a/tests/bot/exts/filtering/test_settings_entries.py b/tests/bot/exts/filtering/test_settings_entries.py
index 3ae0b5ab5..f12b2caa5 100644
--- a/tests/bot/exts/filtering/test_settings_entries.py
+++ b/tests/bot/exts/filtering/test_settings_entries.py
@@ -2,7 +2,9 @@ import unittest
from bot.exts.filtering._filter_context import Event, FilterContext
from bot.exts.filtering._settings_types.actions.infraction_and_notification import (
- Infraction, InfractionAndNotification, InfractionDuration
+ Infraction,
+ InfractionAndNotification,
+ InfractionDuration,
)
from bot.exts.filtering._settings_types.validations.bypass_roles import RoleBypass
from bot.exts.filtering._settings_types.validations.channel_scope import ChannelScope
@@ -173,7 +175,7 @@ class FilterTests(unittest.TestCase):
result = infraction1.union(infraction2)
self.assertDictEqual(
- result.dict(),
+ result.model_dump(),
{
"infraction_type": Infraction.TIMEOUT,
"infraction_reason": "there",
@@ -206,7 +208,7 @@ class FilterTests(unittest.TestCase):
result = infraction1.union(infraction2)
self.assertDictEqual(
- result.dict(),
+ result.model_dump(),
{
"infraction_type": Infraction.BAN,
"infraction_reason": "",
diff --git a/tests/bot/exts/moderation/infraction/test_utils.py b/tests/bot/exts/moderation/infraction/test_utils.py
index 25337673e..e2a7bad9f 100644
--- a/tests/bot/exts/moderation/infraction/test_utils.py
+++ b/tests/bot/exts/moderation/infraction/test_utils.py
@@ -142,7 +142,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase):
),
"expected_output": Embed(
title=utils.INFRACTION_TITLE,
- description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format(
+ description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format(
type="Ban",
expires="2020-02-26 09:20 (23 hours and 59 minutes)",
reason="No reason provided."
@@ -160,7 +160,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase):
"args": (dict(id=0, type="warning", reason="Test reason.", expires_at=None), self.user),
"expected_output": Embed(
title=utils.INFRACTION_TITLE,
- description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format(
+ description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format(
type="Warning",
expires="N/A",
reason="Test reason."
@@ -180,7 +180,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase):
"args": (dict(id=0, type="note", reason=None, expires_at=None), self.user),
"expected_output": Embed(
title=utils.INFRACTION_TITLE,
- description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format(
+ description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format(
type="Note",
expires="N/A",
reason="No reason provided."
@@ -201,7 +201,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase):
),
"expected_output": Embed(
title=utils.INFRACTION_TITLE,
- description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format(
+ description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format(
type="Mute",
expires="2020-02-26 09:20 (23 hours and 59 minutes)",
reason="Test"
@@ -219,7 +219,7 @@ class ModerationUtilsTests(unittest.IsolatedAsyncioTestCase):
"args": (dict(id=0, type="mute", reason="foo bar" * 4000, expires_at=None), self.user),
"expected_output": Embed(
title=utils.INFRACTION_TITLE,
- description=utils.INFRACTION_DESCRIPTION_TEMPLATE.format(
+ description=utils.INFRACTION_DESCRIPTION_NOT_WARNING_TEMPLATE.format(
type="Mute",
expires="N/A",
reason="foo bar" * 4000
diff --git a/tests/bot/exts/moderation/test_incidents.py b/tests/bot/exts/moderation/test_incidents.py
index bb337aeba..444bb1142 100644
--- a/tests/bot/exts/moderation/test_incidents.py
+++ b/tests/bot/exts/moderation/test_incidents.py
@@ -16,8 +16,16 @@ from bot.utils.messages import format_user
from bot.utils.time import TimestampFormats, discord_timestamp
from tests.base import RedisTestCase
from tests.helpers import (
- MockAsyncWebhook, MockAttachment, MockBot, MockMember, MockMessage, MockReaction, MockRole, MockTextChannel,
- MockUser
+ MockAsyncWebhook,
+ MockAttachment,
+ MockBot,
+ MockMember,
+ MockMessage,
+ MockReaction,
+ MockRole,
+ MockTextChannel,
+ MockUser,
+ no_create_task,
)
CURRENT_TIME = datetime.datetime(2022, 1, 1, tzinfo=datetime.UTC)
@@ -306,7 +314,8 @@ class TestIncidents(RedisTestCase):
Note that this will not schedule `crawl_incidents` in the background, as everything
is being mocked. The `crawl_task` attribute will end up being None.
"""
- self.cog_instance = incidents.Incidents(MockBot())
+ with no_create_task():
+ self.cog_instance = incidents.Incidents(MockBot())
@patch("asyncio.sleep", AsyncMock()) # Prevent the coro from sleeping to speed up the test
@@ -458,7 +467,8 @@ class TestMakeConfirmationTask(TestIncidents):
If this function begins to fail, first check that `created_check` is being retrieved
correctly. It should be the function that is built locally in the tested method.
"""
- self.cog_instance.make_confirmation_task(MockMessage(id=123))
+ with no_create_task():
+ self.cog_instance.make_confirmation_task(MockMessage(id=123))
self.cog_instance.bot.wait_for.assert_called_once()
created_check = self.cog_instance.bot.wait_for.call_args.kwargs["check"]
@@ -547,7 +557,7 @@ class TestProcessEvent(TestIncidents):
exception should it propagate out of `process_event`. This is so that we can then manually
fail the test with a more informative message than just the plain traceback.
"""
- mock_task = AsyncMock(side_effect=asyncio.TimeoutError())
+ mock_task = AsyncMock(side_effect=TimeoutError())
try:
with patch("bot.exts.moderation.incidents.Incidents.make_confirmation_task", mock_task):
@@ -556,7 +566,7 @@ class TestProcessEvent(TestIncidents):
incident=MockMessage(id=123, created_at=CURRENT_TIME),
member=MockMember(roles=[MockRole(id=1)])
)
- except asyncio.TimeoutError:
+ except TimeoutError:
self.fail("TimeoutError was not handled gracefully, and propagated out of `process_event`!")
diff --git a/tests/bot/exts/moderation/test_modlog.py b/tests/bot/exts/moderation/test_modlog.py
index 79e04837d..f2b02bd1b 100644
--- a/tests/bot/exts/moderation/test_modlog.py
+++ b/tests/bot/exts/moderation/test_modlog.py
@@ -3,6 +3,7 @@ import unittest
import discord
from bot.exts.moderation.modlog import ModLog
+from bot.utils.modlog import send_log_message
from tests.helpers import MockBot, MockTextChannel
@@ -17,7 +18,8 @@ class ModLogTests(unittest.IsolatedAsyncioTestCase):
async def test_log_entry_description_truncation(self):
"""Test that embed description for ModLog entry is truncated."""
self.bot.get_channel.return_value = self.channel
- await self.cog.send_log_message(
+ await send_log_message(
+ self.bot,
icon_url="foo",
colour=discord.Colour.blue(),
title="bar",
diff --git a/tests/bot/exts/moderation/test_silence.py b/tests/bot/exts/moderation/test_silence.py
index ec0b3bf43..a7f239d7f 100644
--- a/tests/bot/exts/moderation/test_silence.py
+++ b/tests/bot/exts/moderation/test_silence.py
@@ -10,7 +10,14 @@ from bot.constants import Channels, Guild, MODERATION_ROLES, Roles
from bot.exts.moderation import silence
from tests.base import RedisTestCase
from tests.helpers import (
- MockBot, MockContext, MockGuild, MockMember, MockRole, MockTextChannel, MockVoiceChannel, autospec
+ MockBot,
+ MockContext,
+ MockGuild,
+ MockMember,
+ MockRole,
+ MockTextChannel,
+ MockVoiceChannel,
+ autospec,
)
diff --git a/tests/bot/exts/test_cogs.py b/tests/bot/exts/test_cogs.py
index 99bc87120..0c117b3e6 100644
--- a/tests/bot/exts/test_cogs.py
+++ b/tests/bot/exts/test_cogs.py
@@ -62,8 +62,7 @@ class CommandNameTests(unittest.TestCase):
"""Yield all commands for all cogs in all extensions."""
for module in self.walk_modules():
for cog in self.walk_cogs(module):
- for cmd in self.walk_commands(cog):
- yield cmd
+ yield from self.walk_commands(cog)
def test_names_dont_shadow(self):
"""Names and aliases of commands should be unique."""
diff --git a/tests/bot/exts/utils/snekbox/test_snekbox.py b/tests/bot/exts/utils/snekbox/test_snekbox.py
index fa28aade8..8ee0f46ff 100644
--- a/tests/bot/exts/utils/snekbox/test_snekbox.py
+++ b/tests/bot/exts/utils/snekbox/test_snekbox.py
@@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, Mock, call, create_autospec, pat
from discord import AllowedMentions
from discord.ext import commands
+from pydis_core.utils.paste_service import MAX_PASTE_SIZE
from bot import constants
from bot.errors import LockedResourceError
@@ -54,21 +55,15 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
)
resp.json.assert_awaited_once()
+ @patch(
+ "bot.exts.utils.snekbox._cog.paste_service._lexers_supported_by_pastebin",
+ {"https://paste.pythondiscord.com": ["text"]},
+ )
async def test_upload_output_reject_too_long(self):
"""Reject output longer than MAX_PASTE_LENGTH."""
- result = await self.cog.upload_output("-" * (snekbox._cog.MAX_PASTE_LENGTH + 1))
+ result = await self.cog.upload_output("-" * (MAX_PASTE_SIZE + 1))
self.assertEqual(result, "too long to upload")
- @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._cog.MAX_PASTE_LENGTH
- )
-
async def test_codeblock_converter(self):
ctx = MockContext()
cases = (
@@ -108,9 +103,9 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
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._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", ""))
+ ("ERROR", None, ("Your 3.12 eval job has failed", "ERROR", "")),
+ ("", 128 + snekbox._eval.SIGKILL, ("Your 3.12 eval job timed out or ran out of memory", "", "")),
+ ("", 255, ("Your 3.12 eval job has failed", "A fatal NsJail error occurred", ""))
)
for stdout, returncode, expected in cases:
exp_msg, exp_err, exp_files_err = expected
@@ -182,8 +177,8 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
mock_signals.return_value.name = "SIGTEST"
result = EvalResult(stdout="", returncode=127)
self.assertEqual(
- result.get_message(EvalJob([], version="3.11")),
- "Your 3.11 eval job has completed with return code 127 (SIGTEST)"
+ result.get_message(EvalJob([], version="3.12")),
+ "Your 3.12 eval job has completed with return code 127 (SIGTEST)"
)
def test_eval_result_status_emoji(self):
@@ -257,7 +252,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
self.cog.send_job = AsyncMock(return_value=response)
self.cog.continue_job = AsyncMock(return_value=None)
- await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.11", code=["MyAwesomeCode"])
+ await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.12", code=["MyAwesomeCode"])
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")
@@ -271,7 +266,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
self.cog.continue_job = AsyncMock()
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"])
+ await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.12", code=["MyAwesomeCode"])
expected_job = EvalJob.from_code("MyAwesomeFormattedCode")
self.cog.send_job.assert_called_with(ctx, expected_job)
@@ -316,7 +311,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
ctx.send.assert_called_once()
self.assertEqual(
ctx.send.call_args.args[0],
- "@LemonLemonishBeard#0042 :warning: Your 3.11 eval job has completed "
+ "@LemonLemonishBeard#0042 :warning: Your 3.12 eval job has completed "
"with return code 0.\n\n```\n[No output]\n```"
)
allowed_mentions = ctx.send.call_args.kwargs["allowed_mentions"]
@@ -342,13 +337,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
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")
+ job = EvalJob.from_code("MyAwesomeCode").as_version("3.12")
await self.cog.send_job(ctx, job),
ctx.send.assert_called_once()
self.assertEqual(
ctx.send.call_args.args[0],
- "@LemonLemonishBeard#0042 :white_check_mark: Your 3.11 eval job "
+ "@LemonLemonishBeard#0042 :white_check_mark: Your 3.12 eval job "
"has completed with return code 0."
"\n\n```\nWay too long beard\n```\nFull output: lookatmybeard.com"
)
@@ -371,13 +366,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
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")
+ job = EvalJob.from_code("MyAwesomeCode").as_version("3.12")
await self.cog.send_job(ctx, job),
ctx.send.assert_called_once()
self.assertEqual(
ctx.send.call_args.args[0],
- "@LemonLemonishBeard#0042 :x: Your 3.11 eval job has completed with return code 127."
+ "@LemonLemonishBeard#0042 :x: Your 3.12 eval job has completed with return code 127."
"\n\n```\nERROR\n```"
)
@@ -399,13 +394,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):
mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=(False, [".disallowed"]))
self.bot.get_cog.return_value = mocked_filter_cog
- job = EvalJob.from_code("MyAwesomeCode").as_version("3.11")
+ job = EvalJob.from_code("MyAwesomeCode").as_version("3.12")
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.")
+ res.startswith("@user#7700 :white_check_mark: Your 3.12 eval job has completed with return code 0.")
)
self.assertIn("Files with disallowed extensions can't be uploaded: **.disallowed**", res)
diff --git a/tests/bot/test_pagination.py b/tests/bot/test_pagination.py
deleted file mode 100644
index cf23f1948..000000000
--- a/tests/bot/test_pagination.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from unittest import TestCase
-
-from bot import pagination
-
-
-class LinePaginatorTests(TestCase):
- """Tests functionality of the `LinePaginator`."""
-
- def setUp(self):
- """Create a paginator for the test method."""
- self.paginator = pagination.LinePaginator(prefix="", suffix="", max_size=30,
- scale_to_size=50)
-
- def test_add_line_works_on_small_lines(self):
- """`add_line` should allow small lines to be added."""
- self.paginator.add_line("x" * (self.paginator.max_size - 3))
- # Note that the page isn't added to _pages until it's full.
- self.assertEqual(len(self.paginator._pages), 0)
-
- def test_add_line_works_on_long_lines(self):
- """After additional lines after `max_size` is exceeded should go on the next page."""
- self.paginator.add_line("x" * self.paginator.max_size)
- self.assertEqual(len(self.paginator._pages), 0)
-
- # Any additional lines should start a new page after `max_size` is exceeded.
- self.paginator.add_line("x")
- self.assertEqual(len(self.paginator._pages), 1)
-
- def test_add_line_continuation(self):
- """When `scale_to_size` is exceeded, remaining words should be split onto the next page."""
- self.paginator.add_line("zyz " * (self.paginator.scale_to_size//4 + 1))
- self.assertEqual(len(self.paginator._pages), 1)
-
- def test_add_line_no_continuation(self):
- """If adding a new line to an existing page would exceed `max_size`, it should start a new
- page rather than using continuation.
- """
- self.paginator.add_line("z" * (self.paginator.max_size - 3))
- self.paginator.add_line("z")
- self.assertEqual(len(self.paginator._pages), 1)
-
- def test_add_line_truncates_very_long_words(self):
- """`add_line` should truncate if a single long word exceeds `scale_to_size`."""
- self.paginator.add_line("x" * (self.paginator.scale_to_size + 1))
- # Note: item at index 1 is the truncated line, index 0 is prefix
- self.assertEqual(self.paginator._current_page[1], "x" * self.paginator.scale_to_size)
diff --git a/tests/bot/utils/test_services.py b/tests/bot/utils/test_services.py
deleted file mode 100644
index 3c9e037ce..000000000
--- a/tests/bot/utils/test_services.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import logging
-import unittest
-from unittest.mock import AsyncMock, MagicMock, Mock, patch
-
-from aiohttp import ClientConnectorError
-
-from bot.utils.services import (
- FAILED_REQUEST_ATTEMPTS, MAX_PASTE_LENGTH, PasteTooLongError, PasteUploadError, send_to_paste_service
-)
-from tests.helpers import MockBot
-
-
-class PasteTests(unittest.IsolatedAsyncioTestCase):
- def setUp(self) -> None:
- patcher = patch("bot.instance", new=MockBot())
- self.bot = patcher.start()
- self.addCleanup(patcher.stop)
-
- @patch("bot.utils.services.URLs.paste_service", "https://paste_service.com/{key}")
- async def test_url_and_sent_contents(self):
- """Correct url was used and post was called with expected data."""
- response = MagicMock(
- json=AsyncMock(return_value={"key": ""})
- )
- self.bot.http_session.post.return_value.__aenter__.return_value = response
- self.bot.http_session.post.reset_mock()
- await send_to_paste_service("Content")
- self.bot.http_session.post.assert_called_once_with("https://paste_service.com/documents", data="Content")
-
- @patch("bot.utils.services.URLs.paste_service", "https://paste_service.com/{key}")
- async def test_paste_returns_correct_url_on_success(self):
- """Url with specified extension is returned on successful requests."""
- key = "paste_key"
- test_cases = (
- (f"https://paste_service.com/{key}.txt?noredirect", "txt"),
- (f"https://paste_service.com/{key}.py", "py"),
- (f"https://paste_service.com/{key}?noredirect", ""),
- )
- response = MagicMock(
- json=AsyncMock(return_value={"key": key})
- )
- self.bot.http_session.post.return_value.__aenter__.return_value = response
-
- for expected_output, extension in test_cases:
- with self.subTest(msg=f"Send contents with extension {extension!r}"):
- self.assertEqual(
- await send_to_paste_service("", extension=extension),
- expected_output
- )
-
- async def test_request_repeated_on_json_errors(self):
- """Json with error message and invalid json are handled as errors and requests repeated."""
- test_cases = ({"message": "error"}, {"unexpected_key": None}, {})
- self.bot.http_session.post.return_value.__aenter__.return_value = response = MagicMock()
- self.bot.http_session.post.reset_mock()
-
- for error_json in test_cases:
- with self.subTest(error_json=error_json):
- response.json = AsyncMock(return_value=error_json)
- with self.assertRaises(PasteUploadError):
- await send_to_paste_service("")
- self.assertEqual(self.bot.http_session.post.call_count, FAILED_REQUEST_ATTEMPTS)
-
- self.bot.http_session.post.reset_mock()
-
- async def test_request_repeated_on_connection_errors(self):
- """Requests are repeated in the case of connection errors."""
- self.bot.http_session.post = MagicMock(side_effect=ClientConnectorError(Mock(), Mock()))
- with self.assertRaises(PasteUploadError):
- await send_to_paste_service("")
- self.assertEqual(self.bot.http_session.post.call_count, FAILED_REQUEST_ATTEMPTS)
-
- async def test_general_error_handled_and_request_repeated(self):
- """All `Exception`s are handled, logged and request repeated."""
- self.bot.http_session.post = MagicMock(side_effect=Exception)
- with self.assertRaises(PasteUploadError):
- await send_to_paste_service("")
- self.assertEqual(self.bot.http_session.post.call_count, FAILED_REQUEST_ATTEMPTS)
- self.assertLogs("bot.utils", logging.ERROR)
-
- async def test_raises_error_on_too_long_input(self):
- """Ensure PasteTooLongError is raised if `contents` is longer than `MAX_PASTE_LENGTH`."""
- contents = "a" * (MAX_PASTE_LENGTH + 1)
- with self.assertRaises(PasteTooLongError):
- await send_to_paste_service(contents)
-
- async def test_raises_on_too_large_max_length(self):
- """Ensure ValueError is raised if `max_length` passed is greater than `MAX_PASTE_LENGTH`."""
- with self.assertRaises(ValueError):
- await send_to_paste_service("Hello World!", max_length=MAX_PASTE_LENGTH + 1)
diff --git a/tests/helpers.py b/tests/helpers.py
index 26ac42697..580848c25 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -6,6 +6,7 @@ import logging
import unittest.mock
from asyncio import AbstractEventLoop
from collections.abc import Iterable
+from contextlib import contextmanager
from functools import cached_property
import discord
@@ -664,3 +665,12 @@ class MockAsyncWebhook(CustomMockMixin, unittest.mock.MagicMock):
"""
spec_set = webhook_instance
additional_spec_asyncs = ("send", "edit", "delete", "execute")
+
+@contextmanager
+def no_create_task():
+ def side_effect(coro, *_, **__):
+ coro.close()
+
+ with unittest.mock.patch("pydis_core.utils.scheduling.create_task") as create_task:
+ create_task.side_effect = side_effect
+ yield
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index fa7d0eb44..3d4cb09e7 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -316,7 +316,6 @@ class MockObjectTests(unittest.TestCase):
class MyMock(helpers.CustomMockMixin, unittest.mock.MagicMock):
child_mock_type = unittest.mock.MagicMock
- pass
mock = MyMock()
unittest.mock.seal(mock)