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-03-11 20:49:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-regex (Old) and /work/SRC/openSUSE:Factory/.python-regex.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-regex" Wed Mar 11 20:49:38 2026 rev:31 rq:1337895 version:2026.2.28 Changes: -------- --- /work/SRC/openSUSE:Factory/python-regex/python-regex.changes 2026-01-28 15:07:29.290853193 +0100 +++ /work/SRC/openSUSE:Factory/.python-regex.new.8177/python-regex.changes 2026-03-11 20:49:47.428320997 +0100 @@ -1,0 +2,13 @@ +Tue Mar 10 08:15:10 UTC 2026 - Dirk Müller <[email protected]> + +- update to 2026.2.28: + * Replaced atomic operations with mutex on pattern object for + free-threaded Python. + * PR #598: Fix race condition in storage caching with atomic + operations. + * Replaced use of PyUnicode_GET_LENGTH with + PyUnicode_GetLength. + * Added \z as alias of \Z, like in re module. + * Added prefixmatch as alias of match, like in re module. + +------------------------------------------------------------------- Old: ---- regex-2026.1.15.tar.gz New: ---- regex-2026.2.28.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-regex.spec ++++++ --- /var/tmp/diff_new_pack.Scy4D4/_old 2026-03-11 20:49:47.924341107 +0100 +++ /var/tmp/diff_new_pack.Scy4D4/_new 2026-03-11 20:49:47.928341269 +0100 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-regex -Version: 2026.1.15 +Version: 2026.2.28 Release: 0 Summary: Alternative regular expression module for Python License: Apache-2.0 ++++++ regex-2026.1.15.tar.gz -> regex-2026.2.28.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.1.15/PKG-INFO new/regex-2026.2.28/PKG-INFO --- old/regex-2026.1.15/PKG-INFO 2026-01-14 23:06:53.833961200 +0100 +++ new/regex-2026.2.28/PKG-INFO 2026-02-28 02:12:31.823559800 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: regex -Version: 2026.1.15 +Version: 2026.2.28 Summary: Alternative regular expression module, to replace re. Author-email: Matthew Barnett <[email protected]> License-Expression: Apache-2.0 AND CNRI-Python @@ -8,7 +8,6 @@ Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 @@ -18,7 +17,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing Classifier: Topic :: Text Processing :: General -Requires-Python: >=3.9 +Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE.txt Dynamic: license-file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.1.15/changelog.txt new/regex-2026.2.28/changelog.txt --- old/regex-2026.1.15/changelog.txt 2026-01-14 23:06:49.000000000 +0100 +++ new/regex-2026.2.28/changelog.txt 2026-02-28 02:12:26.000000000 +0100 @@ -1,3 +1,19 @@ +Version: 2026.2.28 + + Replaced atomic operations with mutex on pattern object for free-threaded Python. + +Version: 2026.2.26 + + PR #598: Fix race condition in storage caching with atomic operations. + + Replaced use of PyUnicode_GET_LENGTH with PyUnicode_GetLength. + +Version: 2026.2.19 + + Added \z as alias of \Z, like in re module. + + Added prefixmatch as alias of match, like in re module. + Version: 2026.1.15 Re-uploaded. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.1.15/pyproject.toml new/regex-2026.2.28/pyproject.toml --- old/regex-2026.1.15/pyproject.toml 2026-01-14 23:06:49.000000000 +0100 +++ new/regex-2026.2.28/pyproject.toml 2026-02-28 02:12:26.000000000 +0100 @@ -4,7 +4,7 @@ [project] name = "regex" -version = "2026.1.15" +version = "2026.2.28" description = "Alternative regular expression module, to replace re." readme = "README.rst" authors = [ @@ -17,7 +17,6 @@ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -29,7 +28,7 @@ "Topic :: Text Processing :: General", ] -requires-python = ">= 3.9" +requires-python = ">= 3.10" [project.urls] Homepage = "https://github.com/mrabarnett/mrab-regex" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.1.15/regex/_main.py new/regex-2026.2.28/regex/_main.py --- old/regex-2026.1.15/regex/_main.py 2026-01-14 23:06:49.000000000 +0100 +++ new/regex-2026.2.28/regex/_main.py 2026-02-28 02:12:26.000000000 +0100 @@ -167,28 +167,31 @@ \xXX Matches the character with 2-digit hex code XX. \X Matches a grapheme. \Z Matches only at the end of the string. + \z Matches only at the end of the string. Alias of \Z. \\ Matches a literal backslash. This module exports the following functions: - match Match a regular expression pattern at the beginning of a string. - fullmatch Match a regular expression pattern against all of a string. - search Search a string for the presence of a pattern. - sub Substitute occurrences of a pattern found in a string using a - template string. - subf Substitute occurrences of a pattern found in a string using a - format string. - subn Same as sub, but also return the number of substitutions made. - subfn Same as subf, but also return the number of substitutions made. - split Split a string by the occurrences of a pattern. VERSION1: will - split at zero-width match; VERSION0: won't split at zero-width - match. - splititer Return an iterator yielding the parts of a split string. - findall Find all occurrences of a pattern in a string. - finditer Return an iterator yielding a match object for each match. - compile Compile a pattern into a Pattern object. - purge Clear the regular expression cache. - escape Backslash all non-alphanumerics or special characters in a - string. + match Match a regular expression pattern at the beginning of a string. + prefixmatch Match a regular expression pattern at the beginning of a string. + Alias of match. + fullmatch Match a regular expression pattern against all of a string. + search Search a string for the presence of a pattern. + sub Substitute occurrences of a pattern found in a string using a + template string. + subf Substitute occurrences of a pattern found in a string using a + format string. + subn Same as sub, but also return the number of substitutions made. + subfn Same as subf, but also return the number of substitutions made. + split Split a string by the occurrences of a pattern. VERSION1: will + split at zero-width match; VERSION0: won't split at zero-width + match. + splititer Return an iterator yielding the parts of a split string. + findall Find all occurrences of a pattern in a string. + finditer Return an iterator yielding a match object for each match. + compile Compile a pattern into a Pattern object. + purge Clear the regular expression cache. + escape Backslash all non-alphanumerics or special characters in a + string. Most of the functions support a concurrent parameter: if True, the GIL will be released during matching, allowing other Python threads to run concurrently. If @@ -233,7 +236,7 @@ # Public symbols. __all__ = ["cache_all", "compile", "DEFAULT_VERSION", "escape", "findall", - "finditer", "fullmatch", "match", "purge", "search", "split", "splititer", + "finditer", "fullmatch", "match", "prefixmatch", "purge", "search", "split", "splititer", "sub", "subf", "subfn", "subn", "template", "Scanner", "A", "ASCII", "B", "BESTMATCH", "D", "DEBUG", "E", "ENHANCEMATCH", "S", "DOTALL", "F", "FULLCASE", "I", "IGNORECASE", "L", "LOCALE", "M", "MULTILINE", "P", "POSIX", @@ -241,7 +244,7 @@ "VERSION1", "X", "VERBOSE", "W", "WORD", "error", "Regex", "__version__", "__doc__", "RegexFlag"] -__version__ = "2026.1.15" +__version__ = "2026.2.28" # -------------------------------------------------------------------- # Public interface. @@ -253,6 +256,13 @@ pat = _compile(pattern, flags, ignore_unused, kwargs, True) return pat.match(string, pos, endpos, concurrent, partial, timeout) +def prefixmatch(pattern, string, flags=0, pos=None, endpos=None, partial=False, + concurrent=None, timeout=None, ignore_unused=False, **kwargs): + """Try to apply the pattern at the start of the string, returning a match + object, or None if no match was found. Alias of match().""" + pat = _compile(pattern, flags, ignore_unused, kwargs, True) + return pat.match(string, pos, endpos, concurrent, partial, timeout) + def fullmatch(pattern, string, flags=0, pos=None, endpos=None, partial=False, concurrent=None, timeout=None, ignore_unused=False, **kwargs): """Try to apply the pattern against all of the string, returning a match diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.1.15/regex/_regex_core.py new/regex-2026.2.28/regex/_regex_core.py --- old/regex-2026.1.15/regex/_regex_core.py 2026-01-14 23:06:49.000000000 +0100 +++ new/regex-2026.2.28/regex/_regex_core.py 2026-02-28 02:12:26.000000000 +0100 @@ -4641,6 +4641,7 @@ "m": StartOfWord(), "M": EndOfWord(), "Z": EndOfString(), + "z": EndOfString(), } ASCII_POSITION_ESCAPES = dict(POSITION_ESCAPES) ASCII_POSITION_ESCAPES.update({ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.1.15/regex.egg-info/PKG-INFO new/regex-2026.2.28/regex.egg-info/PKG-INFO --- old/regex-2026.1.15/regex.egg-info/PKG-INFO 2026-01-14 23:06:53.000000000 +0100 +++ new/regex-2026.2.28/regex.egg-info/PKG-INFO 2026-02-28 02:12:31.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: regex -Version: 2026.1.15 +Version: 2026.2.28 Summary: Alternative regular expression module, to replace re. Author-email: Matthew Barnett <[email protected]> License-Expression: Apache-2.0 AND CNRI-Python @@ -8,7 +8,6 @@ Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 @@ -18,7 +17,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Text Processing Classifier: Topic :: Text Processing :: General -Requires-Python: >=3.9 +Requires-Python: >=3.10 Description-Content-Type: text/x-rst License-File: LICENSE.txt Dynamic: license-file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/regex-2026.1.15/src/_regex.c new/regex-2026.2.28/src/_regex.c --- old/regex-2026.1.15/src/_regex.c 2026-01-14 23:06:49.000000000 +0100 +++ new/regex-2026.2.28/src/_regex.c 2026-02-28 02:12:26.000000000 +0100 @@ -585,6 +585,9 @@ RE_Node* req_string; /* The required string. */ BOOL is_fuzzy; /* Whether it's a fuzzy pattern. */ BOOL do_search_start; /* Whether to do an initial search. */ + #if defined(Py_GIL_DISABLED) + PyMutex mutex; + #endif } PatternObject; /* The MatchObject created when a match is found. */ @@ -18161,7 +18164,7 @@ return FALSE; str_info->characters = (void*)PyUnicode_DATA(string); - str_info->length = PyUnicode_GET_LENGTH(string); + str_info->length = PyUnicode_GetLength(string); str_info->charsize = PyUnicode_KIND(string); str_info->is_unicode = TRUE; str_info->should_release = FALSE; @@ -18219,6 +18222,10 @@ ByteStack_init(state, &state->pstack); /* We might already have some cached storage we can use for bstack. */ + #if defined(Py_GIL_DISABLED) + PyMutex_Lock(&pattern->mutex); + #endif + if (pattern->stack_storage) { state->bstack.storage = pattern->stack_storage; state->bstack.capacity = pattern->stack_capacity; @@ -18226,6 +18233,10 @@ pattern->stack_capacity = 0; } + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&pattern->mutex); + #endif + state->groups = NULL; state->best_match_groups = NULL; state->repeats = NULL; @@ -18252,10 +18263,22 @@ if (pattern->true_group_count) { size_t g; + #if defined(Py_GIL_DISABLED) + PyMutex_Lock(&pattern->mutex); + #endif + if (pattern->groups_storage) { state->groups = pattern->groups_storage; pattern->groups_storage = NULL; + + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&pattern->mutex); + #endif } else { + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&pattern->mutex); + #endif + state->groups = (RE_GroupData*)re_alloc(pattern->true_group_count * sizeof(RE_GroupData)); if (!state->groups) @@ -18297,6 +18320,9 @@ else if (end > str_info->length) end = str_info->length; + if (end < start) + end = start; + state->overlapped = overlapped; state->min_width = pattern->min_width; @@ -18396,10 +18422,22 @@ state->string = string; if (pattern->repeat_count) { + #if defined(Py_GIL_DISABLED) + PyMutex_Lock(&pattern->mutex); + #endif + if (pattern->repeats_storage) { state->repeats = pattern->repeats_storage; pattern->repeats_storage = NULL; + + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&pattern->mutex); + #endif } else { + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&pattern->mutex); + #endif + state->repeats = (RE_RepeatData*)re_alloc(pattern->repeat_count * sizeof(RE_RepeatData)); if (!state->repeats) @@ -18567,6 +18605,10 @@ pattern = state->pattern; + #if defined(Py_GIL_DISABLED) + PyMutex_Lock(&pattern->mutex); + #endif + /* If possible cache the storage of bstack. */ if (!pattern->stack_storage) { pattern->stack_storage = state->bstack.storage; @@ -18587,14 +18629,6 @@ } } - /* Clear the stacks. */ - ByteStack_fini(state, &state->sstack); - ByteStack_fini(state, &state->bstack); - ByteStack_fini(state, &state->pstack); - - if (state->best_match_groups) - dealloc_groups(state->best_match_groups, pattern->true_group_count); - if (pattern->groups_storage) dealloc_groups(state->groups, pattern->true_group_count); else @@ -18605,6 +18639,18 @@ else pattern->repeats_storage = state->repeats; + #if defined(Py_GIL_DISABLED) + PyMutex_Unlock(&pattern->mutex); + #endif + + if (state->best_match_groups) + dealloc_groups(state->best_match_groups, pattern->true_group_count); + + /* Clear the stacks. */ + ByteStack_fini(state, &state->sstack); + ByteStack_fini(state, &state->bstack); + ByteStack_fini(state, &state->pstack); + for (i = 0; i < pattern->call_ref_info_count; i++) re_dealloc(state->group_call_guard_list[i].spans); @@ -18694,7 +18740,7 @@ Py_ssize_t end) { Py_ssize_t length; - length = PyUnicode_GET_LENGTH(string); + length = PyUnicode_GetLength(string); start = limited_range(start, 0, length); end = limited_range(end, 0, length); @@ -22358,6 +22404,10 @@ "match(string, pos=None, endpos=None, concurrent=None, timeout=None) --> MatchObject or None.\n\ Match zero or more characters at the beginning of the string."); +PyDoc_STRVAR(pattern_prefixmatch_doc, + "prefixmatch(string, pos=None, endpos=None, concurrent=None, timeout=None) --> MatchObject or None.\n\ + Match zero or more characters at the beginning of the string. Alias of match()"); + PyDoc_STRVAR(pattern_fullmatch_doc, "fullmatch(string, pos=None, endpos=None, concurrent=None, timeout=None) --> MatchObject or None.\n\ Match zero or more characters against all of the string."); @@ -22419,6 +22469,8 @@ static PyMethodDef pattern_methods[] = { {"match", (PyCFunction)pattern_match, METH_VARARGS|METH_KEYWORDS, pattern_match_doc}, + {"prefixmatch", (PyCFunction)pattern_match, METH_VARARGS|METH_KEYWORDS, + pattern_prefixmatch_doc}, {"fullmatch", (PyCFunction)pattern_fullmatch, METH_VARARGS|METH_KEYWORDS, pattern_fullmatch_doc}, {"search", (PyCFunction)pattern_search, METH_VARARGS|METH_KEYWORDS, @@ -25793,6 +25845,9 @@ self->req_flags = req_flags; self->req_string = NULL; self->locale_info = NULL; + #if defined(Py_GIL_DISABLED) + memset(&self->mutex, 0, sizeof(self->mutex)); + #endif Py_INCREF(self->pattern); if (unpacked) { Py_INCREF(self->packed_code_list); @@ -26401,9 +26456,9 @@ if (!m) return NULL; -#if defined(Py_GIL_DISABLED) + #if defined(Py_GIL_DISABLED) PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); -#endif + #endif d = PyModule_GetDict(m);
