Skip to content

Commit

Permalink
all tests passing, mypy green, styling done
Browse files Browse the repository at this point in the history
  • Loading branch information
amandarichardsonn committed Aug 20, 2024
1 parent 18b1455 commit 2648258
Show file tree
Hide file tree
Showing 17 changed files with 404 additions and 140 deletions.
12 changes: 8 additions & 4 deletions smartsim/_core/generation/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,16 @@ def _log_file(log_path: pathlib.Path) -> pathlib.Path:
"""
return pathlib.Path(log_path) / "smartsim_params.txt"

def _output_files(self, log_path: pathlib.Path, job_name: str) -> t.Tuple[pathlib.Path, pathlib.Path]:
def _output_files(
self, log_path: pathlib.Path, job_name: str
) -> t.Tuple[pathlib.Path, pathlib.Path]:
out_file_path = log_path / (job_name + ".out")
err_file_path = log_path / (job_name + ".err")
return out_file_path, err_file_path

def generate_job(self, job: Job, job_index: int) -> t.Tuple[pathlib.Path, pathlib.Path, pathlib.Path]:
def generate_job(
self, job: Job, job_index: int
) -> t.Tuple[pathlib.Path, pathlib.Path, pathlib.Path]:
"""Write and configure input files for a Job.
To have files or directories present in the created Job
Expand All @@ -140,7 +144,7 @@ def generate_job(self, job: Job, job_index: int) -> t.Tuple[pathlib.Path, pathli
with open(self._log_file(log_path), mode="w", encoding="utf-8") as log_file:
dt_string = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
log_file.write(f"Generation start date and time: {dt_string}\n")

# Create output files
out_file, err_file = self._output_files(log_path, job.entity.name)
# Open and write to .out file
Expand Down Expand Up @@ -344,4 +348,4 @@ def _build_tagged_files(tagged: TaggedFilesHierarchy) -> None:
# logger.log(
# level=self.log_level,
# msg=f"Configured application {entity.name} with no parameters",
# )
# )
2 changes: 1 addition & 1 deletion smartsim/_core/launcher/dragon/dragonLauncher.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
from __future__ import annotations

import os
import typing as t
import pathlib
import typing as t

from smartsim._core.schemas.dragonRequests import DragonRunPolicy
from smartsim.error import errors
Expand Down
10 changes: 6 additions & 4 deletions smartsim/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@
from smartsim._core.config import CONFIG
from smartsim._core.control.launch_history import LaunchHistory as _LaunchHistory
from smartsim.error import errors
from smartsim.status import InvalidJobStatus, JobStatus
from smartsim.settings import dispatch
from smartsim.status import InvalidJobStatus, JobStatus

from ._core import Controller, Generator, Manifest, previewrenderer
from .database import FeatureStore
Expand All @@ -59,8 +59,8 @@
from .log import ctx_exp_path, get_logger, method_contextualizer

if t.TYPE_CHECKING:
from smartsim.settings.dispatch import ExecutableProtocol
from smartsim.launchable.job import Job
from smartsim.settings.dispatch import ExecutableProtocol
from smartsim.types import LaunchedJobID

logger = get_logger(__name__)
Expand Down Expand Up @@ -281,7 +281,9 @@ def get_status(
return tuple(stats)

@_contextualize
def _generate(self, generator: Generator, job: Job, job_index: int) -> t.Tuple[pathlib.Path, pathlib.Path, pathlib.Path]:
def _generate(
self, generator: Generator, job: Job, job_index: int
) -> t.Tuple[pathlib.Path, pathlib.Path, pathlib.Path]:
"""Generate the directory structure and files for a ``Job``
If files or directories are attached to an ``Application`` object
Expand Down Expand Up @@ -392,4 +394,4 @@ def _append_to_fs_identifier_list(self, fs_identifier: str) -> None:
"with the same identifier"
)
# Otherwise, add
self._fs_identifiers.add(fs_identifier)
self._fs_identifiers.add(fs_identifier)
2 changes: 1 addition & 1 deletion smartsim/settings/arguments/launch/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from __future__ import annotations
from subprocess import PIPE

import typing as t
from subprocess import PIPE

from smartsim.log import get_logger
from smartsim.settings.dispatch import ShellLauncher, dispatch, make_shell_format_fn
Expand Down
30 changes: 20 additions & 10 deletions smartsim/settings/arguments/launch/lsf.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,46 @@

from __future__ import annotations

import typing as t
import pathlib
import subprocess
import typing as t

from smartsim.log import get_logger
from smartsim.settings.dispatch import ShellLauncher, dispatch, ExecutableProtocol, _EnvironMappingType, ShellLauncherCommand
from smartsim.settings.dispatch import (
ExecutableProtocol,
ShellLauncher,
ShellLauncherCommand,
_EnvironMappingType,
dispatch,
)

from ...common import set_check_input
from ...launchCommand import LauncherType
from ..launchArguments import LaunchArguments

logger = get_logger(__name__)

def _as_jsrun_command(args: LaunchArguments,
exe: ExecutableProtocol,
path: pathlib.Path,
env: _EnvironMappingType,
stdout_path: pathlib.Path,
stderr_path: pathlib.Path) -> ShellLauncherCommand:

def _as_jsrun_command(
args: LaunchArguments,
exe: ExecutableProtocol,
path: pathlib.Path,
env: _EnvironMappingType,
stdout_path: pathlib.Path,
stderr_path: pathlib.Path,
) -> ShellLauncherCommand:
command_tuple = (
"jsrun",
*(args.format_launch_args() or ()),
"--",
*exe.as_program_arguments(),
f"--stdio_stdout={stdout_path}",
f"--stdio_stderr={stderr_path}",

)
# add output and err to CMD tuple -> add dev Null for stdout and stderr
return ShellLauncherCommand(env, path, subprocess.DEVNULL, subprocess.DEVNULL, command_tuple)
return ShellLauncherCommand(
env, path, subprocess.DEVNULL, subprocess.DEVNULL, command_tuple
)


@dispatch(with_format=_as_jsrun_command, to_launcher=ShellLauncher)
Expand Down
37 changes: 24 additions & 13 deletions smartsim/settings/arguments/launch/slurm.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,50 @@

import os
import pathlib
import subprocess
import re
import subprocess
import typing as t

from smartsim.log import get_logger
from smartsim.settings.dispatch import ShellLauncher, dispatch, make_shell_format_fn
from smartsim._core.commands import Command
from smartsim.settings.dispatch import ExecutableProtocol, _FormatterType, _EnvironMappingType, ShellLauncherCommand
from smartsim.log import get_logger
from smartsim.settings.dispatch import (
ExecutableProtocol,
ShellLauncher,
ShellLauncherCommand,
_EnvironMappingType,
_FormatterType,
dispatch,
make_shell_format_fn,
)

from ...common import set_check_input
from ...launchCommand import LauncherType
from ..launchArguments import LaunchArguments

logger = get_logger(__name__)

def _as_srun_command(args: LaunchArguments,
exe: ExecutableProtocol,
path: pathlib.Path,
env: _EnvironMappingType,
stdout_path: pathlib.Path,
stderr_path: pathlib.Path) -> ShellLauncherCommand:

def _as_srun_command(
args: LaunchArguments,
exe: ExecutableProtocol,
path: pathlib.Path,
env: _EnvironMappingType,
stdout_path: pathlib.Path,
stderr_path: pathlib.Path,
) -> ShellLauncherCommand:
command_tuple = (
"srun",
*(args.format_launch_args() or ()),
"--",
*exe.as_program_arguments(),
f"--output={stdout_path}",
f"--error={stderr_path}",

)
# add output and err to CMD tuple -> add dev Null for stdout and stderr
return ShellLauncherCommand(env, path, subprocess.DEVNULL, subprocess.DEVNULL, command_tuple)

return ShellLauncherCommand(
env, path, subprocess.DEVNULL, subprocess.DEVNULL, command_tuple
)


@dispatch(with_format=_as_srun_command, to_launcher=ShellLauncher)
class SlurmLaunchArguments(LaunchArguments):
Expand Down
50 changes: 36 additions & 14 deletions smartsim/settings/dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,36 @@
import abc
import collections.abc
import dataclasses
import io
import os
import subprocess as sp
import pathlib
import subprocess
import subprocess as sp
import typing as t
import uuid
import os
from subprocess import STDOUT

import psutil
from typing_extensions import Self, TypeAlias, TypeVarTuple, Unpack

from smartsim._core.commands import Command, CommandList
from smartsim._core.utils import helpers
from smartsim.error import errors
from smartsim.status import JobStatus
from smartsim.types import LaunchedJobID
from smartsim._core.commands import Command, CommandList

from ..settings.launchCommand import LauncherType
from subprocess import STDOUT

if t.TYPE_CHECKING:
from smartsim.experiment import Experiment
from smartsim.settings.arguments import LaunchArguments


class ShellLauncherCommand(t.NamedTuple):
env: _EnvironMappingType
path: pathlib.Path
stdout: TextIOWrapper
stderr: TextIOWrapper
stdout: io.TextIOWrapper | int
stderr: io.TextIOWrapper | int
command_tuple: tuple[str, tuple[str, ...]] | t.Sequence[str]


Expand All @@ -79,7 +82,14 @@ class ShellLauncherCommand(t.NamedTuple):
a job
"""
_FormatterType: TypeAlias = t.Callable[
[_DispatchableT, "ExecutableProtocol", _WorkingDirectory, _EnvironMappingType, pathlib.Path, pathlib.Path],
[
_DispatchableT,
"ExecutableProtocol",
_WorkingDirectory,
_EnvironMappingType,
pathlib.Path,
pathlib.Path,
],
_LaunchableT,
]
"""A callable that is capable of formatting the components of a job into a type
Expand Down Expand Up @@ -448,7 +458,7 @@ def get_status(


def make_shell_format_fn(
run_command: str | None
run_command: str | None,
) -> _FormatterType[LaunchArguments, ShellLauncherCommand]:
"""A function that builds a function that formats a `LaunchArguments` as a
shell executable sequence of strings for a given launching utility.
Expand Down Expand Up @@ -478,6 +488,7 @@ def make_shell_format_fn(
:returns: A function to format an arguments, an executable, and an
environment as a shell launchable sequence for strings.
"""

def impl(
args: LaunchArguments,
exe: ExecutableProtocol,
Expand All @@ -496,27 +507,36 @@ def impl(
if run_command is not None
else exe.as_program_arguments()
)
return ShellLauncherCommand(env, pathlib.Path(path), stdout_path, stderr_path, command_tuple)
return ShellLauncherCommand(
env, pathlib.Path(path), open(stdout_path), open(stderr_path), command_tuple
)

return impl


class ShellLauncher:
"""Mock launcher for launching/tracking simple shell commands"""

# add a def check

def __init__(self) -> None:
self._launched: dict[LaunchedJobID, sp.Popen[bytes]] = {}

# covariant, contravariant, + boliscoff substitution princ
def start(
self, shell_command: ShellLauncherCommand # this should be a named tuple
self, shell_command: ShellLauncherCommand # this should be a named tuple
) -> LaunchedJobID:
id_ = create_job_id()
# raise ValueError -> invalid stuff throw
exe, *rest = shell_command.command_tuple
expanded_exe = helpers.expand_exe_path(exe)
# pylint: disable-next=consider-using-with
self._launched[id_] = sp.Popen((expanded_exe, *rest), cwd=shell_command.path, env={k:v for k,v in shell_command.env.items() if v is not None}, stdout=shell_command.stdout, stderr=shell_command.stderr)
self._launched[id_] = sp.Popen(
(expanded_exe, *rest),
cwd=shell_command.path,
env={k: v for k, v in shell_command.env.items() if v is not None},
stdout=shell_command.stdout,
stderr=shell_command.stderr,
)
# Popen starts a new process and gives you back a handle to process, getting back the pid - process id
return id_

Expand All @@ -529,7 +549,9 @@ def _get_status(self, id_: LaunchedJobID, /) -> JobStatus:
if (proc := self._launched.get(id_)) is None:
msg = f"Launcher `{self}` has not launched a job with id `{id_}`"
raise errors.LauncherJobNotFound(msg)
ret_code = proc.poll() # add a test that mocks out poll and raise some exception - terminal -> import subprocess -> start something echo blah - then poll and see what a valid fake output is
ret_code = (
proc.poll()
) # add a test that mocks out poll and raise some exception - terminal -> import subprocess -> start something echo blah - then poll and see what a valid fake output is
print(ret_code)
# try/catch around here and then reaise a smartsim.error
if ret_code is None:
Expand Down
20 changes: 12 additions & 8 deletions tests/temp_tests/test_settings/test_alpsLauncher.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import pytest
import io
import os
import pathlib

import pytest

from smartsim.settings import LaunchSettings
from smartsim.settings.arguments.launch.alps import (
AprunLaunchArguments,
Expand Down Expand Up @@ -213,14 +216,15 @@ def test_invalid_exclude_hostlist_format():
),
)
def test_formatting_launch_args(mock_echo_executable, args, expected, test_dir):
outfile = "out.txt"
errfile = "err.txt"
shell_launch_cmd = _as_aprun_command(
AprunLaunchArguments(args), mock_echo_executable, test_dir, {}, outfile, errfile
)
out = os.path.join(test_dir, "out.txt")
err = os.path.join(test_dir, "err.txt")
with open(out, "w") as _, open(err, "w") as _:
shell_launch_cmd = _as_aprun_command(
AprunLaunchArguments(args), mock_echo_executable, test_dir, {}, out, err
)
assert isinstance(shell_launch_cmd, ShellLauncherCommand)
assert shell_launch_cmd.command_tuple == expected
assert shell_launch_cmd.path == pathlib.Path(test_dir)
assert shell_launch_cmd.env == {}
assert shell_launch_cmd.stdout == outfile
assert shell_launch_cmd.stderr == errfile
assert isinstance(shell_launch_cmd.stdout, io.TextIOWrapper)
assert isinstance(shell_launch_cmd.stderr, io.TextIOWrapper)
8 changes: 7 additions & 1 deletion tests/temp_tests/test_settings/test_dragonLauncher.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,13 @@ def test_formatting_launch_args_into_request(
if v is not NOT_SET
}
expected_run_req = DragonRunRequestView(
exe="echo", exe_args=["hello", "world"], path=test_dir, env={}, output_file="output.txt", error_file="error.txt", **expected_args
exe="echo",
exe_args=["hello", "world"],
path=test_dir,
env={},
output_file="output.txt",
error_file="error.txt",
**expected_args,
)
assert req.exe == expected_run_req.exe
assert req.exe_args == expected_run_req.exe_args
Expand Down
Loading

0 comments on commit 2648258

Please sign in to comment.