From 698280df7c321b9986ee000054bb9dbbb32625af Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 10 Sep 2008 17:44:35 +0000 Subject: Issue #3756: make re.escape() handle bytes as well as str. Patch by Andrew McNamara, reviewed and tweaked by myself. --- Lib/re.py | 46 +++++++++++++++++++++++++++++++--------------- Lib/test/test_re.py | 14 ++++++++++++++ Misc/NEWS | 2 ++ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/Lib/re.py b/Lib/re.py index 63a95fd..090ec8a 100644 --- a/Lib/re.py +++ b/Lib/re.py @@ -211,23 +211,38 @@ def template(pattern, flags=0): "Compile a template pattern, returning a pattern object" return _compile(pattern, flags|T) -_alphanum = {} -for c in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890': - _alphanum[c] = 1 -del c +_alphanum_str = frozenset( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890") +_alphanum_bytes = frozenset( + b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890") def escape(pattern): "Escape all non-alphanumeric characters in pattern." - s = list(pattern) - alphanum = _alphanum - for i in range(len(pattern)): - c = pattern[i] - if c not in alphanum: - if c == "\000": - s[i] = "\\000" + if isinstance(pattern, str): + alphanum = _alphanum_str + s = list(pattern) + for i in range(len(pattern)): + c = pattern[i] + if c not in alphanum: + if c == "\000": + s[i] = "\\000" + else: + s[i] = "\\" + c + return "".join(s) + else: + alphanum = _alphanum_bytes + s = [] + esc = ord(b"\\") + for c in pattern: + if c in alphanum: + s.append(c) else: - s[i] = "\\" + c - return pattern[:0].join(s) + if c == 0: + s.extend(b"\\000") + else: + s.append(esc) + s.append(c) + return bytes(s) # -------------------------------------------------------------------- # internals @@ -248,7 +263,8 @@ def _compile(*key): pattern, flags = key if isinstance(pattern, _pattern_type): if flags: - raise ValueError('Cannot process flags argument with a compiled pattern') + raise ValueError( + "Cannot process flags argument with a compiled pattern") return pattern if not sre_compile.isstring(pattern): raise TypeError("first argument must be string or compiled pattern") @@ -325,7 +341,7 @@ class Scanner: if i == j: break action = self.lexicon[m.lastindex-1][1] - if hasattr(action, '__call__'): + if hasattr(action, "__call__"): self.match = m action = action(self, m.group()) if action is not None: diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index 8229d4a..11fff78 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -416,6 +416,7 @@ class ReTests(unittest.TestCase): def test_re_escape(self): p="" + self.assertEqual(re.escape(p), p) for i in range(0, 256): p = p + chr(i) self.assertEqual(re.match(re.escape(chr(i)), chr(i)) is not None, @@ -426,6 +427,19 @@ class ReTests(unittest.TestCase): self.assertEqual(pat.match(p) is not None, True) self.assertEqual(pat.match(p).span(), (0,256)) + def test_re_escape_byte(self): + p=b"" + self.assertEqual(re.escape(p), p) + for i in range(0, 256): + b = bytes([i]) + p += b + self.assertEqual(re.match(re.escape(b), b) is not None, True) + self.assertEqual(re.match(re.escape(b), b).span(), (0,1)) + + pat=re.compile(re.escape(p)) + self.assertEqual(pat.match(p) is not None, True) + self.assertEqual(pat.match(p).span(), (0,256)) + def pickle_test(self, pickle): oldpat = re.compile('a(?:b|(c|e){1,2}?|d)+?(.)') s = pickle.dumps(oldpat) diff --git a/Misc/NEWS b/Misc/NEWS index d22d038..21d269b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -96,6 +96,8 @@ C API Library ------- +- Issue #3756: make re.escape() handle bytes as well as str. + - Issue #3800: fix filter() related bug in formatter.py. - Issue #874900: fix behaviour of threading module after a fork. -- cgit v0.12 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
"""A CallTip window class for Tkinter/IDLE.

After ToolTip.py, which uses ideas gleaned from PySol
Used by the CallTips IDLE extension.

"""
from Tkinter import *

class CallTip:

    def __init__(self, widget):
        self.widget = widget
        self.tipwindow = None
        self.id = None
        self.x = self.y = 0

    def showtip(self, text):
        " Display text in calltip window"
        # truncate overly long calltip
        if len(text) >= 79:
            text = text[:75] + ' ...'
        self.text = text
        if self.tipwindow or not self.text:
            return
        self.widget.see("insert")
        x, y, cx, cy = self.widget.bbox("insert")
        x = x + self.widget.winfo_rootx() + 2
        y = y + cy + self.widget.winfo_rooty()
        self.tipwindow = tw = Toplevel(self.widget)
        # XXX 12 Dec 2002 KBK The following command has two effects: It removes
        #     the calltip window border (good) but also causes (at least on
        #     Linux) the calltip to show as a top level window, burning through
        #     any other window dragged over it.  Also, shows on all viewports!
        tw.wm_overrideredirect(1)
        tw.wm_geometry("+%d+%d" % (x, y))
        try:
            # This command is only needed and available on Tk >= 8.4.0 for OSX
            # Without it, call tips intrude on the typing process by grabbing
            # the focus.
            tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
                       "help", "noActivates")
        except TclError:
            pass
        label = Label(tw, text=self.text, justify=LEFT,
                      background="#ffffe0", relief=SOLID, borderwidth=1,
                      font = self.widget['font'])
        label.pack()

    def hidetip(self):
        tw = self.tipwindow
        self.tipwindow = None
        if tw:
            tw.destroy()


###############################
#
# Test Code
#
class container: # Conceptually an editor_window
    def __init__(self):
        root = Tk()
        text = self.text = Text(root)
        text.pack(side=LEFT, fill=BOTH, expand=1)
        text.insert("insert", "string.split")
        root.update()
        self.calltip = CallTip(text)

        text.event_add("<<calltip-show>>", "(")
        text.event_add("<<calltip-hide>>", ")")
        text.bind("<<calltip-show>>", self.calltip_show)
        text.bind("<<calltip-hide>>", self.calltip_hide)

        text.focus_set()
        root.mainloop()

    def calltip_show(self, event):
        self.calltip.showtip("Hello world")

    def calltip_hide(self, event):
        self.calltip.hidetip()

def main():
    # Test code
    c=container()

if __name__=='__main__':
    main()