Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.44.0] - 2025-09-18

### Added

- `tilebox-workflows`: Added `progress_indicators` to the query response of `JobClient.find` to provide programmatic
access to a job's progress indicators.
- `tilebox-workflows`: Added an `ipywidgets` based interactive display for Job objects for interactive environments like
Jupyter notebooks.

## [0.43.0] - 2025-09-12

### Added

- `tilebox-workflows`: Added progress tracking support to the `TaskRunner`.

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

[Unreleased]: https://github.com/tilebox/tilebox-python/compare/v0.43.0...HEAD
[Unreleased]: https://github.com/tilebox/tilebox-python/compare/v0.44.0...HEAD
[0.44.0]: https://github.com/tilebox/tilebox-python/compare/v0.43.0...v0.44.0
[0.43.0]: https://github.com/tilebox/tilebox-python/compare/v0.42.0...v0.43.0
[0.42.0]: https://github.com/tilebox/tilebox-python/compare/v0.41.0...v0.42.0
[0.41.0]: https://github.com/tilebox/tilebox-python/compare/v0.40.0...v0.41.0
Expand Down
2 changes: 2 additions & 0 deletions tilebox-workflows/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ dependencies = [
"tenacity>=8",
"boto3>=1.33",
"boto3-stubs[essential]>=1.33",
"ipywidgets>=8.1.7",
"python-dateutil>=2.9.0.post0",
]

[dependency-groups]
Expand Down
4 changes: 2 additions & 2 deletions tilebox-workflows/tests/runner/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from tilebox.workflows import ExecutionContext, Task
from tilebox.workflows.cache import InMemoryCache, JobCache
from tilebox.workflows.client import Client
from tilebox.workflows.data import JobState, ProgressBar, RunnerContext
from tilebox.workflows.data import JobState, ProgressIndicator, RunnerContext
from tilebox.workflows.runner.task_runner import TaskRunner


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


def replay_client(replay_file: str, assert_request_matches: bool = True) -> Client:
Expand Down
12 changes: 6 additions & 6 deletions tilebox-workflows/tests/tasks_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
Idling,
Job,
JobState,
ProgressBar,
ProgressIndicator,
StorageEventTrigger,
StorageLocation,
StorageType,
Expand Down Expand Up @@ -59,12 +59,12 @@ def clusters(draw: DrawFn) -> Cluster:


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


@composite
Expand Down Expand Up @@ -134,7 +134,7 @@ def jobs(draw: DrawFn, canceled: bool | None = None) -> Job:
if canceled is None:
canceled = draw(booleans())

progress = draw(lists(progress_bars(), min_size=0, max_size=3))
progress = draw(lists(progress_indicators(), min_size=0, max_size=3))

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

return ComputedTask(task_id, display, subtasks, progress_updates)

Expand Down
10 changes: 5 additions & 5 deletions tilebox-workflows/tests/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
computed_tasks,
idling_responses,
jobs,
progress_bars,
progress_indicators,
storage_locations,
task_identifiers,
task_leases,
Expand All @@ -19,7 +19,7 @@
ComputedTask,
Idling,
Job,
ProgressBar,
ProgressIndicator,
StorageLocation,
Task,
TaskIdentifier,
Expand All @@ -33,9 +33,9 @@ def test_task_identifiers_to_message_and_back(task_id: TaskIdentifier) -> None:
assert TaskIdentifier.from_message(task_id.to_message()) == task_id


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


@given(tasks())
Expand Down
3 changes: 2 additions & 1 deletion tilebox-workflows/tilebox/workflows/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
from loguru import logger

from tilebox.workflows.client import Client
from tilebox.workflows.data import Job
from tilebox.workflows.task import ExecutionContext, Task

__all__ = ["Client", "ExecutionContext", "Task"]
__all__ = ["Client", "ExecutionContext", "Job", "Task"]


def _init_logging(level: str = "INFO") -> None:
Expand Down
41 changes: 25 additions & 16 deletions tilebox-workflows/tilebox/workflows/data.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import re
import warnings
from collections.abc import Callable
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from enum import Enum
from functools import lru_cache
from pathlib import Path
from typing import Any
from uuid import UUID

