Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
resolution-strategy: "lowest"
cache-dependency-glob: "uv.lock"
activate-environment: true

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v6.0.0
hooks:
- id: check-merge-conflict
- id: check-toml
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ARG python_version=3.13-slim
ARG python_version=3.14-slim

FROM python:$python_version AS builder
COPY --from=ghcr.io/astral-sh/uv:0.7 /uv /bin/
COPY --from=ghcr.io/astral-sh/uv:0.9 /uv /bin/

ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy
Expand Down
2 changes: 1 addition & 1 deletion bot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@

apply_monkey_patches()

instance: "Bot" = None # Global Bot instance.
instance: Bot = None # Global Bot instance.
2 changes: 0 additions & 2 deletions bot/converters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import re
import typing as t
from datetime import UTC, datetime
Expand Down
2 changes: 0 additions & 2 deletions bot/errors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

from collections.abc import Hashable
from typing import TYPE_CHECKING

Expand Down
6 changes: 4 additions & 2 deletions bot/exts/backend/branding/_repository.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inspect
import typing as t
from datetime import UTC, date, datetime

Expand Down Expand Up @@ -45,10 +46,11 @@ class RemoteObject:

def __init__(self, dictionary: dict[str, t.Any]) -> None:
"""Initialize by grabbing annotated attributes from `dictionary`."""
missing_keys = self.__annotations__.keys() - dictionary.keys()
annotation_keys = inspect.get_annotations(self.__class__)
missing_keys = annotation_keys - dictionary.keys()
if missing_keys:
raise KeyError(f"Fetched object lacks expected keys: {missing_keys}")
for annotation in self.__annotations__:
for annotation in annotation_keys:
setattr(self, annotation, dictionary[annotation])


Expand Down
4 changes: 2 additions & 2 deletions bot/exts/backend/sync/_syncers.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,10 @@ async def _sync(diff: _Diff) -> None:
# Using asyncio.gather would still consume too many resources on the site.
log.trace("Syncing created users...")
if diff.created:
for chunk in batched(diff.created, CHUNK_SIZE):
for chunk in batched(diff.created, CHUNK_SIZE, strict=False):
await bot.instance.api_client.post("bot/users", json=chunk)

log.trace("Syncing updated users...")
if diff.updated:
for chunk in batched(diff.updated, CHUNK_SIZE):
for chunk in batched(diff.updated, CHUNK_SIZE, strict=False):
await bot.instance.api_client.patch("bot/users/bulk_patch", json=chunk)
2 changes: 0 additions & 2 deletions bot/exts/filtering/_filter_context.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import typing
from collections.abc import Callable, Coroutine, Iterable
from dataclasses import dataclass, field, replace
Expand Down
2 changes: 1 addition & 1 deletion bot/exts/filtering/_filter_lists/antispam.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class AntispamList(UniquesListBase):

name = "antispam"

def __init__(self, filtering_cog: "Filtering"):
def __init__(self, filtering_cog: Filtering):
super().__init__(filtering_cog)
self.message_deletion_queue: dict[Member, DeletionContext] = dict()

Expand Down
2 changes: 0 additions & 2 deletions bot/exts/filtering/_filter_lists/domain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import re
import typing

Expand Down
2 changes: 0 additions & 2 deletions bot/exts/filtering/_filter_lists/extension.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import typing
from os.path import splitext

Expand Down
6 changes: 3 additions & 3 deletions bot/exts/filtering/_filter_lists/filter_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ async def _create_filter_list_result(
self, ctx: FilterContext, defaults: Defaults, filters: Iterable[Filter]
) -> list[Filter]:
"""A helper function to evaluate the result of `filter_list_result`."""
passed_by_default, failed_by_default = defaults.validations.evaluate(ctx)
_passed_by_default, failed_by_default = defaults.validations.evaluate(ctx)
default_answer = not bool(failed_by_default)

relevant_filters = []
Expand Down Expand Up @@ -160,7 +160,7 @@ def __hash__(self):
T = typing.TypeVar("T", bound=Filter)


class FilterList(dict[ListType, AtomicList], typing.Generic[T], FieldRequiring):
class FilterList[T](dict[ListType, AtomicList], FieldRequiring):
"""Dispatches events to lists of _filters, and aggregates the responses into a single list of actions to take."""

