Skip to content

Commit 0ccd4fb

Browse files
authored
Merge branch 'main' into swfarnsworth/tag-updates
2 parents 0ff5fad + 8f5a763 commit 0ccd4fb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1202
-1199
lines changed

.github/workflows/lint-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
- name: Upload a Build Artifact
7373
if: always() && steps.prepare-artifact.outcome == 'success'
7474
continue-on-error: true
75-
uses: actions/upload-artifact@v3
75+
uses: actions/upload-artifact@v4
7676
with:
7777
name: pull-request-payload
7878
path: pull_request_payload.json

.github/workflows/status_embed.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
curl -s -H "Authorization: token $GITHUB_TOKEN" ${{ github.event.workflow_run.artifacts_url }} > artifacts.json
4444
DOWNLOAD_URL=$(cat artifacts.json | jq -r '.artifacts[] | select(.name == "pull-request-payload") | .archive_download_url')
4545
[ -z "$DOWNLOAD_URL" ] && exit 1
46-
wget --quiet --header="Authorization: token $GITHUB_TOKEN" -O pull_request_payload.zip $DOWNLOAD_URL || exit 2
46+
curl -sSL -H "Authorization: token $GITHUB_TOKEN" -o pull_request_payload.zip $DOWNLOAD_URL || exit 2
4747
unzip -p pull_request_payload.zip > pull_request_payload.json
4848
[ -s pull_request_payload.json ] || exit 3
4949
echo "::set-output name=pr_author_login::$(jq -r '.user.login // empty' pull_request_payload.json)"

bot/exts/backend/branding/_cog.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from enum import Enum
88
from operator import attrgetter
99

10-
import async_timeout
1110
import discord
1211
from arrow import Arrow
1312
from async_rediscache import RedisCache
@@ -158,7 +157,7 @@ async def apply_asset(self, asset_type: AssetType, download_url: str) -> bool:
158157

159158
timeout = 10 # Seconds.
160159
try:
161-
with async_timeout.timeout(timeout): # Raise after `timeout` seconds.
160+
async with asyncio.timeout(timeout): # Raise after `timeout` seconds.
162161
await pydis.edit(**{asset_type.value: file})
163162
except discord.HTTPException:
164163
log.exception("Asset upload to Discord failed.")

bot/exts/filtering/_ui/ui.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ def format_response_error(e: ResponseCodeError) -> Embed:
155155
"""Format the response error into an embed."""
156156
description = ""
157157
if isinstance(e.response_json, list):
158-
description = "\n".join(f" {error}" for error in e.response_json)
158+
description = "\n".join(f"- {error}" for error in e.response_json)
159159
elif isinstance(e.response_json, dict):
160160
if "non_field_errors" in e.response_json:
161161
non_field_errors = e.response_json.pop("non_field_errors")
162-
description += "\n".join(f" {error}" for error in non_field_errors) + "\n"
162+
description += "\n".join(f"- {error}" for error in non_field_errors) + "\n"
163163
for field, errors in e.response_json.items():
164-
description += "\n".join(f" {field} - {error}" for error in errors) + "\n"
164+
description += "\n".join(f"- {field} - {error}" for error in errors) + "\n"
165165

166166
description = description.strip()
167167
if len(description) > MAX_EMBED_DESCRIPTION:

bot/exts/filtering/_utils.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import inspect
66
import pkgutil
77
import types
8+
import warnings
89
from abc import ABC, abstractmethod
910
from collections import defaultdict
1011
from collections.abc import Callable, Iterable
@@ -15,6 +16,7 @@
1516
import discord
1617
import regex
1718
from discord.ext.commands import Command
19+
from pydantic import PydanticDeprecatedSince20
1820
from pydantic_core import core_schema
1921

2022
import bot
@@ -181,11 +183,16 @@ def inherited(attr: str) -> bool:
181183

