Repository: sopel-irc/sopel

Issue:
Example "e.g." text for help commands omits first character and includes escape character
Let there be these lines in the configuration:
`help_prefix = \-`
`prefix = \-`

Using the help command to get detailed help for something produces this output:
`<kwaaak>	-help time`
` <Sopel>	kwaaak: Returns the current time.`
` <Sopel>	kwaaak: e.g. \- America/New_York`

This is generated by:
https://github.com/sopel-irc/sopel/blob/master/sopel/modules/help.py#L52

I have looked briefly into figuring out bot.doc, but did not manage to do it so far.


Discussion:
`\-` is not a valid value for `help_prefix`. Which is to say, `help_prefix` should not be escaped the way `prefix` is, because it's not used in a regular expression. It's expected to be one character.

The code that does the replacement probably shouldn't use the length of `help_prefix` to do its work:

https://github.com/sopel-irc/sopel/blob/4b8fa0a18862148b7e9032e7bfcd788b1eb1a1f1/sopel/loader.py#L186-L187

Module command examples should use the default `.` prefix in all cases, so this code can simply always replace the first character of the example, and the module API documentation will have to reflect that.

I'll put this in 6.6.0, since I would very much like to stop adding things to 6.5.2 and release it.

Resolving patch:
diff --git a/sopel/loader.py b/sopel/loader.py
--- a/sopel/loader.py
+++ b/sopel/loader.py
@@ -7,6 +7,10 @@
 import sys
 
 from sopel.tools import compile_rule, itervalues, get_command_regexp, get_nickname_command_regexp
+from sopel.config import core_section
+
+default_prefix = core_section.CoreSection.help_prefix.default
+del core_section
 
 if sys.version_info.major >= 3:
     basestring = (str, bytes)
@@ -171,7 +175,7 @@ def clean_callable(func, config):
             example = func.example[0]["example"]
             example = example.replace('$nickname', nick)
             if example[0] != help_prefix and not example.startswith(nick):
-                example = help_prefix + example[len(help_prefix):]
+                example = example.replace(default_prefix, help_prefix, 1)
         if doc or example:
             cmds = []
             cmds.extend(getattr(func, 'commands', []))
diff --git a/sopel/module.py b/sopel/module.py
--- a/sopel/module.py
+++ b/sopel/module.py
@@ -377,27 +377,39 @@ def helper(bot, trigger, match=None):
 class example(object):
     """Decorate a function with an example.
 
-    Add an example attribute into a function and generate a test.
+    Args:
+        msg:
+            (required) The example command as sent by a user on IRC. If it is
+            a prefixed command, the command prefix used in the example must
+            match the default `config.core.help_prefix` for compatibility with
+            the built-in help module.
+        result:
+            What the example command is expected to output. If given, a test is
+            generated using `msg` as input. The test behavior can be modified
+            by the remaining optional arguments.
+        privmsg:
+            If true, the test will behave as if the input was sent to the bot
+            in a private message. If false (default), the test will treat the
+            input as having come from a channel.
+        admin:
+            Whether to treat the test message as having been sent by a bot
+            admin (`trigger.admin == True`).
+        owner:
+            Whether to treat the test message as having been sent by the bot's
+            owner (`trigger.owner == True`).
+        repeat:
+            Integer number of times to repeat the test. Useful for commands
+            that return random results.
+        re:
+            If true, `result` is parsed as a regular expression. Also useful
+            for commands that return random results, or that call an external
+            API that doesn't always return the same value.
+        ignore:
+            List of outputs to ignore. Strings in this list are always
+            interpreted as regular expressions.
     """
-    # TODO dat doc doe >_<
     def __init__(self, msg, result=None, privmsg=False, admin=False,
                  owner=False, repeat=1, re=False, ignore=None):
-        """Accepts arguments for the decorator.
-
-        Args:
-            msg - The example message to give to the function as input.
-            result - Resulting output from calling the function with msg.
-            privmsg - If true, make the message appear to have sent in a
-                private message to the bot. If false, make it appear to have
-                come from a channel.
-            admin - Bool. Make the message appear to have come from an admin.
-            owner - Bool. Make the message appear to have come from an owner.
-            repeat - How many times to repeat the test. Useful for tests that
-                return random stuff.
-            re - Bool. If true, result is interpreted as a regular expression.
-            ignore - a list of outputs to ignore.
-
-        """
         # Wrap result into a list for get_example_test
         if isinstance(result, list):
             self.result = result


Test patch: