Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-regex for openSUSE:Factory checked in at 2026-04-14 17:48:12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-regex (Old) and /work/SRC/openSUSE:Factory/.python-regex.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-regex" Tue Apr 14 17:48:12 2026 rev:33 rq:1346265 version:2026.4.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-regex/python-regex.changes 2026-03-30 18:30:24.984868592 +0200 +++ /work/SRC/openSUSE:Factory/.python-regex.new.21863/python-regex.changes 2026-04-14 17:48:22.275667985 +0200 @@ -1,0 +2,6 @@ +Sun Apr 12 21:05:52 UTC 2026 - Dirk Müller <[email protected]> + +- update to 2026.4.4: + * more fixes for free-threading + +------------------------------------------------------------------- Old: ---- regex-2026.3.32.tar.gz New: ---- regex-2026.4.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-regex.spec ++++++ --- /var/tmp/diff_new_pack.0NviJI/_old 2026-04-14 17:48:23.219707006 +0200 +++ /var/tmp/diff_new_pack.0NviJI/_new 2026-04-14 17:48:23.219707006 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-regex -Version: 2026.3.32 +Version: 2026.4.4 Release: 0 Summary: Alternative regular expression module for Python License: Apache-2.0 ++++++ regex-2026.3.32.tar.gz -> regex-2026.4.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.3.32/PKG-INFO new/regex-2026.4.4/PKG-INFO --- old/regex-2026.3.32/PKG-INFO 2026-03-28 21:45:50.057209300 +0100 +++ new/regex-2026.4.4/PKG-INFO 2026-04-03 22:01:02.882440000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: regex -Version: 2026.3.32 +Version: 2026.4.4 Summary: Alternative regular expression module, to replace re. Author-email: Matthew Barnett <[email protected]> License-Expression: Apache-2.0 AND CNRI-Python diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.3.32/pyproject.toml new/regex-2026.4.4/pyproject.toml --- old/regex-2026.3.32/pyproject.toml 2026-03-28 21:45:46.000000000 +0100 +++ new/regex-2026.4.4/pyproject.toml 2026-04-03 22:00:59.000000000 +0200 @@ -4,7 +4,7 @@ [project] name = "regex" -version = "2026.3.32" +version = "2026.4.4" description = "Alternative regular expression module, to replace re." readme = "README.rst" authors = [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.3.32/regex/_main.py new/regex-2026.4.4/regex/_main.py --- old/regex-2026.3.32/regex/_main.py 2026-03-28 21:45:46.000000000 +0100 +++ new/regex-2026.4.4/regex/_main.py 2026-04-03 22:00:59.000000000 +0200 @@ -244,7 +244,7 @@ "VERSION1", "X", "VERBOSE", "W", "WORD", "error", "Regex", "__version__", "__doc__", "RegexFlag"] -__version__ = "2026.3.32" +__version__ = "2026.4.4" # -------------------------------------------------------------------- # Public interface. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.3.32/regex.egg-info/PKG-INFO new/regex-2026.4.4/regex.egg-info/PKG-INFO --- old/regex-2026.3.32/regex.egg-info/PKG-INFO 2026-03-28 21:45:50.000000000 +0100 +++ new/regex-2026.4.4/regex.egg-info/PKG-INFO 2026-04-03 22:01:02.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: regex -Version: 2026.3.32 +Version: 2026.4.4 Summary: Alternative regular expression module, to replace re. Author-email: Matthew Barnett <[email protected]> License-Expression: Apache-2.0 AND CNRI-Python diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.3.32/src/_regex.c new/regex-2026.4.4/src/_regex.c --- old/regex-2026.3.32/src/_regex.c 2026-03-28 21:45:46.000000000 +0100 +++ new/regex-2026.4.4/src/_regex.c 2026-04-03 22:00:59.000000000 +0200 @@ -212,7 +212,7 @@ " RE 2.3.0 Copyright (c) 1997-2002 by Secret Labs AB "; /* The exception to raise on error. */ -static PyObject* error_exception; +static PyObject* error_exception = NULL; /* The dictionary of Unicode properties. */ static PyObject* property_dict = NULL; @@ -2064,6 +2064,31 @@ Py_LOCAL_INLINE(PyObject*) get_object(char* module_name, char* object_name); +/* Ensures that the error_exception object is initialised. + * + * Unfortunately, because it's imported from the Python code, it can't be + * imported when the extension starts. + */ +Py_LOCAL_INLINE(BOOL) ensure_error_exception(void) { + if (error_exception) + return TRUE; + + #if defined(Py_GIL_DISABLED) + static PyMutex init_mutex = {0}; + PyMutex_Lock(&init_mutex); + #endif + + if (!error_exception) { + error_exception = get_object("regex._regex_core", "error"); + } + + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&init_mutex); + #endif + + return error_exception != NULL; +} + /* Sets the error message. */ Py_LOCAL_INLINE(void) set_error(int status, PyObject* object) { TRACE(("<<set_error>>\n")) @@ -2096,14 +2121,7 @@ PyErr_SetString(PyExc_TypeError, "string indices must be integers"); break; case RE_ERROR_INVALID_GROUP_REF: - if (!error_exception) { - error_exception = get_object("regex._regex_core", "error"); - if (!error_exception) { - PyErr_SetString(PyExc_RuntimeError, "cannot import regex._regex_core.error"); - break; - } - } - + ensure_error_exception(); PyErr_SetString(error_exception, "invalid group reference"); break; case RE_ERROR_MEMORY: @@ -2126,14 +2144,7 @@ PyErr_SetString(PyExc_IndexError, "no such group"); break; case RE_ERROR_REPLACEMENT: - if (!error_exception) { - error_exception = get_object("regex._regex_core", "error"); - if (!error_exception) { - PyErr_SetString(PyExc_RuntimeError, "cannot import regex._regex_core.error"); - break; - } - } - + ensure_error_exception(); PyErr_SetString(error_exception, "invalid replacement"); break; case RE_ERROR_TIMED_OUT: @@ -6547,6 +6558,40 @@ return TRUE; } +/* Gets the status of a node in a thread-safe way. */ +Py_LOCAL_INLINE(RE_STATUS_T) safe_get_status(RE_Node* node) { + #if defined(Py_GIL_DISABLED) + return _Py_atomic_load_uint32_relaxed(&node->status); + #else + return node->status; + #endif +} + +/* Sets the status of a node in a thread-safe way. */ +Py_LOCAL_INLINE(void) safe_set_status(RE_Node* node, RE_STATUS_T status) { + #if defined(Py_GIL_DISABLED) + _Py_atomic_or_uint32(&node->status, status); + #else + node->status |= status; + #endif +} + +/* Locks a pattern in a thread-safe way. */ +Py_LOCAL_INLINE(void) lock_pattern(RE_State* state) { + acquire_GIL(state); + #if defined(Py_GIL_DISABLED) + PyMutex_Lock(&state->pattern->mutex); + #endif +} + +/* Unlocks a pattern in a thread-safe way. */ +Py_LOCAL_INLINE(void) unlock_pattern(RE_State* state) { + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&state->pattern->mutex); + #endif + release_GIL(state); +} + /* Performs a string search. */ Py_LOCAL_INLINE(Py_ssize_t) string_search(RE_State* state, RE_Node* node, Py_ssize_t text_pos, Py_ssize_t limit, BOOL try_fast, BOOL* is_partial) { @@ -6555,25 +6600,19 @@ *is_partial = FALSE; /* Has the node been initialised for fast searching, if necessary? */ - if (try_fast && !(node->status & RE_STATUS_FAST_INIT)) { + if (try_fast && !(safe_get_status(node) & RE_STATUS_FAST_INIT)) { /* Ideally the pattern should immutable and shareable across threads. * Internally, however, it isn't. For safety we need to hold the GIL. */ - acquire_GIL(state); - #if defined(Py_GIL_DISABLED) - PyMutex_Lock(&state->pattern->mutex); - #endif + lock_pattern(state); /* Double-check because of multithreading. */ if (!(node->status & RE_STATUS_FAST_INIT)) { build_fast_tables(state, node, FALSE); - node->status |= RE_STATUS_FAST_INIT; + safe_set_status(node, RE_STATUS_FAST_INIT); } - #if defined(Py_GIL_DISABLED) - PyMutex_Unlock(&state->pattern->mutex); - #endif - release_GIL(state); + unlock_pattern(state); } if (try_fast && node->string.bad_character_offset) { @@ -6749,25 +6788,19 @@ *is_partial = FALSE; /* Has the node been initialised for fast searching, if necessary? */ - if (try_fast && !(node->status & RE_STATUS_FAST_INIT)) { + if (try_fast && !(safe_get_status(node) & RE_STATUS_FAST_INIT)) { /* Ideally the pattern should immutable and shareable across threads. * Internally, however, it isn't. For safety we need to hold the GIL. */ - acquire_GIL(state); - #if defined(Py_GIL_DISABLED) - PyMutex_Lock(&state->pattern->mutex); - #endif + lock_pattern(state); /* Double-check because of multithreading. */ if (!(node->status & RE_STATUS_FAST_INIT)) { build_fast_tables(state, node, TRUE); - node->status |= RE_STATUS_FAST_INIT; + safe_set_status(node, RE_STATUS_FAST_INIT); } - #if defined(Py_GIL_DISABLED) - PyMutex_Unlock(&state->pattern->mutex); - #endif - release_GIL(state); + unlock_pattern(state); } if (try_fast && node->string.bad_character_offset) { @@ -6797,25 +6830,19 @@ *is_partial = FALSE; /* Has the node been initialised for fast searching, if necessary? */ - if (try_fast && !(node->status & RE_STATUS_FAST_INIT)) { + if (try_fast && !(safe_get_status(node) & RE_STATUS_FAST_INIT)) { /* Ideally the pattern should immutable and shareable across threads. * Internally, however, it isn't. For safety we need to hold the GIL. */ - acquire_GIL(state); - #if defined(Py_GIL_DISABLED) - PyMutex_Lock(&state->pattern->mutex); - #endif + lock_pattern(state); /* Double-check because of multithreading. */ if (!(node->status & RE_STATUS_FAST_INIT)) { build_fast_tables_rev(state, node, TRUE); - node->status |= RE_STATUS_FAST_INIT; + safe_set_status(node, RE_STATUS_FAST_INIT); } - #if defined(Py_GIL_DISABLED) - PyMutex_Unlock(&state->pattern->mutex); - #endif - release_GIL(state); + unlock_pattern(state); } if (try_fast && node->string.bad_character_offset) { @@ -6844,25 +6871,19 @@ *is_partial = FALSE; /* Has the node been initialised for fast searching, if necessary? */ - if (try_fast && !(node->status & RE_STATUS_FAST_INIT)) { + if (try_fast && !(safe_get_status(node) & RE_STATUS_FAST_INIT)) { /* Ideally the pattern should immutable and shareable across threads. * Internally, however, it isn't. For safety we need to hold the GIL. */ - acquire_GIL(state); - #if defined(Py_GIL_DISABLED) - PyMutex_Lock(&state->pattern->mutex); - #endif + lock_pattern(state); /* Double-check because of multithreading. */ if (!(node->status & RE_STATUS_FAST_INIT)) { build_fast_tables_rev(state, node, FALSE); - node->status |= RE_STATUS_FAST_INIT; + safe_set_status(node, RE_STATUS_FAST_INIT); } - release_GIL(state); - #if defined(Py_GIL_DISABLED) - PyMutex_Unlock(&state->pattern->mutex); - #endif + unlock_pattern(state); } if (try_fast && node->string.bad_character_offset) { @@ -20415,6 +20436,9 @@ /* MatchObject's 'lastgroup' attribute. */ static PyObject* match_lastgroup(PyObject* self_, void* unused) { MatchObject* self; +#if Py_VERSION_HEX >= 0x030D0000 + int status; +#endif self = (MatchObject*)self_; @@ -20426,6 +20450,15 @@ if (!index) return NULL; +#if Py_VERSION_HEX >= 0x030D0000 + /* PyDict_GetItemRef returns a new reference or NULL for 'result'. */ + status = PyDict_GetItemRef(self->pattern->indexgroup, index, &result); + Py_DECREF(index); + if (status < 0) + return NULL; + if (result) + return result; +#else /* PyDict_GetItem returns borrows a reference. */ result = PyDict_GetItem(self->pattern->indexgroup, index); Py_DECREF(index); @@ -20433,6 +20466,7 @@ Py_INCREF(result); return result; } +#endif } Py_RETURN_NONE;
