From a9aed0475b6d392493d8276e74089374c88f5062 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Mon, 29 Jun 2026 21:49:42 +1000 Subject: [PATCH 1/4] refactor!: remove `rich` dependency --- .pre-commit-config.yaml | 1 - CHANGELOG.md | 5 +++ examples/run.py | 1 - pyproject.toml | 2 +- src/pbspy/__init__.py | 83 ++++------------------------------------- 5 files changed, 13 insertions(+), 79 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 18ee742..169dab9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,7 +48,6 @@ repos: always_run: true args: [src, tests, examples] additional_dependencies: - - rich - typer - repo: https://github.com/scientific-python/cookie rev: 2024.04.23 diff --git a/CHANGELOG.md b/CHANGELOG.md index 57619e8..3be8d47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/MaterialsPhysicsANU/pbspy/compare/v0.0.9...HEAD) +### Removed + +- Remove `progress` parameter from `Job` `wait` methods +- Remove `rich` dependency + ## [0.0.9](https://github.com/MaterialsPhysicsANU/pbspy/releases/tag/v0.0.9) - 2026-05-18 ### Added diff --git a/examples/run.py b/examples/run.py index 5d8c612..3db9e21 100644 --- a/examples/run.py +++ b/examples/run.py @@ -1,5 +1,4 @@ import typer -from rich import print from pbspy import Job, JobDescription, QueueLimits diff --git a/pyproject.toml b/pyproject.toml index 9d54d1f..b81fa64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,9 +19,9 @@ classifiers = [ ] license = {text = "MIT License"} dependencies = [ - "rich~=13.8", ] + [dependency-groups] dev = [ "mypy>=1.14.1", diff --git a/src/pbspy/__init__.py b/src/pbspy/__init__.py index e057e38..c616fd6 100644 --- a/src/pbspy/__init__.py +++ b/src/pbspy/__init__.py @@ -11,8 +11,6 @@ from dataclasses import dataclass, field from typing import Any, Self, TypeAlias -from rich.progress import Progress, TextColumn, TimeElapsedColumn - __all__ = ["JobDescription", "Job", "JobResult", "QueueLimits", "QueueLimitsMap", "gadi"] @@ -66,24 +64,7 @@ def _try_get_exit_code(job_id: str) -> int | None: raise RuntimeError("Could not get exit code") -def _pbs_wait_for_jobs(jobs: list[Job], progress: bool = True) -> None: - """ - Waits for the PBS jobs to complete. - - Args: - jobs (list[Job]): A list of Job objects representing the PBS jobs. - progress (bool): Whether or not to print the state of the job and status on exit. - - Returns: - None - """ - if progress: - _pbs_wait_for_jobs_with_progress(jobs) - else: - _pbs_wait_for_jobs_quiet(jobs) - - -def _pbs_wait_for_jobs_quiet(jobs: list[Job]) -> None: +def _pbs_wait_for_jobs(jobs: list[Job]) -> None: """ Waits for the PBS jobs to complete. @@ -111,56 +92,6 @@ def _pbs_wait_for_jobs_quiet(jobs: list[Job]) -> None: # raise RuntimeError(f"Failed to check job status: {process.stderr.decode('utf-8')}") -def _pbs_wait_for_jobs_with_progress(jobs: list[Job]) -> None: - """ - Waits for the PBS jobs to complete. - - Args: - jobs (list[Job]): A list of Job objects representing the PBS jobs. - - Returns: - None - """ - last_check = time.time() - with Progress( - TimeElapsedColumn(), - TextColumn("[progress.description]{task.description}"), - auto_refresh=False, - ) as progress: - tasks = [progress.add_task(f"{job.job_id} {job.job_name} {job.description or ''}", total=1) for job in jobs] - task_done = [False] * len(jobs) - while not all(task_done): - time.sleep(1) - if time.time() - last_check > 60: - last_check = time.time() - for i, (job, task) in enumerate(zip(jobs, tasks, strict=False)): - if task_done[i]: - continue - process = subprocess.run( - ["qstat", job.job_id], - capture_output=True, - ) - if process.returncode != 0: - output = process.stdout.decode("utf-8") - if job.job_id not in output or "has finished" in process.stderr.decode("utf-8"): - task_done[i] = True - progress.advance(task) - progress.update(task, visible=False) - - # Try and get the exit code - exit_code = _try_get_exit_code(job.job_id) - if exit_code is None: - output_status = "[yellow]?" - else: - output_status = "[green]✓" if exit_code == 0 else "[red]✗" - progress.console.print( - f'{output_status} {job.job_id} {job.job_name} {job.description or ""}' - ) - # else: - # raise RuntimeError(f"Failed to check job status: {process.stderr.decode('utf-8')}") - progress.refresh() - - @dataclass class JobResult: """ @@ -196,14 +127,14 @@ class Job: description: str | None = None """A description of the job for progress updates. Unused by PBS.""" - def wait(self, progress: bool = True) -> None: + def wait(self, **kwargs) -> None: """ Wait for the job to complete. Args: progress (bool): Whether or not to print the state of the job and status on exit. """ - _pbs_wait_for_jobs([self], progress=progress) + _pbs_wait_for_jobs([self]) def _result_no_wait(self) -> JobResult: """ @@ -265,24 +196,24 @@ def result(self, progress: bool = True) -> JobResult: return self._result_no_wait() @staticmethod - def wait_all(jobs: list[Job], progress: bool = True) -> None: + def wait_all(jobs: list[Job], **kwargs) -> None: """ Waits for multiple jobs to complete. Args: progress (bool): Whether or not to print the state of the job and status on exit. """ - _pbs_wait_for_jobs(jobs, progress=progress) + _pbs_wait_for_jobs(jobs) @staticmethod - def result_all(jobs: list[Job], progress: bool = True) -> list[JobResult]: + def result_all(jobs: list[Job], **kwargs) -> list[JobResult]: """ Waits for multiple jobs to complete and returns their results. Args: progress (bool): Whether or not to print the state of the job and status on exit. """ - _pbs_wait_for_jobs(jobs, progress=progress) + _pbs_wait_for_jobs(jobs) return [job._result_no_wait() for job in jobs] From 81ac87da099d36a44c0d3cac35805d7a0c1a7ef9 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Mon, 29 Jun 2026 21:53:47 +1000 Subject: [PATCH 2/4] fix: type hints --- src/pbspy/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pbspy/__init__.py b/src/pbspy/__init__.py index c616fd6..4db8372 100644 --- a/src/pbspy/__init__.py +++ b/src/pbspy/__init__.py @@ -127,7 +127,7 @@ class Job: description: str | None = None """A description of the job for progress updates. Unused by PBS.""" - def wait(self, **kwargs) -> None: + def wait(self, **kwargs: dict[str, Any]) -> None: """ Wait for the job to complete. @@ -196,7 +196,7 @@ def result(self, progress: bool = True) -> JobResult: return self._result_no_wait() @staticmethod - def wait_all(jobs: list[Job], **kwargs) -> None: + def wait_all(jobs: list[Job], **kwargs: dict[str, Any]) -> None: """ Waits for multiple jobs to complete. @@ -206,7 +206,7 @@ def wait_all(jobs: list[Job], **kwargs) -> None: _pbs_wait_for_jobs(jobs) @staticmethod - def result_all(jobs: list[Job], **kwargs) -> list[JobResult]: + def result_all(jobs: list[Job], **kwargs: dict[str, Any]) -> list[JobResult]: """ Waits for multiple jobs to complete and returns their results. From e5cb0e8b12276449b3b820eaca9b54eed5248e09 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Mon, 29 Jun 2026 21:54:57 +1000 Subject: [PATCH 3/4] chore: changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3be8d47..6e79238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed -- Remove `progress` parameter from `Job` `wait` methods +- Remove `progress` parameter from `Job` `wait` methods and stop printing job IDs and progress + - Downstream libraries must implement their own progress reporting - Remove `rich` dependency ## [0.0.9](https://github.com/MaterialsPhysicsANU/pbspy/releases/tag/v0.0.9) - 2026-05-18 From a0494957ed9b39fd9f1f11fa55595a38107775d2 Mon Sep 17 00:00:00 2001 From: Lachlan Deakin Date: Mon, 29 Jun 2026 22:01:35 +1000 Subject: [PATCH 4/4] fix: docs and progress --- CHANGELOG.md | 4 ++-- src/pbspy/__init__.py | 16 ++-------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e79238..5d81445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed -- Remove `progress` parameter from `Job` `wait` methods and stop printing job IDs and progress - - Downstream libraries must implement their own progress reporting +- Remove `progress` parameter from the `wait[_all]` and `result[_all]` methods of `Job` + - These methods no longer print job IDs or progress - Remove `rich` dependency ## [0.0.9](https://github.com/MaterialsPhysicsANU/pbspy/releases/tag/v0.0.9) - 2026-05-18 diff --git a/src/pbspy/__init__.py b/src/pbspy/__init__.py index 4db8372..a407654 100644 --- a/src/pbspy/__init__.py +++ b/src/pbspy/__init__.py @@ -130,9 +130,6 @@ class Job: def wait(self, **kwargs: dict[str, Any]) -> None: """ Wait for the job to complete. - - Args: - progress (bool): Whether or not to print the state of the job and status on exit. """ _pbs_wait_for_jobs([self]) @@ -185,23 +182,17 @@ def _result_no_wait(self) -> JobResult: return JobResult(exit_code=exit_code, output=output, error=error, stats=pbs_stats) - def result(self, progress: bool = True) -> JobResult: + def result(self, **kwargs: dict[str, Any]) -> JobResult: """ Waits for the job to complete and returns the result. - - Args: - progress (bool): Whether or not to print the state of the job and status on exit. """ - self.wait(progress=progress) + self.wait() return self._result_no_wait() @staticmethod def wait_all(jobs: list[Job], **kwargs: dict[str, Any]) -> None: """ Waits for multiple jobs to complete. - - Args: - progress (bool): Whether or not to print the state of the job and status on exit. """ _pbs_wait_for_jobs(jobs) @@ -209,9 +200,6 @@ def wait_all(jobs: list[Job], **kwargs: dict[str, Any]) -> None: def result_all(jobs: list[Job], **kwargs: dict[str, Any]) -> list[JobResult]: """ Waits for multiple jobs to complete and returns their results. - - Args: - progress (bool): Whether or not to print the state of the job and status on exit. """ _pbs_wait_for_jobs(jobs) return [job._result_no_wait() for job in jobs]