Skip to content

Commit e237bf1

Browse files
Merge pull request #222 from dotflow-io/feature/205
🪲 BUG-#205: Fix PR #202 review issues — duration, executor leak, checkpoint, docstring
2 parents f851f09 + a11a024 commit e237bf1

4 files changed

Lines changed: 32 additions & 26 deletions

File tree

dotflow/abc/flow.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from abc import ABC, abstractmethod
44
from uuid import UUID
55

6-
from dotflow.core.context import Context
76
from dotflow.core.task import Task
87
from dotflow.core.types import TypeStatus
98

@@ -43,23 +42,20 @@ def _flow_callback(self, task: Task) -> None:
4342
def run(self) -> None:
4443
return None
4544

46-
def _has_checkpoint(self, task: Task) -> bool:
45+
def _try_restore_checkpoint(self, task: Task):
46+
"""Attempts to restore a checkpoint. Returns the Context if found, None otherwise."""
4747
if not self.resume:
48-
return False
48+
return None
4949

5050
context = task.config.storage.get(
5151
key=task.config.storage.key(task=task)
5252
)
5353

54-
return context.storage is not None
55-
56-
def _restore_checkpoint(self, task: Task) -> Context:
57-
previous_context = task.config.storage.get(
58-
key=task.config.storage.key(task=task)
59-
)
54+
if context.storage is None:
55+
return None
6056

6157
task.status = TypeStatus.COMPLETED
62-
task.current_context = previous_context
58+
task.current_context = context
6359
self._flow_callback(task=task)
6460

65-
return previous_context
61+
return context

dotflow/core/action.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ def is_execution_with_class_internal_error(error: Exception) -> bool:
1919

2020

2121
class Action:
22-
"""
22+
"""Decorator for creating task steps.
23+
24+
Accepts configuration for retry, timeout, retry_delay, and backoff,
25+
which are stored as attributes and consumed by TaskEngine during execution.
26+
2327
Import:
2428
You can import the **action** decorator directly from dotflow:
2529
@@ -40,7 +44,6 @@ def my_task():
4044
def my_task():
4145
print("task")
4246
43-
4447
With Timeout
4548
4649
@action(timeout=60)
@@ -59,6 +62,10 @@ def my_task():
5962
def my_task():
6063
print("task")
6164
65+
Note:
66+
Retry, timeout, and backoff are enforced by TaskEngine.execute_with_retry(),
67+
not by Action itself. Action stores the configuration as attributes.
68+
6269
Args:
6370
func (Callable):
6471

dotflow/core/engine.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,15 @@ def start(self):
7878
self.task.current_context = None
7979
self.task.status = TypeStatus.FAILED
8080
else:
81-
self.task.duration = (
82-
datetime.now() - self._start_time
83-
).total_seconds()
8481
if self.task.status in (
8582
TypeStatus.IN_PROGRESS,
8683
TypeStatus.RETRY,
8784
):
8885
self.task.status = TypeStatus.COMPLETED
8986
finally:
87+
self.task.duration = (
88+
datetime.now() - self._start_time
89+
).total_seconds()
9090
self.task.config.tracer.end_task(task=self.task)
9191

9292
def execute(self):
@@ -172,15 +172,15 @@ def _execute_with_timeout(self, seconds: int):
172172
return future.result(timeout=seconds)
173173
except TimeoutError:
174174
future.cancel()
175-
executor.shutdown(wait=False, cancel_futures=True)
176-
raise
177-
except Exception:
178-
executor.shutdown(wait=False)
179175
raise
176+
finally:
177+
executor.shutdown(wait=False, cancel_futures=True)
180178

181179
@staticmethod
182180
def _is_class_internal_error(error: Exception) -> bool:
183181
"""Checks if an error is an internal class execution error."""
182+
if isinstance(error, ExecutionWithClassError):
183+
return True
184184
message = str(error)
185185
patterns = [
186186
"initial_context",

dotflow/core/workflow.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,9 @@ def run(self) -> None:
218218
previous_context = Context(workflow_id=self.workflow_id)
219219

220220
for task in self.tasks:
221-
if self._has_checkpoint(task):
222-
previous_context = self._restore_checkpoint(task)
221+
restored = self._try_restore_checkpoint(task)
222+
if restored:
223+
previous_context = restored
223224
continue
224225

225226
engine = TaskEngine(
@@ -302,8 +303,9 @@ def _run_group(self, groups: list[Task]) -> None:
302303
previous_context = Context(workflow_id=self.workflow_id)
303304

304305
for task in groups:
305-
if self._has_checkpoint(task):
306-
previous_context = self._restore_checkpoint(task)
306+
restored = self._try_restore_checkpoint(task)
307+
if restored:
308+
previous_context = restored
307309
continue
308310

309311
engine = TaskEngine(
@@ -344,8 +346,9 @@ def _run_sequential(self) -> None:
344346
previous_context = Context(workflow_id=self.workflow_id)
345347

346348
for task in self.tasks:
347-
if self._has_checkpoint(task):
348-
previous_context = self._restore_checkpoint(task)
349+
restored = self._try_restore_checkpoint(task)
350+
if restored:
351+
previous_context = restored
349352
continue
350353

351354
engine = TaskEngine(

0 commit comments

Comments
 (0)