diff -r ebbd2ec5c7cb Lib/sre_constants.py --- a/Lib/sre_constants.py Sat Nov 23 16:16:29 2013 +0100 +++ b/Lib/sre_constants.py Sat Nov 23 18:19:16 2013 +0200 @@ -250,6 +250,8 @@ f.write("#define SRE_FLAG_DOTALL %d\n" % SRE_FLAG_DOTALL) f.write("#define SRE_FLAG_UNICODE %d\n" % SRE_FLAG_UNICODE) f.write("#define SRE_FLAG_VERBOSE %d\n" % SRE_FLAG_VERBOSE) + f.write("#define SRE_FLAG_DEBUG %d\n" % SRE_FLAG_DEBUG) + f.write("#define SRE_FLAG_ASCII %d\n" % SRE_FLAG_ASCII) f.write("#define SRE_INFO_PREFIX %d\n" % SRE_INFO_PREFIX) f.write("#define SRE_INFO_LITERAL %d\n" % SRE_INFO_LITERAL) diff -r ebbd2ec5c7cb Lib/test/test_re.py --- a/Lib/test/test_re.py Sat Nov 23 16:16:29 2013 +0100 +++ b/Lib/test/test_re.py Sat Nov 23 18:19:16 2013 +0200 @@ -1164,6 +1164,54 @@ self.assertEqual(m.group(2), "y") +class PatternReprTests(unittest.TestCase): + def test_without_flags(self): + self.assertEqual(repr(re.compile('random pattern')), + "re.compile('random pattern', re.UNICODE)") + + def test_ascii_flag_only(self): + self.assertEqual(repr(re.compile('random pattern', re.ASCII)), + "re.compile('random pattern', re.ASCII)") + + def test_single_flag(self): + self.assertEqual( + repr(re.compile('random pattern', re.IGNORECASE)), + "re.compile('random pattern', re.IGNORECASE|re.UNICODE)") + + def test_multiple_flags(self): + self.assertEqual( + repr(re.compile('random pattern', re.I|re.S|re.X)), + "re.compile('random pattern', "\ + "re.IGNORECASE|re.DOTALL|re.UNICODE|re.VERBOSE)") + + def test_inline_flags(self): + self.assertEqual(repr(re.compile(b'(?i)pattern')), + "re.compile(b'(?i)pattern', re.IGNORECASE)") + + def test_unknown_flags(self): + self.assertEqual(repr(re.compile('random pattern', 0x123000)), + "re.compile('random pattern', re.UNICODE|0x123000)") + + def test_bytes_without_flags(self): + self.assertEqual(repr(re.compile(b'bytes pattern')), + "re.compile(b'bytes pattern')") + + def test_bytes_unknown_flags(self): + self.assertEqual(repr(re.compile(b'random pattern', 0x123000)), + "re.compile(b'random pattern', 0x123000)") + + def test_quotes(self): + self.assertEqual( + repr(re.compile('random "double quoted" pattern')), + '''re.compile('random "double quoted" pattern', re.UNICODE)''') + self.assertEqual( + repr(re.compile("random 'single quoted' pattern")), + '''re.compile("random 'single quoted' pattern", re.UNICODE)''') + self.assertEqual( + repr(re.compile('''both 'single' and "double" quotes''')), + '''re.compile('both \\'single\\' and "double" quotes', re.UNICODE)''') + + class ImplementationTest(unittest.TestCase): """ Test implementation details of the re module. diff -r ebbd2ec5c7cb Misc/NEWS --- a/Misc/NEWS Sat Nov 23 16:16:29 2013 +0100 +++ b/Misc/NEWS Sat Nov 23 18:19:16 2013 +0200 @@ -68,6 +68,9 @@ Library ------- +- Issue #13592: Improved the repr for regular expression pattern objects. + Based on patch by Hugo Lopes Tavares. + - Issue #19689: Add ssl.create_default_context() factory function. It creates a new SSLContext object with secure default settings. diff -r ebbd2ec5c7cb Modules/_sre.c --- a/Modules/_sre.c Sat Nov 23 16:16:29 2013 +0100 +++ b/Modules/_sre.c Sat Nov 23 18:19:16 2013 +0200 @@ -1139,6 +1139,80 @@ #endif } +static PyObject * +pattern_repr(PatternObject *obj) +{ + static const struct { + const char *name; + int value; + } flag_names[] = { + {"re.TEMPLATE", SRE_FLAG_TEMPLATE}, + {"re.IGNORECASE", SRE_FLAG_IGNORECASE}, + {"re.LOCALE", SRE_FLAG_LOCALE}, + {"re.MULTILINE", SRE_FLAG_MULTILINE}, + {"re.DOTALL", SRE_FLAG_DOTALL}, + {"re.UNICODE", SRE_FLAG_UNICODE}, + {"re.VERBOSE", SRE_FLAG_VERBOSE}, + {"re.DEBUG", SRE_FLAG_DEBUG}, + {"re.ASCII", SRE_FLAG_ASCII}, + }; + PyObject *result = NULL; + PyObject *flag_items; + int i; + int flags = obj->flags; + + flag_items = PyList_New(0); + if (!flag_items) + return NULL; + + for (i = 0; i < Py_ARRAY_LENGTH(flag_names); i++) { + if (flags & flag_names[i].value) { + PyObject *item = PyUnicode_FromString(flag_names[i].name); + if (!item) + goto done; + + if (PyList_Append(flag_items, item) < 0) { + Py_DECREF(item); + goto done; + } + Py_DECREF(item); + flags &= ~flag_names[i].value; + } + } + if (flags) { + PyObject *item = PyUnicode_FromFormat("0x%x", flags); + if (!item) + goto done; + + if (PyList_Append(flag_items, item) < 0) { + Py_DECREF(item); + goto done; + } + Py_DECREF(item); + } + + if (PyList_Size(flag_items) > 0) { + PyObject *flags_result; + PyObject *sep = PyUnicode_FromString("|"); + if (!sep) + goto done; + flags_result = PyUnicode_Join(sep, flag_items); + Py_DECREF(sep); + if (!flags_result) + goto done; + result = PyUnicode_FromFormat("re.compile(%R, %S)", + obj->pattern, flags_result); + Py_DECREF(flags_result); + } + else { + result = PyUnicode_FromFormat("re.compile(%R)", obj->pattern); + } + +done: + Py_DECREF(flag_items); + return result; +} + PyDoc_STRVAR(pattern_match_doc, "match(string[, pos[, endpos]]) -> match object or None.\n\ Matches zero or more characters at the beginning of the string"); @@ -1214,7 +1288,7 @@ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ - 0, /* tp_repr */ + (reprfunc)pattern_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ diff -r ebbd2ec5c7cb Modules/sre_constants.h --- a/Modules/sre_constants.h Sat Nov 23 16:16:29 2013 +0100 +++ b/Modules/sre_constants.h Sat Nov 23 18:19:16 2013 +0200 @@ -81,6 +81,8 @@ #define SRE_FLAG_DOTALL 16 #define SRE_FLAG_UNICODE 32 #define SRE_FLAG_VERBOSE 64 +#define SRE_FLAG_DEBUG 128 +#define SRE_FLAG_ASCII 256 #define SRE_INFO_PREFIX 1 #define SRE_INFO_LITERAL 2 #define SRE_INFO_CHARSET 4