diff options
author | 2022-11-05 16:09:04 +0000 | |
---|---|---|
committer | 2022-11-05 16:09:04 +0000 | |
commit | a2c30bfc9aa345d8699b1ddecc198a58d5e60e56 (patch) | |
tree | 191a37c8ad9d0295049bfee96f1f008bbd2759e0 | |
parent | Refactor some commands to avoid unnecessary API calls (diff) | |
parent | Merge pull request #2230 from meatballs/create-nomodule-tag (diff) |
Merge branch 'main' into 2306-nominations-cleanup
-rw-r--r-- | bot/exts/backend/security.py (renamed from bot/exts/filters/security.py) | 0 | ||||
-rw-r--r-- | bot/exts/info/information.py | 33 | ||||
-rw-r--r-- | bot/exts/utils/snekbox.py | 15 | ||||
-rw-r--r-- | bot/resources/tags/nomodule.md | 13 | ||||
-rw-r--r-- | bot/resources/tags/slicing.md | 24 | ||||
-rw-r--r-- | docker-compose.yml | 2 | ||||
-rw-r--r-- | tests/bot/exts/backend/test_security.py (renamed from tests/bot/exts/filters/test_security.py) | 2 | ||||
-rw-r--r-- | tests/bot/exts/info/test_information.py | 24 |
8 files changed, 86 insertions, 27 deletions
diff --git a/bot/exts/filters/security.py b/bot/exts/backend/security.py index 27e4d9752..27e4d9752 100644 --- a/bot/exts/filters/security.py +++ b/bot/exts/backend/security.py diff --git a/bot/exts/info/information.py b/bot/exts/info/information.py index 2592e093d..733597dd8 100644 --- a/bot/exts/info/information.py +++ b/bot/exts/info/information.py @@ -523,8 +523,19 @@ class Information(Cog): """Shows information about the raw API response in a copy-pasteable Python format.""" await self.send_raw_content(ctx, message, json=True) + async def _set_rules_command_help(self) -> None: + help_string = f"{self.rules.help}\n\n" + help_string += "__Available keywords per rule__:\n\n" + + full_rules = await self.bot.api_client.get("rules", params={"link_format": "md"}) + + for index, (_, keywords) in enumerate(full_rules, start=1): + help_string += f"**Rule {index}**: {', '.join(keywords)}\n\r" + + self.rules.help = help_string + @command(aliases=("rule",)) - async def rules(self, ctx: Context, *args: Optional[str]) -> Optional[Set[int]]: + async def rules(self, ctx: Context, *, args: Optional[str]) -> Optional[Set[int]]: """ Provides a link to all rules or, if specified, displays specific rule(s). @@ -541,13 +552,15 @@ class Information(Cog): for rule_keyword in rule_keywords: keyword_to_rule_number[rule_keyword] = rule_number - for word in args: - try: - rule_numbers.append(int(word)) - except ValueError: - if (kw := word.lower()) not in keyword_to_rule_number: - break - keywords.append(kw) + if args: + for word in args.split(maxsplit=100): + try: + rule_numbers.append(int(word)) + except ValueError: + # Stop on first invalid keyword/index to allow for normal messaging after + if (kw := word.lower()) not in keyword_to_rule_number: + break + keywords.append(kw) if not rule_numbers and not keywords: # Neither rules nor keywords were submitted. Return the default description. @@ -578,6 +591,10 @@ class Information(Cog): return final_rule_numbers + async def cog_load(self) -> None: + """Carry out cog asynchronous initialisation.""" + await self._set_rules_command_help() + async def setup(bot: Bot) -> None: """Load the Information cog.""" diff --git a/bot/exts/utils/snekbox.py b/bot/exts/utils/snekbox.py index 8e961b67c..5e217a288 100644 --- a/bot/exts/utils/snekbox.py +++ b/bot/exts/utils/snekbox.py @@ -125,7 +125,8 @@ class PythonVersionSwitcherButton(ui.Button): version_to_switch_to: Literal["3.10", "3.11"], snekbox_cog: "Snekbox", ctx: Context, - code: str + code: str, + args: Optional[list[str]] = None ) -> None: self.version_to_switch_to = version_to_switch_to super().__init__(label=f"Run in {self.version_to_switch_to}", style=enums.ButtonStyle.primary) @@ -134,6 +135,7 @@ class PythonVersionSwitcherButton(ui.Button): self.ctx = ctx self.job_name = job_name self.code = code + self.args = args async def callback(self, interaction: Interaction) -> None: """ @@ -150,7 +152,9 @@ class PythonVersionSwitcherButton(ui.Button): # The log arg on send_job will stop the actual job from running. await interaction.message.delete() - await self.snekbox_cog.run_job(self.job_name, self.ctx, self.version_to_switch_to, self.code) + await self.snekbox_cog.run_job( + self.job_name, self.ctx, self.version_to_switch_to, self.code, args=self.args + ) class Snekbox(Cog): @@ -165,7 +169,8 @@ class Snekbox(Cog): job_name: str, current_python_version: Literal["3.10", "3.11"], ctx: Context, - code: str + code: str, + args: Optional[list[str]] = None ) -> None: """Return a view that allows the user to change what version of Python their code is run on.""" if current_python_version == "3.10": @@ -177,7 +182,7 @@ class Snekbox(Cog): allowed_users=(ctx.author.id,), allowed_roles=MODERATION_ROLES, ) - view.add_item(PythonVersionSwitcherButton(job_name, alt_python_version, self, ctx, code)) + view.add_item(PythonVersionSwitcherButton(job_name, alt_python_version, self, ctx, code, args)) view.add_item(interactions.DeleteMessageButton()) return view @@ -357,7 +362,7 @@ class Snekbox(Cog): response = await ctx.send("Attempt to circumvent filter detected. Moderator team has been alerted.") else: allowed_mentions = AllowedMentions(everyone=False, roles=False, users=[ctx.author]) - view = self.build_python_version_switcher_view(job_name, python_version, ctx, code) + view = self.build_python_version_switcher_view(job_name, python_version, ctx, code, args) response = await ctx.send(msg, allowed_mentions=allowed_mentions, view=view) view.message = response diff --git a/bot/resources/tags/nomodule.md b/bot/resources/tags/nomodule.md new file mode 100644 index 000000000..adae555be --- /dev/null +++ b/bot/resources/tags/nomodule.md @@ -0,0 +1,13 @@ +**ModuleNotFoundError** + +If you've installed a package but you're getting a ModuleNotFoundError when you try to import it, it's likely that the environment where your code is running is different from the one where you did the installation. + +You can read about Python environments at `!tags environments` and `!tags venv`. + +Common causes of this problem include: + +• You installed your package using `pip install ...`. It could be that the `pip` command is not pointing to the environment where your code runs. For greater control, you could instead run pip as a module within the python environment you specify: +``` +python -m pip install <your_package> +``` +• Your editor/ide is configured to create virtual environments automatically (PyCharm is configured this way by default). diff --git a/bot/resources/tags/slicing.md b/bot/resources/tags/slicing.md new file mode 100644 index 000000000..717fc46b7 --- /dev/null +++ b/bot/resources/tags/slicing.md @@ -0,0 +1,24 @@ +--- +aliases: ["slice", "seqslice", "seqslicing", "sequence-slice", "sequence-slicing"] +embed: + title: "Sequence slicing" +--- +*Slicing* is a way of accessing a part of a sequence by specifying a start, stop, and step. As with normal indexing, negative numbers can be used to count backwards. + +**Examples** +```py +>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] +>>> letters[2:] # from element 2 to the end +['c', 'd', 'e', 'f', 'g'] +>>> letters[:4] # up to element 4 +['a', 'b', 'c', 'd'] +>>> letters[3:5] # elements 3 and 4 -- the right bound is not included +['d', 'e'] +>>> letters[2:-1:2] # Every other element between 2 and the last +['c', 'e'] +>>> letters[::-1] # The whole list in reverse +['g', 'f', 'e', 'd', 'c', 'b', 'a'] +>>> words = "Hello world!" +>>> words[2:7] # Strings are also sequences +"llo w" +``` diff --git a/docker-compose.yml b/docker-compose.yml index be7370d6b..bc53c482b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: postgres: << : *logging << : *restart_policy - image: postgres:13-alpine + image: postgres:15-alpine environment: POSTGRES_DB: pysite POSTGRES_PASSWORD: pysite diff --git a/tests/bot/exts/filters/test_security.py b/tests/bot/exts/backend/test_security.py index 007b7b1eb..c3985c609 100644 --- a/tests/bot/exts/filters/test_security.py +++ b/tests/bot/exts/backend/test_security.py @@ -2,7 +2,7 @@ import unittest from discord.ext.commands import NoPrivateMessage -from bot.exts.filters import security +from bot.exts.backend import security from tests.helpers import MockBot, MockContext diff --git a/tests/bot/exts/info/test_information.py b/tests/bot/exts/info/test_information.py index 9f5143c01..65595e959 100644 --- a/tests/bot/exts/info/test_information.py +++ b/tests/bot/exts/info/test_information.py @@ -603,9 +603,9 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): async def test_return_none_if_one_rule_number_is_invalid(self): test_cases = [ - (('1', '6', '7', '8'), (6, 7, 8)), - (('10', "first"), (10, )), - (("first", 10), (10, )) + ("1 6 7 8", (6, 7, 8)), + ("10 first", (10,)), + ("first 10", (10,)) ] for raw_user_input, extracted_rule_numbers in test_cases: @@ -614,7 +614,7 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): str(rule_number) for rule_number in extracted_rule_numbers if rule_number < 1 or rule_number > len(self.full_rules)) - final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, args=raw_user_input) self.assertEqual( self.ctx.send.call_args, @@ -624,26 +624,26 @@ class RuleCommandTests(unittest.IsolatedAsyncioTestCase): async def test_return_correct_rule_numbers(self): test_cases = [ - (("1", "2", "first"), {1, 2}), - (("1", "hello", "2", "second"), {1}), - (("second", "third", "unknown", "999"), {2, 3}) + ("1 2 first", {1, 2}), + ("1 hello 2 second", {1}), + ("second third unknown 999", {2, 3}), ] for raw_user_input, expected_matched_rule_numbers in test_cases: with self.subTest(identifier=raw_user_input): - final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, args=raw_user_input) self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) async def test_return_default_rules_when_no_input_or_no_match_are_found(self): test_cases = [ - ((), None), - (("hello", "2", "second"), None), - (("hello", "999"), None), + ("", None), + ("hello 2 second", None), + ("hello 999", None), ] for raw_user_input, expected_matched_rule_numbers in test_cases: with self.subTest(identifier=raw_user_input): - final_rule_numbers = await self.cog.rules(self.cog, self.ctx, *raw_user_input) + final_rule_numbers = await self.cog.rules(self.cog, self.ctx, args=raw_user_input) embed = self.ctx.send.call_args.kwargs['embed'] self.assertEqual(information.DEFAULT_RULES_DESCRIPTION, embed.description) self.assertEqual(expected_matched_rule_numbers, final_rule_numbers) |