4

给 Python 的正则匹配限制执行时间

 2 years ago
source link: https://blog.lilydjwg.me/2013/5/25/set-a-time-limit-for-python-regex-matching.39335.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

给 Python 的正则匹配限制执行时间

本文来自依云's Blog,转载请注明。

看到这个标题,你也许会想,这个需要限制么?不是很快就出来结果了么?

感谢 Just Great Software,虽然我没买它的产品,但是其说明书(可免费下载)中的正则教程详细地论述了这点。所以我在自己的 xmpptalk 机器人中一直不敢接受用户输入的正则表达式。引述其中的一句话:「People with little regex experience have surprising skill at coming up with exponentially complex regular expressions.」(不太懂正则的人经常能令人惊奇地写出指数级复杂度的正则。)

但很不幸,我从这里抄到的匹配网址的正则就有这种问题。在将其的修改版给我的 XMPP 机器人 Lisa 使用后,Lisa 两次被含有括号的链接搞到没响应……

所以,如果要使用用户输入的正则,我必须限制其匹配时间。方法也很简单——使用信号就可以了。当 Python 在匹配正则时如果收到信号,会转而调用信号处理器,然后再接着匹配。如果信号处理器抛出了异常,那么此异常会传播到调用正则匹配的地方,从而中断匹配操作。

示例如下:

regex_with_timeout.py
#!/usr/bin/env python3
import re
# import regex as re
import signal
def timed_out(b, c):
print('alarmed')
raise RuntimeError()
signal.signal(signal.SIGALRM, timed_out)
signal.setitimer(signal.ITIMER_REAL, 0.1, 0)
s = '<aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>'
r = re.compile(r'''(?:<(?:[^<>]+)*>)+b''')
try:
r.findall(s)
except RuntimeError:
print('time exceeded')

被注释掉的那句是调用mrab-regex-hg这个正则引擎的;它不会回溯时出这种问题。

优化下代码,写成方便使用(使用了TimeoutError,所以适用于 Python 3.3+):

import contextlib
import signal
@contextlib.contextmanager
def execution_timeout(timeout):
def timed_out(signum, sigframe):
raise TimeoutError
old_hdl = signal.signal(signal.SIGALRM, timed_out)
old_itimer = signal.setitimer(signal.ITIMER_REAL, timeout, 0)
yield
signal.setitimer(signal.ITIMER_REAL, *old_itimer)
signal.signal(signal.SIGALRM, old_hdl)

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK