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,271 @@
1
+ """### Command Line Interface that interacts with the installer."""
2
+
3
+ import argparse
4
+ import multiprocessing
5
+ import os
6
+ import sys
7
+ from typing import Dict, Any
8
+
9
+ from xmipp3_installer.application.cli import arguments
10
+ from xmipp3_installer.application.cli.arguments import modes, params
11
+ from xmipp3_installer.application.cli.parsers import format
12
+ from xmipp3_installer.application.cli.parsers.error_handler_parser import ErrorHandlerArgumentParser
13
+ from xmipp3_installer.application.cli.parsers.general_help_formatter import GeneralHelpFormatter
14
+ from xmipp3_installer.application.cli.parsers.mode_help_formatter import ModeHelpFormatter
15
+ from xmipp3_installer.application.logger.logger import logger
16
+ from xmipp3_installer.installer import installer_service
17
+
18
+ def main():
19
+ """### Main entry point function that starts the execution."""
20
+ parser = __generate_parser()
21
+ parser = __add_params(parser)
22
+ __add_default_usage_mode()
23
+ args = vars(parser.parse_args())
24
+ __validate_args(args, parser)
25
+ installation_manager = installer_service.InstallationManager(args)
26
+ ret_code = installation_manager.run_installer()
27
+ sys.exit(ret_code)
28
+
29
+ def __generate_parser() -> argparse.ArgumentParser:
30
+ """
31
+ ### Generates an argument parser for the installer.
32
+
33
+ #### Returns:
34
+ - (ArgumentParser): Argument parser.
35
+ """
36
+ return ErrorHandlerArgumentParser(
37
+ prog=arguments.XMIPP_PROGRAM_NAME,
38
+ formatter_class=GeneralHelpFormatter,
39
+ )
40
+
41
+ def __add_params(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
42
+ """
43
+ ### Inserts the params into the given parser.
44
+
45
+ #### Params:
46
+ - parser (ArgumentParser): Argument parser.
47
+
48
+ #### Returns:
49
+ - (ArgumentParser): Argument parser with inserted params.
50
+ """
51
+ subparsers = parser.add_subparsers(dest=modes.MODE)
52
+ default_jobs = __get_default_job_number()
53
+
54
+ add_model_subparser = subparsers.add_parser(modes.MODE_ADD_MODEL, formatter_class=ModeHelpFormatter)
55
+ __add_params_mode_add_model(add_model_subparser)
56
+
57
+ all_subparser = subparsers.add_parser(modes.MODE_ALL, formatter_class=ModeHelpFormatter)
58
+ __add_params_mode_all(all_subparser, default_jobs)
59
+
60
+ subparsers.add_parser(modes.MODE_CLEAN_ALL, formatter_class=ModeHelpFormatter)
61
+
62
+ subparsers.add_parser(modes.MODE_CLEAN_BIN, formatter_class=ModeHelpFormatter)
63
+
64
+ compile_and_install_subparser = subparsers.add_parser(modes.MODE_COMPILE_AND_INSTALL, formatter_class=ModeHelpFormatter)
65
+ __add_params_mode_compile_and_install(compile_and_install_subparser, default_jobs)
66
+
67
+ build_config_subparser = subparsers.add_parser(modes.MODE_CONFIG_BUILD, formatter_class=ModeHelpFormatter)
68
+ __add_params_mode_config_build(build_config_subparser)
69
+
70
+ config_subparser = subparsers.add_parser(modes.MODE_CONFIG, formatter_class=ModeHelpFormatter)
71
+ __add_params_mode_config(config_subparser)
72
+
73
+ get_models_subparser = subparsers.add_parser(modes.MODE_GET_MODELS, formatter_class=ModeHelpFormatter)
74
+ __add_params_mode_get_models(get_models_subparser)
75
+
76
+ get_sources_subparser = subparsers.add_parser(modes.MODE_GET_SOURCES, formatter_class=ModeHelpFormatter)
77
+ __add_params_mode_get_sources(get_sources_subparser)
78
+
79
+ git_subparser = subparsers.add_parser(modes.MODE_GIT, formatter_class=ModeHelpFormatter)
80
+ __add_params_mode_git(git_subparser)
81
+
82
+ test_subparser = subparsers.add_parser(modes.MODE_TEST, formatter_class=ModeHelpFormatter)
83
+ __add_params_mode_test(test_subparser)
84
+
85
+ version_subparser = subparsers.add_parser(modes.MODE_VERSION, formatter_class=ModeHelpFormatter)
86
+ __add_params_mode_version(version_subparser)
87
+
88
+ return parser
89
+
90
+ def __add_params_mode_add_model(subparser: argparse.ArgumentParser):
91
+ """
92
+ ### Adds params for mode "addModel"
93
+
94
+ #### Params:
95
+ - subparser (ArgumentParser): Subparser to add the params to.
96
+ """
97
+ subparser.add_argument(*format.get_param_names(params.PARAM_LOGIN))
98
+ subparser.add_argument(*format.get_param_names(params.PARAM_MODEL_PATH))
99
+ subparser.add_argument(*format.get_param_names(params.PARAM_UPDATE), action='store_true')
100
+
101
+ def __add_params_mode_all(subparser: argparse.ArgumentParser, default_jobs: int):
102
+ """
103
+ ### Adds params for mode "all"
104
+
105
+ #### Params:
106
+ - subparser (ArgumentParser): Subparser to add the params to.
107
+ - default_jobs (int): Default number of jobs to run the task.
108
+ """
109
+ subparser.add_argument(*format.get_param_names(params.PARAM_JOBS), type=int, default=default_jobs)
110
+ subparser.add_argument(*format.get_param_names(params.PARAM_BRANCH))
111
+ subparser.add_argument(*format.get_param_names(params.PARAM_KEEP_OUTPUT), action='store_true')
112
+
113
+ def __add_params_mode_compile_and_install(subparser: argparse.ArgumentParser, default_jobs: int):
114
+ """
115
+ ### Adds params for mode "compileAndInstall"
116
+
117
+ #### Params:
118
+ - subparser (ArgumentParser): Subparser to add the params to.
119
+ - default_jobs (int): Default number of jobs to run the task.
120
+ """
121
+ subparser.add_argument(*format.get_param_names(params.PARAM_JOBS), type=int, default=default_jobs)
122
+ subparser.add_argument(*format.get_param_names(params.PARAM_BRANCH))
123
+ subparser.add_argument(*format.get_param_names(params.PARAM_KEEP_OUTPUT), action='store_true')
124
+
125
+ def __add_params_mode_config_build(subparser: argparse.ArgumentParser):
126
+ """
127
+ ### Adds params for mode "configBuild"
128
+
129
+ #### Params:
130
+ - subparser (ArgumentParser): Subparser to add the params to.
131
+ """
132
+ subparser.add_argument(*format.get_param_names(params.PARAM_KEEP_OUTPUT), action='store_true')
133
+
134
+ def __add_params_mode_config(subparser: argparse.ArgumentParser):
135
+ """
136
+ ### Adds params for mode "config"
137
+
138
+ #### Params:
139
+ - subparser (ArgumentParser): Subparser to add the params to.
140
+ """
141
+ subparser.add_argument(*format.get_param_names(params.PARAM_OVERWRITE), action='store_true')
142
+
143
+ def __add_params_mode_get_models(subparser: argparse.ArgumentParser):
144
+ """
145
+ ### Adds params for mode "getModels"
146
+
147
+ #### Params:
148
+ - subparser (ArgumentParser): Subparser to add the params to.
149
+ """
150
+ subparser.add_argument(
151
+ *format.get_param_names(params.PARAM_MODELS_DIRECTORY),
152
+ default=__get_project_root_subpath(arguments.DEFAULT_MODELS_DIR)
153
+ )
154
+
155
+ def __add_params_mode_get_sources(subparser: argparse.ArgumentParser):
156
+ """
157
+ ### Adds params for mode "getSources"
158
+
159
+ #### Params:
160
+ - subparser (ArgumentParser): Subparser to add the params to.
161
+ """
162
+ subparser.add_argument(*format.get_param_names(params.PARAM_BRANCH))
163
+ subparser.add_argument(*format.get_param_names(params.PARAM_KEEP_OUTPUT), action='store_true')
164
+
165
+ def __add_params_mode_git(subparser: argparse.ArgumentParser):
166
+ """
167
+ ### Adds params for mode "git"
168
+
169
+ #### Params:
170
+ - subparser (ArgumentParser): Subparser to add the params to.
171
+ """
172
+ subparser.add_argument(*format.get_param_names(params.PARAM_GIT_COMMAND), nargs='+')
173
+
174
+ def __add_params_mode_test(subparser: argparse.ArgumentParser):
175
+ """
176
+ ### Adds params for mode "test"
177
+
178
+ #### Params:
179
+ - subparser (ArgumentParser): Subparser to add the params to.
180
+ """
181
+ subparser.add_argument(*format.get_param_names(params.PARAM_TEST_NAMES), nargs='*', default=None)
182
+ subparser.add_argument(*format.get_param_names(params.PARAM_SHOW_TESTS), action='store_true')
183
+
184
+ def __add_params_mode_version(subparser: argparse.ArgumentParser):
185
+ """
186
+ ### Adds params for mode "version"
187
+
188
+ #### Params:
189
+ - subparser (ArgumentParser): Subparser to add the params to.
190
+ """
191
+ subparser.add_argument(*format.get_param_names(params.PARAM_SHORT), action='store_true')
192
+
193
+ def __get_default_job_number() -> int:
194
+ """
195
+ ### Gets the default number of jobs to be used by parallelizable tasks.
196
+ Returned number will be 120% of CPU cores, due to not all jobs taking
197
+ 100% of CPU time continuously.
198
+
199
+ #### Returns:
200
+ - (int): Default number of jobs.
201
+ """
202
+ return multiprocessing.cpu_count() + int(multiprocessing.cpu_count() * 0.2)
203
+
204
+ def __get_project_root_subpath(subpath: str) -> str:
205
+ """
206
+ ### Returns a subpath of Xmipp's root directory.
207
+
208
+ #### Params:
209
+ - subpath (str): Subpath inside the root directory.
210
+
211
+ #### Returns:
212
+ - (str): Absolute path to given subpath.
213
+ """
214
+ return os.path.join(__get_project_root_dir(), subpath)
215
+
216
+ def __get_project_root_dir() -> str:
217
+ """
218
+ ### Returns the root directory of Xmipp.
219
+
220
+ #### Returns:
221
+ - (str): Absolute path to Xmipp's root directory.
222
+ """
223
+ return os.path.dirname(os.path.abspath(__file__))
224
+
225
+ def __add_default_usage_mode():
226
+ """
227
+ ### Sets the usage mode as the default one when a mode has not been specifically provided.
228
+ """
229
+ no_args_provided = len(sys.argv) == 1
230
+ args_provided = len(sys.argv) > 1
231
+ if no_args_provided or (
232
+ args_provided and __is_first_arg_optional() and not __help_requested()
233
+ ):
234
+ sys.argv.insert(1, modes.MODE_ALL)
235
+
236
+ def __is_first_arg_optional() -> bool:
237
+ """
238
+ ### Returns True if the first argument provided is optional.
239
+
240
+ #### Returns:
241
+ - (bool): True if the first argument received is optional.
242
+ """
243
+ return sys.argv[1].startswith('-')
244
+
245
+ def __help_requested() -> bool:
246
+ """
247
+ ### Returns True if help is at least one of the args.
248
+
249
+ #### Returns:
250
+ - (bool): True if help is at least one of the args.
251
+ """
252
+ return '-h' in sys.argv or '--help' in sys.argv
253
+
254
+ def __validate_args(args: Dict[str, Any], parser: argparse.ArgumentParser):
255
+ """
256
+ ### Performs validations on the arguments.
257
+
258
+ #### Params:
259
+ - args (dict(str, any)): Arguments to be validated.
260
+ - parser (ArgumentParser): Argument parser.
261
+ """
262
+ jobs = args.get('jobs', 1)
263
+ if jobs < 1:
264
+ parser.error(f"Wrong job number \"{jobs}\". Number of jobs has to be 1 or greater.")
265
+
266
+ branch = args.get('branch')
267
+ if branch is not None and len(branch.split(' ')) > 1:
268
+ parser.error(f"Incorrect branch name \"{branch}\". Branch names can only be one word long.")
269
+
270
+ if args.get('keep_output', False):
271
+ logger.set_allow_substitution(False)
@@ -0,0 +1,244 @@
1
+ """### Defines a base help formatter with extened functions to be used by the custom formatters."""
2
+
3
+ import argparse
4
+ import shutil
5
+ from typing import List, Tuple
6
+
7
+ from xmipp3_installer.application.cli.arguments import modes, params
8
+ from xmipp3_installer.application.cli.parsers import format
9
+
10
+ class BaseHelpFormatter(argparse.HelpFormatter):
11
+ """
12
+ ### Extendes the available functions of the generic help formatter.
13
+ """
14
+ __SECTION_N_DASH = 45
15
+ __SECTION_SPACE_MODE_HELP = 2
16
+ __SECTION_HELP_START = format.TAB_SIZE + __SECTION_N_DASH + __SECTION_SPACE_MODE_HELP
17
+ __LINE_SIZE_LOWER_LIMIT = int(__SECTION_HELP_START * 1.5)
18
+
19
+ def _get_mode_help(self, mode: str, general: bool=True) -> str:
20
+ """
21
+ ### Returns the help message of a given mode.
22
+
23
+ ### Params:
24
+ - mode (str): Mode to get help text for.
25
+ - general (bool). Optional. If True, only the general help message is displayed.
26
+
27
+ ### Returns:
28
+ - (str): Help of the mode (empty if mode not found).
29
+ """
30
+ for group in list(modes.MODES.keys()):
31
+ if mode in list(modes.MODES[group].keys()):
32
+ messages = modes.MODES[group][mode]
33
+ return self.__get_message_from_list(messages, general)
34
+ return ''
35
+
36
+ def _get_param_first_name(self, param_key: str) -> str:
37
+ """
38
+ ### Returns the first name of the given param key. Short name has priority over long name.
39
+
40
+ ### Params:
41
+ - param_key (str): Key to identify the param.
42
+
43
+ ### Returns:
44
+ - (str): Formatted text.
45
+ """
46
+ param = params.PARAMS[param_key]
47
+ return param.get(params.SHORT_VERSION, param.get(params.LONG_VERSION, ''))
48
+
49
+ def _get_help_separator(self) -> str:
50
+ """
51
+ ### Returns the line that separates sections inside the help message.
52
+
53
+ ### Returns:
54
+ - (str): Line that separates sections inside the help message.
55
+ """
56
+ dashes = ['-' for _ in range(self.__SECTION_N_DASH)]
57
+ return format.get_formatting_tabs(f"\t{''.join(dashes)}\n")
58
+
59
+ def _text_with_limits(self, previous_text: str, text: str) -> str:
60
+ """
61
+ ### Returns the given text, formatted so that it does not exceed the character limit by line for the param help section.
62
+
63
+ ### Params:
64
+ - previous_text (str): Text inserted before the one to be returned.
65
+ - text (str): The text to be formatted.
66
+
67
+ ### Returns:
68
+ - (str): Formatted text.
69
+ """
70
+ remaining_space, fill_in_space = self.__get_spaces(previous_text)
71
+ formatted_help = self.__multi_line_help_text(
72
+ text,
73
+ remaining_space,
74
+ self.__get_start_section_fill_in_space('')
75
+ )
76
+ return f"{previous_text}{fill_in_space}{formatted_help}\n"
77
+
78
+ def _get_text_length(self, text: str) -> int:
79
+ """
80
+ ### Returns the length of a text that might contain tabs.
81
+
82
+ #### Params:
83
+ - text (str): Text to measure.
84
+
85
+ #### Returns:
86
+ - (int): Text's length.
87
+ """
88
+ return len(format.get_formatting_tabs(text))
89
+
90
+ def __get_message_from_list(self, messages: List[str], only_general: bool) -> str:
91
+ """
92
+ ### Return the appropiate message given a list of them and a condition.
93
+
94
+ #### Params:
95
+ - messages (list[str]): List of messages.
96
+ - only_general (bool): If True, only the general (first) message is returned.
97
+
98
+ #### Returns:
99
+ - (str): Expected messages in a string.
100
+ """
101
+ return messages[0] if only_general else '\n'.join(messages)
102
+
103
+ def __get_line_size(self) -> int:
104
+ """
105
+ ### Returns the maximum size for a line.
106
+
107
+ ### Returns:
108
+ - (int): Maximum line size.
109
+ """
110
+ size = shutil.get_terminal_size().columns
111
+ return self.__LINE_SIZE_LOWER_LIMIT if size < self.__LINE_SIZE_LOWER_LIMIT else size
112
+
113
+ def __multi_line_help_text(self, text: str, size_limit: int, left_fill: str) -> str:
114
+ """
115
+ ### This function returns the given text, formatted in several lines so that it does not exceed the given character limit.
116
+
117
+ ### Params:
118
+ - text (str): The text to be formatted.
119
+ - size_limit (int): Size limit for the text.
120
+ - left_fill (str): String to add at the left of each new line.
121
+
122
+ ### Returns:
123
+ - (str): Formatted text.
124
+ """
125
+ return (
126
+ text
127
+ if len(text) <= size_limit else
128
+ self.__format_text_in_lines(text, size_limit, left_fill)
129
+ )
130
+
131
+ def __fit_words_in_line(self, words: List[str], size_limit: int) -> Tuple[str, List[str]]:
132
+ """
133
+ ### Returns a tuple containig a line with the words from the given list that could fit given the size limit, and the list with the remaining words.
134
+
135
+ ### Params:
136
+ - words (list[str]): List of words to try to fit into a line.
137
+ - size_limit (int): Size limit for the text.
138
+
139
+ ### Returns:
140
+ - (str): Line with the words that were able to fit in it.
141
+ - (list[str]): List containing the words that could not fit in the line.
142
+ """
143
+ line = ''
144
+ remaining_words = words
145
+ for word in words:
146
+ if self.__word_fits_in_line(line, word, size_limit):
147
+ line, remaining_words = self.__add_word_to_line(line, word, remaining_words)
148
+ else:
149
+ break
150
+ return line, remaining_words
151
+
152
+ def __word_fits_in_line(self, line: str, word: str, size_limit: int) -> bool:
153
+ """
154
+ ### Checks if a word can fit in the current line without exceeding the size limit.
155
+
156
+ ### Params:
157
+ - line (str): The current line of text.
158
+ - word (str): The word to check.
159
+ - size_limit (int): The maximum allowed size for the line.
160
+
161
+ ### Returns:
162
+ - (bool): True if the word fits in the line, False otherwise.
163
+ """
164
+ if line:
165
+ return len(f"{line} {word}") <= size_limit
166
+ return len(word) <= size_limit
167
+
168
+ def __add_word_to_line(self, line: str, word: str, remaining_words: List[str]) -> Tuple[str, List[str]]:
169
+ """
170
+ ### Adds a word to the current line and updates the list of remaining words.
171
+
172
+ ### Params:
173
+ - line (str): The current line of text.
174
+ - word (str): The word to add to the line.
175
+ - remaining_words (list[str]): The list of words yet to be added to the line.
176
+
177
+ ### Returns:
178
+ - (str): The updated line with the new word added.
179
+ - (list[str]): The updated list of remaining words.
180
+ """
181
+ if line:
182
+ line += f" {word}"
183
+ else:
184
+ line = word
185
+ return line, remaining_words[1:]
186
+
187
+ def __format_text_in_lines(self, text: str, size_limit: int, left_fill: str):
188
+ """
189
+ ### Returns the text formatted into size-limited lines.
190
+
191
+ #### Params:
192
+ - text (str): Text to format.
193
+ - size_limit (int): Max number of characters allowed in a single line.
194
+ - left_fill (str): Starting characters of each line.
195
+
196
+ #### Returns:
197
+ - (str): Text formatted into lines.
198
+ """
199
+ words = text.split(' ')
200
+ lines = []
201
+ while words:
202
+ iteration_size_limit = size_limit if size_limit >= len(words[0]) else len(words[0])
203
+ line, words = self.__fit_words_in_line(words, iteration_size_limit)
204
+ line = left_fill + line if lines else line
205
+ lines.append(line)
206
+ return '\n'.join(lines)
207
+
208
+ def __get_spaces(self, start_section_text: str) -> Tuple[str, str]:
209
+ if self.__is_start_section_text_exceeding_size_limit(start_section_text):
210
+ # If text exceeds size limit, it means that section space for modes and params
211
+ # is too low and should be set to a higher number, but for now we need to print anyways,
212
+ # so we reduce space from the one reserved for mode help and add minimum fill-in space
213
+ remaining_space = self.__get_line_size() - self._get_text_length(start_section_text)
214
+ fill_in_space = ' '
215
+ else:
216
+ remaining_space = self.__get_line_size() - self.__SECTION_HELP_START
217
+ fill_in_space = self.__get_start_section_fill_in_space(start_section_text)
218
+ return remaining_space, fill_in_space
219
+
220
+ def __get_start_section_fill_in_space(self, text: str) -> str:
221
+ """
222
+ ### Returns the fill-in space for the start section.
223
+
224
+ #### Params:
225
+ - text (str): Text inside the start section.
226
+
227
+ #### Returns:
228
+ - (str): The required number of spaces to generate the start section's fill-in.
229
+ """
230
+ return ''.join(
231
+ [' ' for _ in range(self.__SECTION_HELP_START - self._get_text_length(text))]
232
+ )
233
+
234
+ def __is_start_section_text_exceeding_size_limit(self, start_section_text: str) -> bool:
235
+ """
236
+ ### Indicates if the given start section text exceedes allowed size limit.
237
+
238
+ #### Params:
239
+ - start_section_text (str): Text to measure.
240
+
241
+ #### Returns:
242
+ - (bool): True if the text exceedes allowed size limit.
243
+ """
244
+ return self._get_text_length(start_section_text) >= self.__SECTION_HELP_START
@@ -0,0 +1,68 @@
1
+ """### Argparser that shows the error messages formatted in a custom way."""
2
+
3
+ import argparse
4
+ from typing import List
5
+
6
+ from xmipp3_installer.application.cli.parsers import format
7
+ from xmipp3_installer.application.logger.logger import logger
8
+
9
+ class ErrorHandlerArgumentParser(argparse.ArgumentParser):
10
+ """
11
+ Overrides the error function of the standard argument parser
12
+ to display better error messages.
13
+ """
14
+ def error(self, message):
15
+ """
16
+ ### Prints through stderr the error message and exits with specific return code.
17
+
18
+ #### Params:
19
+ - message (str): Error message.
20
+ """
21
+ args = self.__get_args()
22
+ mode = self.__get_mode(args)
23
+
24
+ if self.__is_mode_generic(args):
25
+ args = ' '.join(args[:-1])
26
+ extra_line_break = '\n'
27
+ else:
28
+ args = self.format_help()
29
+ extra_line_break = ''
30
+
31
+ error_message = logger.red(f"{mode}: error: {message}\n")
32
+ self.exit(
33
+ 1,
34
+ format.get_formatting_tabs(f"{args}{extra_line_break}{error_message}")
35
+ )
36
+
37
+ def __get_args(self) -> List[str]:
38
+ """
39
+ ### Obtains args from stored class data.
40
+
41
+ #### Returns:
42
+ - (list[str]): List of arguments.
43
+ """
44
+ return self.prog.split(' ')
45
+
46
+ def __get_mode(self, args: List[str]) -> str:
47
+ """
48
+ ### Obtains the usage mode from the received args.
49
+
50
+ #### Params:
51
+ - args (list[str]): List of args received by the parser.
52
+
53
+ #### Returns:
54
+ - (str): Usage mode.
55
+ """
56
+ return args[-1]
57
+
58
+ def __is_mode_generic(self, args: List[str]) -> bool:
59
+ """
60
+ ### Returns True if the usage mode selected is the generic one.
61
+
62
+ #### Params:
63
+ - args (list[str]): List of received args.
64
+
65
+ #### Returns:
66
+ - (bool): True if the received mode is the generic one.
67
+ """
68
+ return len(args) > 1
@@ -0,0 +1,35 @@
1
+ """### Common formatting functions for parsers."""
2
+
3
+ from typing import List
4
+
5
+ from xmipp3_installer.application.cli.arguments import params
6
+
7
+ TAB_SIZE = 4
8
+
9
+ def get_formatting_tabs(text: str) -> str:
10
+ """
11
+ ### Returns the given text, formatted to expand tabs into a fixed tab size.
12
+
13
+ ### Params:
14
+ - text (str): The text to be formatted.
15
+
16
+ ### Returns:
17
+ - (str): Formatted text.
18
+ """
19
+ return text.expandtabs(TAB_SIZE)
20
+
21
+ def get_param_names(param_key: str) -> List[str]:
22
+ """
23
+ ### Returns the list of possible names a given param has.
24
+
25
+ #### Params:
26
+ - param_key (str): Key to find the param.
27
+
28
+ #### Returns:
29
+ - (list[str]): Names of the given param.
30
+ """
31
+ names = [
32
+ params.PARAMS[param_key].get(params.SHORT_VERSION, ''),
33
+ params.PARAMS[param_key].get(params.LONG_VERSION, '')
34
+ ]
35
+ return [name for name in names if name]