Summary
re.Match.expand() is broken: it raises AttributeError: module 're' has no attribute '_expand'.
import re
m = re.match(r"(a)(b)", "ab")
m.expand(r"\2\1")
# AttributeError: module 're' has no attribute '_expand'
CPython 3.14.2 returns 'ba'. Verified on main @ 7044fdc.
Cause
crates/vm/src/stdlib/_sre.rs:706 delegates to a re._expand helper:
fn expand(zelf: PyRef<Self>, template: PyStrRef, vm: &VirtualMachine) -> PyResult {
let re = vm.import("re", 0)?;
let func = re.get_attr("_expand", vm)?;
func.call((zelf.pattern.clone(), zelf, template), vm)
}
re._expand existed in older CPython, but CPython 3.12 removed it when template expansion moved to the compiled-template mechanism (_sre.template() + re._compiler._compile_template). Since Lib/re/ is now the 3.14 copy, the helper no longer exists and every Match.expand call fails.
Options:
- implement
_sre.template(pattern, template) and the compiled-template fast path like CPython 3.12+, and route Match.expand through re._compiler._compile_template (this also makes re.sub with string templates faster and is needed for full parity), or
- as a minimal fix, call
re._compiler._compile_template(pattern, template) directly from Match.expand and apply it.
Lib/test/test_re.py::test_expand covers this but is currently marked @unittest.expectedFailure # TODO: RUSTPYTHON (test_re.py:729), so the regression is invisible in CI; the marker can be dropped once fixed.
Found while running synthetic benchmark/compat suites; researched and reported by Claude on behalf of @youknowone.
Summary
re.Match.expand()is broken: it raisesAttributeError: module 're' has no attribute '_expand'.CPython 3.14.2 returns
'ba'. Verified onmain@ 7044fdc.Cause
crates/vm/src/stdlib/_sre.rs:706delegates to are._expandhelper:re._expandexisted in older CPython, but CPython 3.12 removed it when template expansion moved to the compiled-template mechanism (_sre.template()+re._compiler._compile_template). SinceLib/re/is now the 3.14 copy, the helper no longer exists and everyMatch.expandcall fails.Options:
_sre.template(pattern, template)and the compiled-template fast path like CPython 3.12+, and routeMatch.expandthroughre._compiler._compile_template(this also makesre.subwith string templates faster and is needed for full parity), orre._compiler._compile_template(pattern, template)directly fromMatch.expandand apply it.Lib/test/test_re.py::test_expandcovers this but is currently marked@unittest.expectedFailure # TODO: RUSTPYTHON(test_re.py:729), so the regression is invisible in CI; the marker can be dropped once fixed.Found while running synthetic benchmark/compat suites; researched and reported by Claude on behalf of @youknowone.