diff options
Diffstat (limited to '')
| -rw-r--r-- | bot/exts/utils/snekbox/__init__.py | 4 | ||||
| -rw-r--r-- | bot/exts/utils/snekbox/_cog.py | 47 | ||||
| -rw-r--r-- | bot/exts/utils/snekbox/_eval.py | 9 | ||||
| -rw-r--r-- | tests/bot/exts/utils/snekbox/test_snekbox.py | 38 | 
4 files changed, 57 insertions, 41 deletions
| diff --git a/bot/exts/utils/snekbox/__init__.py b/bot/exts/utils/snekbox/__init__.py index 92bf366be..fa91d0d6f 100644 --- a/bot/exts/utils/snekbox/__init__.py +++ b/bot/exts/utils/snekbox/__init__.py @@ -1,8 +1,8 @@  from bot.bot import Bot -from bot.exts.utils.snekbox._cog import CodeblockConverter, Snekbox +from bot.exts.utils.snekbox._cog import CodeblockConverter, Snekbox, SupportedPythonVersions  from bot.exts.utils.snekbox._eval import EvalJob, EvalResult -__all__ = ("CodeblockConverter", "EvalJob", "EvalResult", "Snekbox") +__all__ = ("CodeblockConverter", "EvalJob", "EvalResult", "Snekbox", "SupportedPythonVersions")  async def setup(bot: Bot) -> None: diff --git a/bot/exts/utils/snekbox/_cog.py b/bot/exts/utils/snekbox/_cog.py index 39f61c6e2..0afd3c408 100644 --- a/bot/exts/utils/snekbox/_cog.py +++ b/bot/exts/utils/snekbox/_cog.py @@ -87,7 +87,7 @@ SNEKBOX_ROLES = (Roles.helpers, Roles.moderators, Roles.admins, Roles.owners, Ro  REDO_EMOJI = "\U0001f501"  # :repeat:  REDO_TIMEOUT = 30 -SupportedPythonVersions = Literal["3.12", "3.13", "3.13t"] +SupportedPythonVersions = Literal["3.13", "3.13t", "3.14"]  class FilteredFiles(NamedTuple):      allowed: list[FileAttachment] @@ -569,7 +569,29 @@ class Snekbox(Cog):                  break              log.info(f"Re-evaluating code from message {ctx.message.id}:\n{job}") -    @command(name="eval", aliases=("e",), usage="[python_version] <code, ...>") +    @command( +        name="eval", +        aliases=("e",), +        usage="[python_version] <code, ...>", +        help=f""" +            Run Python code and get the results. + +            This command supports multiple lines of code, including formatted code blocks. +            Code can be re-evaluated by editing the original message within 10 seconds and +            clicking the reaction that subsequently appears. + +            The starting working directory `/home`, is a writeable temporary file system. +            Files created, excluding names with leading underscores, will be uploaded in the response. + +            If multiple codeblocks are in a message, all of them will be joined and evaluated, +            ignoring the text outside them. + +            The currently supported versions are {", ".join(get_args(SupportedPythonVersions))}. + +            We've done our best to make this sandboxed, but do let us know if you manage to find an +            issue with it! +        """ +    )      @guild_only()      @redirect_output(          destination_channel=Channels.bot_commands, @@ -585,26 +607,9 @@ class Snekbox(Cog):          *,          code: CodeblockConverter      ) -> None: -        """ -        Run Python code and get the results. - -        This command supports multiple lines of code, including formatted code blocks. -        Code can be re-evaluated by editing the original message within 10 seconds and -        clicking the reaction that subsequently appears. - -        The starting working directory `/home`, is a writeable temporary file system. -        Files created, excluding names with leading underscores, will be uploaded in the response. - -        If multiple codeblocks are in a message, all of them will be joined and evaluated, -        ignoring the text outside them. - -        The currently supported verisons are 3.12, 3.13, and 3.13t. - -        We've done our best to make this sandboxed, but do let us know if you manage to find an -        issue with it! -        """ +        """Run Python code and get the results."""          code: list[str] -        python_version = python_version or "3.12" +        python_version = python_version or get_args(SupportedPythonVersions)[0]          job = EvalJob.from_code("\n".join(code)).as_version(python_version)          await self.run_job(ctx, job) diff --git a/bot/exts/utils/snekbox/_eval.py b/bot/exts/utils/snekbox/_eval.py index ac67d1ed7..6136b6a81 100644 --- a/bot/exts/utils/snekbox/_eval.py +++ b/bot/exts/utils/snekbox/_eval.py @@ -26,7 +26,7 @@ class EvalJob:      args: list[str]      files: list[FileAttachment] = field(default_factory=list)      name: str = "eval" -    version: SupportedPythonVersions = "3.12" +    version: SupportedPythonVersions = "3.13"      @classmethod      def from_code(cls, code: str, path: str = "main.py") -> EvalJob: @@ -144,7 +144,12 @@ class EvalResult:      def get_status_message(self, job: EvalJob) -> str:          """Return a user-friendly message corresponding to the process's return code.""" -        version_text = job.version.replace("t", " [free threaded](<https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython>)") +        if job.version == "3.13t": +            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"          if self.returncode is None: diff --git a/tests/bot/exts/utils/snekbox/test_snekbox.py b/tests/bot/exts/utils/snekbox/test_snekbox.py index 9cfd75df8..69262bf61 100644 --- a/tests/bot/exts/utils/snekbox/test_snekbox.py +++ b/tests/bot/exts/utils/snekbox/test_snekbox.py @@ -1,6 +1,7 @@  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 @@ -10,7 +11,7 @@ from pydis_core.utils.paste_service import MAX_PASTE_SIZE  from bot import constants  from bot.errors import LockedResourceError  from bot.exts.utils import snekbox -from bot.exts.utils.snekbox import EvalJob, EvalResult, Snekbox +from bot.exts.utils.snekbox import EvalJob, EvalResult, Snekbox, SupportedPythonVersions  from bot.exts.utils.snekbox._io import FileAttachment  from tests.helpers import MockBot, MockContext, MockMember, MockMessage, MockReaction, MockUser @@ -21,6 +22,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          self.bot = MockBot()          self.cog = Snekbox(bot=self.bot)          self.job = EvalJob.from_code("import random") +        self.default_version = get_args(SupportedPythonVersions)[0]      @staticmethod      def code_args(code: str) -> tuple[EvalJob]: @@ -35,7 +37,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          context_manager = MagicMock()          context_manager.__aenter__.return_value = resp          self.bot.http_session.post.return_value = context_manager -        py_version = "3.12" +        py_version = self.default_version          job = EvalJob.from_code("import random").as_version(py_version)          self.assertEqual(await self.cog.post_job(job), EvalResult("Hi", 137)) @@ -104,9 +106,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):      def test_eval_result_message(self):          """EvalResult.get_message(), should return message."""          cases = ( -            ("ERROR", None, ("Your 3.12 eval job has failed", "ERROR", "")), -            ("", 128 + snekbox._eval.SIGKILL, ("Your 3.12 eval job timed out or ran out of memory", "", "")), -            ("", 255, ("Your 3.12 eval job has failed", "A fatal NsJail error occurred", "")) +            ("ERROR", None, (f"Your {self.default_version} eval job has failed", "ERROR", "")), +            ( +                "", +                128 + snekbox._eval.SIGKILL, +                (f"Your {self.default_version} eval job timed out or ran out of memory", "", "") +            ), +            ("", 255, (f"Your {self.default_version} eval job has failed", "A fatal NsJail error occurred", ""))          )          for stdout, returncode, expected in cases:              exp_msg, exp_err, exp_files_err = expected @@ -178,8 +184,8 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          mock_signals.return_value.name = "SIGTEST"          result = EvalResult(stdout="", returncode=127)          self.assertEqual( -            result.get_status_message(EvalJob([], version="3.12")), -            "Your 3.12 eval job has completed with return code 127 (SIGTEST)" +            result.get_status_message(EvalJob([])), +            f"Your {self.default_version} eval job has completed with return code 127 (SIGTEST)"          )      def test_eval_result_status_emoji(self): @@ -253,7 +259,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          self.cog.send_job = AsyncMock(return_value=response)          self.cog.continue_job = AsyncMock(return_value=None) -        await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.12", code=["MyAwesomeCode"]) +        await self.cog.eval_command(self.cog, ctx=ctx, python_version=self.default_version, code=["MyAwesomeCode"])          job = EvalJob.from_code("MyAwesomeCode")          self.cog.send_job.assert_called_once_with(ctx, job)          self.cog.continue_job.assert_called_once_with(ctx, response, "eval") @@ -267,7 +273,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          self.cog.continue_job = AsyncMock()          self.cog.continue_job.side_effect = (EvalJob.from_code("MyAwesomeFormattedCode"), None) -        await self.cog.eval_command(self.cog, ctx=ctx, python_version="3.12", code=["MyAwesomeCode"]) +        await self.cog.eval_command(self.cog, ctx=ctx, python_version=self.default_version, code=["MyAwesomeCode"])          expected_job = EvalJob.from_code("MyAwesomeFormattedCode")          self.cog.send_job.assert_called_with(ctx, expected_job) @@ -311,7 +317,7 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          ctx.send.assert_called_once()          self.assertEqual(              ctx.send.call_args.args[0], -            ":warning: Your 3.12 eval job has completed " +            f":warning: Your {self.default_version} eval job has completed "              "with return code 0.\n\n```ansi\n[No output]\n```"          )          allowed_mentions = ctx.send.call_args.kwargs["allowed_mentions"] @@ -335,13 +341,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=(False, []))          self.bot.get_cog.return_value = mocked_filter_cog -        job = EvalJob.from_code("MyAwesomeCode").as_version("3.12") +        job = EvalJob.from_code("MyAwesomeCode").as_version(self.default_version)          await self.cog.send_job(ctx, job),          ctx.send.assert_called_once()          self.assertEqual(              ctx.send.call_args.args[0], -            ":white_check_mark: Your 3.12 eval job " +            f":white_check_mark: Your {self.default_version} eval job "              "has completed with return code 0."              "\n\n```ansi\nWay too long beard\n```\nFull output: lookatmybeard.com"          ) @@ -362,13 +368,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=(False, []))          self.bot.get_cog.return_value = mocked_filter_cog -        job = EvalJob.from_code("MyAwesomeCode").as_version("3.12") +        job = EvalJob.from_code("MyAwesomeCode").as_version(self.default_version)          await self.cog.send_job(ctx, job),          ctx.send.assert_called_once()          self.assertEqual(              ctx.send.call_args.args[0], -            ":x: Your 3.12 eval job has completed with return code 127." +            f":x: Your {self.default_version} eval job has completed with return code 127."              "\n\n```ansi\nERROR\n```"          ) @@ -395,13 +401,13 @@ class SnekboxTests(unittest.IsolatedAsyncioTestCase):          mocked_filter_cog.filter_snekbox_output = AsyncMock(return_value=(False, disallowed_exts))          self.bot.get_cog.return_value = mocked_filter_cog -        job = EvalJob.from_code("MyAwesomeCode").as_version("3.12") +        job = EvalJob.from_code("MyAwesomeCode").as_version(self.default_version)          await self.cog.send_job(ctx, job),          ctx.send.assert_called_once()          res = ctx.send.call_args.args[0]          self.assertTrue( -            res.startswith(":white_check_mark: Your 3.12 eval job has completed with return code 0.") +            res.startswith(f":white_check_mark: Your {self.default_version} eval job has completed with return code 0.")          )          self.assertIn("Files with disallowed extensions can't be uploaded: **.disallowed, .disallowed2, ...**", res) | 