182184
# If a new attribute with the value MUST_SET_UNIQUE was defined in an abstract class, record it.
183185
if inspect.isabstract(cls):
184-
for attribute in dir(cls):
185-
if getattr(cls, attribute, None) is FieldRequiring.MUST_SET_UNIQUE:
186-
if not inherited(attribute):
187-
# A new attribute with the value MUST_SET_UNIQUE.
188-
FieldRequiring.__unique_attributes[cls][attribute] = set()
186+
with warnings.catch_warnings():
187+
# The code below will raise a warning about the use the __fields__ attr on a pydantic model
188+
# This will continue to be warned about until removed in pydantic 3.0
189+
# This warning is a false-positive as only the custom MUST_SET_UNIQUE attr is used here
190+
warnings.simplefilter("ignore", category=PydanticDeprecatedSince20)
191+
for attribute in dir(cls):
192+
if getattr(cls, attribute, None) is FieldRequiring.MUST_SET_UNIQUE:
193+
if not inherited(attribute):
194+
# A new attribute with the value MUST_SET_UNIQUE.
195+
FieldRequiring.__unique_attributes[cls][attribute] = set()
189196
return
190197

191198
for attribute in dir(cls):

bot/exts/fun/off_topic_names.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ async def toggle_ot_name_activity(self, ctx: Context, name: str, active: bool) -
8181
async def list_ot_names(self, ctx: Context, active: bool = True) -> None:
8282
"""Send an embed containing active/deactivated off-topic channel names."""
8383
result = await self.bot.api_client.get("bot/off-topic-channel-names", params={"active": json.dumps(active)})
84-
lines = sorted(f" {name}" for name in result)
84+
lines = sorted(f"- {name}" for name in result)
8585
embed = Embed(
8686
title=f"{'Active' if active else 'Deactivated'} off-topic names (`{len(result)}` total)",
8787
colour=Colour.blue()
@@ -286,7 +286,7 @@ async def search_command(self, ctx: Context, *, query: OffTopicName) -> None:
286286
close_matches = difflib.get_close_matches(query, result.keys(), n=10, cutoff=0.70)
287287

288288
# Send Results
289-
lines = sorted(f" {result[name]}" for name in in_matches.union(close_matches))
289+
lines = sorted(f"- {result[name]}" for name in in_matches.union(close_matches))
290290
embed = Embed(
291291
title="Query results",
292292
colour=Colour.blue()

bot/exts/help_channels/_channel.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919

2020
NEW_POST_MSG = """
2121
**Remember to:**
22-
**Ask** your Python question, not if you can ask or if there's an expert who can help.
23-
**Show** a code sample as text (rather than a screenshot) and the error message, if you've got one.
24-
**Explain** what you expect to happen and what actually happens.
22+
- **Ask** your Python question, not if you can ask or if there's an expert who can help.
23+
- **Show** a code sample as text (rather than a screenshot) and the error message, if you've got one.
24+
- **Explain** what you expect to happen and what actually happens.
2525
2626
:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.
2727
"""

bot/exts/info/code_snippets.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ def _snippet_to_codeblock(self, file_contents: str, file_path: str, start_line:
210210
if not is_valid_language:
211211
language = ""
212212

213+
if language == "pyi":
214+
language = "py"
215+
213216
# Adds a label showing the file path to the snippet
214217
if start_line == end_line:
215218
ret = f"`{file_path}` line {start_line}\n"

bot/exts/info/doc/_cog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ async def get_command(self, ctx: commands.Context, *, symbol_name: str | None) -
325325
colour=discord.Colour.blue()
326326
)
327327

328-
lines = sorted(f" [`{name}`]({url})" for name, url in self.base_urls.items())
328+
lines = sorted(f"- [`{name}`]({url})" for name, url in self.base_urls.items())
329329
if self.base_urls:
330330
await LinePaginator.paginate(lines, ctx, inventory_embed, max_size=400, empty=False)
331331

bot/exts/info/patreon.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async def send_current_supporters(self, channel: discord.abc.Messageable, automa
7777

7878
# Filter out any members where this is not their highest tier.
7979
patrons = [member for member in role.members if get_patreon_tier(member) == tier]
80-
patron_names = [f" {patron}" for patron in patrons]
80+
patron_names = [f"- {patron}" for patron in patrons]
8181

8282
embed = discord.Embed(
8383
title=role.name,

bot/exts/info/source.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def get_source_link(self, source_item: SourceType) -> tuple[str, str, int | None
6969

7070
# Handle tag file location differently than others to avoid errors in some cases
7171
if not first_line_no:
72-
file_location = Path(filename).relative_to("bot/")
72+
file_location = Path(filename)
7373
else:
7474
file_location = Path(filename).relative_to(Path.cwd()).as_posix()
7575

bot/exts/moderation/infraction/management.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,8 @@ async def search_user(self, ctx: Context, user: MemberOrUser | discord.Object) -
299299
user_str = escape_markdown(str(user))
300300
else:
301301
if infraction_list:
302-
user = infraction_list[0]["user"]
303-
user_str = escape_markdown(user["name"]) + f"#{user['discriminator']:04}"
302+
user_data = infraction_list[0]["user"]
303+
user_str = escape_markdown(user_data["name"]) + f"#{user_data['discriminator']:04}"
304304
else:
305305
user_str = str(user.id)
306306

bot/exts/moderation/voice_gate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ async def voice_verify(self, ctx: Context, *_) -> None:
181181
if failed:
182182
embed = discord.Embed(
183183
title="Voice Gate failed",
184-
description=FAILED_MESSAGE.format(reasons="\n".join(f" You {reason}." for reason in failed_reasons)),
184+
description=FAILED_MESSAGE.format(reasons="\n".join(f"- You {reason}." for reason in failed_reasons)),
185185
color=Colour.red()
186186
)
187187
try:

bot/exts/moderation/watchchannels/_watchchannel.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
from pydis_core.site_api import ResponseCodeError
1313
from pydis_core.utils import scheduling
1414
from pydis_core.utils.channel import get_or_fetch_channel
15+
from pydis_core.utils.logging import CustomLogger
1516
from pydis_core.utils.members import get_or_fetch_member
1617

1718
from bot.bot import Bot
1819
from bot.constants import BigBrother as BigBrotherConfig, Guild as GuildConfig, Icons
1920
from bot.exts.filtering._filters.unique.discord_token import DiscordTokenFilter
2021
from bot.exts.filtering._filters.unique.webhook import WEBHOOK_URL_RE
2122
from bot.exts.moderation.modlog import ModLog
22-
from bot.log import CustomLogger, get_logger
23+
from bot.log import get_logger
2324
from bot.pagination import LinePaginator
2425
from bot.utils import CogABCMeta, messages, time
2526

@@ -352,7 +353,7 @@ async def prepare_watched_users_data(
352353
list_data["info"] = {}
353354
for user_id, user_data in watched_iter:
354355
member = await get_or_fetch_member(ctx.guild, user_id)
355-
line = f" `{user_id}`"
356+
line = f"- `{user_id}`"
356357
if member:
357358
line += f" ({member.name}#{member.discriminator})"
358359
inserted_at = user_data["inserted_at"]

bot/exts/recruitment/talentpool/_cog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ async def list_nominations(
279279
return ["*None*"]
280280

281281
for nomination in nominations:
282-
line = f" `{nomination.user_id}`"
282+
line = f"- `{nomination.user_id}`"
283283

284284
member = await get_or_fetch_member(ctx.guild, nomination.user_id)
285285
if member:

bot/log.py

Lines changed: 9 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,29 @@
11
import logging
22
import os
33
import sys
4-
from logging import Logger, handlers
4+
from logging import handlers
55
from pathlib import Path
6-
from typing import TYPE_CHECKING, cast
76

87
import coloredlogs
98
import sentry_sdk
9+
from pydis_core.utils import logging as core_logging
1010
from sentry_sdk.integrations.logging import LoggingIntegration
1111
from sentry_sdk.integrations.redis import RedisIntegration
1212

1313
from bot import constants
1414

15-
TRACE_LEVEL = 5
16-
17-
18-
if TYPE_CHECKING:
19-
LoggerClass = Logger
20-
else:
21-
LoggerClass = logging.getLoggerClass()
22-
23-
24-
class CustomLogger(LoggerClass):
25-
"""Custom implementation of the `Logger` class with an added `trace` method."""
26-
27-
def trace(self, msg: str, *args, **kwargs) -> None:
28-
"""
29-
Log 'msg % args' with severity 'TRACE'.
30-
31-
To pass exception information, use the keyword argument exc_info with
32-
a true value, e.g.
33-
34-
logger.trace("Houston, we have an %s", "interesting problem", exc_info=1)
35-
"""
36-
if self.isEnabledFor(TRACE_LEVEL):
37-
self.log(TRACE_LEVEL, msg, *args, **kwargs)
38-
39-
40-
def get_logger(name: str | None = None) -> CustomLogger:
41-
"""Utility to make mypy recognise that logger is of type `CustomLogger`."""
42-
return cast(CustomLogger, logging.getLogger(name))
15+
get_logger = core_logging.get_logger
4316

4417

4518
def setup() -> None:
4619
"""Set up loggers."""
47-
logging.TRACE = TRACE_LEVEL
48-
logging.addLevelName(TRACE_LEVEL, "TRACE")
49-
logging.setLoggerClass(CustomLogger)
50-
5120
root_log = get_logger()
5221

53-
format_string = "%(asctime)s | %(name)s | %(levelname)s | %(message)s"
54-
log_format = logging.Formatter(format_string)
55-
5622
if constants.FILE_LOGS:
5723
log_file = Path("logs", "bot.log")
5824
log_file.parent.mkdir(exist_ok=True)
5925
file_handler = handlers.RotatingFileHandler(log_file, maxBytes=5242880, backupCount=7, encoding="utf8")
60-
file_handler.setFormatter(log_format)
26+
file_handler.setFormatter(core_logging.log_format)
6127
root_log.addHandler(file_handler)
6228

6329
if "COLOREDLOGS_LEVEL_STYLES" not in os.environ:
@@ -69,18 +35,11 @@ def setup() -> None:
6935
}
7036

7137
if "COLOREDLOGS_LOG_FORMAT" not in os.environ:
72-
coloredlogs.DEFAULT_LOG_FORMAT = format_string
38+
coloredlogs.DEFAULT_LOG_FORMAT = core_logging.log_format._fmt
7339

74-
coloredlogs.install(level=TRACE_LEVEL, logger=root_log, stream=sys.stdout)
40+
coloredlogs.install(level=core_logging.TRACE_LEVEL, logger=root_log, stream=sys.stdout)
7541

7642
root_log.setLevel(logging.DEBUG if constants.DEBUG_MODE else logging.INFO)
77-
get_logger("discord").setLevel(logging.WARNING)
78-
get_logger("websockets").setLevel(logging.WARNING)
79-
get_logger("chardet").setLevel(logging.WARNING)
80-
get_logger("async_rediscache").setLevel(logging.WARNING)
81-
82-
# Set back to the default of INFO even if asyncio's debug mode is enabled.
83-
get_logger("asyncio").setLevel(logging.INFO)
8443

8544
_set_trace_loggers()
8645

@@ -121,13 +80,13 @@ def _set_trace_loggers() -> None:
12180
level_filter = constants.Bot.trace_loggers
12281
if level_filter:
12382
if level_filter.startswith("*"):
124-
get_logger().setLevel(TRACE_LEVEL)
83+
get_logger().setLevel(core_logging.TRACE_LEVEL)
12584

12685
elif level_filter.startswith("!"):
127-
get_logger().setLevel(TRACE_LEVEL)
86+
get_logger().setLevel(core_logging.TRACE_LEVEL)
12887
for logger_name in level_filter.strip("!,").split(","):
12988
get_logger(logger_name).setLevel(logging.DEBUG)
13089

13190
else:
13291
for logger_name in level_filter.strip(",").split(","):
133-
get_logger(logger_name).setLevel(TRACE_LEVEL)
92+
get_logger(logger_name).setLevel(core_logging.TRACE_LEVEL)

bot/resources/tags/args-kwargs.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ These special parameters allow functions to take arbitrary amounts of positional
1111
`**kwargs` will ingest an arbitrary amount of **keyword arguments**, and store it in a dictionary. There can be **no** additional parameters **after** `**kwargs` in the parameter list.
1212

1313
**Use cases**
14-
**Decorators** (see `/tag decorators`)
15-
**Inheritance** (overriding methods)
16-
**Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break)
17-
**Flexibility** (writing functions that behave like `dict()` or `print()`)
14+
- **Decorators** (see `/tag decorators`)
15+
- **Inheritance** (overriding methods)
16+
- **Future proofing** (in the case of the first two bullet points, if the parameters change, your code won't break)
17+
- **Flexibility** (writing functions that behave like `dict()` or `print()`)
1818

1919
*See* `/tag positional-keyword` *for information about positional and keyword arguments*

bot/resources/tags/blocking.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ async def ping(ctx):
1919
A blocking operation is wherever you do something without `await`ing it. This tells Python that this step must be completed before it can do anything else. Common examples of blocking operations, as simple as they may seem, include: outputting text, adding two numbers and appending an item onto a list. Most common Python libraries have an asynchronous version available to use in asynchronous contexts.
2020

2121
**`async` libraries**
22-
The standard async library - `asyncio`
23-
Asynchronous web requests - `aiohttp`
24-
Talking to PostgreSQL asynchronously - `asyncpg`
25-
MongoDB interactions asynchronously - `motor`
26-
Check out [this](https://github.com/timofurrer/awesome-asyncio) list for even more!
22+
- The standard async library - `asyncio`
23+
- Asynchronous web requests - `aiohttp`
24+
- Talking to PostgreSQL asynchronously - `asyncpg`
25+
- MongoDB interactions asynchronously - `motor`
26+
- Check out [this](https://github.com/timofurrer/awesome-asyncio) list for even more!

bot/resources/tags/contribute.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ embed:
55
Looking to contribute to Open Source Projects for the first time? Want to add a feature or fix a bug on the bots on this server? We have on-going projects that people can contribute to, even if you've never contributed to open source before!
66

77
**Projects to Contribute to**
8-
[Sir Lancebot](https://github.com/python-discord/sir-lancebot) - our fun, beginner-friendly bot
9-
[Python](https://github.com/python-discord/bot) - our utility & moderation bot
10-
[Site](https://github.com/python-discord/site) - resources, guides, and more
8+
- [Sir Lancebot](https://github.com/python-discord/sir-lancebot) - our fun, beginner-friendly bot
9+
- [Python](https://github.com/python-discord/bot) - our utility & moderation bot
10+
- [Site](https://github.com/python-discord/site) - resources, guides, and more
1111

1212
**Where to start**
1313
1. Read our [contribution guide](https://pythondiscord.com/pages/guides/pydis-guides/contributing/)

bot/resources/tags/decorators.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ Finished!
2929
```
3030

3131
More information:
32-
[Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U)
33-
[Real python article](https://realpython.com/primer-on-python-decorators/)
32+
- [Corey Schafer's video on decorators](https://youtu.be/FsAPt_9Bf3U)
33+
- [Real python article](https://realpython.com/primer-on-python-decorators/)

bot/resources/tags/environments.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ If Python's `sys.executable` doesn't match pip's, then they are currently using
1818

1919
**Why use a virtual environment?**
2020

21-
Resolve dependency issues by allowing the use of different versions of a package for different projects. For example, you could use Package A v2.7 for Project X and Package A v1.3 for Project Y.
22-
Make your project self-contained and reproducible by capturing all package dependencies in a requirements file. Try running `pip freeze` to see what you currently have installed!
23-
Keep your global `site-packages/` directory tidy by removing the need to install packages system-wide which you might only need for one project.
21+
- Resolve dependency issues by allowing the use of different versions of a package for different projects. For example, you could use Package A v2.7 for Project X and Package A v1.3 for Project Y.
22+
- Make your project self-contained and reproducible by capturing all package dependencies in a requirements file. Try running `pip freeze` to see what you currently have installed!
23+
- Keep your global `site-packages/` directory tidy by removing the need to install packages system-wide which you might only need for one project.
2424

2525

2626
**Further reading:**
2727

28-
[Python Virtual Environments: A Primer](https://realpython.com/python-virtual-environments-a-primer)
29-
[pyenv: Simple Python Version Management](https://github.com/pyenv/pyenv)
28+
- [Python Virtual Environments: A Primer](https://realpython.com/python-virtual-environments-a-primer)
29+
- [pyenv: Simple Python Version Management](https://github.com/pyenv/pyenv)

bot/resources/tags/foo.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ Common examples include `foobar`, `foo`, `bar`, `baz`, and `qux`.
88
Python has its own metasyntactic variables, namely `spam`, `eggs`, and `bacon`. This is a reference to a [Monty Python](https://en.wikipedia.org/wiki/Monty_Python) sketch (the eponym of the language).
99

1010
More information:
11-
[History of foobar](https://en.wikipedia.org/wiki/Foobar)
12-
[Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29)
11+
- [History of foobar](https://en.wikipedia.org/wiki/Foobar)
12+
- [Monty Python sketch](https://en.wikipedia.org/wiki/Spam_%28Monty_Python%29)

0 commit comments

Comments
 (0)