Skip to content

Commit 8c69faf

Browse files
Progress bar response to job_client.find() (#18)
* Rename progress bars to progress indicators * Interactive job display
1 parent e96f916 commit 8c69faf

File tree

22 files changed

+1142
-400
lines changed

22 files changed

+1142
-400
lines changed

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.44.0] - 2025-09-18
11+
12+
### Added
13+
14+
- `tilebox-workflows`: Added `progress_indicators` to the query response of `JobClient.find` to provide programmatic
15+
access to a job's progress indicators.
16+
- `tilebox-workflows`: Added an `ipywidgets` based interactive display for Job objects for interactive environments like
17+
Jupyter notebooks.
18+
1019
## [0.43.0] - 2025-09-12
1120

21+
### Added
22+
1223
- `tilebox-workflows`: Added progress tracking support to the `TaskRunner`.
1324

1425
## [0.42.0] - 2025-08-22
@@ -255,7 +266,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
255266
- Released under the [MIT](https://opensource.org/license/mit) license.
256267
- Released packages: `tilebox-datasets`, `tilebox-workflows`, `tilebox-storage`, `tilebox-grpc`
257268

258-
[Unreleased]: https://github.com/tilebox/tilebox-python/compare/v0.43.0...HEAD
269+
[Unreleased]: https://github.com/tilebox/tilebox-python/compare/v0.44.0...HEAD
270+
[0.44.0]: https://github.com/tilebox/tilebox-python/compare/v0.43.0...v0.44.0
259271
[0.43.0]: https://github.com/tilebox/tilebox-python/compare/v0.42.0...v0.43.0
260272
[0.42.0]: https://github.com/tilebox/tilebox-python/compare/v0.41.0...v0.42.0
261273
[0.41.0]: https://github.com/tilebox/tilebox-python/compare/v0.40.0...v0.41.0

tilebox-workflows/pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ dependencies = [
3030
"tenacity>=8",
3131
"boto3>=1.33",
3232
"boto3-stubs[essential]>=1.33",
33+
"ipywidgets>=8.1.7",
34+
"python-dateutil>=2.9.0.post0",
3335
]
3436

3537
[dependency-groups]

tilebox-workflows/tests/runner/test_runner.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from tilebox.workflows import ExecutionContext, Task
1010
from tilebox.workflows.cache import InMemoryCache, JobCache
1111
from tilebox.workflows.client import Client
12-
from tilebox.workflows.data import JobState, ProgressBar, RunnerContext
12+
from tilebox.workflows.data import JobState, ProgressIndicator, RunnerContext
1313
from tilebox.workflows.runner.task_runner import TaskRunner
1414

1515

@@ -147,7 +147,7 @@ def test_runner_with_workflow_tracking_progress() -> None:
147147
runner.run_all()
148148
job = job_client.find(job) # load current job state
149149
assert job.state == JobState.COMPLETED
150-
assert job.progress_bars == [ProgressBar("test", 4, 4)]
150+
assert job.progress == [ProgressIndicator("test", 4, 4)]
151151

152152

153153
def replay_client(replay_file: str, assert_request_matches: bool = True) -> Client:

tilebox-workflows/tests/tasks_data.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
Idling,
3232
Job,
3333
JobState,
34-
ProgressBar,
34+
ProgressIndicator,
3535
StorageEventTrigger,
3636
StorageLocation,
3737
StorageType,
@@ -59,12 +59,12 @@ def clusters(draw: DrawFn) -> Cluster:
5959

6060

6161
@composite
62-
def progress_bars(draw: DrawFn) -> ProgressBar:
63-
"""A hypothesis strategy for generating random progress_bars"""
62+
def progress_indicators(draw: DrawFn) -> ProgressIndicator:
63+
"""A hypothesis strategy for generating random progress indicators"""
6464
label = draw(one_of(alphanumerical_text(), none()))
6565
total = draw(integers(min_value=50, max_value=1000))
6666
done = draw(integers(min_value=0, max_value=total))
67-
return ProgressBar(label, total, done)
67+
return ProgressIndicator(label, total, done)
6868

6969

7070
@composite
@@ -134,7 +134,7 @@ def jobs(draw: DrawFn, canceled: bool | None = None) -> Job:
134134
if canceled is None:
135135
canceled = draw(booleans())
136136

137-
progress = draw(lists(progress_bars(), min_size=0, max_size=3))
137+
progress = draw(lists(progress_indicators(), min_size=0, max_size=3))
138138

139139
return Job(
140140
job_id,
@@ -167,7 +167,7 @@ def computed_tasks(draw: DrawFn) -> ComputedTask:
167167
task_id = draw(uuids(version=4))
168168
display = draw(alphanumerical_text())
169169
subtasks: list[TaskSubmission] = draw(lists(task_submissions(), min_size=1, max_size=10))
170-
progress_updates = draw(lists(progress_bars(), min_size=0, max_size=3))
170+
progress_updates = draw(lists(progress_indicators(), min_size=0, max_size=3))
171171

172172
return ComputedTask(task_id, display, subtasks, progress_updates)
173173

tilebox-workflows/tests/test_data.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
computed_tasks,
77
idling_responses,
88
jobs,
9-
progress_bars,
9+
progress_indicators,
1010
storage_locations,
1111
task_identifiers,
1212
task_leases,
@@ -19,7 +19,7 @@
1919
ComputedTask,
2020
Idling,
2121
Job,
22-
ProgressBar,
22+
ProgressIndicator,
2323
StorageLocation,
2424
Task,
2525
TaskIdentifier,
@@ -33,9 +33,9 @@ def test_task_identifiers_to_message_and_back(task_id: TaskIdentifier) -> None:
3333
assert TaskIdentifier.from_message(task_id.to_message()) == task_id
3434

3535

36-
@given(progress_bars())
37-
def test_progress_bars_to_message_and_back(progress_bar: ProgressBar) -> None:
38-
assert ProgressBar.from_message(progress_bar.to_message()) == progress_bar
36+
@given(progress_indicators())
37+
def test_progress_indicators_to_message_and_back(progress_indicator: ProgressIndicator) -> None:
38+
assert ProgressIndicator.from_message(progress_indicator.to_message()) == progress_indicator
3939

4040

4141
@given(tasks())

tilebox-workflows/tilebox/workflows/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
from loguru import logger
55

66
from tilebox.workflows.client import Client
7+
from tilebox.workflows.data import Job
78
from tilebox.workflows.task import ExecutionContext, Task
89

9-
__all__ = ["Client", "ExecutionContext", "Task"]
10+
__all__ = ["Client", "ExecutionContext", "Job", "Task"]
1011

1112

1213
def _init_logging(level: str = "INFO") -> None:

tilebox-workflows/tilebox/workflows/data.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import re
22
import warnings
3+
from collections.abc import Callable
34
from dataclasses import dataclass, field
45
from datetime import datetime, timedelta
56
from enum import Enum
67
from functools import lru_cache
78
from pathlib import Path
9+
from typing import Any
810
from uuid import UUID
911

1012
import boto3
@@ -108,19 +110,19 @@ def to_message(self) -> core_pb2.TaskLease:
108110

109111

110112
@dataclass(order=True)
111-
class ProgressBar:
113+
class ProgressIndicator:
112114
label: str | None
113115
total: int
114116
done: int
115117

116118
@classmethod
117-
def from_message(cls, progress_bar: core_pb2.ProgressBar) -> "ProgressBar":
118-
"""Convert a ProgressBar protobuf message to a ProgressBar object."""
119-
return cls(label=progress_bar.label or None, total=progress_bar.total, done=progress_bar.done)
119+
def from_message(cls, progress_indicator: core_pb2.Progress) -> "ProgressIndicator":
120+
"""Convert a ProgressIndicator protobuf message to a ProgressIndicator object."""
121+
return cls(label=progress_indicator.label or None, total=progress_indicator.total, done=progress_indicator.done)
120122

121-
def to_message(self) -> core_pb2.ProgressBar:
122-
"""Convert a ProgressBar object to a ProgressBar protobuf message."""
123-
return core_pb2.ProgressBar(label=self.label, total=self.total, done=self.done)
123+
def to_message(self) -> core_pb2.Progress:
124+
"""Convert a ProgressIndicator object to a ProgressIndicator protobuf message."""
125+
return core_pb2.Progress(label=self.label, total=self.total, done=self.done)
124126

125127

126128
@dataclass(order=True)
@@ -195,7 +197,7 @@ class JobState(Enum):
195197
_JOB_STATES = {state.value: state for state in JobState}
196198

197199

198-
@dataclass(order=True)
200+
@dataclass(order=True, frozen=True)
199201
class Job:
200202
id: UUID
201203
name: str
@@ -204,10 +206,12 @@ class Job:
204206
submitted_at: datetime
205207
started_at: datetime | None
206208
canceled: bool
207-
progress_bars: list[ProgressBar]
209+
progress: list[ProgressIndicator]
208210

209211
@classmethod
210-
def from_message(cls, job: core_pb2.Job) -> "Job": # lets use typing.Self once we require python >= 3.11
212+
def from_message(
213+
cls, job: core_pb2.Job, **extra_kwargs: Any
214+
) -> "Job": # lets use typing.Self once we require python >= 3.11
211215
"""Convert a Job protobuf message to a Job object."""
212216
return cls(
213217
id=uuid_message_to_uuid(job.id),
@@ -217,7 +221,8 @@ def from_message(cls, job: core_pb2.Job) -> "Job": # lets use typing.Self once
217221
submitted_at=timestamp_to_datetime(job.submitted_at),
218222
started_at=timestamp_to_datetime(job.started_at) if job.HasField("started_at") else None,
219223
canceled=job.canceled,
220-
progress_bars=[ProgressBar.from_message(progress_bar) for progress_bar in job.progress_bars],
224+
progress=[ProgressIndicator.from_message(progress) for progress in job.progress],
225+
**extra_kwargs,
221226
)
222227

223228
def to_message(self) -> core_pb2.Job:
@@ -230,7 +235,7 @@ def to_message(self) -> core_pb2.Job:
230235
submitted_at=datetime_to_timestamp(self.submitted_at),
231236
started_at=datetime_to_timestamp(self.started_at) if self.started_at else None,
232237
canceled=self.canceled,
233-
progress_bars=[progress_bar.to_message() for progress_bar in self.progress_bars],
238+
progress=[progress.to_message() for progress in self.progress],
234239
)
235240

236241

@@ -303,7 +308,7 @@ class ComputedTask:
303308
id: UUID
304309
display: str | None
305310
sub_tasks: list[TaskSubmission]
306-
progress_updates: list[ProgressBar]
311+
progress_updates: list[ProgressIndicator]
307312

308313
@classmethod
309314
def from_message(cls, computed_task: task_pb2.ComputedTask) -> "ComputedTask":
@@ -312,7 +317,7 @@ def from_message(cls, computed_task: task_pb2.ComputedTask) -> "ComputedTask":
312317
id=uuid_message_to_uuid(computed_task.id),
313318
display=computed_task.display,
314319
sub_tasks=[TaskSubmission.from_message(sub_task) for sub_task in computed_task.sub_tasks],
315-
progress_updates=[ProgressBar.from_message(progress) for progress in computed_task.progress_updates],
320+
progress_updates=[ProgressIndicator.from_message(progress) for progress in computed_task.progress_updates],
316321
)
317322

318323
def to_message(self) -> task_pb2.ComputedTask:
@@ -571,9 +576,13 @@ class QueryJobsResponse:
571576
next_page: Pagination
572577

573578
@classmethod
574-
def from_message(cls, page: job_pb2.QueryJobsResponse) -> "QueryJobsResponse":
579+
def from_message(
580+
cls,
581+
page: job_pb2.QueryJobsResponse,
582+
job_factory: Callable[[core_pb2.Job], Job] = Job.from_message,
583+
) -> "QueryJobsResponse":
575584
return cls(
576-
jobs=[Job.from_message(job) for job in page.jobs],
585+
jobs=[job_factory(job) for job in page.jobs],
577586
next_page=Pagination.from_message(page.next_page),
578587
)
579588

tilebox-workflows/tilebox/workflows/formatting/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)