|
@@ -144,3 +144,94 @@ if docutils_is_available:
|
|
|
|
|
|
for name, urlbase in ROLES.items():
|
|
|
create_reference_role(name, urlbase)
|
|
|
+
|
|
|
+
|
|
|
+named_group_matcher = re.compile(r'\(\?P(<\w+>)')
|
|
|
+unnamed_group_matcher = re.compile(r'\(')
|
|
|
+
|
|
|
+
|
|
|
+def replace_named_groups(pattern):
|
|
|
+ r"""
|
|
|
+ Find named groups in `pattern` and replace them with the group name. E.g.,
|
|
|
+ 1. ^(?P<a>\w+)/b/(\w+)$ ==> ^<a>/b/(\w+)$
|
|
|
+ 2. ^(?P<a>\w+)/b/(?P<c>\w+)/$ ==> ^<a>/b/<c>/$
|
|
|
+ """
|
|
|
+ named_group_indices = [
|
|
|
+ (m.start(0), m.end(0), m.group(1))
|
|
|
+ for m in named_group_matcher.finditer(pattern)
|
|
|
+ ]
|
|
|
+
|
|
|
+ group_pattern_and_name = []
|
|
|
+
|
|
|
+ for start, end, group_name in named_group_indices:
|
|
|
+
|
|
|
+ unmatched_open_brackets, prev_char = 1, None
|
|
|
+ for idx, val in enumerate(list(pattern[end:])):
|
|
|
+
|
|
|
+
|
|
|
+ if unmatched_open_brackets == 0:
|
|
|
+ group_pattern_and_name.append((pattern[start:end + idx], group_name))
|
|
|
+ break
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if val == '(' and prev_char != '\\':
|
|
|
+ unmatched_open_brackets += 1
|
|
|
+ elif val == ')' and prev_char != '\\':
|
|
|
+ unmatched_open_brackets -= 1
|
|
|
+ prev_char = val
|
|
|
+
|
|
|
+
|
|
|
+ for group_pattern, group_name in group_pattern_and_name:
|
|
|
+ pattern = pattern.replace(group_pattern, group_name)
|
|
|
+ return pattern
|
|
|
+
|
|
|
+
|
|
|
+def replace_unnamed_groups(pattern):
|
|
|
+ r"""
|
|
|
+ Find unnamed groups in `pattern` and replace them with '<var>'. E.g.,
|
|
|
+ 1. ^(?P<a>\w+)/b/(\w+)$ ==> ^(?P<a>\w+)/b/<var>$
|
|
|
+ 2. ^(?P<a>\w+)/b/((x|y)\w+)$ ==> ^(?P<a>\w+)/b/<var>$
|
|
|
+ """
|
|
|
+ unnamed_group_indices = [m.start(0) for m in unnamed_group_matcher.finditer(pattern)]
|
|
|
+
|
|
|
+ group_indices = []
|
|
|
+
|
|
|
+ for start in unnamed_group_indices:
|
|
|
+
|
|
|
+ unmatched_open_brackets, prev_char = 1, None
|
|
|
+ for idx, val in enumerate(list(pattern[start + 1:])):
|
|
|
+ if unmatched_open_brackets == 0:
|
|
|
+ group_indices.append((start, start + 1 + idx))
|
|
|
+ break
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if val == '(' and prev_char != '\\':
|
|
|
+ unmatched_open_brackets += 1
|
|
|
+ elif val == ')' and prev_char != '\\':
|
|
|
+ unmatched_open_brackets -= 1
|
|
|
+ prev_char = val
|
|
|
+
|
|
|
+
|
|
|
+ group_start_end_indices = []
|
|
|
+ prev_end = None
|
|
|
+ for start, end in group_indices:
|
|
|
+ if prev_end and start > prev_end or not prev_end:
|
|
|
+ group_start_end_indices.append((start, end))
|
|
|
+ prev_end = end
|
|
|
+
|
|
|
+ if group_start_end_indices:
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ final_pattern, prev_end = [], None
|
|
|
+ for start, end in group_start_end_indices:
|
|
|
+ if prev_end:
|
|
|
+ final_pattern.append(pattern[prev_end:start])
|
|
|
+ final_pattern.append(pattern[:start] + '<var>')
|
|
|
+ prev_end = end
|
|
|
+ final_pattern.append(pattern[prev_end:])
|
|
|
+ return ''.join(final_pattern)
|
|
|
+ else:
|
|
|
+ return pattern
|