String & core: Add support for G flag in matchers. Closes GH-1072.

This commit is contained in:
Valentin Lorentz 2015-05-15 16:48:00 +02:00
parent 7031e47ebd
commit ed37fb6646
4 changed files with 29 additions and 8 deletions

View File

@ -191,7 +191,7 @@ class String(callbacks.Plugin):
len = wrap(len, ['text']) len = wrap(len, ['text'])
@internationalizeDocstring @internationalizeDocstring
def re(self, irc, msg, args, ff, text): def re(self, irc, msg, args, f, text):
"""<regexp> <text> """<regexp> <text>
If <regexp> is of the form m/regexp/flags, returns the portion of If <regexp> is of the form m/regexp/flags, returns the portion of
@ -199,10 +199,6 @@ class String(callbacks.Plugin):
s/regexp/replacement/flags, returns the result of applying such a s/regexp/replacement/flags, returns the result of applying such a
regexp to <text>. regexp to <text>.
""" """
if isinstance(ff, (types.FunctionType, types.MethodType)):
f = ff
else:
f = lambda s: ff.search(s) and ff.search(s).group(0) or ''
if f('') and len(f(' ')) > len(f(''))+1: # Matches the empty string. if f('') and len(f(' ')) > len(f(''))+1: # Matches the empty string.
s = _('You probably don\'t want to match the empty string.') s = _('You probably don\'t want to match the empty string.')
irc.error(s) irc.error(s)
@ -210,12 +206,14 @@ class String(callbacks.Plugin):
t = self.registryValue('re.timeout') t = self.registryValue('re.timeout')
try: try:
v = process(f, text, timeout=t, pn=self.name(), cn='re') v = process(f, text, timeout=t, pn=self.name(), cn='re')
if isinstance(v, list):
v = format('%L', v)
irc.reply(v) irc.reply(v)
except commands.ProcessTimeoutError as e: except commands.ProcessTimeoutError as e:
irc.error("ProcessTimeoutError: %s" % (e,)) irc.error("ProcessTimeoutError: %s" % (e,))
except re.error as e: except re.error as e:
irc.error(e.args[0]) irc.error(e.args[0])
re = thread(wrap(re, [first('regexpMatcher', 'regexpReplacer'), re = thread(wrap(re, [first('regexpMatcherMany', 'regexpReplacer'),
'text'])) 'text']))
def xor(self, irc, msg, args, password, text): def xor(self, irc, msg, args, password, text):

View File

@ -117,6 +117,8 @@ class StringTestCase(PluginTestCase):
self.assertResponse('re s/user/luser/ user user', 'luser user') self.assertResponse('re s/user/luser/ user user', 'luser user')
self.assertNotRegexp('re m/foo/ bar', 'has no attribute') self.assertNotRegexp('re m/foo/ bar', 'has no attribute')
self.assertResponse('re m/a\S+y/ "the bot angryman is hairy"','angry') self.assertResponse('re m/a\S+y/ "the bot angryman is hairy"','angry')
self.assertResponse('re m/a\S+y/g "the bot angryman is hairy"',
'angry and airy')
def testReNotEmptyString(self): def testReNotEmptyString(self):
self.assertError('re s//foo/g blah') self.assertError('re s//foo/g blah')

View File

@ -450,6 +450,7 @@ def _getRe(f):
return get return get
getMatcher = _getRe(utils.str.perlReToPythonRe) getMatcher = _getRe(utils.str.perlReToPythonRe)
getMatcherMany = _getRe(utils.str.perlReToFindall)
getReplacer = _getRe(utils.str.perlReToReplacer) getReplacer = _getRe(utils.str.perlReToReplacer)
def getNick(irc, msg, args, state): def getNick(irc, msg, args, state):
@ -772,6 +773,7 @@ wrappers = ircutils.IrcDict({
'private': private, 'private': private,
'public': public, 'public': public,
'regexpMatcher': getMatcher, 'regexpMatcher': getMatcher,
'regexpMatcherMany': getMatcherMany,
'regexpReplacer': getReplacer, 'regexpReplacer': getReplacer,
'seenNick': getSeenNick, 'seenNick': getSeenNick,
'something': getSomething, 'something': getSomething,

View File

@ -170,7 +170,7 @@ def _getSep(s, allowBraces=False):
'"%s"' % braces) '"%s"' % braces)
return separator return separator
def perlReToPythonRe(s): def perlReToPythonRe(s, allowG=False):
"""Converts a string representation of a Perl regular expression (i.e., """Converts a string representation of a Perl regular expression (i.e.,
m/^foo$/i or /foo|bar/) to a Python regular expression. m/^foo$/i or /foo|bar/) to a Python regular expression.
""" """
@ -188,15 +188,34 @@ def perlReToPythonRe(s):
if opener != closer: if opener != closer:
regexp = regexp.replace('\\'+closer, closer) regexp = regexp.replace('\\'+closer, closer)
flag = 0 flag = 0
g = False
try: try:
for c in flags.upper(): for c in flags.upper():
if c == 'G' and allowG:
g = True
continue
flag |= getattr(re, c) flag |= getattr(re, c)
except AttributeError: except AttributeError:
raise ValueError('Invalid flag: %s' % c) raise ValueError('Invalid flag: %s' % c)
try: try:
return re.compile(regexp, flag) r = re.compile(regexp, flag)
except re.error as e: except re.error as e:
raise ValueError(str(e)) raise ValueError(str(e))
if allowG:
return (r, g)
else:
return r
def perlReToFindall(s):
"""Converts a string representation of a Perl regular expression (i.e.,
m/^foo$/i or /foo|bar/) to a Python regular expression, with support for
G flag
"""
(r, g) = perlReToPythonRe(s, allowG=True)
if g:
return lambda s: r.findall(s)
else:
return lambda s: r.search(s) and r.search(s).group(0) or ''
def perlReToReplacer(s): def perlReToReplacer(s):
"""Converts a string representation of a Perl regular expression (i.e., """Converts a string representation of a Perl regular expression (i.e.,