Root cause
Prior to Python 3.5, re.sub to failed capture groups in Python re.sub were not populated with an empty string. The following is a description of Bug 1519638 on bugs.python.org . Thus, when using the backlink to a group that did not participate in the match, an error occurred.
There are two ways to fix this problem.
Solution 1: Add empty alternatives to add optional groups
You can replace all optional capture groups (those constructs like (\d+)? ), With mandatory ones, with an empty alternative (i.e. (\d+|) ).
Here is an example of a failure :
import re old = 'regexregex' new = re.sub(r'regex(group)?regex', r'something\1something', old) print(new)
Replacing a single line with
new = re.sub(r'regex(group|)regex', r'something\1something', old)
He works.
Solution 2: Use a lambda expression in the replacement and check if the group is not None
This approach is necessary if you have additional groups within another optional group.
You can use lambda in the replacement part to check if the group is initialized, and not None , with lambda m: m.group(n) or '' Use this solution in your case , because you have two backlinks - # 3 and # 4 - in the replacement pattern, but several matches (see Match 1 and 3) do not have an initialized capture group 3. This is because the entire first part is (\s*\{{2}funcA(ka|)\s*\|\s*([^}]*)\s*\}{2}\s*|) - does not participate in the match, and the internal group Capture 3 (ie ([^}]*) ) simply does not fill up even after adding an empty alternative.
re.sub(r'(?i)(\s*\{{2}funcA(ka|)\s*\|\s*([^\}]*)\s*\}{2}\s*|)\{{2}funcB\s*\|\s*([^\}]*)\s*\}{2}\s*', r"\n | funcA"+str(n)+r" = \3\n | funcB"+str(n)+r" = \4\n | string"+str(n)+r" = \n", text, count=1)
should be rewritten with
re.sub(r'(?i)(\s*{{funcA(ka|)\s*\|\s*([^}]*)\s*}}\s*|){{funcB\s*\|\s*([^}]*)\s*}}\s*', lambda m: r"\n | funcA"+str(n)+r" = " + (m.group(3) or '') + "\n | funcB" + str(n) + r" = " + (m.group(4) or '') + "\n | string" + str(n) + r" = \n", text, count=1)
See the IDEONE demo
import re text = r''' {{funcB|param1}} *some string* {{funcA|param2}} {{funcB|param3}} *some string2* {{funcB|param4}} *some string3* {{funcAka|param5}} {{funcB|param6}} *some string4* ''' for n in (range(1,(text.count('funcB')+1))): text = re.sub(r'(?i)(\s*\{{2}funcA(ka|)\s*\|\s*([^\}]*)\s*\}{2}\s*|)\{{2}funcB\s*\|\s*([^\}]*)\s*\}{2}\s*', lambda m: r"\n | funcA"+str(n)+r" = "+(m.group(3) or '')+"\n | funcB"+str(n)+r" = "+(m.group(4) or '')+"\n | string"+str(n)+r" = \n", text, count=1) assert text == r''' | funcA1 = | funcB1 = param1 | string1 = *some string* | funcA2 = param2 | funcB2 = param3 | string2 = *some string2* | funcA3 = | funcB3 = param4 | string3 = *some string3* | funcA4 = param5 | funcB4 = param6 | string4 = *some string4* ''' print 'ok'