Coverage for src / mafw / devtools / cli / dependencies / oldest.py: 99%

55 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-06-28 13:34 +0000

1# Copyright 2026 European Union 

2# Author: Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu) 

3# SPDX-License-Identifier: EUPL-1.2 

4"""Oldest-dependency verification commands.""" 

5 

6from __future__ import annotations 

7 

8import shlex 

9from typing import Final 

10 

11import click 

12 

13from mafw.devtools.dependencies.compile import ( 

14 ensure_mafw_project_root, 

15 python_versions_between, 

16) 

17from mafw.devtools.dependencies.verify import ( 

18 get_expected_lower_bounds, 

19 verify_lowest_resolution, 

20) 

21from mafw.tools.click_extensions import AbbreviateGroup, DeprecatedOption 

22from mafw.tools.shell_tools import run as cmd 

23 

24DEFAULT_TEST_FILES: Final[list[str]] = [ 

25 'tests/test_full_integration.py', 

26] 

27"""List of test files executed by default during dependency verification.""" 

28 

29 

30@click.group( 

31 context_settings={'help_option_names': ['-h', '--help']}, 

32 help='Placeholder commands for the oldest dependency stack.', 

33 cls=AbbreviateGroup, 

34) 

35def oldest() -> None: 

36 """Group for oldest-dependency commands.""" 

37 

38 

39@oldest.command( 

40 name='check', 

41 context_settings={'help_option_names': ['-h', '--help']}, 

42 help='Verify compatibility with oldest supported dependencies.', 

43) 

44@click.option( 

45 '--min-python-ver', 

46 default='3.11', 

47 show_default=True, 

48 type=click.STRING, 

49 help='The oldest version of python to be used for the dependencies verification, constrain it to >=3.11', 

50) 

51@click.option( 

52 '--max-python-ver', 

53 default='3.14', 

54 show_default=True, 

55 type=click.STRING, 

56 help='The newest version of python to be used for the dependencies verification', 

57) 

58@click.option( 

59 '--full-unittest', 

60 is_flag=True, 

61 default=False, 

62 show_default=True, 

63 help='Perform the test over the whole test suite.', 

64) 

65@click.option( 

66 '--preserve-envs', 

67 'preserve_envs', 

68 is_flag=True, 

69 default=False, 

70 show_default=True, 

71 help='Preserve created hatch environments after the check is completed.', 

72) 

73@click.option( 

74 '--remove-envs/--no-remove-envs', 

75 'remove_envs', 

76 default=True, 

77 show_default=True, 

78 cls=DeprecatedOption, 

79 deprecated_message=( 

80 '--remove-envs/--no-remove-envs is deprecated and will be removed in MAFw 3.0.0. Use --preserve-envs instead.' 

81 ), 

82 help='Remove or preserve environments after check.', 

83) 

84def check( 

85 min_python_ver: str, 

86 max_python_ver: str, 

87 full_unittest: bool, 

88 preserve_envs: bool, 

89 remove_envs: bool, 

90) -> None: 

91 """Verify MAFw compatibility against the oldest supported dependency stack.""" 

92 # --- Conflict detection and resolution between --preserve-envs and --remove-envs --- 

93 ctx = click.get_current_context() 

94 preserve_source = ctx.get_parameter_source('preserve_envs') 

95 remove_source = ctx.get_parameter_source('remove_envs') 

96 

97 if ( 

98 preserve_source == click.core.ParameterSource.COMMANDLINE 

99 and remove_source == click.core.ParameterSource.COMMANDLINE 

100 and remove_envs 

101 ): 

102 raise click.UsageError('--preserve-envs and --remove-envs are mutually exclusive.') 

103 

104 if preserve_source == click.core.ParameterSource.COMMANDLINE: 

105 effective_preserve = True 

106 elif remove_source == click.core.ParameterSource.COMMANDLINE: 

107 effective_preserve = not remove_envs 

108 else: 

109 effective_preserve = False 

110 

111 supported_python_versions = ensure_mafw_project_root() 

112 expected_bounds = get_expected_lower_bounds() 

113 lowest_resolution = {'UV_RESOLUTION': 'lowest-direct'} 

114 

115 for python_version in python_versions_between(min_python_ver, max_python_ver, supported_python_versions): 

116 # remove existing test envs 

117 cmd_line = shlex.split(f'hatch env remove hatch-test.py{python_version}') 

118 cmd(cmd_line) 

119 

120 # create test env with lowest-direct resolution 

121 cmd_line = shlex.split(f'hatch env create hatch-test.py{python_version}') 

122 cmd(cmd_line, env=lowest_resolution) 

123 

124 # verify that the freshly created environment has the right lowest resolution 

125 verify_lowest_resolution('hatch-test', python_version, expected_bounds) 

126 

127 # run the test suite full or reduced 

128 files_to_be_tested = [] 

129 if not full_unittest: 129 ↛ 131line 129 didn't jump to line 131 because the condition on line 129 was always true

130 files_to_be_tested = DEFAULT_TEST_FILES 

131 cmd_line = shlex.split(f'hatch test -py {python_version}') 

132 cmd(cmd_line + files_to_be_tested) 

133 

134 # remove existing typing env 

135 cmd_line = shlex.split(f'hatch env remove types.py{python_version}') 

136 cmd(cmd_line) 

137 

138 # create static typing env with lowest direct resolution 

139 cmd_line = shlex.split(f'hatch env create types.py{python_version}') 

140 cmd(cmd_line, env=lowest_resolution) 

141 

142 # verify that the freshly created environment has the right lowest resolution 

143 verify_lowest_resolution('types', python_version, expected_bounds) 

144 

145 # run static typing 

146 cmd_line = shlex.split(f'hatch run types.py{python_version}:check --python-version {python_version}') 

147 cmd(cmd_line) 

148 

149 if not effective_preserve: 

150 # remove environments 

151 envs = ['hatch-test.py', 'types.py'] 

152 for env in envs: 

153 cmd_line = shlex.split(f'hatch env remove {env}{python_version}') 

154 cmd(cmd_line)