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
3 changes: 2 additions & 1 deletion bot/exts/utils/snekbox/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from bot.bot import Bot
from bot.exts.utils.snekbox._cog import CodeblockConverter, Snekbox, SupportedPythonVersions
from bot.exts.utils.snekbox._cog import CodeblockConverter, Snekbox
from bot.exts.utils.snekbox._constants import SupportedPythonVersions
from bot.exts.utils.snekbox._eval import EvalJob, EvalResult

__all__ = ("CodeblockConverter", "EvalJob", "EvalResult", "Snekbox", "SupportedPythonVersions")
Expand Down
38 changes: 16 additions & 22 deletions bot/exts/utils/snekbox/_cog.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from __future__ import annotations

import contextlib
import re
from collections.abc import Iterable
from functools import partial
from operator import attrgetter
from textwrap import dedent
from typing import Literal, NamedTuple, TYPE_CHECKING, get_args
from typing import NamedTuple, TYPE_CHECKING, get_args

from discord import AllowedMentions, HTTPException, Interaction, Message, NotFound, Reaction, User, enums, ui
from discord.ext.commands import Cog, Command, Context, Converter, command, guild_only
Expand All @@ -19,6 +18,19 @@
from bot.decorators import redirect_output
from bot.exts.filtering._filter_lists.extension import TXT_LIKE_FILES
from bot.exts.help_channels._channel import is_help_forum_post
from bot.exts.utils.snekbox._constants import (
ANSI_REGEX,
DEFAULT_PYTHON_VERSION,
ESCAPE_REGEX,
MAX_OUTPUT_BLOCK_CHARS,
MAX_OUTPUT_BLOCK_LINES,
NO_SNEKBOX_CATEGORIES,
NO_SNEKBOX_CHANNELS,
REDO_EMOJI,
REDO_TIMEOUT,
SNEKBOX_ROLES,
SupportedPythonVersions,
)
from bot.exts.utils.snekbox._eval import EvalJob, EvalResult
from bot.exts.utils.snekbox._io import FileAttachment
from bot.log import get_logger
Expand All @@ -29,9 +41,6 @@

log = get_logger(__name__)

ANSI_REGEX = re.compile(r"\N{ESC}\[[0-9;:]*m")
ESCAPE_REGEX = re.compile("[`\u202E\u200B]{3,}")

# The timeit command should only output the very last line, so all other output should be suppressed.
# This will be used as the setup code along with any setup code provided.
TIMEIT_SETUP_WRAPPER = """
Expand Down Expand Up @@ -74,21 +83,6 @@ def print_last_line():
{setup}
"""

# Max to display in a codeblock before sending to a paste service
# This also applies to text files
MAX_OUTPUT_BLOCK_LINES = 10
MAX_OUTPUT_BLOCK_CHARS = 1000

# The Snekbox commands' whitelists and blacklists.
NO_SNEKBOX_CHANNELS = (Channels.python_general,)
NO_SNEKBOX_CATEGORIES = ()
SNEKBOX_ROLES = (Roles.helpers, Roles.moderators, Roles.admins, Roles.owners, Roles.python_community, Roles.partners)

REDO_EMOJI = "\U0001f501" # :repeat:
REDO_TIMEOUT = 30

SupportedPythonVersions = Literal["3.13", "3.13t", "3.14"]

class FilteredFiles(NamedTuple):
allowed: list[FileAttachment]
blocked: list[FileAttachment]
Expand Down Expand Up @@ -609,7 +603,7 @@ async def eval_command(
) -> None:
"""Run Python code and get the results."""
code: list[str]
python_version = python_version or get_args(SupportedPythonVersions)[0]
python_version = python_version or DEFAULT_PYTHON_VERSION
job = EvalJob.from_code("\n".join(code)).as_version(python_version)
await self.run_job(ctx, job)

Expand Down Expand Up @@ -650,7 +644,7 @@ async def timeit_command(
) -> None:
"""Profile Python Code to find execution time."""
code: list[str]
python_version = python_version or get_args(SupportedPythonVersions)[0]
python_version = python_version or DEFAULT_PYTHON_VERSION
args = self.prepare_timeit_input(code)
job = EvalJob(args, version=python_version, name="timeit")

