Skip to content

Commit d7d8155

Browse files
Disallow registering duplicate task identifiers (#14)
1 parent af7ff94 commit d7d8155

3 files changed

Lines changed: 71 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- `tilebox-storage`: Added `quicklook` and `download_quicklook` methods to the `CopernicusStorageClient` to download and
1616
display preview images for Sentinel data.
1717

18+
### Fixed
19+
20+
- `tilebox-workflows`: Registering duplicate task identifiers with a task runner now raises a `ValueError` instead of
21+
overwriting the existing task.
22+
1823
## [0.41.0] - 2025-08-01
1924

2025
### Added

tilebox-workflows/tests/runner/test_runner.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import os
22
from pathlib import Path
3-
from unittest.mock import patch
3+
from unittest.mock import MagicMock, patch
4+
5+
import pytest
46

57
from _tilebox.grpc.replay import open_recording_channel, open_replay_channel
68
from tilebox.workflows import ExecutionContext, Task
79
from tilebox.workflows.cache import InMemoryCache, JobCache
810
from tilebox.workflows.client import Client
9-
from tilebox.workflows.data import JobState
11+
from tilebox.workflows.data import JobState, RunnerContext
12+
from tilebox.workflows.runner.task_runner import TaskRunner
1013

1114

1215
def int_to_bytes(n: int) -> bytes:
@@ -143,3 +146,59 @@ def record_client(recording_file: str) -> Client:
143146
open_channel_mock.assert_called_once()
144147

145148
return client
149+
150+
151+
class ExplicitIdentifierTaskV1(Task):
152+
@classmethod
153+
def identifier(cls) -> tuple[str, str]:
154+
return "tilebox.com/explicit", "v1.0"
155+
156+
def execute(self, context: ExecutionContext) -> None:
157+
pass
158+
159+
160+
class ExplicitIdentifierTaskV2(Task):
161+
@classmethod
162+
def identifier(cls) -> tuple[str, str]:
163+
return "tilebox.com/explicit", "v2.0"
164+
165+
def execute(self, context: ExecutionContext) -> None:
166+
pass
167+
168+
169+
def test_runner_disallow_duplicate_task_identifiers() -> None:
170+
runner = TaskRunner(
171+
MagicMock(),
172+
"dummy-cluster",
173+
InMemoryCache(),
174+
None,
175+
None,
176+
MagicMock(),
177+
RunnerContext(),
178+
)
179+
180+
runner.register(FlakyTask)
181+
with pytest.raises(
182+
ValueError, match="Duplicate task identifier: A task 'FlakyTask' with version 'v0.0' is already registered."
183+
):
184+
runner.register(FlakyTask)
185+
186+
runner.register(SumResultTask)
187+
with pytest.raises(
188+
ValueError, match="Duplicate task identifier: A task 'SumResultTask' with version 'v0.0' is already registered."
189+
):
190+
runner.register(SumResultTask)
191+
192+
runner.register(ExplicitIdentifierTaskV1)
193+
with pytest.raises(
194+
ValueError,
195+
match="Duplicate task identifier: A task 'tilebox.com/explicit' with version 'v1.0' is already registered.",
196+
):
197+
runner.register(ExplicitIdentifierTaskV1)
198+
199+
runner.register(ExplicitIdentifierTaskV2) # this one has a different version, so it's fine
200+
with pytest.raises(
201+
ValueError,
202+
match="Duplicate task identifier: A task 'tilebox.com/explicit' with version 'v2.0' is already registered.",
203+
):
204+
runner.register(ExplicitIdentifierTaskV2)

tilebox-workflows/tilebox/workflows/runner/task_runner.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,11 @@ def register(self, task: type) -> None:
317317
f"Task {task_repr} is not executable. It must have an execute method in order to "
318318
f"register it with a task runner."
319319
)
320+
if meta.identifier in self.tasks_to_run.identifiers:
321+
raise ValueError(
322+
f"Duplicate task identifier: A task '{meta.identifier.name}' with version '{meta.identifier.version}' "
323+
f"is already registered."
324+
)
320325
self.tasks_to_run.identifiers[meta.identifier] = task
321326

322327
def add_interceptor(self, interceptor: InterceptorType) -> None:

0 commit comments

Comments
 (0)