import boto3
Expand Down Expand Up @@ -108,19 +110,19 @@ def to_message(self) -> core_pb2.TaskLease:


@dataclass(order=True)
class ProgressBar:
class ProgressIndicator:
label: str | None
total: int
done: int

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

def to_message(self) -> core_pb2.ProgressBar:
"""Convert a ProgressBar object to a ProgressBar protobuf message."""
return core_pb2.ProgressBar(label=self.label, total=self.total, done=self.done)
def to_message(self) -> core_pb2.Progress:
"""Convert a ProgressIndicator object to a ProgressIndicator protobuf message."""
return core_pb2.Progress(label=self.label, total=self.total, done=self.done)


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


@dataclass(order=True)
@dataclass(order=True, frozen=True)
class Job:
id: UUID
name: str
Expand All @@ -204,10 +206,12 @@ class Job:
submitted_at: datetime
started_at: datetime | None
canceled: bool
progress_bars: list[ProgressBar]
progress: list[ProgressIndicator]

@classmethod
def from_message(cls, job: core_pb2.Job) -> "Job": # lets use typing.Self once we require python >= 3.11
def from_message(
cls, job: core_pb2.Job, **extra_kwargs: Any
) -> "Job": # lets use typing.Self once we require python >= 3.11
"""Convert a Job protobuf message to a Job object."""
return cls(
id=uuid_message_to_uuid(job.id),
Expand All @@ -217,7 +221,8 @@ def from_message(cls, job: core_pb2.Job) -> "Job": # lets use typing.Self once
submitted_at=timestamp_to_datetime(job.submitted_at),
started_at=timestamp_to_datetime(job.started_at) if job.HasField("started_at") else None,
canceled=job.canceled,
progress_bars=[ProgressBar.from_message(progress_bar) for progress_bar in job.progress_bars],
progress=[ProgressIndicator.from_message(progress) for progress in job.progress],
**extra_kwargs,
)

def to_message(self) -> core_pb2.Job:
Expand All @@ -230,7 +235,7 @@ def to_message(self) -> core_pb2.Job:
submitted_at=datetime_to_timestamp(self.submitted_at),
started_at=datetime_to_timestamp(self.started_at) if self.started_at else None,
canceled=self.canceled,
progress_bars=[progress_bar.to_message() for progress_bar in self.progress_bars],
progress=[progress.to_message() for progress in self.progress],
)


Expand Down Expand Up @@ -303,7 +308,7 @@ class ComputedTask:
id: UUID
display: str | None
sub_tasks: list[TaskSubmission]
progress_updates: list[ProgressBar]
progress_updates: list[ProgressIndicator]

@classmethod
def from_message(cls, computed_task: task_pb2.ComputedTask) -> "ComputedTask":
Expand All @@ -312,7 +317,7 @@ def from_message(cls, computed_task: task_pb2.ComputedTask) -> "ComputedTask":
id=uuid_message_to_uuid(computed_task.id),
display=computed_task.display,
sub_tasks=[TaskSubmission.from_message(sub_task) for sub_task in computed_task.sub_tasks],
progress_updates=[ProgressBar.from_message(progress) for progress in computed_task.progress_updates],
progress_updates=[ProgressIndicator.from_message(progress) for progress in computed_task.progress_updates],
)

def to_message(self) -> task_pb2.ComputedTask:
Expand Down Expand Up @@ -571,9 +576,13 @@ class QueryJobsResponse:
next_page: Pagination

@classmethod
def from_message(cls, page: job_pb2.QueryJobsResponse) -> "QueryJobsResponse":
def from_message(
cls,
page: job_pb2.QueryJobsResponse,
job_factory: Callable[[core_pb2.Job], Job] = Job.from_message,
) -> "QueryJobsResponse":
return cls(
jobs=[Job.from_message(job) for job in page.jobs],
jobs=[job_factory(job) for job in page.jobs],
next_page=Pagination.from_message(page.next_page),
)

Expand Down
Empty file.
Loading
Loading