idmtools-test 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_test/__init__.py +16 -8
- idmtools_test/inputs/__init__.py +0 -0
- idmtools_test/inputs/assets/collections/1/a.txt +0 -0
- idmtools_test/inputs/assets/collections/1/b.txt +0 -0
- idmtools_test/inputs/assets/collections/2/c.txt +0 -0
- idmtools_test/inputs/assets/collections/d.txt +0 -0
- idmtools_test/inputs/builder/sweeps.csv +6 -0
- idmtools_test/inputs/builder/sweeps.yaml +8 -0
- idmtools_test/inputs/compsplatform/__init__.py +0 -0
- idmtools_test/inputs/compsplatform/failing_model.py +5 -0
- idmtools_test/inputs/compsplatform/mixed_model.py +10 -0
- idmtools_test/inputs/compsplatform/working_model.py +5 -0
- idmtools_test/inputs/configuration/idmtools_test.ini +71 -0
- idmtools_test/inputs/custom/Eradication.exe +0 -0
- idmtools_test/inputs/custom/Local_Migration.bin +0 -0
- idmtools_test/inputs/custom/Local_Migration.bin.json +12 -0
- idmtools_test/inputs/custom/Regional_Migration.bin +0 -0
- idmtools_test/inputs/custom/Regional_Migration.bin.json +12 -0
- idmtools_test/inputs/custom/Zambia_30arcsec_air_temperature_daily.bin +0 -0
- idmtools_test/inputs/custom/Zambia_30arcsec_air_temperature_daily.bin.json +26 -0
- idmtools_test/inputs/custom/Zambia_30arcsec_rainfall_daily.bin +0 -0
- idmtools_test/inputs/custom/Zambia_30arcsec_rainfall_daily.bin.json +26 -0
- idmtools_test/inputs/custom/Zambia_30arcsec_relative_humidity_daily.bin +0 -0
- idmtools_test/inputs/custom/Zambia_30arcsec_relative_humidity_daily.bin.json +26 -0
- idmtools_test/inputs/custom/campaign.json +95384 -0
- idmtools_test/inputs/custom/config.json +943 -0
- idmtools_test/inputs/custom/custom_reports.json +163 -0
- idmtools_test/inputs/custom/demo.json +1258 -0
- idmtools_test/inputs/custom/emodules_map.json +9 -0
- idmtools_test/inputs/custom/reporter_plugins/libReportMalariaFiltered.dll +0 -0
- idmtools_test/inputs/custom/reporter_plugins/libSpatialReportMalariaFiltered.dll +0 -0
- idmtools_test/inputs/custom/reporter_plugins/libreporteventcounter.dll +0 -0
- idmtools_test/inputs/duplicated_model/exe/Eradication +0 -0
- idmtools_test/inputs/duplicated_model/f1 +0 -0
- idmtools_test/inputs/emod/Eradication.exe +0 -0
- idmtools_test/inputs/emod_files/campaign.json +21 -0
- idmtools_test/inputs/emod_files/config.json +125 -0
- idmtools_test/inputs/emod_files/demographics.json +81 -0
- idmtools_test/inputs/fakemodels/AnotherOne +0 -0
- idmtools_test/inputs/fakemodels/Eradication +0 -0
- idmtools_test/inputs/fakemodels/Eradication-2.11.custom.exe +0 -0
- idmtools_test/inputs/fakemodels/Eradication.exe +0 -0
- idmtools_test/inputs/files/campaign.json +21 -0
- idmtools_test/inputs/files/config.json +119 -0
- idmtools_test/inputs/files/demographics.json +82 -0
- idmtools_test/inputs/files/hello.txt +1 -0
- idmtools_test/inputs/id_files/slurm.example_python_experiment.id +1 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Brazil_Central_West_Brazil_Central_West_2.5arcmin_air_temperature_daily.bin +0 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Brazil_Central_West_Brazil_Central_West_2.5arcmin_air_temperature_daily.bin.json +26 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Brazil_Central_West_Brazil_Central_West_2.5arcmin_demographics.json +559 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Brazil_Central_West_Brazil_Central_West_2.5arcmin_rainfall_daily.bin +0 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Brazil_Central_West_Brazil_Central_West_2.5arcmin_rainfall_daily.bin.json +26 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Brazil_Central_West_Brazil_Central_West_2.5arcmin_relative_humidity_daily.bin +0 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Brazil_Central_West_Brazil_Central_West_2.5arcmin_relative_humidity_daily.bin.json +26 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Eradication +0 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/Assets/Eradication.exe +0 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/campaign.json +4 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/config.json +667 -0
- idmtools_test/inputs/malaria_brazil_central_west_spatial/malaria_brazil_central_west_spatial-ERA5Input_demo.csv +37 -0
- idmtools_test/inputs/python/Assets/MyExternalLibrary/__init__.py +0 -0
- idmtools_test/inputs/python/Assets/MyExternalLibrary/functions.py +15 -0
- idmtools_test/inputs/python/Assets/MyLib/functions.py +2 -0
- idmtools_test/inputs/python/Assets/MyLib/temp.py +271 -0
- idmtools_test/inputs/python/Assets/__init__.py +0 -0
- idmtools_test/inputs/python/__init__.py +0 -0
- idmtools_test/inputs/python/folder_dup_file/__init__.py +0 -0
- idmtools_test/inputs/python/folder_dup_file/model1.py +19 -0
- idmtools_test/inputs/python/hello_world.py +1 -0
- idmtools_test/inputs/python/model.py +26 -0
- idmtools_test/inputs/python/model1.py +20 -0
- idmtools_test/inputs/python/model3.py +21 -0
- idmtools_test/inputs/python/newmodel2.py +20 -0
- idmtools_test/inputs/python/output_generator/generate.py +39 -0
- idmtools_test/inputs/python/realpath_verify.py +6 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/MyExternalLibrary/Python36/dtk_generic_intrahost.pyd +0 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/MyExternalLibrary/Python36/dtk_nodedemog.pyd +0 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/MyExternalLibrary/Python37/dtk_generic_intrahost.pyd +0 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/MyExternalLibrary/Python37/dtk_nodedemog.pyd +0 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/SEIR_model.py +252 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/SEIR_model_slurm.py +242 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/config_sim.py +48 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/custom_csv_analyzer.py +133 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/python.sh +4 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/requirements.txt +4 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/templates/config.json +68 -0
- idmtools_test/inputs/python/ye_seir_model/Assets/templates/demographics_template.json +44 -0
- idmtools_test/inputs/python/ye_seir_model/__init__.py +0 -0
- idmtools_test/inputs/python_experiments/__init__.py +0 -0
- idmtools_test/inputs/python_experiments/model.py +10 -0
- idmtools_test/inputs/r/model1.R +1 -0
- idmtools_test/inputs/r/ncov_analysis/individual_dynamics_estimates/estimate_incubation_period.R +89 -0
- idmtools_test/inputs/regression/107/Assets/__init__.py +0 -0
- idmtools_test/inputs/regression/107/Assets/model.py +1 -0
- idmtools_test/inputs/regression/107/__init__.py +0 -0
- idmtools_test/inputs/regression/125/Assets/__init__.py +0 -0
- idmtools_test/inputs/regression/125/Assets/model.py +1 -0
- idmtools_test/inputs/regression/125/Assets2/__init__.py +0 -0
- idmtools_test/inputs/regression/125/Assets2/dir1/__init__.py +0 -0
- idmtools_test/inputs/regression/125/Assets2/dir1/model.py +1 -0
- idmtools_test/inputs/regression/125/Assets2/dir2/__init__.py +0 -0
- idmtools_test/inputs/regression/125/Assets2/dir2/model.py +1 -0
- idmtools_test/inputs/regression/125/__init__.py +0 -0
- idmtools_test/inputs/regression/__init__.py +0 -0
- idmtools_test/inputs/scheduling/hpc/WorkOrder.json +7 -0
- idmtools_test/inputs/scheduling/slurm/WorkOrder.json +11 -0
- idmtools_test/inputs/scheduling/slurm/WorkOrder1.json +11 -0
- idmtools_test/inputs/scheduling/slurm/WorkOrder2.json +13 -0
- idmtools_test/inputs/scheduling/slurm/commandline_model.py +22 -0
- idmtools_test/inputs/serialization/Eradication.exe +0 -0
- idmtools_test/inputs/serialization/single_node_demographics.json +82 -0
- idmtools_test/inputs/singularity/alpine_simple/Singularity.def +28 -0
- idmtools_test/inputs/singularity/alpine_simple/run_model.py +41 -0
- idmtools_test/inputs/singularity/alpine_template/Singularity.jinja +22 -0
- idmtools_test/test_precreate_hooks.py +25 -0
- idmtools_test/utils/__init__.py +0 -0
- idmtools_test/utils/cli.py +41 -0
- idmtools_test/utils/common_experiments.py +79 -0
- idmtools_test/utils/comps.py +152 -0
- idmtools_test/utils/decorators.py +208 -0
- idmtools_test/utils/execute_operations/__init__.py +0 -0
- idmtools_test/utils/execute_operations/experiment_operations.py +237 -0
- idmtools_test/utils/execute_operations/simulate_operations.py +368 -0
- idmtools_test/utils/itest_with_persistence.py +25 -0
- idmtools_test/utils/operations/__init__.py +0 -0
- idmtools_test/utils/operations/experiment_operations.py +64 -0
- idmtools_test/utils/operations/simulation_operations.py +114 -0
- idmtools_test/utils/shared_functions.py +25 -0
- idmtools_test/utils/test_asset.py +89 -0
- idmtools_test/utils/test_asset_collection.py +223 -0
- idmtools_test/utils/test_execute_platform.py +137 -0
- idmtools_test/utils/test_platform.py +94 -0
- idmtools_test/utils/test_task.py +69 -0
- idmtools_test/utils/utils.py +146 -0
- idmtools_test-0.0.2.dist-info/METADATA +48 -0
- idmtools_test-0.0.2.dist-info/RECORD +139 -0
- idmtools_test-0.0.2.dist-info/entry_points.txt +9 -0
- idmtools_test-0.0.2.dist-info/licenses/LICENSE.TXT +3 -0
- idmtools_test-0.0.0.dev0.dist-info/METADATA +0 -41
- idmtools_test-0.0.0.dev0.dist-info/RECORD +0 -5
- {idmtools_test-0.0.0.dev0.dist-info → idmtools_test-0.0.2.dist-info}/WHEEL +0 -0
- {idmtools_test-0.0.0.dev0.dist-info → idmtools_test-0.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import typing
|
|
3
|
+
from typing import NoReturn
|
|
4
|
+
|
|
5
|
+
from idmtools.assets import TAssetList, TAssetFilterList, TAsset
|
|
6
|
+
from idmtools.assets.errors import DuplicatedAssetError
|
|
7
|
+
from idmtools.core import FilterMode
|
|
8
|
+
from idmtools.core.interfaces.ientity import IEntity
|
|
9
|
+
from idmtools.frozen.frozen_utils import IFrozen
|
|
10
|
+
from idmtools.utils.file import scan_directory
|
|
11
|
+
from idmtools.utils.filters.asset_filters import default_asset_file_filter
|
|
12
|
+
from idmtools_test.utils.test_asset import Asset
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AssetCollection(IEntity, IFrozen):
|
|
16
|
+
"""
|
|
17
|
+
A class that represents a collection of assets.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# region Constructors
|
|
21
|
+
def __init__(self, assets: 'TAssetList' = None): # noqa: F821
|
|
22
|
+
"""
|
|
23
|
+
A constructor.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
assets: An optional list of assets to create the collection with.
|
|
27
|
+
"""
|
|
28
|
+
super().__init__()
|
|
29
|
+
self.assets = assets or []
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_directory(cls, assets_directory: str, recursive: bool = True, flatten: bool = False,
|
|
33
|
+
filters: 'TAssetFilterList' = None, filters_mode: 'FilterMode' = FilterMode.OR, # noqa: F821
|
|
34
|
+
relative_path: str = None) -> 'TAssetCollection':
|
|
35
|
+
"""
|
|
36
|
+
Fill up an :class:`AssetCollection` from the specified directory. See
|
|
37
|
+
:meth:`~AssetCollection.assets_from_directory` for arguments.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
A created :class:`AssetCollection` object.
|
|
41
|
+
"""
|
|
42
|
+
assets = cls.assets_from_directory(assets_directory, recursive, flatten, filters, filters_mode, relative_path)
|
|
43
|
+
return cls(assets=assets)
|
|
44
|
+
|
|
45
|
+
# endregion
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def assets_from_directory(assets_directory: 'str', recursive: 'bool' = True, flatten: 'bool' = False,
|
|
49
|
+
filters: 'TAssetFilterList' = None, # noqa: F821
|
|
50
|
+
filters_mode: 'FilterMode' = FilterMode.OR,
|
|
51
|
+
forced_relative_path: 'str' = None) -> 'TAssetList': # noqa: F821
|
|
52
|
+
"""
|
|
53
|
+
Create assets for files in a given directory.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
assets_directory: The root directory of the assets.
|
|
57
|
+
recursive: True to recursively traverse the subdirectory.
|
|
58
|
+
flatten: Put all the files in root regardless of whether they were in a subdirectory or not.
|
|
59
|
+
filters: A list of filters to apply to the assets. The filters are functions taking an
|
|
60
|
+
:class:`~idmtools.assets.asset.Asset`
|
|
61
|
+
as argument and returning true or false. True adds the asset to the collection; False filters
|
|
62
|
+
it out. See :meth:`~idmtools.utils.filters.asset_filters`.
|
|
63
|
+
filters_mode: When given multiple filters, either OR or AND the results.
|
|
64
|
+
forced_relative_path: Prefix a relative path to the path created from the root directory.
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
For **relative_path**, given the following folder structure root/a/1,txt root/b.txt and relative_path="test".
|
|
68
|
+
Will return assets with relative path: test/a/1,txt and test/b.txt
|
|
69
|
+
|
|
70
|
+
Given the previous example, if flatten is also set to True, the following relative_path will be set:
|
|
71
|
+
/1.txt and /b.txt
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
A list of assets.
|
|
75
|
+
"""
|
|
76
|
+
found_assets = []
|
|
77
|
+
for entry in scan_directory(assets_directory, recursive):
|
|
78
|
+
relative_path = os.path.relpath(os.path.dirname(entry.path), assets_directory)
|
|
79
|
+
found_assets.append(Asset(absolute_path=os.path.abspath(entry.path),
|
|
80
|
+
relative_path=None if relative_path == "." else relative_path,
|
|
81
|
+
filename=entry.name))
|
|
82
|
+
|
|
83
|
+
# Apply the default filter
|
|
84
|
+
found_assets = list(filter(default_asset_file_filter, found_assets))
|
|
85
|
+
|
|
86
|
+
# Operations on assets (filter, flatten, force relative_path)
|
|
87
|
+
assets = []
|
|
88
|
+
for asset in found_assets:
|
|
89
|
+
if filters:
|
|
90
|
+
results = [f(asset) for f in filters]
|
|
91
|
+
if filters_mode == FilterMode.OR and not any(results):
|
|
92
|
+
continue
|
|
93
|
+
if filters_mode == FilterMode.AND and not all(results):
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
if flatten:
|
|
97
|
+
asset.relative_path = None
|
|
98
|
+
|
|
99
|
+
if forced_relative_path:
|
|
100
|
+
asset.relative_path = os.path.join(forced_relative_path, asset.relative_path)
|
|
101
|
+
|
|
102
|
+
assets.append(asset)
|
|
103
|
+
|
|
104
|
+
return assets
|
|
105
|
+
|
|
106
|
+
def add_directory(self, assets_directory: 'str', recursive: 'bool' = True, flatten: 'bool' = False,
|
|
107
|
+
filters: 'TAssetFilterList' = None, filters_mode: 'FilterMode' = FilterMode.OR, # noqa: F821
|
|
108
|
+
relative_path: 'str' = None):
|
|
109
|
+
"""
|
|
110
|
+
Retrieve assets from the specified directory and add them to the collection.
|
|
111
|
+
See :meth:`~AssetCollection.assets_from_directory` for arguments.
|
|
112
|
+
"""
|
|
113
|
+
assets = AssetCollection.assets_from_directory(assets_directory, recursive, flatten, filters, filters_mode,
|
|
114
|
+
relative_path)
|
|
115
|
+
for asset in assets:
|
|
116
|
+
self.add_asset(asset)
|
|
117
|
+
|
|
118
|
+
def add_asset(self, asset: 'TAsset', fail_on_duplicate: 'bool' = True): # noqa: F821
|
|
119
|
+
"""
|
|
120
|
+
Add an asset to the collection.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
asset: An :class:`~idmtools.assets.asset.Asset` object to add.
|
|
124
|
+
fail_on_duplicate: Raise a **DuplicateAssetError** if an asset is duplicated.
|
|
125
|
+
If not, simply replace it.
|
|
126
|
+
"""
|
|
127
|
+
if asset in self.assets:
|
|
128
|
+
if fail_on_duplicate:
|
|
129
|
+
raise DuplicatedAssetError(asset)
|
|
130
|
+
else:
|
|
131
|
+
# The equality not considering the content of the asset, even if it is already present
|
|
132
|
+
# nothing guarantees that the content is the same. So remove and add the fresh one.
|
|
133
|
+
self.assets.remove(asset)
|
|
134
|
+
self.assets.append(asset)
|
|
135
|
+
|
|
136
|
+
def get_one(self, **kwargs):
|
|
137
|
+
"""
|
|
138
|
+
Get an asset out of the collection based on the filers passed.
|
|
139
|
+
Examples:
|
|
140
|
+
>>> a = AssetCollection()
|
|
141
|
+
>>> a.get_one(filename="filename.txt")
|
|
142
|
+
Args:
|
|
143
|
+
**kwargs: keyword argument representing the filters.
|
|
144
|
+
|
|
145
|
+
Returns: None or Asset if found
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
return next(filter(lambda a: all(getattr(a, k) == kwargs.get(k) for k in kwargs), self.assets))
|
|
150
|
+
except StopIteration:
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
def delete(self, **kwargs) -> 'NoReturn':
|
|
154
|
+
"""
|
|
155
|
+
Delete an asset based on keywords attributes
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
**kwargs: Filter for the asset to delete.
|
|
159
|
+
"""
|
|
160
|
+
if 'index' in kwargs:
|
|
161
|
+
return self.assets.remove(self.assets[kwargs.get('index')])
|
|
162
|
+
|
|
163
|
+
if 'asset' in kwargs:
|
|
164
|
+
return self.assets.remove(kwargs.get('asset'))
|
|
165
|
+
|
|
166
|
+
asset = self.get_one(**kwargs)
|
|
167
|
+
if asset:
|
|
168
|
+
self.assets.remove(asset)
|
|
169
|
+
|
|
170
|
+
def pop(self, **kwargs) -> 'TAsset':
|
|
171
|
+
"""
|
|
172
|
+
Get and delete an asset based on keywords.
|
|
173
|
+
Args:
|
|
174
|
+
**kwargs: Filter for the asset to pop.
|
|
175
|
+
|
|
176
|
+
"""
|
|
177
|
+
if not kwargs:
|
|
178
|
+
return self.assets.pop()
|
|
179
|
+
|
|
180
|
+
asset = self.get_one(**kwargs)
|
|
181
|
+
if asset:
|
|
182
|
+
self.assets.remove(asset)
|
|
183
|
+
return asset
|
|
184
|
+
|
|
185
|
+
def extend(self, assets: 'TAssetList', fail_on_duplicate: 'bool' = True) -> 'NoReturn':
|
|
186
|
+
"""
|
|
187
|
+
Extend the collection with new assets
|
|
188
|
+
Args:
|
|
189
|
+
assets: Which assets to add
|
|
190
|
+
fail_on_duplicate: Fail if duplicated asset is included.
|
|
191
|
+
|
|
192
|
+
"""
|
|
193
|
+
for asset in assets:
|
|
194
|
+
self.add_asset(asset, fail_on_duplicate)
|
|
195
|
+
|
|
196
|
+
def clear(self):
|
|
197
|
+
self.assets.clear()
|
|
198
|
+
|
|
199
|
+
def set_all_persisted(self):
|
|
200
|
+
for a in self:
|
|
201
|
+
a.persisted = True
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def count(self):
|
|
205
|
+
return len(self.assets)
|
|
206
|
+
|
|
207
|
+
@IEntity.uid.getter
|
|
208
|
+
def uid(self):
|
|
209
|
+
if self.count == 0:
|
|
210
|
+
return None
|
|
211
|
+
return super().uid
|
|
212
|
+
|
|
213
|
+
def __len__(self):
|
|
214
|
+
return len(self.assets)
|
|
215
|
+
|
|
216
|
+
def __getitem__(self, index):
|
|
217
|
+
return self.assets[index]
|
|
218
|
+
|
|
219
|
+
def __iter__(self):
|
|
220
|
+
yield from self.assets
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
TAssetCollection = typing.TypeVar("TAssetCollection", bound=AssetCollection)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
from concurrent.futures._base import as_completed, Executor
|
|
5
|
+
from concurrent.futures.process import ProcessPoolExecutor
|
|
6
|
+
from concurrent.futures.thread import ThreadPoolExecutor
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import List, Union, Type
|
|
11
|
+
from tqdm import tqdm
|
|
12
|
+
from idmtools.core import ItemType
|
|
13
|
+
from idmtools.entities import Suite
|
|
14
|
+
from idmtools.entities.experiment import Experiment
|
|
15
|
+
from idmtools.entities.iplatform import IPlatform
|
|
16
|
+
from idmtools.entities.iworkflow_item import IWorkflowItem
|
|
17
|
+
from idmtools.entities.platform_requirements import PlatformRequirements
|
|
18
|
+
from idmtools.registry.platform_specification import PlatformSpecification, get_platform_impl, \
|
|
19
|
+
example_configuration_impl, get_platform_type_impl
|
|
20
|
+
from idmtools.registry.plugin_specification import get_description_impl
|
|
21
|
+
from idmtools_test.utils.execute_operations.experiment_operations import TestExecutePlatformExperimentOperation
|
|
22
|
+
from idmtools_test.utils.execute_operations.simulate_operations import TestExecutePlatformSimulationOperation
|
|
23
|
+
|
|
24
|
+
supported_types = [PlatformRequirements.SHELL, PlatformRequirements.NativeBinary, PlatformRequirements.PYTHON,
|
|
25
|
+
PlatformRequirements.WINDOWS if os.name == "nt" else PlatformRequirements.LINUX]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PoolType(Enum):
|
|
29
|
+
thread = ThreadPoolExecutor
|
|
30
|
+
process = ProcessPoolExecutor
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
DEFAULT_OUTPUT_PATH = Path(os.getenv("IDMTOOLS_TEST_EXECUTE_PATH", Path().cwd().joinpath(".test_platform")))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def clear_execute_platform():
|
|
37
|
+
if Path.exists(DEFAULT_OUTPUT_PATH):
|
|
38
|
+
for file in os.listdir(DEFAULT_OUTPUT_PATH):
|
|
39
|
+
path = Path(DEFAULT_OUTPUT_PATH).joinpath(file)
|
|
40
|
+
try:
|
|
41
|
+
shutil.rmtree(path)
|
|
42
|
+
except OSError:
|
|
43
|
+
os.remove(path)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(repr=False)
|
|
47
|
+
class TestExecutePlatform(IPlatform):
|
|
48
|
+
_experiments: TestExecutePlatformExperimentOperation = field(
|
|
49
|
+
default=None, compare=False, metadata={"pickle_ignore": True}, repr=False, init=False
|
|
50
|
+
)
|
|
51
|
+
_simulations: TestExecutePlatformSimulationOperation = field(default=None, compare=False, metadata={"pickle_ignore": True},
|
|
52
|
+
repr=False, init=False)
|
|
53
|
+
|
|
54
|
+
_platform_supports: List[PlatformRequirements] = field(default_factory=lambda: copy.deepcopy(supported_types),
|
|
55
|
+
repr=False, init=False)
|
|
56
|
+
|
|
57
|
+
__test__ = False # Hide from test discovery
|
|
58
|
+
execute_directory: str = field(default=DEFAULT_OUTPUT_PATH)
|
|
59
|
+
pool: Executor = field(default=None, compare=False, metadata={"pickle_ignore": True}, repr=False, init=False)
|
|
60
|
+
pool_type: PoolType = field(default=PoolType.thread)
|
|
61
|
+
queue: List = field(default_factory=list, compare=False, metadata={"pickle_ignore": True}, repr=False, init=False)
|
|
62
|
+
workers: int = field(default=os.cpu_count()-1 if os.cpu_count() > 1 else 1)
|
|
63
|
+
|
|
64
|
+
def __post_init__(self):
|
|
65
|
+
self.init_interfaces()
|
|
66
|
+
self.supported_types = {ItemType.EXPERIMENT, ItemType.SIMULATION}
|
|
67
|
+
self.pool = self.pool_type.value(max_workers=self.workers)
|
|
68
|
+
super().__post_init__()
|
|
69
|
+
|
|
70
|
+
def init_interfaces(self):
|
|
71
|
+
self._experiments = TestExecutePlatformExperimentOperation(self)
|
|
72
|
+
self._simulations = TestExecutePlatformSimulationOperation(self)
|
|
73
|
+
|
|
74
|
+
def post_setstate(self):
|
|
75
|
+
self.init_interfaces()
|
|
76
|
+
|
|
77
|
+
def run_simulations(self, experiment: Experiment) -> None:
|
|
78
|
+
from idmtools.core import EntityStatus
|
|
79
|
+
self._simulations.set_simulation_status(experiment.uid, EntityStatus.RUNNING)
|
|
80
|
+
|
|
81
|
+
def cleanup(self):
|
|
82
|
+
self._experiments.experiments = dict()
|
|
83
|
+
self._simulations.simulations = dict()
|
|
84
|
+
|
|
85
|
+
def wait_till_done(self, item: Union[Experiment, IWorkflowItem, Suite], timeout: int = 60 * 60 * 24,
|
|
86
|
+
refresh_interval: int = 5, progress=True):
|
|
87
|
+
for future in as_completed(self.queue):
|
|
88
|
+
result = future.result()
|
|
89
|
+
for sim in self._simulations.simulations[result[0]]:
|
|
90
|
+
if sim.id == result[1]:
|
|
91
|
+
sim.status = result[2]
|
|
92
|
+
break
|
|
93
|
+
|
|
94
|
+
def wait_till_done_progress(self, item: Union[Experiment, IWorkflowItem, Suite], timeout: int = 60 * 60 * 24,
|
|
95
|
+
refresh_interval: int = 5):
|
|
96
|
+
for future in tqdm(as_completed(self.queue), total=len(self.queue)):
|
|
97
|
+
result = future.result()
|
|
98
|
+
if isinstance(item, Experiment):
|
|
99
|
+
for sim in item.simulations:
|
|
100
|
+
if sim.id == result[1]:
|
|
101
|
+
sim.status = result[2]
|
|
102
|
+
self._simulations.save_metadata(sim, update_data=dict(status=result[2]))
|
|
103
|
+
break
|
|
104
|
+
del self.queue
|
|
105
|
+
self.queue = []
|
|
106
|
+
|
|
107
|
+
def __del__(self):
|
|
108
|
+
self.pool.shutdown(False)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class TestExecutePlatformSpecification(PlatformSpecification):
|
|
112
|
+
|
|
113
|
+
@get_description_impl
|
|
114
|
+
def get_description(self) -> str:
|
|
115
|
+
return "Provides access to the Test Platform to IDM Tools"
|
|
116
|
+
|
|
117
|
+
@get_platform_impl
|
|
118
|
+
def get(self, **configuration) -> IPlatform:
|
|
119
|
+
"""
|
|
120
|
+
Build our test platform from the passed in configuration object
|
|
121
|
+
|
|
122
|
+
We do our import of platform here to avoid any weir
|
|
123
|
+
Args:
|
|
124
|
+
configuration:
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
|
|
128
|
+
"""
|
|
129
|
+
return TestExecutePlatform(**configuration)
|
|
130
|
+
|
|
131
|
+
@example_configuration_impl
|
|
132
|
+
def example_configuration(self):
|
|
133
|
+
return ""
|
|
134
|
+
|
|
135
|
+
@get_platform_type_impl
|
|
136
|
+
def get_type(self) -> Type[TestExecutePlatform]:
|
|
137
|
+
return TestExecutePlatform
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
import os
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
from typing import List, Type
|
|
6
|
+
|
|
7
|
+
from idmtools.core import ItemType
|
|
8
|
+
from idmtools.entities.experiment import Experiment
|
|
9
|
+
from idmtools.entities.iplatform import IPlatform
|
|
10
|
+
from idmtools.entities.platform_requirements import PlatformRequirements
|
|
11
|
+
from idmtools.registry.platform_specification import example_configuration_impl, get_platform_impl, \
|
|
12
|
+
get_platform_type_impl, PlatformSpecification
|
|
13
|
+
from idmtools.registry.plugin_specification import get_description_impl
|
|
14
|
+
from idmtools_test.utils.operations.experiment_operations import TestPlatformExperimentOperation
|
|
15
|
+
from idmtools_test.utils.operations.simulation_operations import TestPlatformSimulationOperation
|
|
16
|
+
|
|
17
|
+
logger = getLogger(__name__)
|
|
18
|
+
current_directory = os.path.dirname(os.path.realpath(__file__))
|
|
19
|
+
data_path = os.path.abspath(os.path.join(current_directory, "..", "data"))
|
|
20
|
+
|
|
21
|
+
supported_types = [PlatformRequirements.SHELL, PlatformRequirements.NativeBinary, PlatformRequirements.PYTHON,
|
|
22
|
+
PlatformRequirements.WINDOWS if os.name == "nt" else PlatformRequirements.LINUX]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(repr=False)
|
|
26
|
+
class TestPlatform(IPlatform):
|
|
27
|
+
"""
|
|
28
|
+
Test platform simulating a working platform to use in the test suites.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
_experiments: TestPlatformExperimentOperation = field(default=None, compare=False, metadata={"pickle_ignore": True},
|
|
32
|
+
repr=False, init=False)
|
|
33
|
+
_simulations: TestPlatformSimulationOperation = field(default=None, compare=False, metadata={"pickle_ignore": True},
|
|
34
|
+
repr=False, init=False)
|
|
35
|
+
|
|
36
|
+
_platform_supports: List[PlatformRequirements] = field(default_factory=lambda: copy.deepcopy(supported_types),
|
|
37
|
+
repr=False, init=False)
|
|
38
|
+
|
|
39
|
+
__test__ = False # Hide from test discovery
|
|
40
|
+
|
|
41
|
+
def __post_init__(self):
|
|
42
|
+
self.init_interfaces()
|
|
43
|
+
self.supported_types = {ItemType.EXPERIMENT, ItemType.SIMULATION}
|
|
44
|
+
super().__post_init__()
|
|
45
|
+
|
|
46
|
+
def init_interfaces(self):
|
|
47
|
+
self._experiments = TestPlatformExperimentOperation(self)
|
|
48
|
+
self._simulations = TestPlatformSimulationOperation(self)
|
|
49
|
+
|
|
50
|
+
def post_setstate(self):
|
|
51
|
+
self.init_interfaces()
|
|
52
|
+
|
|
53
|
+
def run_simulations(self, experiment: Experiment) -> None:
|
|
54
|
+
from idmtools.core import EntityStatus
|
|
55
|
+
self._simulations.set_simulation_status(experiment.uid, EntityStatus.RUNNING)
|
|
56
|
+
|
|
57
|
+
def cleanup(self):
|
|
58
|
+
self._experiments.experiments = dict()
|
|
59
|
+
self._simulations.simulations = dict()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
TEST_PLATFORM_EXAMPLE_CONFIG = """
|
|
63
|
+
[Test]
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class TestPlatformSpecification(PlatformSpecification):
|
|
69
|
+
|
|
70
|
+
@get_description_impl
|
|
71
|
+
def get_description(self) -> str:
|
|
72
|
+
return "Provides access to the Test Platform to IDM Tools"
|
|
73
|
+
|
|
74
|
+
@get_platform_impl
|
|
75
|
+
def get(self, **configuration) -> IPlatform:
|
|
76
|
+
"""
|
|
77
|
+
Build our test platform from the passed in configuration object
|
|
78
|
+
|
|
79
|
+
We do our import of platform here to avoid any weir
|
|
80
|
+
Args:
|
|
81
|
+
configuration:
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
return TestPlatform(**configuration)
|
|
87
|
+
|
|
88
|
+
@example_configuration_impl
|
|
89
|
+
def example_configuration(self):
|
|
90
|
+
return TEST_PLATFORM_EXAMPLE_CONFIG
|
|
91
|
+
|
|
92
|
+
@get_platform_type_impl
|
|
93
|
+
def get_type(self) -> Type[TestPlatform]:
|
|
94
|
+
return TestPlatform
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Type
|
|
4
|
+
|
|
5
|
+
from idmtools.assets import Asset, AssetCollection
|
|
6
|
+
from idmtools.entities import CommandLine
|
|
7
|
+
from idmtools.entities.itask import ITask
|
|
8
|
+
from idmtools.entities.simulation import Simulation
|
|
9
|
+
from idmtools.registry.task_specification import TaskSpecification
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(repr=False)
|
|
13
|
+
class TestTask(ITask):
|
|
14
|
+
command: CommandLine = field(default=None, metadata={"md": True})
|
|
15
|
+
parameters: dict = field(default_factory=lambda: {}, metadata={"md": True})
|
|
16
|
+
common_asset_paths: list = field(default_factory=lambda: [])
|
|
17
|
+
|
|
18
|
+
__test__ = False # Hide from test discovery
|
|
19
|
+
|
|
20
|
+
def __post_init__(self):
|
|
21
|
+
if self.command is None:
|
|
22
|
+
self.command = CommandLine.from_string('echo this is a test')
|
|
23
|
+
|
|
24
|
+
def set_parameter(self, name: str, value: any) -> dict:
|
|
25
|
+
self.parameters[name] = value
|
|
26
|
+
return {"name": value}
|
|
27
|
+
|
|
28
|
+
def get_parameter(self, name, default=None):
|
|
29
|
+
"""
|
|
30
|
+
Get a parameter in the simulation
|
|
31
|
+
Args:
|
|
32
|
+
name: Name of the parameter
|
|
33
|
+
Returns:the Value of the parameter
|
|
34
|
+
"""
|
|
35
|
+
return self.parameters.get(name, default)
|
|
36
|
+
|
|
37
|
+
def update_parameters(self, params):
|
|
38
|
+
"""
|
|
39
|
+
Bulk update parameters
|
|
40
|
+
Args:
|
|
41
|
+
params: dict with new values
|
|
42
|
+
Returns:None
|
|
43
|
+
"""
|
|
44
|
+
self.parameters.update(params)
|
|
45
|
+
|
|
46
|
+
def gather_common_assets(self) -> AssetCollection:
|
|
47
|
+
# modified for test (uid hashing means changing uids) copied version from python_task.py
|
|
48
|
+
assets = [Asset(absolute_path=path) for path in self.common_asset_paths]
|
|
49
|
+
return AssetCollection(assets=assets)
|
|
50
|
+
|
|
51
|
+
def gather_transient_assets(self) -> AssetCollection:
|
|
52
|
+
if not self.transient_assets.has_asset(filename="config.json"):
|
|
53
|
+
self.transient_assets.add_asset(Asset(filename="config.json", content=json.dumps(self.parameters)))
|
|
54
|
+
return self.transient_assets
|
|
55
|
+
|
|
56
|
+
def reload_from_simulation(self, simulation: 'Simulation'):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestTaskSpecification(TaskSpecification):
|
|
61
|
+
|
|
62
|
+
def get(self, configuration: dict) -> TestTask:
|
|
63
|
+
return TestTask(**configuration)
|
|
64
|
+
|
|
65
|
+
def get_description(self) -> str:
|
|
66
|
+
return "Defines a task that is just used for testing purposes"
|
|
67
|
+
|
|
68
|
+
def get_type(self) -> Type[TestTask]:
|
|
69
|
+
return TestTask
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from io import StringIO
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from idmtools.entities.simulation import Simulation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_case_name(name: str):
|
|
12
|
+
"""
|
|
13
|
+
Add prefix from environment var to names of experiments
|
|
14
|
+
Args:
|
|
15
|
+
name:
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
if os.environ.get("IDMTOOLS_TEST_PREFIX", None):
|
|
21
|
+
return f'{os.environ["IDMTOOLS_TEST_PREFIX"]}.{name}'
|
|
22
|
+
return name
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def del_folder(path: str):
|
|
26
|
+
"""
|
|
27
|
+
Delete Folder
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
path: Path to delete
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
if os.path.exists(path):
|
|
36
|
+
shutil.rmtree(path)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def del_file(filename: str, dir: str = None):
|
|
40
|
+
"""
|
|
41
|
+
Delete a file
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
filename: Filename
|
|
45
|
+
dir: Optional Directory
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
if dir:
|
|
51
|
+
filepath = os.path.join(dir, filename)
|
|
52
|
+
else:
|
|
53
|
+
filepath = os.path.join(os.path.curdir, filename)
|
|
54
|
+
|
|
55
|
+
if os.path.exists(filepath):
|
|
56
|
+
print(filepath)
|
|
57
|
+
os.remove(filepath)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def load_csv_file(filename: str, dir: str = None) -> pd.DataFrame():
|
|
61
|
+
"""
|
|
62
|
+
Load CSV File
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
filename: Filename
|
|
66
|
+
dir: Optional Directory
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
df = None
|
|
72
|
+
if dir:
|
|
73
|
+
filepath = os.path.join(dir, filename)
|
|
74
|
+
else:
|
|
75
|
+
filepath = os.path.join(os.path.curdir, filename)
|
|
76
|
+
|
|
77
|
+
if os.path.exists(filepath):
|
|
78
|
+
df = pd.read_csv(filepath)
|
|
79
|
+
return df
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def verify_simulation(simulation: Simulation, expected_parameters, expected_values):
|
|
83
|
+
for value_set in expected_values:
|
|
84
|
+
for i, value in enumerate(list(value_set)):
|
|
85
|
+
if not simulation.task.parameters[expected_parameters[i]] == expected_values:
|
|
86
|
+
break
|
|
87
|
+
return True
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def load_python_files_from_folder(analyzers_folder):
|
|
92
|
+
for file in os.listdir(analyzers_folder):
|
|
93
|
+
if '.py' in file:
|
|
94
|
+
yield file
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def execute_example(cmd, cwd=None, shell=True):
|
|
98
|
+
popen_opts = dict(shell=shell)
|
|
99
|
+
if cwd:
|
|
100
|
+
popen_opts['cwd'] = cwd
|
|
101
|
+
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True,
|
|
102
|
+
**popen_opts)
|
|
103
|
+
for stdout_line in iter(popen.stdout.readline, ""):
|
|
104
|
+
print(stdout_line.strip())
|
|
105
|
+
popen.stdout.close()
|
|
106
|
+
return_code = popen.wait()
|
|
107
|
+
if return_code:
|
|
108
|
+
raise subprocess.CalledProcessError(return_code, cmd)
|
|
109
|
+
return return_code
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_example_folder(tc, analyzers_folder):
|
|
113
|
+
for file in load_python_files_from_folder(analyzers_folder):
|
|
114
|
+
with tc.subTest(file):
|
|
115
|
+
working_directory = analyzers_folder
|
|
116
|
+
try:
|
|
117
|
+
print(f'Running example: python {file}')
|
|
118
|
+
result = execute_example(f'python {file}', cwd=working_directory, shell=True)
|
|
119
|
+
tc.assertEqual(result, 0)
|
|
120
|
+
except subprocess.CalledProcessError as e:
|
|
121
|
+
tc.assertEqual(result, e.returncode, f'Result for example {file} failed. See {e.output}')
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@contextmanager
|
|
125
|
+
def captured_output():
|
|
126
|
+
new_out, new_err = StringIO(), StringIO()
|
|
127
|
+
old_out, old_err = sys.stdout, sys.stderr
|
|
128
|
+
try:
|
|
129
|
+
sys.stdout, sys.stderr = new_out, new_err
|
|
130
|
+
yield sys.stdout, sys.stderr
|
|
131
|
+
finally:
|
|
132
|
+
sys.stdout, sys.stderr = old_out, old_err
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def is_global_configuration_enabled() -> bool:
|
|
136
|
+
from idmtools import IdmConfigParser
|
|
137
|
+
return os.path.exists(IdmConfigParser.get_global_configuration_name()) or os.environ.get("IDMTOOLS_CONFIG_FILE", None) is not None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_performance_scale() -> int:
|
|
141
|
+
try:
|
|
142
|
+
scale = int(os.getenv("IDMTOOLS_TEST_SCALE", "1"))
|
|
143
|
+
except:
|
|
144
|
+
scale = 1
|
|
145
|
+
return scale
|
|
146
|
+
|