Coverage for /Users/coordt/Documents/code/bump-my-version/bumpversion/context.py: 73%

33 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-06-11 14:29 -0500

1"""Context for rendering messages and tags.""" 

2 

3import calendar 

4import datetime 

5from collections import ChainMap 

6from dataclasses import asdict 

7from typing import TYPE_CHECKING, Optional 

8 

9if TYPE_CHECKING: # pragma: no-coverage 9 ↛ 10line 9 didn't jump to line 10, because the condition on line 9 was never true

10 from bumpversion.config import Config 

11 from bumpversion.scm import SCMInfo 

12 from bumpversion.versioning.models import Version 

13 

14MONTH_ABBREVIATIONS = [abbr for abbr in calendar.month_abbr if abbr] 

15 

16CALVER_PATTERN_MAP = { 

17 "{YYYY}": r"(?:[1-9][0-9]{3})", 

18 "{YY}": r"(?:[1-9][0-9]?)", 

19 "{0Y}": r"(?:[0-9]{2})", 

20 "{MMM}": f'(?:{"|".join(MONTH_ABBREVIATIONS)})', 

21 "{MM}": r"(?:1[0-2]|[1-9])", 

22 "{0M}": r"(?:1[0-2]|0[1-9])", 

23 "{DD}": r"(?:3[0-1]|[1-2][0-9]|[1-9])", 

24 "{0D}": r"(?:3[0-1]|[1-2][0-9]|0[1-9])", 

25 "{JJJ}": r"(?:36[0-6]|3[0-5][0-9]|[1-2][0-9][0-9]|[1-9][0-9]|[1-9])", 

26 "{00J}": r"(?:36[0-6]|3[0-5][0-9]|[1-2][0-9][0-9]|0[1-9][0-9]|00[1-9])", 

27 "{Q}": r"(?:[1-4])", 

28 "{WW}": r"(?:5[0-3]|[1-4][0-9]|[0-9])", 

29 "{0W}": r"(?:5[0-3]|[0-4][0-9])", 

30 "{UU}": r"(?:5[0-3]|[1-4][0-9]|[0-9])", 

31 "{0U}": r"(?:5[0-3]|[0-4][0-9])", 

32 "{VV}": r"(?:5[0-3]|[1-4][0-9]|[1-9])", 

33 "{0V}": r"(?:5[0-3]|[1-4][0-9]|0[1-9])", 

34 "{GGGG}": r"(?:[1-9][0-9]{3})", 

35 "{GG}": r"(?:[0-9][0-9]?)", 

36 "{0G}": r"(?:[0-9]{2})", 

37 "{INC0}": r"(?:[0-9]+)", 

38 "{INC1}": r"[1-9][0-9]*", 

39} 

40 

41 

42def calver_string_to_regex(calver_format: str) -> str: 

43 """Convert the calver format string to a regex pattern.""" 

44 pattern = calver_format 

45 for key, value in CALVER_PATTERN_MAP.items(): 

46 pattern = pattern.replace(key, value) 

47 return pattern 

48 

49 

50def prefixed_environ() -> dict: 

51 """Return a dict of the environment with keys wrapped in `${}`.""" 

52 import os 

53 

54 return {f"${key}": value for key, value in os.environ.items()} 

55 

56 

57def base_context(scm_info: Optional["SCMInfo"] = None) -> ChainMap: 

58 """The default context for rendering messages and tags.""" 

59 from bumpversion.scm import SCMInfo # Including this here to avoid circular imports 

60 

61 scm = asdict(scm_info) if scm_info else asdict(SCMInfo()) 

62 

63 return ChainMap( 

64 { 

65 "now": datetime.datetime.now(), 

66 "utcnow": datetime.datetime.now(datetime.timezone.utc), 

67 }, 

68 prefixed_environ(), 

69 scm, 

70 {c: c for c in ("#", ";")}, 

71 ) 

72 

73 

74def get_context( 

75 config: "Config", current_version: Optional["Version"] = None, new_version: Optional["Version"] = None 

76) -> ChainMap: 

77 """Return the context for rendering messages and tags.""" 

78 ctx = base_context(config.scm_info) 

79 ctx = ctx.new_child({"current_version": config.current_version}) 

80 if current_version: 

81 ctx = ctx.new_child({f"current_{part}": current_version[part].value for part in current_version}) 

82 if new_version: 

83 ctx = ctx.new_child({f"new_{part}": new_version[part].value for part in new_version}) 

84 return ctx 

85 

86 

87def get_datetime_info(current_dt: datetime.datetime) -> dict: 

88 """Return the full structure of the given datetime for formatting.""" 

89 return { 

90 "YYYY": current_dt.strftime("%Y"), 

91 "YY": current_dt.strftime("%y").lstrip("0") or "0", 

92 "0Y": current_dt.strftime("%y"), 

93 "MMM": current_dt.strftime("%b"), 

94 "MM": str(current_dt.month), 

95 "0M": current_dt.strftime("%m"), 

96 "DD": str(current_dt.day), 

97 "0D": current_dt.strftime("%d"), 

98 "JJJ": current_dt.strftime("%j").lstrip("0"), 

99 "00J": current_dt.strftime("%j"), 

100 "Q": str((current_dt.month - 1) // 3 + 1), 

101 "WW": current_dt.strftime("%W").lstrip("0") or "0", 

102 "0W": current_dt.strftime("%W"), 

103 "UU": current_dt.strftime("%U").lstrip("0") or "0", 

104 "0U": current_dt.strftime("%U"), 

105 "VV": current_dt.strftime("%V").lstrip("0") or "0", 

106 "0V": current_dt.strftime("%V"), 

107 "GGGG": current_dt.strftime("%G"), 

108 "GG": current_dt.strftime("%G")[2:].lstrip("0") or "0", 

109 "0G": current_dt.strftime("%G")[2:], 

110 }