# Each subclass must define a name matching the filter_list name we're expecting to receive from the database.
Expand Down Expand Up @@ -268,7 +268,7 @@ class UniquesListBase(FilterList[UniqueFilter], ABC):
Each unique filter subscribes to a subset of events to respond to.
"""

def __init__(self, filtering_cog: "Filtering"):
def __init__(self, filtering_cog: Filtering):
super().__init__()
self.filtering_cog = filtering_cog
self.loaded_types: dict[str, type[UniqueFilter]] = {}
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/filtering/_filter_lists/invite.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import re
import typing

Expand Down
2 changes: 0 additions & 2 deletions bot/exts/filtering/_filter_lists/token.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import re
import typing

Expand Down
2 changes: 0 additions & 2 deletions bot/exts/filtering/_settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import operator
import traceback
from abc import abstractmethod
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/filtering/_settings_types/settings_entry.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

from abc import abstractmethod
from typing import Any, ClassVar, Self

Expand Down
4 changes: 1 addition & 3 deletions bot/exts/filtering/_ui/filter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

from collections.abc import Callable
from typing import Any

Expand Down Expand Up @@ -293,7 +291,7 @@ async def update_embed(
if setting_name:
# Find the right dictionary to update.
if "/" in setting_name:
filter_name, setting_name = setting_name.split("/", maxsplit=1)
_filter_name, setting_name = setting_name.split("/", maxsplit=1)
dict_to_edit = self.filter_settings_overrides
default_value = self.filter_type.extra_fields_type().model_dump()[setting_name]
else:
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/filtering/_ui/filter_list.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

from collections.abc import Callable
from typing import Any

Expand Down
6 changes: 2 additions & 4 deletions bot/exts/filtering/_ui/search.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

from collections.abc import Callable
from typing import Any

Expand Down Expand Up @@ -114,7 +112,7 @@ def template_settings(
result = get_filter(filter_id, filter_lists)
if not result:
raise BadArgument(f"Could not find a filter with ID `{filter_id}`.")
filter_, filter_list, list_type = result
filter_, _filter_list, _list_type = result

if filter_type and not isinstance(filter_, filter_type):
raise BadArgument(f"The filter with ID `{filter_id}` is not of type {filter_type.name!r}.")
Expand Down Expand Up @@ -256,7 +254,7 @@ async def update_embed(
return

if "/" in setting_name:
filter_name, setting_name = setting_name.split("/", maxsplit=1)
_filter_name, setting_name = setting_name.split("/", maxsplit=1)
dict_to_edit = self.filter_settings
else:
dict_to_edit = self.settings
Expand Down
4 changes: 1 addition & 3 deletions bot/exts/filtering/_ui/ui.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import re
from abc import ABC, abstractmethod
from collections.abc import Callable, Coroutine
Expand Down Expand Up @@ -136,7 +134,7 @@ def populate_embed_from_dict(embed: Embed, data: dict) -> None:
embed.add_field(name=setting, value=value, inline=len(value) < MAX_INLINE_SIZE)


def parse_value(value: str, type_: type[T]) -> T:
def parse_value[T](value: str, type_: type[T]) -> T:
"""Parse the value provided in the CLI and attempt to convert it to the provided type."""
blank = value == '""'
type_ = normalize_type(type_, prioritize_nonetype=blank)
Expand Down
6 changes: 2 additions & 4 deletions bot/exts/filtering/_utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import importlib
import importlib.util
import inspect
Expand Down Expand Up @@ -34,7 +32,7 @@
Serializable = bool | int | float | str | list | dict | None


def subclasses_in_package(package: str, prefix: str, parent: T) -> set[T]:
def subclasses_in_package[T](package: str, prefix: str, parent: T) -> set[T]:
"""Return all the subclasses of class `parent`, found in the top-level of `package`, given by absolute path."""
subclasses = set()

Expand Down Expand Up @@ -157,7 +155,7 @@ def normalize_type(type_: type, *, prioritize_nonetype: bool = True) -> type:
return type_


def starting_value(type_: type[T]) -> T:
def starting_value[T](type_: type[T]) -> T:
"""Return a value of the given type."""
type_ = normalize_type(type_)
try:
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/info/doc/_batch_parser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import asyncio
import collections
from collections import defaultdict, deque
Expand Down
20 changes: 2 additions & 18 deletions bot/exts/info/doc/_cog.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from __future__ import annotations

import asyncio
import sys
import textwrap
from collections import defaultdict
from contextlib import suppress
from types import SimpleNamespace
from typing import Literal, NamedTuple
from typing import Literal

import aiohttp
import discord
Expand All @@ -23,6 +21,7 @@
from bot.utils.messages import send_denial, wait_for_deletion

from . import NAMESPACE, PRIORITY_PACKAGES, _batch_parser, doc_cache
from ._doc_item import DocItem
from ._inventory_parser import InvalidHeaderError, InventoryDict, fetch_inventory

log = get_logger(__name__)
Expand All @@ -43,21 +42,6 @@
COMMAND_LOCK_SINGLETON = "inventory refresh"


class DocItem(NamedTuple):
"""Holds inventory symbol information."""

package: str # Name of the package name the symbol is from
group: str # Interpshinx "role" of the symbol, for example `label` or `method`
base_url: str # Absolute path to to which the relative path resolves, same for all items with the same package
relative_url_path: str # Relative path to the page where the symbol is located
symbol_id: str # Fragment id used to locate the symbol on the page

@property
def url(self) -> str:
"""Return the absolute url to the symbol."""
return self.base_url + self.relative_url_path


class DocCog(commands.Cog):
"""A set of commands for querying & displaying documentation."""

Expand Down
25 changes: 25 additions & 0 deletions bot/exts/info/doc/_doc_item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import NamedTuple


class DocItem(NamedTuple):
"""Holds inventory symbol information."""

package: str
"""Name of the package name the symbol is from"""

group: str
"""Interpshinx "role" of the symbol, for example `label` or `method`"""

base_url: str
"""Absolute path to to which the relative path resolves, same for all items with the same package"""

relative_url_path: str
"""Relative path to the page where the symbol is located"""

symbol_id: str
"""Fragment id used to locate the symbol on the page"""

@property
def url(self) -> str:
"""Return the absolute url to the symbol."""
return self.base_url + self.relative_url_path
2 changes: 1 addition & 1 deletion bot/exts/info/doc/_markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def convert_li(self, el: PageElement, text: str, parent_tags: set[str]) -> str:
bullet = bullets[depth % len(bullets)]
return f"{bullet} {text}\n"

def _convert_hn(self, _n: int, el: PageElement, text: str, parent_tags: set[str]) -> str:
def convert_hN(self, _n: int, el: PageElement, text: str, parent_tags: set[str]) -> str: # noqa: N802
"""Convert h tags to bold text with ** instead of adding #."""
if "_inline" in parent_tags:
return text
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/info/doc/_parsing.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import re
import string
import textwrap
Expand Down
6 changes: 1 addition & 5 deletions bot/exts/info/doc/_redis_cache.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
from __future__ import annotations

