idmtools 0.0.0.dev0__py3-none-any.whl → 0.0.3__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 (118) hide show
  1. idmtools/__init__.py +27 -8
  2. idmtools/analysis/__init__.py +5 -0
  3. idmtools/analysis/add_analyzer.py +89 -0
  4. idmtools/analysis/analyze_manager.py +490 -0
  5. idmtools/analysis/csv_analyzer.py +103 -0
  6. idmtools/analysis/download_analyzer.py +96 -0
  7. idmtools/analysis/map_worker_entry.py +100 -0
  8. idmtools/analysis/platform_analysis_bootstrap.py +94 -0
  9. idmtools/analysis/platform_anaylsis.py +291 -0
  10. idmtools/analysis/tags_analyzer.py +93 -0
  11. idmtools/assets/__init__.py +9 -0
  12. idmtools/assets/asset.py +453 -0
  13. idmtools/assets/asset_collection.py +514 -0
  14. idmtools/assets/content_handlers.py +19 -0
  15. idmtools/assets/errors.py +23 -0
  16. idmtools/assets/file_list.py +191 -0
  17. idmtools/builders/__init__.py +11 -0
  18. idmtools/builders/arm_simulation_builder.py +152 -0
  19. idmtools/builders/csv_simulation_builder.py +76 -0
  20. idmtools/builders/simulation_builder.py +348 -0
  21. idmtools/builders/sweep_arm.py +109 -0
  22. idmtools/builders/yaml_simulation_builder.py +82 -0
  23. idmtools/config/__init__.py +7 -0
  24. idmtools/config/idm_config_parser.py +486 -0
  25. idmtools/core/__init__.py +10 -0
  26. idmtools/core/cache_enabled.py +114 -0
  27. idmtools/core/context.py +68 -0
  28. idmtools/core/docker_task.py +207 -0
  29. idmtools/core/enums.py +51 -0
  30. idmtools/core/exceptions.py +91 -0
  31. idmtools/core/experiment_factory.py +71 -0
  32. idmtools/core/id_file.py +70 -0
  33. idmtools/core/interfaces/__init__.py +5 -0
  34. idmtools/core/interfaces/entity_container.py +64 -0
  35. idmtools/core/interfaces/iassets_enabled.py +58 -0
  36. idmtools/core/interfaces/ientity.py +331 -0
  37. idmtools/core/interfaces/iitem.py +206 -0
  38. idmtools/core/interfaces/imetadata_operations.py +89 -0
  39. idmtools/core/interfaces/inamed_entity.py +17 -0
  40. idmtools/core/interfaces/irunnable_entity.py +159 -0
  41. idmtools/core/logging.py +387 -0
  42. idmtools/core/platform_factory.py +316 -0
  43. idmtools/core/system_information.py +104 -0
  44. idmtools/core/task_factory.py +145 -0
  45. idmtools/entities/__init__.py +10 -0
  46. idmtools/entities/command_line.py +229 -0
  47. idmtools/entities/command_task.py +155 -0
  48. idmtools/entities/experiment.py +787 -0
  49. idmtools/entities/generic_workitem.py +43 -0
  50. idmtools/entities/ianalyzer.py +163 -0
  51. idmtools/entities/iplatform.py +1106 -0
  52. idmtools/entities/iplatform_default.py +39 -0
  53. idmtools/entities/iplatform_ops/__init__.py +5 -0
  54. idmtools/entities/iplatform_ops/iplatform_asset_collection_operations.py +148 -0
  55. idmtools/entities/iplatform_ops/iplatform_experiment_operations.py +415 -0
  56. idmtools/entities/iplatform_ops/iplatform_simulation_operations.py +315 -0
  57. idmtools/entities/iplatform_ops/iplatform_suite_operations.py +322 -0
  58. idmtools/entities/iplatform_ops/iplatform_workflowitem_operations.py +301 -0
  59. idmtools/entities/iplatform_ops/utils.py +185 -0
  60. idmtools/entities/itask.py +316 -0
  61. idmtools/entities/iworkflow_item.py +167 -0
  62. idmtools/entities/platform_requirements.py +20 -0
  63. idmtools/entities/relation_type.py +14 -0
  64. idmtools/entities/simulation.py +255 -0
  65. idmtools/entities/suite.py +188 -0
  66. idmtools/entities/task_proxy.py +37 -0
  67. idmtools/entities/templated_simulation.py +325 -0
  68. idmtools/frozen/frozen_dict.py +71 -0
  69. idmtools/frozen/frozen_list.py +66 -0
  70. idmtools/frozen/frozen_set.py +86 -0
  71. idmtools/frozen/frozen_tuple.py +18 -0
  72. idmtools/frozen/frozen_utils.py +179 -0
  73. idmtools/frozen/ifrozen.py +66 -0
  74. idmtools/plugins/__init__.py +5 -0
  75. idmtools/plugins/git_commit.py +117 -0
  76. idmtools/registry/__init__.py +4 -0
  77. idmtools/registry/experiment_specification.py +105 -0
  78. idmtools/registry/functions.py +28 -0
  79. idmtools/registry/hook_specs.py +132 -0
  80. idmtools/registry/master_plugin_registry.py +51 -0
  81. idmtools/registry/platform_specification.py +138 -0
  82. idmtools/registry/plugin_specification.py +129 -0
  83. idmtools/registry/task_specification.py +104 -0
  84. idmtools/registry/utils.py +119 -0
  85. idmtools/services/__init__.py +5 -0
  86. idmtools/services/ipersistance_service.py +135 -0
  87. idmtools/services/platforms.py +13 -0
  88. idmtools/utils/__init__.py +5 -0
  89. idmtools/utils/caller.py +24 -0
  90. idmtools/utils/collections.py +246 -0
  91. idmtools/utils/command_line.py +45 -0
  92. idmtools/utils/decorators.py +300 -0
  93. idmtools/utils/display/__init__.py +22 -0
  94. idmtools/utils/display/displays.py +181 -0
  95. idmtools/utils/display/settings.py +25 -0
  96. idmtools/utils/dropbox_location.py +30 -0
  97. idmtools/utils/entities.py +127 -0
  98. idmtools/utils/file.py +72 -0
  99. idmtools/utils/file_parser.py +151 -0
  100. idmtools/utils/filter_simulations.py +182 -0
  101. idmtools/utils/filters/__init__.py +5 -0
  102. idmtools/utils/filters/asset_filters.py +88 -0
  103. idmtools/utils/general.py +286 -0
  104. idmtools/utils/gitrepo.py +336 -0
  105. idmtools/utils/hashing.py +239 -0
  106. idmtools/utils/info.py +124 -0
  107. idmtools/utils/json.py +82 -0
  108. idmtools/utils/language.py +107 -0
  109. idmtools/utils/local_os.py +40 -0
  110. idmtools/utils/time.py +22 -0
  111. idmtools-0.0.3.dist-info/METADATA +120 -0
  112. idmtools-0.0.3.dist-info/RECORD +116 -0
  113. idmtools-0.0.3.dist-info/entry_points.txt +9 -0
  114. idmtools-0.0.3.dist-info/licenses/LICENSE.TXT +3 -0
  115. idmtools-0.0.0.dev0.dist-info/METADATA +0 -41
  116. idmtools-0.0.0.dev0.dist-info/RECORD +0 -5
  117. {idmtools-0.0.0.dev0.dist-info → idmtools-0.0.3.dist-info}/WHEEL +0 -0
  118. {idmtools-0.0.0.dev0.dist-info → idmtools-0.0.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,159 @@
1
+ """
2
+ IRunnableEntity definition. IRunnableEntity defines items that can be ran using platform.run().
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ from dataclasses import field, dataclass
7
+ from inspect import signature
8
+ from logging import getLogger, DEBUG
9
+ from typing import List, Callable, TYPE_CHECKING, NoReturn
10
+ from abc import ABCMeta
11
+ from idmtools.core import EntityStatus
12
+ from idmtools.core.interfaces.ientity import IEntity
13
+ from idmtools.registry.functions import FunctionPluginManager
14
+
15
+ if TYPE_CHECKING: # pragma: no cover
16
+ from idmtools.entities.iplatform import IPlatform
17
+
18
+ runnable_hook = Callable[['IRunnableEntity', 'IPlatform'], None]
19
+ logger = getLogger(__name__)
20
+ user_logger = getLogger('user')
21
+
22
+
23
+ @dataclass
24
+ class IRunnableEntity(IEntity, metaclass=ABCMeta):
25
+ """
26
+ IRunnableEntity are items that can be ran on platforms like Experiments or WorkItems.
27
+
28
+ IRunnableEntity also add pre and post run hooks available to the IEntity class.
29
+ """
30
+ __pre_run_hooks: List[runnable_hook] = field(default_factory=list, metadata={"md": True})
31
+ __post_run_hooks: List[runnable_hook] = field(default_factory=list, metadata={"md": True})
32
+
33
+ def pre_run(self, platform: 'IPlatform') -> None:
34
+ """
35
+ Called before the actual creation of the entity.
36
+
37
+ Args:
38
+ platform: Platform item is being created on
39
+
40
+ Returns:
41
+ None
42
+ """
43
+ for hook in self.__pre_run_hooks:
44
+ if logger.isEnabledFor(DEBUG):
45
+ logger.debug(f'Calling pre-create hook named {hook.__name__ if hasattr(hook, "__name__") else str(hook)}')
46
+ hook(self, platform)
47
+
48
+ def post_run(self, platform: 'IPlatform') -> None:
49
+ """
50
+ Called after the actual creation of the entity.
51
+
52
+ Args:
53
+ platform: Platform item was created on
54
+
55
+ Returns:
56
+ None
57
+ """
58
+ for hook in self.__post_run_hooks:
59
+ if logger.isEnabledFor(DEBUG):
60
+ logger.debug(f'Calling pre-create hook named {hook.__name__ if hasattr(hook, "__name__") else str(hook)}')
61
+ hook(self, platform)
62
+
63
+ def add_pre_run_hook(self, hook: runnable_hook):
64
+ """
65
+ Adds a hook function to be called before an item is ran.
66
+
67
+ Args:
68
+ hook: Hook function. This should have two arguments, the item and the platform
69
+
70
+ Returns:
71
+ None
72
+ """
73
+ if len(signature(hook).parameters) != 2:
74
+ raise ValueError("Pre Run hooks should have 2 arguments. The first argument will be the item, the second the platform")
75
+ self.__pre_run_hooks.append(hook)
76
+
77
+ def add_post_run_hook(self, hook: runnable_hook):
78
+ """
79
+ Adds a hook function to be called after an item has ran.
80
+
81
+ Args:
82
+ hook: Hook function. This should have two arguments, the item and the platform
83
+
84
+ Returns:
85
+ None
86
+ """
87
+ if len(signature(hook).parameters) != 2:
88
+ raise ValueError("Post Run hooks should have 2 arguments. The first argument will be the item, the second the platform")
89
+ self.__post_run_hooks.append(hook)
90
+
91
+ def run(self, wait_until_done: bool = False, platform: 'IPlatform' = None, wait_on_done_progress: bool = True, **run_opts) -> NoReturn:
92
+ """
93
+ Runs an item.
94
+
95
+ Args:
96
+ wait_until_done: Whether we should wait on item to finish running as well. Defaults to False
97
+ platform: Platform object to use. If not specified, we first check object for platform object then the current context
98
+ wait_on_done_progress: Defaults to true
99
+ **run_opts: Options to pass to the platform
100
+
101
+ Returns:
102
+ None
103
+ """
104
+ p = super()._check_for_platform_from_context(platform)
105
+ if 'wait_on_done' in run_opts:
106
+ raise TypeError("The 'wait_on_done' parameter has been removed in idmtools 1.8.0. Please update your code with 'wait_until_done'.")
107
+ p.run_items(self, wait_on_done_progress=wait_on_done_progress, **run_opts)
108
+ dry_run = run_opts.get('dry_run', False)
109
+ if dry_run:
110
+ return
111
+ if wait_until_done:
112
+ _refresh_interval = run_opts.get('refresh_interval', None)
113
+ if _refresh_interval is None:
114
+ _refresh_interval = p.refresh_interval
115
+ self.wait(wait_on_done_progress=wait_on_done_progress, platform=p, refresh_interval=_refresh_interval)
116
+
117
+ def wait(self, wait_on_done_progress: bool = True, timeout: int = None, refresh_interval=None, platform: 'IPlatform' = None, **kwargs):
118
+ """
119
+ Wait on an item to finish running.
120
+
121
+ Args:
122
+ wait_on_done_progress: Should we show progress as we wait?
123
+ timeout: Timeout to wait
124
+ refresh_interval: How often to refresh object
125
+ platform: Platform. If not specified, we try to determine this from context
126
+
127
+ Returns:
128
+ None
129
+ """
130
+ # If done, exit
131
+ if self.status in [EntityStatus.SUCCEEDED, EntityStatus.FAILED]:
132
+ return
133
+ if self.status not in [EntityStatus.CREATED, EntityStatus.RUNNING]:
134
+ raise ValueError("The item cannot be waited for if it is not in Running/Created state")
135
+ opts = dict(**kwargs)
136
+ if timeout:
137
+ opts['timeout'] = timeout
138
+ if refresh_interval:
139
+ opts['refresh_interval'] = refresh_interval
140
+ p = super()._check_for_platform_from_context(platform)
141
+ if wait_on_done_progress:
142
+ p.wait_till_done_progress(self, **opts)
143
+ else:
144
+ p.wait_till_done(self, **opts)
145
+
146
+ self.after_done()
147
+
148
+ def after_done(self):
149
+ """
150
+ Run after an item is done after waiting. Currently we call the on succeeded and on failure plugins.
151
+
152
+ Returns:
153
+ Runs after an item is done after waiting
154
+ """
155
+ FunctionPluginManager.instance().hook.idmtools_runnable_on_done(item=self)
156
+ if self.succeeded:
157
+ FunctionPluginManager.instance().hook.idmtools_runnable_on_succeeded(item=self)
158
+ else:
159
+ FunctionPluginManager.instance().hook.idmtools_runnable_on_failure(item=self)
@@ -0,0 +1,387 @@
1
+ """
2
+ idmtools logging module.
3
+
4
+ We configure our logging here, manage multi-process logging, alternate logging level, and additional utilities to manage logging.
5
+
6
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
7
+ """
8
+ import logging
9
+ import os
10
+ import sys
11
+ import time
12
+ import threading
13
+ from contextlib import suppress
14
+ from dataclasses import dataclass
15
+ from logging import getLogger
16
+ from logging.handlers import RotatingFileHandler
17
+ from typing import Union, Optional
18
+ import coloredlogs as coloredlogs
19
+ from idmtools.core import TRUTHY_VALUES
20
+
21
+ LOGGING_STARTED = False
22
+ LOGGING_FILE_STARTED = False
23
+ LOGGING_FILE_HANDLER = None
24
+
25
+ VERBOSE = 15
26
+ NOTICE = 25
27
+ SUCCESS = 35
28
+ CRITICAL = 50
29
+
30
+
31
+ @dataclass
32
+ class IdmToolsLoggingConfig:
33
+ """
34
+ Defines the config options available for idmtools logs.
35
+ """
36
+ #: Console level
37
+ level: Union[str, int] = logging.WARN
38
+ #: Filename for idmtools logs
39
+ filename: Optional[str] = 'idmtools.log'
40
+ #: Toggle to enable/disable console logging
41
+ console: bool = False
42
+ #: File log level
43
+ file_level: Union[str, int] = 'DEBUG'
44
+ #: Should we force reload
45
+ force: bool = False
46
+ #: File format string. See https://docs.python.org/3/library/logging.html#logrecord-attributes for format vars
47
+ file_log_format_str: str = None
48
+ #: Logging format. See https://docs.python.org/3/library/logging.html#logrecord-attributes for format vars
49
+ user_log_format_str: str = '%(message)s'
50
+ #: Toggle to enable/disable coloredlogs
51
+ use_colored_logs: bool = True
52
+ #: Toggle user output. This should only be used in certain situations like CLI's that output JSON
53
+ user_output: bool = True
54
+ #: Toggle enable file logging
55
+ enable_file_logging: Union[str, bool] = True
56
+
57
+ def __post_init__(self):
58
+ """
59
+ Validates logging config creation.
60
+ """
61
+ # load the user input from string
62
+ if isinstance(self.user_output, str):
63
+ self.user_output = self.user_output.lower() in TRUTHY_VALUES
64
+
65
+ # load color logs from string
66
+ if isinstance(self.use_colored_logs, str):
67
+ self.use_colored_logs = self.use_colored_logs.lower() in TRUTHY_VALUES
68
+
69
+ # load console from string
70
+ if isinstance(self.console, str):
71
+ self.console = self.console.lower() in TRUTHY_VALUES
72
+
73
+ if type(self.enable_file_logging) is str:
74
+ self.enable_file_logging = self.enable_file_logging.lower() in TRUTHY_VALUES
75
+
76
+ # ensure level is a logging level
77
+ for attr in ['level', 'file_level']:
78
+ if isinstance(getattr(self, attr), str):
79
+ setattr(self, attr, logging.getLevelName(getattr(self, attr)))
80
+
81
+ # set default name is not set.
82
+ if self.filename is None:
83
+ self.filename = 'idmtools.log'
84
+
85
+ # ensure whitespace is gone
86
+ self.filename = self.filename.strip()
87
+
88
+ # disable file logging when set to -1
89
+ if self.filename == "-1" or not self.enable_file_logging:
90
+ self.filename = None
91
+
92
+ # handle special case
93
+ if not self.enable_file_logging and self.console is None:
94
+ self.console = True
95
+
96
+ # set default file log format str
97
+ if self.file_log_format_str is None:
98
+ if self.file_level == logging.DEBUG:
99
+ # Enable detailed logging format
100
+ self.file_log_format_str = '%(asctime)s.%(msecs)d %(pathname)s:%(lineno)d %(funcName)s [%(levelname)s] (%(process)d,%(thread)d) - %(message)s'
101
+ else:
102
+ self.file_log_format_str = '%(asctime)s.%(msecs)d %(pathname)s:%(lineno)d %(funcName)s [%(levelname)s] - %(message)s'
103
+
104
+
105
+ class MultiProcessSafeRotatingFileHandler(RotatingFileHandler):
106
+ """
107
+ Multi-process safe logger.
108
+ """
109
+
110
+ def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False):
111
+ """
112
+ See RotatingFileHandler for full details on arguments.
113
+
114
+ Args:
115
+ filename:Filename to use
116
+ mode:Mode
117
+ maxBytes: Max bytes
118
+ backupCount: Total backups
119
+ encoding: Encoding
120
+ delay: Delay
121
+ """
122
+ super().__init__(filename, mode=mode, maxBytes=maxBytes, backupCount=backupCount, encoding=encoding,
123
+ delay=delay)
124
+ self.logger_lock = threading.Lock()
125
+
126
+ def handle(self, record: logging.LogRecord) -> None:
127
+ """
128
+ Thread safe logger.
129
+
130
+ Args:
131
+ record: Record to handle
132
+
133
+ Returns:
134
+ None
135
+ """
136
+ self.logger_lock.acquire()
137
+ try:
138
+ super(MultiProcessSafeRotatingFileHandler, self).handle(record)
139
+ finally:
140
+ self.logger_lock.release()
141
+
142
+ def doRollover(self) -> None:
143
+ """
144
+ Perform rollover safely.
145
+
146
+ We loop and try to move the log file. If we encounter an issue, we try to retry three times.
147
+ If we failed after three times, we try a new process id appended to file name.
148
+
149
+ Returns:
150
+ None
151
+ """
152
+ attempts = 0
153
+ while True:
154
+ try:
155
+ super().doRollover()
156
+ break
157
+ except PermissionError:
158
+ attempts += 1
159
+ if attempts > 3:
160
+ # add a pid to make unique or expand
161
+ self.baseFilename += f".{os.getpid()}"
162
+ attempts = 0
163
+ time.sleep(0.08)
164
+
165
+
166
+ class PrintHandler(logging.Handler):
167
+ """
168
+ A simple print handler. Used in cases where logging fails.
169
+ """
170
+
171
+ def handle(self, record: logging.LogRecord) -> None:
172
+ """
173
+ Simple log handler that prints to stdout.
174
+
175
+ Args:
176
+ record: Record to print
177
+
178
+ Returns:
179
+ None
180
+ """
181
+ try:
182
+ print(record.msg)
183
+ except: # noqa: E722
184
+ pass
185
+
186
+
187
+ def setup_logging(logging_config: IdmToolsLoggingConfig) -> None:
188
+ """
189
+ Set up logging.
190
+
191
+ Args:
192
+ logging_config: IdmToolsLoggingConfig that defines our config
193
+
194
+ Returns:
195
+ Returns None
196
+
197
+ See Also:
198
+ For logging levels, see https://coloredlogs.readthedocs.io/en/latest/api.html#id26
199
+ """
200
+ global LOGGING_STARTED, LOGGING_FILE_STARTED, LOGGING_FILE_HANDLER
201
+ if not LOGGING_STARTED or logging_config.force:
202
+ # reset logging and remove all handlers and delete our current handler
203
+ reset_logging_handlers()
204
+
205
+ logging.addLevelName(15, 'VERBOSE')
206
+ logging.addLevelName(25, 'NOTICE')
207
+ logging.addLevelName(35, 'SUCCESS')
208
+
209
+ # get a file handler
210
+ root = logging.getLogger()
211
+ user = logging.getLogger('user')
212
+ # allow setting the debug of logger via environment variable
213
+ root.setLevel(logging_config.level)
214
+ user.setLevel(logging.DEBUG)
215
+
216
+ # file logging config
217
+ if not LOGGING_FILE_STARTED or logging_config.force:
218
+ LOGGING_FILE_HANDLER = setup_handlers(logging_config)
219
+ if LOGGING_FILE_HANDLER:
220
+ LOGGING_FILE_STARTED = True
221
+
222
+ # Show we enable user output. The only time we really should not do this is for specific CLI use cases
223
+ # # such as json output
224
+ setup_user_logger(logging_config)
225
+
226
+ if root.isEnabledFor(logging.DEBUG):
227
+ from idmtools import __version__
228
+ root.debug(f"idmtools core version: {__version__}")
229
+
230
+ # python logger creates default stream handler when no handlers are set so create null handler
231
+ if len(root.handlers) == 0:
232
+ root.addHandler(logging.NullHandler())
233
+
234
+ # set file logging stated
235
+ LOGGING_FILE_STARTED = True
236
+ # set logging stated
237
+ LOGGING_STARTED = True
238
+
239
+
240
+ def setup_handlers(logging_config: IdmToolsLoggingConfig):
241
+ """
242
+ Setup Handlers for Global and user Loggers.
243
+
244
+ Args:
245
+ logging_config: Logging config
246
+
247
+ Returns:
248
+ FileHandler or None
249
+ """
250
+ # We only one to do this setup once per process. Having the logging_queue setup help prevent that issue
251
+ # get a file handler
252
+
253
+ exclude_logging_classes()
254
+ file_handler = None
255
+ if logging_config.filename:
256
+ formatter = logging.Formatter(logging_config.file_log_format_str)
257
+ # set the logging to either common level or the file-level
258
+ file_handler = set_file_logging(logging_config, formatter)
259
+
260
+ # If user output is enabled and console is enabled
261
+ if logging_config.user_output and logging_config.console:
262
+ # is colored logging enabled? if so, add our logger
263
+ # note: this will be used for user logging as well
264
+ if logging_config.use_colored_logs:
265
+ coloredlogs.install(level=logging_config.level, milliseconds=True, stream=sys.stdout)
266
+ else:
267
+ # Mainly for test/local platform
268
+ print_handler = PrintHandler(level=logging_config.level)
269
+ getLogger().addHandler(print_handler)
270
+ return file_handler
271
+
272
+
273
+ def setup_user_logger(logging_config: IdmToolsLoggingConfig):
274
+ """
275
+ Setup the user logger. This logger is meant for user output only.
276
+
277
+ Args:
278
+ logging_config: Logging config object.
279
+
280
+ Returns:
281
+ None
282
+ """
283
+ if logging_config.user_output and not logging_config.console:
284
+ # is colored logs enabled? If so, make the user logger a coloredlogger
285
+ if logging_config.use_colored_logs:
286
+ coloredlogs.install(logger=getLogger('user'), level=VERBOSE, fmt='%(message)s', stream=sys.stdout)
287
+ else: # fall back to a print handler
288
+ setup_user_print_logger()
289
+
290
+
291
+ def setup_user_print_logger():
292
+ """
293
+ Setup a print based logger for user messages.
294
+
295
+ Returns:
296
+ None
297
+ """
298
+ formatter = logging.Formatter(fmt='%(message)s')
299
+ handler = PrintHandler(level=VERBOSE)
300
+ handler.setFormatter(formatter)
301
+ # should everything be printed using the print logger or filename was set to be empty. This means log
302
+ # everything to the screen without color
303
+ getLogger('user').addHandler(handler)
304
+
305
+
306
+ def set_file_logging(logging_config: IdmToolsLoggingConfig, formatter: logging.Formatter):
307
+ """
308
+ Set File Logging.
309
+
310
+ Args:
311
+ logging_config: Logging config object.
312
+ formatter: Formatter obj
313
+
314
+ Returns:
315
+ Return File handler
316
+ """
317
+ file_handler = create_file_handler(logging_config.file_level, formatter, logging_config.filename)
318
+ if file_handler is None:
319
+ # We had an issue creating file handler, so let's try using default name + pids
320
+ for i in range(64): # We go to 64. This is a reasonable max id for any computer we might actually run item.
321
+ file_handler = create_file_handler(logging_config.file_level, formatter,
322
+ f"{logging_config.filename}.{i}.log")
323
+ if file_handler:
324
+ break
325
+ if file_handler is None:
326
+ raise ValueError(
327
+ "Could not file a valid log. Either all the files are opened or you are on a read-only filesystem. You can disable file-based logging by setting")
328
+ logging.root.addHandler(file_handler)
329
+ return file_handler
330
+
331
+
332
+ def create_file_handler(file_level, formatter: logging.Formatter, filename: str):
333
+ """
334
+ Create a MultiProcessSafeRotatingFileHandler for idmtools.log.
335
+
336
+ Args:
337
+ file_level: Level to log to file
338
+ formatter: Formatter to set on the handler
339
+ filename: Filename to use
340
+
341
+ Returns:
342
+ SafeRotatingFileHandler with properties provided
343
+ """
344
+ try:
345
+ file_handler = MultiProcessSafeRotatingFileHandler(filename, maxBytes=(2 ** 20) * 10, backupCount=5)
346
+ file_handler.setLevel(file_level)
347
+ file_handler.setFormatter(formatter)
348
+ except PermissionError:
349
+ return None
350
+ return file_handler
351
+
352
+
353
+ def reset_logging_handlers():
354
+ """
355
+ Reset all the logging handlers by removing the root handler.
356
+
357
+ Returns:
358
+ None
359
+ """
360
+ global LOGGING_STARTED, LOGGING_FILE_STARTED, LOGGING_FILE_HANDLER
361
+
362
+ with suppress(KeyError):
363
+ # Remove all handlers associated with the root logger object.
364
+ for handler in logging.root.handlers[:]:
365
+ logging.root.removeHandler(handler)
366
+ # Clean up file handler now
367
+ LOGGING_FILE_STARTED = False
368
+ LOGGING_STARTED = False
369
+ LOGGING_FILE_HANDLER = None
370
+
371
+
372
+ def exclude_logging_classes(items_to_exclude=None):
373
+ """
374
+ Exclude items from our logger by setting level to warning.
375
+
376
+ Args:
377
+ items_to_exclude: Items to exclude
378
+
379
+ Returns:
380
+ None
381
+ """
382
+ if items_to_exclude is None:
383
+ items_to_exclude = ['urllib3', 'paramiko', 'matplotlib']
384
+ # remove comps by default
385
+ for item in items_to_exclude:
386
+ other_logger = getLogger(item)
387
+ other_logger.setLevel(logging.WARN)