From 1346dc9b835dc5216f2a150cb2c221e81f4f67a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:01:06 +0000 Subject: [PATCH 1/5] Bump ruff from 0.7.4 to 0.8.0 in the python-dependencies group Bumps the python-dependencies group with 1 update: [ruff](https://github.com/astral-sh/ruff). Updates `ruff` from 0.7.4 to 0.8.0 - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.7.4...0.8.0) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-minor dependency-group: python-dependencies ... Signed-off-by: dependabot[bot] --- poetry.lock | 40 ++++++++++++++++++++-------------------- pyproject.toml | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/poetry.lock b/poetry.lock index eb134b6b..ba3abf3b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1782,29 +1782,29 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.7.4" +version = "0.8.0" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, - {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, - {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, - {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, - {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, - {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, - {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, + {file = "ruff-0.8.0-py3-none-linux_armv6l.whl", hash = "sha256:fcb1bf2cc6706adae9d79c8d86478677e3bbd4ced796ccad106fd4776d395fea"}, + {file = "ruff-0.8.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:295bb4c02d58ff2ef4378a1870c20af30723013f441c9d1637a008baaf928c8b"}, + {file = "ruff-0.8.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b1f1c76b47c18fa92ee78b60d2d20d7e866c55ee603e7d19c1e991fad933a9a"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb0d4f250a7711b67ad513fde67e8870109e5ce590a801c3722580fe98c33a99"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e55cce9aa93c5d0d4e3937e47b169035c7e91c8655b0974e61bb79cf398d49c"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f4cd64916d8e732ce6b87f3f5296a8942d285bbbc161acee7fe561134af64f9"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c5c1466be2a2ebdf7c5450dd5d980cc87c8ba6976fb82582fea18823da6fa362"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2dabfd05b96b7b8f2da00d53c514eea842bff83e41e1cceb08ae1966254a51df"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:facebdfe5a5af6b1588a1d26d170635ead6892d0e314477e80256ef4a8470cf3"}, + {file = "ruff-0.8.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87a8e86bae0dbd749c815211ca11e3a7bd559b9710746c559ed63106d382bd9c"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:85e654f0ded7befe2d61eeaf3d3b1e4ef3894469cd664ffa85006c7720f1e4a2"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:83a55679c4cb449fa527b8497cadf54f076603cc36779b2170b24f704171ce70"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:812e2052121634cf13cd6fddf0c1871d0ead1aad40a1a258753c04c18bb71bbd"}, + {file = "ruff-0.8.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:780d5d8523c04202184405e60c98d7595bdb498c3c6abba3b6d4cdf2ca2af426"}, + {file = "ruff-0.8.0-py3-none-win32.whl", hash = "sha256:5fdb6efecc3eb60bba5819679466471fd7d13c53487df7248d6e27146e985468"}, + {file = "ruff-0.8.0-py3-none-win_amd64.whl", hash = "sha256:582891c57b96228d146725975fbb942e1f30a0c4ba19722e692ca3eb25cc9b4f"}, + {file = "ruff-0.8.0-py3-none-win_arm64.whl", hash = "sha256:ba93e6294e9a737cd726b74b09a6972e36bb511f9a102f1d9a7e1ce94dd206a6"}, + {file = "ruff-0.8.0.tar.gz", hash = "sha256:a7ccfe6331bf8c8dad715753e157457faf7351c2b69f62f32c165c2dbcbacd44"}, ] [[package]] @@ -2111,4 +2111,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "32295d5ba32cf0f7f98b1a85b71df0ec757ee2b3137104b370ab0de9b3c957b0" +content-hash = "262c37a2e01e2c0898320e5cd41b3a909a2aaa16a8465167919351015c567cd3" diff --git a/pyproject.toml b/pyproject.toml index 6421b637..5c0a204e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ mypy = "~1.13" django-stubs = {extras = ["compatible-mypy"], version = "~5.1"} types-beautifulsoup4 = "^4.12" pytest = "^8.3" -ruff = "^0.7" +ruff = "^0.8" gitpython = "^3.1" pymarkdownlnt = "^0.9" ccft-pymarkdown = "^1.1" From c444314e533aa21a735208f809fc69aa09c1adaf Mon Sep 17 00:00:00 2001 From: MattyTheHacker <18513864+MattyTheHacker@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:31:40 +0000 Subject: [PATCH 2/5] Remove redundant rule ignores --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5c0a204e..ac0b0fbc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -164,8 +164,6 @@ ignore = [ "N806", "D203", "D212", - "ANN101", - "ANN102", "Q003", "TD002", "TD003", From 2865152ffdeb9e50a0dbdf216059db34d67249fc Mon Sep 17 00:00:00 2001 From: Matt Norton Date: Sat, 23 Nov 2024 13:20:29 +0000 Subject: [PATCH 3/5] Fix ruff errors --- cogs/__init__.py | 79 ++-- cogs/annual_handover_and_reset.py | 36 +- cogs/archive.py | 32 +- cogs/command_error.py | 23 +- cogs/committee_actions_tracking.py | 64 +-- cogs/delete_all.py | 24 +- cogs/edit_message.py | 24 +- cogs/get_token_authorisation.py | 32 +- cogs/induct.py | 58 +-- cogs/kill.py | 23 +- cogs/make_applicant.py | 39 +- cogs/make_member.py | 34 +- cogs/ping.py | 19 +- cogs/remind_me.py | 30 +- cogs/send_get_roles_reminders.py | 21 +- cogs/send_introduction_reminders.py | 33 +- cogs/source.py | 15 +- cogs/startup.py | 17 +- cogs/stats.py | 27 +- cogs/strike.py | 72 ++-- cogs/write_roles.py | 15 +- config.py | 61 +-- db/__init__.py | 11 +- db/_settings.py | 15 +- db/core/__init__.py | 8 +- db/core/app_config.py | 9 +- db/core/models/__init__.py | 30 +- db/core/models/managers.py | 43 +- db/core/models/utils.py | 30 +- exceptions/__init__.py | 58 +-- exceptions/base.py | 49 +-- exceptions/committee_actions.py | 16 +- exceptions/config_changes.py | 30 +- exceptions/does_not_exist.py | 99 ++--- exceptions/guild.py | 24 +- exceptions/messages.py | 24 +- exceptions/strike.py | 22 +- main.py | 15 +- poetry.lock | 617 +++++++++++++--------------- pyproject.toml | 134 +++--- tests/__init__.py | 7 +- tests/test_utils.py | 17 +- utils/__init__.py | 49 +-- utils/command_checks.py | 30 +- utils/error_capture_decorators.py | 39 +- utils/message_sender_components.py | 53 +-- utils/suppress_traceback.py | 14 +- utils/tex_bot.py | 22 +- utils/tex_bot_base_cog.py | 37 +- utils/tex_bot_contexts.py | 15 +- 50 files changed, 1155 insertions(+), 1140 deletions(-) mode change 100644 => 100755 main.py diff --git a/cogs/__init__.py b/cogs/__init__.py index 65c9202c..be1de51f 100644 --- a/cogs/__init__.py +++ b/cogs/__init__.py @@ -5,47 +5,8 @@ cogs for each activity. """ -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "AnnualRolesResetCommandCog", - "AnnualYearChannelsIncrementCommandCog", - "ArchiveCommandCog", - "ClearRemindersBacklogTaskCog", - "CommandErrorCog", - "CommitteeActionsTrackingSlashCommandsCog", - "CommitteeActionsTrackingContextCommandsCog", - "CommitteeHandoverCommandCog", - "DeleteAllCommandsCog", - "EditMessageCommandCog", - "EnsureMembersInductedCommandCog", - "GetTokenAuthorisationCommandCog", - "InductContextCommandsCog", - "InductSendMessageCog", - "InductSlashCommandCog", - "KillCommandCog", - "MakeApplicantContextCommandsCog", - "MakeApplicantSlashCommandCog", - "MakeMemberCommandCog", - "ManualModerationCog", - "PingCommandCog", - "RemindMeCommandCog", - "SendGetRolesRemindersTaskCog", - "SendIntroductionRemindersTaskCog", - "setup", - "SourceCommandCog", - "StartupCog", - "StatsCommandsCog", - "StrikeCommandCog", - "StrikeUserCommandCog", - "WriteRolesCommandCog", -) - - from typing import TYPE_CHECKING -from utils import TeXBot - from .annual_handover_and_reset import ( AnnualRolesResetCommandCog, AnnualYearChannelsIncrementCommandCog, @@ -80,12 +41,46 @@ from .write_roles import WriteRolesCommandCog if TYPE_CHECKING: - from collections.abc import Iterable + from collections.abc import Iterable, Sequence + + from utils import TeXBot, TeXBotBaseCog - from utils import TeXBotBaseCog +__all__: "Sequence[str]" = ( + "AnnualRolesResetCommandCog", + "AnnualYearChannelsIncrementCommandCog", + "ArchiveCommandCog", + "ClearRemindersBacklogTaskCog", + "CommandErrorCog", + "CommitteeActionsTrackingContextCommandsCog", + "CommitteeActionsTrackingSlashCommandsCog", + "CommitteeHandoverCommandCog", + "DeleteAllCommandsCog", + "EditMessageCommandCog", + "EnsureMembersInductedCommandCog", + "GetTokenAuthorisationCommandCog", + "InductContextCommandsCog", + "InductSendMessageCog", + "InductSlashCommandCog", + "KillCommandCog", + "MakeApplicantContextCommandsCog", + "MakeApplicantSlashCommandCog", + "MakeMemberCommandCog", + "ManualModerationCog", + "PingCommandCog", + "RemindMeCommandCog", + "SendGetRolesRemindersTaskCog", + "SendIntroductionRemindersTaskCog", + "SourceCommandCog", + "StartupCog", + "StatsCommandsCog", + "StrikeCommandCog", + "StrikeUserCommandCog", + "WriteRolesCommandCog", + "setup", +) -def setup(bot: TeXBot) -> None: +def setup(bot: "TeXBot") -> None: """Add all the cogs to the bot, at bot startup.""" cogs: Iterable[type[TeXBotBaseCog]] = ( AnnualRolesResetCommandCog, diff --git a/cogs/annual_handover_and_reset.py b/cogs/annual_handover_and_reset.py index f12bcaa6..b9ecbd43 100644 --- a/cogs/annual_handover_and_reset.py +++ b/cogs/annual_handover_and_reset.py @@ -1,24 +1,28 @@ """Contains cog classes for annual handover and role reset functionality.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "AnnualRolesResetCommandCog", - "AnnualYearChannelsIncrementCommandCog", - "CommitteeHandoverCommandCog", -) - import datetime import logging -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord from db.core.models import GroupMadeMember -from utils import AllChannelTypes, CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from typing import Final + + from utils import AllChannelTypes, TeXBotApplicationContext + +__all__: "Sequence[str]" = ( + "AnnualRolesResetCommandCog", + "AnnualYearChannelsIncrementCommandCog", + "CommitteeHandoverCommandCog", +) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class CommitteeHandoverCommandCog(TeXBotBaseCog): @@ -30,7 +34,7 @@ class CommitteeHandoverCommandCog(TeXBotBaseCog): ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def committee_handover(self, ctx: TeXBotApplicationContext) -> None: + async def committee_handover(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "committee_handover" command. @@ -156,7 +160,7 @@ async def committee_handover(self, ctx: TeXBotApplicationContext) -> None: class AnnualRolesResetCommandCog(TeXBotBaseCog): """Cog class that defines the "/annual-roles-reset" command.""" - ACADEMIC_YEAR_ROLE_NAMES: Final[frozenset[str]] = frozenset( + ACADEMIC_YEAR_ROLE_NAMES: "Final[frozenset[str]]" = frozenset( { "Foundation Year", "First Year", @@ -175,7 +179,7 @@ class AnnualRolesResetCommandCog(TeXBotBaseCog): ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def annual_roles_reset(self, ctx: TeXBotApplicationContext) -> None: + async def annual_roles_reset(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "annual_roles_reset" command. @@ -244,7 +248,7 @@ class AnnualYearChannelsIncrementCommandCog(TeXBotBaseCog): ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def increment_year_channels(self, ctx: TeXBotApplicationContext) -> None: + async def increment_year_channels(self, ctx: "TeXBotApplicationContext") -> None: """ Definition and callback response of the "increment_year_channels" command. diff --git a/cogs/archive.py b/cogs/archive.py index 7e348ade..3ee5d117 100644 --- a/cogs/archive.py +++ b/cogs/archive.py @@ -1,36 +1,40 @@ """Contains cog classes for any archival interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("ArchiveCommandCog",) - - import logging import re -from collections.abc import Set -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord from exceptions import DiscordMemberNotInMainGuildError from exceptions.base import BaseDoesNotExistError from utils import ( - AllChannelTypes, CommandChecks, - TeXBotApplicationContext, - TeXBotAutocompleteContext, TeXBotBaseCog, ) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +if TYPE_CHECKING: + from collections.abc import Sequence + from collections.abc import Set as AbstractSet + from logging import Logger + from typing import Final + + from utils import ( + AllChannelTypes, + TeXBotApplicationContext, + TeXBotAutocompleteContext, + ) + +__all__: "Sequence[str]" = ("ArchiveCommandCog",) + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class ArchiveCommandCog(TeXBotBaseCog): """Cog class that defines the "/archive" command and its call-back method.""" @staticmethod - async def autocomplete_get_categories(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501 + async def autocomplete_get_categories(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 """ Autocomplete callable that generates the set of available selectable categories. @@ -74,7 +78,7 @@ async def autocomplete_get_categories(ctx: TeXBotAutocompleteContext) -> Set[dis ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def archive(self, ctx: TeXBotApplicationContext, str_category_id: str) -> None: + async def archive(self, ctx: "TeXBotApplicationContext", str_category_id: str) -> None: """ Definition & callback response of the "archive" command. diff --git a/cogs/command_error.py b/cogs/command_error.py index f7906d0c..4bd9e271 100644 --- a/cogs/command_error.py +++ b/cogs/command_error.py @@ -1,14 +1,8 @@ """Contains cog classes for any command_error interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("CommandErrorCog",) - - import contextlib import logging -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord from discord import Forbidden @@ -19,16 +13,25 @@ GuildDoesNotExistError, ) from exceptions.base import BaseErrorWithErrorCode -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from typing import Final + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("CommandErrorCog",) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class CommandErrorCog(TeXBotBaseCog): """Cog class that defines additional code to execute upon a command error.""" @TeXBotBaseCog.listener() - async def on_application_command_error(self, ctx: TeXBotApplicationContext, error: discord.ApplicationCommandError) -> None: # noqa: E501 + async def on_application_command_error(self, ctx: "TeXBotApplicationContext", error: discord.ApplicationCommandError) -> None: # noqa: E501 """Log any major command errors in the logging channel & stderr.""" error_code: str | None = None message: str | None = "Please contact a committee member." diff --git a/cogs/committee_actions_tracking.py b/cogs/committee_actions_tracking.py index dbf25e06..c0a05c1e 100644 --- a/cogs/committee_actions_tracking.py +++ b/cogs/committee_actions_tracking.py @@ -1,20 +1,9 @@ """Contains cog classes for tracking committee-actions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "CommitteeActionsTrackingBaseCog", - "CommitteeActionsTrackingSlashCommandsCog", - "CommitteeActionsTrackingContextCommandsCog", -) - - import logging import random -from collections.abc import Set from enum import Enum -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist, ValidationError @@ -28,12 +17,27 @@ ) from utils import ( CommandChecks, - TeXBotApplicationContext, - TeXBotAutocompleteContext, TeXBotBaseCog, ) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +if TYPE_CHECKING: + from collections.abc import Sequence + from collections.abc import Set as AbstractSet + from logging import Logger + from typing import Final + + from utils import ( + TeXBotApplicationContext, + TeXBotAutocompleteContext, + ) + +__all__: "Sequence[str]" = ( + "CommitteeActionsTrackingBaseCog", + "CommitteeActionsTrackingContextCommandsCog", + "CommitteeActionsTrackingSlashCommandsCog", +) + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class Status(Enum): @@ -49,7 +53,7 @@ class Status(Enum): class CommitteeActionsTrackingBaseCog(TeXBotBaseCog): """Base cog class that defines methods for committee actions tracking.""" - async def _create_action(self, ctx: TeXBotApplicationContext, action_user: discord.Member, description: str) -> AssignedCommitteeAction | None: # noqa: E501 + async def _create_action(self, ctx: "TeXBotApplicationContext", action_user: discord.Member, description: str) -> AssignedCommitteeAction | None: # noqa: E501 """ Create the action object with the given description for the given user. @@ -115,7 +119,7 @@ class CommitteeActionsTrackingSlashCommandsCog(CommitteeActionsTrackingBaseCog): ) @staticmethod - async def autocomplete_get_committee_members(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501 + async def autocomplete_get_committee_members(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 """Autocomplete callable that generates a set of selectable committee members.""" try: committee_role: discord.Role = await ctx.bot.committee_role @@ -128,7 +132,7 @@ async def autocomplete_get_committee_members(ctx: TeXBotAutocompleteContext) -> } @staticmethod - async def autocomplete_get_user_action_ids(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501 + async def autocomplete_get_user_action_ids(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 """Autocomplete callable that provides a set of actions that belong to the user.""" if not ctx.interaction.user: logger.debug("User actions autocomplete did not have an interaction user!!") @@ -169,7 +173,7 @@ async def autocomplete_get_user_action_ids(ctx: TeXBotAutocompleteContext) -> Se } @staticmethod - async def autocomplete_get_action_status(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501, ARG004 + async def autocomplete_get_action_status(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501, ARG004 """Autocomplete callable that provides the set of possible Status' of actions.""" status_options: Sequence[tuple[str, str]] = ( AssignedCommitteeAction._meta.get_field("status").choices # type: ignore[assignment] @@ -203,7 +207,7 @@ async def autocomplete_get_action_status(ctx: TeXBotAutocompleteContext) -> Set[ ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def create(self, ctx: TeXBotApplicationContext, action_description: str, *, action_member_id: str | None) -> None: # noqa: E501 + async def create(self, ctx: "TeXBotApplicationContext", action_description: str, *, action_member_id: str | None) -> None: # noqa: E501 """ Definition and callback response of the "create" command. @@ -262,7 +266,7 @@ async def create(self, ctx: TeXBotApplicationContext, action_description: str, * ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def update_status(self, ctx: TeXBotApplicationContext, action_id: str, status: str) -> None: # noqa: E501 + async def update_status(self, ctx: "TeXBotApplicationContext", action_id: str, status: str) -> None: # noqa: E501 """ Definition and callback of the "update-status" command. @@ -333,7 +337,7 @@ async def update_status(self, ctx: TeXBotApplicationContext, action_id: str, sta required=True, parameter_name="action_description", ) - async def update_description(self, ctx: TeXBotApplicationContext, action_id: str, new_description: str) -> None: # noqa: E501 + async def update_description(self, ctx: "TeXBotApplicationContext", action_id: str, new_description: str) -> None: # noqa: E501 """ Definition and callback response of the "update-description" command. @@ -387,7 +391,7 @@ async def update_description(self, ctx: TeXBotApplicationContext, action_id: str ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def action_random_user(self, ctx: TeXBotApplicationContext, action_description: str) -> None: # noqa: E501 + async def action_random_user(self, ctx: "TeXBotApplicationContext", action_description: str) -> None: # noqa: E501 """ Definition and callback response of the "action-random-user" command. @@ -405,7 +409,7 @@ async def action_random_user(self, ctx: TeXBotApplicationContext, action_descrip ) return - index: int = random.randint(0, len(committee_members)) + index: int = random.randint(0, len(committee_members)) # noqa: S311 try: action_user: discord.Member = committee_members[index] @@ -443,7 +447,7 @@ async def action_random_user(self, ctx: TeXBotApplicationContext, action_descrip ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def action_all_committee(self, ctx: TeXBotApplicationContext, action_description: str) -> None: # noqa: E501 + async def action_all_committee(self, ctx: "TeXBotApplicationContext", action_description: str) -> None: # noqa: E501 """ Definition and callback response of the "action-all-committee" command. @@ -526,7 +530,7 @@ async def action_all_committee(self, ctx: TeXBotApplicationContext, action_descr ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def list_user_actions(self, ctx: TeXBotApplicationContext, *, action_member_id: str, ping: bool, status: str) -> None: # noqa: E501 + async def list_user_actions(self, ctx: "TeXBotApplicationContext", *, action_member_id: str, ping: bool, status: str) -> None: # noqa: E501 """ Definition and callback of the "/list" command. @@ -604,7 +608,7 @@ async def list_user_actions(self, ctx: TeXBotApplicationContext, *, action_membe ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def reassign_action(self, ctx:TeXBotApplicationContext, action_id: str, member_id: str) -> None: # noqa: E501 + async def reassign_action(self, ctx:"TeXBotApplicationContext", action_id: str, member_id: str) -> None: # noqa: E501 """Reassign the specified action to the specified user.""" try: action_id_int: int = int(action_id) @@ -683,7 +687,7 @@ async def reassign_action(self, ctx:TeXBotApplicationContext, action_id: str, me ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def list_all_actions(self, ctx:TeXBotApplicationContext, *, ping: bool, status: str) -> None: # noqa: E501 + async def list_all_actions(self, ctx:"TeXBotApplicationContext", *, ping: bool, status: str) -> None: # noqa: E501 """List all actions.""" # NOTE: this doesn't actually list *all* actions as it is possible for non-committee to be actioned. committee_role: discord.Role = await self.bot.committee_role @@ -739,7 +743,7 @@ async def list_all_actions(self, ctx:TeXBotApplicationContext, *, ping: bool, st required=True, parameter_name="action_id", ) - async def delete_action(self, ctx: TeXBotApplicationContext, action_id: str) -> None: + async def delete_action(self, ctx: "TeXBotApplicationContext", action_id: str) -> None: """ Definition & callback response of the "delete" command. @@ -783,7 +787,7 @@ class CommitteeActionsTrackingContextCommandsCog(CommitteeActionsTrackingBaseCog ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def action_message_author(self, ctx: TeXBotApplicationContext, message: discord.Message) -> None: # noqa: E501 + async def action_message_author(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 """ Definition and callback response of the "action-message-author" message command. diff --git a/cogs/delete_all.py b/cogs/delete_all.py index 7ad04ad3..0cc588d2 100644 --- a/cogs/delete_all.py +++ b/cogs/delete_all.py @@ -1,15 +1,19 @@ """Contains cog classes for any delete_all interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("DeleteAllCommandsCog",) - +from typing import TYPE_CHECKING import discord from db.core.models import AssignedCommitteeAction, DiscordReminder, GroupMadeMember -from db.core.models.utils import AsyncBaseModel -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + + from db.core.models.utils import AsyncBaseModel + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("DeleteAllCommandsCog",) class DeleteAllCommandsCog(TeXBotBaseCog): @@ -23,7 +27,7 @@ class DeleteAllCommandsCog(TeXBotBaseCog): ) @staticmethod - async def _delete_all(ctx: TeXBotApplicationContext, delete_model: type[AsyncBaseModel]) -> None: # noqa: E501 + async def _delete_all(ctx: "TeXBotApplicationContext", delete_model: type["AsyncBaseModel"]) -> None: # noqa: E501 """Perform the actual deletion process of all instances of the given model class.""" # noinspection PyProtectedMember await delete_model._default_manager.all().adelete() @@ -45,7 +49,7 @@ async def _delete_all(ctx: TeXBotApplicationContext, delete_model: type[AsyncBas ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def delete_all_reminders(self, ctx: TeXBotApplicationContext) -> None: + async def delete_all_reminders(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "delete_all_reminders" command. @@ -60,7 +64,7 @@ async def delete_all_reminders(self, ctx: TeXBotApplicationContext) -> None: ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def delete_all_group_made_members(self, ctx: TeXBotApplicationContext) -> None: + async def delete_all_group_made_members(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "delete_all_group_made_members" command. @@ -75,7 +79,7 @@ async def delete_all_group_made_members(self, ctx: TeXBotApplicationContext) -> ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def delete_all_actions(self, ctx: TeXBotApplicationContext) -> None: + async def delete_all_actions(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback respoonse of the "delete-all-actions" command. diff --git a/cogs/edit_message.py b/cogs/edit_message.py index f6139efb..89861a7f 100644 --- a/cogs/edit_message.py +++ b/cogs/edit_message.py @@ -1,12 +1,7 @@ """Contains cog classes for any edit_message interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("EditMessageCommandCog",) - - import re -from collections.abc import Set +from typing import TYPE_CHECKING import discord @@ -14,18 +9,27 @@ from exceptions.base import BaseDoesNotExistError from utils import ( CommandChecks, - TeXBotApplicationContext, - TeXBotAutocompleteContext, TeXBotBaseCog, ) +if TYPE_CHECKING: + from collections.abc import Sequence + from collections.abc import Set as AbstractSet + + from utils import ( + TeXBotApplicationContext, + TeXBotAutocompleteContext, + ) + +__all__: "Sequence[str]" = ("EditMessageCommandCog",) + class EditMessageCommandCog(TeXBotBaseCog): # noinspection SpellCheckingInspection """Cog class that defines the "/edit-message" command and its call-back method.""" @staticmethod - async def autocomplete_get_text_channels(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501 + async def autocomplete_get_text_channels(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 """ Autocomplete callable that generates the set of available selectable channels. @@ -76,7 +80,7 @@ async def autocomplete_get_text_channels(ctx: TeXBotAutocompleteContext) -> Set[ ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def edit_message(self, ctx: TeXBotApplicationContext, str_channel_id: str, str_message_id: str, new_message_content: str) -> None: # noqa: E501 + async def edit_message(self, ctx: "TeXBotApplicationContext", str_channel_id: str, str_message_id: str, new_message_content: str) -> None: # noqa: E501 """ Definition & callback response of the "edit_message" command. diff --git a/cogs/get_token_authorisation.py b/cogs/get_token_authorisation.py index 9d5e59c8..3b1ab90f 100644 --- a/cogs/get_token_authorisation.py +++ b/cogs/get_token_authorisation.py @@ -1,15 +1,8 @@ """Contains cog classes for token authorisation check interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("GetTokenAuthorisationCommandCog",) - - import contextlib import logging -from collections.abc import Iterable, Mapping -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import aiohttp import bs4 @@ -18,21 +11,30 @@ from config import settings from exceptions.does_not_exist import GuestRoleDoesNotExistError -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Iterable, Mapping, Sequence + from logging import Logger + from typing import Final + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("GetTokenAuthorisationCommandCog",) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") -REQUEST_HEADERS: Final[Mapping[str, str]] = { +REQUEST_HEADERS: "Final[Mapping[str, str]]" = { "Cache-Control": "no-cache", "Pragma": "no-cache", "Expires": "0", } -REQUEST_COOKIES: Final[Mapping[str, str]] = { +REQUEST_COOKIES: "Final[Mapping[str, str]]" = { ".ASPXAUTH": settings["MEMBERS_LIST_AUTH_SESSION_COOKIE"], } -REQUEST_URL: Final[str] = "https://guildofstudents.com/profile" +REQUEST_URL: "Final[str]" = "https://guildofstudents.com/profile" class GetTokenAuthorisationCommandCog(TeXBotBaseCog): @@ -44,7 +46,7 @@ class GetTokenAuthorisationCommandCog(TeXBotBaseCog): ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def get_token_authorisation(self, ctx: TeXBotApplicationContext) -> None: + async def get_token_authorisation(self, ctx: "TeXBotApplicationContext") -> None: """ Definition of the "get_token_authorisation" command. @@ -123,6 +125,6 @@ async def get_token_authorisation(self, ctx: TeXBotApplicationContext) -> None: ephemeral=bool( (not guest_role) or ctx.channel.permissions_for(guest_role).is_superset( discord.Permissions(view_channel=True), - ) # noqa: COM812 + ) ), ) diff --git a/cogs/induct.py b/cogs/induct.py index fe9ed678..0a687498 100644 --- a/cogs/induct.py +++ b/cogs/induct.py @@ -1,22 +1,9 @@ """Contains cog classes for any induction interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "BaseInductCog", - "EnsureMembersInductedCommandCog", - "InductContextCommandsCog", - "InductSendMessageCog", - "InductSlashCommandCog", -) - - import contextlib import logging import random -from collections.abc import Set -from logging import Logger -from typing import Final, Literal +from typing import TYPE_CHECKING import discord @@ -33,13 +20,30 @@ ) from utils import ( CommandChecks, - TeXBotApplicationContext, - TeXBotAutocompleteContext, TeXBotBaseCog, ) from utils.error_capture_decorators import capture_guild_does_not_exist_error -logger: Final[Logger] = logging.getLogger("TeX-Bot") +if TYPE_CHECKING: + from collections.abc import Sequence + from collections.abc import Set as AbstractSet + from logging import Logger + from typing import Final, Literal + + from utils import ( + TeXBotApplicationContext, + TeXBotAutocompleteContext, + ) + +__all__: "Sequence[str]" = ( + "BaseInductCog", + "EnsureMembersInductedCommandCog", + "InductContextCommandsCog", + "InductSendMessageCog", + "InductSlashCommandCog", +) + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class InductSendMessageCog(TeXBotBaseCog): @@ -77,7 +81,7 @@ async def on_member_update(self, before: discord.Member, after: discord.Member) MESSAGE_IS_INTRODUCTION_REMINDER: bool = bool( ("joined the " in message.content) and (" Discord guild but have not yet introduced" in message.content) - and message.author.bot # noqa: COM812 + and message.author.bot ) if MESSAGE_IS_INTRODUCTION_REMINDER: await message.delete( @@ -139,7 +143,7 @@ class BaseInductCog(TeXBotBaseCog): async def get_random_welcome_message(self, induction_member: discord.User | discord.Member | None = None) -> str: # noqa: E501 """Get & format a random welcome message.""" - random_welcome_message: str = random.choice(tuple(settings["WELCOME_MESSAGES"])) + random_welcome_message: str = random.choice(tuple(settings["WELCOME_MESSAGES"])) # noqa: S311 if "" in random_welcome_message: if not induction_member: @@ -179,7 +183,7 @@ async def get_random_welcome_message(self, induction_member: discord.User | disc return random_welcome_message.strip() - async def _perform_induction(self, ctx: TeXBotApplicationContext, induction_member: discord.Member, *, silent: bool) -> None: # noqa: E501 + async def _perform_induction(self, ctx: "TeXBotApplicationContext", induction_member: discord.Member, *, silent: bool) -> None: # noqa: E501 """Perform the actual process of inducting a member by giving them the Guest role.""" # NOTE: Shortcut accessors are placed at the top of the function, so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = self.bot.main_guild @@ -278,7 +282,7 @@ class InductSlashCommandCog(BaseInductCog): """Cog class that defines the "/induct" command and its call-back method.""" @staticmethod - async def autocomplete_get_members(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501 + async def autocomplete_get_members(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 """ Autocomplete callable that generates the set of available selectable members. @@ -331,7 +335,7 @@ async def autocomplete_get_members(ctx: TeXBotAutocompleteContext) -> Set[discor ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def induct(self, ctx: TeXBotApplicationContext, str_induct_member_id: str, *, silent: bool) -> None: # noqa: E501 + async def induct(self, ctx: "TeXBotApplicationContext", str_induct_member_id: str, *, silent: bool) -> None: # noqa: E501 """ Definition & callback response of the "induct" command. @@ -356,7 +360,7 @@ class InductContextCommandsCog(BaseInductCog): @discord.user_command(name="Induct User") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def non_silent_user_induct(self, ctx: TeXBotApplicationContext, member: discord.Member) -> None: # noqa: E501 + async def non_silent_user_induct(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 """ Definition & callback response of the "non_silent_induct" user-context-command. @@ -371,7 +375,7 @@ async def non_silent_user_induct(self, ctx: TeXBotApplicationContext, member: di @discord.user_command(name="Silently Induct User") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def silent_user_induct(self, ctx: TeXBotApplicationContext, member: discord.Member) -> None: # noqa: E501 + async def silent_user_induct(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 """ Definition & callback response of the "silent_induct" user-context-command. @@ -386,7 +390,7 @@ async def silent_user_induct(self, ctx: TeXBotApplicationContext, member: discor @discord.message_command(name="Induct Message Author") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def non_silent_message_induct(self, ctx: TeXBotApplicationContext, message: discord.Message) -> None: # noqa: E501 + async def non_silent_message_induct(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 """ Definition and callback response of the "non_silent_induct" message-context-command. @@ -416,7 +420,7 @@ async def non_silent_message_induct(self, ctx: TeXBotApplicationContext, message @discord.message_command(name="Silently Induct Message Author") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def silent_message_induct(self, ctx: TeXBotApplicationContext, message: discord.Message) -> None: # noqa: E501 + async def silent_message_induct(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 """ Definition and callback response of the "silent_induct" message-context-command. @@ -453,7 +457,7 @@ class EnsureMembersInductedCommandCog(TeXBotBaseCog): ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def ensure_members_inducted(self, ctx: TeXBotApplicationContext) -> None: + async def ensure_members_inducted(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "ensure_members_inducted" command. diff --git a/cogs/kill.py b/cogs/kill.py index 56b7e5bc..86e12909 100644 --- a/cogs/kill.py +++ b/cogs/kill.py @@ -1,22 +1,25 @@ """Contains cog classes for any killing interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("ConfirmKillView", "KillCommandCog") - - import contextlib import logging -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord from discord.ui import View from exceptions import CommitteeRoleDoesNotExistError -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from typing import Final + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("ConfirmKillView", "KillCommandCog") -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class ConfirmKillView(View): @@ -50,7 +53,7 @@ class KillCommandCog(TeXBotBaseCog): ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def kill(self, ctx: TeXBotApplicationContext) -> None: + async def kill(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "kill" command. diff --git a/cogs/make_applicant.py b/cogs/make_applicant.py index daceec7e..96854b63 100644 --- a/cogs/make_applicant.py +++ b/cogs/make_applicant.py @@ -1,24 +1,27 @@ """Contains cog classes for making a user into an applicant.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "BaseMakeApplicantCog", - "MakeApplicantContextCommandsCog", - "MakeApplicantSlashCommandCog", -) - - import logging -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord from exceptions.does_not_exist import ApplicantRoleDoesNotExistError, GuildDoesNotExistError -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from typing import Final + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ( + "BaseMakeApplicantCog", + "MakeApplicantContextCommandsCog", + "MakeApplicantSlashCommandCog", +) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class BaseMakeApplicantCog(TeXBotBaseCog): @@ -29,7 +32,7 @@ class BaseMakeApplicantCog(TeXBotBaseCog): child cog container classes. """ - async def _perform_make_applicant(self, ctx: TeXBotApplicationContext, applicant_member: discord.Member) -> None: # noqa: E501 + async def _perform_make_applicant(self, ctx: "TeXBotApplicationContext", applicant_member: discord.Member) -> None: # noqa: E501 """Perform the actual process of making the user into a group-applicant.""" # NOTE: Shortcut accessors are placed at the top of the function, so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = ctx.bot.main_guild @@ -94,7 +97,7 @@ class MakeApplicantSlashCommandCog(BaseMakeApplicantCog): """Cog class that defines the "/make_applicant" slash-command.""" @staticmethod - async def autocomplete_get_members(ctx: TeXBotApplicationContext) -> set[discord.OptionChoice]: # noqa: E501 + async def autocomplete_get_members(ctx: "TeXBotApplicationContext") -> set[discord.OptionChoice]: # noqa: E501 """ Autocomplete callable that generates the set of available selectable members. @@ -137,7 +140,7 @@ async def autocomplete_get_members(ctx: TeXBotApplicationContext) -> set[discord ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def make_applicant(self, ctx: TeXBotApplicationContext, str_applicant_member_id: str) -> None: # noqa: E501 + async def make_applicant(self, ctx: "TeXBotApplicationContext", str_applicant_member_id: str) -> None: # noqa: E501 """ Definition & callback response of the "make_applicant" command. @@ -162,7 +165,7 @@ class MakeApplicantContextCommandsCog(BaseMakeApplicantCog): @discord.user_command(name="Make Applicant") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def user_make_applicant(self, ctx: TeXBotApplicationContext, member: discord.Member) -> None: # noqa: E501 + async def user_make_applicant(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 """ Definition and callback response of the "make_applicant" user-context-command. @@ -175,7 +178,7 @@ async def user_make_applicant(self, ctx: TeXBotApplicationContext, member: disco @discord.message_command(name="Make Message Author Applicant") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def message_make_applicant(self, ctx: TeXBotApplicationContext, message: discord.Message) -> None: # noqa: E501 + async def message_make_applicant(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 """ Definition of the "message_make_applicant" message-context-command. diff --git a/cogs/make_member.py b/cogs/make_member.py index 322179d7..b68bcd01 100644 --- a/cogs/make_member.py +++ b/cogs/make_member.py @@ -1,16 +1,9 @@ """Contains cog classes for any make_member interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("MakeMemberCommandCog",) - - import contextlib import logging import re -from collections.abc import Mapping -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import aiohttp import bs4 @@ -25,11 +18,20 @@ CommitteeRoleDoesNotExistError, GuestRoleDoesNotExistError, ) -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Mapping, Sequence + from logging import Logger + from typing import Final + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("MakeMemberCommandCog",) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") -_GROUP_MEMBER_ID_ARGUMENT_DESCRIPTIVE_NAME: Final[str] = f"""{ +_GROUP_MEMBER_ID_ARGUMENT_DESCRIPTIVE_NAME: "Final[str]" = f"""{ "Student" if ( settings["_GROUP_FULL_NAME"] @@ -48,24 +50,24 @@ else "Member" } ID""" -_GROUP_MEMBER_ID_ARGUMENT_NAME: Final[str] = ( +_GROUP_MEMBER_ID_ARGUMENT_NAME: "Final[str]" = ( _GROUP_MEMBER_ID_ARGUMENT_DESCRIPTIVE_NAME.lower().replace( " ", "", ) ) -REQUEST_HEADERS: Final[Mapping[str, str]] = { +REQUEST_HEADERS: "Final[Mapping[str, str]]" = { "Cache-Control": "no-cache", "Pragma": "no-cache", "Expires": "0", } -REQUEST_COOKIES: Final[Mapping[str, str]] = { +REQUEST_COOKIES: "Final[Mapping[str, str]]" = { ".ASPXAUTH": settings["MEMBERS_LIST_AUTH_SESSION_COOKIE"], } -REQUEST_URL: Final[str] = settings["MEMBERS_LIST_URL"] +REQUEST_URL: "Final[str]" = settings["MEMBERS_LIST_URL"] class MakeMemberCommandCog(TeXBotBaseCog): @@ -109,7 +111,7 @@ class MakeMemberCommandCog(TeXBotBaseCog): parameter_name="group_member_id", ) @CommandChecks.check_interaction_user_in_main_guild - async def make_member(self, ctx: TeXBotApplicationContext, group_member_id: str) -> None: + async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: str) -> None: """ Definition & callback response of the "make_member" command. diff --git a/cogs/ping.py b/cogs/ping.py index f3f3d13a..2d3c0179 100644 --- a/cogs/ping.py +++ b/cogs/ping.py @@ -1,26 +1,29 @@ """Contains cog classes for any ping interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("PingCommandCog",) - - import random +from typing import TYPE_CHECKING import discord from config import settings -from utils import TeXBotApplicationContext, TeXBotBaseCog +from utils import TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("PingCommandCog",) class PingCommandCog(TeXBotBaseCog): """Cog class that defines the "/remindme" command and its call-back method.""" @discord.slash_command(description="Replies with Pong!") # type: ignore[no-untyped-call, misc] - async def ping(self, ctx: TeXBotApplicationContext) -> None: + async def ping(self, ctx: "TeXBotApplicationContext") -> None: """Definition & callback response of the "ping" command.""" await ctx.respond( - random.choices( + random.choices( # noqa: S311 [ "Pong!", "`64 bytes from TeX-Bot: icmp_seq=1 ttl=63 time=0.01 ms`", diff --git a/cogs/remind_me.py b/cogs/remind_me.py index 43a6713f..9f6021e6 100644 --- a/cogs/remind_me.py +++ b/cogs/remind_me.py @@ -1,18 +1,11 @@ """Contains cog classes for any remind_me interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("ClearRemindersBacklogTaskCog", "RemindMeCommandCog") - - import datetime import functools import itertools import logging import re -from collections.abc import Set -from logging import Logger -from typing import TYPE_CHECKING, Final, override +from typing import TYPE_CHECKING, override import discord import parsedatetime @@ -21,21 +14,28 @@ from django.utils import timezone from db.core.models import DiscordMember, DiscordReminder -from utils import TeXBot, TeXBotApplicationContext, TeXBotAutocompleteContext, TeXBotBaseCog +from utils import TeXBotBaseCog if TYPE_CHECKING: import time - from collections.abc import Iterator + from collections.abc import Iterator, Sequence + from collections.abc import Set as AbstractSet + from logging import Logger + from typing import Final + from utils import TeXBot, TeXBotApplicationContext, TeXBotAutocompleteContext -logger: Final[Logger] = logging.getLogger("TeX-Bot") +__all__: "Sequence[str]" = ("ClearRemindersBacklogTaskCog", "RemindMeCommandCog") + + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class RemindMeCommandCog(TeXBotBaseCog): """Cog class that defines the "/remind-me" command and its call-back method.""" @staticmethod - async def autocomplete_get_delays(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: C901, PLR0912, PLR0915, E501 + async def autocomplete_get_delays(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: PLR0912, PLR0915, E501 """ Autocomplete callable that generates the common delay input values. @@ -191,7 +191,7 @@ async def autocomplete_get_delays(ctx: TeXBotAutocompleteContext) -> Set[discord description="The message you want to be reminded with.", required=False, ) - async def remind_me(self, ctx: TeXBotApplicationContext, delay: str, message: str) -> None: + async def remind_me(self, ctx: "TeXBotApplicationContext", delay: str, message: str) -> None: # noqa: E501 """ Definition & callback response of the "remind_me" command. @@ -229,7 +229,7 @@ async def remind_me(self, ctx: TeXBotApplicationContext, delay: str, message: st and any( "already exists" in error for error in create_discord_reminder_error.message_dict["__all__"] - ) # noqa: COM812 + ) ) if not ERROR_IS_ALREADY_EXISTS: await self.command_send_error(ctx, message="An unrecoverable error occurred.") @@ -262,7 +262,7 @@ class ClearRemindersBacklogTaskCog(TeXBotBaseCog): """Cog class that defines the clear_reminders_backlog task.""" @override - def __init__(self, bot: TeXBot) -> None: + def __init__(self, bot: "TeXBot") -> None: """Start all task managers when this cog is initialised.""" self.clear_reminders_backlog.start() diff --git a/cogs/send_get_roles_reminders.py b/cogs/send_get_roles_reminders.py index c9157ad4..f1290849 100644 --- a/cogs/send_get_roles_reminders.py +++ b/cogs/send_get_roles_reminders.py @@ -1,15 +1,9 @@ """Contains cog classes for any send_get_roles_reminders interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("SendGetRolesRemindersTaskCog",) - - import contextlib import functools import logging -from logging import Logger -from typing import TYPE_CHECKING, Final, override +from typing import TYPE_CHECKING, override import discord from discord import AuditLogAction @@ -19,7 +13,7 @@ from config import settings from db.core.models import SentGetRolesReminderMember from exceptions import GuestRoleDoesNotExistError, RolesChannelDoesNotExistError -from utils import TeXBot, TeXBotBaseCog +from utils import TeXBotBaseCog from utils.error_capture_decorators import ( ErrorCaptureDecorators, capture_guild_does_not_exist_error, @@ -27,15 +21,22 @@ if TYPE_CHECKING: import datetime + from collections.abc import Sequence + from logging import Logger + from typing import Final + + from utils import TeXBot + +__all__: "Sequence[str]" = ("SendGetRolesRemindersTaskCog",) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class SendGetRolesRemindersTaskCog(TeXBotBaseCog): """Cog class that defines the send_get_roles_reminders task.""" @override - def __init__(self, bot: TeXBot) -> None: + def __init__(self, bot: "TeXBot") -> None: """Start all task managers when this cog is initialised.""" if settings["SEND_GET_ROLES_REMINDERS"]: self.send_get_roles_reminders.start() diff --git a/cogs/send_introduction_reminders.py b/cogs/send_introduction_reminders.py index 8abbcadd..03b983c3 100644 --- a/cogs/send_introduction_reminders.py +++ b/cogs/send_introduction_reminders.py @@ -1,14 +1,8 @@ """Contains cog classes for any send_introduction_reminders interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("SendIntroductionRemindersTaskCog",) - - import functools import logging -from logging import Logger -from typing import Final, override +from typing import TYPE_CHECKING, override import discord import emoji @@ -24,20 +18,29 @@ SentOneOffIntroductionReminderMember, ) from exceptions import DiscordMemberNotInMainGuildError, GuestRoleDoesNotExistError -from utils import TeXBot, TeXBotBaseCog +from utils import TeXBotBaseCog from utils.error_capture_decorators import ( ErrorCaptureDecorators, capture_guild_does_not_exist_error, ) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from typing import Final + + from utils import TeXBot + +__all__: "Sequence[str]" = ("SendIntroductionRemindersTaskCog",) + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class SendIntroductionRemindersTaskCog(TeXBotBaseCog): """Cog class that defines the send_introduction_reminders task.""" @override - def __init__(self, bot: TeXBot) -> None: + def __init__(self, bot: "TeXBot") -> None: """Start all task managers when this cog is initialised.""" if settings["SEND_INTRODUCTION_REMINDERS"]: if settings["SEND_INTRODUCTION_REMINDERS"] == "interval": @@ -134,7 +137,7 @@ async def send_introduction_reminders(self) -> None: bool(message.components) and isinstance(message.components[0], discord.ActionRow) and isinstance(message.components[0].children[0], discord.Button) - and message.components[0].children[0].custom_id == "opt_out_introduction_reminders_button" # noqa: COM812, E501 + and message.components[0].children[0].custom_id == "opt_out_introduction_reminders_button" # noqa: E501 ) if MESSAGE_CONTAINS_OPT_IN_OUT_BUTTON: await message.edit(view=None) @@ -187,7 +190,7 @@ class OptOutIntroductionRemindersView(View): """ @override - def __init__(self, bot: TeXBot) -> None: + def __init__(self, bot: "TeXBot") -> None: """Initialise a new discord.View, to opt-in/out of introduction reminders.""" self.bot: TeXBot = bot # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 @@ -227,7 +230,7 @@ async def opt_out_introduction_reminders_button_callback(self, button: discord.B BUTTON_WILL_MAKE_OPT_OUT: Final[bool] = bool( button.style == discord.ButtonStyle.red or str(button.emoji) == emoji.emojize(":no_good:", language="alias") - or (button.label and "Opt-out" in button.label) # noqa: COM812 + or (button.label and "Opt-out" in button.label) ) BUTTON_WILL_MAKE_OPT_IN: Final[bool] = bool( @@ -236,11 +239,11 @@ async def opt_out_introduction_reminders_button_callback(self, button: discord.B ":raised_hand:", language="alias", ) - or (button.label and "Opt back in" in button.label) # noqa: COM812 + or (button.label and "Opt back in" in button.label) ) INCOMPATIBLE_BUTTONS: Final[bool] = bool( (BUTTON_WILL_MAKE_OPT_OUT and BUTTON_WILL_MAKE_OPT_IN) - or (not BUTTON_WILL_MAKE_OPT_OUT and not BUTTON_WILL_MAKE_OPT_IN) # noqa: COM812 + or (not BUTTON_WILL_MAKE_OPT_OUT and not BUTTON_WILL_MAKE_OPT_IN) ) if INCOMPATIBLE_BUTTONS: INCOMPATIBLE_BUTTONS_MESSAGE: Final[str] = "Conflicting buttons pressed" diff --git a/cogs/source.py b/cogs/source.py index 7ec7bb2c..8e4c8583 100644 --- a/cogs/source.py +++ b/cogs/source.py @@ -1,13 +1,18 @@ """Contains cog classes for any source interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("SourceCommandCog",) +from typing import TYPE_CHECKING import discord -from utils import TeXBotApplicationContext, TeXBotBaseCog +from utils import TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("SourceCommandCog",) class SourceCommandCog(TeXBotBaseCog): @@ -16,7 +21,7 @@ class SourceCommandCog(TeXBotBaseCog): @discord.slash_command( # type: ignore[no-untyped-call, misc] description="Displays information about the source code of TeX-Bot.", ) - async def source(self, ctx: TeXBotApplicationContext) -> None: + async def source(self, ctx: "TeXBotApplicationContext") -> None: """Definition & callback response of the "source" command.""" await ctx.respond( ( diff --git a/cogs/startup.py b/cogs/startup.py index 0e179caa..d1ef5aa9 100644 --- a/cogs/startup.py +++ b/cogs/startup.py @@ -1,13 +1,7 @@ """Contains cog classes for any startup interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("StartupCog",) - - import logging -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord from discord_logging.handler import DiscordHandler @@ -25,7 +19,14 @@ ) from utils import TeXBotBaseCog -logger: Final[Logger] = logging.getLogger("TeX-Bot") +if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from typing import Final + +__all__: "Sequence[str]" = ("StartupCog",) + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class StartupCog(TeXBotBaseCog): diff --git a/cogs/stats.py b/cogs/stats.py index 6ce1f7d1..147c014c 100644 --- a/cogs/stats.py +++ b/cogs/stats.py @@ -1,15 +1,9 @@ """Contains cog classes for any stats interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("amount_of_time_formatter", "plot_bar_chart", "StatsCommandsCog") - - import io import math import re -from collections.abc import AsyncIterable -from typing import TYPE_CHECKING, Final +from typing import TYPE_CHECKING import discord import matplotlib.pyplot as plt @@ -17,14 +11,19 @@ from config import settings from db.core.models import LeftDiscordMember -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog from utils.error_capture_decorators import capture_guild_does_not_exist_error if TYPE_CHECKING: - from collections.abc import Collection + from collections.abc import AsyncIterable, Collection, Sequence + from typing import Final from matplotlib.text import Text as Plot_Text + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("StatsCommandsCog", "amount_of_time_formatter", "plot_bar_chart") + def amount_of_time_formatter(value: float, time_scale: str) -> str: """ @@ -136,7 +135,7 @@ def plot_bar_chart(data: dict[str, int], x_label: str, y_label: str, title: str, class StatsCommandsCog(TeXBotBaseCog): """Cog class that defines the "/stats" command group and its command call-back methods.""" - _DISCORD_SERVER_NAME: Final[str] = f"""{ + _DISCORD_SERVER_NAME: "Final[str]" = f"""{ "the " if ( settings["_GROUP_SHORT_NAME"] is not None and ( @@ -179,7 +178,7 @@ class StatsCommandsCog(TeXBotBaseCog): required=False, parameter_name="str_channel_id", ) - async def channel_stats(self, ctx: TeXBotApplicationContext, str_channel_id: str) -> None: + async def channel_stats(self, ctx: "TeXBotApplicationContext", str_channel_id: str) -> None: # noqa: E501 """ Definition & callback response of the "channel_stats" command. @@ -290,7 +289,7 @@ async def channel_stats(self, ctx: TeXBotApplicationContext, str_channel_id: str name="server", description=f"Displays the stats for the whole of {_DISCORD_SERVER_NAME}", ) - async def server_stats(self, ctx: TeXBotApplicationContext) -> None: + async def server_stats(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "server_stats" command. @@ -425,7 +424,7 @@ async def server_stats(self, ctx: TeXBotApplicationContext) -> None: description="Displays stats about the number of messages you have sent.", ) @CommandChecks.check_interaction_user_in_main_guild - async def user_stats(self, ctx: TeXBotApplicationContext) -> None: + async def user_stats(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "user_stats" command. @@ -513,7 +512,7 @@ async def user_stats(self, ctx: TeXBotApplicationContext) -> None: name="left-members", description=f"Displays the stats about members that have left {_DISCORD_SERVER_NAME}", ) - async def left_member_stats(self, ctx: TeXBotApplicationContext) -> None: + async def left_member_stats(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "left_member_stats" command. diff --git a/cogs/strike.py b/cogs/strike.py index 6c99102d..400be22d 100644 --- a/cogs/strike.py +++ b/cogs/strike.py @@ -1,27 +1,11 @@ """Contains cog classes for any strike interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "BaseStrikeCog", - "ConfirmManualModerationView", - "ConfirmStrikeMemberView", - "ConfirmStrikesOutOfSyncWithBanView", - "ManualModerationCog", - "perform_moderation_action", - "StrikeCommandCog", - "StrikeUserCommandCog", -) - - import asyncio import contextlib import datetime import logging import re -from collections.abc import Mapping, Set -from logging import Logger -from typing import Final +from typing import TYPE_CHECKING import discord @@ -39,8 +23,6 @@ ) from utils import ( CommandChecks, - TeXBotApplicationContext, - TeXBotAutocompleteContext, TeXBotBaseCog, ) from utils.error_capture_decorators import ( @@ -49,13 +31,37 @@ ) from utils.message_sender_components import ( ChannelMessageSender, - MessageSavingSenderComponent, ResponseMessageSender, ) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +if TYPE_CHECKING: + from collections.abc import Mapping, Sequence + from collections.abc import Set as AbstractSet + from logging import Logger + from typing import Final + + from utils import ( + TeXBotApplicationContext, + TeXBotAutocompleteContext, + ) + from utils.message_sender_components import ( + MessageSavingSenderComponent, + ) + +__all__: "Sequence[str]" = ( + "BaseStrikeCog", + "ConfirmManualModerationView", + "ConfirmStrikeMemberView", + "ConfirmStrikesOutOfSyncWithBanView", + "ManualModerationCog", + "StrikeCommandCog", + "StrikeUserCommandCog", + "perform_moderation_action", +) + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") -FORMATTED_MODERATION_ACTIONS: Final[Mapping[discord.AuditLogAction, str]] = { +FORMATTED_MODERATION_ACTIONS: "Final[Mapping[discord.AuditLogAction, str]]" = { discord.AuditLogAction.member_update: "timed-out", discord.AuditLogAction.kick: "kicked", discord.AuditLogAction.ban: "banned", @@ -220,7 +226,7 @@ class BaseStrikeCog(TeXBotBaseCog): by child strike cog container classes. """ - SUGGESTED_ACTIONS: Final[Mapping[int, str]] = {1: "time-out", 2: "kick", 3: "ban"} + SUGGESTED_ACTIONS: "Final[Mapping[int, str]]" = {1: "time-out", 2: "kick", 3: "ban"} # noqa: RUF012 async def _send_strike_user_message(self, strike_user: discord.User | discord.Member, member_strikes: DiscordMemberStrikes) -> None: # noqa: E501 # noinspection PyUnusedLocal @@ -239,7 +245,7 @@ async def _send_strike_user_message(self, strike_user: discord.User | discord.Me else "" ) - actual_strike_amount: int = member_strikes.strikes if member_strikes.strikes < 3 else 3 + actual_strike_amount: int = min(3, member_strikes.strikes) await strike_user.send( "Hi, a recent incident occurred in which you may have broken one or more of " @@ -256,7 +262,7 @@ async def _send_strike_user_message(self, strike_user: discord.User | discord.Me "with you shortly, to discuss this further.", ) - async def _confirm_perform_moderation_action(self, message_sender_component: MessageSavingSenderComponent, interaction_user: discord.User, strike_user: discord.Member, confirm_strike_message: str, actual_strike_amount: int, button_callback_channel: discord.TextChannel | discord.DMChannel) -> None: # noqa: E501 + async def _confirm_perform_moderation_action(self, message_sender_component: "MessageSavingSenderComponent", interaction_user: discord.User, strike_user: discord.Member, confirm_strike_message: str, actual_strike_amount: int, button_callback_channel: discord.TextChannel | discord.DMChannel) -> None: # noqa: E501 await message_sender_component.send( content=confirm_strike_message, view=ConfirmStrikeMemberView(), @@ -302,7 +308,7 @@ async def _confirm_perform_moderation_action(self, message_sender_component: Mes raise ValueError - async def _confirm_increase_strike(self, message_sender_component: MessageSavingSenderComponent, interaction_user: discord.User, strike_user: discord.User | discord.Member, member_strikes: DiscordMemberStrikes, button_callback_channel: discord.TextChannel | discord.DMChannel, *, perform_action: bool) -> None: # noqa: E501 + async def _confirm_increase_strike(self, message_sender_component: "MessageSavingSenderComponent", interaction_user: discord.User, strike_user: discord.User | discord.Member, member_strikes: DiscordMemberStrikes, button_callback_channel: discord.TextChannel | discord.DMChannel, *, perform_action: bool) -> None: # noqa: E501 if perform_action and isinstance(strike_user, discord.User): STRIKE_USER_TYPE_ERROR_MESSAGE: Final[str] = ( "Cannot perform moderation action on non-guild member." @@ -371,11 +377,11 @@ async def _confirm_increase_strike(self, message_sender_component: MessageSaving interaction_user, strike_user, confirm_strike_message, - (member_strikes.strikes if member_strikes.strikes < 3 else 3), + min(3, member_strikes.strikes), button_callback_channel, ) - async def _command_perform_strike(self, ctx: TeXBotApplicationContext, strike_member: discord.Member) -> None: # noqa: E501 + async def _command_perform_strike(self, ctx: "TeXBotApplicationContext", strike_member: discord.Member) -> None: # noqa: E501 """ Perform the actual process of giving a member an additional strike. @@ -513,7 +519,7 @@ async def _confirm_manual_add_strike(self, strike_user: discord.User | discord.M STRIKES_OUT_OF_SYNC_WITH_BAN: Final[bool] = bool( (action != discord.AuditLogAction.ban and member_strikes.strikes >= 3) - or (action == discord.AuditLogAction.ban and member_strikes.strikes > 3) # noqa: COM812 + or (action == discord.AuditLogAction.ban and member_strikes.strikes > 3) ) if STRIKES_OUT_OF_SYNC_WITH_BAN: out_of_sync_ban_confirmation_message: discord.Message = await confirmation_message_channel.send( # noqa: E501 @@ -728,7 +734,7 @@ async def on_member_remove(self, member: discord.Member) -> None: MEMBER_REMOVED_BECAUSE_OF_MANUALLY_APPLIED_KICK: Final[bool] = bool( member.guild == self.bot.main_guild and not member.bot - and not await asyncany(ban.user == member async for ban in main_guild.bans()) # noqa: COM812 + and not await asyncany(ban.user == member async for ban in main_guild.bans()) ) if not MEMBER_REMOVED_BECAUSE_OF_MANUALLY_APPLIED_KICK: return @@ -756,7 +762,7 @@ class StrikeCommandCog(BaseStrikeCog): """Cog class that defines the "/strike" command and its call-back method.""" @staticmethod - async def autocomplete_get_members(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501 + async def autocomplete_get_members(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 """ Autocomplete callable that generates the set of available selectable members. @@ -799,7 +805,7 @@ async def autocomplete_get_members(ctx: TeXBotAutocompleteContext) -> Set[discor ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def strike(self, ctx: TeXBotApplicationContext, str_strike_member_id: str) -> None: + async def strike(self, ctx: "TeXBotApplicationContext", str_strike_member_id: str) -> None: """ Definition & callback response of the "strike" command. @@ -824,6 +830,6 @@ class StrikeUserCommandCog(BaseStrikeCog): @discord.user_command(name="Strike User") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def user_strike(self, ctx: TeXBotApplicationContext, member: discord.Member) -> None: + async def user_strike(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 """Call the _strike command, providing the required command arguments.""" await self._command_perform_strike(ctx, member) diff --git a/cogs/write_roles.py b/cogs/write_roles.py index 8c3503c9..4646aeb9 100644 --- a/cogs/write_roles.py +++ b/cogs/write_roles.py @@ -1,14 +1,19 @@ """Contains cog classes for any write_roles interactions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("WriteRolesCommandCog",) +from typing import TYPE_CHECKING import discord from config import settings -from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog +from utils import CommandChecks, TeXBotBaseCog + +if TYPE_CHECKING: + from collections.abc import Sequence + + from utils import TeXBotApplicationContext + +__all__: "Sequence[str]" = ("WriteRolesCommandCog",) class WriteRolesCommandCog(TeXBotBaseCog): @@ -22,7 +27,7 @@ class WriteRolesCommandCog(TeXBotBaseCog): ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def write_roles(self, ctx: TeXBotApplicationContext) -> None: + async def write_roles(self, ctx: "TeXBotApplicationContext") -> None: """ Definition & callback response of the "write_roles" command. diff --git a/config.py b/config.py index 460f598c..b42b669a 100644 --- a/config.py +++ b/config.py @@ -5,19 +5,6 @@ These values are used to configure the functionality of the bot at run-time. """ -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "TRUE_VALUES", - "FALSE_VALUES", - "VALID_SEND_INTRODUCTION_REMINDERS_VALUES", - "DEFAULT_STATISTICS_ROLES", - "LOG_LEVEL_CHOICES", - "run_setup", - "settings", -) - - import abc import functools import importlib @@ -27,10 +14,8 @@ import re from collections.abc import Iterable, Mapping from datetime import timedelta -from logging import Logger from pathlib import Path -from re import Match -from typing import IO, Any, ClassVar, Final, final +from typing import TYPE_CHECKING, final import dotenv import validators @@ -41,14 +26,30 @@ MessagesJSONFileValueError, ) -PROJECT_ROOT: Final[Path] = Path(__file__).parent.resolve() +if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from re import Match + from typing import IO, Any, ClassVar, Final + +__all__: "Sequence[str]" = ( + "DEFAULT_STATISTICS_ROLES", + "FALSE_VALUES", + "LOG_LEVEL_CHOICES", + "TRUE_VALUES", + "VALID_SEND_INTRODUCTION_REMINDERS_VALUES", + "run_setup", + "settings", +) + +PROJECT_ROOT: "Final[Path]" = Path(__file__).parent.resolve() -TRUE_VALUES: Final[frozenset[str]] = frozenset({"true", "1", "t", "y", "yes", "on"}) -FALSE_VALUES: Final[frozenset[str]] = frozenset({"false", "0", "f", "n", "no", "off"}) -VALID_SEND_INTRODUCTION_REMINDERS_VALUES: Final[frozenset[str]] = frozenset( +TRUE_VALUES: "Final[frozenset[str]]" = frozenset({"true", "1", "t", "y", "yes", "on"}) +FALSE_VALUES: "Final[frozenset[str]]" = frozenset({"false", "0", "f", "n", "no", "off"}) +VALID_SEND_INTRODUCTION_REMINDERS_VALUES: "Final[frozenset[str]]" = frozenset( {"once"} | TRUE_VALUES | FALSE_VALUES, ) -DEFAULT_STATISTICS_ROLES: Final[frozenset[str]] = frozenset( +DEFAULT_STATISTICS_ROLES: "Final[frozenset[str]]" = frozenset( { "Committee", "Committee-Elect", @@ -69,7 +70,7 @@ "Quiz Victor", }, ) -LOG_LEVEL_CHOICES: Final[Sequence[str]] = ( +LOG_LEVEL_CHOICES: "Final[Sequence[str]]" = ( "DEBUG", "INFO", "WARNING", @@ -77,7 +78,7 @@ "CRITICAL", ) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class Settings(abc.ABC): @@ -87,15 +88,15 @@ class Settings(abc.ABC): Settings values can be accessed via key (like a dictionary) or via class attribute. """ - _is_env_variables_setup: ClassVar[bool] - _settings: ClassVar[dict[str, object]] + _is_env_variables_setup: "ClassVar[bool]" + _settings: "ClassVar[dict[str, object]]" @classmethod def get_invalid_settings_key_message(cls, item: str) -> str: """Return the message to state that the given settings key is invalid.""" return f"{item!r} is not a valid settings key." - def __getattr__(self, item: str) -> Any: # type: ignore[misc] # noqa: ANN401 + def __getattr__(self, item: str) -> "Any": # type: ignore[misc] # noqa: ANN401 """Retrieve settings value by attribute lookup.""" MISSING_ATTRIBUTE_MESSAGE: Final[str] = ( f"{type(self).__name__!r} object has no attribute {item!r}" @@ -118,7 +119,7 @@ def __getattr__(self, item: str) -> Any: # type: ignore[misc] # noqa: ANN401 raise AttributeError(MISSING_ATTRIBUTE_MESSAGE) - def __getitem__(self, item: str) -> Any: # type: ignore[misc] # noqa: ANN401 + def __getitem__(self, item: str) -> "Any": # type: ignore[misc] # noqa: ANN401 """Retrieve settings value by key lookup.""" attribute_not_exist_error: AttributeError try: @@ -735,13 +736,13 @@ class RuntimeSettings(Settings): Settings values can be accessed via key (like a dictionary) or via class attribute. """ - _is_env_variables_setup: ClassVar[bool] = False - _settings: ClassVar[dict[str, object]] = {} + _is_env_variables_setup: "ClassVar[bool]" = False + _settings: "ClassVar[dict[str, object]]" = {} # noqa: RUF012 return RuntimeSettings -settings: Final[Settings] = _settings_class_factory()() +settings: "Final[Settings]" = _settings_class_factory()() def run_setup() -> None: diff --git a/db/__init__.py b/db/__init__.py index 581581d4..02debfb1 100644 --- a/db/__init__.py +++ b/db/__init__.py @@ -1,14 +1,15 @@ """Contains the entire package required to run Django's ORM as a database connector.""" -from collections.abc import Sequence - -__all__: Sequence[str] = () - - import os +from typing import TYPE_CHECKING import django +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = () + os.environ["DJANGO_SETTINGS_MODULE"] = "db._settings" diff --git a/db/_settings.py b/db/_settings.py index 3eadc4df..87f42dd8 100644 --- a/db/_settings.py +++ b/db/_settings.py @@ -4,20 +4,21 @@ Partially generated by 'django-admin startproject' using Django 4.2.1. """ -from collections.abc import Sequence - -__all__: Sequence[str] = () - - import inspect from pathlib import Path -from typing import Final +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Sequence + from typing import Final + +__all__: "Sequence[str]" = () # Build paths inside the project like this: BASE_DIR / "subdir". BASE_DIR = Path(__file__).resolve().parent # NOTE: settings.py is called when setting up the mypy_django_plugin & when running Pytest. When mypy/Pytest runs no config settings variables are set, so they should not be accessed -IMPORTED_BY_MYPY_OR_PYTEST: Final[bool] = any( +IMPORTED_BY_MYPY_OR_PYTEST: "Final[bool]" = any( "mypy_django_plugin" in frame.filename or "pytest" in frame.filename for frame in inspect.stack()[1:] diff --git a/db/core/__init__.py b/db/core/__init__.py index 85a428db..91c8b098 100644 --- a/db/core/__init__.py +++ b/db/core/__init__.py @@ -1,5 +1,9 @@ """The core (and only) Django app storing all models.""" -from collections.abc import Sequence -__all__: Sequence[str] = () +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = () diff --git a/db/core/app_config.py b/db/core/app_config.py index 0b3a7df5..315c9a0c 100644 --- a/db/core/app_config.py +++ b/db/core/app_config.py @@ -1,12 +1,15 @@ """Configurations to make the core app ready to import into _settings.py.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("CoreConfig",) +from typing import TYPE_CHECKING from django.apps import AppConfig +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = ("CoreConfig",) + class CoreConfig(AppConfig): """ diff --git a/db/core/models/__init__.py b/db/core/models/__init__.py index 0e3e86e8..0f02eded 100644 --- a/db/core/models/__init__.py +++ b/db/core/models/__init__.py @@ -1,22 +1,8 @@ """Model classes that store extra information between individual event handling call-backs.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "IntroductionReminderOptOutMember", - "SentOneOffIntroductionReminderMember", - "SentGetRolesReminderMember", - "GroupMadeMember", - "DiscordReminder", - "LeftDiscordMember", - "DiscordMemberStrikes", - "DiscordMember", -) - - import hashlib import re -from typing import Final, override +from typing import TYPE_CHECKING, Final, override import discord from django.core.exceptions import ValidationError @@ -26,6 +12,20 @@ from .utils import AsyncBaseModel, BaseDiscordMemberWrapper, DiscordMember +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = ( + "DiscordMember", + "DiscordMemberStrikes", + "DiscordReminder", + "GroupMadeMember", + "IntroductionReminderOptOutMember", + "LeftDiscordMember", + "SentGetRolesReminderMember", + "SentOneOffIntroductionReminderMember", +) + class AssignedCommitteeAction(BaseDiscordMemberWrapper): """Model to represent an action that has been assigned to a Discord committee-member.""" diff --git a/db/core/models/managers.py b/db/core/models/managers.py index 091d3e86..ef6f5cc7 100644 --- a/db/core/models/managers.py +++ b/db/core/models/managers.py @@ -1,35 +1,34 @@ """Manager classes used for DB access upon models.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("HashedDiscordMemberManager", "RelatedDiscordMemberManager") - - import abc import logging -from collections.abc import Callable, MutableMapping -from logging import Logger -from typing import TYPE_CHECKING, Final, TypeAlias, TypeVar, final, override +from typing import TYPE_CHECKING, TypeVar, final, override -from django.db.models import Manager, QuerySet +from django.db.models import Manager import utils if TYPE_CHECKING: + from collections.abc import Callable, MutableMapping, Sequence + from logging import Logger + from typing import Final + from django.core.exceptions import ObjectDoesNotExist + from django.db.models import QuerySet from .utils import AsyncBaseModel, BaseDiscordMemberWrapper, DiscordMember # noqa: F401 +__all__: "Sequence[str]" = ("HashedDiscordMemberManager", "RelatedDiscordMemberManager") + +if TYPE_CHECKING: T_model = TypeVar("T_model", bound=AsyncBaseModel) T_BaseDiscordMemberWrapper = TypeVar("T_BaseDiscordMemberWrapper", bound=BaseDiscordMemberWrapper) # noqa: E501 -Defaults: TypeAlias = ( - MutableMapping[str, object | Callable[[], object]] - | None -) + type Defaults = MutableMapping[str, object | Callable[[], object]] | None + -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class BaseHashedIDManager(Manager["T_model"], abc.ABC): @@ -70,27 +69,27 @@ async def aget(self, *args: object, **kwargs: object) -> "T_model": ) @override - def filter(self, *args: object, **kwargs: object) -> QuerySet["T_model"]: + def filter(self, *args: object, **kwargs: object) -> "QuerySet[T_model]": return super().filter( *args, **self._perform_remove_unhashed_id_from_kwargs(kwargs), ) - async def afilter(self, *args: object, **kwargs: object) -> QuerySet["T_model"]: + async def afilter(self, *args: object, **kwargs: object) -> "QuerySet[T_model]": return super().filter( *args, **(await self._aremove_unhashed_id_from_kwargs(kwargs)), ) @override - def exclude(self, *args: object, **kwargs: object) -> QuerySet["T_model"]: + def exclude(self, *args: object, **kwargs: object) -> "QuerySet[T_model]": return super().exclude( *args, **self._perform_remove_unhashed_id_from_kwargs(kwargs), ) # noinspection SpellCheckingInspection - async def aexclude(self, *args: object, **kwargs: object) -> QuerySet["T_model"]: + async def aexclude(self, *args: object, **kwargs: object) -> "QuerySet[T_model]": return super().exclude( *args, **(await self._aremove_unhashed_id_from_kwargs(kwargs)), @@ -106,21 +105,21 @@ async def acreate(self, **kwargs: object) -> "T_model": return await super().acreate(**(await self._aremove_unhashed_id_from_kwargs(kwargs))) @override - def get_or_create(self, defaults: Defaults = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + def get_or_create(self, defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 return super().get_or_create( defaults=defaults, **self._perform_remove_unhashed_id_from_kwargs(kwargs), ) @override - async def aget_or_create(self, defaults: Defaults = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + async def aget_or_create(self, defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 return await super().aget_or_create( defaults=defaults, **(await self._aremove_unhashed_id_from_kwargs(kwargs)), ) @override - def update_or_create(self, defaults: Defaults = None, create_defaults: Defaults = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + def update_or_create(self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 return super().get_or_create( defaults=defaults, create_defaults=create_defaults, @@ -129,7 +128,7 @@ def update_or_create(self, defaults: Defaults = None, create_defaults: Defaults # noinspection SpellCheckingInspection @override - async def aupdate_or_create(self, defaults: Defaults = None, create_defaults: Defaults = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + async def aupdate_or_create(self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 return await super().aupdate_or_create( defaults=defaults, create_defaults=create_defaults, diff --git a/db/core/models/utils.py b/db/core/models/utils.py index 1645756d..25868745 100644 --- a/db/core/models/utils.py +++ b/db/core/models/utils.py @@ -1,14 +1,8 @@ """Utility classes & functions.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("AsyncBaseModel", "DiscordMember", "BaseDiscordMemberWrapper") - - import hashlib import re -from collections.abc import Iterable -from typing import Final, Never, NoReturn, override +from typing import TYPE_CHECKING, override from asgiref.sync import sync_to_async from django.core.exceptions import FieldDoesNotExist @@ -17,6 +11,12 @@ from .managers import HashedDiscordMemberManager, RelatedDiscordMemberManager +if TYPE_CHECKING: + from collections.abc import Iterable, Sequence + from typing import Final, Never, NoReturn + +__all__: "Sequence[str]" = ("AsyncBaseModel", "BaseDiscordMemberWrapper", "DiscordMember") + class AsyncBaseModel(models.Model): """ @@ -32,7 +32,7 @@ class Meta: # noqa: D106 abstract = True @override - def save(self, *, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: Iterable[str] | None = None) -> None: # type: ignore[override] # noqa: E501 + def save(self, *, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: "Iterable[str] | None" = None) -> None: # type: ignore[override] # noqa: E501 self.full_clean() return super().save(force_insert, force_update, using, update_fields) @@ -52,7 +52,7 @@ def __init__(self, *args: object, **kwargs: object) -> None: for field_name, value in proxy_fields.items(): setattr(self, field_name, value) - def update(self, *, commit: bool = True, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: Iterable[str] | None = None, **kwargs: object) -> None: # noqa: E501 + def update(self, *, commit: bool = True, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: "Iterable[str] | None" = None, **kwargs: object) -> None: # noqa: E501 """ Change an in-memory object's values, then save it to the database. @@ -98,7 +98,7 @@ def update(self, *, commit: bool = True, force_insert: bool = False, force_updat update.alters_data: bool = True # type: ignore[attr-defined, misc] # noinspection SpellCheckingInspection - async def aupdate(self, *, commit: bool = True, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: Iterable[str] | None = None, **kwargs: object) -> None: # noqa: E501 + async def aupdate(self, *, commit: bool = True, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: "Iterable[str] | None" = None, **kwargs: object) -> None: # noqa: E501 """ Asyncronously change an in-memory object's values, then save it to the database. @@ -182,7 +182,7 @@ def __setattr__(self, name: str, value: object) -> None: super().__setattr__(name, value) @property - def discord_id(self) -> NoReturn: + def discord_id(self) -> "NoReturn": """Return the Discord ID of this member.""" HASHED_ID_CANNOT_BE_REVERSED_ERROR_MESSAGE: Final[str] = ( "The Discord IDs of members are hashed before being sent into the database. " @@ -191,22 +191,22 @@ def discord_id(self) -> NoReturn: raise ValueError(HASHED_ID_CANNOT_BE_REVERSED_ERROR_MESSAGE) @property - def member_id(self) -> NoReturn: + def member_id(self) -> "NoReturn": """Return the Discord ID of this member.""" return self.discord_id # type: ignore[misc] @property - def hashed_member_id(self) -> NoReturn: + def hashed_member_id(self) -> "NoReturn": """Return the hashed Discord ID of this member.""" raise DeprecationWarning @hashed_member_id.setter - def hashed_member_id(self, value: Never) -> None: # noqa: ARG002 + def hashed_member_id(self, value: "Never") -> None: # noqa: ARG002 """Assign the hashed Discord ID of this member.""" raise DeprecationWarning @classmethod - def hash_member_id(cls, member_id: Never) -> NoReturn: # noqa: ARG003 + def hash_member_id(cls, member_id: "Never") -> "NoReturn": # noqa: ARG003 """ Hash the provided discord_id. diff --git a/exceptions/__init__.py b/exceptions/__init__.py index a072074c..50c47335 100644 --- a/exceptions/__init__.py +++ b/exceptions/__init__.py @@ -1,33 +1,6 @@ """Exception classes & functions provided for use across the whole of the project.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "ApplicantRoleDoesNotExistError", - "ArchivistRoleDoesNotExistError", - "ChannelDoesNotExistError", - "CommitteeElectRoleDoesNotExistError", - "CommitteeRoleDoesNotExistError", - "DiscordMemberNotInMainGuildError", - "EveryoneRoleCouldNotBeRetrievedError", - "GeneralChannelDoesNotExistError", - "GuestRoleDoesNotExistError", - "GuildDoesNotExistError", - "ImproperlyConfiguredError", - "InvalidActionDescriptionError", - "InvalidActionTargetError", - "InvalidMessagesJSONFileError", - "MemberRoleDoesNotExistError", - "MessagesJSONFileMissingKeyError", - "MessagesJSONFileValueError", - "NoAuditLogsStrikeTrackingError", - "RestartRequiredDueToConfigChange", - "RoleDoesNotExistError", - "RolesChannelDoesNotExistError", - "RulesChannelDoesNotExistError", - "StrikeTrackingError", -) - +from typing import TYPE_CHECKING from .committee_actions import InvalidActionDescriptionError, InvalidActionTargetError from .config_changes import ( @@ -58,3 +31,32 @@ MessagesJSONFileValueError, ) from .strike import NoAuditLogsStrikeTrackingError, StrikeTrackingError + +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = ( + "ApplicantRoleDoesNotExistError", + "ArchivistRoleDoesNotExistError", + "ChannelDoesNotExistError", + "CommitteeElectRoleDoesNotExistError", + "CommitteeRoleDoesNotExistError", + "DiscordMemberNotInMainGuildError", + "EveryoneRoleCouldNotBeRetrievedError", + "GeneralChannelDoesNotExistError", + "GuestRoleDoesNotExistError", + "GuildDoesNotExistError", + "ImproperlyConfiguredError", + "InvalidActionDescriptionError", + "InvalidActionTargetError", + "InvalidMessagesJSONFileError", + "MemberRoleDoesNotExistError", + "MessagesJSONFileMissingKeyError", + "MessagesJSONFileValueError", + "NoAuditLogsStrikeTrackingError", + "RestartRequiredDueToConfigChange", + "RoleDoesNotExistError", + "RolesChannelDoesNotExistError", + "RulesChannelDoesNotExistError", + "StrikeTrackingError", +) diff --git a/exceptions/base.py b/exceptions/base.py index d10ed0f9..422507e3 100644 --- a/exceptions/base.py +++ b/exceptions/base.py @@ -1,18 +1,19 @@ """Base exception classes inherited by other custom exceptions used within this project.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "BaseTeXBotError", - "BaseErrorWithErrorCode", - "BaseDoesNotExistError", -) +import abc +from typing import TYPE_CHECKING, override +from typed_classproperties import classproperty -import abc -from typing import Final, override +if TYPE_CHECKING: + from collections.abc import Sequence + from typing import Final -from classproperties import classproperty +__all__: "Sequence[str]" = ( + "BaseDoesNotExistError", + "BaseErrorWithErrorCode", + "BaseTeXBotError", +) class BaseTeXBotError(BaseException, abc.ABC): @@ -21,8 +22,8 @@ class BaseTeXBotError(BaseException, abc.ABC): # noinspection PyMethodParameters,PyPep8Naming @classproperty @abc.abstractmethod - def DEFAULT_MESSAGE(cls) -> str: # noqa: N802, N805 - """The message to be displayed alongside this exception class if none is provided.""" # noqa: D401 + def DEFAULT_MESSAGE(cls) -> str: # noqa: N802 + """The message to be displayed alongside this exception class if none is provided.""" @override def __init__(self, message: str | None = None) -> None: @@ -52,14 +53,14 @@ def __repr__(self) -> str: return formatted -class BaseErrorWithErrorCode(BaseTeXBotError, abc.ABC): +class BaseErrorWithErrorCode(BaseTeXBotError, abc.ABC): # noqa: N818 """Base class for exception errors that have an error code.""" # noinspection PyMethodParameters,PyPep8Naming @classproperty @abc.abstractmethod - def ERROR_CODE(cls) -> str: # noqa: N802, N805 - """The unique error code for users to tell admins about an error that occurred.""" # noqa: D401 + def ERROR_CODE(cls) -> str: # noqa: N802 + """The unique error code for users to tell admins about an error that occurred.""" class BaseDoesNotExistError(BaseErrorWithErrorCode, ValueError, abc.ABC): @@ -67,45 +68,45 @@ class BaseDoesNotExistError(BaseErrorWithErrorCode, ValueError, abc.ABC): # noinspection PyMethodParameters,PyPep8Naming @classproperty - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N802, N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N802 """ The set of names of commands that require this Discord entity. This set being empty could mean that all commands require this Discord entity, or no commands require this Discord entity. - """ # noqa: D401 + """ return frozenset() # noinspection PyMethodParameters,PyPep8Naming @classproperty - def DEPENDENT_TASKS(cls) -> frozenset[str]: # noqa: N802, N805 + def DEPENDENT_TASKS(cls) -> frozenset[str]: # noqa: N802 """ The set of names of tasks that require this Discord entity. This set being empty could mean that all tasks require this Discord entity, or no tasks require this Discord entity. - """ # noqa: D401 + """ return frozenset() # noinspection PyMethodParameters,PyPep8Naming @classproperty - def DEPENDENT_EVENTS(cls) -> frozenset[str]: # noqa: N802, N805 + def DEPENDENT_EVENTS(cls) -> frozenset[str]: # noqa: N802 """ The set of names of event listeners that require this Discord entity. This set being empty could mean that all event listeners require this Discord entity, or no event listeners require this Discord entity. - """ # noqa: D401 + """ return frozenset() # noinspection PyMethodParameters,PyPep8Naming @classproperty @abc.abstractmethod - def DOES_NOT_EXIST_TYPE(cls) -> str: # noqa: N802, N805 - """The name of the Discord entity that this `DoesNotExistError` is associated with.""" # noqa: D401 + def DOES_NOT_EXIST_TYPE(cls) -> str: # noqa: N802 + """The name of the Discord entity that this `DoesNotExistError` is associated with.""" @classmethod - def get_formatted_message(cls, non_existent_object_identifier: str) -> str: # noqa: C901, PLR0912, PLR0915 + def get_formatted_message(cls, non_existent_object_identifier: str) -> str: # noqa: PLR0912, PLR0915 """ Format the exception message with the dependants that require the non-existent object. diff --git a/exceptions/committee_actions.py b/exceptions/committee_actions.py index d6db4644..c8512509 100644 --- a/exceptions/committee_actions.py +++ b/exceptions/committee_actions.py @@ -1,15 +1,15 @@ """Custom exception classes for committee-action tracking.""" -from collections.abc import Sequence +from typing import TYPE_CHECKING, override -__all__: Sequence[str] = () +from typed_classproperties import classproperty +from .base import BaseTeXBotError -from typing import override - -from classproperties import classproperty +if TYPE_CHECKING: + from collections.abc import Sequence -from .base import BaseTeXBotError +__all__: "Sequence[str]" = () class InvalidActionTargetError(BaseTeXBotError, RuntimeError): @@ -21,7 +21,7 @@ class InvalidActionTargetError(BaseTeXBotError, RuntimeError): @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "The target of the action is invalid." @@ -34,5 +34,5 @@ class InvalidActionDescriptionError(BaseTeXBotError, RuntimeError): @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "The description of the action is invalid." diff --git a/exceptions/config_changes.py b/exceptions/config_changes.py index d1d5914b..7b083e1a 100644 --- a/exceptions/config_changes.py +++ b/exceptions/config_changes.py @@ -1,19 +1,19 @@ """Custom exception classes related to configuration changes.""" -from collections.abc import Sequence +from typing import TYPE_CHECKING, override -__all__: Sequence[str] = ( - "ImproperlyConfiguredError", - "RestartRequiredDueToConfigChange", -) +from typed_classproperties import classproperty +from .base import BaseTeXBotError -from collections.abc import Set -from typing import override - -from classproperties import classproperty +if TYPE_CHECKING: + from collections.abc import Sequence + from collections.abc import Set as AbstractSet -from .base import BaseTeXBotError +__all__: "Sequence[str]" = ( + "ImproperlyConfiguredError", + "RestartRequiredDueToConfigChange", +) class ImproperlyConfiguredError(BaseTeXBotError, Exception): @@ -22,23 +22,23 @@ class ImproperlyConfiguredError(BaseTeXBotError, Exception): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "One or more provided environment variable values are invalid." -class RestartRequiredDueToConfigChange(BaseTeXBotError, Exception): +class RestartRequiredDueToConfigChange(BaseTeXBotError, Exception): # noqa: N818 """Exception class to raise when a restart is required to apply config changes.""" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "TeX-Bot requires a restart to apply configuration changes." @override - def __init__(self, message: str | None = None, changed_settings: Set[str] | None = None) -> None: # noqa: E501 + def __init__(self, message: str | None = None, changed_settings: "AbstractSet[str] | None" = None) -> None: # noqa: E501 """Initialise an Exception to apply configuration changes.""" - self.changed_settings: Set[str] | None = ( + self.changed_settings: AbstractSet[str] | None = ( changed_settings if changed_settings else set() ) diff --git a/exceptions/does_not_exist.py b/exceptions/does_not_exist.py index aa7984d4..af0aa9fb 100644 --- a/exceptions/does_not_exist.py +++ b/exceptions/does_not_exist.py @@ -1,8 +1,17 @@ """Custom exception classes to be raised when retrieved Discord objects do not exist.""" -from collections.abc import Sequence +import abc +from typing import TYPE_CHECKING, override + +from typed_classproperties import classproperty + +from .base import BaseDoesNotExistError, BaseTeXBotError + +if TYPE_CHECKING: + from collections.abc import Sequence + from typing import Final -__all__: Sequence[str] = ( +__all__: "Sequence[str]" = ( "ApplicantRoleDoesNotExistError", "ArchivistRoleDoesNotExistError", "ChannelDoesNotExistError", @@ -18,21 +27,13 @@ ) -import abc -from typing import Final, override - -from classproperties import classproperty - -from .base import BaseDoesNotExistError, BaseTeXBotError - - class RulesChannelDoesNotExistError(BaseTeXBotError, ValueError): """Exception class to raise when the channel, marked as the rules channel, is missing.""" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "There is no channel marked as the rules channel." @@ -42,19 +43,19 @@ class GuildDoesNotExistError(BaseDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "Server with given ID does not exist or is not accessible to the bot." # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1011" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DOES_NOT_EXIST_TYPE(cls) -> str: # noqa: N805 + def DOES_NOT_EXIST_TYPE(cls) -> str: return "guild" @override @@ -74,26 +75,26 @@ class RoleDoesNotExistError(BaseDoesNotExistError, abc.ABC): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return f"Role with name \"{cls.ROLE_NAME}\" does not exist." # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DOES_NOT_EXIST_TYPE(cls) -> str: # noqa: N805 + def DOES_NOT_EXIST_TYPE(cls) -> str: return "role" # noinspection PyMethodParameters,PyPep8Naming @classproperty @abc.abstractmethod - def ROLE_NAME(cls) -> str: # noqa: N802, N805 - """The name of the Discord role that does not exist.""" # noqa: D401 + def ROLE_NAME(cls) -> str: # noqa: N802 + """The name of the Discord role that does not exist.""" @override def __init__(self, message: str | None = None) -> None: """Initialise a new DoesNotExist exception for a role not existing.""" HAS_DEPENDANTS: Final[bool] = bool( - self.DEPENDENT_COMMANDS or self.DEPENDENT_TASKS or self.DEPENDENT_EVENTS # noqa: COM812 + self.DEPENDENT_COMMANDS or self.DEPENDENT_TASKS or self.DEPENDENT_EVENTS ) if not message and HAS_DEPENDANTS: @@ -108,13 +109,13 @@ class CommitteeRoleDoesNotExistError(RoleDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1021" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noinspection SpellCheckingInspection return frozenset( { @@ -133,7 +134,7 @@ def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ROLE_NAME(cls) -> str: # noqa: N805 + def ROLE_NAME(cls) -> str: return "Committee" @@ -143,19 +144,19 @@ class CommitteeElectRoleDoesNotExistError(RoleDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1026" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: return frozenset({"handover"}) # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ROLE_NAME(cls) -> str: # noqa: N805 + def ROLE_NAME(cls) -> str: return "Committee-Elect" @@ -165,13 +166,13 @@ class GuestRoleDoesNotExistError(RoleDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1022" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noinspection SpellCheckingInspection return frozenset( { @@ -186,14 +187,14 @@ def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_TASKS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_TASKS(cls) -> frozenset[str]: # noinspection SpellCheckingInspection return frozenset({"send_get_roles_reminders"}) # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ROLE_NAME(cls) -> str: # noqa: N805 + def ROLE_NAME(cls) -> str: return "Guest" @@ -203,20 +204,20 @@ class MemberRoleDoesNotExistError(RoleDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1023" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noinspection SpellCheckingInspection return frozenset({"makemember", "ensure-members-inducted", "annual-roles-reset"}) # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ROLE_NAME(cls) -> str: # noqa: N805 + def ROLE_NAME(cls) -> str: return "Member" @@ -226,20 +227,20 @@ class ArchivistRoleDoesNotExistError(RoleDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1024" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noinspection SpellCheckingInspection return frozenset({"archive", "increment-year-channels"}) # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ROLE_NAME(cls) -> str: # noqa: N805 + def ROLE_NAME(cls) -> str: return "Archivist" @@ -249,19 +250,19 @@ class ApplicantRoleDoesNotExistError(RoleDoesNotExistError): # noinspection PyMethodParameters @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1025" # noinspection PyMethodParameters @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: return frozenset({"make_applicant"}) # noinspection PyMethodParameters @classproperty @override - def ROLE_NAME(cls) -> str: # noqa: N805 + def ROLE_NAME(cls) -> str: return "Applicant" @@ -271,26 +272,26 @@ class ChannelDoesNotExistError(BaseDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return f"Channel with name \"{cls.CHANNEL_NAME}\" does not exist." # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DOES_NOT_EXIST_TYPE(cls) -> str: # noqa: N805 + def DOES_NOT_EXIST_TYPE(cls) -> str: return "channel" # noinspection PyMethodParameters,PyPep8Naming @classproperty @abc.abstractmethod - def CHANNEL_NAME(cls) -> str: # noqa: N802, N805 - """The name of the Discord channel that does not exist.""" # noqa: D401 + def CHANNEL_NAME(cls) -> str: # noqa: N802 + """The name of the Discord channel that does not exist.""" @override def __init__(self, message: str | None = None) -> None: """Initialise a new DoesNotExist exception for a role not existing.""" HAS_DEPENDANTS: Final[bool] = bool( - self.DEPENDENT_COMMANDS or self.DEPENDENT_TASKS or self.DEPENDENT_EVENTS # noqa: COM812 + self.DEPENDENT_COMMANDS or self.DEPENDENT_TASKS or self.DEPENDENT_EVENTS ) if not message and HAS_DEPENDANTS: @@ -307,20 +308,20 @@ class RolesChannelDoesNotExistError(ChannelDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1031" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noinspection SpellCheckingInspection return frozenset({"writeroles"}) # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def CHANNEL_NAME(cls) -> str: # noqa: N805 + def CHANNEL_NAME(cls) -> str: return "roles" @@ -330,18 +331,18 @@ class GeneralChannelDoesNotExistError(ChannelDoesNotExistError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1032" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noqa: N805 + def DEPENDENT_COMMANDS(cls) -> frozenset[str]: # noinspection SpellCheckingInspection return frozenset({"induct"}) # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def CHANNEL_NAME(cls) -> str: # noqa: N805 + def CHANNEL_NAME(cls) -> str: return "general" diff --git a/exceptions/guild.py b/exceptions/guild.py index 77032202..0a53085c 100644 --- a/exceptions/guild.py +++ b/exceptions/guild.py @@ -1,18 +1,18 @@ """Custom exception classes raised when errors occur with properties of a guild object.""" -from collections.abc import Sequence +from typing import TYPE_CHECKING, override -__all__: Sequence[str] = ( - "DiscordMemberNotInMainGuildError", - "EveryoneRoleCouldNotBeRetrievedError", -) +from typed_classproperties import classproperty +from .base import BaseErrorWithErrorCode, BaseTeXBotError -from typing import override - -from classproperties import classproperty +if TYPE_CHECKING: + from collections.abc import Sequence -from .base import BaseErrorWithErrorCode, BaseTeXBotError +__all__: "Sequence[str]" = ( + "DiscordMemberNotInMainGuildError", + "EveryoneRoleCouldNotBeRetrievedError", +) class DiscordMemberNotInMainGuildError(BaseTeXBotError, ValueError): @@ -21,7 +21,7 @@ class DiscordMemberNotInMainGuildError(BaseTeXBotError, ValueError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "Given user ID does not represent any member of your group's Discord guild." @override @@ -38,11 +38,11 @@ class EveryoneRoleCouldNotBeRetrievedError(BaseErrorWithErrorCode, ValueError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "The reference to the \"@everyone\" role could not be correctly retrieved." # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def ERROR_CODE(cls) -> str: # noqa: N805 + def ERROR_CODE(cls) -> str: return "E1042" diff --git a/exceptions/messages.py b/exceptions/messages.py index 049f55d9..4dbecd12 100644 --- a/exceptions/messages.py +++ b/exceptions/messages.py @@ -1,28 +1,28 @@ """Custom exception classes raised when errors occur with retrieving messages from the file.""" -from collections.abc import Sequence +from typing import TYPE_CHECKING, override -__all__: Sequence[str] = ( +from typed_classproperties import classproperty + +from .config_changes import ImproperlyConfiguredError + +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = ( "InvalidMessagesJSONFileError", "MessagesJSONFileMissingKeyError", "MessagesJSONFileValueError", ) -from typing import override - -from classproperties import classproperty - -from .config_changes import ImproperlyConfiguredError - - class InvalidMessagesJSONFileError(ImproperlyConfiguredError): """Exception class to raise when the messages.json file has an invalid structure.""" # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "The messages JSON file has an invalid structure at the given key." @override @@ -39,7 +39,7 @@ class MessagesJSONFileMissingKeyError(InvalidMessagesJSONFileError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "The messages JSON file is missing a required key." @override @@ -63,7 +63,7 @@ class MessagesJSONFileValueError(InvalidMessagesJSONFileError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "The messages JSON file has an invalid value." @override diff --git a/exceptions/strike.py b/exceptions/strike.py index 7209546d..8cfe9e59 100644 --- a/exceptions/strike.py +++ b/exceptions/strike.py @@ -1,18 +1,18 @@ """Custom exception classes raised when errors occur during use of the "/strike" command.""" -from collections.abc import Sequence +from typing import TYPE_CHECKING, override -__all__: Sequence[str] = ( - "NoAuditLogsStrikeTrackingError", - "StrikeTrackingError", -) +from typed_classproperties import classproperty +from .base import BaseTeXBotError -from typing import override - -from classproperties import classproperty +if TYPE_CHECKING: + from collections.abc import Sequence -from .base import BaseTeXBotError +__all__: "Sequence[str]" = ( + "NoAuditLogsStrikeTrackingError", + "StrikeTrackingError", +) class StrikeTrackingError(BaseTeXBotError, RuntimeError): @@ -26,7 +26,7 @@ class StrikeTrackingError(BaseTeXBotError, RuntimeError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "An error occurred while trying to track manually applied moderation actions." @@ -41,5 +41,5 @@ class NoAuditLogsStrikeTrackingError(BaseTeXBotError, RuntimeError): # noinspection PyMethodParameters,PyPep8Naming @classproperty @override - def DEFAULT_MESSAGE(cls) -> str: # noqa: N805 + def DEFAULT_MESSAGE(cls) -> str: return "Unable to retrieve audit log entry after possible manual moderation action." diff --git a/main.py b/main.py old mode 100644 new mode 100755 index c321572d..4cd36407 --- a/main.py +++ b/main.py @@ -7,12 +7,7 @@ the asynchronous running process for TeX-Bot. """ -from collections.abc import Sequence - -__all__: Sequence[str] = ("bot",) - - -from typing import NoReturn +from typing import TYPE_CHECKING import discord @@ -20,6 +15,12 @@ from config import settings from utils import SuppressTraceback, TeXBot +if TYPE_CHECKING: + from collections.abc import Sequence + from typing import NoReturn + +__all__: "Sequence[str]" = ("bot",) + with SuppressTraceback(): config.run_setup() @@ -32,7 +33,7 @@ bot.load_extension("cogs") -def _run_bot() -> NoReturn: # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 +def _run_bot() -> "NoReturn": # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 bot.run(settings["DISCORD_BOT_TOKEN"]) if bot.EXIT_WAS_DUE_TO_KILL_COMMAND: diff --git a/poetry.lock b/poetry.lock index ba3abf3b..e6cfaeda 100644 --- a/poetry.lock +++ b/poetry.lock @@ -13,102 +13,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.11" +version = "3.11.7" description = "Async http client/server framework (asyncio)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5077b1a5f40ffa3ba1f40d537d3bec4383988ee51fbba6b74aa8fb1bc466599e"}, - {file = "aiohttp-3.10.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8d6a14a4d93b5b3c2891fca94fa9d41b2322a68194422bef0dd5ec1e57d7d298"}, - {file = "aiohttp-3.10.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffbfde2443696345e23a3c597049b1dd43049bb65337837574205e7368472177"}, - {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20b3d9e416774d41813bc02fdc0663379c01817b0874b932b81c7f777f67b217"}, - {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b943011b45ee6bf74b22245c6faab736363678e910504dd7531a58c76c9015a"}, - {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48bc1d924490f0d0b3658fe5c4b081a4d56ebb58af80a6729d4bd13ea569797a"}, - {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e12eb3f4b1f72aaaf6acd27d045753b18101524f72ae071ae1c91c1cd44ef115"}, - {file = "aiohttp-3.10.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f14ebc419a568c2eff3c1ed35f634435c24ead2fe19c07426af41e7adb68713a"}, - {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72b191cdf35a518bfc7ca87d770d30941decc5aaf897ec8b484eb5cc8c7706f3"}, - {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5ab2328a61fdc86424ee540d0aeb8b73bbcad7351fb7cf7a6546fc0bcffa0038"}, - {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:aa93063d4af05c49276cf14e419550a3f45258b6b9d1f16403e777f1addf4519"}, - {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:30283f9d0ce420363c24c5c2421e71a738a2155f10adbb1a11a4d4d6d2715cfc"}, - {file = "aiohttp-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e5358addc8044ee49143c546d2182c15b4ac3a60be01c3209374ace05af5733d"}, - {file = "aiohttp-3.10.11-cp310-cp310-win32.whl", hash = "sha256:e1ffa713d3ea7cdcd4aea9cddccab41edf6882fa9552940344c44e59652e1120"}, - {file = "aiohttp-3.10.11-cp310-cp310-win_amd64.whl", hash = "sha256:778cbd01f18ff78b5dd23c77eb82987ee4ba23408cbed233009fd570dda7e674"}, - {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:80ff08556c7f59a7972b1e8919f62e9c069c33566a6d28586771711e0eea4f07"}, - {file = "aiohttp-3.10.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c8f96e9ee19f04c4914e4e7a42a60861066d3e1abf05c726f38d9d0a466e695"}, - {file = "aiohttp-3.10.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fb8601394d537da9221947b5d6e62b064c9a43e88a1ecd7414d21a1a6fba9c24"}, - {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ea224cf7bc2d8856d6971cea73b1d50c9c51d36971faf1abc169a0d5f85a382"}, - {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db9503f79e12d5d80b3efd4d01312853565c05367493379df76d2674af881caa"}, - {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0f449a50cc33f0384f633894d8d3cd020e3ccef81879c6e6245c3c375c448625"}, - {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82052be3e6d9e0c123499127782a01a2b224b8af8c62ab46b3f6197035ad94e9"}, - {file = "aiohttp-3.10.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20063c7acf1eec550c8eb098deb5ed9e1bb0521613b03bb93644b810986027ac"}, - {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:489cced07a4c11488f47aab1f00d0c572506883f877af100a38f1fedaa884c3a"}, - {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ea9b3bab329aeaa603ed3bf605f1e2a6f36496ad7e0e1aa42025f368ee2dc07b"}, - {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ca117819d8ad113413016cb29774b3f6d99ad23c220069789fc050267b786c16"}, - {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2dfb612dcbe70fb7cdcf3499e8d483079b89749c857a8f6e80263b021745c730"}, - {file = "aiohttp-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9b615d3da0d60e7d53c62e22b4fd1c70f4ae5993a44687b011ea3a2e49051b8"}, - {file = "aiohttp-3.10.11-cp311-cp311-win32.whl", hash = "sha256:29103f9099b6068bbdf44d6a3d090e0a0b2be6d3c9f16a070dd9d0d910ec08f9"}, - {file = "aiohttp-3.10.11-cp311-cp311-win_amd64.whl", hash = "sha256:236b28ceb79532da85d59aa9b9bf873b364e27a0acb2ceaba475dc61cffb6f3f"}, - {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7480519f70e32bfb101d71fb9a1f330fbd291655a4c1c922232a48c458c52710"}, - {file = "aiohttp-3.10.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f65267266c9aeb2287a6622ee2bb39490292552f9fbf851baabc04c9f84e048d"}, - {file = "aiohttp-3.10.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7400a93d629a0608dc1d6c55f1e3d6e07f7375745aaa8bd7f085571e4d1cee97"}, - {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f34b97e4b11b8d4eb2c3a4f975be626cc8af99ff479da7de49ac2c6d02d35725"}, - {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e7b825da878464a252ccff2958838f9caa82f32a8dbc334eb9b34a026e2c636"}, - {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f92a344c50b9667827da308473005f34767b6a2a60d9acff56ae94f895f385"}, - {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f1ab987a27b83c5268a17218463c2ec08dbb754195113867a27b166cd6087"}, - {file = "aiohttp-3.10.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1dc0f4ca54842173d03322793ebcf2c8cc2d34ae91cc762478e295d8e361e03f"}, - {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7ce6a51469bfaacff146e59e7fb61c9c23006495d11cc24c514a455032bcfa03"}, - {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:aad3cd91d484d065ede16f3cf15408254e2469e3f613b241a1db552c5eb7ab7d"}, - {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f4df4b8ca97f658c880fb4b90b1d1ec528315d4030af1ec763247ebfd33d8b9a"}, - {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2e4e18a0a2d03531edbc06c366954e40a3f8d2a88d2b936bbe78a0c75a3aab3e"}, - {file = "aiohttp-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6ce66780fa1a20e45bc753cda2a149daa6dbf1561fc1289fa0c308391c7bc0a4"}, - {file = "aiohttp-3.10.11-cp312-cp312-win32.whl", hash = "sha256:a919c8957695ea4c0e7a3e8d16494e3477b86f33067478f43106921c2fef15bb"}, - {file = "aiohttp-3.10.11-cp312-cp312-win_amd64.whl", hash = "sha256:b5e29706e6389a2283a91611c91bf24f218962717c8f3b4e528ef529d112ee27"}, - {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:703938e22434d7d14ec22f9f310559331f455018389222eed132808cd8f44127"}, - {file = "aiohttp-3.10.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9bc50b63648840854e00084c2b43035a62e033cb9b06d8c22b409d56eb098413"}, - {file = "aiohttp-3.10.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f0463bf8b0754bc744e1feb61590706823795041e63edf30118a6f0bf577461"}, - {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6c6dec398ac5a87cb3a407b068e1106b20ef001c344e34154616183fe684288"}, - {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcaf2d79104d53d4dcf934f7ce76d3d155302d07dae24dff6c9fffd217568067"}, - {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25fd5470922091b5a9aeeb7e75be609e16b4fba81cdeaf12981393fb240dd10e"}, - {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbde2ca67230923a42161b1f408c3992ae6e0be782dca0c44cb3206bf330dee1"}, - {file = "aiohttp-3.10.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:249c8ff8d26a8b41a0f12f9df804e7c685ca35a207e2410adbd3e924217b9006"}, - {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:878ca6a931ee8c486a8f7b432b65431d095c522cbeb34892bee5be97b3481d0f"}, - {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8663f7777ce775f0413324be0d96d9730959b2ca73d9b7e2c2c90539139cbdd6"}, - {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:6cd3f10b01f0c31481fba8d302b61603a2acb37b9d30e1d14e0f5a58b7b18a31"}, - {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e8d8aad9402d3aa02fdc5ca2fe68bcb9fdfe1f77b40b10410a94c7f408b664d"}, - {file = "aiohttp-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:38e3c4f80196b4f6c3a85d134a534a56f52da9cb8d8e7af1b79a32eefee73a00"}, - {file = "aiohttp-3.10.11-cp313-cp313-win32.whl", hash = "sha256:fc31820cfc3b2863c6e95e14fcf815dc7afe52480b4dc03393c4873bb5599f71"}, - {file = "aiohttp-3.10.11-cp313-cp313-win_amd64.whl", hash = "sha256:4996ff1345704ffdd6d75fb06ed175938c133425af616142e7187f28dc75f14e"}, - {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:74baf1a7d948b3d640badeac333af581a367ab916b37e44cf90a0334157cdfd2"}, - {file = "aiohttp-3.10.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:473aebc3b871646e1940c05268d451f2543a1d209f47035b594b9d4e91ce8339"}, - {file = "aiohttp-3.10.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c2f746a6968c54ab2186574e15c3f14f3e7f67aef12b761e043b33b89c5b5f95"}, - {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d110cabad8360ffa0dec8f6ec60e43286e9d251e77db4763a87dcfe55b4adb92"}, - {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0099c7d5d7afff4202a0c670e5b723f7718810000b4abcbc96b064129e64bc7"}, - {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0316e624b754dbbf8c872b62fe6dcb395ef20c70e59890dfa0de9eafccd2849d"}, - {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a5f7ab8baf13314e6b2485965cbacb94afff1e93466ac4d06a47a81c50f9cca"}, - {file = "aiohttp-3.10.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c891011e76041e6508cbfc469dd1a8ea09bc24e87e4c204e05f150c4c455a5fa"}, - {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9208299251370ee815473270c52cd3f7069ee9ed348d941d574d1457d2c73e8b"}, - {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:459f0f32c8356e8125f45eeff0ecf2b1cb6db1551304972702f34cd9e6c44658"}, - {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:14cdc8c1810bbd4b4b9f142eeee23cda528ae4e57ea0923551a9af4820980e39"}, - {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:971aa438a29701d4b34e4943e91b5e984c3ae6ccbf80dd9efaffb01bd0b243a9"}, - {file = "aiohttp-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9a309c5de392dfe0f32ee57fa43ed8fc6ddf9985425e84bd51ed66bb16bce3a7"}, - {file = "aiohttp-3.10.11-cp38-cp38-win32.whl", hash = "sha256:9ec1628180241d906a0840b38f162a3215114b14541f1a8711c368a8739a9be4"}, - {file = "aiohttp-3.10.11-cp38-cp38-win_amd64.whl", hash = "sha256:9c6e0ffd52c929f985c7258f83185d17c76d4275ad22e90aa29f38e211aacbec"}, - {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cdc493a2e5d8dc79b2df5bec9558425bcd39aff59fc949810cbd0832e294b106"}, - {file = "aiohttp-3.10.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b3e70f24e7d0405be2348da9d5a7836936bf3a9b4fd210f8c37e8d48bc32eca6"}, - {file = "aiohttp-3.10.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968b8fb2a5eee2770eda9c7b5581587ef9b96fbdf8dcabc6b446d35ccc69df01"}, - {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deef4362af9493d1382ef86732ee2e4cbc0d7c005947bd54ad1a9a16dd59298e"}, - {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:686b03196976e327412a1b094f4120778c7c4b9cff9bce8d2fdfeca386b89829"}, - {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3bf6d027d9d1d34e1c2e1645f18a6498c98d634f8e373395221121f1c258ace8"}, - {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:099fd126bf960f96d34a760e747a629c27fb3634da5d05c7ef4d35ef4ea519fc"}, - {file = "aiohttp-3.10.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c73c4d3dae0b4644bc21e3de546530531d6cdc88659cdeb6579cd627d3c206aa"}, - {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0c5580f3c51eea91559db3facd45d72e7ec970b04528b4709b1f9c2555bd6d0b"}, - {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:fdf6429f0caabfd8a30c4e2eaecb547b3c340e4730ebfe25139779b9815ba138"}, - {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d97187de3c276263db3564bb9d9fad9e15b51ea10a371ffa5947a5ba93ad6777"}, - {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0acafb350cfb2eba70eb5d271f55e08bd4502ec35e964e18ad3e7d34d71f7261"}, - {file = "aiohttp-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c13ed0c779911c7998a58e7848954bd4d63df3e3575f591e321b19a2aec8df9f"}, - {file = "aiohttp-3.10.11-cp39-cp39-win32.whl", hash = "sha256:22b7c540c55909140f63ab4f54ec2c20d2635c0289cdd8006da46f3327f971b9"}, - {file = "aiohttp-3.10.11-cp39-cp39-win_amd64.whl", hash = "sha256:7b26b1551e481012575dab8e3727b16fe7dd27eb2711d2e63ced7368756268fb"}, - {file = "aiohttp-3.10.11.tar.gz", hash = "sha256:9dc2b8f3dcab2e39e0fa309c8da50c3b55e6f34ab25f1a71d3288f24924d33a7"}, + {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8bedb1f6cb919af3b6353921c71281b1491f948ca64408871465d889b4ee1b66"}, + {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f5022504adab881e2d801a88b748ea63f2a9d130e0b2c430824682a96f6534be"}, + {file = "aiohttp-3.11.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e22d1721c978a6494adc824e0916f9d187fa57baeda34b55140315fa2f740184"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e993676c71288618eb07e20622572b1250d8713e7e00ab3aabae28cb70f3640d"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e13a05db87d3b241c186d0936808d0e4e12decc267c617d54e9c643807e968b6"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ba8d043fed7ffa117024d7ba66fdea011c0e7602327c6d73cacaea38abe4491"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda3ed0a7869d2fa16aa41f9961ade73aa2c2e3b2fcb0a352524e7b744881889"}, + {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43bfd25113c1e98aec6c70e26d5f4331efbf4aa9037ba9ad88f090853bf64d7f"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3dd3e7e7c9ef3e7214f014f1ae260892286647b3cf7c7f1b644a568fd410f8ca"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78c657ece7a73b976905ab9ec8be9ef2df12ed8984c24598a1791c58ce3b4ce4"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:db70a47987e34494b451a334605bee57a126fe8d290511349e86810b4be53b01"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9e67531370a3b07e49b280c1f8c2df67985c790ad2834d1b288a2f13cd341c5f"}, + {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9202f184cc0582b1db15056f2225ab4c1e3dac4d9ade50dd0613ac3c46352ac2"}, + {file = "aiohttp-3.11.7-cp310-cp310-win32.whl", hash = "sha256:2257bdd5cf54a4039a4337162cd8048f05a724380a2283df34620f55d4e29341"}, + {file = "aiohttp-3.11.7-cp310-cp310-win_amd64.whl", hash = "sha256:b7215bf2b53bc6cb35808149980c2ae80a4ae4e273890ac85459c014d5aa60ac"}, + {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cea52d11e02123f125f9055dfe0ccf1c3857225fb879e4a944fae12989e2aef2"}, + {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ce18f703b7298e7f7633efd6a90138d99a3f9a656cb52c1201e76cb5d79cf08"}, + {file = "aiohttp-3.11.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:670847ee6aeb3a569cd7cdfbe0c3bec1d44828bbfbe78c5d305f7f804870ef9e"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dda726f89bfa5c465ba45b76515135a3ece0088dfa2da49b8bb278f3bdeea12"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25b74a811dba37c7ea6a14d99eb9402d89c8d739d50748a75f3cf994cf19c43"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5522ee72f95661e79db691310290c4618b86dff2d9b90baedf343fd7a08bf79"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fbf41a6bbc319a7816ae0f0177c265b62f2a59ad301a0e49b395746eb2a9884"}, + {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59ee1925b5a5efdf6c4e7be51deee93984d0ac14a6897bd521b498b9916f1544"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24054fce8c6d6f33a3e35d1c603ef1b91bbcba73e3f04a22b4f2f27dac59b347"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:351849aca2c6f814575c1a485c01c17a4240413f960df1bf9f5deb0003c61a53"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:12724f3a211fa243570e601f65a8831372caf1a149d2f1859f68479f07efec3d"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7ea4490360b605804bea8173d2d086b6c379d6bb22ac434de605a9cbce006e7d"}, + {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e0bf378db07df0a713a1e32381a1b277e62ad106d0dbe17b5479e76ec706d720"}, + {file = "aiohttp-3.11.7-cp311-cp311-win32.whl", hash = "sha256:cd8d62cab363dfe713067027a5adb4907515861f1e4ce63e7be810b83668b847"}, + {file = "aiohttp-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:bf0e6cce113596377cadda4e3ac5fb89f095bd492226e46d91b4baef1dd16f60"}, + {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4bb7493c3e3a36d3012b8564bd0e2783259ddd7ef3a81a74f0dbfa000fce48b7"}, + {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e143b0ef9cb1a2b4f74f56d4fbe50caa7c2bb93390aff52f9398d21d89bc73ea"}, + {file = "aiohttp-3.11.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7c58a240260822dc07f6ae32a0293dd5bccd618bb2d0f36d51c5dbd526f89c0"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d20cfe63a1c135d26bde8c1d0ea46fd1200884afbc523466d2f1cf517d1fe33"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12e4d45847a174f77b2b9919719203769f220058f642b08504cf8b1cf185dacf"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf4efa2d01f697a7dbd0509891a286a4af0d86902fc594e20e3b1712c28c0106"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee6a4cdcbf54b8083dc9723cdf5f41f722c00db40ccf9ec2616e27869151129"}, + {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6095aaf852c34f42e1bd0cf0dc32d1e4b48a90bfb5054abdbb9d64b36acadcb"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1cf03d27885f8c5ebf3993a220cc84fc66375e1e6e812731f51aab2b2748f4a6"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1a17f6a230f81eb53282503823f59d61dff14fb2a93847bf0399dc8e87817307"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:481f10a1a45c5f4c4a578bbd74cff22eb64460a6549819242a87a80788461fba"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:db37248535d1ae40735d15bdf26ad43be19e3d93ab3f3dad8507eb0f85bb8124"}, + {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd"}, + {file = "aiohttp-3.11.7-cp312-cp312-win32.whl", hash = "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8"}, + {file = "aiohttp-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0"}, + {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:241a6ca732d2766836d62c58c49ca7a93d08251daef0c1e3c850df1d1ca0cbc4"}, + {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aa3705a8d14de39898da0fbad920b2a37b7547c3afd2a18b9b81f0223b7d0f68"}, + {file = "aiohttp-3.11.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9acfc7f652b31853eed3b92095b0acf06fd5597eeea42e939bd23a17137679d5"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcefcf2915a2dbdbce37e2fc1622129a1918abfe3d06721ce9f6cdac9b6d2eaa"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c1f6490dd1862af5aae6cfcf2a274bffa9a5b32a8f5acb519a7ecf5a99a88866"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac5462582d6561c1c1708853a9faf612ff4e5ea5e679e99be36143d6eabd8e"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1a6309005acc4b2bcc577ba3b9169fea52638709ffacbd071f3503264620da"}, + {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b973cce96793725ef63eb449adfb74f99c043c718acb76e0d2a447ae369962"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ce91a24aac80de6be8512fb1c4838a9881aa713f44f4e91dd7bb3b34061b497d"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:875f7100ce0e74af51d4139495eec4025affa1a605280f23990b6434b81df1bd"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c171fc35d3174bbf4787381716564042a4cbc008824d8195eede3d9b938e29a8"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ee9afa1b0d2293c46954f47f33e150798ad68b78925e3710044e0d67a9487791"}, + {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8360c7cc620abb320e1b8d603c39095101391a82b1d0be05fb2225471c9c5c52"}, + {file = "aiohttp-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7a9318da4b4ada9a67c1dd84d1c0834123081e746bee311a16bb449f363d965e"}, + {file = "aiohttp-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:fc6da202068e0a268e298d7cd09b6e9f3997736cd9b060e2750963754552a0a9"}, + {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:17829f37c0d31d89aa6b8b010475a10233774771f9b6dc2cc352ea4f8ce95d9a"}, + {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d6177077a31b1aecfc3c9070bd2f11419dbb4a70f30f4c65b124714f525c2e48"}, + {file = "aiohttp-3.11.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:badda65ac99555791eed75e234afb94686ed2317670c68bff8a4498acdaee935"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0de6466b9d742b4ee56fe1b2440706e225eb48c77c63152b1584864a236e7a50"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04b0cc74d5a882c9dacaeeccc1444f0233212b6f5be8bc90833feef1e1ce14b9"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c7af3e50e5903d21d7b935aceed901cc2475463bc16ddd5587653548661fdb"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c63f898f683d1379b9be5afc3dd139e20b30b0b1e0bf69a3fc3681f364cf1629"}, + {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdadc3f6a32d6eca45f9a900a254757fd7855dfb2d8f8dcf0e88f0fae3ff8eb1"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d329300fb23e14ed1f8c6d688dfd867d1dcc3b1d7cd49b7f8c5b44e797ce0932"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5578cf40440eafcb054cf859964bc120ab52ebe0e0562d2b898126d868749629"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7b2f8107a3c329789f3c00b2daad0e35f548d0a55cda6291579136622099a46e"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:43dd89a6194f6ab02a3fe36b09e42e2df19c211fc2050ce37374d96f39604997"}, + {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d2fa6fc7cc865d26ff42480ac9b52b8c9b7da30a10a6442a9cdf429de840e949"}, + {file = "aiohttp-3.11.7-cp39-cp39-win32.whl", hash = "sha256:a7d9a606355655617fee25dd7e54d3af50804d002f1fd3118dd6312d26692d70"}, + {file = "aiohttp-3.11.7-cp39-cp39-win_amd64.whl", hash = "sha256:53c921b58fdc6485d6b2603e0132bb01cd59b8f0620ffc0907f525e0ba071687"}, + {file = "aiohttp-3.11.7.tar.gz", hash = "sha256:01a8aca4af3da85cea5c90141d23f4b0eee3cbecfd33b029a45a80f28c66c668"}, ] [package.dependencies] @@ -117,7 +102,8 @@ aiosignal = ">=1.1.2" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" -yarl = ">=1.12.0,<2.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" [package.extras] speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] @@ -374,21 +360,6 @@ files = [ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, ] -[[package]] -name = "classproperties" -version = "0.2" -description = "Decorators for classproperty and cached_classproperty" -optional = false -python-versions = ">=3.5" -files = [] -develop = false - -[package.source] -type = "git" -url = "https://github.com/hottwaj/classproperties.git" -reference = "HEAD" -resolved_reference = "cc20df2fbdf7dd7c4cd595b99418a21618bd7812" - [[package]] name = "colorama" version = "0.4.6" @@ -417,76 +388,65 @@ wcwidth = "*" [[package]] name = "contourpy" -version = "1.3.0" +version = "1.3.1" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, - {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589"}, - {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d"}, - {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223"}, - {file = "contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f"}, - {file = "contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, - {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, - {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, - {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, - {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, - {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, - {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, - {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, - {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, - {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, - {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, - {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, - {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, - {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, - {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, - {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, - {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, - {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, - {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8"}, - {file = "contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc"}, - {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e"}, - {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800"}, - {file = "contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5"}, - {file = "contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779"}, - {file = "contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102"}, - {file = "contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb"}, - {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab"}, + {file = "contourpy-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453"}, + {file = "contourpy-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277"}, + {file = "contourpy-1.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595"}, + {file = "contourpy-1.3.1-cp310-cp310-win32.whl", hash = "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697"}, + {file = "contourpy-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b"}, + {file = "contourpy-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85"}, + {file = "contourpy-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291"}, + {file = "contourpy-1.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f"}, + {file = "contourpy-1.3.1-cp311-cp311-win32.whl", hash = "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375"}, + {file = "contourpy-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509"}, + {file = "contourpy-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec"}, + {file = "contourpy-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b"}, + {file = "contourpy-1.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d"}, + {file = "contourpy-1.3.1-cp312-cp312-win32.whl", hash = "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e"}, + {file = "contourpy-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2"}, + {file = "contourpy-1.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7"}, + {file = "contourpy-1.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3"}, + {file = "contourpy-1.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1"}, + {file = "contourpy-1.3.1-cp313-cp313-win32.whl", hash = "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82"}, + {file = "contourpy-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30"}, + {file = "contourpy-1.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f"}, + {file = "contourpy-1.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242"}, + {file = "contourpy-1.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win32.whl", hash = "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1"}, + {file = "contourpy-1.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750"}, + {file = "contourpy-1.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53"}, + {file = "contourpy-1.3.1.tar.gz", hash = "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699"}, ] [package.dependencies] @@ -633,59 +593,61 @@ typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "fonttools" -version = "4.54.1" +version = "4.55.0" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.54.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2"}, - {file = "fonttools-4.54.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882"}, - {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10"}, - {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e"}, - {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e"}, - {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44"}, - {file = "fonttools-4.54.1-cp310-cp310-win32.whl", hash = "sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02"}, - {file = "fonttools-4.54.1-cp310-cp310-win_amd64.whl", hash = "sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d"}, - {file = "fonttools-4.54.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20"}, - {file = "fonttools-4.54.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2"}, - {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7"}, - {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07"}, - {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8"}, - {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a"}, - {file = "fonttools-4.54.1-cp311-cp311-win32.whl", hash = "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc"}, - {file = "fonttools-4.54.1-cp311-cp311-win_amd64.whl", hash = "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6"}, - {file = "fonttools-4.54.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d"}, - {file = "fonttools-4.54.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08"}, - {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263"}, - {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab"}, - {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d"}, - {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714"}, - {file = "fonttools-4.54.1-cp312-cp312-win32.whl", hash = "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"}, - {file = "fonttools-4.54.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e"}, - {file = "fonttools-4.54.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff"}, - {file = "fonttools-4.54.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb"}, - {file = "fonttools-4.54.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a"}, - {file = "fonttools-4.54.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c"}, - {file = "fonttools-4.54.1-cp313-cp313-win32.whl", hash = "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58"}, - {file = "fonttools-4.54.1-cp313-cp313-win_amd64.whl", hash = "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d"}, - {file = "fonttools-4.54.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b"}, - {file = "fonttools-4.54.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c"}, - {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556"}, - {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b"}, - {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1"}, - {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386"}, - {file = "fonttools-4.54.1-cp38-cp38-win32.whl", hash = "sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664"}, - {file = "fonttools-4.54.1-cp38-cp38-win_amd64.whl", hash = "sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab"}, - {file = "fonttools-4.54.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13"}, - {file = "fonttools-4.54.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac"}, - {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986"}, - {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55"}, - {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9"}, - {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33"}, - {file = "fonttools-4.54.1-cp39-cp39-win32.whl", hash = "sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a"}, - {file = "fonttools-4.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7"}, - {file = "fonttools-4.54.1-py3-none-any.whl", hash = "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd"}, - {file = "fonttools-4.54.1.tar.gz", hash = "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285"}, + {file = "fonttools-4.55.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:51c029d4c0608a21a3d3d169dfc3fb776fde38f00b35ca11fdab63ba10a16f61"}, + {file = "fonttools-4.55.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bca35b4e411362feab28e576ea10f11268b1aeed883b9f22ed05675b1e06ac69"}, + {file = "fonttools-4.55.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ce4ba6981e10f7e0ccff6348e9775ce25ffadbee70c9fd1a3737e3e9f5fa74f"}, + {file = "fonttools-4.55.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31d00f9852a6051dac23294a4cf2df80ced85d1d173a61ba90a3d8f5abc63c60"}, + {file = "fonttools-4.55.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e198e494ca6e11f254bac37a680473a311a88cd40e58f9cc4dc4911dfb686ec6"}, + {file = "fonttools-4.55.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7208856f61770895e79732e1dcbe49d77bd5783adf73ae35f87fcc267df9db81"}, + {file = "fonttools-4.55.0-cp310-cp310-win32.whl", hash = "sha256:e7e6a352ff9e46e8ef8a3b1fe2c4478f8a553e1b5a479f2e899f9dc5f2055880"}, + {file = "fonttools-4.55.0-cp310-cp310-win_amd64.whl", hash = "sha256:636caaeefe586d7c84b5ee0734c1a5ab2dae619dc21c5cf336f304ddb8f6001b"}, + {file = "fonttools-4.55.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fa34aa175c91477485c44ddfbb51827d470011e558dfd5c7309eb31bef19ec51"}, + {file = "fonttools-4.55.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:37dbb3fdc2ef7302d3199fb12468481cbebaee849e4b04bc55b77c24e3c49189"}, + {file = "fonttools-4.55.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5263d8e7ef3c0ae87fbce7f3ec2f546dc898d44a337e95695af2cd5ea21a967"}, + {file = "fonttools-4.55.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f307f6b5bf9e86891213b293e538d292cd1677e06d9faaa4bf9c086ad5f132f6"}, + {file = "fonttools-4.55.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f0a4b52238e7b54f998d6a56b46a2c56b59c74d4f8a6747fb9d4042190f37cd3"}, + {file = "fonttools-4.55.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3e569711464f777a5d4ef522e781dc33f8095ab5efd7548958b36079a9f2f88c"}, + {file = "fonttools-4.55.0-cp311-cp311-win32.whl", hash = "sha256:2b3ab90ec0f7b76c983950ac601b58949f47aca14c3f21eed858b38d7ec42b05"}, + {file = "fonttools-4.55.0-cp311-cp311-win_amd64.whl", hash = "sha256:aa046f6a63bb2ad521004b2769095d4c9480c02c1efa7d7796b37826508980b6"}, + {file = "fonttools-4.55.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:838d2d8870f84fc785528a692e724f2379d5abd3fc9dad4d32f91cf99b41e4a7"}, + {file = "fonttools-4.55.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f46b863d74bab7bb0d395f3b68d3f52a03444964e67ce5c43ce43a75efce9246"}, + {file = "fonttools-4.55.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33b52a9cfe4e658e21b1f669f7309b4067910321757fec53802ca8f6eae96a5a"}, + {file = "fonttools-4.55.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:732a9a63d6ea4a81b1b25a1f2e5e143761b40c2e1b79bb2b68e4893f45139a40"}, + {file = "fonttools-4.55.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7dd91ac3fcb4c491bb4763b820bcab6c41c784111c24172616f02f4bc227c17d"}, + {file = "fonttools-4.55.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f0e115281a32ff532118aa851ef497a1b7cda617f4621c1cdf81ace3e36fb0c"}, + {file = "fonttools-4.55.0-cp312-cp312-win32.whl", hash = "sha256:6c99b5205844f48a05cb58d4a8110a44d3038c67ed1d79eb733c4953c628b0f6"}, + {file = "fonttools-4.55.0-cp312-cp312-win_amd64.whl", hash = "sha256:f8c8c76037d05652510ae45be1cd8fb5dd2fd9afec92a25374ac82255993d57c"}, + {file = "fonttools-4.55.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8118dc571921dc9e4b288d9cb423ceaf886d195a2e5329cc427df82bba872cd9"}, + {file = "fonttools-4.55.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01124f2ca6c29fad4132d930da69158d3f49b2350e4a779e1efbe0e82bd63f6c"}, + {file = "fonttools-4.55.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81ffd58d2691f11f7c8438796e9f21c374828805d33e83ff4b76e4635633674c"}, + {file = "fonttools-4.55.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5435e5f1eb893c35c2bc2b9cd3c9596b0fcb0a59e7a14121562986dd4c47b8dd"}, + {file = "fonttools-4.55.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d12081729280c39d001edd0f4f06d696014c26e6e9a0a55488fabc37c28945e4"}, + {file = "fonttools-4.55.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7ad1f1b98ab6cb927ab924a38a8649f1ffd7525c75fe5b594f5dab17af70e18"}, + {file = "fonttools-4.55.0-cp313-cp313-win32.whl", hash = "sha256:abe62987c37630dca69a104266277216de1023cf570c1643bb3a19a9509e7a1b"}, + {file = "fonttools-4.55.0-cp313-cp313-win_amd64.whl", hash = "sha256:2863555ba90b573e4201feaf87a7e71ca3b97c05aa4d63548a4b69ea16c9e998"}, + {file = "fonttools-4.55.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:00f7cf55ad58a57ba421b6a40945b85ac7cc73094fb4949c41171d3619a3a47e"}, + {file = "fonttools-4.55.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f27526042efd6f67bfb0cc2f1610fa20364396f8b1fc5edb9f45bb815fb090b2"}, + {file = "fonttools-4.55.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e67974326af6a8879dc2a4ec63ab2910a1c1a9680ccd63e4a690950fceddbe"}, + {file = "fonttools-4.55.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61dc0a13451143c5e987dec5254d9d428f3c2789a549a7cf4f815b63b310c1cc"}, + {file = "fonttools-4.55.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b2e526b325a903868c62155a6a7e24df53f6ce4c5c3160214d8fe1be2c41b478"}, + {file = "fonttools-4.55.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b7ef9068a1297714e6fefe5932c33b058aa1d45a2b8be32a4c6dee602ae22b5c"}, + {file = "fonttools-4.55.0-cp38-cp38-win32.whl", hash = "sha256:55718e8071be35dff098976bc249fc243b58efa263768c611be17fe55975d40a"}, + {file = "fonttools-4.55.0-cp38-cp38-win_amd64.whl", hash = "sha256:553bd4f8cc327f310c20158e345e8174c8eed49937fb047a8bda51daf2c353c8"}, + {file = "fonttools-4.55.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f901cef813f7c318b77d1c5c14cf7403bae5cb977cede023e22ba4316f0a8f6"}, + {file = "fonttools-4.55.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8c9679fc0dd7e8a5351d321d8d29a498255e69387590a86b596a45659a39eb0d"}, + {file = "fonttools-4.55.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd2820a8b632f3307ebb0bf57948511c2208e34a4939cf978333bc0a3f11f838"}, + {file = "fonttools-4.55.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23bbbb49bec613a32ed1b43df0f2b172313cee690c2509f1af8fdedcf0a17438"}, + {file = "fonttools-4.55.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a656652e1f5d55b9728937a7e7d509b73d23109cddd4e89ee4f49bde03b736c6"}, + {file = "fonttools-4.55.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f50a1f455902208486fbca47ce33054208a4e437b38da49d6721ce2fef732fcf"}, + {file = "fonttools-4.55.0-cp39-cp39-win32.whl", hash = "sha256:161d1ac54c73d82a3cded44202d0218ab007fde8cf194a23d3dd83f7177a2f03"}, + {file = "fonttools-4.55.0-cp39-cp39-win_amd64.whl", hash = "sha256:ca7fd6987c68414fece41c96836e945e1f320cda56fc96ffdc16e54a44ec57a2"}, + {file = "fonttools-4.55.0-py3-none-any.whl", hash = "sha256:12db5888cd4dd3fcc9f0ee60c6edd3c7e1fd44b7dd0f31381ea03df68f8a153f"}, + {file = "fonttools-4.55.0.tar.gz", hash = "sha256:7636acc6ab733572d5e7eec922b254ead611f1cdad17be3f0be7418e8bfaca71"}, ] [package.extras] @@ -837,13 +799,13 @@ test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", [[package]] name = "identify" -version = "2.6.1" +version = "2.6.2" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, - {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, + {file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"}, + {file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"}, ] [package.extras] @@ -1315,13 +1277,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1857,13 +1819,13 @@ files = [ [[package]] name = "sqlparse" -version = "0.5.1" +version = "0.5.2" description = "A non-validating SQL parser." optional = false python-versions = ">=3.8" files = [ - {file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"}, - {file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"}, + {file = "sqlparse-0.5.2-py3-none-any.whl", hash = "sha256:e99bc85c78160918c3e1d9230834ab8d80fc06c59d03f8db2618f65f65dda55e"}, + {file = "sqlparse-0.5.2.tar.gz", hash = "sha256:9e37b35e16d1cc652a2545f0997c1deb23ea28fa1f3eefe609eee3063c3b105f"}, ] [package.extras] @@ -1872,13 +1834,13 @@ doc = ["sphinx"] [[package]] name = "tomli" -version = "2.0.2" +version = "2.1.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, ] [[package]] @@ -1892,6 +1854,17 @@ files = [ {file = "toolz-1.0.0.tar.gz", hash = "sha256:2c86e3d9a04798ac556793bced838816296a2f085017664e4995cb40a1047a02"}, ] +[[package]] +name = "typed-classproperties" +version = "1.0.2" +description = "Typed decorators for classproperty and cached_classproperty." +optional = false +python-versions = ">=3.9" +files = [ + {file = "typed_classproperties-1.0.2-py3-none-any.whl", hash = "sha256:66e486e78df9be31cb6c2e077378a4d10469df051dac3b981330699d4637ee17"}, + {file = "typed_classproperties-1.0.2.tar.gz", hash = "sha256:f82c7f80c2a5b7241c5cefd0186ff7d365ebfa0b4da6ab579320724734ae241c"}, +] + [[package]] name = "types-beautifulsoup4" version = "4.12.0.20241020" @@ -2014,93 +1987,93 @@ files = [ [[package]] name = "yarl" -version = "1.17.1" +version = "1.18.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" files = [ - {file = "yarl-1.17.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1794853124e2f663f0ea54efb0340b457f08d40a1cef78edfa086576179c91"}, - {file = "yarl-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fbea1751729afe607d84acfd01efd95e3b31db148a181a441984ce9b3d3469da"}, - {file = "yarl-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ee427208c675f1b6e344a1f89376a9613fc30b52646a04ac0c1f6587c7e46ec"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b74ff4767d3ef47ffe0cd1d89379dc4d828d4873e5528976ced3b44fe5b0a21"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:62a91aefff3d11bf60e5956d340eb507a983a7ec802b19072bb989ce120cd948"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:846dd2e1243407133d3195d2d7e4ceefcaa5f5bf7278f0a9bda00967e6326b04"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e844be8d536afa129366d9af76ed7cb8dfefec99f5f1c9e4f8ae542279a6dc3"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc7c92c1baa629cb03ecb0c3d12564f172218fb1739f54bf5f3881844daadc6d"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae3476e934b9d714aa8000d2e4c01eb2590eee10b9d8cd03e7983ad65dfbfcba"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c7e177c619342e407415d4f35dec63d2d134d951e24b5166afcdfd1362828e17"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64cc6e97f14cf8a275d79c5002281f3040c12e2e4220623b5759ea7f9868d6a5"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:84c063af19ef5130084db70ada40ce63a84f6c1ef4d3dbc34e5e8c4febb20822"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:482c122b72e3c5ec98f11457aeb436ae4aecca75de19b3d1de7cf88bc40db82f"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:380e6c38ef692b8fd5a0f6d1fa8774d81ebc08cfbd624b1bca62a4d4af2f9931"}, - {file = "yarl-1.17.1-cp310-cp310-win32.whl", hash = "sha256:16bca6678a83657dd48df84b51bd56a6c6bd401853aef6d09dc2506a78484c7b"}, - {file = "yarl-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:561c87fea99545ef7d692403c110b2f99dced6dff93056d6e04384ad3bc46243"}, - {file = "yarl-1.17.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cbad927ea8ed814622305d842c93412cb47bd39a496ed0f96bfd42b922b4a217"}, - {file = "yarl-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fca4b4307ebe9c3ec77a084da3a9d1999d164693d16492ca2b64594340999988"}, - {file = "yarl-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff5c6771c7e3511a06555afa317879b7db8d640137ba55d6ab0d0c50425cab75"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b29beab10211a746f9846baa39275e80034e065460d99eb51e45c9a9495bcca"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a52a1ffdd824fb1835272e125385c32fd8b17fbdefeedcb4d543cc23b332d74"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58c8e9620eb82a189c6c40cb6b59b4e35b2ee68b1f2afa6597732a2b467d7e8f"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d216e5d9b8749563c7f2c6f7a0831057ec844c68b4c11cb10fc62d4fd373c26d"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:881764d610e3269964fc4bb3c19bb6fce55422828e152b885609ec176b41cf11"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8c79e9d7e3d8a32d4824250a9c6401194fb4c2ad9a0cec8f6a96e09a582c2cc0"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:299f11b44d8d3a588234adbe01112126010bd96d9139c3ba7b3badd9829261c3"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cc7d768260f4ba4ea01741c1b5fe3d3a6c70eb91c87f4c8761bbcce5181beafe"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:de599af166970d6a61accde358ec9ded821234cbbc8c6413acfec06056b8e860"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2b24ec55fad43e476905eceaf14f41f6478780b870eda5d08b4d6de9a60b65b4"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9fb815155aac6bfa8d86184079652c9715c812d506b22cfa369196ef4e99d1b4"}, - {file = "yarl-1.17.1-cp311-cp311-win32.whl", hash = "sha256:7615058aabad54416ddac99ade09a5510cf77039a3b903e94e8922f25ed203d7"}, - {file = "yarl-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:14bc88baa44e1f84164a392827b5defb4fa8e56b93fecac3d15315e7c8e5d8b3"}, - {file = "yarl-1.17.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:327828786da2006085a4d1feb2594de6f6d26f8af48b81eb1ae950c788d97f61"}, - {file = "yarl-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc353841428d56b683a123a813e6a686e07026d6b1c5757970a877195f880c2d"}, - {file = "yarl-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c73df5b6e8fabe2ddb74876fb82d9dd44cbace0ca12e8861ce9155ad3c886139"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bdff5e0995522706c53078f531fb586f56de9c4c81c243865dd5c66c132c3b5"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:06157fb3c58f2736a5e47c8fcbe1afc8b5de6fb28b14d25574af9e62150fcaac"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1654ec814b18be1af2c857aa9000de7a601400bd4c9ca24629b18486c2e35463"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6595c852ca544aaeeb32d357e62c9c780eac69dcd34e40cae7b55bc4fb1147"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:459e81c2fb920b5f5df744262d1498ec2c8081acdcfe18181da44c50f51312f7"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e48cdb8226644e2fbd0bdb0a0f87906a3db07087f4de77a1b1b1ccfd9e93685"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d9b6b28a57feb51605d6ae5e61a9044a31742db557a3b851a74c13bc61de5172"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e594b22688d5747b06e957f1ef822060cb5cb35b493066e33ceac0cf882188b7"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5f236cb5999ccd23a0ab1bd219cfe0ee3e1c1b65aaf6dd3320e972f7ec3a39da"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a2a64e62c7a0edd07c1c917b0586655f3362d2c2d37d474db1a509efb96fea1c"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d0eea830b591dbc68e030c86a9569826145df485b2b4554874b07fea1275a199"}, - {file = "yarl-1.17.1-cp312-cp312-win32.whl", hash = "sha256:46ddf6e0b975cd680eb83318aa1d321cb2bf8d288d50f1754526230fcf59ba96"}, - {file = "yarl-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:117ed8b3732528a1e41af3aa6d4e08483c2f0f2e3d3d7dca7cf538b3516d93df"}, - {file = "yarl-1.17.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5d1d42556b063d579cae59e37a38c61f4402b47d70c29f0ef15cee1acaa64488"}, - {file = "yarl-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0167540094838ee9093ef6cc2c69d0074bbf84a432b4995835e8e5a0d984374"}, - {file = "yarl-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2f0a6423295a0d282d00e8701fe763eeefba8037e984ad5de44aa349002562ac"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5b078134f48552c4d9527db2f7da0b5359abd49393cdf9794017baec7506170"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d401f07261dc5aa36c2e4efc308548f6ae943bfff20fcadb0a07517a26b196d8"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5f1ac7359e17efe0b6e5fec21de34145caef22b260e978336f325d5c84e6938"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f63d176a81555984e91f2c84c2a574a61cab7111cc907e176f0f01538e9ff6e"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e275792097c9f7e80741c36de3b61917aebecc08a67ae62899b074566ff8556"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:81713b70bea5c1386dc2f32a8f0dab4148a2928c7495c808c541ee0aae614d67"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:aa46dce75078fceaf7cecac5817422febb4355fbdda440db55206e3bd288cfb8"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1ce36ded585f45b1e9bb36d0ae94765c6608b43bd2e7f5f88079f7a85c61a4d3"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2d374d70fdc36f5863b84e54775452f68639bc862918602d028f89310a034ab0"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2d9f0606baaec5dd54cb99667fcf85183a7477f3766fbddbe3f385e7fc253299"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b0341e6d9a0c0e3cdc65857ef518bb05b410dbd70d749a0d33ac0f39e81a4258"}, - {file = "yarl-1.17.1-cp313-cp313-win32.whl", hash = "sha256:2e7ba4c9377e48fb7b20dedbd473cbcbc13e72e1826917c185157a137dac9df2"}, - {file = "yarl-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:949681f68e0e3c25377462be4b658500e85ca24323d9619fdc41f68d46a1ffda"}, - {file = "yarl-1.17.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8994b29c462de9a8fce2d591028b986dbbe1b32f3ad600b2d3e1c482c93abad6"}, - {file = "yarl-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9cbfbc5faca235fbdf531b93aa0f9f005ec7d267d9d738761a4d42b744ea159"}, - {file = "yarl-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b40d1bf6e6f74f7c0a567a9e5e778bbd4699d1d3d2c0fe46f4b717eef9e96b95"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5efe0661b9fcd6246f27957f6ae1c0eb29bc60552820f01e970b4996e016004"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5c4804e4039f487e942c13381e6c27b4b4e66066d94ef1fae3f6ba8b953f383"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5d6a6c9602fd4598fa07e0389e19fe199ae96449008d8304bf5d47cb745462e"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4c9156c4d1eb490fe374fb294deeb7bc7eaccda50e23775b2354b6a6739934"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6324274b4e0e2fa1b3eccb25997b1c9ed134ff61d296448ab8269f5ac068c4c"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d8a8b74d843c2638f3864a17d97a4acda58e40d3e44b6303b8cc3d3c44ae2d29"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7fac95714b09da9278a0b52e492466f773cfe37651cf467a83a1b659be24bf71"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c180ac742a083e109c1a18151f4dd8675f32679985a1c750d2ff806796165b55"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:578d00c9b7fccfa1745a44f4eddfdc99d723d157dad26764538fbdda37209857"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1a3b91c44efa29e6c8ef8a9a2b583347998e2ba52c5d8280dbd5919c02dfc3b5"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ac5b4984c468ce4f4a553df281450df0a34aefae02e58d77a0847be8d1e11f"}, - {file = "yarl-1.17.1-cp39-cp39-win32.whl", hash = "sha256:7294e38f9aa2e9f05f765b28ffdc5d81378508ce6dadbe93f6d464a8c9594473"}, - {file = "yarl-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:eb6dce402734575e1a8cc0bb1509afca508a400a57ce13d306ea2c663bad1138"}, - {file = "yarl-1.17.1-py3-none-any.whl", hash = "sha256:f1790a4b1e8e8e028c391175433b9c8122c39b46e1663228158e61e6f915bf06"}, - {file = "yarl-1.17.1.tar.gz", hash = "sha256:067a63fcfda82da6b198fa73079b1ca40b7c9b7994995b6ee38acda728b64d47"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a"}, + {file = "yarl-1.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae38bd86eae3ba3d2ce5636cc9e23c80c9db2e9cb557e40b98153ed102b5a736"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:685cc37f3f307c6a8e879986c6d85328f4c637f002e219f50e2ef66f7e062c1d"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8254dbfce84ee5d1e81051ee7a0f1536c108ba294c0fdb5933476398df0654f3"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20de4a8b04de70c49698dc2390b7fd2d18d424d3b876371f9b775e2b462d4b41"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a2074a37285570d54b55820687de3d2f2b9ecf1b714e482e48c9e7c0402038"}, + {file = "yarl-1.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f576ed278860df2721a5d57da3381040176ef1d07def9688a385c8330db61a1"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3a3709450a574d61be6ac53d582496014342ea34876af8dc17cc16da32826c9a"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bd80ed29761490c622edde5dd70537ca8c992c2952eb62ed46984f8eff66d6e8"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:32141e13a1d5a48525e519c9197d3f4d9744d818d5c7d6547524cc9eccc8971e"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8b8d3e4e014fb4274f1c5bf61511d2199e263909fb0b8bda2a7428b0894e8dc6"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:701bb4a8f4de191c8c0cc9a1e6d5142f4df880e9d1210e333b829ca9425570ed"}, + {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a45d94075ac0647621eaaf693c8751813a3eccac455d423f473ffed38c8ac5c9"}, + {file = "yarl-1.18.0-cp310-cp310-win32.whl", hash = "sha256:34176bfb082add67cb2a20abd85854165540891147f88b687a5ed0dc225750a0"}, + {file = "yarl-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:73553bbeea7d6ec88c08ad8027f4e992798f0abc459361bf06641c71972794dc"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b8e8c516dc4e1a51d86ac975b0350735007e554c962281c432eaa5822aa9765c"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e6b4466714a73f5251d84b471475850954f1fa6acce4d3f404da1d55d644c34"}, + {file = "yarl-1.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c893f8c1a6d48b25961e00922724732d00b39de8bb0b451307482dc87bddcd74"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13aaf2bdbc8c86ddce48626b15f4987f22e80d898818d735b20bd58f17292ee8"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd21c0128e301851de51bc607b0a6da50e82dc34e9601f4b508d08cc89ee7929"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205de377bd23365cd85562c9c6c33844050a93661640fda38e0567d2826b50df"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed69af4fe2a0949b1ea1d012bf065c77b4c7822bad4737f17807af2adb15a73c"}, + {file = "yarl-1.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e1c18890091aa3cc8a77967943476b729dc2016f4cfe11e45d89b12519d4a93"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:91b8fb9427e33f83ca2ba9501221ffaac1ecf0407f758c4d2f283c523da185ee"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:536a7a8a53b75b2e98ff96edb2dfb91a26b81c4fed82782035767db5a465be46"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a64619a9c47c25582190af38e9eb382279ad42e1f06034f14d794670796016c0"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c73a6bbc97ba1b5a0c3c992ae93d721c395bdbb120492759b94cc1ac71bc6350"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a173401d7821a2a81c7b47d4e7d5c4021375a1441af0c58611c1957445055056"}, + {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7520e799b1f84e095cce919bd6c23c9d49472deeef25fe1ef960b04cca51c3fc"}, + {file = "yarl-1.18.0-cp311-cp311-win32.whl", hash = "sha256:c4cb992d8090d5ae5f7afa6754d7211c578be0c45f54d3d94f7781c495d56716"}, + {file = "yarl-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:52c136f348605974c9b1c878addd6b7a60e3bf2245833e370862009b86fa4689"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ece25e2251c28bab737bdf0519c88189b3dd9492dc086a1d77336d940c28ced"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:454902dc1830d935c90b5b53c863ba2a98dcde0fbaa31ca2ed1ad33b2a7171c6"}, + {file = "yarl-1.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01be8688fc211dc237e628fcc209dda412d35de7642453059a0553747018d075"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d26f1fa9fa2167bb238f6f4b20218eb4e88dd3ef21bb8f97439fa6b5313e30d"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b234a4a9248a9f000b7a5dfe84b8cb6210ee5120ae70eb72a4dcbdb4c528f72f"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe94d1de77c4cd8caff1bd5480e22342dbd54c93929f5943495d9c1e8abe9f42"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4c90c5363c6b0a54188122b61edb919c2cd1119684999d08cd5e538813a28e"}, + {file = "yarl-1.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a98ecadc5a241c9ba06de08127ee4796e1009555efd791bac514207862b43d"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9106025c7f261f9f5144f9aa7681d43867eed06349a7cfb297a1bc804de2f0d1"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f275ede6199d0f1ed4ea5d55a7b7573ccd40d97aee7808559e1298fe6efc8dbd"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f7edeb1dcc7f50a2c8e08b9dc13a413903b7817e72273f00878cb70e766bdb3b"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c083f6dd6951b86e484ebfc9c3524b49bcaa9c420cb4b2a78ef9f7a512bfcc85"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:80741ec5b471fbdfb997821b2842c59660a1c930ceb42f8a84ba8ca0f25a66aa"}, + {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1a3297b9cad594e1ff0c040d2881d7d3a74124a3c73e00c3c71526a1234a9f7"}, + {file = "yarl-1.18.0-cp312-cp312-win32.whl", hash = "sha256:cd6ab7d6776c186f544f893b45ee0c883542b35e8a493db74665d2e594d3ca75"}, + {file = "yarl-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:039c299a0864d1f43c3e31570045635034ea7021db41bf4842693a72aca8df3a"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6fb64dd45453225f57d82c4764818d7a205ee31ce193e9f0086e493916bd4f72"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3adaaf9c6b1b4fc258584f4443f24d775a2086aee82d1387e48a8b4f3d6aecf6"}, + {file = "yarl-1.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:da206d1ec78438a563c5429ab808a2b23ad7bc025c8adbf08540dde202be37d5"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:576d258b21c1db4c6449b1c572c75d03f16a482eb380be8003682bdbe7db2f28"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e547c0a375c4bfcdd60eef82e7e0e8698bf84c239d715f5c1278a73050393"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3818eabaefb90adeb5e0f62f047310079d426387991106d4fbf3519eec7d90a"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f72421246c21af6a92fbc8c13b6d4c5427dfd949049b937c3b731f2f9076bd"}, + {file = "yarl-1.18.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fa7d37f2ada0f42e0723632993ed422f2a679af0e200874d9d861720a54f53e"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:42ba84e2ac26a3f252715f8ec17e6fdc0cbf95b9617c5367579fafcd7fba50eb"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6a49ad0102c0f0ba839628d0bf45973c86ce7b590cdedf7540d5b1833ddc6f00"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:96404e8d5e1bbe36bdaa84ef89dc36f0e75939e060ca5cd45451aba01db02902"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a0509475d714df8f6d498935b3f307cd122c4ca76f7d426c7e1bb791bcd87eda"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ff116f0285b5c8b3b9a2680aeca29a858b3b9e0402fc79fd850b32c2bcb9f8b"}, + {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2580c1d7e66e6d29d6e11855e3b1c6381971e0edd9a5066e6c14d79bc8967af"}, + {file = "yarl-1.18.0-cp313-cp313-win32.whl", hash = "sha256:14408cc4d34e202caba7b5ac9cc84700e3421a9e2d1b157d744d101b061a4a88"}, + {file = "yarl-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:1db1537e9cb846eb0ff206eac667f627794be8b71368c1ab3207ec7b6f8c5afc"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fa2c9cb607e0f660d48c54a63de7a9b36fef62f6b8bd50ff592ce1137e73ac7d"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c0f4808644baf0a434a3442df5e0bedf8d05208f0719cedcd499e168b23bfdc4"}, + {file = "yarl-1.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7db9584235895a1dffca17e1c634b13870852094f6389b68dcc6338086aa7b08"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f8d27d6f93ceeeb80aa6980e883aa57895270f7f41842b92247e65d7aeddf"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:609ffd44fed2ed88d9b4ef62ee860cf86446cf066333ad4ce4123505b819e581"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f172b8b2c72a13a06ea49225a9c47079549036ad1b34afa12d5491b881f5b993"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d89ae7de94631b60d468412c18290d358a9d805182373d804ec839978b120422"}, + {file = "yarl-1.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:466d31fd043ef9af822ee3f1df8fdff4e8c199a7f4012c2642006af240eade17"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7609b8462351c4836b3edce4201acb6dd46187b207c589b30a87ffd1813b48dc"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d9d4f5e471e8dc49b593a80766c2328257e405f943c56a3dc985c125732bc4cf"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:67b336c15e564d76869c9a21316f90edf546809a5796a083b8f57c845056bc01"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b212452b80cae26cb767aa045b051740e464c5129b7bd739c58fbb7deb339e7b"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:38b39b7b3e692b6c92b986b00137a3891eddb66311b229d1940dcbd4f025083c"}, + {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ee6884a8848792d58b854946b685521f41d8871afa65e0d4a774954e9c9e89"}, + {file = "yarl-1.18.0-cp39-cp39-win32.whl", hash = "sha256:b4095c5019bb889aa866bf12ed4c85c0daea5aafcb7c20d1519f02a1e738f07f"}, + {file = "yarl-1.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:2d90f2e4d16a5b0915ee065218b435d2ef619dd228973b1b47d262a6f7cd8fa5"}, + {file = "yarl-1.18.0-py3-none-any.whl", hash = "sha256:dbf53db46f7cf176ee01d8d98c39381440776fcda13779d269a8ba664f69bec0"}, + {file = "yarl-1.18.0.tar.gz", hash = "sha256:20d95535e7d833889982bfe7cc321b7f63bf8879788fee982c76ae2b24cfb715"}, ] [package.dependencies] @@ -2111,4 +2084,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "262c37a2e01e2c0898320e5cd41b3a909a2aaa16a8465167919351015c567cd3" +content-hash = "0e3577f07672ceec4dde1c1ff5d8f5d2f2cf914b9cc4b67c8df3d9ec34ded4b9" diff --git a/pyproject.toml b/pyproject.toml index ac0b0fbc..0c43b144 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,9 +39,8 @@ django = "~5.1" matplotlib = "^3.9" mplcyberpunk = "^0.7" python-logging-discord-handler = "^0.1" -classproperties = {git = "https://github.com/hottwaj/classproperties.git"} +typed_classproperties = "^1" asyncstdlib = "~3.13" -setuptools = "^70.3" [tool.poetry.group.dev.dependencies] pre-commit = "^4.0" @@ -99,91 +98,39 @@ django_settings_module = "db._settings" [tool.ruff] output-format = "concise" line-length = 95 +indent-width = 4 target-version ="py312" -extend-exclude = [ - "db/**/migrations/", - "venv/", - ".vscode", - ".idea", - ".pytest_cache", - ".mypy_cache", - ".ruff_cache", - "__pycache__", -] +extend-exclude = ["db/**/migrations/"] [tool.ruff.lint] -select = [ - "E", - "F", - "W", - "C", - "I", - "N", - "D", - "UP", - "YTT", - "ANN", - "ASYNC", - "S", - "BLE", - "FBT", - "B", - "A", - "COM", - "DTZ", - "T", - "DJ", - "EM", - "G", - "INP", - "PIE", - "PT", - "Q", - "RSE", - "RET", - "SLF", - "SLOT", - "SIM", - "TID", - "TCH", - "ARG", - "PTH", - "TD", - "FIX", - "ERA", - "PGH", - "PL", - "TRY", - "FLY", - "PERF", - "RUF", -] -fixable = ["I001", "TCH", "F401", "COM812"] ignore = [ - "N818", - "N806", - "D203", + "C90", + "COM812", + "COM819", + "CPY", + "D206", "D212", + "FA", + "FAST", + "INP001", + "ISC001", + "ISC002", + "N806", + "PD", + "PIE808", + "Q000", + "Q001", + "Q002", "Q003", + "S603", + "SIM910", "TD002", "TD003", - "S311", - "UP040", # NOTE: Mypy does not currently support PEP 695 type aliases, so they should not be used - "PT009", - "PT027", -] -task-tags = [ - "TODO", - "FIXME", - "HACK", - "BUG", - "NOBUG", - "REQ", - "IDEA", - "NOTE", - "ISSUE", - "DONE", + "W191", ] +select = ["ALL", "D204", "D213", "D401"] +task-tags = ["BUG", "DONE", "FIXME", "HACK", "IDEA", "ISSUE", "NOBUG", "NOTE", "REQ", "TODO"] +fixable = ["I001", "TCH", "F401", "RUF022", "RUF100", "TC003", "TC004", "PYI025", "UP037", "UP040"] allowed-confusables = [ "á´€", "Ê™", @@ -216,24 +163,41 @@ allowed-confusables = [ [tool.ruff.lint.per-file-ignores] "tests/**/test_*.py" = ["S101"] -[tool.ruff.lint.flake8-pytest-style] -parametrize-values-type = "tuple" +[tool.ruff.lint.flake8-type-checking] +quote-annotations = true +strict = true [tool.ruff.lint.flake8-self] extend-ignore-names = ["_base_manager", "_default_manager", "_meta", "_get_wrap_line_width"] -[tool.ruff.lint.mccabe] -max-complexity = 18 +[tool.ruff.lint.pep8-naming] +classmethod-decorators = [ + "typed_classproperties.cached_classproperty", + "typed_classproperties.classproperty", +] +extend-ignore-names = ["BROKEN_*_MESSAGE", "INVALID_*_MESSAGE", "NO_*_MESSAGE"] [tool.ruff.lint.pycodestyle] ignore-overlong-task-comments = true max-doc-length = 95 +[tool.ruff.lint.pydocstyle] +convention = "google" +property-decorators = [ + "typed_classproperties.cached_classproperty", + "typed_classproperties.classproperty", +] + [tool.ruff.lint.pylint] -allow-magic-value-types = ["str", "bytes", "int"] -max-args = 7 -max-returns = 10 +allow-magic-value-types = ["bytes", "int", "str"] +max-args = 8 max-branches = 19 +max-returns = 10 + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = true +mark-parentheses = true +parametrize-values-type = "tuple" [tool.ruff.lint.pyupgrade] keep-runtime-typing = true diff --git a/tests/__init__.py b/tests/__init__.py index ac06ab29..c7605762 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,8 @@ """Contains the complete test suite for TeX-Bot-Py-V2 using pytest.""" -from collections.abc import Sequence +from typing import TYPE_CHECKING -__all__: Sequence[str] = () +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = () diff --git a/tests/test_utils.py b/tests/test_utils.py index a10ed97f..0b31917c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,16 +1,17 @@ """Test suite for utils package.""" -from collections.abc import Sequence - -__all__: Sequence[str] = () - - import random import re -from typing import Final +from typing import TYPE_CHECKING import utils +if TYPE_CHECKING: + from collections.abc import Sequence + from typing import Final + +__all__: "Sequence[str]" = () + # TODO(CarrotManMatt): Move to stats_tests # noqa: FIX002 # https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/57 # class TestPlotBarChart: @@ -84,10 +85,10 @@ class TestGenerateInviteURL: @staticmethod def test_url_generates() -> None: """Test that the invite URL generates successfully when valid arguments are passed.""" - DISCORD_BOT_APPLICATION_ID: Final[int] = random.randint( + DISCORD_BOT_APPLICATION_ID: Final[int] = random.randint( # noqa: S311 10000000000000000, 99999999999999999999, ) - DISCORD_MAIN_GUILD_ID: Final[int] = random.randint( + DISCORD_MAIN_GUILD_ID: Final[int] = random.randint( # noqa: S311 10000000000000000, 99999999999999999999, ) diff --git a/utils/__init__.py b/utils/__init__.py index 91cefb24..c547a197 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,24 +1,7 @@ """Utility classes & functions provided for use across the whole of the project.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "AllChannelTypes", - "CommandChecks", - "generate_invite_url", - "is_member_inducted", - "is_running_in_async", - "MessageSavingSenderComponent", - "SuppressTraceback", - "TeXBot", - "TeXBotApplicationContext", - "TeXBotAutocompleteContext", - "TeXBotBaseCog", -) - - import asyncio -from typing import TypeAlias +from typing import TYPE_CHECKING import discord @@ -29,14 +12,32 @@ from .tex_bot_base_cog import TeXBotBaseCog from .tex_bot_contexts import TeXBotApplicationContext, TeXBotAutocompleteContext -AllChannelTypes: TypeAlias = ( - discord.VoiceChannel - | discord.StageChannel - | discord.TextChannel - | discord.ForumChannel - | discord.CategoryChannel +if TYPE_CHECKING: + from collections.abc import Sequence + +__all__: "Sequence[str]" = ( + "AllChannelTypes", + "CommandChecks", + "MessageSavingSenderComponent", + "SuppressTraceback", + "TeXBot", + "TeXBotApplicationContext", + "TeXBotAutocompleteContext", + "TeXBotBaseCog", + "generate_invite_url", + "is_member_inducted", + "is_running_in_async", ) +if TYPE_CHECKING: + type AllChannelTypes = ( + discord.VoiceChannel + | discord.StageChannel + | discord.TextChannel + | discord.ForumChannel + | discord.CategoryChannel + ) + def generate_invite_url(discord_bot_application_id: int, discord_guild_id: int) -> str: """Execute the logic that this util function provides.""" diff --git a/utils/command_checks.py b/utils/command_checks.py index 12f08e05..cac2ded9 100644 --- a/utils/command_checks.py +++ b/utils/command_checks.py @@ -1,34 +1,34 @@ """Command check decorators to ensure given predicates before executing a command.""" -from collections.abc import Sequence +from typing import TYPE_CHECKING -__all__: Sequence[str] = ("CommandChecks",) +from discord.ext import commands +from exceptions import DiscordMemberNotInMainGuildError -from collections.abc import Callable +if TYPE_CHECKING: + from collections.abc import Callable, Sequence -from discord.ext import commands -from discord.ext.commands import CheckFailure + from discord.ext.commands import CheckFailure + from discord.ext.commands.core import T -# noinspection PyProtectedMember -from discord.ext.commands.core import T + from utils.tex_bot_contexts import TeXBotApplicationContext -from exceptions import DiscordMemberNotInMainGuildError -from utils.tex_bot_contexts import TeXBotApplicationContext +__all__: "Sequence[str]" = ("CommandChecks",) class CommandChecks: """Command check decorators to ensure given predicates before executing a command.""" @staticmethod - async def _check_interaction_user_in_main_guild(ctx: TeXBotApplicationContext) -> bool: + async def _check_interaction_user_in_main_guild(ctx: "TeXBotApplicationContext") -> bool: try: await ctx.bot.get_main_guild_member(ctx.user) except DiscordMemberNotInMainGuildError: return False return True - check_interaction_user_in_main_guild: Callable[[T], T] + check_interaction_user_in_main_guild: "Callable[[T], T]" """ Decorator to ensure the interaction user of a command is within your group's Discord guild. @@ -37,10 +37,10 @@ async def _check_interaction_user_in_main_guild(ctx: TeXBotApplicationContext) - """ @staticmethod - async def _check_interaction_user_has_committee_role(ctx: TeXBotApplicationContext) -> bool: # noqa: E501 + async def _check_interaction_user_has_committee_role(ctx: "TeXBotApplicationContext") -> bool: # noqa: E501 return await ctx.bot.check_user_has_committee_role(ctx.user) - check_interaction_user_has_committee_role: Callable[[T], T] + check_interaction_user_has_committee_role: "Callable[[T], T]" """ Command check decorator to ensure the interaction user has the "Committee" role. @@ -49,13 +49,13 @@ async def _check_interaction_user_has_committee_role(ctx: TeXBotApplicationConte """ @classmethod - def is_interaction_user_in_main_guild_failure(cls, check: CheckFailure) -> bool: + def is_interaction_user_in_main_guild_failure(cls, check: "CheckFailure") -> bool: # noinspection GrazieInspection """Whether check failed due to the interaction user not being in your Discord guild.""" return bool(check.__name__ == cls._check_interaction_user_in_main_guild.__name__) # type: ignore[attr-defined] @classmethod - def is_interaction_user_has_committee_role_failure(cls, check: CheckFailure) -> bool: + def is_interaction_user_has_committee_role_failure(cls, check: "CheckFailure") -> bool: # noinspection GrazieInspection """Whether check failed due to the interaction user not having the committee role.""" return bool(check.__name__ == cls._check_interaction_user_has_committee_role.__name__) # type: ignore[attr-defined] diff --git a/utils/error_capture_decorators.py b/utils/error_capture_decorators.py index 6c215528..559c5815 100644 --- a/utils/error_capture_decorators.py +++ b/utils/error_capture_decorators.py @@ -4,27 +4,24 @@ Capturing errors is necessary in contexts where exceptions are not already suppressed. """ -from collections.abc import Sequence - -__all__: Sequence[str] = ( - "capture_guild_does_not_exist_error", - "capture_strike_tracking_error", - "ErrorCaptureDecorators", -) - - import functools import logging -from collections.abc import Callable, Coroutine -from logging import Logger -from typing import TYPE_CHECKING, Final, ParamSpec, TypeVar +from typing import TYPE_CHECKING, ParamSpec, TypeVar from exceptions import GuildDoesNotExistError, StrikeTrackingError from .tex_bot_base_cog import TeXBotBaseCog if TYPE_CHECKING: - from typing import Concatenate, TypeAlias + from collections.abc import Callable, Coroutine, Sequence + from logging import Logger + from typing import Concatenate, Final + +__all__: "Sequence[str]" = ( + "ErrorCaptureDecorators", + "capture_guild_does_not_exist_error", + "capture_strike_tracking_error", +) P = ParamSpec("P") @@ -32,16 +29,18 @@ T_cog = TypeVar("T_cog", bound=TeXBotBaseCog) if TYPE_CHECKING: - WrapperInputFunc: TypeAlias = ( - Callable[Concatenate[TeXBotBaseCog, P], Coroutine[object, object, T_ret]] - | Callable[P, Coroutine[object, object, T_ret]] + type WrapperInputFunc[T_ret] = ( + Callable[ + Concatenate[TeXBotBaseCog, P], + Coroutine[object, object, T_ret]] | Callable[P, Coroutine[object, object, T_ret], + ] ) - WrapperOutputFunc: TypeAlias = Callable[P, Coroutine[object, object, T_ret | None]] - DecoratorInputFunc: TypeAlias = ( + type WrapperOutputFunc[T_ret] = Callable[P, Coroutine[object, object, T_ret | None]] + type DecoratorInputFunc[T_cog: TeXBotBaseCog, T_ret] = ( Callable[Concatenate[T_cog, P], Coroutine[object, object, T_ret]] ) -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class ErrorCaptureDecorators: @@ -52,7 +51,7 @@ class ErrorCaptureDecorators: """ @staticmethod - def capture_error_and_close(func: "DecoratorInputFunc[T_cog, P, T_ret]", error_type: type[BaseException], close_func: Callable[[BaseException], None]) -> "WrapperOutputFunc[P, T_ret]": # noqa: E501 + def capture_error_and_close(func: "DecoratorInputFunc[T_cog, P, T_ret]", error_type: type[BaseException], close_func: "Callable[[BaseException], None]") -> "WrapperOutputFunc[P, T_ret]": # noqa: E501 """ Decorator to send an error message to the user when the given exception type is raised. diff --git a/utils/message_sender_components.py b/utils/message_sender_components.py index e013c8c6..1662c601 100644 --- a/utils/message_sender_components.py +++ b/utils/message_sender_components.py @@ -1,21 +1,23 @@ """Class definitions of components that send provided message content to a defined endpoint.""" -from collections.abc import Sequence +import abc +from typing import TYPE_CHECKING, final, override -__all__: Sequence[str] = ( - "ChannelMessageSender", - "MessageSavingSenderComponent", - "ResponseMessageSender", -) +import discord +if TYPE_CHECKING: + from collections.abc import Sequence + from typing import Final, TypedDict -import abc -from typing import Final, TypedDict, final, override + from discord.ui import View -import discord -from discord.ui import View + from .tex_bot_contexts import TeXBotApplicationContext -from .tex_bot_contexts import TeXBotApplicationContext +__all__: "Sequence[str]" = ( + "ChannelMessageSender", + "MessageSavingSenderComponent", + "ResponseMessageSender", +) class MessageSavingSenderComponent(abc.ABC): @@ -30,7 +32,7 @@ def __init__(self) -> None: self.sent_message: discord.Message | discord.Interaction | None = None @abc.abstractmethod - async def _send(self, content: str, *, view: View | None = None) -> discord.Message | discord.Interaction: # noqa: E501 + async def _send(self, content: str, *, view: "View | None" = None) -> discord.Message | discord.Interaction: # noqa: E501 """ Subclass implementation of `send()` method. @@ -39,7 +41,7 @@ async def _send(self, content: str, *, view: View | None = None) -> discord.Mess """ @final - async def send(self, content: str, *, view: View | None = None) -> None: + async def send(self, content: str, *, view: "View | None" = None) -> None: """Send the provided message content & optional view to the defined endpoint.""" if self.sent_message is not None: ALREADY_SENT_MESSAGE: Final[str] = ( @@ -78,20 +80,21 @@ def __init__(self, channel: discord.DMChannel | discord.TextChannel) -> None: super().__init__() @override - async def _send(self, content: str, *, view: View | None = None) -> discord.Message | discord.Interaction: # noqa: E501 - class _BaseChannelSendKwargs(TypedDict): - """Type-hint-definition for the required kwargs to the channel-send-function.""" + async def _send(self, content: str, *, view: "View | None" = None) -> discord.Message | discord.Interaction: # noqa: E501 + if TYPE_CHECKING: + class _BaseChannelSendKwargs(TypedDict): + """Type-hint for the required kwargs to the channel-send-function.""" - content: str + content: str - class ChannelSendKwargs(_BaseChannelSendKwargs, total=False): - """ - Type-hint-definition for all kwargs to the channel-send-function. + class ChannelSendKwargs(_BaseChannelSendKwargs, total=False): + """ + Type-hint-definition for all kwargs to the channel-send-function. - Includes both required & optional kwargs. - """ + Includes both required & optional kwargs. + """ - view: View + view: "View" send_kwargs: ChannelSendKwargs = {"content": content} if view: @@ -109,11 +112,11 @@ class ResponseMessageSender(MessageSavingSenderComponent): """ @override - def __init__(self, ctx: TeXBotApplicationContext) -> None: + def __init__(self, ctx: "TeXBotApplicationContext") -> None: self.ctx: TeXBotApplicationContext = ctx super().__init__() @override - async def _send(self, content: str, *, view: View | None = None) -> discord.Message | discord.Interaction: # noqa: E501 + async def _send(self, content: str, *, view: "View | None" = None) -> discord.Message | discord.Interaction: # noqa: E501 return await self.ctx.respond(content=content, view=view, ephemeral=True) diff --git a/utils/suppress_traceback.py b/utils/suppress_traceback.py index 807cef9d..52e8abf6 100644 --- a/utils/suppress_traceback.py +++ b/utils/suppress_traceback.py @@ -4,14 +4,14 @@ The previous traceback limit is returned when exiting the context manager. """ -from collections.abc import Sequence - -__all__: Sequence[str] = ("SuppressTraceback",) +import sys +from typing import TYPE_CHECKING, override +if TYPE_CHECKING: + from collections.abc import Sequence + from types import TracebackType -import sys -from types import TracebackType -from typing import override +__all__: "Sequence[str]" = ("SuppressTraceback",) class SuppressTraceback: @@ -38,7 +38,7 @@ def __enter__(self) -> None: # noinspection SpellCheckingInspection sys.tracebacklimit = 0 - def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) -> None: # noqa: E501 + def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: "TracebackType | None") -> None: # noqa: E501, PYI036 """Exit the context manager, reverting the limit of traceback output.""" if exc_type is not None or exc_val is not None or exc_tb is not None: return diff --git a/utils/tex_bot.py b/utils/tex_bot.py index 4d458d06..50cc6ba0 100644 --- a/utils/tex_bot.py +++ b/utils/tex_bot.py @@ -1,14 +1,8 @@ """Custom Pycord Bot class implementation.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("TeXBot",) - - import logging import re -from logging import Logger -from typing import TYPE_CHECKING, Final, NoReturn, override +from typing import TYPE_CHECKING, override import aiohttp import discord @@ -31,9 +25,15 @@ ) if TYPE_CHECKING: + from collections.abc import Sequence + from logging import Logger + from typing import Final, NoReturn + from utils import AllChannelTypes -logger: Final[Logger] = logging.getLogger("TeX-Bot") +__all__: "Sequence[str]" = ("TeXBot",) + +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class TeXBot(discord.Bot): @@ -65,7 +65,7 @@ def __init__(self, *args: object, **options: object) -> None: super().__init__(*args, **options) # type: ignore[no-untyped-call] @override - async def close(self) -> NoReturn: # type: ignore[misc] + async def close(self) -> "NoReturn": # type: ignore[misc] await super().close() logger.info("TeX-Bot manually terminated.") @@ -89,7 +89,7 @@ def main_guild(self) -> discord.Guild: """ MAIN_GUILD_EXISTS: Final[bool] = bool( self._main_guild - and self._check_guild_accessible(settings["_DISCORD_MAIN_GUILD_ID"]) # noqa: COM812 + and self._check_guild_accessible(settings["_DISCORD_MAIN_GUILD_ID"]) ) if not MAIN_GUILD_EXISTS: raise GuildDoesNotExistError(guild_id=settings["_DISCORD_MAIN_GUILD_ID"]) @@ -404,7 +404,7 @@ async def _fetch_text_channel(self, name: str) -> discord.TextChannel | None: return text_channel - async def perform_kill_and_close(self, initiated_by_user: discord.User | discord.Member | None = None) -> NoReturn: # noqa: E501 + async def perform_kill_and_close(self, initiated_by_user: discord.User | discord.Member | None = None) -> "NoReturn": # noqa: E501 """ Shutdown TeX-Bot by using the "/kill" command. diff --git a/utils/tex_bot_base_cog.py b/utils/tex_bot_base_cog.py index 60da668c..05b1cb07 100644 --- a/utils/tex_bot_base_cog.py +++ b/utils/tex_bot_base_cog.py @@ -1,16 +1,9 @@ """Custom cog subclass that stores a reference to the custom bot class.""" -from collections.abc import Sequence - -__all__: Sequence[str] = ("TeXBotBaseCog",) - - import contextlib import logging import re -from collections.abc import Mapping, Set -from logging import Logger -from typing import TYPE_CHECKING, Final, override +from typing import TYPE_CHECKING, override import discord from discord import Cog @@ -20,23 +13,29 @@ BaseDoesNotExistError, ) -from .tex_bot import TeXBot -from .tex_bot_contexts import TeXBotApplicationContext, TeXBotAutocompleteContext - if TYPE_CHECKING: - from typing import TypeAlias + from collections.abc import Mapping, Sequence + from collections.abc import Set as AbstractSet + from logging import Logger + from typing import Final + + from .tex_bot import TeXBot + from .tex_bot_contexts import TeXBotApplicationContext, TeXBotAutocompleteContext + + +__all__: "Sequence[str]" = ("TeXBotBaseCog",) if TYPE_CHECKING: - MentionableMember: TypeAlias = discord.Member | discord.Role + type MentionableMember = discord.Member | discord.Role -logger: Final[Logger] = logging.getLogger("TeX-Bot") +logger: "Final[Logger]" = logging.getLogger("TeX-Bot") class TeXBotBaseCog(Cog): """Base Cog subclass that stores a reference to the currently running TeXBot instance.""" - ERROR_ACTIVITIES: Final[Mapping[str, str]] = { + ERROR_ACTIVITIES: "Final[Mapping[str, str]]" = { # noqa: RUF012 "archive": "archive the selected category", "delete_all_reminders": ( "delete all `DiscordReminder` objects from the backend database" @@ -63,7 +62,7 @@ class TeXBotBaseCog(Cog): } @override - def __init__(self, bot: TeXBot) -> None: + def __init__(self, bot: "TeXBot") -> None: """ Initialise a new cog instance. @@ -71,7 +70,7 @@ def __init__(self, bot: TeXBot) -> None: """ self.bot: TeXBot = bot # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 - async def command_send_error(self, ctx: TeXBotApplicationContext, error_code: str | None = None, message: str | None = None, logging_message: str | BaseException | None = None) -> None: # noqa: E501 + async def command_send_error(self, ctx: "TeXBotApplicationContext", error_code: str | None = None, message: str | None = None, logging_message: str | BaseException | None = None) -> None: # noqa: E501 """ Construct & format an error message from the given details. @@ -97,7 +96,7 @@ async def command_send_error(self, ctx: TeXBotApplicationContext, error_code: st ) @classmethod - async def send_error(cls, bot: TeXBot, interaction: discord.Interaction, interaction_name: str, error_code: str | None = None, message: str | None = None, logging_message: str | BaseException | None = None) -> None: # noqa: E501 + async def send_error(cls, bot: "TeXBot", interaction: discord.Interaction, interaction_name: str, error_code: str | None = None, message: str | None = None, logging_message: str | BaseException | None = None) -> None: # noqa: E501 """ Construct & format an error message from the given details. @@ -152,7 +151,7 @@ async def send_error(cls, bot: TeXBot, interaction: discord.Interaction, interac ) @staticmethod - async def autocomplete_get_text_channels(ctx: TeXBotAutocompleteContext) -> Set[discord.OptionChoice] | Set[str]: # noqa: E501 + async def autocomplete_get_text_channels(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 """ Autocomplete callable that generates the set of available selectable channels. diff --git a/utils/tex_bot_contexts.py b/utils/tex_bot_contexts.py index 39703664..88ce8753 100644 --- a/utils/tex_bot_contexts.py +++ b/utils/tex_bot_contexts.py @@ -5,14 +5,17 @@ rather than Pycord's default Bot class. """ -from collections.abc import Sequence +from typing import TYPE_CHECKING -__all__: Sequence[str] = ("TeXBotAutocompleteContext", "TeXBotApplicationContext") +import discord +if TYPE_CHECKING: + from collections.abc import Sequence + + from utils.tex_bot import TeXBot -import discord -from utils.tex_bot import TeXBot +__all__: "Sequence[str]" = ("TeXBotApplicationContext", "TeXBotAutocompleteContext") class TeXBotAutocompleteContext(discord.AutocompleteContext): @@ -24,7 +27,7 @@ class TeXBotAutocompleteContext(discord.AutocompleteContext): should be used in cogs instead. """ - bot: TeXBot + bot: "TeXBot" class TeXBotApplicationContext(discord.ApplicationContext): @@ -36,4 +39,4 @@ class TeXBotApplicationContext(discord.ApplicationContext): should be used in cogs instead. """ - bot: TeXBot + bot: "TeXBot" From 989265cb55994fd1b360cb254e28a99867e6fc27 Mon Sep 17 00:00:00 2001 From: Matt Norton Date: Sun, 24 Nov 2024 23:05:10 +0000 Subject: [PATCH 4/5] Fix mypy errors and use ruff format --- cogs/annual_handover_and_reset.py | 16 +-- cogs/archive.py | 45 ++++---- cogs/command_error.py | 8 +- cogs/committee_actions_tracking.py | 122 ++++++++++++++------ cogs/delete_all.py | 4 +- cogs/edit_message.py | 12 +- cogs/get_token_authorisation.py | 3 +- cogs/induct.py | 51 +++++---- cogs/kill.py | 12 +- cogs/make_applicant.py | 22 +++- cogs/make_member.py | 12 +- cogs/remind_me.py | 47 +++++--- cogs/send_get_roles_reminders.py | 4 +- cogs/send_introduction_reminders.py | 38 +++++-- cogs/source.py | 1 - cogs/startup.py | 10 +- cogs/stats.py | 14 ++- cogs/strike.py | 167 +++++++++++++++++++--------- cogs/write_roles.py | 1 - config.py | 58 +++++----- db/_settings.py | 3 +- db/core/__init__.py | 1 - db/core/app_config.py | 1 - db/core/models/__init__.py | 19 ++-- db/core/models/managers.py | 44 ++++++-- db/core/models/utils.py | 34 +++++- exceptions/base.py | 14 +-- exceptions/config_changes.py | 4 +- exceptions/does_not_exist.py | 4 +- exceptions/guild.py | 2 +- exceptions/messages.py | 7 +- main.py | 4 +- poetry.lock | 6 +- tests/test_utils.py | 9 +- utils/__init__.py | 4 +- utils/command_checks.py | 4 +- utils/error_capture_decorators.py | 42 +++---- utils/message_sender_components.py | 13 ++- utils/suppress_traceback.py | 7 +- utils/tex_bot.py | 54 +++++---- utils/tex_bot_base_cog.py | 25 ++++- 41 files changed, 618 insertions(+), 330 deletions(-) diff --git a/cogs/annual_handover_and_reset.py b/cogs/annual_handover_and_reset.py index b9ecbd43..2a67e1a3 100644 --- a/cogs/annual_handover_and_reset.py +++ b/cogs/annual_handover_and_reset.py @@ -58,7 +58,7 @@ async def committee_handover(self, ctx: "TeXBotApplicationContext") -> None: logger.debug("Running the handover command!") HANDOVER_AUDIT_MESSAGE: Final[str] = ( - f"{ctx.user} used TeX-Bot slash-command: \"/committee-handover\"" + f'{ctx.user} used TeX-Bot slash-command: "/committee-handover"' ) if main_guild.me.top_role.position < committee_role.position: @@ -69,7 +69,7 @@ async def committee_handover(self, ctx: "TeXBotApplicationContext") -> None: await initial_response.edit( content=( ":warning: This command requires TeX-Bot to hold a role higher than " - "that of the \"Committee\" role to perform this action. Operation aborted." + 'that of the "Committee" role to perform this action. Operation aborted.' " :warning:" ), ) @@ -95,7 +95,7 @@ async def committee_handover(self, ctx: "TeXBotApplicationContext") -> None: await initial_response.edit( content=( ":hourglass: Giving committee users access to the #handover channel and " - "removing the \"Committee\" role... :hourglass:" + 'removing the "Committee" role... :hourglass:' ), ) @@ -133,8 +133,8 @@ async def committee_handover(self, ctx: "TeXBotApplicationContext") -> None: await initial_response.edit( content=( - ":hourglass: Giving committee-elect users the \"Committee\" role " - "and removing their \"Committee-Elect\" role... :hourglass:" + ':hourglass: Giving committee-elect users the "Committee" role ' + 'and removing their "Committee-Elect" role... :hourglass:' ), ) @@ -197,7 +197,7 @@ async def annual_roles_reset(self, ctx: "TeXBotApplicationContext") -> None: ) ROLE_RESET_AUDIT_MESSAGE: Final[str] = ( - f"{ctx.user} used TeX-Bot slash-command: \"/annual_roles_reset\"" + f'{ctx.user} used TeX-Bot slash-command: "/annual_roles_reset"' ) member: discord.Member @@ -273,7 +273,7 @@ async def increment_year_channels(self, ctx: "TeXBotApplicationContext") -> None if final_year_channel: await initial_message.edit( - content=":hourglass: Archiving \"final-years\" channel... :hourglass:", + content=':hourglass: Archiving "final-years" channel... :hourglass:', ) archivist_role: discord.Role = await self.bot.archivist_role @@ -318,7 +318,7 @@ async def increment_year_channels(self, ctx: "TeXBotApplicationContext") -> None await initial_message.edit( content=( - ":hourglass: Creating new \"first-years\" channel and setting permissions... " + ':hourglass: Creating new "first-years" channel and setting permissions... ' ":hourglass:" ), ) diff --git a/cogs/archive.py b/cogs/archive.py index 3ee5d117..9b599cc3 100644 --- a/cogs/archive.py +++ b/cogs/archive.py @@ -34,7 +34,9 @@ class ArchiveCommandCog(TeXBotBaseCog): """Cog class that defines the "/archive" command and its call-back method.""" @staticmethod - async def autocomplete_get_categories(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 + async def autocomplete_get_categories( + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """ Autocomplete callable that generates the set of available selectable categories. @@ -57,8 +59,7 @@ async def autocomplete_get_categories(ctx: "TeXBotAutocompleteContext") -> "Abst return { discord.OptionChoice(name=category.name, value=str(category.id)) - for category - in main_guild.categories + for category in main_guild.categories if category.permissions_for(interaction_user).is_superset( discord.Permissions(send_messages=True, view_channel=True), ) @@ -127,64 +128,64 @@ async def archive(self, ctx: "TeXBotApplicationContext", str_category_id: str) - channel: AllChannelTypes for channel in category.channels: try: - CHANNEL_NEEDS_COMMITTEE_ARCHIVING: bool = ( - channel.permissions_for(committee_role).is_superset( - discord.Permissions(view_channel=True), - ) and not channel.permissions_for(guest_role).is_superset( - discord.Permissions(view_channel=True), - ) + CHANNEL_NEEDS_COMMITTEE_ARCHIVING: bool = channel.permissions_for( + committee_role + ).is_superset( + discord.Permissions(view_channel=True), + ) and not channel.permissions_for(guest_role).is_superset( + discord.Permissions(view_channel=True), ) - CHANNEL_NEEDS_NORMAL_ARCHIVING: bool = ( - channel.permissions_for(guest_role).is_superset( - discord.Permissions(view_channel=True), - ) + CHANNEL_NEEDS_NORMAL_ARCHIVING: bool = channel.permissions_for( + guest_role + ).is_superset( + discord.Permissions(view_channel=True), ) if CHANNEL_NEEDS_COMMITTEE_ARCHIVING: await channel.set_permissions( everyone_role, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', view_channel=False, ) await channel.set_permissions( guest_role, overwrite=None, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', ) await channel.set_permissions( member_role, overwrite=None, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', ) await channel.set_permissions( committee_role, overwrite=None, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', ) elif CHANNEL_NEEDS_NORMAL_ARCHIVING: await channel.set_permissions( everyone_role, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', view_channel=False, ) await channel.set_permissions( guest_role, overwrite=None, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', ) await channel.set_permissions( member_role, overwrite=None, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', ) await channel.set_permissions( committee_role, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', view_channel=False, ) await channel.set_permissions( archivist_role, - reason=f"{interaction_member.display_name} used \"/archive\".", + reason=f'{interaction_member.display_name} used "/archive".', view_channel=True, ) diff --git a/cogs/command_error.py b/cogs/command_error.py index 4bd9e271..a1f8cf64 100644 --- a/cogs/command_error.py +++ b/cogs/command_error.py @@ -31,7 +31,9 @@ class CommandErrorCog(TeXBotBaseCog): """Cog class that defines additional code to execute upon a command error.""" @TeXBotBaseCog.listener() - async def on_application_command_error(self, ctx: "TeXBotApplicationContext", error: discord.ApplicationCommandError) -> None: # noqa: E501 + async def on_application_command_error( + self, ctx: "TeXBotApplicationContext", error: discord.ApplicationCommandError + ) -> None: """Log any major command errors in the logging channel & stderr.""" error_code: str | None = None message: str | None = "Please contact a committee member." @@ -70,7 +72,9 @@ async def on_application_command_error(self, ctx: "TeXBotApplicationContext", er logging_message=logging_message, ) - if isinstance(error, discord.ApplicationCommandInvokeError) and isinstance(error.original, GuildDoesNotExistError): # noqa: E501 + if isinstance(error, discord.ApplicationCommandInvokeError) and isinstance( + error.original, GuildDoesNotExistError + ): command_name: str = ( ctx.command.callback.__name__ if ( diff --git a/cogs/committee_actions_tracking.py b/cogs/committee_actions_tracking.py index c0a05c1e..90004a76 100644 --- a/cogs/committee_actions_tracking.py +++ b/cogs/committee_actions_tracking.py @@ -53,7 +53,9 @@ class Status(Enum): class CommitteeActionsTrackingBaseCog(TeXBotBaseCog): """Base cog class that defines methods for committee actions tracking.""" - async def _create_action(self, ctx: "TeXBotApplicationContext", action_user: discord.Member, description: str) -> AssignedCommitteeAction | None: # noqa: E501 + async def _create_action( + self, ctx: "TeXBotApplicationContext", action_user: discord.Member, description: str + ) -> AssignedCommitteeAction | None: """ Create the action object with the given description for the given user. @@ -119,7 +121,9 @@ class CommitteeActionsTrackingSlashCommandsCog(CommitteeActionsTrackingBaseCog): ) @staticmethod - async def autocomplete_get_committee_members(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 + async def autocomplete_get_committee_members( + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """Autocomplete callable that generates a set of selectable committee members.""" try: committee_role: discord.Role = await ctx.bot.committee_role @@ -128,11 +132,14 @@ async def autocomplete_get_committee_members(ctx: "TeXBotAutocompleteContext") - return { discord.OptionChoice(name=str(member), value=str(member.id)) - for member in committee_role.members if not member.bot + for member in committee_role.members + if not member.bot } @staticmethod - async def autocomplete_get_user_action_ids(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 + async def autocomplete_get_user_action_ids( + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """Autocomplete callable that provides a set of actions that belong to the user.""" if not ctx.interaction.user: logger.debug("User actions autocomplete did not have an interaction user!!") @@ -164,20 +171,22 @@ async def autocomplete_get_user_action_ids(ctx: "TeXBotAutocompleteContext") -> discord.OptionChoice(name=action.description, value=str(action.id)) async for action in await AssignedCommitteeAction.objects.afilter( ( - Q(status=Status.IN_PROGRESS.value) | - Q(status=Status.BLOCKED.value) | - Q(status=Status.NOT_STARTED.value) + Q(status=Status.IN_PROGRESS.value) + | Q(status=Status.BLOCKED.value) + | Q(status=Status.NOT_STARTED.value) ), discord_id=int(interaction_user.id), ) } @staticmethod - async def autocomplete_get_action_status(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501, ARG004 + async def autocomplete_get_action_status( + ctx: "TeXBotAutocompleteContext", # noqa: ARG004 + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """Autocomplete callable that provides the set of possible Status' of actions.""" - status_options: Sequence[tuple[str, str]] = ( - AssignedCommitteeAction._meta.get_field("status").choices # type: ignore[assignment] - ) + status_options: Sequence[tuple[str, str]] = AssignedCommitteeAction._meta.get_field( + "status" + ).choices # type: ignore[assignment] if not status_options: logger.error("The autocomplete could not find any action Status'!") @@ -207,7 +216,13 @@ async def autocomplete_get_action_status(ctx: "TeXBotAutocompleteContext") -> "A ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def create(self, ctx: "TeXBotApplicationContext", action_description: str, *, action_member_id: str | None) -> None: # noqa: E501 + async def create( + self, + ctx: "TeXBotApplicationContext", + action_description: str, + *, + action_member_id: str | None, + ) -> None: """ Definition and callback response of the "create" command. @@ -240,7 +255,8 @@ async def create(self, ctx: "TeXBotApplicationContext", action_description: str, content=f"Action `{action_description}` created for: {action_user.mention}", ) except ( - InvalidActionDescriptionError, InvalidActionTargetError, + InvalidActionDescriptionError, + InvalidActionTargetError, ) as creation_failed_error: await ctx.respond(content=creation_failed_error.message) @@ -266,7 +282,9 @@ async def create(self, ctx: "TeXBotApplicationContext", action_description: str, ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def update_status(self, ctx: "TeXBotApplicationContext", action_id: str, status: str) -> None: # noqa: E501 + async def update_status( + self, ctx: "TeXBotApplicationContext", action_id: str, status: str + ) -> None: """ Definition and callback of the "update-status" command. @@ -337,7 +355,9 @@ async def update_status(self, ctx: "TeXBotApplicationContext", action_id: str, s required=True, parameter_name="action_description", ) - async def update_description(self, ctx: "TeXBotApplicationContext", action_id: str, new_description: str) -> None: # noqa: E501 + async def update_description( + self, ctx: "TeXBotApplicationContext", action_id: str, new_description: str + ) -> None: """ Definition and callback response of the "update-description" command. @@ -391,7 +411,9 @@ async def update_description(self, ctx: "TeXBotApplicationContext", action_id: s ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def action_random_user(self, ctx: "TeXBotApplicationContext", action_description: str) -> None: # noqa: E501 + async def action_random_user( + self, ctx: "TeXBotApplicationContext", action_description: str + ) -> None: """ Definition and callback response of the "action-random-user" command. @@ -429,7 +451,8 @@ async def action_random_user(self, ctx: "TeXBotApplicationContext", action_descr content=f"Action `{action_description}` created for: {action_user.mention}", ) except ( - InvalidActionTargetError, InvalidActionDescriptionError, + InvalidActionTargetError, + InvalidActionDescriptionError, ) as creation_failed_error: await ctx.respond(content=creation_failed_error.message) return @@ -447,7 +470,9 @@ async def action_random_user(self, ctx: "TeXBotApplicationContext", action_descr ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def action_all_committee(self, ctx: "TeXBotApplicationContext", action_description: str) -> None: # noqa: E501 + async def action_all_committee( + self, ctx: "TeXBotApplicationContext", action_description: str + ) -> None: """ Definition and callback response of the "action-all-committee" command. @@ -473,7 +498,8 @@ async def action_all_committee(self, ctx: "TeXBotApplicationContext", action_des ) success_members.append(committee_member) except ( - InvalidActionDescriptionError, InvalidActionTargetError, + InvalidActionDescriptionError, + InvalidActionTargetError, ) as creation_failed_error: failed_members += creation_failed_error.message + "\n" @@ -530,7 +556,14 @@ async def action_all_committee(self, ctx: "TeXBotApplicationContext", action_des ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def list_user_actions(self, ctx: "TeXBotApplicationContext", *, action_member_id: str, ping: bool, status: str) -> None: # noqa: E501 + async def list_user_actions( + self, + ctx: "TeXBotApplicationContext", + *, + action_member_id: str, + ping: bool, + status: str, + ) -> None: """ Definition and callback of the "/list" command. @@ -549,18 +582,20 @@ async def list_user_actions(self, ctx: "TeXBotApplicationContext", *, action_mem if not status: user_actions = [ - action async for action in await AssignedCommitteeAction.objects.afilter( + action + async for action in await AssignedCommitteeAction.objects.afilter( ( - Q(status=Status.IN_PROGRESS.value) | - Q(status=Status.BLOCKED.value) | - Q(status=Status.NOT_STARTED.value) + Q(status=Status.IN_PROGRESS.value) + | Q(status=Status.BLOCKED.value) + | Q(status=Status.NOT_STARTED.value) ), discord_id=int(action_member.id), ) ] else: user_actions = [ - action async for action in await AssignedCommitteeAction.objects.afilter( + action + async for action in await AssignedCommitteeAction.objects.afilter( status=status, discord_id=int(action_member.id), ) @@ -608,7 +643,9 @@ async def list_user_actions(self, ctx: "TeXBotApplicationContext", *, action_mem ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def reassign_action(self, ctx:"TeXBotApplicationContext", action_id: str, member_id: str) -> None: # noqa: E501 + async def reassign_action( + self, ctx: "TeXBotApplicationContext", action_id: str, member_id: str + ) -> None: """Reassign the specified action to the specified user.""" try: action_id_int: int = int(action_id) @@ -660,7 +697,8 @@ async def reassign_action(self, ctx:"TeXBotApplicationContext", action_id: str, ), ) except ( - InvalidActionDescriptionError, InvalidActionTargetError, + InvalidActionDescriptionError, + InvalidActionTargetError, ) as invalid_description_error: await ctx.respond(content=invalid_description_error.message) return @@ -687,25 +725,32 @@ async def reassign_action(self, ctx:"TeXBotApplicationContext", action_id: str, ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def list_all_actions(self, ctx:"TeXBotApplicationContext", *, ping: bool, status: str) -> None: # noqa: E501 - """List all actions.""" # NOTE: this doesn't actually list *all* actions as it is possible for non-committee to be actioned. + async def list_all_actions( + self, ctx: "TeXBotApplicationContext", *, ping: bool, status: str + ) -> None: + """List all actions.""" # NOTE: this doesn't actually list *all* actions as it is possible for non-committee to be actioned. committee_role: discord.Role = await self.bot.committee_role actions: list[AssignedCommitteeAction] = [ action async for action in AssignedCommitteeAction.objects.select_related().all() ] - desired_status: list[str] = [status] if status else [ - Status.NOT_STARTED.value, - Status.IN_PROGRESS.value, - Status.BLOCKED.value, - ] + desired_status: list[str] = ( + [status] + if status + else [ + Status.NOT_STARTED.value, + Status.IN_PROGRESS.value, + Status.BLOCKED.value, + ] + ) committee_members: list[discord.Member] = committee_role.members committee_actions: dict[discord.Member, list[AssignedCommitteeAction]] = { committee: [ - action for action in actions + action + for action in actions if str(action.discord_member) == DiscordMember.hash_discord_id(committee.id) # type: ignore[has-type] and action.status in desired_status ] @@ -787,7 +832,9 @@ class CommitteeActionsTrackingContextCommandsCog(CommitteeActionsTrackingBaseCog ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def action_message_author(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 + async def action_message_author( + self, ctx: "TeXBotApplicationContext", message: discord.Message + ) -> None: """ Definition and callback response of the "action-message-author" message command. @@ -817,6 +864,7 @@ async def action_message_author(self, ctx: "TeXBotApplicationContext", message: f"for: {actioned_message_user.mention}", ) except ( - InvalidActionTargetError, InvalidActionDescriptionError, + InvalidActionTargetError, + InvalidActionDescriptionError, ) as creation_failure_error: await ctx.respond(content=creation_failure_error.message) diff --git a/cogs/delete_all.py b/cogs/delete_all.py index 0cc588d2..ed51fd64 100644 --- a/cogs/delete_all.py +++ b/cogs/delete_all.py @@ -27,7 +27,9 @@ class DeleteAllCommandsCog(TeXBotBaseCog): ) @staticmethod - async def _delete_all(ctx: "TeXBotApplicationContext", delete_model: type["AsyncBaseModel"]) -> None: # noqa: E501 + async def _delete_all( + ctx: "TeXBotApplicationContext", delete_model: type["AsyncBaseModel"] + ) -> None: """Perform the actual deletion process of all instances of the given model class.""" # noinspection PyProtectedMember await delete_model._default_manager.all().adelete() diff --git a/cogs/edit_message.py b/cogs/edit_message.py index 89861a7f..13181ae0 100644 --- a/cogs/edit_message.py +++ b/cogs/edit_message.py @@ -29,7 +29,9 @@ class EditMessageCommandCog(TeXBotBaseCog): """Cog class that defines the "/edit-message" command and its call-back method.""" @staticmethod - async def autocomplete_get_text_channels(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 + async def autocomplete_get_text_channels( + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """ Autocomplete callable that generates the set of available selectable channels. @@ -80,7 +82,13 @@ async def autocomplete_get_text_channels(ctx: "TeXBotAutocompleteContext") -> "A ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def edit_message(self, ctx: "TeXBotApplicationContext", str_channel_id: str, str_message_id: str, new_message_content: str) -> None: # noqa: E501 + async def edit_message( + self, + ctx: "TeXBotApplicationContext", + str_channel_id: str, + str_message_id: str, + new_message_content: str, + ) -> None: """ Definition & callback response of the "edit_message" command. diff --git a/cogs/get_token_authorisation.py b/cogs/get_token_authorisation.py index 3b1ab90f..f60fa9f7 100644 --- a/cogs/get_token_authorisation.py +++ b/cogs/get_token_authorisation.py @@ -123,7 +123,8 @@ async def get_token_authorisation(self, ctx: "TeXBotApplicationContext") -> None organisation for organisation in organisations )}", ephemeral=bool( - (not guest_role) or ctx.channel.permissions_for(guest_role).is_superset( + (not guest_role) + or ctx.channel.permissions_for(guest_role).is_superset( discord.Permissions(view_channel=True), ) ), diff --git a/cogs/induct.py b/cogs/induct.py index 0a687498..ef7a429e 100644 --- a/cogs/induct.py +++ b/cogs/induct.py @@ -114,7 +114,7 @@ async def on_member_update(self, before: discord.Member, after: discord.Member) "optional roles like pronouns and year groups\n" "3. Change your nickname to whatever you wish others to refer to you as " "(You can do this by right-clicking your name in the members-list " - "to the right & selecting \"Edit Server Profile\").", + 'to the right & selecting "Edit Server Profile").', ) if user_type != "member": await after.send( @@ -141,7 +141,9 @@ class BaseInductCog(TeXBotBaseCog): child user-induction cog container classes. """ - async def get_random_welcome_message(self, induction_member: discord.User | discord.Member | None = None) -> str: # noqa: E501 + async def get_random_welcome_message( + self, induction_member: discord.User | discord.Member | None = None + ) -> str: """Get & format a random welcome message.""" random_welcome_message: str = random.choice(tuple(settings["WELCOME_MESSAGES"])) # noqa: S311 @@ -182,8 +184,13 @@ async def get_random_welcome_message(self, induction_member: discord.User | disc return random_welcome_message.strip() - - async def _perform_induction(self, ctx: "TeXBotApplicationContext", induction_member: discord.Member, *, silent: bool) -> None: # noqa: E501 + async def _perform_induction( + self, + ctx: "TeXBotApplicationContext", + induction_member: discord.Member, + *, + silent: bool, + ) -> None: """Perform the actual process of inducting a member by giving them the Guest role.""" # NOTE: Shortcut accessors are placed at the top of the function, so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = self.bot.main_guild @@ -191,9 +198,7 @@ async def _perform_induction(self, ctx: "TeXBotApplicationContext", induction_me logger.debug("Inducting member %s, silent=%s", induction_member, silent) - INDUCT_AUDIT_MESSAGE: Final[str] = ( - f"{ctx.user} used TeX Bot slash-command: \"/induct\"" - ) + INDUCT_AUDIT_MESSAGE: Final[str] = f'{ctx.user} used TeX Bot slash-command: "/induct"' intro_channel: discord.TextChannel | None = discord.utils.get( main_guild.text_channels, @@ -282,7 +287,9 @@ class InductSlashCommandCog(BaseInductCog): """Cog class that defines the "/induct" command and its call-back method.""" @staticmethod - async def autocomplete_get_members(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 + async def autocomplete_get_members( + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """ Autocomplete callable that generates the set of available selectable members. @@ -297,8 +304,7 @@ async def autocomplete_get_members(ctx: "TeXBotAutocompleteContext") -> "Abstrac members: set[discord.Member] = { member - for member - in main_guild.members + for member in main_guild.members if not member.bot and guest_role not in member.roles } @@ -335,7 +341,9 @@ async def autocomplete_get_members(ctx: "TeXBotAutocompleteContext") -> "Abstrac ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def induct(self, ctx: "TeXBotApplicationContext", str_induct_member_id: str, *, silent: bool) -> None: # noqa: E501 + async def induct( + self, ctx: "TeXBotApplicationContext", str_induct_member_id: str, *, silent: bool + ) -> None: """ Definition & callback response of the "induct" command. @@ -360,7 +368,9 @@ class InductContextCommandsCog(BaseInductCog): @discord.user_command(name="Induct User") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def non_silent_user_induct(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 + async def non_silent_user_induct( + self, ctx: "TeXBotApplicationContext", member: discord.Member + ) -> None: """ Definition & callback response of the "non_silent_induct" user-context-command. @@ -371,11 +381,12 @@ async def non_silent_user_induct(self, ctx: "TeXBotApplicationContext", member: """ await self._perform_induction(ctx, member, silent=False) - @discord.user_command(name="Silently Induct User") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def silent_user_induct(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 + async def silent_user_induct( + self, ctx: "TeXBotApplicationContext", member: discord.Member + ) -> None: """ Definition & callback response of the "silent_induct" user-context-command. @@ -386,11 +397,12 @@ async def silent_user_induct(self, ctx: "TeXBotApplicationContext", member: disc """ await self._perform_induction(ctx, member, silent=True) - @discord.message_command(name="Induct Message Author") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def non_silent_message_induct(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 + async def non_silent_message_induct( + self, ctx: "TeXBotApplicationContext", message: discord.Message + ) -> None: """ Definition and callback response of the "non_silent_induct" message-context-command. @@ -416,11 +428,12 @@ async def non_silent_message_induct(self, ctx: "TeXBotApplicationContext", messa await self._perform_induction(ctx, member, silent=False) - @discord.message_command(name="Silently Induct Message Author") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def silent_message_induct(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 + async def silent_message_induct( + self, ctx: "TeXBotApplicationContext", message: discord.Message + ) -> None: """ Definition and callback response of the "silent_induct" message-context-command. @@ -484,7 +497,7 @@ async def ensure_members_inducted(self, ctx: "TeXBotApplicationContext") -> None await member.add_roles( guest_role, reason=( - f"{ctx.user} used TeX Bot slash-command: \"/ensure-members-inducted\"" + f'{ctx.user} used TeX Bot slash-command: "/ensure-members-inducted"' ), ) diff --git a/cogs/kill.py b/cogs/kill.py index 86e12909..24fe229e 100644 --- a/cogs/kill.py +++ b/cogs/kill.py @@ -30,18 +30,22 @@ class ConfirmKillView(View): style=discord.ButtonStyle.red, custom_id="shutdown_confirm", ) - async def confirm_shutdown_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def confirm_shutdown_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """When the shutdown button is pressed, delete the message.""" - logger.debug("\"Confirm\" button pressed. %s", interaction) + logger.debug('"Confirm" button pressed. %s', interaction) @discord.ui.button( # type: ignore[misc] label="CANCEL", style=discord.ButtonStyle.grey, custom_id="shutdown_cancel", ) - async def cancel_shutdown_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def cancel_shutdown_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """When the cancel button is pressed, delete the message.""" - logger.debug("\"Cancel\" button pressed. %s", interaction) + logger.debug('"Cancel" button pressed. %s', interaction) class KillCommandCog(TeXBotBaseCog): diff --git a/cogs/make_applicant.py b/cogs/make_applicant.py index 96854b63..3b173c77 100644 --- a/cogs/make_applicant.py +++ b/cogs/make_applicant.py @@ -32,7 +32,9 @@ class BaseMakeApplicantCog(TeXBotBaseCog): child cog container classes. """ - async def _perform_make_applicant(self, ctx: "TeXBotApplicationContext", applicant_member: discord.Member) -> None: # noqa: E501 + async def _perform_make_applicant( + self, ctx: "TeXBotApplicationContext", applicant_member: discord.Member + ) -> None: """Perform the actual process of making the user into a group-applicant.""" # NOTE: Shortcut accessors are placed at the top of the function, so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = ctx.bot.main_guild @@ -52,7 +54,7 @@ async def _perform_make_applicant(self, ctx: "TeXBotApplicationContext", applica ephemeral=True, ) - AUDIT_MESSAGE: Final[str] = f"{ctx.user} used TeX-Bot Command \"Make User Applicant\"" + AUDIT_MESSAGE: Final[str] = f'{ctx.user} used TeX-Bot Command "Make User Applicant"' if guest_role in applicant_member.roles: await applicant_member.remove_roles(guest_role, reason=AUDIT_MESSAGE) @@ -97,7 +99,9 @@ class MakeApplicantSlashCommandCog(BaseMakeApplicantCog): """Cog class that defines the "/make_applicant" slash-command.""" @staticmethod - async def autocomplete_get_members(ctx: "TeXBotApplicationContext") -> set[discord.OptionChoice]: # noqa: E501 + async def autocomplete_get_members( + ctx: "TeXBotApplicationContext", + ) -> set[discord.OptionChoice]: """ Autocomplete callable that generates the set of available selectable members. @@ -140,7 +144,9 @@ async def autocomplete_get_members(ctx: "TeXBotApplicationContext") -> set[disco ) @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def make_applicant(self, ctx: "TeXBotApplicationContext", str_applicant_member_id: str) -> None: # noqa: E501 + async def make_applicant( + self, ctx: "TeXBotApplicationContext", str_applicant_member_id: str + ) -> None: """ Definition & callback response of the "make_applicant" command. @@ -165,7 +171,9 @@ class MakeApplicantContextCommandsCog(BaseMakeApplicantCog): @discord.user_command(name="Make Applicant") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def user_make_applicant(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 + async def user_make_applicant( + self, ctx: "TeXBotApplicationContext", member: discord.Member + ) -> None: """ Definition and callback response of the "make_applicant" user-context-command. @@ -178,7 +186,9 @@ async def user_make_applicant(self, ctx: "TeXBotApplicationContext", member: dis @discord.message_command(name="Make Message Author Applicant") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def message_make_applicant(self, ctx: "TeXBotApplicationContext", message: discord.Message) -> None: # noqa: E501 + async def message_make_applicant( + self, ctx: "TeXBotApplicationContext", message: discord.Message + ) -> None: """ Definition of the "message_make_applicant" message-context-command. diff --git a/cogs/make_member.py b/cogs/make_member.py index b68bcd01..178d8711 100644 --- a/cogs/make_member.py +++ b/cogs/make_member.py @@ -234,7 +234,7 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st # NOTE: The "Member" role must be added to the user **before** the "Guest" role to ensure that the welcome message does not include the suggestion to purchase membership await interaction_member.add_roles( member_role, - reason="TeX Bot slash-command: \"/makemember\"", + reason='TeX Bot slash-command: "/makemember"', ) try: @@ -258,15 +258,15 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st guest_role: discord.Role = await self.bot.guest_role except GuestRoleDoesNotExistError: logger.warning( - "\"/makemember\" command used but the \"Guest\" role does not exist. " - "Some user's may now have the \"Member\" role without the \"Guest\" role. " - "Use the \"/ensure-members-inducted\" command to fix this issue.", + '"/makemember" command used but the "Guest" role does not exist. ' + 'Some user\'s may now have the "Member" role without the "Guest" role. ' + 'Use the "/ensure-members-inducted" command to fix this issue.', ) else: if guest_role not in interaction_member.roles: await interaction_member.add_roles( guest_role, - reason="TeX Bot slash-command: \"/makemember\"", + reason='TeX Bot slash-command: "/makemember"', ) # noinspection PyUnusedLocal @@ -277,5 +277,5 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st if applicant_role and applicant_role in interaction_member.roles: await interaction_member.remove_roles( applicant_role, - reason="TeX Bot slash-command: \"/makemember\"", + reason='TeX Bot slash-command: "/makemember"', ) diff --git a/cogs/remind_me.py b/cogs/remind_me.py index 9f6021e6..72df107e 100644 --- a/cogs/remind_me.py +++ b/cogs/remind_me.py @@ -35,7 +35,9 @@ class RemindMeCommandCog(TeXBotBaseCog): """Cog class that defines the "/remind-me" command and its call-back method.""" @staticmethod - async def autocomplete_get_delays(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: PLR0912, PLR0915, E501 + async def autocomplete_get_delays( # noqa: PLR0912, PLR0915 + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """ Autocomplete callable that generates the common delay input values. @@ -139,38 +141,52 @@ async def autocomplete_get_delays(ctx: "TeXBotAutocompleteContext") -> "Abstract slice_size: int for slice_size in range(1, len(formatted_time_choice) + 1): - if match.group("ctx_time_choice").casefold() == formatted_time_choice[:slice_size]: # noqa: E501 + if ( + match.group("ctx_time_choice").casefold() + == formatted_time_choice[:slice_size] + ): delay_choices.add(formatted_time_choice[slice_size:]) elif match := re.fullmatch(r"\A(?P\d{1,2}) ?[/\-.] ?\Z", ctx.value): if 1 <= int(match.group("date")) <= 31: - FORMATTED_DAY_AND_JOINER_DATE_CHOICES: Final[Iterator[tuple[int, int, str]]] = ( # noqa: E501 - itertools.product( - range(1, 12), - range(current_year, current_year + 40), - ("/", " / ", "-", " - ", ".", " . "), - ) + FORMATTED_DAY_AND_JOINER_DATE_CHOICES: Final[ + Iterator[tuple[int, int, str]] + ] = itertools.product( + range(1, 12), + range(current_year, current_year + 40), + ("/", " / ", "-", " - ", ".", " . "), ) for month, year, joiner in FORMATTED_DAY_AND_JOINER_DATE_CHOICES: delay_choices.add(f"{month}{joiner}{year}") if month < 10: delay_choices.add(f"0{month}{joiner}{year}") - elif match := re.fullmatch(r"\A(?P\d{1,2}) ?[/\-.] ?(?P\d{1,2})\Z", ctx.value): # noqa: E501 + elif match := re.fullmatch( + r"\A(?P\d{1,2}) ?[/\-.] ?(?P\d{1,2})\Z", ctx.value + ): if 1 <= int(match.group("date")) <= 31 and 1 <= int(match.group("month")) <= 12: for year in range(current_year, current_year + 40): for joiner in ("/", " / ", "-", " - ", ".", " . "): delay_choices.add(f"{joiner}{year}") - elif match := re.fullmatch(r"\A(?P\d{1,2}) ?[/\-.] ?(?P\d{1,2}) ?[/\-.] ?\Z", ctx.value): # noqa: E501 + elif match := re.fullmatch( + r"\A(?P\d{1,2}) ?[/\-.] ?(?P\d{1,2}) ?[/\-.] ?\Z", ctx.value + ): if 1 <= int(match.group("date")) <= 31 and 1 <= int(match.group("month")) <= 12: for year in range(current_year, current_year + 40): delay_choices.add(f"{year}") - elif match := re.fullmatch(r"\A(?P\d{1,2}) ?[/\-.] ?(?P\d{1,2}) ?[/\-.] ?(?P\d{1,3})\Z", ctx.value): # noqa: E501 + elif match := re.fullmatch( + ( + r"\A(?P\d{1,2}) ?[/\-.] ?" + r"(?P\d{1,2}) ?[/\-.] ?" + r"(?P\d{1,3})\Z" + ), + ctx.value, + ): if 1 <= int(match.group("date")) <= 31 and 1 <= int(match.group("month")) <= 12: for year in range(current_year, current_year + 40): - delay_choices.add(f"{year}"[len(match.group("partial_year")):]) + delay_choices.add(f"{year}"[len(match.group("partial_year")) :]) return {f"{ctx.value}{delay_choice}".casefold() for delay_choice in delay_choices} @@ -191,7 +207,9 @@ async def autocomplete_get_delays(ctx: "TeXBotAutocompleteContext") -> "Abstract description="The message you want to be reminded with.", required=False, ) - async def remind_me(self, ctx: "TeXBotApplicationContext", delay: str, message: str) -> None: # noqa: E501 + async def remind_me( + self, ctx: "TeXBotApplicationContext", delay: str, message: str + ) -> None: """ Definition & callback response of the "remind_me" command. @@ -299,7 +317,8 @@ async def clear_reminders_backlog(self) -> None: functools.partial( lambda _user, _reminder: ( not _user.bot - and DiscordMember.hash_discord_id(_user.id) == _reminder.discord_member.hashed_discord_id # noqa: E501 + and DiscordMember.hash_discord_id(_user.id) + == _reminder.discord_member.hashed_discord_id ), _reminder=reminder, ), diff --git a/cogs/send_get_roles_reminders.py b/cogs/send_get_roles_reminders.py index f1290849..5810136f 100644 --- a/cogs/send_get_roles_reminders.py +++ b/cogs/send_get_roles_reminders.py @@ -154,7 +154,9 @@ async def send_get_roles_reminders(self) -> None: if time_since_role_received <= settings["SEND_GET_ROLES_REMINDERS_DELAY"]: continue - if member not in main_guild.members: # HACK: Caching errors can cause the member to no longer be part of the guild at this point, so this check must be performed before sending that member a message # noqa: FIX004 + if ( + member not in main_guild.members + ): # HACK: Caching errors can cause the member to no longer be part of the guild at this point, so this check must be performed before sending that member a message # noqa: FIX004 logger.info( ( "Member with ID: %s does not need to be sent a reminder " diff --git a/cogs/send_introduction_reminders.py b/cogs/send_introduction_reminders.py index 03b983c3..13ddd65e 100644 --- a/cogs/send_introduction_reminders.py +++ b/cogs/send_introduction_reminders.py @@ -137,12 +137,15 @@ async def send_introduction_reminders(self) -> None: bool(message.components) and isinstance(message.components[0], discord.ActionRow) and isinstance(message.components[0].children[0], discord.Button) - and message.components[0].children[0].custom_id == "opt_out_introduction_reminders_button" # noqa: E501 + and message.components[0].children[0].custom_id + == "opt_out_introduction_reminders_button" ) if MESSAGE_CONTAINS_OPT_IN_OUT_BUTTON: await message.edit(view=None) - if member not in main_guild.members: # HACK: Caching errors can cause the member to no longer be part of the guild at this point, so this check must be performed before sending that member a message # noqa: FIX004 + if ( + member not in main_guild.members + ): # HACK: Caching errors can cause the member to no longer be part of the guild at this point, so this check must be performed before sending that member a message # noqa: FIX004 logger.info( ( "Member with ID: %s does not need to be sent a reminder " @@ -192,11 +195,19 @@ class OptOutIntroductionRemindersView(View): @override def __init__(self, bot: "TeXBot") -> None: """Initialise a new discord.View, to opt-in/out of introduction reminders.""" - self.bot: TeXBot = bot # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 + self.bot: TeXBot = ( + bot # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 + ) super().__init__(timeout=None) - async def send_error(self, interaction: discord.Interaction, error_code: str | None = None, message: str | None = None, logging_message: str | BaseException | None = None) -> None: # noqa: E501 + async def send_error( + self, + interaction: discord.Interaction, + error_code: str | None = None, + message: str | None = None, + logging_message: str | BaseException | None = None, + ) -> None: """ Construct & format an error message from the given details. @@ -220,7 +231,9 @@ async def send_error(self, interaction: discord.Interaction, error_code: str | N emoji.emojize(":no_good:", language="alias"), ), ) - async def opt_out_introduction_reminders_button_callback(self, button: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def opt_out_introduction_reminders_button_callback( + self, button: discord.Button, interaction: discord.Interaction + ) -> None: """ Set the opt-in/out flag depending on the status of the button. @@ -235,7 +248,8 @@ async def opt_out_introduction_reminders_button_callback(self, button: discord.B BUTTON_WILL_MAKE_OPT_IN: Final[bool] = bool( button.style == discord.ButtonStyle.green - or str(button.emoji) == emoji.emojize( + or str(button.emoji) + == emoji.emojize( ":raised_hand:", language="alias", ) @@ -279,13 +293,15 @@ async def opt_out_introduction_reminders_button_callback(self, button: discord.B ) except ValidationError as create_introduction_reminder_opt_out_member_error: error_is_already_exists: bool = ( - "hashed_member_id" in create_introduction_reminder_opt_out_member_error.message_dict # noqa: E501 + "hashed_member_id" + in create_introduction_reminder_opt_out_member_error.message_dict and any( "already exists" in error - for error - in create_introduction_reminder_opt_out_member_error.message_dict[ - "hashed_member_id" - ] + for error in ( + create_introduction_reminder_opt_out_member_error.message_dict[ + "hashed_member_id" + ] + ) ) ) if not error_is_already_exists: diff --git a/cogs/source.py b/cogs/source.py index 8e4c8583..cb64daad 100644 --- a/cogs/source.py +++ b/cogs/source.py @@ -1,6 +1,5 @@ """Contains cog classes for any source interactions.""" - from typing import TYPE_CHECKING import discord diff --git a/cogs/startup.py b/cogs/startup.py index d1ef5aa9..c6b62bdd 100644 --- a/cogs/startup.py +++ b/cogs/startup.py @@ -79,8 +79,8 @@ async def on_ready(self) -> None: settings["_DISCORD_MAIN_GUILD_ID"], ), ) - logger.critical(GuildDoesNotExistError( - guild_id=settings["_DISCORD_MAIN_GUILD_ID"]), + logger.critical( + GuildDoesNotExistError(guild_id=settings["_DISCORD_MAIN_GUILD_ID"]), ) await self.bot.close() @@ -126,9 +126,9 @@ async def on_ready(self) -> None: ), repr(settings["STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION"]), ) - manual_moderation_warning_message_location_similar_to_dm: bool = ( - settings["STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION"].lower() in ("dm", "dms") # noqa: E501 - ) + manual_moderation_warning_message_location_similar_to_dm: bool = settings[ + "STRIKE_PERFORMED_MANUALLY_WARNING_LOCATION" + ].lower() in ("dm", "dms") if manual_moderation_warning_message_location_similar_to_dm: logger.info( ( diff --git a/cogs/stats.py b/cogs/stats.py index 147c014c..60260123 100644 --- a/cogs/stats.py +++ b/cogs/stats.py @@ -42,7 +42,15 @@ def amount_of_time_formatter(value: float, time_scale: str) -> str: return f"{value:.2f} {time_scale}s" -def plot_bar_chart(data: dict[str, int], x_label: str, y_label: str, title: str, filename: str, description: str, extra_text: str = "") -> discord.File: # noqa: E501 +def plot_bar_chart( + data: dict[str, int], + x_label: str, + y_label: str, + title: str, + filename: str, + description: str, + extra_text: str = "", +) -> discord.File: """Generate an image of a plot bar chart from the given data & format variables.""" plt.style.use("cyberpunk") @@ -178,7 +186,9 @@ class StatsCommandsCog(TeXBotBaseCog): required=False, parameter_name="str_channel_id", ) - async def channel_stats(self, ctx: "TeXBotApplicationContext", str_channel_id: str) -> None: # noqa: E501 + async def channel_stats( + self, ctx: "TeXBotApplicationContext", str_channel_id: str + ) -> None: """ Definition & callback response of the "channel_stats" command. diff --git a/cogs/strike.py b/cogs/strike.py index 400be22d..619f492c 100644 --- a/cogs/strike.py +++ b/cogs/strike.py @@ -69,7 +69,9 @@ } -async def perform_moderation_action(strike_user: discord.Member, strikes: int, committee_member: discord.Member | discord.User) -> None: # noqa: E501 +async def perform_moderation_action( + strike_user: discord.Member, strikes: int, committee_member: discord.Member | discord.User +) -> None: """ Perform the actual process of applying a moderation action to a member. @@ -108,7 +110,9 @@ class ConfirmStrikeMemberView(View): style=discord.ButtonStyle.red, custom_id="yes_strike_member", ) - async def yes_strike_member_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def yes_strike_member_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """ Delete the message associated with the view when the Yes button is pressed. @@ -117,15 +121,19 @@ async def yes_strike_member_button_callback(self, _: discord.Button, interaction The actual handling of the event is done by the command that sent the view, so all that is required is to delete the original message that sent this view. """ - logger.debug("\"Yes\" button pressed. %s", interaction) - await interaction.response.edit_message(view=None) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error + logger.debug('"Yes" button pressed. %s', interaction) + await interaction.response.edit_message( + view=None + ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error @discord.ui.button( # type: ignore[misc] label="No", style=discord.ButtonStyle.grey, custom_id="no_strike_member", ) - async def no_strike_member_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def no_strike_member_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """ Delete the message associated with the view when the No button is pressed. @@ -134,8 +142,10 @@ async def no_strike_member_button_callback(self, _: discord.Button, interaction: The actual handling of the event is done by the command that sent the view, so all that is required is to delete the original message that sent this view. """ - logger.debug("\"No\" button pressed. %s", interaction) - await interaction.response.edit_message(view=None) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error + logger.debug('"No" button pressed. %s', interaction) + await interaction.response.edit_message( + view=None + ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error class ConfirmManualModerationView(View): @@ -146,7 +156,9 @@ class ConfirmManualModerationView(View): style=discord.ButtonStyle.red, custom_id="yes_manual_moderation_action", ) - async def yes_manual_moderation_action_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def yes_manual_moderation_action_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """ Delete the message associated with the view when the Yes button is pressed. @@ -156,15 +168,19 @@ async def yes_manual_moderation_action_button_callback(self, _: discord.Button, the manual moderation tracker subroutine that sent the view, so all that is required is to delete the original message that sent this view. """ - logger.debug("\"Yes\" button pressed. %s", interaction) - await interaction.response.edit_message(view=None) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error + logger.debug('"Yes" button pressed. %s', interaction) + await interaction.response.edit_message( + view=None + ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error @discord.ui.button( # type: ignore[misc] label="No", style=discord.ButtonStyle.grey, custom_id="no_manual_moderation_action", ) - async def no_manual_moderation_action_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def no_manual_moderation_action_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """ Delete the message associated with the view when the No button is pressed. @@ -174,8 +190,10 @@ async def no_manual_moderation_action_button_callback(self, _: discord.Button, i the manual moderation tracker subroutine that sent the view, so all that is required is to delete the original message that sent this view. """ - logger.debug("\"No\" button pressed. %s", interaction) - await interaction.response.edit_message(view=None) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error + logger.debug('"No" button pressed. %s', interaction) + await interaction.response.edit_message( + view=None + ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error class ConfirmStrikesOutOfSyncWithBanView(View): @@ -186,7 +204,9 @@ class ConfirmStrikesOutOfSyncWithBanView(View): style=discord.ButtonStyle.red, custom_id="yes_out_of_sync_ban_member", ) - async def yes_out_of_sync_ban_member_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def yes_out_of_sync_ban_member_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """ Delete the message associated with the view when the Yes button is pressed. @@ -196,15 +216,19 @@ async def yes_out_of_sync_ban_member_button_callback(self, _: discord.Button, in the manual moderation tracker subroutine that sent the view, so all that is required is to delete the original message that sent this view. """ - logger.debug("\"Yes\" button pressed. %s", interaction) - await interaction.response.edit_message(view=None) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error + logger.debug('"Yes" button pressed. %s', interaction) + await interaction.response.edit_message( + view=None + ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error @discord.ui.button( # type: ignore[misc] label="No", style=discord.ButtonStyle.grey, custom_id="no_out_of_sync_ban_member", ) - async def no_out_of_sync_ban_member_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501 + async def no_out_of_sync_ban_member_button_callback( + self, _: discord.Button, interaction: discord.Interaction + ) -> None: """ Delete the message associated with the view when the No button is pressed. @@ -214,8 +238,10 @@ async def no_out_of_sync_ban_member_button_callback(self, _: discord.Button, int the manual moderation tracker subroutine that sent the view, so all that is required is to delete the original message that sent this view. """ - logger.debug("\"No\" button pressed. %s", interaction) - await interaction.response.edit_message(view=None) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error + logger.debug('"No" button pressed. %s', interaction) + await interaction.response.edit_message( + view=None + ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error class BaseStrikeCog(TeXBotBaseCog): @@ -228,7 +254,9 @@ class BaseStrikeCog(TeXBotBaseCog): SUGGESTED_ACTIONS: "Final[Mapping[int, str]]" = {1: "time-out", 2: "kick", 3: "ban"} # noqa: RUF012 - async def _send_strike_user_message(self, strike_user: discord.User | discord.Member, member_strikes: DiscordMemberStrikes) -> None: # noqa: E501 + async def _send_strike_user_message( + self, strike_user: discord.User | discord.Member, member_strikes: DiscordMemberStrikes + ) -> None: # noinspection PyUnusedLocal rules_channel_mention: str = "**`#welcome`**" with contextlib.suppress(RulesChannelDoesNotExistError): @@ -262,7 +290,15 @@ async def _send_strike_user_message(self, strike_user: discord.User | discord.Me "with you shortly, to discuss this further.", ) - async def _confirm_perform_moderation_action(self, message_sender_component: "MessageSavingSenderComponent", interaction_user: discord.User, strike_user: discord.Member, confirm_strike_message: str, actual_strike_amount: int, button_callback_channel: discord.TextChannel | discord.DMChannel) -> None: # noqa: E501 + async def _confirm_perform_moderation_action( + self, + message_sender_component: "MessageSavingSenderComponent", + interaction_user: discord.User, + strike_user: discord.Member, + confirm_strike_message: str, + actual_strike_amount: int, + button_callback_channel: discord.TextChannel | discord.DMChannel, + ) -> None: await message_sender_component.send( content=confirm_strike_message, view=ConfirmStrikeMemberView(), @@ -308,7 +344,16 @@ async def _confirm_perform_moderation_action(self, message_sender_component: "Me raise ValueError - async def _confirm_increase_strike(self, message_sender_component: "MessageSavingSenderComponent", interaction_user: discord.User, strike_user: discord.User | discord.Member, member_strikes: DiscordMemberStrikes, button_callback_channel: discord.TextChannel | discord.DMChannel, *, perform_action: bool) -> None: # noqa: E501 + async def _confirm_increase_strike( + self, + message_sender_component: "MessageSavingSenderComponent", + interaction_user: discord.User, + strike_user: discord.User | discord.Member, + member_strikes: DiscordMemberStrikes, + button_callback_channel: discord.TextChannel | discord.DMChannel, + *, + perform_action: bool, + ) -> None: if perform_action and isinstance(strike_user, discord.User): STRIKE_USER_TYPE_ERROR_MESSAGE: Final[str] = ( "Cannot perform moderation action on non-guild member." @@ -381,7 +426,9 @@ async def _confirm_increase_strike(self, message_sender_component: "MessageSavin button_callback_channel, ) - async def _command_perform_strike(self, ctx: "TeXBotApplicationContext", strike_member: discord.Member) -> None: # noqa: E501 + async def _command_perform_strike( + self, ctx: "TeXBotApplicationContext", strike_member: discord.Member + ) -> None: """ Perform the actual process of giving a member an additional strike. @@ -419,7 +466,9 @@ class ManualModerationCog(BaseStrikeCog): will be run to confirm the actions are tracked. """ - async def get_confirmation_message_channel(self, user: discord.User | discord.Member) -> discord.DMChannel | discord.TextChannel: # noqa: E501 + async def get_confirmation_message_channel( + self, user: discord.User | discord.Member + ) -> discord.DMChannel | discord.TextChannel: """ Retrieve the correct channel to send the strike confirmation message to. @@ -464,7 +513,9 @@ async def get_confirmation_message_channel(self, user: discord.User | discord.Me # noinspection PyTypeHints @capture_strike_tracking_error - async def _confirm_manual_add_strike(self, strike_user: discord.User | discord.Member, action: discord.AuditLogAction) -> None: # noqa: E501 + async def _confirm_manual_add_strike( + self, strike_user: discord.User | discord.Member, action: discord.AuditLogAction + ) -> None: # NOTE: Shortcut accessors are placed at the top of the function, so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = self.bot.main_guild committee_role: discord.Role = await self.bot.committee_role @@ -477,7 +528,8 @@ async def _confirm_manual_add_strike(self, strike_user: discord.User | discord.M after=discord.utils.utcnow() - datetime.timedelta(minutes=1), action=action, ) - if _audit_log_entry.target.id == strike_user.id # NOTE: IDs are checked here rather than the objects themselves as the audit log provides an unusual object type in some cases. + if _audit_log_entry.target.id + == strike_user.id # NOTE: IDs are checked here rather than the objects themselves as the audit log provides an unusual object type in some cases. ) except (StopIteration, StopAsyncIteration): logger.debug("Printing 5 most recent audit logs:") @@ -545,27 +597,29 @@ async def _confirm_manual_add_strike(self, strike_user: discord.User | discord.M view=ConfirmStrikesOutOfSyncWithBanView(), ) - out_of_sync_ban_button_interaction: discord.Interaction = ( - await self.bot.wait_for( - "interaction", - check=lambda interaction: ( - interaction.type == discord.InteractionType.component - and ( - (interaction.user == applied_action_user) - if not applied_action_user.bot - else (committee_role in interaction.user.roles) - ) - and interaction.channel == confirmation_message_channel - and "custom_id" in interaction.data - and interaction.data["custom_id"] in { - "yes_out_of_sync_ban_member", - "no_out_of_sync_ban_member", - } - ), - ) + out_of_sync_ban_button_interaction: discord.Interaction = await self.bot.wait_for( + "interaction", + check=lambda interaction: ( + interaction.type == discord.InteractionType.component + and ( + (interaction.user == applied_action_user) + if not applied_action_user.bot + else (committee_role in interaction.user.roles) + ) + and interaction.channel == confirmation_message_channel + and "custom_id" in interaction.data + and interaction.data["custom_id"] + in { + "yes_out_of_sync_ban_member", + "no_out_of_sync_ban_member", + } + ), ) - if out_of_sync_ban_button_interaction.data["custom_id"] == "no_out_of_sync_ban_member": # type: ignore[index, typeddict-item] # noqa: E501 + if ( + out_of_sync_ban_button_interaction.data["custom_id"] + == "no_out_of_sync_ban_member" + ): # type: ignore[index, typeddict-item] await out_of_sync_ban_confirmation_message.edit( content=( f"Aborted performing ban action upon {strike_user.mention}. " @@ -584,7 +638,10 @@ async def _confirm_manual_add_strike(self, strike_user: discord.User | discord.M await out_of_sync_ban_confirmation_message.delete() return - if out_of_sync_ban_button_interaction.data["custom_id"] == "yes_out_of_sync_ban_member": # type: ignore[index, typeddict-item] # noqa: E501 + if ( + out_of_sync_ban_button_interaction.data["custom_id"] + == "yes_out_of_sync_ban_member" + ): # type: ignore[index, typeddict-item] await self._send_strike_user_message(strike_user, member_strikes) await main_guild.ban( strike_user, @@ -647,7 +704,8 @@ async def _confirm_manual_add_strike(self, strike_user: discord.User | discord.M ) and interaction.channel == confirmation_message_channel and "custom_id" in interaction.data - and interaction.data["custom_id"] in { + and interaction.data["custom_id"] + in { "yes_manual_moderation_action", "no_manual_moderation_action", } @@ -710,7 +768,8 @@ async def on_member_update(self, before: discord.Member, after: discord.Member) async for audit_log_entry in main_guild.audit_logs(limit=5): FOUND_CORRECT_AUDIT_LOG_ENTRY: bool = ( audit_log_entry.target.id == after.id - and audit_log_entry.action == (discord.AuditLogAction.auto_moderation_user_communication_disabled) # noqa: E501 + and audit_log_entry.action + == (discord.AuditLogAction.auto_moderation_user_communication_disabled) ) if FOUND_CORRECT_AUDIT_LOG_ENTRY: await self._confirm_manual_add_strike( @@ -747,7 +806,9 @@ async def on_member_remove(self, member: discord.Member) -> None: @TeXBotBaseCog.listener() @capture_guild_does_not_exist_error - async def on_member_ban(self, guild: discord.Guild, user: discord.User | discord.Member) -> None: # noqa: E501 + async def on_member_ban( + self, guild: discord.Guild, user: discord.User | discord.Member + ) -> None: """Flag manually applied ban & track strikes accordingly.""" if guild != self.bot.main_guild or user.bot: return @@ -762,7 +823,9 @@ class StrikeCommandCog(BaseStrikeCog): """Cog class that defines the "/strike" command and its call-back method.""" @staticmethod - async def autocomplete_get_members(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 + async def autocomplete_get_members( + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """ Autocomplete callable that generates the set of available selectable members. @@ -830,6 +893,8 @@ class StrikeUserCommandCog(BaseStrikeCog): @discord.user_command(name="Strike User") # type: ignore[no-untyped-call, misc] @CommandChecks.check_interaction_user_has_committee_role @CommandChecks.check_interaction_user_in_main_guild - async def user_strike(self, ctx: "TeXBotApplicationContext", member: discord.Member) -> None: # noqa: E501 + async def user_strike( + self, ctx: "TeXBotApplicationContext", member: discord.Member + ) -> None: """Call the _strike command, providing the required command arguments.""" await self._command_perform_strike(ctx, member) diff --git a/cogs/write_roles.py b/cogs/write_roles.py index 4646aeb9..8fb760f5 100644 --- a/cogs/write_roles.py +++ b/cogs/write_roles.py @@ -1,6 +1,5 @@ """Contains cog classes for any write_roles interactions.""" - from typing import TYPE_CHECKING import discord diff --git a/config.py b/config.py index b42b669a..b72310dd 100644 --- a/config.py +++ b/config.py @@ -102,7 +102,9 @@ def __getattr__(self, item: str) -> "Any": # type: ignore[misc] # noqa: ANN401 f"{type(self).__name__!r} object has no attribute {item!r}" ) - if "_pytest" in item or item in ("__bases__", "__test__"): # NOTE: Overriding __getattr__() leads to many edge-case issues where external libraries will attempt to call getattr() with peculiar values + if ( + "_pytest" in item or item in ("__bases__", "__test__") + ): # NOTE: Overriding __getattr__() leads to many edge-case issues where external libraries will attempt to call getattr() with peculiar values raise AttributeError(MISSING_ATTRIBUTE_MESSAGE) if not self._is_env_variables_setup: @@ -178,8 +180,8 @@ def _setup_discord_bot_token(cls) -> None: @classmethod def _setup_discord_log_channel_webhook_url(cls) -> None: raw_discord_log_channel_webhook_url: str = os.getenv( - "DISCORD_LOG_CHANNEL_WEBHOOK_URL", - "", + "DISCORD_LOG_CHANNEL_WEBHOOK_URL", + "", ) DISCORD_LOG_CHANNEL_WEBHOOK_URL_IS_VALID: Final[bool] = bool( @@ -205,8 +207,7 @@ def _setup_discord_guild_id(cls) -> None: raw_discord_guild_id: str | None = os.getenv("DISCORD_GUILD_ID") DISCORD_GUILD_ID_IS_VALID: Final[bool] = bool( - raw_discord_guild_id - and re.fullmatch(r"\A\d{17,20}\Z", raw_discord_guild_id), + raw_discord_guild_id and re.fullmatch(r"\A\d{17,20}\Z", raw_discord_guild_id), ) if not DISCORD_GUILD_ID_IS_VALID: INVALID_DISCORD_GUILD_ID_MESSAGE: Final[str] = ( @@ -252,8 +253,7 @@ def _setup_purchase_membership_url(cls) -> None: raw_purchase_membership_url: str | None = os.getenv("PURCHASE_MEMBERSHIP_URL") PURCHASE_MEMBERSHIP_URL_IS_VALID: Final[bool] = bool( - not raw_purchase_membership_url - or validators.url(raw_purchase_membership_url), + not raw_purchase_membership_url or validators.url(raw_purchase_membership_url), ) if not PURCHASE_MEMBERSHIP_URL_IS_VALID: INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE: Final[str] = ( @@ -268,8 +268,7 @@ def _setup_membership_perks_url(cls) -> None: raw_membership_perks_url: str | None = os.getenv("MEMBERSHIP_PERKS_URL") MEMBERSHIP_PERKS_URL_IS_VALID: Final[bool] = bool( - not raw_membership_perks_url - or validators.url(raw_membership_perks_url), + not raw_membership_perks_url or validators.url(raw_membership_perks_url), ) if not MEMBERSHIP_PERKS_URL_IS_VALID: INVALID_MEMBERSHIP_PERKS_URL_MESSAGE: Final[str] = ( @@ -367,10 +366,9 @@ def _setup_roles_messages(cls) -> None: if "roles_messages" not in messages_dict: raise MessagesJSONFileMissingKeyError(missing_key="roles_messages") - ROLES_MESSAGES_KEY_IS_VALID: Final[bool] = ( - isinstance(messages_dict["roles_messages"], Iterable) - and bool(messages_dict["roles_messages"]) - ) + ROLES_MESSAGES_KEY_IS_VALID: Final[bool] = isinstance( + messages_dict["roles_messages"], Iterable + ) and bool(messages_dict["roles_messages"]) if not ROLES_MESSAGES_KEY_IS_VALID: raise MessagesJSONFileValueError( dict_key="roles_messages", @@ -383,8 +381,7 @@ def _setup_members_list_url(cls) -> None: raw_members_list_url: str | None = os.getenv("MEMBERS_LIST_URL") MEMBERS_LIST_URL_IS_VALID: Final[bool] = bool( - raw_members_list_url - and validators.url(raw_members_list_url), + raw_members_list_url and validators.url(raw_members_list_url), ) if not MEMBERS_LIST_URL_IS_VALID: INVALID_MEMBERS_LIST_URL_MESSAGE: Final[str] = ( @@ -422,8 +419,7 @@ def _setup_send_introduction_reminders(cls) -> None: if raw_send_introduction_reminders not in VALID_SEND_INTRODUCTION_REMINDERS_VALUES: INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE: Final[str] = ( - "SEND_INTRODUCTION_REMINDERS must be one of: " - "\"Once\", \"Interval\" or \"False\"." + "SEND_INTRODUCTION_REMINDERS must be one of: " '"Once", "Interval" or "False".' ) raise ImproperlyConfiguredError(INVALID_SEND_INTRODUCTION_REMINDERS_MESSAGE) @@ -464,8 +460,7 @@ def _setup_send_introduction_reminders_delay(cls) -> None: raw_timedelta_send_introduction_reminders_delay = timedelta( **{ key: float(value) - for key, value - in raw_send_introduction_reminders_delay.groupdict().items() + for key, value in raw_send_introduction_reminders_delay.groupdict().items() if value }, ) @@ -513,8 +508,7 @@ def _setup_send_introduction_reminders_interval(cls) -> None: raw_timedelta_details_send_introduction_reminders_interval = { key: float(value) - for key, value - in raw_send_introduction_reminders_interval.groupdict().items() + for key, value in raw_send_introduction_reminders_interval.groupdict().items() if value } @@ -534,9 +528,7 @@ def _setup_send_get_roles_reminders(cls) -> None: ) raise ImproperlyConfiguredError(INVALID_SEND_GET_ROLES_REMINDERS_MESSAGE) - cls._settings["SEND_GET_ROLES_REMINDERS"] = ( - raw_send_get_roles_reminders in TRUE_VALUES - ) + cls._settings["SEND_GET_ROLES_REMINDERS"] = raw_send_get_roles_reminders in TRUE_VALUES @classmethod def _setup_send_get_roles_reminders_delay(cls) -> None: @@ -567,8 +559,7 @@ def _setup_send_get_roles_reminders_delay(cls) -> None: raw_timedelta_send_get_roles_reminders_delay = timedelta( **{ key: float(value) - for key, value - in raw_send_get_roles_reminders_delay.groupdict().items() + for key, value in raw_send_get_roles_reminders_delay.groupdict().items() if value }, ) @@ -600,7 +591,9 @@ def _setup_advanced_send_get_roles_reminders_interval(cls) -> None: str(os.getenv("ADVANCED_SEND_GET_ROLES_REMINDERS_INTERVAL", "24h")), ) - raw_timedelta_details_advanced_send_get_roles_reminders_interval: Mapping[str, float] = { # noqa: E501 + raw_timedelta_details_advanced_send_get_roles_reminders_interval: Mapping[ + str, float + ] = { "hours": 24, } @@ -616,8 +609,9 @@ def _setup_advanced_send_get_roles_reminders_interval(cls) -> None: raw_timedelta_details_advanced_send_get_roles_reminders_interval = { key: float(value) - for key, value - in raw_advanced_send_get_roles_reminders_interval.groupdict().items() + for key, value in ( + raw_advanced_send_get_roles_reminders_interval.groupdict().items() + ) if value } @@ -648,8 +642,7 @@ def _setup_statistics_roles(cls) -> None: else: cls._settings["STATISTICS_ROLES"] = { raw_statistics_role - for raw_statistics_role - in raw_statistics_roles.split(",") + for raw_statistics_role in raw_statistics_roles.split(",") if raw_statistics_role } @@ -658,8 +651,7 @@ def _setup_moderation_document_url(cls) -> None: raw_moderation_document_url: str | None = os.getenv("MODERATION_DOCUMENT_URL") MODERATION_DOCUMENT_URL_IS_VALID: Final[bool] = bool( - raw_moderation_document_url - and validators.url(raw_moderation_document_url), + raw_moderation_document_url and validators.url(raw_moderation_document_url), ) if not MODERATION_DOCUMENT_URL_IS_VALID: MODERATION_DOCUMENT_URL_MESSAGE: Final[str] = ( diff --git a/db/_settings.py b/db/_settings.py index 87f42dd8..a5119630 100644 --- a/db/_settings.py +++ b/db/_settings.py @@ -20,8 +20,7 @@ # NOTE: settings.py is called when setting up the mypy_django_plugin & when running Pytest. When mypy/Pytest runs no config settings variables are set, so they should not be accessed IMPORTED_BY_MYPY_OR_PYTEST: "Final[bool]" = any( "mypy_django_plugin" in frame.filename or "pytest" in frame.filename - for frame - in inspect.stack()[1:] + for frame in inspect.stack()[1:] if not frame.filename.startswith("<") ) if IMPORTED_BY_MYPY_OR_PYTEST: diff --git a/db/core/__init__.py b/db/core/__init__.py index 91c8b098..11e8a627 100644 --- a/db/core/__init__.py +++ b/db/core/__init__.py @@ -1,6 +1,5 @@ """The core (and only) Django app storing all models.""" - from typing import TYPE_CHECKING if TYPE_CHECKING: diff --git a/db/core/app_config.py b/db/core/app_config.py index 315c9a0c..efd5b767 100644 --- a/db/core/app_config.py +++ b/db/core/app_config.py @@ -1,6 +1,5 @@ """Configurations to make the core app ready to import into _settings.py.""" - from typing import TYPE_CHECKING from django.apps import AppConfig diff --git a/db/core/models/__init__.py b/db/core/models/__init__.py index 0f02eded..ef98d296 100644 --- a/db/core/models/__init__.py +++ b/db/core/models/__init__.py @@ -39,7 +39,7 @@ class Status(models.TextChoices): INSTANCES_NAME_PLURAL: str = "Assigned Committee Actions" - discord_member = models.ForeignKey( # type: ignore[assignment] + discord_member = models.ForeignKey( # type: ignore[assignment] DiscordMember, on_delete=models.CASCADE, related_name="assigned_committee_actions", @@ -61,6 +61,7 @@ class Status(models.TextChoices): null=False, blank=False, ) + class Meta: verbose_name = "Assigned Committee Action" constraints = [ # noqa: RUF012 @@ -72,9 +73,7 @@ class Meta: def __repr__(self) -> str: """Generate a developer-focused representation of this Assigned Committee Action's attributes.""" # noqa: E501, W505 - return ( - f"<{self._meta.verbose_name}: {self.discord_member}, {self.description}" # type: ignore[has-type] - ) + return f"<{self._meta.verbose_name}: {self.discord_member}, {self.description}" # type: ignore[has-type] def __str__(self) -> str: """Generate the string representation of this Assigned Committee Action.""" @@ -161,9 +160,9 @@ class SentGetRolesReminderMember(BaseDiscordMemberWrapper): ) class Meta: # noqa: D106 - verbose_name = "Discord Member that has had a \"Get Roles\" reminder sent to their DMs" + verbose_name = 'Discord Member that has had a "Get Roles" reminder sent to their DMs' verbose_name_plural = ( - "Discord Members that have had a \"Get Roles\" reminder sent to their DMs" + 'Discord Members that have had a "Get Roles" reminder sent to their DMs' ) @@ -223,7 +222,9 @@ def __repr__(self) -> str: return f"<{self._meta.verbose_name}: {self.hashed_group_member_id!r}>" @classmethod - def hash_group_member_id(cls, group_member_id: str | int, group_member_id_type: str = "community group") -> str: # noqa: E501 + def hash_group_member_id( + cls, group_member_id: str | int, group_member_id_type: str = "community group" + ) -> str: """ Hash the provided group_member_id. @@ -281,9 +282,7 @@ class DiscordReminder(BaseDiscordMemberWrapper): _channel_type = models.IntegerField( "Discord Channel Type of the channel that the reminder needs to be sent in", choices=[ - (channel_type.value, channel_type.name) - for channel_type - in discord.ChannelType + (channel_type.value, channel_type.name) for channel_type in discord.ChannelType ], null=True, blank=True, diff --git a/db/core/models/managers.py b/db/core/models/managers.py index ef6f5cc7..27e3e4c0 100644 --- a/db/core/models/managers.py +++ b/db/core/models/managers.py @@ -23,7 +23,9 @@ if TYPE_CHECKING: T_model = TypeVar("T_model", bound=AsyncBaseModel) - T_BaseDiscordMemberWrapper = TypeVar("T_BaseDiscordMemberWrapper", bound=BaseDiscordMemberWrapper) # noqa: E501 + T_BaseDiscordMemberWrapper = TypeVar( + "T_BaseDiscordMemberWrapper", bound=BaseDiscordMemberWrapper + ) type Defaults = MutableMapping[str, object | Callable[[], object]] | None @@ -42,7 +44,9 @@ def _remove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str """Remove any unhashed ID values from the kwargs dict before executing a query.""" @final - def _perform_remove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str, object]: # noqa: E501 + def _perform_remove_unhashed_id_from_kwargs( + self, kwargs: dict[str, object] + ) -> dict[str, object]: if utils.is_running_in_async(): SYNC_IN_ASYNC_MESSAGE: Final[str] = ( "Synchronous database access used in asynchronous context. " @@ -54,7 +58,9 @@ def _perform_remove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> # noinspection SpellCheckingInspection @abc.abstractmethod - async def _aremove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str, object]: # noqa: E501 + async def _aremove_unhashed_id_from_kwargs( + self, kwargs: dict[str, object] + ) -> dict[str, object]: """Remove any unhashed_id values from the kwargs dict before executing a query.""" @override @@ -105,21 +111,27 @@ async def acreate(self, **kwargs: object) -> "T_model": return await super().acreate(**(await self._aremove_unhashed_id_from_kwargs(kwargs))) @override - def get_or_create(self, defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + def get_or_create( + self, defaults: "Defaults" = None, **kwargs: object + ) -> tuple["T_model", bool]: # type: ignore[override] return super().get_or_create( defaults=defaults, **self._perform_remove_unhashed_id_from_kwargs(kwargs), ) @override - async def aget_or_create(self, defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + async def aget_or_create( + self, defaults: "Defaults" = None, **kwargs: object + ) -> tuple["T_model", bool]: # type: ignore[override] return await super().aget_or_create( defaults=defaults, **(await self._aremove_unhashed_id_from_kwargs(kwargs)), ) @override - def update_or_create(self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + def update_or_create( + self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object + ) -> tuple["T_model", bool]: # type: ignore[override] return super().get_or_create( defaults=defaults, create_defaults=create_defaults, @@ -128,7 +140,9 @@ def update_or_create(self, defaults: "Defaults" = None, create_defaults: "Defaul # noinspection SpellCheckingInspection @override - async def aupdate_or_create(self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object) -> tuple["T_model", bool]: # type: ignore[override] # noqa: E501 + async def aupdate_or_create( + self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object + ) -> tuple["T_model", bool]: # type: ignore[override] return await super().aupdate_or_create( defaults=defaults, create_defaults=create_defaults, @@ -177,7 +191,9 @@ def _remove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str # noinspection SpellCheckingInspection @override - async def _aremove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str, object]: # noqa: E501 + async def _aremove_unhashed_id_from_kwargs( + self, kwargs: dict[str, object] + ) -> dict[str, object]: raw_discord_id: object | None = None field_name: str @@ -231,14 +247,18 @@ def _remove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str discord_id=discord_id, )[0] ) - except self.model.discord_member.field.remote_field.model.DoesNotExist as does_not_exist_error: # type: ignore[attr-defined] # noqa: E501 + except ( + self.model.discord_member.field.remote_field.model.DoesNotExist + ) as does_not_exist_error: # type: ignore[attr-defined] raise self.model.DoesNotExist from does_not_exist_error return kwargs # noinspection SpellCheckingInspection @override - async def _aremove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str, object]: # noqa: E501 + async def _aremove_unhashed_id_from_kwargs( + self, kwargs: dict[str, object] + ) -> dict[str, object]: raw_discord_id: object | None = None field_name: str @@ -261,7 +281,9 @@ async def _aremove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> d discord_id=discord_id, ) )[0] - except self.model.discord_member.field.remote_field.model.DoesNotExist as does_not_exist_error: # type: ignore[attr-defined] # noqa: E501 + except ( + self.model.discord_member.field.remote_field.model.DoesNotExist + ) as does_not_exist_error: # type: ignore[attr-defined] raise self.model.DoesNotExist from does_not_exist_error return kwargs diff --git a/db/core/models/utils.py b/db/core/models/utils.py index 25868745..63037f60 100644 --- a/db/core/models/utils.py +++ b/db/core/models/utils.py @@ -32,7 +32,14 @@ class Meta: # noqa: D106 abstract = True @override - def save(self, *, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: "Iterable[str] | None" = None) -> None: # type: ignore[override] # noqa: E501 + def save( + self, + *, + force_insert: bool = False, + force_update: bool = False, + using: str | None = None, + update_fields: "Iterable[str] | None" = None, + ) -> None: # type: ignore[override] self.full_clean() return super().save(force_insert, force_update, using, update_fields) @@ -41,8 +48,7 @@ def save(self, *, force_insert: bool = False, force_update: bool = False, using: def __init__(self, *args: object, **kwargs: object) -> None: proxy_fields: dict[str, object] = { field_name: kwargs.pop(field_name) - for field_name - in set(kwargs.keys()) & self.get_proxy_field_names() + for field_name in set(kwargs.keys()) & self.get_proxy_field_names() } super().__init__(*args, **kwargs) @@ -52,7 +58,16 @@ def __init__(self, *args: object, **kwargs: object) -> None: for field_name, value in proxy_fields.items(): setattr(self, field_name, value) - def update(self, *, commit: bool = True, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: "Iterable[str] | None" = None, **kwargs: object) -> None: # noqa: E501 + def update( + self, + *, + commit: bool = True, + force_insert: bool = False, + force_update: bool = False, + using: str | None = None, + update_fields: "Iterable[str] | None" = None, + **kwargs: object, + ) -> None: """ Change an in-memory object's values, then save it to the database. @@ -98,7 +113,16 @@ def update(self, *, commit: bool = True, force_insert: bool = False, force_updat update.alters_data: bool = True # type: ignore[attr-defined, misc] # noinspection SpellCheckingInspection - async def aupdate(self, *, commit: bool = True, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: "Iterable[str] | None" = None, **kwargs: object) -> None: # noqa: E501 + async def aupdate( + self, + *, + commit: bool = True, + force_insert: bool = False, + force_update: bool = False, + using: str | None = None, + update_fields: "Iterable[str] | None" = None, + **kwargs: object, + ) -> None: """ Asyncronously change an in-memory object's values, then save it to the database. diff --git a/exceptions/base.py b/exceptions/base.py index 422507e3..42d4831b 100644 --- a/exceptions/base.py +++ b/exceptions/base.py @@ -123,13 +123,13 @@ def get_formatted_message(cls, non_existent_object_identifier: str) -> str: # n if cls.DEPENDENT_COMMANDS: if len(cls.DEPENDENT_COMMANDS) == 1: formatted_dependent_commands += ( - f"\"/{next(iter(cls.DEPENDENT_COMMANDS))}\" command" + f'"/{next(iter(cls.DEPENDENT_COMMANDS))}" command' ) else: index: int dependent_command: str for index, dependent_command in enumerate(cls.DEPENDENT_COMMANDS): - formatted_dependent_commands += f"\"/{dependent_command}\"" + formatted_dependent_commands += f'"/{dependent_command}"' if index < len(cls.DEPENDENT_COMMANDS) - 2: formatted_dependent_commands += ", " @@ -142,7 +142,7 @@ def get_formatted_message(cls, non_existent_object_identifier: str) -> str: # n non_existent_object_identifier = f"#{non_existent_object_identifier}" partial_message: str = ( - f"\"{non_existent_object_identifier}\" {cls.DOES_NOT_EXIST_TYPE} must exist " + f'"{non_existent_object_identifier}" {cls.DOES_NOT_EXIST_TYPE} must exist ' f"in order to use the {formatted_dependent_commands}" ) @@ -156,11 +156,11 @@ def get_formatted_message(cls, non_existent_object_identifier: str) -> str: # n partial_message += ", the " if len(cls.DEPENDENT_TASKS) == 1: - formatted_dependent_tasks += f"\"{next(iter(cls.DEPENDENT_TASKS))}\" task" + formatted_dependent_tasks += f'"{next(iter(cls.DEPENDENT_TASKS))}" task' else: dependent_task: str for index, dependent_task in enumerate(cls.DEPENDENT_TASKS): - formatted_dependent_tasks += f"\"{dependent_task}\"" + formatted_dependent_tasks += f'"{dependent_task}"' if index < len(cls.DEPENDENT_TASKS) - 2: formatted_dependent_tasks += ", " @@ -178,11 +178,11 @@ def get_formatted_message(cls, non_existent_object_identifier: str) -> str: # n partial_message += " and the " if len(cls.DEPENDENT_EVENTS) == 1: - formatted_dependent_events += f"\"{next(iter(cls.DEPENDENT_EVENTS))}\" event" + formatted_dependent_events += f'"{next(iter(cls.DEPENDENT_EVENTS))}" event' else: dependent_event: str for index, dependent_event in enumerate(cls.DEPENDENT_EVENTS): - formatted_dependent_events += f"\"{dependent_event}\"" + formatted_dependent_events += f'"{dependent_event}"' if index < len(cls.DEPENDENT_EVENTS) - 2: formatted_dependent_events += ", " diff --git a/exceptions/config_changes.py b/exceptions/config_changes.py index 7b083e1a..c2c04a58 100644 --- a/exceptions/config_changes.py +++ b/exceptions/config_changes.py @@ -36,7 +36,9 @@ def DEFAULT_MESSAGE(cls) -> str: return "TeX-Bot requires a restart to apply configuration changes." @override - def __init__(self, message: str | None = None, changed_settings: "AbstractSet[str] | None" = None) -> None: # noqa: E501 + def __init__( + self, message: str | None = None, changed_settings: "AbstractSet[str] | None" = None + ) -> None: """Initialise an Exception to apply configuration changes.""" self.changed_settings: AbstractSet[str] | None = ( changed_settings if changed_settings else set() diff --git a/exceptions/does_not_exist.py b/exceptions/does_not_exist.py index af0aa9fb..79af1670 100644 --- a/exceptions/does_not_exist.py +++ b/exceptions/does_not_exist.py @@ -76,7 +76,7 @@ class RoleDoesNotExistError(BaseDoesNotExistError, abc.ABC): @classproperty @override def DEFAULT_MESSAGE(cls) -> str: - return f"Role with name \"{cls.ROLE_NAME}\" does not exist." + return f'Role with name "{cls.ROLE_NAME}" does not exist.' # noinspection PyMethodParameters,PyPep8Naming @classproperty @@ -273,7 +273,7 @@ class ChannelDoesNotExistError(BaseDoesNotExistError): @classproperty @override def DEFAULT_MESSAGE(cls) -> str: - return f"Channel with name \"{cls.CHANNEL_NAME}\" does not exist." + return f'Channel with name "{cls.CHANNEL_NAME}" does not exist.' # noinspection PyMethodParameters,PyPep8Naming @classproperty diff --git a/exceptions/guild.py b/exceptions/guild.py index 0a53085c..cf851bb6 100644 --- a/exceptions/guild.py +++ b/exceptions/guild.py @@ -39,7 +39,7 @@ class EveryoneRoleCouldNotBeRetrievedError(BaseErrorWithErrorCode, ValueError): @classproperty @override def DEFAULT_MESSAGE(cls) -> str: - return "The reference to the \"@everyone\" role could not be correctly retrieved." + return 'The reference to the "@everyone" role could not be correctly retrieved.' # noinspection PyMethodParameters,PyPep8Naming @classproperty diff --git a/exceptions/messages.py b/exceptions/messages.py index 4dbecd12..1505186f 100644 --- a/exceptions/messages.py +++ b/exceptions/messages.py @@ -67,7 +67,12 @@ def DEFAULT_MESSAGE(cls) -> str: return "The messages JSON file has an invalid value." @override - def __init__(self, message: str | None = None, dict_key: str | None = None, invalid_value: object | None = None) -> None: # noqa: E501 + def __init__( + self, + message: str | None = None, + dict_key: str | None = None, + invalid_value: object | None = None, + ) -> None: """Initialise a new InvalidMessagesJSONFile exception for a key's invalid value.""" self.invalid_value: object | None = invalid_value diff --git a/main.py b/main.py index 4cd36407..165fe96d 100755 --- a/main.py +++ b/main.py @@ -28,7 +28,9 @@ # noinspection PyDunderSlots,PyUnresolvedReferences intents.members = True - bot: TeXBot = TeXBot(intents=intents) # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 + bot: TeXBot = TeXBot( + intents=intents + ) # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 bot.load_extension("cogs") diff --git a/poetry.lock b/poetry.lock index e6cfaeda..6f193672 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1856,13 +1856,13 @@ files = [ [[package]] name = "typed-classproperties" -version = "1.0.2" +version = "1.1.0" description = "Typed decorators for classproperty and cached_classproperty." optional = false python-versions = ">=3.9" files = [ - {file = "typed_classproperties-1.0.2-py3-none-any.whl", hash = "sha256:66e486e78df9be31cb6c2e077378a4d10469df051dac3b981330699d4637ee17"}, - {file = "typed_classproperties-1.0.2.tar.gz", hash = "sha256:f82c7f80c2a5b7241c5cefd0186ff7d365ebfa0b4da6ab579320724734ae241c"}, + {file = "typed_classproperties-1.1.0-py3-none-any.whl", hash = "sha256:ed4524d53d1f0091640c3c8061ab61ed6245e5c92b613a8b7798b433df1c99a7"}, + {file = "typed_classproperties-1.1.0.tar.gz", hash = "sha256:8d47dac5d3a66befa2f098f50c66db5c5e1f4522f4db9504def5e4bc7d50a105"}, ] [[package]] diff --git a/tests/test_utils.py b/tests/test_utils.py index 0b31917c..599ae0f9 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -86,14 +86,17 @@ class TestGenerateInviteURL: def test_url_generates() -> None: """Test that the invite URL generates successfully when valid arguments are passed.""" DISCORD_BOT_APPLICATION_ID: Final[int] = random.randint( # noqa: S311 - 10000000000000000, 99999999999999999999, + 10000000000000000, + 99999999999999999999, ) DISCORD_MAIN_GUILD_ID: Final[int] = random.randint( # noqa: S311 - 10000000000000000, 99999999999999999999, + 10000000000000000, + 99999999999999999999, ) invite_url: str = utils.generate_invite_url( - DISCORD_BOT_APPLICATION_ID, DISCORD_MAIN_GUILD_ID, + DISCORD_BOT_APPLICATION_ID, + DISCORD_MAIN_GUILD_ID, ) assert re.fullmatch( diff --git a/utils/__init__.py b/utils/__init__.py index c547a197..801e5208 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -72,9 +72,7 @@ def is_member_inducted(member: discord.Member) -> bool: Returns True if the member has any role other than "@News". The set of ignored roles is a tuple to make the set easily expandable. """ - return any( - role.name.lower().strip("@ \n\t") not in ("news",) for role in member.roles - ) + return any(role.name.lower().strip("@ \n\t") not in ("news",) for role in member.roles) def is_running_in_async() -> bool: diff --git a/utils/command_checks.py b/utils/command_checks.py index cac2ded9..dae3bb0d 100644 --- a/utils/command_checks.py +++ b/utils/command_checks.py @@ -37,7 +37,9 @@ async def _check_interaction_user_in_main_guild(ctx: "TeXBotApplicationContext") """ @staticmethod - async def _check_interaction_user_has_committee_role(ctx: "TeXBotApplicationContext") -> bool: # noqa: E501 + async def _check_interaction_user_has_committee_role( + ctx: "TeXBotApplicationContext", + ) -> bool: return await ctx.bot.check_user_has_committee_role(ctx.user) check_interaction_user_has_committee_role: "Callable[[T], T]" diff --git a/utils/error_capture_decorators.py b/utils/error_capture_decorators.py index 559c5815..a81b137b 100644 --- a/utils/error_capture_decorators.py +++ b/utils/error_capture_decorators.py @@ -6,39 +6,35 @@ import functools import logging -from typing import TYPE_CHECKING, ParamSpec, TypeVar +from typing import TYPE_CHECKING from exceptions import GuildDoesNotExistError, StrikeTrackingError -from .tex_bot_base_cog import TeXBotBaseCog - if TYPE_CHECKING: from collections.abc import Callable, Coroutine, Sequence from logging import Logger from typing import Concatenate, Final + from .tex_bot_base_cog import TeXBotBaseCog + __all__: "Sequence[str]" = ( "ErrorCaptureDecorators", "capture_guild_does_not_exist_error", "capture_strike_tracking_error", ) - -P = ParamSpec("P") -T_ret = TypeVar("T_ret") -T_cog = TypeVar("T_cog", bound=TeXBotBaseCog) - if TYPE_CHECKING: - type WrapperInputFunc[T_ret] = ( - Callable[ - Concatenate[TeXBotBaseCog, P], - Coroutine[object, object, T_ret]] | Callable[P, Coroutine[object, object, T_ret], + type WrapperInputFunc[**P, T_ret] = ( + Callable[Concatenate[TeXBotBaseCog, P], Coroutine[object, object, T_ret]] + | Callable[ + P, + Coroutine[object, object, T_ret], ] ) - type WrapperOutputFunc[T_ret] = Callable[P, Coroutine[object, object, T_ret | None]] - type DecoratorInputFunc[T_cog: TeXBotBaseCog, T_ret] = ( - Callable[Concatenate[T_cog, P], Coroutine[object, object, T_ret]] - ) + type WrapperOutputFunc[**P, T_ret] = Callable[P, Coroutine[object, object, T_ret | None]] + type DecoratorInputFunc[**P, T_cog: TeXBotBaseCog, T_ret] = Callable[ + Concatenate[T_cog, P], Coroutine[object, object, T_ret] + ] logger: "Final[Logger]" = logging.getLogger("TeX-Bot") @@ -51,7 +47,11 @@ class ErrorCaptureDecorators: """ @staticmethod - def capture_error_and_close(func: "DecoratorInputFunc[T_cog, P, T_ret]", error_type: type[BaseException], close_func: "Callable[[BaseException], None]") -> "WrapperOutputFunc[P, T_ret]": # noqa: E501 + def capture_error_and_close[**P, T_ret, T_cog: TeXBotBaseCog]( + func: "DecoratorInputFunc[P, T_cog, T_ret]", + error_type: type[BaseException], + close_func: "Callable[[BaseException], None]", + ) -> "WrapperOutputFunc[P, T_ret]": """ Decorator to send an error message to the user when the given exception type is raised. @@ -80,7 +80,9 @@ def strike_tracking_error_close_func(cls, error: BaseException) -> None: logger.warning("Critical errors are likely to lead to untracked moderation actions") -def capture_guild_does_not_exist_error(func: "WrapperInputFunc[P, T_ret]") -> "WrapperOutputFunc[P, T_ret]": # noqa: E501 +def capture_guild_does_not_exist_error[**P, T_ret]( + func: "WrapperInputFunc[P, T_ret]", +) -> "WrapperOutputFunc[P, T_ret]": """ Decorator to send an error message to the Discord user when a GuildDoesNotExist is raised. @@ -93,7 +95,9 @@ def capture_guild_does_not_exist_error(func: "WrapperInputFunc[P, T_ret]") -> "W ) -def capture_strike_tracking_error(func: "WrapperInputFunc[P, T_ret]") -> "WrapperOutputFunc[P, T_ret]": # noqa: E501 +def capture_strike_tracking_error[**P, T_ret]( + func: "WrapperInputFunc[P, T_ret]", +) -> "WrapperOutputFunc[P, T_ret]": """ Decorator to send an error message to the user when a StrikeTrackingError is raised. diff --git a/utils/message_sender_components.py b/utils/message_sender_components.py index 1662c601..5674cd44 100644 --- a/utils/message_sender_components.py +++ b/utils/message_sender_components.py @@ -32,7 +32,9 @@ def __init__(self) -> None: self.sent_message: discord.Message | discord.Interaction | None = None @abc.abstractmethod - async def _send(self, content: str, *, view: "View | None" = None) -> discord.Message | discord.Interaction: # noqa: E501 + async def _send( + self, content: str, *, view: "View | None" = None + ) -> discord.Message | discord.Interaction: """ Subclass implementation of `send()` method. @@ -80,8 +82,11 @@ def __init__(self, channel: discord.DMChannel | discord.TextChannel) -> None: super().__init__() @override - async def _send(self, content: str, *, view: "View | None" = None) -> discord.Message | discord.Interaction: # noqa: E501 + async def _send( + self, content: str, *, view: "View | None" = None + ) -> discord.Message | discord.Interaction: if TYPE_CHECKING: + class _BaseChannelSendKwargs(TypedDict): """Type-hint for the required kwargs to the channel-send-function.""" @@ -118,5 +123,7 @@ def __init__(self, ctx: "TeXBotApplicationContext") -> None: super().__init__() @override - async def _send(self, content: str, *, view: "View | None" = None) -> discord.Message | discord.Interaction: # noqa: E501 + async def _send( + self, content: str, *, view: "View | None" = None + ) -> discord.Message | discord.Interaction: return await self.ctx.respond(content=content, view=view, ephemeral=True) diff --git a/utils/suppress_traceback.py b/utils/suppress_traceback.py index 52e8abf6..325e3aa9 100644 --- a/utils/suppress_traceback.py +++ b/utils/suppress_traceback.py @@ -38,7 +38,12 @@ def __enter__(self) -> None: # noinspection SpellCheckingInspection sys.tracebacklimit = 0 - def __exit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: "TracebackType | None") -> None: # noqa: E501, PYI036 + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: "TracebackType | None", # noqa: PYI036 + ) -> None: """Exit the context manager, reverting the limit of traceback output.""" if exc_type is not None or exc_val is not None or exc_tb is not None: return diff --git a/utils/tex_bot.py b/utils/tex_bot.py index 50cc6ba0..0f3a4bbd 100644 --- a/utils/tex_bot.py +++ b/utils/tex_bot.py @@ -313,26 +313,32 @@ def group_short_name(self) -> str: if no group-short-name is provided/could not be determined. """ return ( - settings["_GROUP_SHORT_NAME"] - if settings["_GROUP_SHORT_NAME"] - else ( - "CSS" - if ( - "computer science society" in self.group_full_name.lower() - or "css" in self.group_full_name.lower() + ( + settings["_GROUP_SHORT_NAME"] + if settings["_GROUP_SHORT_NAME"] + else ( + "CSS" + if ( + "computer science society" in self.group_full_name.lower() + or "css" in self.group_full_name.lower() + ) + else self.group_full_name ) - else self.group_full_name ) - ).replace( - "the", - "", - ).replace( - "THE", - "", - ).replace( - "The", - "", - ).strip() + .replace( + "the", + "", + ) + .replace( + "THE", + "", + ) + .replace( + "The", + "", + ) + .strip() + ) @property def group_member_id_type(self) -> str: @@ -404,7 +410,9 @@ async def _fetch_text_channel(self, name: str) -> discord.TextChannel | None: return text_channel - async def perform_kill_and_close(self, initiated_by_user: discord.User | discord.Member | None = None) -> "NoReturn": # noqa: E501 + async def perform_kill_and_close( + self, initiated_by_user: discord.User | discord.Member | None = None + ) -> "NoReturn": """ Shutdown TeX-Bot by using the "/kill" command. @@ -456,7 +464,9 @@ def set_main_guild(self, main_guild: discord.Guild) -> None: self._main_guild = main_guild self._main_guild_set = True - async def get_main_guild_member(self, user: discord.Member | discord.User) -> discord.Member: # noqa: E501 + async def get_main_guild_member( + self, user: discord.Member | discord.User + ) -> discord.Member: """ Util method to retrieve a member of your group's Discord guild from their User object. @@ -477,9 +487,7 @@ async def get_member_from_str_id(self, str_member_id: str) -> discord.Member: str_member_id = str_member_id.replace("<@", "").replace(">", "") if not re.fullmatch(r"\A\d{17,20}\Z", str_member_id): - INVALID_USER_ID_MESSAGE: Final[str] = ( - f"'{str_member_id}' is not a valid user ID." - ) + INVALID_USER_ID_MESSAGE: Final[str] = f"'{str_member_id}' is not a valid user ID." raise ValueError(INVALID_USER_ID_MESSAGE) user: discord.User | None = self.get_user(int(str_member_id)) diff --git a/utils/tex_bot_base_cog.py b/utils/tex_bot_base_cog.py index 05b1cb07..22b5cd96 100644 --- a/utils/tex_bot_base_cog.py +++ b/utils/tex_bot_base_cog.py @@ -70,7 +70,13 @@ def __init__(self, bot: "TeXBot") -> None: """ self.bot: TeXBot = bot # NOTE: See https://github.com/CSSUoB/TeX-Bot-Py-V2/issues/261 - async def command_send_error(self, ctx: "TeXBotApplicationContext", error_code: str | None = None, message: str | None = None, logging_message: str | BaseException | None = None) -> None: # noqa: E501 + async def command_send_error( + self, + ctx: "TeXBotApplicationContext", + error_code: str | None = None, + message: str | None = None, + logging_message: str | BaseException | None = None, + ) -> None: """ Construct & format an error message from the given details. @@ -96,7 +102,15 @@ async def command_send_error(self, ctx: "TeXBotApplicationContext", error_code: ) @classmethod - async def send_error(cls, bot: "TeXBot", interaction: discord.Interaction, interaction_name: str, error_code: str | None = None, message: str | None = None, logging_message: str | BaseException | None = None) -> None: # noqa: E501 + async def send_error( + cls, + bot: "TeXBot", + interaction: discord.Interaction, + interaction_name: str, + error_code: str | None = None, + message: str | None = None, + logging_message: str | BaseException | None = None, + ) -> None: """ Construct & format an error message from the given details. @@ -141,7 +155,8 @@ async def send_error(cls, bot: "TeXBot", interaction: discord.Interaction, inter if logging_message: logger.error( " ".join( - message_part for message_part in ( + message_part + for message_part in ( error_code if error_code else "", f"({interaction_name})", str(logging_message), @@ -151,7 +166,9 @@ async def send_error(cls, bot: "TeXBot", interaction: discord.Interaction, inter ) @staticmethod - async def autocomplete_get_text_channels(ctx: "TeXBotAutocompleteContext") -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": # noqa: E501 + async def autocomplete_get_text_channels( + ctx: "TeXBotAutocompleteContext", + ) -> "AbstractSet[discord.OptionChoice] | AbstractSet[str]": """ Autocomplete callable that generates the set of available selectable channels. From 85906fc2ef85c4cf8709918270763fe42964e78b Mon Sep 17 00:00:00 2001 From: Matt Norton Date: Sun, 24 Nov 2024 23:11:46 +0000 Subject: [PATCH 5/5] Fix mypy errors --- cogs/strike.py | 8 ++++---- db/core/models/managers.py | 24 ++++++++++++------------ db/core/models/utils.py | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/cogs/strike.py b/cogs/strike.py index 619f492c..d2f9e52b 100644 --- a/cogs/strike.py +++ b/cogs/strike.py @@ -617,9 +617,9 @@ async def _confirm_manual_add_strike( ) if ( - out_of_sync_ban_button_interaction.data["custom_id"] + out_of_sync_ban_button_interaction.data["custom_id"] # type: ignore[index, typeddict-item] == "no_out_of_sync_ban_member" - ): # type: ignore[index, typeddict-item] + ): await out_of_sync_ban_confirmation_message.edit( content=( f"Aborted performing ban action upon {strike_user.mention}. " @@ -639,9 +639,9 @@ async def _confirm_manual_add_strike( return if ( - out_of_sync_ban_button_interaction.data["custom_id"] + out_of_sync_ban_button_interaction.data["custom_id"] # type: ignore[index, typeddict-item] == "yes_out_of_sync_ban_member" - ): # type: ignore[index, typeddict-item] + ): await self._send_strike_user_message(strike_user, member_strikes) await main_guild.ban( strike_user, diff --git a/db/core/models/managers.py b/db/core/models/managers.py index 27e3e4c0..30c24bcd 100644 --- a/db/core/models/managers.py +++ b/db/core/models/managers.py @@ -112,8 +112,8 @@ async def acreate(self, **kwargs: object) -> "T_model": @override def get_or_create( - self, defaults: "Defaults" = None, **kwargs: object - ) -> tuple["T_model", bool]: # type: ignore[override] + self, defaults: "Defaults" = None, **kwargs: object # type: ignore[override] + ) -> tuple["T_model", bool]: return super().get_or_create( defaults=defaults, **self._perform_remove_unhashed_id_from_kwargs(kwargs), @@ -121,8 +121,8 @@ def get_or_create( @override async def aget_or_create( - self, defaults: "Defaults" = None, **kwargs: object - ) -> tuple["T_model", bool]: # type: ignore[override] + self, defaults: "Defaults" = None, **kwargs: object # type: ignore[override] + ) -> tuple["T_model", bool]: return await super().aget_or_create( defaults=defaults, **(await self._aremove_unhashed_id_from_kwargs(kwargs)), @@ -130,8 +130,8 @@ async def aget_or_create( @override def update_or_create( - self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object - ) -> tuple["T_model", bool]: # type: ignore[override] + self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object # type: ignore[override] + ) -> tuple["T_model", bool]: return super().get_or_create( defaults=defaults, create_defaults=create_defaults, @@ -141,8 +141,8 @@ def update_or_create( # noinspection SpellCheckingInspection @override async def aupdate_or_create( - self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object - ) -> tuple["T_model", bool]: # type: ignore[override] + self, defaults: "Defaults" = None, create_defaults: "Defaults" = None, **kwargs: object # type: ignore[override] + ) -> tuple["T_model", bool]: return await super().aupdate_or_create( defaults=defaults, create_defaults=create_defaults, @@ -248,8 +248,8 @@ def _remove_unhashed_id_from_kwargs(self, kwargs: dict[str, object]) -> dict[str )[0] ) except ( - self.model.discord_member.field.remote_field.model.DoesNotExist - ) as does_not_exist_error: # type: ignore[attr-defined] + self.model.discord_member.field.remote_field.model.DoesNotExist # type: ignore[attr-defined] + ) as does_not_exist_error: raise self.model.DoesNotExist from does_not_exist_error return kwargs @@ -282,8 +282,8 @@ async def _aremove_unhashed_id_from_kwargs( ) )[0] except ( - self.model.discord_member.field.remote_field.model.DoesNotExist - ) as does_not_exist_error: # type: ignore[attr-defined] + self.model.discord_member.field.remote_field.model.DoesNotExist # type: ignore[attr-defined] + ) as does_not_exist_error: raise self.model.DoesNotExist from does_not_exist_error return kwargs diff --git a/db/core/models/utils.py b/db/core/models/utils.py index 63037f60..37901734 100644 --- a/db/core/models/utils.py +++ b/db/core/models/utils.py @@ -32,14 +32,14 @@ class Meta: # noqa: D106 abstract = True @override - def save( + def save( # type: ignore[override] self, *, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: "Iterable[str] | None" = None, - ) -> None: # type: ignore[override] + ) -> None: self.full_clean() return super().save(force_insert, force_update, using, update_fields)