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.
- idmtools/__init__.py +27 -8
- idmtools/analysis/__init__.py +5 -0
- idmtools/analysis/add_analyzer.py +89 -0
- idmtools/analysis/analyze_manager.py +490 -0
- idmtools/analysis/csv_analyzer.py +103 -0
- idmtools/analysis/download_analyzer.py +96 -0
- idmtools/analysis/map_worker_entry.py +100 -0
- idmtools/analysis/platform_analysis_bootstrap.py +94 -0
- idmtools/analysis/platform_anaylsis.py +291 -0
- idmtools/analysis/tags_analyzer.py +93 -0
- idmtools/assets/__init__.py +9 -0
- idmtools/assets/asset.py +453 -0
- idmtools/assets/asset_collection.py +514 -0
- idmtools/assets/content_handlers.py +19 -0
- idmtools/assets/errors.py +23 -0
- idmtools/assets/file_list.py +191 -0
- idmtools/builders/__init__.py +11 -0
- idmtools/builders/arm_simulation_builder.py +152 -0
- idmtools/builders/csv_simulation_builder.py +76 -0
- idmtools/builders/simulation_builder.py +348 -0
- idmtools/builders/sweep_arm.py +109 -0
- idmtools/builders/yaml_simulation_builder.py +82 -0
- idmtools/config/__init__.py +7 -0
- idmtools/config/idm_config_parser.py +486 -0
- idmtools/core/__init__.py +10 -0
- idmtools/core/cache_enabled.py +114 -0
- idmtools/core/context.py +68 -0
- idmtools/core/docker_task.py +207 -0
- idmtools/core/enums.py +51 -0
- idmtools/core/exceptions.py +91 -0
- idmtools/core/experiment_factory.py +71 -0
- idmtools/core/id_file.py +70 -0
- idmtools/core/interfaces/__init__.py +5 -0
- idmtools/core/interfaces/entity_container.py +64 -0
- idmtools/core/interfaces/iassets_enabled.py +58 -0
- idmtools/core/interfaces/ientity.py +331 -0
- idmtools/core/interfaces/iitem.py +206 -0
- idmtools/core/interfaces/imetadata_operations.py +89 -0
- idmtools/core/interfaces/inamed_entity.py +17 -0
- idmtools/core/interfaces/irunnable_entity.py +159 -0
- idmtools/core/logging.py +387 -0
- idmtools/core/platform_factory.py +316 -0
- idmtools/core/system_information.py +104 -0
- idmtools/core/task_factory.py +145 -0
- idmtools/entities/__init__.py +10 -0
- idmtools/entities/command_line.py +229 -0
- idmtools/entities/command_task.py +155 -0
- idmtools/entities/experiment.py +787 -0
- idmtools/entities/generic_workitem.py +43 -0
- idmtools/entities/ianalyzer.py +163 -0
- idmtools/entities/iplatform.py +1106 -0
- idmtools/entities/iplatform_default.py +39 -0
- idmtools/entities/iplatform_ops/__init__.py +5 -0
- idmtools/entities/iplatform_ops/iplatform_asset_collection_operations.py +148 -0
- idmtools/entities/iplatform_ops/iplatform_experiment_operations.py +415 -0
- idmtools/entities/iplatform_ops/iplatform_simulation_operations.py +315 -0
- idmtools/entities/iplatform_ops/iplatform_suite_operations.py +322 -0
- idmtools/entities/iplatform_ops/iplatform_workflowitem_operations.py +301 -0
- idmtools/entities/iplatform_ops/utils.py +185 -0
- idmtools/entities/itask.py +316 -0
- idmtools/entities/iworkflow_item.py +167 -0
- idmtools/entities/platform_requirements.py +20 -0
- idmtools/entities/relation_type.py +14 -0
- idmtools/entities/simulation.py +255 -0
- idmtools/entities/suite.py +188 -0
- idmtools/entities/task_proxy.py +37 -0
- idmtools/entities/templated_simulation.py +325 -0
- idmtools/frozen/frozen_dict.py +71 -0
- idmtools/frozen/frozen_list.py +66 -0
- idmtools/frozen/frozen_set.py +86 -0
- idmtools/frozen/frozen_tuple.py +18 -0
- idmtools/frozen/frozen_utils.py +179 -0
- idmtools/frozen/ifrozen.py +66 -0
- idmtools/plugins/__init__.py +5 -0
- idmtools/plugins/git_commit.py +117 -0
- idmtools/registry/__init__.py +4 -0
- idmtools/registry/experiment_specification.py +105 -0
- idmtools/registry/functions.py +28 -0
- idmtools/registry/hook_specs.py +132 -0
- idmtools/registry/master_plugin_registry.py +51 -0
- idmtools/registry/platform_specification.py +138 -0
- idmtools/registry/plugin_specification.py +129 -0
- idmtools/registry/task_specification.py +104 -0
- idmtools/registry/utils.py +119 -0
- idmtools/services/__init__.py +5 -0
- idmtools/services/ipersistance_service.py +135 -0
- idmtools/services/platforms.py +13 -0
- idmtools/utils/__init__.py +5 -0
- idmtools/utils/caller.py +24 -0
- idmtools/utils/collections.py +246 -0
- idmtools/utils/command_line.py +45 -0
- idmtools/utils/decorators.py +300 -0
- idmtools/utils/display/__init__.py +22 -0
- idmtools/utils/display/displays.py +181 -0
- idmtools/utils/display/settings.py +25 -0
- idmtools/utils/dropbox_location.py +30 -0
- idmtools/utils/entities.py +127 -0
- idmtools/utils/file.py +72 -0
- idmtools/utils/file_parser.py +151 -0
- idmtools/utils/filter_simulations.py +182 -0
- idmtools/utils/filters/__init__.py +5 -0
- idmtools/utils/filters/asset_filters.py +88 -0
- idmtools/utils/general.py +286 -0
- idmtools/utils/gitrepo.py +336 -0
- idmtools/utils/hashing.py +239 -0
- idmtools/utils/info.py +124 -0
- idmtools/utils/json.py +82 -0
- idmtools/utils/language.py +107 -0
- idmtools/utils/local_os.py +40 -0
- idmtools/utils/time.py +22 -0
- idmtools-0.0.2.dist-info/METADATA +120 -0
- idmtools-0.0.2.dist-info/RECORD +116 -0
- idmtools-0.0.2.dist-info/entry_points.txt +9 -0
- idmtools-0.0.2.dist-info/licenses/LICENSE.TXT +3 -0
- idmtools-0.0.0.dev0.dist-info/METADATA +0 -41
- idmtools-0.0.0.dev0.dist-info/RECORD +0 -5
- {idmtools-0.0.0.dev0.dist-info → idmtools-0.0.2.dist-info}/WHEEL +0 -0
- {idmtools-0.0.0.dev0.dist-info → idmtools-0.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DockerTask provides a utility to run docker images.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
import sys
|
|
9
|
+
import unicodedata
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from datetime import datetime, timezone, timedelta
|
|
12
|
+
from typing import Optional, Type
|
|
13
|
+
from idmtools import __version__ as idmtools_version, IdmConfigParser
|
|
14
|
+
from idmtools.assets import AssetCollection, json
|
|
15
|
+
from idmtools.core.logging import getLogger
|
|
16
|
+
from idmtools.entities.itask import ITask
|
|
17
|
+
from idmtools.entities.platform_requirements import PlatformRequirements
|
|
18
|
+
from idmtools.registry.task_specification import TaskSpecification
|
|
19
|
+
|
|
20
|
+
logger = getLogger(__name__)
|
|
21
|
+
user_logger = getLogger('user')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class DockerTask(ITask):
|
|
26
|
+
"""
|
|
27
|
+
Provides a task to run or optionally build a docker container.
|
|
28
|
+
"""
|
|
29
|
+
image_name: str = field(default=None, metadata={"md": True})
|
|
30
|
+
# Optional config to build the docker image
|
|
31
|
+
build: bool = field(default=False, metadata={"md": True})
|
|
32
|
+
build_path: Optional[str] = field(default=None, metadata={"md": True})
|
|
33
|
+
# This should in the build_path directory
|
|
34
|
+
Dockerfile: Optional[str] = field(default=None, metadata={"md": True})
|
|
35
|
+
pull_before_build: bool = field(default=True, metadata={"md": True})
|
|
36
|
+
use_nvidia_run: bool = field(default=False, metadata={"md": True})
|
|
37
|
+
|
|
38
|
+
__image_built: bool = field(default=False)
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
"""
|
|
42
|
+
Set our platform requirements and optionally trigger image build.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
None
|
|
46
|
+
"""
|
|
47
|
+
super().__post_init__()
|
|
48
|
+
self.add_platform_requirement(PlatformRequirements.DOCKER)
|
|
49
|
+
if self.build:
|
|
50
|
+
self.build_image()
|
|
51
|
+
|
|
52
|
+
def gather_common_assets(self) -> AssetCollection:
|
|
53
|
+
"""
|
|
54
|
+
Gather common(experiment-level) assets from task.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
AssetCollection containing all the common assets
|
|
58
|
+
"""
|
|
59
|
+
if self.image_name is None:
|
|
60
|
+
raise ValueError("Image Name is required")
|
|
61
|
+
return self.common_assets
|
|
62
|
+
|
|
63
|
+
def gather_transient_assets(self) -> AssetCollection:
|
|
64
|
+
"""
|
|
65
|
+
Gather transient(simulation-level) assets from task.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
AssetCollection
|
|
69
|
+
"""
|
|
70
|
+
return self.transient_assets
|
|
71
|
+
|
|
72
|
+
def build_image(self, spinner=None, **extra_build_args):
|
|
73
|
+
"""
|
|
74
|
+
Build our docker image.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
spinner: Should we display a CLI spinner
|
|
78
|
+
**extra_build_args: Extra build arguments to pass to docker
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
None
|
|
82
|
+
"""
|
|
83
|
+
if not self.__image_built:
|
|
84
|
+
import docker
|
|
85
|
+
from docker.errors import BuildError
|
|
86
|
+
if spinner:
|
|
87
|
+
spinner.text = f"Building {self.image_name}"
|
|
88
|
+
# if the build_path is none use current working directory
|
|
89
|
+
if self.build_path is None:
|
|
90
|
+
self.build_path = os.getcwd()
|
|
91
|
+
|
|
92
|
+
client = docker.client.from_env()
|
|
93
|
+
build_config = dict(
|
|
94
|
+
path=self.build_path,
|
|
95
|
+
dockerfile=self.Dockerfile,
|
|
96
|
+
tag=self.image_name,
|
|
97
|
+
labels=dict(
|
|
98
|
+
uildstamp=f'built-by idmtools {idmtools_version}',
|
|
99
|
+
builddate=str(datetime.now(timezone(timedelta(hours=-8)))))
|
|
100
|
+
)
|
|
101
|
+
if extra_build_args:
|
|
102
|
+
build_config.update(extra_build_args)
|
|
103
|
+
logger.debug(f"Build configuration used: {str(build_config)}")
|
|
104
|
+
self.__image_built = True
|
|
105
|
+
if not IdmConfigParser.is_progress_bar_disabled():
|
|
106
|
+
from tqdm import tqdm
|
|
107
|
+
prog = tqdm(
|
|
108
|
+
desc='Building docker image',
|
|
109
|
+
total=10,
|
|
110
|
+
bar_format='Building Docker Image: |{bar}| {percentage:3.0f}% [{n_fmt}/{total_fmt}] [{elapsed}] {desc}'
|
|
111
|
+
)
|
|
112
|
+
try:
|
|
113
|
+
build_step = None
|
|
114
|
+
# regular expression to grab progress
|
|
115
|
+
progress_grep = re.compile(r'Step ([0-9]+)/([0-9]+) : (.*)')
|
|
116
|
+
# regular expression to filter out ansi codes
|
|
117
|
+
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
|
118
|
+
for line in client.api.build(**build_config):
|
|
119
|
+
line = json.loads(line)
|
|
120
|
+
if 'stream' in line:
|
|
121
|
+
line = line['stream']
|
|
122
|
+
line = ansi_escape.sub('', line).strip()
|
|
123
|
+
# strip unicode data
|
|
124
|
+
line = "".join(ch for ch in line if unicodedata.category(ch)[0] != "C")
|
|
125
|
+
logger.debug('Raw Docker Output: %s', line)
|
|
126
|
+
if line:
|
|
127
|
+
grps = progress_grep.match(line)
|
|
128
|
+
if grps:
|
|
129
|
+
try:
|
|
130
|
+
step = int(grps.group(1))
|
|
131
|
+
total_steps = int(grps.group(2))
|
|
132
|
+
if prog:
|
|
133
|
+
prog.n = step
|
|
134
|
+
prog.total = total_steps
|
|
135
|
+
line = grps.group(3)
|
|
136
|
+
except: # noqa E722
|
|
137
|
+
pass
|
|
138
|
+
if prog:
|
|
139
|
+
prog.set_description(line)
|
|
140
|
+
build_step = line
|
|
141
|
+
# update build step with output
|
|
142
|
+
elif build_step:
|
|
143
|
+
if len(line) > 40:
|
|
144
|
+
line = line[:40]
|
|
145
|
+
if prog:
|
|
146
|
+
prog.set_description(f'{build_step}: {line}')
|
|
147
|
+
elif 'status' in line:
|
|
148
|
+
line = line['status'].strip()
|
|
149
|
+
if prog:
|
|
150
|
+
prog.set_description(line)
|
|
151
|
+
|
|
152
|
+
logger.info('Build Successful)')
|
|
153
|
+
except BuildError as e:
|
|
154
|
+
logger.info(f"Build failed for {self.image_name} with message {e.msg}")
|
|
155
|
+
logger.info(f'Build log: {e.build_log}')
|
|
156
|
+
sys.exit(-1)
|
|
157
|
+
finally:
|
|
158
|
+
if prog:
|
|
159
|
+
prog.close()
|
|
160
|
+
|
|
161
|
+
def reload_from_simulation(self, simulation: 'Simulation'): # noqa E821
|
|
162
|
+
"""
|
|
163
|
+
Method to reload task details from simulation object. Currently we do not do this for docker task.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
simulation: Simulation to load data from
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
None
|
|
170
|
+
"""
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class DockerTaskSpecification(TaskSpecification):
|
|
175
|
+
"""
|
|
176
|
+
DockerTaskSpecification provides the task plugin to idmtools for DockerTask.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def get(self, configuration: dict) -> DockerTask:
|
|
180
|
+
"""
|
|
181
|
+
Get instance of DockerTask with configuration provided.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
configuration: configuration for DockerTask
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
DockerTask with configuration
|
|
188
|
+
"""
|
|
189
|
+
return DockerTask(**configuration)
|
|
190
|
+
|
|
191
|
+
def get_description(self) -> str:
|
|
192
|
+
"""
|
|
193
|
+
Get description of plugin.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Plugin description
|
|
197
|
+
"""
|
|
198
|
+
return "Defines a docker command"
|
|
199
|
+
|
|
200
|
+
def get_type(self) -> Type[DockerTask]:
|
|
201
|
+
"""
|
|
202
|
+
Get type of task provided by plugin.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
DockerTask
|
|
206
|
+
"""
|
|
207
|
+
return DockerTask
|
idmtools/core/enums.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Define our common enums to be used through idmtools.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from enum import Enum
|
|
9
|
+
|
|
10
|
+
TRUTHY_VALUES = ['1', 'y', 'yes', 'on', 'true', 't', 1, True]
|
|
11
|
+
# Used to store idmtools user specific config/data
|
|
12
|
+
IDMTOOLS_USER_HOME = Path().home().joinpath(".idmtools")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EntityStatus(Enum):
|
|
16
|
+
"""
|
|
17
|
+
EntityStatus provides status values for Experiment/Simulations/WorkItems.
|
|
18
|
+
"""
|
|
19
|
+
COMMISSIONING = 'commissioning'
|
|
20
|
+
CREATED = 'created'
|
|
21
|
+
RUNNING = 'running'
|
|
22
|
+
SUCCEEDED = 'succeeded'
|
|
23
|
+
FAILED = 'failed'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FilterMode(Enum):
|
|
27
|
+
"""
|
|
28
|
+
Allows user to specify AND/OR for the filtering system.
|
|
29
|
+
"""
|
|
30
|
+
AND = 0
|
|
31
|
+
OR = 1
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ItemType(Enum):
|
|
35
|
+
"""
|
|
36
|
+
ItemTypes supported by idmtools.
|
|
37
|
+
"""
|
|
38
|
+
SUITE = "Suite"
|
|
39
|
+
EXPERIMENT = "Experiment"
|
|
40
|
+
SIMULATION = "Simulation"
|
|
41
|
+
WORKFLOW_ITEM = "WorkItem" # On Comps this is workitems
|
|
42
|
+
ASSETCOLLECTION = "Asset Collection"
|
|
43
|
+
|
|
44
|
+
def __str__(self):
|
|
45
|
+
"""
|
|
46
|
+
Returns a string representation of our item type.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
The string version of our enum value
|
|
50
|
+
"""
|
|
51
|
+
return str(self.value)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Define idmtools common exception as well as idmtools system exception handler.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import typing
|
|
8
|
+
from logging import getLogger
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from idmtools.entities.iplatform import TPlatform
|
|
12
|
+
|
|
13
|
+
user_logger = getLogger('user')
|
|
14
|
+
logger = getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExperimentNotFound(Exception):
|
|
18
|
+
"""
|
|
19
|
+
Thrown when an experiment cannot be found on a platform.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, experiment_id: str, platform: 'TPlatform' = None):
|
|
23
|
+
"""
|
|
24
|
+
Initialize our ExperimentNotFound.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
experiment_id: Experiment id to say wasn't found
|
|
28
|
+
platform: Optional platform. Used in error message
|
|
29
|
+
"""
|
|
30
|
+
if platform:
|
|
31
|
+
super().__init__(f"Experiment with id '{experiment_id}' could not be retrieved on platform {platform}.")
|
|
32
|
+
else:
|
|
33
|
+
super().__init__(f"Experiment with id '{experiment_id}' could not be retrieved.")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class UnknownItemException(Exception):
|
|
37
|
+
"""
|
|
38
|
+
Thrown when an unknown item type is passed to idmtools.
|
|
39
|
+
|
|
40
|
+
This usually occurs within the platform operation area.
|
|
41
|
+
"""
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class NoPlatformException(Exception):
|
|
46
|
+
"""
|
|
47
|
+
Cannot find a platform matching the one requested by user.
|
|
48
|
+
"""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TopLevelItem(Exception):
|
|
53
|
+
"""
|
|
54
|
+
Thrown when a parent of a top-level item is requested by the platform.
|
|
55
|
+
"""
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class UnsupportedPlatformType(Exception):
|
|
60
|
+
"""
|
|
61
|
+
Occurs when an item is not supported by a platform but is requested.
|
|
62
|
+
"""
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class NoTaskFound(Exception):
|
|
67
|
+
"""
|
|
68
|
+
Thrown when a simulation has no task defined.
|
|
69
|
+
"""
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def idmtools_error_handler(exctype, value: Exception, tb):
|
|
74
|
+
"""
|
|
75
|
+
Global exception handler. This will write our errors in a nice format as well as find document links if attached to the exception.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
exctype: Type of exception
|
|
79
|
+
value: Value of the exception
|
|
80
|
+
tb: Traceback
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
None
|
|
84
|
+
"""
|
|
85
|
+
if hasattr(value, 'doc_link'):
|
|
86
|
+
from idmtools.utils.info import get_help_version_url
|
|
87
|
+
user_logger.error(f"{value.args[0]}. For more details, see {get_help_version_url(value.doc_link)}")
|
|
88
|
+
|
|
89
|
+
logger.exception(value)
|
|
90
|
+
# Call native exception manager
|
|
91
|
+
sys.__excepthook__(exctype, value, tb)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Define ExperimentFactory.
|
|
3
|
+
|
|
4
|
+
This is used mostly internally. It does allow us to support specialized experiment types when needed.
|
|
5
|
+
|
|
6
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
7
|
+
"""
|
|
8
|
+
from logging import getLogger, DEBUG
|
|
9
|
+
from idmtools.entities.experiment import Experiment
|
|
10
|
+
from idmtools.registry import experiment_specification
|
|
11
|
+
|
|
12
|
+
logger = getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ExperimentFactory:
|
|
16
|
+
"""
|
|
17
|
+
ExperimentFactory allows creating experiments that could be derived through plugins.
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
DEFAULT_KEY = 'idmtools.entities.experiment.Experiment'
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
"""
|
|
24
|
+
Initialize our factory.
|
|
25
|
+
|
|
26
|
+
On initialize, we load our plugin and build a map of ids for experiments.
|
|
27
|
+
"""
|
|
28
|
+
from idmtools.registry.experiment_specification import ExperimentPlugins
|
|
29
|
+
self._builders = ExperimentPlugins().get_plugin_map()
|
|
30
|
+
aliases = dict()
|
|
31
|
+
# register types as full paths as well
|
|
32
|
+
for _model, spec in self._builders.items():
|
|
33
|
+
aliases[f'{spec.get_type().__module__}.{spec.get_type().__name__}'] = spec
|
|
34
|
+
self._builders.update(aliases)
|
|
35
|
+
|
|
36
|
+
def create(self, key, fallback=None, **kwargs) -> Experiment: # noqa: F821
|
|
37
|
+
"""
|
|
38
|
+
Create an experiment of type key.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
key: Experiment Type
|
|
42
|
+
fallback: Fallback type. If none, uses DEFAULT_KEY
|
|
43
|
+
**kwargs: Options to pass to the experiment object
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Experiment object that was created
|
|
47
|
+
"""
|
|
48
|
+
if logger.isEnabledFor(DEBUG):
|
|
49
|
+
logger.debug(f"Attempting to create experiment of type {key}")
|
|
50
|
+
|
|
51
|
+
if not key:
|
|
52
|
+
key = self.DEFAULT_KEY
|
|
53
|
+
else:
|
|
54
|
+
logger.warning(f'Experiment type: {key}')
|
|
55
|
+
|
|
56
|
+
if key not in self._builders:
|
|
57
|
+
if not fallback:
|
|
58
|
+
raise ValueError(f"The ExperimentFactory could not create an experiment of type {key}")
|
|
59
|
+
else:
|
|
60
|
+
logger.warning(f'Could not find experiment type {key}. Using Fallback type of {fallback.__class__}')
|
|
61
|
+
return fallback()
|
|
62
|
+
|
|
63
|
+
model_spec: experiment_specification = self._builders.get(key)
|
|
64
|
+
result = model_spec.get(kwargs)
|
|
65
|
+
|
|
66
|
+
if logger.isEnabledFor(DEBUG):
|
|
67
|
+
logger.debug(f"Experiment created for type {key}")
|
|
68
|
+
return result
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
experiment_factory = ExperimentFactory()
|
idmtools/core/id_file.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility method for writing and reading id files.
|
|
3
|
+
|
|
4
|
+
ID Files allow us to reload entities like Experiment, Simulations, AssetCollections, etc from a platform through files. This can
|
|
5
|
+
be enabling for workflows to chain steps together, or to self-document remote outputs in the local project directory.
|
|
6
|
+
|
|
7
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
8
|
+
"""
|
|
9
|
+
import json
|
|
10
|
+
from os import PathLike
|
|
11
|
+
from typing import Union, Dict, TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from idmtools.core.interfaces.ientity import IEntity
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def read_id_file(filename: Union[str, PathLike]):
|
|
18
|
+
"""
|
|
19
|
+
Reads an id from an id file.
|
|
20
|
+
|
|
21
|
+
An id file is in the format of
|
|
22
|
+
|
|
23
|
+
<id>::<item_type>::<config block>::<extra args>
|
|
24
|
+
Args:
|
|
25
|
+
filename:
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
None
|
|
29
|
+
"""
|
|
30
|
+
if isinstance(filename, PathLike):
|
|
31
|
+
filename = str(filename)
|
|
32
|
+
platform_block = None
|
|
33
|
+
item_id = None
|
|
34
|
+
item_type = None
|
|
35
|
+
extra_args = None
|
|
36
|
+
with open(filename, 'r') as id_in:
|
|
37
|
+
item_id = id_in.read().strip()
|
|
38
|
+
if "::" in item_id:
|
|
39
|
+
parts = item_id.split("::")
|
|
40
|
+
if len(parts) == 2:
|
|
41
|
+
item_id, item_type = item_id.split("::")
|
|
42
|
+
elif len(parts) == 3:
|
|
43
|
+
item_id, item_type, platform_block = item_id.split("::")
|
|
44
|
+
elif len(parts) == 4:
|
|
45
|
+
item_id, item_type, platform_block, extra_args = item_id.split("::")
|
|
46
|
+
return item_id, item_type, platform_block, extra_args
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def write_id_file(filename: Union[str, PathLike], item: 'IEntity', save_platform: bool = False, platform_args: Dict = None):
|
|
50
|
+
"""
|
|
51
|
+
Write an item as and id file.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
filename: Filename to write file to
|
|
55
|
+
item: Item to write out
|
|
56
|
+
save_platform: When true, writes platform details to the file
|
|
57
|
+
platform_args: Platform arguments to write out
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
None
|
|
61
|
+
"""
|
|
62
|
+
from idmtools.utils.json import IDMJSONEncoder
|
|
63
|
+
if isinstance(filename, PathLike):
|
|
64
|
+
filename = str(filename)
|
|
65
|
+
with open(filename, 'w') as filename:
|
|
66
|
+
filename.write(f'{item.id}::{item.item_type}')
|
|
67
|
+
if save_platform and hasattr(item.platform, '_config_block'):
|
|
68
|
+
filename.write(f"::{item.platform._config_block}")
|
|
69
|
+
if platform_args:
|
|
70
|
+
filename.write(json.dumps(platform_args, cls=IDMJSONEncoder))
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EntityContainer definition. EntityContainer provides an envelope for a Parent to container a list of sub-items.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
import typing
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
if typing.TYPE_CHECKING:
|
|
10
|
+
from idmtools.core.interfaces.ientity import IEntity
|
|
11
|
+
from idmtools.core.enums import EntityStatus
|
|
12
|
+
from typing import List
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EntityContainer(list):
|
|
16
|
+
"""
|
|
17
|
+
EntityContainer is a wrapper classes used by Experiments and Suites to wrap their children.
|
|
18
|
+
|
|
19
|
+
It provides utilities to set status on entities
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, children: 'List[IEntity]' = None):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the EntityContainer.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
children: Children to initialize with
|
|
28
|
+
"""
|
|
29
|
+
super().__init__()
|
|
30
|
+
self.extend(children or [])
|
|
31
|
+
|
|
32
|
+
def set_status(self, status: 'EntityStatus'):
|
|
33
|
+
"""
|
|
34
|
+
Set status on all the children.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
status: Status to set
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
None
|
|
41
|
+
"""
|
|
42
|
+
for entity in self:
|
|
43
|
+
entity.status = status
|
|
44
|
+
|
|
45
|
+
def set_status_for_item(self, item_id, status: 'EntityStatus'):
|
|
46
|
+
"""
|
|
47
|
+
Set status for specific sub-item.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
item_id: Item id to set status for
|
|
51
|
+
status: Status to set
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
None
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
ValueError when the item_id is not in the children list
|
|
58
|
+
"""
|
|
59
|
+
for entity in self:
|
|
60
|
+
if str(entity.uid) == str(item_id):
|
|
61
|
+
entity.status = status
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
raise ValueError(f"Item with id {item_id} not found in the container")
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
IAssetsEnabled interface definition.
|
|
3
|
+
|
|
4
|
+
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
from abc import ABCMeta, abstractmethod
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from typing import NoReturn, Union
|
|
9
|
+
from idmtools.assets import TAsset, TAssetList
|
|
10
|
+
from idmtools.assets.asset_collection import AssetCollection
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class IAssetsEnabled(metaclass=ABCMeta):
|
|
15
|
+
"""
|
|
16
|
+
Base class for objects containing an asset collection.
|
|
17
|
+
"""
|
|
18
|
+
assets: AssetCollection = field(default_factory=lambda: AssetCollection(), compare=False,
|
|
19
|
+
metadata={"pickle_ignore": True})
|
|
20
|
+
|
|
21
|
+
def __post_init__(self):
|
|
22
|
+
"""
|
|
23
|
+
Post init. Nothing needed for IAssetsEnabled.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
None
|
|
27
|
+
"""
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def gather_assets(self) -> NoReturn:
|
|
32
|
+
"""
|
|
33
|
+
Function called at runtime to gather all assets in the collection.
|
|
34
|
+
"""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def add_assets(self, assets: Union[TAssetList, AssetCollection] = None, fail_on_duplicate: bool = True) -> NoReturn:
|
|
38
|
+
"""
|
|
39
|
+
Add more assets to :class:`~idmtools.assets.asset_collection.AssetCollection`.
|
|
40
|
+
"""
|
|
41
|
+
for asset in assets:
|
|
42
|
+
self.assets.add_asset(asset, fail_on_duplicate)
|
|
43
|
+
|
|
44
|
+
def add_asset(self, asset: Union[str, 'TAsset'] = None, fail_on_duplicate: bool = True) -> NoReturn:
|
|
45
|
+
"""
|
|
46
|
+
Add an asset to our item.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
asset: Asset to add. Asset can be a string in which case it is assumed to be a file path
|
|
50
|
+
fail_on_duplicate: Should we rain an exception if there is an existing file with same information
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
None
|
|
54
|
+
|
|
55
|
+
Raise:
|
|
56
|
+
DuplicatedAssetError in cases where fail_on_duplicate are true
|
|
57
|
+
"""
|
|
58
|
+
self.assets.add_asset(asset, fail_on_duplicate)
|