Skip to content

Commit 88d5923

Browse files
committed
Use a status enum instead of hard-coded strings for step status
this allow separation of "Skipped" into skipped by error and by user
1 parent 136af82 commit 88d5923

File tree

5 files changed

+64
-45
lines changed

5 files changed

+64
-45
lines changed

cosmotech/orchestrator/api/run.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from cosmotech.orchestrator import VERSION
2323
from cosmotech.orchestrator.core.orchestrator import Orchestrator
24-
from cosmotech.orchestrator.core.step import Step
24+
from cosmotech.orchestrator.core.step import Step, StepStatus
2525
from cosmotech.orchestrator.utils.logger import LOGGER
2626
from cosmotech.orchestrator.utils.translate import T
2727

@@ -144,7 +144,7 @@ def run_template(
144144
LOGGER.info(v[0].simple_repr())
145145
LOGGER.debug(str(v[0]))
146146
results[k] = v[0]
147-
if v[0].status == "RunError":
147+
if v[0].status == StepStatus.ERROR:
148148
success = False
149149

150150
if exit_handlers:

cosmotech/orchestrator/core/orchestrator.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from cosmotech.orchestrator.core.runner import Runner
1515
from cosmotech.orchestrator.core.step import Step
16+
from cosmotech.orchestrator.core.step import StepStatus
1617
from cosmotech.orchestrator.templates.library import Library
1718
from cosmotech.orchestrator.templates.plugin import Plugin
1819
from cosmotech.orchestrator.utils.logger import LOGGER
@@ -102,7 +103,7 @@ def _load_from_json_content(
102103
for _precedent in _step.precedents:
103104
if isinstance(_precedent, str):
104105
if _precedent not in _steps:
105-
_step.status = "Error"
106+
_step.status = StepStatus.ERROR
106107
raise ValueError(
107108
T("csm-orc.orchestrator.core.orchestrator.step_not_exists").format(step_id=_precedent)
108109
)

cosmotech/orchestrator/core/step.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626
from cosmotech.orchestrator.utils.translate import T
2727

2828

29+
from enum import Enum
30+
31+
32+
class StepStatus(Enum):
33+
CREATED = 0
34+
INITIALIZED = 1
35+
SUCCESS = 2
36+
SKIPPED_BY_USER = 3
37+
SKIPPED_AFTER_FAILURE = 4
38+
ERROR = 5
39+
DRY_RUN = 6
40+
41+
2942
@dataclass
3043
class Step:
3144
id: str = field()
@@ -40,7 +53,7 @@ class Step:
4053
inputs: dict = field(default_factory=dict)
4154
captured_output: dict = field(default_factory=dict)
4255
loaded = False
43-
status = None
56+
status: StepStatus = StepStatus.CREATED
4457
skipped = False
4558
stop_library_load: InitVar[bool] = field(default=False, repr=False)
4659

@@ -94,7 +107,7 @@ def __load_command_from_library(self):
94107
self.display_command_id = self.commandId
95108
command: CommandTemplate = library.find_template_by_name(self.commandId)
96109
if command is None:
97-
self.status = "Error"
110+
self.StepStatus = StepStatus.ERROR
98111
LOGGER.error(
99112
T("csm-orc.orchestrator.core.step.template_not_found").format(
100113
step_id=self.display_id, command_id=self.display_command_id
@@ -119,13 +132,13 @@ def __load_command_from_library(self):
119132

120133
def __post_init__(self, stop_library_load):
121134
if not bool(self.command) ^ bool(self.commandId):
122-
self.status = "Error"
135+
self.status = StepStatus.ERROR
123136
raise ValueError(T("csm-orc.orchestrator.core.step.command_required"))
124137
tmp_env = dict()
125138
for k, v in self.environment.items():
126139
tmp_env[k] = EnvironmentVariable(k, **v)
127140
self.environment = tmp_env
128-
self.status = "Init"
141+
self.status = StepStatus.INITIALIZED
129142
self.display_id = self.id
130143
if self.commandId and not stop_library_load:
131144
self.__load_command_from_library()
@@ -185,26 +198,30 @@ def run(self, dry: bool = False, previous=None, input_data: dict = None, as_exit
185198
step_type = "exit handler"
186199

187200
LOGGER.info(T("csm-orc.orchestrator.core.step.starting").format(step_type=step_type, step_id=self.display_id))
188-
self.status = "Ready"
189201

190-
if isinstance(previous, dict) and any(map(lambda a: a not in ["Done", "DryRun"], previous.values())):
202+
if isinstance(previous, dict) and any(
203+
map(
204+
lambda a: a not in [StepStatus.SUCCESS, StepStatus.DRY_RUN, StepStatus.SKIPPED_BY_USER],
205+
previous.values(),
206+
)
207+
):
191208
LOGGER.warning(
192209
T("csm-orc.orchestrator.core.step.skipping_previous_errors").format(
193210
step_type=step_type, step_id=self.display_id
194211
)
195212
)
196-
self.status = "Skipped"
213+
self.status = StepStatus.SKIPPED_AFTER_FAILURE
197214

198-
if self.status == "Ready":
215+
if self.status == StepStatus.INITIALIZED:
199216
if self.skipped:
200217
LOGGER.info(
201218
T("csm-orc.orchestrator.core.step.skipping_as_required").format(
202219
step_type=step_type, step_id=self.display_id
203220
)
204221
)
205-
self.status = "Done"
222+
self.status = StepStatus.SKIPPED_BY_USER
206223
elif dry:
207-
self.status = "DryRun"
224+
self.status = StepStatus.DRY_RUN
208225
else:
209226
# Set up environment with input data
210227
_e = self._effective_env()
@@ -355,7 +372,7 @@ def run(self, dry: bool = False, previous=None, input_data: dict = None, as_exit
355372
step_type=step_type, step_id=self.display_id
356373
)
357374
)
358-
self.status = "Done"
375+
self.status = StepStatus.SUCCESS
359376

360377
except subprocess.CalledProcessError as e:
361378
LOGGER.error(
@@ -364,7 +381,7 @@ def run(self, dry: bool = False, previous=None, input_data: dict = None, as_exit
364381
)
365382
)
366383
LOGGER.error(str(e))
367-
self.status = "RunError"
384+
self.status = StepStatus.ERROR
368385

369386
return self.status
370387

@@ -379,9 +396,9 @@ def check_env(self):
379396
def simple_repr(self):
380397
if self.description:
381398
return T("csm-orc.orchestrator.core.step.info.simple_repr").format(
382-
id=self.id, status=self.status, description=self.description
399+
id=self.id, status=self.status.name, description=self.description
383400
)
384-
return T("csm-orc.orchestrator.core.step.info.simple_repr_no_desc").format(id=self.id, status=self.status)
401+
return T("csm-orc.orchestrator.core.step.info.simple_repr_no_desc").format(id=self.id, status=self.status.name)
385402

386403
def __str__(self):
387404
r = list()
@@ -406,5 +423,5 @@ def __str__(self):
406423
r.append(T("csm-orc.orchestrator.core.step.info.use_system_env"))
407424
if self.skipped:
408425
r.append(T("csm-orc.orchestrator.core.step.info.skipped"))
409-
r.append(T("csm-orc.orchestrator.core.step.info.status").format(status=self.status))
426+
r.append(T("csm-orc.orchestrator.core.step.info.status").format(status=self.status.name))
410427
return "\n".join(r)

tests/unit/orchestrator/api/test_run.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from cosmotech.orchestrator.api.run import generate_env_file
99
from cosmotech.orchestrator.api.run import run_template
1010
from cosmotech.orchestrator.api.run import validate_template
11+
from cosmotech.orchestrator.core.step import StepStatus
1112

1213

1314
class TestValidateTemplate:
@@ -151,11 +152,11 @@ def test_successful_run_with_all_steps_done(self, mock_orchestrator_class):
151152

152153
# Create mock steps and graph
153154
mock_step1 = MagicMock()
154-
mock_step1.status = "Done"
155+
mock_step1.status = StepStatus.SUCCESS
155156
mock_step1.simple_repr.return_value = "Step 1 Done"
156157

157158
mock_step2 = MagicMock()
158-
mock_step2.status = "Done"
159+
mock_step2.status = StepStatus.SUCCESS
159160
mock_step2.simple_repr.return_value = "Step 2 Done"
160161

161162
mock_graph = MagicMock()
@@ -185,11 +186,11 @@ def test_run_with_error_step(self, mock_orchestrator_class):
185186

186187
# Create mock steps and graph
187188
mock_step1 = MagicMock()
188-
mock_step1.status = "Done"
189+
mock_step1.status = StepStatus.SUCCESS
189190
mock_step1.simple_repr.return_value = "Step 1 Done"
190191

191192
mock_step2 = MagicMock()
192-
mock_step2.status = "RunError"
193+
mock_step2.status = StepStatus.ERROR
193194
mock_step2.simple_repr.return_value = "Step 2 Error"
194195

195196
mock_graph = MagicMock()
@@ -221,7 +222,7 @@ def test_run_with_exit_handlers(self, mock_step_class, mock_library_class, mock_
221222

222223
# Create mock steps and graph
223224
mock_step1 = MagicMock()
224-
mock_step1.status = "Done"
225+
mock_step1.status = StepStatus.SUCCESS
225226
mock_step1.simple_repr.return_value = "Step 1 Done"
226227

227228
mock_graph = MagicMock()
@@ -262,7 +263,7 @@ def test_run_with_dry_run_flag(self, mock_orchestrator_class):
262263

263264
# Create mock steps and graph
264265
mock_step1 = MagicMock()
265-
mock_step1.status = "DryRun"
266+
mock_step1.status = StepStatus.DRY_RUN
266267
mock_step1.simple_repr.return_value = "Step 1 DryRun"
267268

268269
mock_graph = MagicMock()
@@ -287,7 +288,7 @@ def test_run_with_display_env_flag(self, mock_orchestrator_class):
287288

288289
# Create mock steps and graph
289290
mock_step1 = MagicMock()
290-
mock_step1.status = "Done"
291+
mock_step1.status = StepStatus.SUCCESS
291292
mock_step1.simple_repr.return_value = "Step 1 Done"
292293

293294
mock_graph = MagicMock()
@@ -312,11 +313,11 @@ def test_run_with_skipped_steps(self, mock_orchestrator_class):
312313

313314
# Create mock steps and graph
314315
mock_step1 = MagicMock()
315-
mock_step1.status = "Done"
316+
mock_step1.status = StepStatus.SUCCESS
316317
mock_step1.simple_repr.return_value = "Step 1 Done"
317318

318319
mock_step2 = MagicMock()
319-
mock_step2.status = "Skipped"
320+
mock_step2.status = StepStatus.SKIPPED_BY_USER
320321
mock_step2.simple_repr.return_value = "Step 2 Skipped"
321322

322323
mock_graph = MagicMock()

tests/unit/orchestrator/core/test_step.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import sys
99
from pathlib import Path
1010

11-
from cosmotech.orchestrator.core.step import Step
11+
from cosmotech.orchestrator.core.step import Step, StepStatus
1212
from cosmotech.orchestrator.core.environment import EnvironmentVariable
1313
from cosmotech.orchestrator.templates.library import Library
1414

@@ -203,8 +203,8 @@ def test_run_successful_command(self, mock_remove, mock_popen, mock_temp_file):
203203
result = step.run()
204204

205205
# Verify
206-
assert result == "Done"
207-
assert step.status == "Done"
206+
assert result == StepStatus.SUCCESS
207+
assert step.status == StepStatus.SUCCESS
208208
assert "output1" in step.captured_output
209209
assert step.captured_output["output1"] == "value1"
210210
mock_temp_file.assert_called_once()
@@ -233,8 +233,8 @@ def test_run_command_with_error(self, mock_remove, mock_popen, mock_temp_file):
233233
result = step.run()
234234

235235
# Verify
236-
assert result == "RunError"
237-
assert step.status == "RunError"
236+
assert result == StepStatus.ERROR
237+
assert step.status == StepStatus.ERROR
238238
mock_temp_file.assert_called_once()
239239
mock_popen.assert_called_once()
240240
mock_remove.assert_called_once_with("/tmp/test_file")
@@ -247,8 +247,8 @@ def test_run_with_dry_run(self):
247247
result = step.run(dry=True)
248248

249249
# Verify
250-
assert result == "DryRun"
251-
assert step.status == "DryRun"
250+
assert result == StepStatus.DRY_RUN
251+
assert step.status == StepStatus.DRY_RUN
252252

253253
def test_run_with_skipped_step(self):
254254
# Setup
@@ -259,19 +259,19 @@ def test_run_with_skipped_step(self):
259259
result = step.run()
260260

261261
# Verify
262-
assert result == "Done"
263-
assert step.status == "Done"
262+
assert result == StepStatus.SKIPPED_BY_USER
263+
assert step.status == StepStatus.SKIPPED_BY_USER
264264

265265
def test_run_with_previous_errors(self):
266266
# Setup
267267
step = Step(id="test-step", command="echo", arguments=["Hello", "World"])
268268

269269
# Execute
270-
result = step.run(previous={"step1": "RunError"})
270+
result = step.run(previous={"step1": StepStatus.ERROR})
271271

272272
# Verify
273-
assert result == "Skipped"
274-
assert step.status == "Skipped"
273+
assert result == StepStatus.SKIPPED_AFTER_FAILURE
274+
assert step.status == StepStatus.SKIPPED_AFTER_FAILURE
275275

276276
@patch("tempfile.NamedTemporaryFile")
277277
@patch("subprocess.Popen")
@@ -300,7 +300,7 @@ def test_run_with_input_data(self, mock_remove, mock_popen, mock_temp_file):
300300
result = step.run(input_data={"input1": "input_value1"})
301301

302302
# Verify
303-
assert result == "Done"
303+
assert result == StepStatus.SUCCESS
304304
mock_popen.assert_called_once()
305305
# Check that environment variables were set correctly
306306
env = mock_popen.call_args[1]["env"]
@@ -352,7 +352,7 @@ def test_run_with_default_input_value(self, mock_remove, mock_popen, mock_temp_f
352352
result = step.run(input_data={})
353353

354354
# Verify
355-
assert result == "Done"
355+
assert result == StepStatus.SUCCESS
356356
# Check that environment variables were set correctly
357357
env = mock_popen.call_args[1]["env"]
358358
assert "INPUT_VAR1" in env
@@ -397,14 +397,14 @@ def test_check_env_returns_empty_dict_for_skipped_step(self):
397397
def test_simple_repr(self):
398398
# Setup
399399
step = Step(id="test-step", command="echo", description="Test step")
400-
step.status = "Done"
400+
step.status = StepStatus.SUCCESS
401401

402402
# Execute
403403
result = step.simple_repr()
404404

405405
# Verify
406406
assert "test-step" in result
407-
assert "Done" in result
407+
assert StepStatus.SUCCESS.name in result
408408
assert "Test step" in result
409409

410410
def test_str_representation(self):
@@ -417,7 +417,7 @@ def test_str_representation(self):
417417
environment={"TEST_VAR": {"value": "test_value", "description": "Test var"}},
418418
useSystemEnvironment=True,
419419
)
420-
step.status = "Done"
420+
step.status = StepStatus.SUCCESS
421421

422422
# Execute
423423
result = str(step)
@@ -428,7 +428,7 @@ def test_str_representation(self):
428428
assert "Test step" in result
429429
assert "TEST_VAR" in result
430430
assert "Test var" in result
431-
assert "Done" in result
431+
assert StepStatus.SUCCESS.name in result
432432

433433

434434
class TestOutputParser:

0 commit comments

Comments
 (0)