Ver código fonte

refactor: accrue results as the compound actions run

Sam Jaffe 1 mês atrás
pai
commit
860fc94cf5
4 arquivos alterados com 49 adições e 29 exclusões
  1. 12 15
      src/cipy/action.py
  2. 20 0
      src/cipy/common.py
  3. 1 1
      src/cipy/runner.py
  4. 16 13
      src/cipy/workflow.py

+ 12 - 15
src/cipy/action.py

@@ -11,7 +11,7 @@ from typing import final
 from pydantic import Field, PrivateAttr
 
 import cipy.runner
-from cipy.common import Action, Context, Outputs, Status, _validate
+from cipy.common import Action, Context, Results, Status, _validate
 
 
 class Shell(StrEnum):
@@ -108,26 +108,23 @@ class Composite(Action):
     steps: list[Action] = Field(frozen=True)
     _counter: int = PrivateAttr(default=0)
 
-    def _tick(self) -> None:
-        self._counter += 1
-
-    def _outputs(self) -> dict[str, Outputs]:
-        return {s.id: s.outputs for s in self.steps if s.id}
-
     @final
     def run(self, context: Context) -> Status:
         status = Status.SKIPPED
 
-        for step in self.steps:
-            if not step.enabled(status, context):
-                status |= Status.SKIPPED
-                continue
+        with context.extend(steps=Results()) as outctx:
+            for step in self.steps:
+                self._counter += 1
+
+                if step.enabled(status, context):
+                    stat = step.run(context)
+                    outctx.steps[step.id] = Results.Item(stat, _validate(step.outputs))
+                else:
+                    stat = Status.SKIPPED
+                    outctx.steps[step.id] = Results.Item(Status.SKIPPED)
 
-            status |= step.run(context)
-            self._counter += 1
-            _validate(step.outputs)
+                status |= stat
 
-        with context.extend(steps=self._outputs) as outctx:
             outctx.fabricate(self, "outputs")
 
         return status

+ 20 - 0
src/cipy/common.py

@@ -45,6 +45,26 @@ class Factory:
     __call__: Callable[[Context], Any]
 
 
+class Results(SimpleNamespace):
+    """
+    Holder object for tracking the result of an action for Composite/Workflow actions
+    """
+
+    @dataclass
+    class Item:
+        """Result of a single action that needs to be tracked"""
+
+        conclusion: Status = Status.NOT_RUN
+        outputs: Outputs = Outputs()
+
+    def __setitem__(self, subscript: str, value: Results.Item) -> None:
+        if subscript:
+            self.__setattr__(subscript, value)
+
+    def __getitem__(self, subscript: str) -> Results.Item:
+        return self.__getattribute__(subscript)
+
+
 class Context(SimpleNamespace):
     """Wrapper class for the context of the CI runtime"""
 

+ 1 - 1
src/cipy/runner.py

@@ -7,7 +7,7 @@ import os
 import tempfile
 
 from contextlib import contextmanager
-from typing import Any, Callable, Iterator, TypeVar, overload
+from typing import Any, Callable, Iterator, TypeVar
 
 from dotenv import dotenv_values
 

+ 16 - 13
src/cipy/workflow.py

@@ -4,7 +4,7 @@ from typing import Any, final, override
 
 from pydantic import BaseModel, PrivateAttr
 
-from cipy.common import Action, Context, Outputs, Status, _validate
+from cipy.common import Action, Context, Results, Status, _validate
 
 
 class Job(BaseModel):
@@ -34,9 +34,6 @@ class Workflow(Action):
             except ValueError:
                 pass
 
-    def _outputs(self) -> dict[str, Outputs]:
-        return {j.id: j.action.outputs for j in self.jobs}
-
     @final
     def run(self, context: Context) -> Status:
         status = Status.SKIPPED
@@ -47,16 +44,22 @@ class Workflow(Action):
                 iter(j for j in self.jobs if j.id not in visited and not j.needs), None
             )
 
-        while job := _next():
-            visited.add(job.id)
-            if not job.action.enabled(status, context):
-                status |= Status.SKIPPED
-                continue
-            status |= job.action.run(context)
-            self._finished(job.id)
-            _validate(job.action.outputs)
+        with context.extend(needs=Results()) as outctx:
+            while job := _next():
+                visited.add(job.id)
+
+                if job.action.enabled(status, context):
+                    stat = job.action.run(context)
+                    outctx.needs[job.id] = Results.Item(
+                        stat, _validate(job.action.outputs)
+                    )
+                else:
+                    stat = Status.SKIPPED
+                    outctx.needs[job.id] = Results.Item(Status.SKIPPED)
+
+                status |= stat
+                self._finished(job.id)
 
-        with context.extend(needs=self._outputs) as outctx:
             outctx.fabricate(self, "outputs")
 
         return status