Source code for idmtools_models.python.python_task
"""idmtools python task.
Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
"""
import os
from dataclasses import dataclass, field
from logging import getLogger, DEBUG
from typing import Set, List, Type, Union, TYPE_CHECKING
from idmtools.assets import Asset, AssetCollection
from idmtools.entities import CommandLine
from idmtools.entities.itask import ITask
from idmtools.entities.iworkflow_item import IWorkflowItem
from idmtools.entities.platform_requirements import PlatformRequirements
from idmtools.entities.simulation import Simulation
from idmtools.registry.task_specification import TaskSpecification
if TYPE_CHECKING: # pragma: no cover
from idmtools.entities.iplatform import IPlatform
logger = getLogger(__name__)
[docs]@dataclass()
class PythonTask(ITask):
"""
PythonTask makes running python scripts a bit easier through idmtools.
Notes:
TODO - Link examples here
"""
script_path: str = field(default=None, metadata={"md": True})
python_path: str = field(default='python', metadata={"md": True})
platform_requirements: Set[PlatformRequirements] = field(default_factory=lambda: [PlatformRequirements.PYTHON])
def __post_init__(self):
"""
Constructor.
"""
super().__post_init__()
if os.path.exists(self.script_path):
if self.script_path:
self.script_path = os.path.abspath(self.script_path)
else:
# don't error if we can't find script. Maybe it is in the asset collection? but warn user
logger.warning(f'Cannot find script at {self.script_path}. If script does not exist in Assets '
f'as {os.path.basename(self.script_path)}, execution could fail')
if self.command is None:
self.command = CommandLine('')
[docs] def gather_common_assets(self) -> AssetCollection:
"""
Get the common assets. This should be a set of assets that are common to all tasks in an experiment.
Returns:
AssetCollection
"""
if logger.isEnabledFor(DEBUG):
logger.debug('Adding Common asset from %s', self.script_path)
self.common_assets.add_or_replace_asset(Asset(absolute_path=self.script_path))
return self.common_assets
[docs] def gather_transient_assets(self) -> AssetCollection:
"""
Gather transient assets. Generally this is the simulation level assets.
Returns:
Transient assets. Also known as simulation level assets.
"""
return self.transient_assets
[docs] def reload_from_simulation(self, simulation: Simulation, **kwargs):
"""
Reloads a python task from a simulation.
Args:
simulation: Simulation to reload
Returns:
None
"""
# check experiment level assets for items
if simulation.parent.assets:
new_assets = AssetCollection()
for _i, asset in enumerate(simulation.parent.assets.assets):
if asset.filename != self.script_path and asset.absolute_path != self.script_path:
new_assets.add_asset(asset)
simulation.parent.assets = new_assets
logger.debug("Reload from simulation")
[docs] def pre_creation(self, parent: Union[Simulation, IWorkflowItem], platform: 'IPlatform'):
"""
Called before creation of parent.
Args:
parent: Parent
platform: Platform Python Task is being executed on
Returns:
None
Raise:
ValueError if script name is not provided
"""
if self.script_path is None:
raise ValueError("Script name is required")
self.command = CommandLine.from_string(f'{self.python_path} {platform.join_path(platform.common_asset_path, os.path.basename(self.script_path))}')
[docs]class PythonTaskSpecification(TaskSpecification):
"""
PythonTaskSpecification provides the plugin info for PythonTask.
"""
[docs] def get(self, configuration: dict) -> PythonTask:
"""
Get instance of Python Task with specified configuration.
Args:
configuration: Configuration for task
Returns:
Python task
"""
return PythonTask(**configuration)
[docs] def get_description(self) -> str:
"""
Description of the plugin.
Returns:
Description string
"""
return "Defines a python script command"
[docs] def get_example_urls(self) -> List[str]:
"""
Return List of urls that have examples using PythonTask.
Returns:
List of urls(str) that point to examples
"""
from idmtools_models import __version__
examples = [f'examples/{example}' for example in ['load_lib']]
return [self.get_version_url(f'v{__version__}', x) for x in examples]
[docs] def get_type(self) -> Type[PythonTask]:
"""
Get Type for Plugin.
Returns:
PythonTask
"""
return PythonTask
[docs] def get_version(self) -> str:
"""
Returns the version of the plugin.
Returns:
Plugin Version
"""
from idmtools_models import __version__
return __version__