import datetime
import fnmatch
import time
from typing import TYPE_CHECKING

from async_rediscache.types.base import RedisObject

from bot.log import get_logger
from bot.utils.lock import lock

if TYPE_CHECKING:
from ._cog import DocItem
from ._doc_item import DocItem

WEEK_SECONDS = int(datetime.timedelta(weeks=1).total_seconds())

Expand Down
2 changes: 0 additions & 2 deletions bot/exts/info/help.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import itertools
import re
from collections import namedtuple
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/info/tags.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import enum
import re
import time
Expand Down
2 changes: 1 addition & 1 deletion bot/exts/recruitment/talentpool/_cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class NominationContextModal(discord.ui.Modal, title="New Nomination"):
max_length=REASON_MAX_CHARS - 110
)

def __init__(self, cog: "TalentPool", message: discord.Message, noms_channel: discord.TextChannel):
def __init__(self, cog: TalentPool, message: discord.Message, noms_channel: discord.TextChannel):
self.message = message
self.api = cog.api
self.noms_channel = noms_channel
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/utils/attachment_pastebin_uploader.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import re

import discord
Expand Down
2 changes: 1 addition & 1 deletion bot/exts/utils/reminders.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ async def cancel(self, interaction: Interaction, button: discord.ui.Button) -> N
class OptInReminderMentionView(discord.ui.View):
"""A button to opt-in to get notified of someone else's reminder."""

def __init__(self, cog: "Reminders", reminder: dict, expiration: Duration):
def __init__(self, cog: Reminders, reminder: dict, expiration: Duration):
super().__init__()

self.cog = cog
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/utils/snekbox/_cog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import contextlib
from collections.abc import Iterable
from functools import partial
Expand Down
2 changes: 0 additions & 2 deletions bot/exts/utils/snekbox/_eval.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from __future__ import annotations

import contextlib
from dataclasses import dataclass, field
from signal import Signals
Expand Down
Loading
Loading