Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Doc/library/re.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_re.py
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,15 @@ def test_pickling(self):
# current pickle expects the _compile() reconstructor in re module
from re import _compile

def test_copying(self):
import copy
p = re.compile(r'(?P<int>\d+)(?:\.(?P<frac>\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)
Expand Down
3 changes: 3 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
131 changes: 14 additions & 117 deletions Modules/_sre.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@ static const char copyright[] =
/* defining this one enables tracing */
#undef VERBOSE

/* -------------------------------------------------------------------- */
/* optional features */

/* enables copy/deepcopy handling (work in progress) */
#undef USE_BUILTIN_COPY

/* -------------------------------------------------------------------- */

#if defined(_MSC_VER)
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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(&copy->groupindex, memo) ||
!deepcopy(&copy->indexgroup, memo) ||
!deepcopy(&copy->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 *
Expand Down Expand Up @@ -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**) &copy->pattern, memo) ||
!deepcopy(&copy->string, memo) ||
!deepcopy(&copy->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,
Expand Down
52 changes: 5 additions & 47 deletions Modules/clinic/_sre.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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]*/