idmtools 0.0.0.dev0__py3-none-any.whl → 0.0.2__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.2.dist-info/METADATA +120 -0
  112. idmtools-0.0.2.dist-info/RECORD +116 -0
  113. idmtools-0.0.2.dist-info/entry_points.txt +9 -0
  114. idmtools-0.0.2.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.2.dist-info}/WHEEL +0 -0
  118. {idmtools-0.0.0.dev0.dist-info → idmtools-0.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,138 @@
1
+ """
2
+ PlatformSpecification provided definition for the platform plugin specification, hooks, and plugin manager.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ # Define our platform specific specifications
7
+ import typing
8
+ from abc import ABC
9
+ from logging import getLogger, DEBUG
10
+ import pluggy
11
+ from idmtools.registry import PluginSpecification
12
+ from idmtools.registry.plugin_specification import PLUGIN_REFERENCE_NAME
13
+ from idmtools.registry.utils import load_plugin_map
14
+ from idmtools.utils.decorators import SingletonMixin
15
+
16
+ if typing.TYPE_CHECKING:
17
+ from idmtools.entities.iplatform import IPlatform
18
+ example_configuration_spec = pluggy.HookspecMarker(PLUGIN_REFERENCE_NAME)
19
+ get_platform_spec = pluggy.HookspecMarker(PLUGIN_REFERENCE_NAME)
20
+ get_platform_type_spec = pluggy.HookspecMarker(PLUGIN_REFERENCE_NAME)
21
+ example_configuration_impl = pluggy.HookimplMarker(PLUGIN_REFERENCE_NAME)
22
+ get_platform_impl = pluggy.HookimplMarker(PLUGIN_REFERENCE_NAME)
23
+ get_platform_type_impl = pluggy.HookimplMarker(PLUGIN_REFERENCE_NAME)
24
+ logger = getLogger(__name__)
25
+
26
+
27
+ class PlatformSpecification(PluginSpecification, ABC):
28
+ """
29
+ PlatformSpecification for Platform Plugins.
30
+ """
31
+
32
+ @classmethod
33
+ def get_name(cls, strip_all: bool = True) -> str:
34
+ """
35
+ Get name of plugin. By default we remove the PlatformSpecification portion.
36
+
37
+ Args:
38
+ strip_all: When true, PlatformSpecification is stripped from name. When false only Specification is Stripped
39
+
40
+ Returns:
41
+ Name of plugin
42
+ """
43
+ if strip_all:
44
+ ret = cls.__name__.replace("PlatformSpecification", '')
45
+ else:
46
+ ret = cls.__name__.replace('Specification', '')
47
+ return ret
48
+
49
+ @example_configuration_spec
50
+ def example_configuration(self):
51
+ """
52
+ Example configuration for the platform. This is useful in help or error messages.
53
+
54
+ Returns:
55
+ Example configuration
56
+ """
57
+ raise NotImplementedError("Plugin did not implement example_configuration")
58
+
59
+ @get_platform_spec
60
+ def get(self, configuration: dict) -> 'IPlatform':
61
+ """
62
+ Return a new platform using the passed in configuration.
63
+
64
+ Args:
65
+ configuration: The INI configuration file to use.
66
+
67
+ Returns:
68
+ The new platform.
69
+ """
70
+ raise NotImplementedError("Plugin did not implement get")
71
+
72
+ @get_platform_type_spec
73
+ def get_type(self) -> typing.Type['IPlatform']:
74
+ """
75
+ Get type of the Platform type.
76
+ """
77
+ pass
78
+
79
+ def get_configuration_aliases(self) -> typing.Dict[str, typing.Dict]:
80
+ """
81
+ Get a list of configuration aliases for the platform.
82
+
83
+ A configuration alias should be in the form of "name" -> "Spec, Config Options Dict" where name is the alias the user will use, and the config options is a dictionary of config options to be passed to the item
84
+ Returns:
85
+
86
+ """
87
+ return {}
88
+
89
+
90
+ class PlatformPlugins(SingletonMixin):
91
+ """
92
+ PlatformPlugin registry.
93
+ """
94
+
95
+ def __init__(self, strip_all: bool = True) -> None:
96
+ """
97
+ Initialize the Platform Registry. When strip all is false, the full plugin name will be used for names in map.
98
+
99
+ Args:
100
+ strip_all: Whether to strip common parts of name from plugins in plugin map
101
+ """
102
+ self._plugins = typing.cast(typing.Dict[str, PlatformSpecification],
103
+ load_plugin_map('idmtools_platform', PlatformSpecification, strip_all))
104
+ self._aliases: typing.Dict[str, typing.Tuple[PlatformSpecification, typing.Dict]] = dict()
105
+ for _name, spec in self._plugins.items():
106
+ for alias, details in spec.get_configuration_aliases().items():
107
+ if alias.upper() in self._aliases or alias.upper() in self._plugins:
108
+ logger.debug(f"Conflicting alias found: {alias.upper()} from {spec.get_name()}")
109
+ if logger.isEnabledFor(DEBUG):
110
+ logger.debug(f"Found Platform Configuration Alias: {alias}")
111
+ self._aliases[alias.upper()] = (spec, details)
112
+
113
+ def get_plugins(self) -> typing.Set[PlatformSpecification]:
114
+ """
115
+ Get platform plugins.
116
+
117
+ Returns:
118
+ Platform plugins
119
+ """
120
+ return set(self._plugins.values())
121
+
122
+ def get_aliases(self) -> typing.Dict[str, typing.Tuple[PlatformSpecification, typing.Dict]]:
123
+ """
124
+ Get Platform Configuration Aliases for Platform Plugin.
125
+
126
+ Returns:
127
+ Platform CConfiguration Aliases
128
+ """
129
+ return self._aliases
130
+
131
+ def get_plugin_map(self) -> typing.Dict[str, PlatformSpecification]:
132
+ """
133
+ Get plugin map for Platform Plugins.
134
+
135
+ Returns:
136
+ Plugin map
137
+ """
138
+ return self._plugins
@@ -0,0 +1,129 @@
1
+ """
2
+ Defines our base plugin definition and specifications.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ from dataclasses import dataclass
7
+ from logging import getLogger
8
+ import pluggy
9
+ from typing import List, Union, Dict
10
+
11
+ PLUGIN_REFERENCE_NAME = 'idmtools_plugins'
12
+ get_description_spec = pluggy.HookspecMarker(PLUGIN_REFERENCE_NAME)
13
+ get_description_impl = pluggy.HookimplMarker(PLUGIN_REFERENCE_NAME)
14
+ logger = getLogger(__name__)
15
+
16
+
17
+ @dataclass
18
+ class ProjectTemplate:
19
+ """
20
+ Defines a ProjectTemplate that plugins can define.
21
+ """
22
+ name: str
23
+ url: Union[str, List[str]]
24
+ description: str = None
25
+ info: str = None
26
+
27
+ @staticmethod
28
+ def read_templates_from_json_stream(s) -> List['ProjectTemplate']:
29
+ """
30
+ Read Project Template from stream.
31
+
32
+ Args:
33
+ s: Stream where json data resides
34
+
35
+ Returns:
36
+ Templates loaded from json
37
+ """
38
+ import json
39
+ data = json.loads(s.read().decode())
40
+ ret = list()
41
+ if isinstance(data, list):
42
+ for item in data:
43
+ ret.append(ProjectTemplate(**item))
44
+ else:
45
+ ret.append(ProjectTemplate(**data))
46
+ return ret
47
+
48
+
49
+ class PluginSpecification:
50
+ """
51
+ Base class for all plugins.
52
+ """
53
+
54
+ @classmethod
55
+ def get_name(cls, strip_all: bool = True) -> str:
56
+ """
57
+ Get the name of the plugin. Although it can be overridden, the best practice is to use the class name as the plugin name.
58
+
59
+ Returns:
60
+ The name of the plugin as a string.
61
+ """
62
+ if strip_all:
63
+ return cls.__name__.replace("Specification", "")
64
+ else:
65
+ return cls.__name__
66
+
67
+ @get_description_spec
68
+ def get_description(self) -> str:
69
+ """
70
+ Get a brief description of the plugin and its functionality.
71
+
72
+ Returns:
73
+ The plugin description.
74
+ """
75
+ raise NotImplementedError("The plugin did not implement a description!")
76
+
77
+ def get_project_templates(self) -> List[ProjectTemplate]:
78
+ """
79
+ Returns a list of project templates related to the a plugin.
80
+
81
+ Returns:
82
+ List of project templates
83
+ """
84
+ return list()
85
+
86
+ def get_example_urls(self) -> List[str]:
87
+ """
88
+ Returns a list of URLs that a series of Examples for plugin can be downloaded from.
89
+
90
+ Returns:
91
+ List of urls
92
+ """
93
+ return list()
94
+
95
+ def get_help_urls(self) -> Dict[str, str]:
96
+ """
97
+ Returns a dictionary of topics and links to help.
98
+
99
+ Returns:
100
+ Dict of help urls
101
+ """
102
+ return dict()
103
+
104
+ @staticmethod
105
+ def get_version_url(version: str, extra: str = None,
106
+ repo_base_url: str = 'https://github.com/InstituteforDiseaseModeling/idmtools/tree/',
107
+ nightly_branch: str = 'dev'):
108
+ """
109
+ Build a url using version.
110
+
111
+ Here we assume the tag will exist for that specific version
112
+ Args:
113
+ version: Version to look up. If it contains nightly, we default to nightly_branch
114
+ extra: Extra parts of url pass base
115
+ repo_base_url: Optional url
116
+ nightly_branch: default to dev
117
+ Returns:
118
+ URL for item
119
+ """
120
+ return f'{repo_base_url}{nightly_branch if "nightly" in version else version[0:6]}/{extra}'
121
+
122
+ def get_version(self) -> str:
123
+ """
124
+ Returns the version of the plugin.
125
+
126
+ Returns:
127
+ Version for the plugin
128
+ """
129
+ return None
@@ -0,0 +1,104 @@
1
+ """
2
+ TaskSpecification provided definition for the experiment plugin specification, hooks, and plugin manager.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ # Define our model specific specifications
7
+ import typing
8
+ from abc import ABC
9
+ from logging import getLogger
10
+ import pluggy
11
+ from idmtools.entities.itask import ITask
12
+ from idmtools.registry import PluginSpecification
13
+ from idmtools.registry.plugin_specification import PLUGIN_REFERENCE_NAME
14
+ from idmtools.registry.utils import load_plugin_map
15
+ from idmtools.utils.decorators import SingletonMixin
16
+
17
+ example_configuration_spec = pluggy.HookspecMarker(PLUGIN_REFERENCE_NAME)
18
+ get_task_spec = pluggy.HookspecMarker(PLUGIN_REFERENCE_NAME)
19
+ get_task_type_spec = pluggy.HookspecMarker(PLUGIN_REFERENCE_NAME)
20
+ example_configuration_impl = pluggy.HookimplMarker(PLUGIN_REFERENCE_NAME)
21
+ get_task_impl = pluggy.HookimplMarker(PLUGIN_REFERENCE_NAME)
22
+ get_task_type_impl = pluggy.HookimplMarker(PLUGIN_REFERENCE_NAME)
23
+ logger = getLogger(__name__)
24
+
25
+
26
+ class TaskSpecification(PluginSpecification, ABC):
27
+ """
28
+ TaskSpecification is spec for Task plugins.
29
+ """
30
+
31
+ @classmethod
32
+ def get_name(cls, strip_all: bool = True) -> str:
33
+ """
34
+ Get name of plugin. By default we remove the PlatformSpecification portion.
35
+
36
+ Args:
37
+ strip_all: When true, TaskSpecification and TaskSpec is stripped from name. When false only
38
+ Specification and Spec is Stripped
39
+
40
+ Returns:
41
+ Name of plugin
42
+ """
43
+ if strip_all:
44
+ ret = cls.__name__.replace('TaskSpecification', '').replace("TaskSpec", '').replace('Spec', '')
45
+ else:
46
+ ret = cls.__name__.replace('Specification', '').replace('Spec', '')
47
+ return ret
48
+
49
+ @get_task_spec
50
+ def get(self, configuration: dict) -> 'ITask': # noqa: F821
51
+ """
52
+ Return a new model using the passed in configuration.
53
+
54
+ Args:
55
+ configuration: The INI configuration file to use.
56
+
57
+ Returns:
58
+ The new model.
59
+ """
60
+ raise NotImplementedError("Plugin did not implement get")
61
+
62
+ @get_task_type_spec
63
+ def get_type(self) -> typing.Type['ITask']: # noqa: F821
64
+ """
65
+ Get task type.
66
+
67
+ Returns:
68
+ Task type
69
+ """
70
+ pass
71
+
72
+
73
+ class TaskPlugins(SingletonMixin):
74
+ """
75
+ TaskPlugins acts as a registry for Task Plugins.
76
+ """
77
+
78
+ def __init__(self, strip_all: bool = True) -> None:
79
+ """
80
+ Initialize the Task Registry. When strip all is false, the full plugin name will be used for names in map.
81
+
82
+ Args:
83
+ strip_all: Whether to strip common parts of name from plugins in plugin map
84
+ """
85
+ self._plugins = typing.cast(typing.Dict[str, TaskSpecification],
86
+ load_plugin_map('idmtools_task', TaskSpecification, strip_all))
87
+
88
+ def get_plugins(self) -> typing.Set[TaskSpecification]:
89
+ """
90
+ Get plugins for Tasks.
91
+
92
+ Returns:
93
+ Plugins
94
+ """
95
+ return set(self._plugins.values())
96
+
97
+ def get_plugin_map(self) -> typing.Dict[str, TaskSpecification]:
98
+ """
99
+ Get a map of task plugins.
100
+
101
+ Returns:
102
+ Task plugin map
103
+ """
104
+ return self._plugins
@@ -0,0 +1,119 @@
1
+ """
2
+ Provides utilities for plugins.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ import functools
7
+ import inspect
8
+ import logging
9
+ from logging import DEBUG, getLogger
10
+ from typing import Type, List, Any, Set, Dict
11
+ import pluggy
12
+ from idmtools.registry import PluginSpecification
13
+ from idmtools.registry.plugin_specification import PLUGIN_REFERENCE_NAME
14
+
15
+
16
+ logger = getLogger(__name__)
17
+ user_logger = getLogger('user')
18
+
19
+
20
+ def is_a_plugin_of_type(value, plugin_specification: Type[PluginSpecification]) -> bool:
21
+ """
22
+ Determine if a value of a plugin specification is of type :class:`~idmtools.registry.plugin_specification.PluginSpecification`.
23
+
24
+ Args:
25
+ value: The value to inspect.
26
+ plugin_specification: Plugin specification to check against.
27
+
28
+ Returns:
29
+ A Boolean indicating True if the plugin is of a subclass of :class:`~idmtools.registry.plugin_specification.PluginSpecification`,
30
+ else False.
31
+ """
32
+ return inspect.isclass(value) and issubclass(value, plugin_specification) \
33
+ and not inspect.isabstract(value) and value is not plugin_specification
34
+
35
+
36
+ def load_plugin_map(entrypoint: str, spec_type: Type[PluginSpecification], strip_all: bool = True) -> Dict[str, Type[PluginSpecification]]:
37
+ """
38
+ Load plugins from entry point with the indicated type of specification into a map.
39
+
40
+ .. warning::
41
+
42
+ This could cause name collisions if plugins of the same name are installed.
43
+
44
+ Args:
45
+ entrypoint: The name of the entry point.
46
+ spec_type: The type of plugin specification.
47
+ strip_all: Pass through for get_name from Plugins. Changes names in plugin registries
48
+
49
+ Returns:
50
+ (Dict[str, Type[PluginSpecification]]): Returns a dictionary of name and :class:`~idmtools.registry.plugin_specification.PluginSpecification`.
51
+ """
52
+ plugins = plugins_loader(entrypoint, spec_type)
53
+ # create instances of the plugins
54
+ _plugin_map = dict()
55
+ for plugin in plugins:
56
+ if logger.isEnabledFor(logging.DEBUG):
57
+ logger.debug(f"Loading {str(plugin)} as {plugin.get_name()}")
58
+ try:
59
+ _plugin_map[plugin.get_name(strip_all)] = plugin()
60
+ except Exception as e:
61
+ logger.exception(e)
62
+ user_logger.error(f'Problem loading plugin: {plugin.get_name()}')
63
+ return _plugin_map
64
+
65
+
66
+ def plugins_loader(entry_points_name: str, plugin_specification: Type[PluginSpecification]) -> Set[PluginSpecification]:
67
+ """
68
+ Loads all the plugins of type :class:`~idmtools.registry.plugin_specification.PluginSpecification` from entry point name.
69
+
70
+ |IT_s| also supports loading plugins through a list of strings representing the paths to modules containing plugins.
71
+
72
+ Args:
73
+ entry_points_name: Entry point name for plugins.
74
+ plugin_specification: Plugin specification to load.
75
+
76
+ Returns:
77
+ (Set[PluginSpecification]): All the plugins of the type indicated.
78
+ """
79
+ manager = pluggy.PluginManager(PLUGIN_REFERENCE_NAME)
80
+ manager.add_hookspecs(plugin_specification)
81
+ manager.load_setuptools_entrypoints(entry_points_name)
82
+
83
+ manager.check_pending()
84
+ return manager.get_plugins()
85
+
86
+
87
+ @functools.lru_cache(maxsize=32)
88
+ def discover_plugins_from(library: Any, plugin_specification: Type[PluginSpecification]) -> \
89
+ List[Type[PluginSpecification]]:
90
+ """
91
+ Search a library object for plugins of type :class:`~idmtools.registry.plugin_specification.PluginSpecification`.
92
+
93
+ Currently it detects module and classes. In the future support for strings will be added.
94
+
95
+ Args:
96
+ library: Library object to discover plugins from.
97
+ plugin_specification: Specification to search for.
98
+
99
+ Returns:
100
+ List[Type[PluginSpecification]]: List of plugins.
101
+ """
102
+ plugins = []
103
+ # check if the item is a module
104
+ if inspect.ismodule(library):
105
+ if logger.isEnabledFor(DEBUG):
106
+ logger.debug('Attempting to load library as a module: %s', library.__name__)
107
+ for k, v in library.__dict__.items():
108
+ if k[:2] != '__' and is_a_plugin_of_type(v, plugin_specification):
109
+ if logger.isEnabledFor(DEBUG):
110
+ logger.debug('Adding class %s from %s as a plugin', v.__name__, library.__name__)
111
+ plugins.append(v)
112
+ # or maybe a plugin object
113
+ elif is_a_plugin_of_type(library, plugin_specification):
114
+ if logger.isEnabledFor(DEBUG):
115
+ logger.debug('Adding class %s as a plugin', library.__name__)
116
+ plugins.append(library)
117
+ else:
118
+ logger.warn('Could not determine the the type of library specified by %s', str(library))
119
+ return plugins
@@ -0,0 +1,5 @@
1
+ """
2
+ Internal services for idmtools.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
@@ -0,0 +1,135 @@
1
+ """
2
+ IPersistenceService allows caching of items locally into a diskcache db that does not expire upon deletion.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ import os
7
+ import logging
8
+ import time
9
+ from pathlib import Path
10
+ from multiprocessing import cpu_count
11
+
12
+ import diskcache
13
+ from abc import ABCMeta
14
+
15
+ from idmtools.core import IDMTOOLS_USER_HOME
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class IPersistenceService(metaclass=ABCMeta):
21
+ """
22
+ IPersistenceService provides a persistent cache. This is useful for network heavy operations.
23
+ """
24
+ cache_directory = None
25
+ cache_name = None
26
+
27
+ @classmethod
28
+ def _open_cache(cls):
29
+ """
30
+ Open cache.
31
+
32
+ Returns:
33
+ None
34
+ """
35
+ import sqlite3
36
+ from idmtools import IdmConfigParser
37
+ cls.cache_directory = Path(
38
+ IdmConfigParser.get_option(option="cache_directory", fallback=IDMTOOLS_USER_HOME.joinpath("cache")))
39
+
40
+ # the more the cpus, the more likely we are to encounter a scaling issue. Let's try to scale with that up to
41
+ # one second. above one second, we are introducing to much lag in processes
42
+ default_timeout = min(max(0.25, cpu_count() * 0.025 * 2), 2)
43
+ retries = 0
44
+ while retries < 5:
45
+
46
+ try:
47
+ os.makedirs(cls.cache_directory, exist_ok=True)
48
+ cache = diskcache.FanoutCache(os.path.join(str(cls.cache_directory), 'disk_cache', cls.cache_name),
49
+ timeout=default_timeout, shards=cpu_count() * 2)
50
+ return cache
51
+ except (sqlite3.OperationalError, FileNotFoundError):
52
+ retries += 1
53
+ time.sleep(0.1)
54
+
55
+ @classmethod
56
+ def retrieve(cls, uid):
57
+ """
58
+ Retrieve item with id <uid> from cache.
59
+
60
+ Args:
61
+ uid: Id to fetch
62
+
63
+ Returns:
64
+ Item loaded from cache
65
+ """
66
+ with cls._open_cache() as cache:
67
+ obj = cache.get(uid, retry=True)
68
+ return obj
69
+
70
+ @classmethod
71
+ def save(cls, obj):
72
+ """
73
+ Save an item to our cache.
74
+
75
+ Args:
76
+ obj: Object to save.
77
+
78
+ Returns:
79
+ Object uid
80
+ """
81
+ with cls._open_cache() as cache:
82
+ if logger.isEnabledFor(logging.DEBUG):
83
+ logging.debug('Saving %s to %s', obj.uid, cls.cache_name)
84
+ cache.set(obj.uid, obj, retry=True)
85
+
86
+ return obj.uid
87
+
88
+ @classmethod
89
+ def delete(cls, uid):
90
+ """
91
+ Delete at item from our cache with id <uid>.
92
+
93
+ Args:
94
+ uid: Id to delete
95
+
96
+ Returns:
97
+ None
98
+ """
99
+ with cls._open_cache() as cache:
100
+ cache.delete(uid, retry=True)
101
+
102
+ @classmethod
103
+ def clear(cls):
104
+ """
105
+ Clear our cache.
106
+
107
+ Returns:
108
+ None
109
+ """
110
+ with cls._open_cache() as cache:
111
+ cache.clear(retry=True)
112
+
113
+ @classmethod
114
+ def list(cls):
115
+ """
116
+ List items in our cache.
117
+
118
+ Returns:
119
+ List of items in our cache
120
+ """
121
+ with cls._open_cache() as cache:
122
+ _list = list(cache)
123
+ return _list
124
+
125
+ @classmethod
126
+ def length(cls):
127
+ """
128
+ Total length of our persistence cache.
129
+
130
+ Returns:
131
+ Count of our cache
132
+ """
133
+ with cls._open_cache() as cache:
134
+ _len = len(cache)
135
+ return _len
@@ -0,0 +1,13 @@
1
+ """
2
+ PlatformPersistService provides cache for platforms.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ from idmtools.services.ipersistance_service import IPersistenceService
7
+
8
+
9
+ class PlatformPersistService(IPersistenceService):
10
+ """
11
+ Provide a cache for our platforms.
12
+ """
13
+ cache_name = "platforms"
@@ -0,0 +1,5 @@
1
+ """
2
+ root of utilities for idmtools.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
@@ -0,0 +1,24 @@
1
+ """
2
+ Defines our IAnalyzer interface used as base of all other analyzers.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+
7
+
8
+ def get_caller():
9
+ """
10
+ Trace the stack and find the caller.
11
+
12
+ Returns:
13
+ The direct caller.
14
+ """
15
+ import inspect
16
+
17
+ try:
18
+ s = inspect.stack()
19
+ except (IndexError, RuntimeError):
20
+ # in some high thread environments and under heavy load, we can get environment changes before retrieving
21
+ # stack in those case assume we are good
22
+ # We can also encounter IndexError in dynamic environments like Snakemake, jinja, etc
23
+ return "__newobj__"
24
+ return s[2][3]