Expand Down
24 changes: 24 additions & 0 deletions bot/exts/utils/snekbox/_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import re
from typing import Literal

from bot.constants import Channels, Roles

ANSI_REGEX = re.compile(r"\N{ESC}\[[0-9;:]*m")
ESCAPE_REGEX = re.compile("[`\u202E\u200B]{3,}")

# Max to display in a codeblock before sending to a paste service
# This also applies to text files
MAX_OUTPUT_BLOCK_LINES = 10
MAX_OUTPUT_BLOCK_CHARS = 1000

# The Snekbox commands' whitelists and blacklists.
NO_SNEKBOX_CHANNELS = (Channels.python_general,)
NO_SNEKBOX_CATEGORIES = ()
SNEKBOX_ROLES = (Roles.helpers, Roles.moderators, Roles.admins, Roles.owners, Roles.python_community, Roles.partners)

REDO_EMOJI = "\U0001f501" # :repeat:
REDO_TIMEOUT = 30

SupportedPythonVersions = Literal["3.13", "3.14", "3.14t"]

DEFAULT_PYTHON_VERSION: SupportedPythonVersions = "3.14"
11 changes: 3 additions & 8 deletions bot/exts/utils/snekbox/_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,14 @@
import contextlib
from dataclasses import dataclass, field
from signal import Signals
from typing import TYPE_CHECKING

from discord.utils import escape_markdown, escape_mentions

from bot.constants import Emojis
from bot.exts.utils.snekbox._constants import DEFAULT_PYTHON_VERSION, SupportedPythonVersions
from bot.exts.utils.snekbox._io import FILE_COUNT_LIMIT, FILE_SIZE_LIMIT, FileAttachment, sizeof_fmt
from bot.log import get_logger

if TYPE_CHECKING:
from bot.exts.utils.snekbox._cog import SupportedPythonVersions

log = get_logger(__name__)

SIGKILL = 9
Expand All @@ -26,7 +23,7 @@ class EvalJob:
args: list[str]
files: list[FileAttachment] = field(default_factory=list)
name: str = "eval"
version: SupportedPythonVersions = "3.13"
version: SupportedPythonVersions = DEFAULT_PYTHON_VERSION

@classmethod
def from_code(cls, code: str, path: str = "main.py") -> EvalJob:
Expand Down Expand Up @@ -144,10 +141,8 @@ def get_failed_files_str(self, char_max: int = 85) -> str:

def get_status_message(self, job: EvalJob) -> str:
"""Return a user-friendly message corresponding to the process's return code."""
if job.version == "3.13t":
if job.version[-1] == "t":
version_text = job.version.replace("t", " [free threaded](<https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython>)")
elif job.version == "3.14":
version_text = "3.14 [pre-release](<https://docs.python.org/3.14/whatsnew/3.14.html#development>)"
else:
version_text = job.version
msg = f"Your {version_text} {job.name} job"
Expand Down
6 changes: 3 additions & 3 deletions tests/bot/exts/utils/snekbox/test_snekbox.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import unittest
from base64 import b64encode
from typing import get_args
from unittest.mock import AsyncMock, MagicMock, Mock, call, create_autospec, patch

from discord import AllowedMentions
Expand All @@ -11,7 +10,8 @@
from bot import constants
from bot.errors import LockedResourceError
from bot.exts.utils import snekbox
from bot.exts.utils.snekbox import EvalJob, EvalResult, Snekbox, SupportedPythonVersions
from bot.exts.utils.snekbox import EvalJob, EvalResult, Snekbox
from bot.exts.utils.snekbox._constants import DEFAULT_PYTHON_VERSION
from bot.exts.utils.snekbox._io import FileAttachment
from tests.helpers import MockBot, MockContext, MockMember, MockMessage, MockReaction, MockUser

Expand All @@ -22,7 +22,7 @@ def setUp(self):
self.bot = MockBot()
self.cog = Snekbox(bot=self.bot)
self.job = EvalJob.from_code("import random")
self.default_version = get_args(SupportedPythonVersions)[0]
self.default_version = DEFAULT_PYTHON_VERSION

@staticmethod
def code_args(code: str) -> tuple[EvalJob]:
Expand Down
Loading