xmipp3-installer 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. xmipp3_installer/__init__.py +1 -0
  2. xmipp3_installer/__main__.py +6 -0
  3. xmipp3_installer/api_client/api_client.py +50 -0
  4. xmipp3_installer/api_client/assembler/installation_info_assembler.py +181 -0
  5. xmipp3_installer/application/__init__.py +1 -0
  6. xmipp3_installer/application/cli/__init__.py +1 -0
  7. xmipp3_installer/application/cli/arguments/__init__.py +8 -0
  8. xmipp3_installer/application/cli/arguments/modes.py +130 -0
  9. xmipp3_installer/application/cli/arguments/params.py +76 -0
  10. xmipp3_installer/application/cli/cli.py +271 -0
  11. xmipp3_installer/application/cli/parsers/base_help_formatter.py +244 -0
  12. xmipp3_installer/application/cli/parsers/error_handler_parser.py +68 -0
  13. xmipp3_installer/application/cli/parsers/format.py +35 -0
  14. xmipp3_installer/application/cli/parsers/general_help_formatter.py +92 -0
  15. xmipp3_installer/application/cli/parsers/mode_help_formatter.py +115 -0
  16. xmipp3_installer/application/logger/__init__.py +0 -0
  17. xmipp3_installer/application/logger/errors.py +28 -0
  18. xmipp3_installer/application/logger/logger.py +230 -0
  19. xmipp3_installer/application/logger/predefined_messages.py +66 -0
  20. xmipp3_installer/application/user_interactions.py +16 -0
  21. xmipp3_installer/installer/__init__.py +1 -0
  22. xmipp3_installer/installer/constants/__init__.py +17 -0
  23. xmipp3_installer/installer/constants/paths.py +32 -0
  24. xmipp3_installer/installer/handlers/__init__.py +1 -0
  25. xmipp3_installer/installer/handlers/cmake/__init__.py +1 -0
  26. xmipp3_installer/installer/handlers/cmake/cmake_constants.py +27 -0
  27. xmipp3_installer/installer/handlers/cmake/cmake_handler.py +69 -0
  28. xmipp3_installer/installer/handlers/conda_handler.py +13 -0
  29. xmipp3_installer/installer/handlers/generic_package_handler.py +18 -0
  30. xmipp3_installer/installer/handlers/git_handler.py +185 -0
  31. xmipp3_installer/installer/handlers/shell_handler.py +114 -0
  32. xmipp3_installer/installer/handlers/versions_manager.py +98 -0
  33. xmipp3_installer/installer/installer_service.py +66 -0
  34. xmipp3_installer/installer/modes/__init__.py +1 -0
  35. xmipp3_installer/installer/modes/mode_all_executor.py +63 -0
  36. xmipp3_installer/installer/modes/mode_clean/__init__.py +1 -0
  37. xmipp3_installer/installer/modes/mode_clean/mode_clean_all_executor.py +44 -0
  38. xmipp3_installer/installer/modes/mode_clean/mode_clean_bin_executor.py +94 -0
  39. xmipp3_installer/installer/modes/mode_clean/mode_clean_executor.py +45 -0
  40. xmipp3_installer/installer/modes/mode_cmake/__init__.py +1 -0
  41. xmipp3_installer/installer/modes/mode_cmake/mode_cmake_executor.py +55 -0
  42. xmipp3_installer/installer/modes/mode_cmake/mode_compile_and_install_executor.py +49 -0
  43. xmipp3_installer/installer/modes/mode_cmake/mode_config_build_executor.py +64 -0
  44. xmipp3_installer/installer/modes/mode_config_executor.py +46 -0
  45. xmipp3_installer/installer/modes/mode_executor.py +43 -0
  46. xmipp3_installer/installer/modes/mode_get_sources_executor.py +132 -0
  47. xmipp3_installer/installer/modes/mode_git_executor.py +41 -0
  48. xmipp3_installer/installer/modes/mode_selector.py +25 -0
  49. xmipp3_installer/installer/modes/mode_sync/mode_add_model_executor.py +104 -0
  50. xmipp3_installer/installer/modes/mode_sync/mode_get_models_executor.py +51 -0
  51. xmipp3_installer/installer/modes/mode_sync/mode_sync_executor.py +48 -0
  52. xmipp3_installer/installer/modes/mode_sync/mode_test_executor.py +91 -0
  53. xmipp3_installer/installer/modes/mode_version_executor.py +164 -0
  54. xmipp3_installer/installer/orquestrator.py +37 -0
  55. xmipp3_installer/installer/urls.py +8 -0
  56. xmipp3_installer/repository/__init__.py +1 -0
  57. xmipp3_installer/repository/config.py +241 -0
  58. xmipp3_installer/repository/config_vars/__init__.py +0 -0
  59. xmipp3_installer/repository/config_vars/config_values_adapter.py +107 -0
  60. xmipp3_installer/repository/config_vars/default_values.py +36 -0
  61. xmipp3_installer/repository/config_vars/variables.py +48 -0
  62. xmipp3_installer/repository/invalid_config_line.py +15 -0
  63. xmipp3_installer/shared/file_operations.py +18 -0
  64. xmipp3_installer/shared/singleton.py +25 -0
  65. xmipp3_installer-1.0.0.dist-info/LICENSE +674 -0
  66. xmipp3_installer-1.0.0.dist-info/METADATA +729 -0
  67. xmipp3_installer-1.0.0.dist-info/RECORD +70 -0
  68. xmipp3_installer-1.0.0.dist-info/WHEEL +5 -0
  69. xmipp3_installer-1.0.0.dist-info/entry_points.txt +2 -0
  70. xmipp3_installer-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,69 @@
