From fdbd01151dbd5feea3e4c0316d102db3d2a2a412 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 16 Apr 2017 10:16:03 +0300 Subject: bpo-10076: Compiled regular expression and match objects now are copyable. (#1000) --- Doc/library/re.rst | 10 ++++ Lib/test/test_re.py | 9 ++++ Misc/NEWS | 3 ++ Modules/_sre.c | 131 ++++++------------------------------------------ Modules/clinic/_sre.c.h | 52 ++----------------- 5 files changed, 41 insertions(+), 164 deletions(-) diff --git a/Doc/library/re.rst b/Doc/library/re.rst index ce90ec7..0fa7196 100644 --- a/Doc/library/re.rst +++ b/Doc/library/re.rst @@ -970,6 +970,11 @@ attributes: The pattern string from which the RE object was compiled. +.. versionchanged:: 3.7 + Added support of :func:`copy.copy` and :func:`copy.deepcopy`. Compiled + regular expression objects are considered atomic. + + .. _match-objects: Match Objects @@ -1171,6 +1176,11 @@ Match objects support the following methods and attributes: The string passed to :meth:`~regex.match` or :meth:`~regex.search`. +.. versionchanged:: 3.7 + Added support of :func:`copy.copy` and :func:`copy.deepcopy`. Match objects + are considered atomic. + + .. _re-examples: Regular Expression Examples diff --git a/Lib/test/test_re.py b/Lib/test/test_re.py index b3b29f8..da5c953 100644 --- a/Lib/test/test_re.py +++ b/Lib/test/test_re.py @@ -971,6 +971,15 @@ class ReTests(unittest.TestCase): # current pickle expects the _compile() reconstructor in re module from re import _compile + def test_copying(self): + import copy + p = re.compile(r'(?P\d+)(?:\.(?P\d*))?') + self.assertIs(copy.copy(p), p) + self.assertIs(copy.deepcopy(p), p) + m = p.match('12.34') + self.assertIs(copy.copy(m), m) + self.assertIs(copy.deepcopy(m), m) + def test_constants(self): self.assertEqual(re.I, re.IGNORECASE) self.assertEqual(re.L, re.LOCALE) diff --git a/Misc/NEWS b/Misc/NEWS index 5981b3a..512592e 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -313,6 +313,9 @@ Extension Modules Library ------- +- bpo-10076: Compiled regular expression and match objects in the re module + now support copy.copy() and copy.deepcopy() (they are considered atomic). + - bpo-30068: _io._IOBase.readlines will check if it's closed first when hint is present. diff --git a/Modules/_sre.c b/Modules/_sre.c index ef8295a..84e2f93 100644 --- a/Modules/_sre.c +++ b/Modules/_sre.c @@ -60,12 +60,6 @@ static const char copyright[] = #undef VERBOSE /* -------------------------------------------------------------------- */ -/* optional features */ - -/* enables copy/deepcopy handling (work in progress) */ -#undef USE_BUILTIN_COPY - -/* -------------------------------------------------------------------- */ #if defined(_MSC_VER) #pragma optimize("agtw", on) /* doesn't seem to make much difference... */ @@ -695,28 +689,6 @@ call(const char* module, const char* function, PyObject* args) return result; } -#ifdef USE_BUILTIN_COPY -static int -deepcopy(PyObject** object, PyObject* memo) -{ - PyObject* copy; - - if (!*object) - return 1; - - copy = call( - "copy", "deepcopy", - PyTuple_Pack(2, *object, memo) - ); - if (!copy) - return 0; - - Py_SETREF(*object, copy); - - return 1; /* success */ -} -#endif - /*[clinic input] _sre.SRE_Pattern.findall @@ -1229,60 +1201,24 @@ static PyObject * _sre_SRE_Pattern___copy___impl(PatternObject *self) /*[clinic end generated code: output=85dedc2db1bd8694 input=a730a59d863bc9f5]*/ { -#ifdef USE_BUILTIN_COPY - PatternObject* copy; - int offset; - - copy = PyObject_NEW_VAR(PatternObject, &Pattern_Type, self->codesize); - if (!copy) - return NULL; - - offset = offsetof(PatternObject, groups); - - Py_XINCREF(self->groupindex); - Py_XINCREF(self->indexgroup); - Py_XINCREF(self->pattern); - - memcpy((char*) copy + offset, (char*) self + offset, - sizeof(PatternObject) + self->codesize * sizeof(SRE_CODE) - offset); - copy->weakreflist = NULL; - - return (PyObject*) copy; -#else - PyErr_SetString(PyExc_TypeError, "cannot copy this pattern object"); - return NULL; -#endif + Py_INCREF(self); + return (PyObject *)self; } /*[clinic input] _sre.SRE_Pattern.__deepcopy__ memo: object + / [clinic start generated code]*/ static PyObject * -_sre_SRE_Pattern___deepcopy___impl(PatternObject *self, PyObject *memo) -/*[clinic end generated code: output=75efe69bd12c5d7d input=3959719482c07f70]*/ +_sre_SRE_Pattern___deepcopy__(PatternObject *self, PyObject *memo) +/*[clinic end generated code: output=2ad25679c1f1204a input=a465b1602f997bed]*/ { -#ifdef USE_BUILTIN_COPY - PatternObject* copy; - - copy = (PatternObject*) pattern_copy(self); - if (!copy) - return NULL; - - if (!deepcopy(©->groupindex, memo) || - !deepcopy(©->indexgroup, memo) || - !deepcopy(©->pattern, memo)) { - Py_DECREF(copy); - return NULL; - } - -#else - PyErr_SetString(PyExc_TypeError, "cannot deepcopy this pattern object"); - return NULL; -#endif + Py_INCREF(self); + return (PyObject *)self; } static PyObject * @@ -2298,63 +2234,24 @@ static PyObject * _sre_SRE_Match___copy___impl(MatchObject *self) /*[clinic end generated code: output=a779c5fc8b5b4eb4 input=3bb4d30b6baddb5b]*/ { -#ifdef USE_BUILTIN_COPY - MatchObject* copy; - Py_ssize_t slots, offset; - - slots = 2 * (self->pattern->groups+1); - - copy = PyObject_NEW_VAR(MatchObject, &Match_Type, slots); - if (!copy) - return NULL; - - /* this value a constant, but any compiler should be able to - figure that out all by itself */ - offset = offsetof(MatchObject, string); - - Py_XINCREF(self->pattern); - Py_XINCREF(self->string); - Py_XINCREF(self->regs); - - memcpy((char*) copy + offset, (char*) self + offset, - sizeof(MatchObject) + slots * sizeof(Py_ssize_t) - offset); - - return (PyObject*) copy; -#else - PyErr_SetString(PyExc_TypeError, "cannot copy this match object"); - return NULL; -#endif + Py_INCREF(self); + return (PyObject *)self; } /*[clinic input] _sre.SRE_Match.__deepcopy__ memo: object + / [clinic start generated code]*/ static PyObject * -_sre_SRE_Match___deepcopy___impl(MatchObject *self, PyObject *memo) -/*[clinic end generated code: output=2b657578eb03f4a3 input=b65b72489eac64cc]*/ +_sre_SRE_Match___deepcopy__(MatchObject *self, PyObject *memo) +/*[clinic end generated code: output=ba7cb46d655e4ee2 input=779d12a31c2c325e]*/ { -#ifdef USE_BUILTIN_COPY - MatchObject* copy; - - copy = (MatchObject*) match_copy(self); - if (!copy) - return NULL; - - if (!deepcopy((PyObject**) ©->pattern, memo) || - !deepcopy(©->string, memo) || - !deepcopy(©->regs, memo)) { - Py_DECREF(copy); - return NULL; - } - -#else - PyErr_SetString(PyExc_TypeError, "cannot deepcopy this match object"); - return NULL; -#endif + Py_INCREF(self); + return (PyObject *)self; } PyDoc_STRVAR(match_doc, diff --git a/Modules/clinic/_sre.c.h b/Modules/clinic/_sre.c.h index fcb23e8..5278323 100644 --- a/Modules/clinic/_sre.c.h +++ b/Modules/clinic/_sre.c.h @@ -383,33 +383,12 @@ _sre_SRE_Pattern___copy__(PatternObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(_sre_SRE_Pattern___deepcopy____doc__, -"__deepcopy__($self, /, memo)\n" +"__deepcopy__($self, memo, /)\n" "--\n" "\n"); #define _SRE_SRE_PATTERN___DEEPCOPY___METHODDEF \ - {"__deepcopy__", (PyCFunction)_sre_SRE_Pattern___deepcopy__, METH_FASTCALL, _sre_SRE_Pattern___deepcopy____doc__}, - -static PyObject * -_sre_SRE_Pattern___deepcopy___impl(PatternObject *self, PyObject *memo); - -static PyObject * -_sre_SRE_Pattern___deepcopy__(PatternObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - static const char * const _keywords[] = {"memo", NULL}; - static _PyArg_Parser _parser = {"O:__deepcopy__", _keywords, 0}; - PyObject *memo; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &memo)) { - goto exit; - } - return_value = _sre_SRE_Pattern___deepcopy___impl(self, memo); - -exit: - return return_value; -} + {"__deepcopy__", (PyCFunction)_sre_SRE_Pattern___deepcopy__, METH_O, _sre_SRE_Pattern___deepcopy____doc__}, PyDoc_STRVAR(_sre_compile__doc__, "compile($module, /, pattern, flags, code, groups, groupindex,\n" @@ -671,33 +650,12 @@ _sre_SRE_Match___copy__(MatchObject *self, PyObject *Py_UNUSED(ignored)) } PyDoc_STRVAR(_sre_SRE_Match___deepcopy____doc__, -"__deepcopy__($self, /, memo)\n" +"__deepcopy__($self, memo, /)\n" "--\n" "\n"); #define _SRE_SRE_MATCH___DEEPCOPY___METHODDEF \ - {"__deepcopy__", (PyCFunction)_sre_SRE_Match___deepcopy__, METH_FASTCALL, _sre_SRE_Match___deepcopy____doc__}, - -static PyObject * -_sre_SRE_Match___deepcopy___impl(MatchObject *self, PyObject *memo); - -static PyObject * -_sre_SRE_Match___deepcopy__(MatchObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) -{ - PyObject *return_value = NULL; - static const char * const _keywords[] = {"memo", NULL}; - static _PyArg_Parser _parser = {"O:__deepcopy__", _keywords, 0}; - PyObject *memo; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &memo)) { - goto exit; - } - return_value = _sre_SRE_Match___deepcopy___impl(self, memo); - -exit: - return return_value; -} + {"__deepcopy__", (PyCFunction)_sre_SRE_Match___deepcopy__, METH_O, _sre_SRE_Match___deepcopy____doc__}, PyDoc_STRVAR(_sre_SRE_Scanner_match__doc__, "match($self, /)\n" @@ -732,4 +690,4 @@ _sre_SRE_Scanner_search(ScannerObject *self, PyObject *Py_UNUSED(ignored)) { return _sre_SRE_Scanner_search_impl(self); } -/*[clinic end generated code: output=5df18da8e2dc762c input=a9049054013a1b77]*/ +/*[clinic end generated code: output=e6dab3ba8864da9e input=a9049054013a1b77]*/ -- cgit v0.12