Coverage for cz_keep_a_changelog_plugin/utils.py: 100.00%

23 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-01 14:01 +0000

1"""Utils.""" 

2 

3import re 

4 

5import jinja2 as j2 

6 

7MD_LINK_PATTERN: re.Pattern[str] = re.compile(r"\[[^\]]*\]\([^)]+\)") 

8GH_ISSUE_PATTERN: re.Pattern[str] = re.compile(r"(?<!\w)#(?P<issue_num>\d+)\b") 

9 

10 

11def replace_github_issues( 

12 text: str, 

13 issue_url_template: j2.Template, 

14) -> str: 

15 """Replace GitHub issue numbers `"#123"` by a corresponding markdown link. 

16 

17 Notes: 

18 Ignores GitHub issue numbers in markdown links. 

19 

20 Args: 

21 text: The text to replace the GitHub issue numbers in. 

22 issue_url_template: The issue URL template (`issue` will be replaced by 

23 the issue number). 

24 

25 Returns: 

26 The text with replaced GitHub issue numbers. 

27 """ 

28 link_spans: list[tuple[int, int]] = [ 

29 (m.start(), m.end()) for m in MD_LINK_PATTERN.finditer(text) 

30 ] 

31 link_spans.sort() 

32 

33 out = [] 

34 i_link = 0 

35 i_text = 0 

36 

37 for m in GH_ISSUE_PATTERN.finditer(text): 

38 start, end = m.span() 

39 while i_link < len(link_spans) and link_spans[i_link][1] <= start: 

40 i_link += 1 

41 # Skip in case of overlap with link 

42 if ( 

43 i_link < len(link_spans) 

44 and link_spans[i_link][0] <= start < link_spans[i_link][1] 

45 ): 

46 continue 

47 # Append text inbetween 

48 out.append(text[i_text:start]) 

49 # Append issue url 

50 issue_num = m.group("issue_num") 

51 issue_url = issue_url_template.render(issue=issue_num) 

52 out.append(f"[#{issue_num:s}]({issue_url:s})") 

53 # Set start of next text part to end 

54 i_text = end 

55 

56 out.append(text[i_text:]) 

57 return "".join(out)