Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

find: support escaping backslash, fix double-bolding, and add basic tests #2589

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
23 changes: 17 additions & 6 deletions sopel/builtins/find.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ def kick_cleanup(bot, trigger):
[:,]\s+)? # Followed by optional colon/comma and whitespace
s(?P<sep>/) # The literal s and a separator / as group 2
(?P<old> # Group 3 is the thing to find
(?:\\/|[^/])+ # One or more non-slashes or escaped slashes
(?:\\\\|\\/|[^/])+ # One or more non-slashes or escaped slashes
)
/ # The separator again
(?P<new> # Group 4 is what to replace with
(?:\\/|[^/])* # One or more non-slashes or escaped slashes
(?:\\\\|\\/|[^/])* # One or more non-slashes or escaped slashes
)
(?:/ # Optional separator followed by group 5 (flags)
(?P<flags>\S+)
Expand All @@ -136,11 +136,11 @@ def kick_cleanup(bot, trigger):
[:,]\s+)? # Followed by optional colon/comma and whitespace
s(?P<sep>\|) # The literal s and a separator | as group 2
(?P<old> # Group 3 is the thing to find
(?:\\\||[^|])+ # One or more non-pipe or escaped pipe
(?:\\\\|\\\||[^|])+ # One or more non-pipe or escaped pipe
)
\| # The separator again
(?P<new> # Group 4 is what to replace with
(?:\\\||[^|])* # One or more non-pipe or escaped pipe
(?:\\\\|\\\||[^|])* # One or more non-pipe or escaped pipe
)
(?:\| # Optional separator followed by group 5 (flags)
(?P<flags>\S+)
Expand All @@ -161,14 +161,16 @@ def findandreplace(bot, trigger):
return

sep = trigger.group('sep')
old = trigger.group('old').replace('\\%s' % sep, sep)
escape_sequence_pattern = re.compile(r'\\[\\%s]' % sep)

old = escape_sequence_pattern.sub(decode_escape, trigger.group('old'))
new = trigger.group('new')
me = False # /me command
flags = trigger.group('flags') or ''

# only clean/format the new string if it's non-empty
if new:
new = bold(new.replace('\\%s' % sep, sep))
new = bold(escape_sequence_pattern.sub(decode_escape, new))

# If g flag is given, replace all. Otherwise, replace once.
if 'g' in flags:
Expand Down Expand Up @@ -217,3 +219,12 @@ def repl(s):
phrase = '%s %s' % (trigger.nick, new_phrase)

bot.say(phrase)


def decode_escape(match):
print("Substituting %s" % match.group(0))
return {
r'\\': '\\',
r'\|': '|',
r'\/': '/',
}[match.group(0)]
61 changes: 61 additions & 0 deletions test/builtins/test_builtins_find.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Tests for Sopel's ``find`` plugin"""
from __future__ import annotations

import pytest

from sopel.formatting import bold
from sopel.tests import rawlist


TMP_CONFIG = """
[core]
owner = Admin
nick = Sopel
enable =
find
host = irc.libera.chat
"""


@pytest.fixture
def bot(botfactory, configfactory):
settings = configfactory('default.ini', TMP_CONFIG)
return botfactory.preloaded(settings, ['find'])


@pytest.fixture
def irc(bot, ircfactory):
return ircfactory(bot)


@pytest.fixture
def user(userfactory):
return userfactory('User')


@pytest.fixture
def channel():
return '#testing'


REPLACES_THAT_WORK = (
("A simple line.", r"s/line/message/", f"A simple {bold('message')}."),
("An escaped / line.", r"s/\//slash/", f"An escaped {bold('slash')} line."),
("A piped line.", r"s|line|replacement|", f"A piped {bold('replacement')}."),
("An escaped | line.", r"s|\||pipe|", f"An escaped {bold('pipe')} line."),
("An escaped \\ line.", r"s/\\/backslash/", f"An escaped {bold('backslash')} line."),
)


@pytest.mark.parametrize('original, command, result', REPLACES_THAT_WORK)
def test_valid_replacements(bot, irc, user, channel, original, command, result):
irc.channel_joined(channel, ['User'])
dgw marked this conversation as resolved.
Show resolved Hide resolved

irc.say(user, channel, original)
irc.say(user, channel, command)

assert len(bot.backend.message_sent) == 1, (
"The bot should respond with exactly one line.")
assert bot.backend.message_sent == rawlist(
"PRIVMSG %s :User meant to say: %s" % (channel, result),
dgw marked this conversation as resolved.
Show resolved Hide resolved
)
Loading