|
@@ -1,14 +1,20 @@
|
|
|
"""
|
|
"""
|
|
|
Common functions for setting up/tearing down environments for running an action.
|
|
Common functions for setting up/tearing down environments for running an action.
|
|
|
"""
|
|
"""
|
|
|
|
|
+
|
|
|
|
|
+import functools
|
|
|
import os
|
|
import os
|
|
|
-import re
|
|
|
|
|
import tempfile
|
|
import tempfile
|
|
|
|
|
|
|
|
from contextlib import contextmanager
|
|
from contextlib import contextmanager
|
|
|
-from typing import Any, Callable, Iterator
|
|
|
|
|
|
|
+from typing import Any, Callable, Iterator, TypeVar, overload
|
|
|
|
|
+
|
|
|
|
|
+from dotenv import dotenv_values
|
|
|
|
|
|
|
|
-from cipy.common import Action, Context, Status, _validate
|
|
|
|
|
|
|
+import cipy.common
|
|
|
|
|
+from cipy.common import Context, Status, _validate
|
|
|
|
|
+
|
|
|
|
|
+Action = TypeVar("Action", bound=cipy.common.Action)
|
|
|
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
@contextmanager
|
|
@@ -31,8 +37,9 @@ def environ(*, error_on_override: bool = True, **overrides: Any) -> Iterator[Non
|
|
|
os.environ[key] = value
|
|
os.environ[key] = value
|
|
|
|
|
|
|
|
|
|
|
|
|
-@contextmanager
|
|
|
|
|
-def pipe(action: Action) -> Iterator[None]:
|
|
|
|
|
|
|
+def ipc(
|
|
|
|
|
+ func: Callable[[Action, Context], Status],
|
|
|
|
|
+) -> Callable[[Action, Context], Status]:
|
|
|
"""
|
|
"""
|
|
|
IPC tool for passing inputs and outputs between an Action that is
|
|
IPC tool for passing inputs and outputs between an Action that is
|
|
|
implemented as some manner of script.
|
|
implemented as some manner of script.
|
|
@@ -43,35 +50,26 @@ def pipe(action: Action) -> Iterator[None]:
|
|
|
Action.Outputs and output environment variables will be writiable into
|
|
Action.Outputs and output environment variables will be writiable into
|
|
|
special temporary files, which will then be read into the context.
|
|
special temporary files, which will then be read into the context.
|
|
|
"""
|
|
"""
|
|
|
- inputs = {"INPUT_" + re.sub("[ -]", "_", k): v for k, v in action.inputs}
|
|
|
|
|
- with (
|
|
|
|
|
- tempfile.TemporaryFile(mode="w+") as output,
|
|
|
|
|
- tempfile.TemporaryFile(mode="w+") as envfile,
|
|
|
|
|
- environ(CI_OUTPUT=output.name, CI_ENVIRON=envfile.name, **inputs),
|
|
|
|
|
- ):
|
|
|
|
|
- yield
|
|
|
|
|
- # TODO: Compute output
|
|
|
|
|
|
|
|
|
|
|
|
+ @functools.wraps(func)
|
|
|
|
|
+ def wrapper(self: Action, context: Context) -> Status:
|
|
|
|
|
+ inputs = {f"INPUT_{k}": v for k, v in self.inputs}
|
|
|
|
|
+ with (
|
|
|
|
|
+ tempfile.TemporaryFile(mode="w+") as output,
|
|
|
|
|
+ tempfile.TemporaryFile(mode="w+") as envfile,
|
|
|
|
|
+ environ(CI_OUTPUT=output.name, CI_ENVIRON=envfile.name, **inputs),
|
|
|
|
|
+ ):
|
|
|
|
|
+ rval = func(self, context)
|
|
|
|
|
|
|
|
-def run(
|
|
|
|
|
- context: Context,
|
|
|
|
|
- status: Status,
|
|
|
|
|
- action: Action,
|
|
|
|
|
- *,
|
|
|
|
|
- pre_validate: Callable[[], None] | None = None,
|
|
|
|
|
-) -> Status:
|
|
|
|
|
- """
|
|
|
|
|
- General executor for an action - guarantees that we have constructed and
|
|
|
|
|
- validated the Action.Inputs data.
|
|
|
|
|
|
|
+ if self.outputs is None:
|
|
|
|
|
+ self.outputs = self.__pydantic_fields__["outputs"].annotation()
|
|
|
|
|
|
|
|
- Allows for a callback hook in between the run finishing and output
|
|
|
|
|
- validation.
|
|
|
|
|
- """
|
|
|
|
|
- action.inputs = context.fabricate(action, "inputs")
|
|
|
|
|
|
|
+ outdata = dotenv_values(output.name)
|
|
|
|
|
+ for k, field in self.outputs.__pydantic_fields__.items():
|
|
|
|
|
+ if k in outdata:
|
|
|
|
|
+ setattr(self.outputs, k, outdata[k])
|
|
|
|
|
|
|
|
- stat = action.run(context)
|
|
|
|
|
- if pre_validate:
|
|
|
|
|
- pre_validate()
|
|
|
|
|
|
|
+ _validate(self.outputs)
|
|
|
|
|
+ return rval
|
|
|
|
|
|
|
|
- _validate(action.outputs)
|
|
|
|
|
- return stat if stat.value > status.value else status
|
|
|
|
|
|
|
+ return wrapper
|