1
+ """### Functions that interact with CMake."""
2
+
3
+ import os
4
+ import shutil
5
+ from typing import Dict, Any
6
+
7
+ from xmipp3_installer.installer.handlers.cmake import cmake_constants
8
+ from xmipp3_installer.repository.config_vars import variables
9
+
10
+ def get_cmake_path(config: Dict[str, Any]) -> str:
11
+ """
12
+ ### Retrieves information about the CMake package and updates the dictionary accordingly.
13
+
14
+ #### Params:
15
+ - packages (dict): Dictionary containing package information.
16
+
17
+ #### Returns:
18
+ - (dict): Param 'packages' with the 'CMAKE' key updated based on the availability of 'cmake'.
19
+ """
20
+ return config.get(variables.CMAKE) or shutil.which(cmake_constants.DEFAULT_CMAKE)
21
+
22
+ def get_cmake_vars_str(config: Dict[str, Any]) -> str:
23
+ """
24
+ ### Converts the variables in the config dictionary into a list as CMake args.
25
+
26
+ #### Params:
27
+ - config (dict): Dictionary to obtain the parameters from.
28
+ """
29
+ result = []
30
+ for (key, value) in config.items():
31
+ if key not in variables.INTERNAL_LOGIC_VARS and bool(value):
32
+ result.append(f"-D{key}={value}")
33
+ return ' '.join(result)
34
+
35
+ def get_library_versions_from_cmake_file(path: str) -> Dict[str, Any]:
36
+ """
37
+ ### Obtains the library versions from the CMake cache file.
38
+
39
+ #### Params:
40
+ - path (str): Path to the file containing all versions.
41
+
42
+ #### Returns:
43
+ - (dict(str, any)): Dictionary containing all the library versions in the file.
44
+ """
45
+ if not os.path.exists(path):
46
+ return {}
47
+
48
+ result = {}
49
+ with open(path, 'r') as versions_file:
50
+ for line in versions_file.readlines():
51
+ result.update(__get_library_version_from_line(line))
52
+ return result
53
+
54
+ def __get_library_version_from_line(version_line: str) -> Dict[str, Any]:
55
+ """
56
+ ### Retrieves the name and version of the library in the given line.
57
+
58
+ #### Params:
59
+ - version_line (str): Text line containing the name and version of the library.
60
+
61
+ #### Returns:
62
+ - (dict(str, any)): Dictionary where the key is the name and the value is the version.
63
+ """
64
+ library_with_version = {}
65
+ name_and_version = version_line.replace("\n", "").split('=')
66
+ if len(name_and_version) == 2:
67
+ version = name_and_version[1] if name_and_version[1] else None
68
+ library_with_version[name_and_version[0]] = version
69
+ return library_with_version
@@ -0,0 +1,13 @@
1
+ """### Functions that interact with Conda via shell."""
2
+
3
+ import os
4
+ from typing import Optional
5
+
6
+ def get_conda_prefix_path() -> Optional[str]:
7
+ """
8
+ ### Returns the path for the current Conda enviroment.
9
+
10
+ #### Returns:
11
+ - (str | None): Path for current Conda enviroment.
12
+ """
13
+ return os.environ.get('CONDA_PREFIX')
@@ -0,0 +1,18 @@
1
+ """### Contains functions that can interact with packages via shell with a generic interface."""
2
+
3
+ from typing import Optional
4
+
5
+ from xmipp3_installer.installer.handlers import shell_handler
6
+
7
+ def get_package_version(package_name: str) -> Optional[str]:
8
+ """
9
+ ### Retrieves the version of a package or program by executing '[package_name] --version' command.
10
+
11
+ Params:
12
+ - package_name (str): Name of the package or program.
13
+
14
+ Returns:
15
+ - (str | None): Version information of the package or None if not found or errors happened.
16
+ """
17
+ ret_code, output = shell_handler.run_shell_command(f'{package_name} --version')
18
+ return output if ret_code == 0 else None
@@ -0,0 +1,185 @@
1
+ """### Functions that interact with Git via shell."""
2
+
3
+ import os
4
+ from typing import Optional, Tuple
5
+
6
+ from xmipp3_installer.application.logger.logger import logger
7
+ from xmipp3_installer.installer.constants import paths
8
+ from xmipp3_installer.installer.handlers import shell_handler
9
+
10
+ def get_current_branch(dir: str='./') -> str:
11
+ """
12
+ ### Returns the current branch of the repository of the given directory or empty string if it is not a repository or a recognizable tag.
13
+
14
+ #### Params:
15
+ - dir (str): Optional. Directory of the repository to get current branch from. Default is current directory.
16
+
17
+ #### Returns:
18
+ - (str): The name of the branch, 'HEAD' if a tag, or empty string if given directory is not a repository or a recognizable tag.
19
+ """
20
+ ret_code, branch_name = shell_handler.run_shell_command("git rev-parse --abbrev-ref HEAD", cwd=dir)
21
+ # If there was an error, we are in no branch
22
+ return branch_name if not ret_code else ''
23
+
24
+ def is_tag(dir: str='./') -> bool:
25
+ """
26
+ ### Returns True if the current Xmipp repository is in a tag.
27
+
28
+ #### Params:
29
+ - dir (str): Optional. Directory of the repository where the check will happen. Default is current directory.
30
+
31
+ #### Returns:
32
+ - (bool): True if the repository is a tag. False otherwise.
33
+ """
34
+ current_branch = get_current_branch(dir=dir)
35
+ return not current_branch or current_branch == "HEAD"
36
+
37
+ def is_branch_up_to_date(dir: str='./') -> bool:
38
+ """
39
+ ### Returns True if the current branch is up to date, or False otherwise or if some error happened.
40
+
41
+ #### Params:
42
+ - dir (str): Optional. Directory of the repository to get current branch from. Default is current directory.
43
+
44
+ #### Returns:
45
+ - (bool): True if the current branch is up to date, or False otherwise or if some error happened.
46
+ """
47
+ current_branch = get_current_branch(dir=dir)
48
+ if not current_branch:
49
+ return False
50
+
51
+ ret_code = shell_handler.run_shell_command("git fetch", cwd=dir)[0]
52
+ if ret_code != 0:
53
+ return False
54
+
55
+ latest_local_commit = shell_handler.run_shell_command(f"git rev-parse {current_branch}", cwd=dir)[1]
56
+ ret_code, latest_remote_commit = shell_handler.run_shell_command(f"git rev-parse origin/{current_branch}")
57
+ if ret_code != 0:
58
+ return False
59
+
60
+ return latest_local_commit == latest_remote_commit
61
+
62
+ def get_current_commit(dir: str="./") -> str:
63
+ """
64
+ ### Rreturns the current commit short hash of a given repository:
65
+
66
+ #### Params:
67
+ - dir (str): Optional. Directory of repository.
68
+
69
+ #### Returns:
70
+ - (str): Current commit short hash, or empty string if it is not a repo or there were errors.
71
+ """
72
+ ret_code, output = shell_handler.run_shell_command("git rev-parse --short HEAD", cwd=dir)
73
+ if ret_code or not output:
74
+ return ''
75
+ return output
76
+
77
+ def get_commit_branch(commit: str, dir: str="./") -> str:
78
+ """
79
+ ### Returns the name of the commit branch. It can be a branch name or a release name.
80
+
81
+ #### Params:
82
+ - commit (str): Commit hash.
83
+ - dir (str): Optional. Directory to repository.
84
+
85
+ #### Returns:
86
+ - (str): Name of the commit branch or release.
87
+ """
88
+ ret_code, output = shell_handler.run_shell_command(f"git name-rev {commit}", cwd=dir)
89
+ if ret_code or not output:
90
+ return ''
91
+ return output.replace(commit, "").replace(" ", "")
92
+
93
+ def branch_exists_in_repo(repo_url: str, branch: str) -> bool:
94
+ """
95
+ ### Checks if the given branch exists in the given repository.
96
+
97
+ #### Params:
98
+ - repo (str): Repository to check from.
99
+ - branch (str): Name of the branch to check for.
100
+
101
+ #### Returns:
102
+ - (bool): True if the branch exists, False otherwise.
103
+ """
104
+ return __ref_exists_in_repo(repo_url, branch, True)
105
+
106
+ def tag_exists_in_repo(repo_url: str, tag: str) -> bool:
107
+ """
108
+ ### Checks if the given tag exists in the given repository.
109
+
110
+ #### Params:
111
+ - repo_url (str): Repository to check from.
112
+ - tag (str): Name of the tag to check for.
113
+
114
+ #### Returns:
115
+ - (bool): True if the tag exists, False otherwise.
116
+ """
117
+ return __ref_exists_in_repo(repo_url, tag, False)
118
+
119
+ def get_clonable_branch(repo_url: str, preferred_branch: str, viable_tag: str) -> Optional[str]:
120
+ """
121
+ ### Decides the target to be cloned from a given repository.
122
+
123
+ The preferred branch will be selected if exists,
124
+ followed in priority by the viable tag if provided.
125
+ Finally, if no branch could be selected, None is returned,
126
+ meaning that repository's default branch will be used.
127
+
128
+ #### Params:
129
+ - repo_url (str): Url of the repositori to be cloned.
130
+ - preferred_branch (str): Preferred branch to clone into.
131
+ - viable_tag (str): If exists, it is returned if branch does not.
132
+
133
+ #### Returns:
134
+ - (str): Name of the branch to clone the repository into.
135
+ """
136
+ if preferred_branch and branch_exists_in_repo(repo_url, preferred_branch):
137
+ return preferred_branch
138
+ if viable_tag and tag_exists_in_repo(repo_url, viable_tag):
139
+ return viable_tag
140
+ return None
141
+
142
+ def execute_git_command_for_source(command: str, source: str) -> Tuple[int, str]:
143
+ """
144
+ ### Executes the git command for a specific source.
145
+
146
+ #### Params:
147
+ - command (str): Command to execute on the source.
148
+ - source (str): The source repository name.
149
+
150
+ #### Returns:
151
+ - (tuple(int, str)): Tuple containing the return code and output message.
152
+ """
153
+ source_path = paths.get_source_path(source)
154
+ if not os.path.exists(source_path):
155
+ logger(logger.yellow(
156
+ f"WARNING: Source {source} does not exist in path {source_path}. Skipping."
157
+ ))
158
+ return 0, ""
159
+
160
+ return shell_handler.run_shell_command(
161
+ f"git {command}",
162
+ cwd=source_path,
163
+ show_output=True,
164
+ show_error=True
165
+ )
166
+
167
+ def __ref_exists_in_repo(repo_url: str, ref: str, is_branch: bool) -> bool:
168
+ """
169
+ ### Checks if a given reference exists in the given repository.
170
+
171
+ #### Params:
172
+ - repo_url (str): Repository to check from.
173
+ - ref (str): Reference to check for.
174
+ - is_branch (bool): If True, the reference is a branch. If False, it is a tag.
175
+
176
+ #### Returns:
177
+ - (bool): True if the ref exists, False otherwise.
178
+ """
179
+ ref_type = "heads" if is_branch else "tags"
180
+ ret_code, output = shell_handler.run_shell_command(
181
+ f"git ls-remote --{ref_type} {repo_url}.git refs/{ref_type}/{ref}"
182
+ )
183
+ if ret_code:
184
+ return False
185
+ return f"refs/{ref_type}/{ref}" in output
@@ -0,0 +1,114 @@
1
+ """### Functions that interact with the shell."""
2
+
3
+ import os
4
+ import subprocess
5
+ import threading
6
+
7
+ from typing import Tuple
8
+
9
+ from xmipp3_installer.application.logger.logger import logger
10
+ from xmipp3_installer.application.logger import errors
11
+
12
+ def run_shell_command(
13
+ cmd: str,
14
+ cwd: str='./',
15
+ show_command: bool=False,
16
+ show_output: bool=False,
17
+ show_error: bool=False,
18
+ substitute: bool=False
19
+ ) -> Tuple[int, str]:
20
+ """
21
+ ### This function runs the given command.
22
+
23
+ #### Params:
24
+ - cmd (str): Command to run.
25
+ - cwd (str): Optional. Path to run the command from. Default is current directory.
26
+ - show_output (bool): Optional. If True, output is printed.
27
+ - show_error (bool): Optional. If True, errors are printed.
28
+ - show_command (bool): Optional. If True, command is printed in blue.
29
+ - substitute (bool): Optional. If True, output will replace previous line.
30
+
31
+ #### Returns:
32
+ - (int): Return code.
33
+ - (str): Output of the command, regardless of if it is an error or regular output.
34
+ """
35
+ if show_command:
36
+ logger(logger.blue(cmd), substitute=substitute)
37
+ ret_code, output_str = __run_command(cmd, cwd=cwd)
38
+
39
+ if not ret_code and show_output:
40
+ logger(output_str, substitute=substitute)
41
+
42
+ if ret_code and show_error:
43
+ logger.log_error(output_str, ret_code=ret_code)
44
+
45
+ return ret_code, output_str
46
+
47
+ def run_shell_command_in_streaming(
48
+ cmd: str,
49
+ cwd: str='./',
50
+ show_output: bool=False,
51
+ show_error: bool=False,
52
+ substitute: bool=False
53
+ ) -> int:
54
+ """
55
+ ### Runs the given command and shows its output as it is being generated.
56
+
57
+ #### Params:
58
+ - cmd (str): Command to run.
59
+ - cwd (str): Optional. Path to run the command from. Default is current directory.
60
+ - show_output (bool): Optional. If True, output is printed.
61
+ - show_error (bool): Optional. If True, errors are printed.
62
+ - substitute (bool): Optional. If True, output will replace previous line.
63
+
64
+ #### Returns:
65
+ - (int): Return code.
66
+ """
67
+ logger(cmd)
68
+ process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
69
+
70
+ thread_out = threading.Thread(
71
+ target=logger.log_in_streaming,
72
+ args=(process.stdout,),
73
+ kwargs={"show_in_terminal": show_output, "substitute": substitute, "err": False}
74
+ )
75
+ thread_err = threading.Thread(
76
+ target=logger.log_in_streaming,
77
+ args=(process.stderr,),
78
+ kwargs={"show_in_terminal": show_error, "substitute": substitute, "err": True}
79
+ )
80
+ thread_out.start()
81
+ thread_err.start()
82
+
83
+ try:
84
+ process.wait()
85
+ thread_out.join()
86
+ thread_err.join()
87
+ except KeyboardInterrupt:
88
+ process.returncode = errors.INTERRUPTED_ERROR
89
+
90
+ return process.returncode
91
+
92
+ def __run_command(cmd: str, cwd: str='./') -> Tuple[int, str]:
93
+ """
94
+ ### Runs the given shell command.
95
+
96
+ #### Params:
97
+ - cmd (str): Command to run.
98
+ - cwd (str): Optional. Path to run the command from.
99
+
100
+ #### Returns:
101
+ - (int): Return code of the operation.
102
+ - (str): Return message of the operation.
103
+ """
104
+ process = subprocess.Popen(cmd, cwd=cwd, env=os.environ, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
105
+ try:
106
+ process.wait()
107
+ except KeyboardInterrupt:
108
+ return errors.INTERRUPTED_ERROR, ""
109
+
110
+ ret_code = process.returncode
111
+ output, err = process.communicate()
112
+ output_str = output.decode() if not ret_code and output else err.decode()
113
+ output_str = output_str[:-1] if output_str.endswith('\n') else output_str
114
+ return ret_code, output_str
@@ -0,0 +1,98 @@
1
+ import json
2
+ from datetime import datetime
3
+ from typing import Dict, List
4
+
5
+ from xmipp3_installer.installer import constants
6
+ from xmipp3_installer.shared import singleton
7
+
8
+ class VersionsManager(singleton.Singleton):
9
+ def __init__(self, version_file_path: str):
10
+ """
11
+ ### Constructor.
12
+
13
+ Initializes the version manager by reading and validating version information from a JSON file.
14
+
15
+ #### Params:
16
+ - version_file_path (str): Path to the JSON file containing version information.
17
+
18
+ #### Raises:
19
+ - ValueError: If version numbers don't follow semver format (x.y.z) or release date isn't dd/mm/yyyy.
20
+ """
21
+ super().__init__()
22
+ self.version_file_path = version_file_path
23
+ version_info = self.__get_version_info()
24
+ self.xmipp_version_number = version_info[constants.XMIPP]["version_number"]
25
+ self.xmipp_version_name = version_info[constants.XMIPP]["version_name"]
26
+ self.xmipp_release_date = version_info[constants.XMIPP]["release_date"]
27
+ self.sources_versions = version_info["sources_target_tag"]
28
+ self.__validate_fields()
29
+
30
+ def __get_version_info(self) -> Dict[str, str]:
31
+ """
32
+ ### Retrieves the version info from the version information JSON file.
33
+
34
+ #### Returns:
35
+ - (dict(str, str)): Dictionary containing the parsed values.
36
+ """
37
+ with open(self.version_file_path) as json_data:
38
+ version_info = json.load(json_data)
39
+ return version_info
40
+
41
+ def __validate_fields(self):
42
+ """
43
+ ### Validates version numbers and release date format.
44
+
45
+ Checks that:
46
+ - Version numbers follow semantic versioning (x.y.z)
47
+ - Release date follows dd/mm/yyyy format
48
+
49
+ #### Raises:
50
+ - ValueError: If any field doesn't match its required format.
51
+ """
52
+ self.__validate_version_number()
53
+ self.__validate_release_date()
54
+
55
+ def __validate_version_number(self):
56
+ """
57
+ ### Validates that version numbers follow semantic versioning format.
58
+
59
+ Checks that version numbers are in x.y.z format where x, y, z are integers.
60
+
61
+ #### Raises:
62
+ - ValueError: If any version number doesn't follow the required format.
63
+ """
64
+ parts = self.xmipp_version_number.split('.')
65
+ if not self.__is_valid_semver(parts):
66
+ raise ValueError(
67
+ f"Version number '{self.xmipp_version_number}' is invalid. Must be three numbers separated by dots (x.y.z)."
68
+ )
69
+
70
+ def __is_valid_semver(self, version_parts: List[str]) -> bool:
71
+ """
72
+ ### Checks if version parts constitute a valid semantic version.
73
+
74
+ #### Params:
75
+ - version_parts (list(str)): List of strings representing version number parts.
76
+
77
+ #### Returns:
78
+ - (bool): True if version follows semver format, False otherwise.
79
+ """
80
+ return len(version_parts) == 3 and all(part.isdigit() for part in version_parts)
81
+
82
+ def __validate_release_date(self):
83
+ """
84
+ ### Validates that release date follows dd/mm/yyyy format.
85
+
86
+ Checks that:
87
+ - Date string follows dd/mm/yyyy format
88
+ - Date is a valid calendar date
89
+
90
+ #### Raises:
91
+ - ValueError: If release date doesn't follow the required format or is invalid.
92
+ """
93
+ try:
94
+ datetime.strptime(self.xmipp_release_date, "%d/%m/%Y")
95
+ except ValueError:
96
+ raise ValueError(
97
+ f"Release date '{self.xmipp_release_date}' is invalid. Must be in dd/mm/yyyy format."
98
+ )
@@ -0,0 +1,66 @@
1
+ from typing import Dict
2
+
3
+ from xmipp3_installer.api_client import api_client
4
+ from xmipp3_installer.api_client.assembler import installation_info_assembler
5
+ from xmipp3_installer.application.cli.arguments import modes
6
+ from xmipp3_installer.application.logger import errors
7
+ from xmipp3_installer.application.logger.logger import logger
8
+ from xmipp3_installer.installer import constants
9
+ from xmipp3_installer.installer.constants import paths
10
+ from xmipp3_installer.installer.handlers import versions_manager
11
+ from xmipp3_installer.installer.modes.mode_executor import ModeExecutor
12
+ from xmipp3_installer.application.logger import predefined_messages
13
+ from xmipp3_installer.installer.modes import mode_selector
14
+ from xmipp3_installer.repository import config
15
+ from xmipp3_installer.repository.config_vars import variables
16
+
17
+ class InstallationManager:
18
+ def __init__(self, args: Dict):
19
+ """
20
+ ### Constructor.
21
+
22
+ #### Params:
23
+ - args (dict): Dictionary containing all parsed command-line arguments.
24
+ """
25
+ self.mode = args.pop(modes.MODE, modes.MODE_ALL)
26
+ config_handler = config.ConfigurationFileHandler(path=paths.CONFIG_FILE, show_errors=False)
27
+ self.context = {
28
+ **args,
29
+ **config_handler.values,
30
+ variables.LAST_MODIFIED_KEY: config_handler.last_modified,
31
+ constants.VERSIONS_CONTEXT_KEY: versions_manager.VersionsManager(paths.VERSION_INFO_FILE)
32
+ }
33
+ self.mode_executor: ModeExecutor = mode_selector.MODE_EXECUTORS[self.mode](self.context)
34
+
35
+ def run_installer(self):
36
+ """
37
+ ### Runs the installer with the given arguments.
38
+
39
+ #### Returns:
40
+ - (int): Return code.
41
+ """
42
+ try:
43
+ ret_code, output = self.mode_executor.run()
44
+ except KeyboardInterrupt:
45
+ logger.log_error("", ret_code=errors.INTERRUPTED_ERROR, add_portal_link=False)
46
+ return errors.INTERRUPTED_ERROR
47
+ if ret_code:
48
+ logger.log_error(output, ret_code=ret_code, add_portal_link=ret_code != errors.INTERRUPTED_ERROR)
49
+ if (
50
+ self.mode_executor.sends_installation_info and
51
+ self.context[variables.SEND_INSTALLATION_STATISTICS]
52
+ ):
53
+ logger("Sending anonymous installation info...")
54
+ api_client.send_installation_attempt(
55
+ installation_info_assembler.get_installation_info(
56
+ self.context[constants.VERSIONS_CONTEXT_KEY],
57
+ ret_code=ret_code
58
+ )
59
+ )
60
+ if not ret_code and self.mode_executor.prints_banner_on_exit:
61
+ logger(predefined_messages.get_success_message(
62
+ self.context[constants.VERSIONS_CONTEXT_KEY].xmipp_version_number
63
+ ))
64
+ logger.close()
65
+ return ret_code
66
+
@@ -0,0 +1 @@
1
+ """### Contains the executors for each mode."""
@@ -0,0 +1,63 @@
1
+ from typing import Tuple, Dict
2
+
3
+ from xmipp3_installer.application.cli.arguments import params
4
+ from xmipp3_installer.application.logger.logger import logger
5
+ from xmipp3_installer.installer.modes import (
6
+ mode_executor, mode_config_executor, mode_get_sources_executor
7
+ )
8
+ from xmipp3_installer.installer.modes.mode_cmake import (
9
+ mode_config_build_executor, mode_compile_and_install_executor
10
+ )
11
+
12
+ class ModeAllExecutor(mode_executor.ModeExecutor):
13
+ def __init__(self, context: Dict):
14
+ """
15
+ ### Constructor.
16
+
17
+ #### Params:
18
+ - context (dict): Dictionary containing the installation context variables.
19
+ """
20
+ config_executor = mode_config_executor.ModeConfigExecutor(
21
+ {**context, params.PARAM_OVERWRITE: False}
22
+ )
23
+ get_sources_executor = mode_get_sources_executor.ModeGetSourcesExecutor(
24
+ context
25
+ )
26
+ config_build_executor = mode_config_build_executor.ModeConfigBuildExecutor(
27
+ context
28
+ )
29
+ compile_and_install_executor = mode_compile_and_install_executor.ModeCompileAndInstallExecutor(
30
+ {**context, params.PARAM_BRANCH: None}
31
+ )
32
+ self.executors = [
33
+ config_executor,
34
+ get_sources_executor,
35
+ config_build_executor,
36
+ compile_and_install_executor
37
+ ]
38
+ super().__init__(context)
39
+
40
+ def _set_executor_config(self):
41
+ """
42
+ ### Sets the specific executor params for this mode.
43
+ """
44
+ super()._set_executor_config()
45
+ self.logs_to_file = True
46
+ self.prints_with_substitution = True
47
+ self.prints_banner_on_exit = True
48
+ self.sends_installation_info = True
49
+
50
+ def run(self) -> Tuple[int, str]:
51
+ """
52
+ ### Runs the whole installation process with the appropiate params.
53
+
54
+ #### Returns:
55
+ - (tuple(int, str)): Tuple containing the error status and an error message if there was an error.
56
+ """
57
+ for executor_index in range(len(self.executors)):
58
+ if executor_index != 0:
59
+ logger("")
60
+ ret_code, output = self.executors[executor_index].run()
61
+ if ret_code:
62
+ return ret_code, output
63
+ return 0, ""
@@ -0,0 +1 @@
1
+ """### Contains the executors for the cleaning modes."""
@@ -0,0 +1,44 @@
1
+ from typing import List
2
+
3
+ from xmipp3_installer.application.logger.logger import logger
4
+ from xmipp3_installer.installer import constants
5
+ from xmipp3_installer.installer.constants import paths
6
+ from xmipp3_installer.installer.modes.mode_clean import mode_clean_executor
7
+
8
+ class ModeCleanAllExecutor(mode_clean_executor.ModeCleanExecutor):
9
+ def _get_paths_to_delete(self) -> List[str]:
10
+ """
11
+ ### Returns a list of all the paths to be deleted.
12
+
13
+ #### Returns:
14
+ - (list(str)): List containing all the paths to delete.
15
+ """
16
+ return [
17
+ *[paths.get_source_path(source) for source in constants.XMIPP_SOURCES],
18
+ paths.INSTALL_PATH,
19
+ paths.BUILD_PATH,
20
+ paths.CONFIG_FILE
21
+ ]
22
+
23
+ def _get_confirmation_keyword(self) -> str:
24
+ """
25
+ ### Returns the keyword needed to be introduced by the user to confirm an operation.
26
+
27
+ #### Returns:
28
+ - (str): Confirmation keyword.
29
+ """
30
+ return "YeS"
31
+
32
+ def _get_confirmation_message(self) -> str:
33
+ """
34
+ ### Returns message to be printed when asking for user confirmation.
35
+
36
+ #### Returns:
37
+ - (str): Confirmation message.
38
+ """
39
+ return '\n'.join([
40
+ logger.yellow("WARNING: This will DELETE ALL content from src and build, and also the xmipp.conf file."),
41
+ logger.yellow("\tNotice that if you have unpushed changes, they will be deleted."),
42
+ logger.yellow(f"\nIf you are sure you want to do this, type '{self._get_confirmation_keyword()}' (case sensitive):")
43
+ ])
44
+