Skip to content

Commit 2a2d782

Browse files
authored
Merge pull request #11 from VachaLab/v0.12
v0.12
2 parents 5255da8 + 24542c2 commit 2a2d782

55 files changed

Lines changed: 3487 additions & 946 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@ jobs:
1212
runs-on: [self-hosted, Linux, X64]
1313

1414
steps:
15+
- name: Clean workspace
16+
run: |
17+
rm -rf "$GITHUB_WORKSPACE/.git"
18+
1519
- uses: actions/checkout@v5
1620
with:
1721
fetch-depth: 0
22+
ref: ${{ github.event.release.tag_name }}
1823

1924
- name: Fix git ownership
2025
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@ htmlcov/
1818
docs/
1919

2020
# Version
21-
src/qq_lib/_version.py
21+
src/qq_lib/_version.py
22+
23+
# Playground
24+
src/_playground*.py

.readthedocs.yaml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
version: 2
22

33
build:
4-
os: ubuntu-22.04
5-
tools:
6-
python: "3.12"
7-
commands:
8-
- pip install uv
9-
- uv pip install --system pdoc
10-
- uv pip install --system .
11-
- pdoc src/qq_lib -d google -o $READTHEDOCS_OUTPUT/html
4+
os: ubuntu-22.04
5+
tools:
6+
python: "3.13"
7+
commands:
8+
- pip install uv
9+
- uv pip install --system pdoc
10+
- uv pip install --system .
11+
- pdoc src/qq_lib -d google -o $READTHEDOCS_OUTPUT/html

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## Version 0.12.0
2+
### Job collections
3+
- **BREAKING CHANGE**: The `-s` option of `qq info` now corresponds to the `--server` option, not to the `--short` flag, for consistency with other commands. Use `--brief`/`-b` or the legacy long variant `--short` instead to get a brief summary of the job.
4+
- The `qq killall` command has been deprecated in favor of `qq kill --all`, which has very similar behavior. It can still be used but will be completely removed in a future version of qq.
5+
- `qq info`, `qq kill`, `qq sync`, `qq respawn`, `qq wipe`, and `qq go` can now accept one or more directories and operate on jobs within them. Job resolution is parallelized and therefore much faster than calling these commands individually. Some of these commands also accept the `--all` flag to operate on all unfinished jobs on the given batch server.
6+
- `qq submit` now supports submitting multiple scripts at once. Submission is parallelized and therefore much faster than submitting scripts one by one.
7+
- `qq clear` can now clear runtime files from multiple directories at once.
8+
- *For more information about job collections, read [the manual](https://vachalab.github.io/qq-manual/job_collections.html).*
9+
10+
### Other changes
11+
- If a loop job does not create any archive files for the next cycle, `qq` creates an empty `.init` file fulfilling the loop job's requirements.
12+
- If a loop job-specific or continuous job-specific option is used either on the command line or inside the submitted script, a warning is printed to inform the user that the option will be ignored.
13+
14+
***
15+
116
## Version 0.11
217
### qq respawn
318
- Failed or killed jobs can now be easily "respawned" using `qq respawn`. When respawning a job, qq will remove the working directory of the failed job, clear all runtime files, and resubmit the job with the same parameters as before.

scripts/qq_scripts/gmx-eta

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
"""
77
Get the estimated time of a Gromacs simulation finishing.
8-
Version qq 0.11.0.
8+
Version qq 0.12.0.
99
Requires `uv`: https://docs.astral.sh/uv
1010
"""
1111

@@ -16,7 +16,7 @@ Requires `uv`: https://docs.astral.sh/uv
1616
# ]
1717
#
1818
# [tool.uv.sources]
19-
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.11.0" }
19+
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.12.0" }
2020
# ///
2121

2222
import argparse

scripts/qq_scripts/multi-check

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
"""
77
Check the state of qq jobs in multiple directories.
8-
Version qq 0.11.0.
8+
THIS SCRIPT IS DEPRECATED. YOU CAN USE `qq info -d <directories>` INSTEAD.
9+
Version qq 0.12.0.
910
Requires `uv`: https://docs.astral.sh/uv
1011
"""
1112

@@ -16,7 +17,7 @@ Requires `uv`: https://docs.astral.sh/uv
1617
# ]
1718
#
1819
# [tool.uv.sources]
19-
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.11.0" }
20+
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.12.0" }
2021
# ///
2122

2223
import argparse

scripts/qq_scripts/multi-kill

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
"""
77
Kill qq jobs in multiple directories.
8-
Version qq 0.11.0.
8+
THIS SCRIPT IS DEPRECATED. YOU CAN USE `qq kill -d <directories>` INSTEAD.
9+
Version qq 0.12.0.
910
Requires `uv`: https://docs.astral.sh/uv
1011
"""
1112

@@ -16,7 +17,7 @@ Requires `uv`: https://docs.astral.sh/uv
1617
# ]
1718
#
1819
# [tool.uv.sources]
19-
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.11.0" }
20+
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.12.0" }
2021
# ///
2122

2223
import argparse

scripts/qq_scripts/multi-submit

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
"""
77
Submit qq jobs from multiple directories.
8-
Version qq 0.11.0.
8+
THIS SCRIPT IS DEPRECATED. YOU CAN USE `qq submit <directories>/script.sh` INSTEAD.
9+
Version qq 0.12.0.
910
Requires `uv`: https://docs.astral.sh/uv
1011
"""
1112

@@ -16,7 +17,7 @@ Requires `uv`: https://docs.astral.sh/uv
1617
# ]
1718
#
1819
# [tool.uv.sources]
19-
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.11.0" }
20+
# qq = { git = "https://github.com/VachaLab/qq.git", tag = "v0.12.0" }
2021
# ///
2122

2223
import argparse
@@ -65,7 +66,7 @@ def submit_job(base_submitter: Submitter, directory: str) -> bool:
6566

6667
with suppress(QQError):
6768
# remove runtime files in the directory, if possible
68-
clearer = Clearer(Path(directory))
69+
clearer = Clearer([Path(directory)])
6970
clearer.clear()
7071

7172
# make sure that the directory is suitable for submission

src/qq_lib/archive/archiver.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def from_archive(self, dir: Path, cycle: int | None = None) -> None:
7474
QQError: If file transfer fails.
7575
"""
7676
if not (
77-
files := self._get_files(
77+
files := self.get_files_matching_pattern(
7878
self._archive, self._input_machine, self._archive_format, cycle, False
7979
)
8080
):
@@ -108,7 +108,11 @@ def to_archive(self, dir: Path) -> None:
108108
Raises:
109109
QQError: If file transfer or removal fails.
110110
"""
111-
if not (files := self._get_files(dir, None, self._archive_format, None, False)):
111+
if not (
112+
files := self.get_files_matching_pattern(
113+
dir, None, self._archive_format, None, False
114+
)
115+
):
112116
logger.debug("Nothing to archive.")
113117
return
114118

@@ -153,7 +157,7 @@ def archive_runtime_files(self, job_name: str, cycle: int) -> None:
153157
QQError: If moving the runtime files fails.
154158
"""
155159
if not (
156-
files := self._get_files(
160+
files := self.get_files_matching_pattern(
157161
self._input_dir,
158162
self._input_machine,
159163
# only use the stem of the job name, the extension will not be matched
@@ -183,7 +187,7 @@ def archive_runtime_files(self, job_name: str, cycle: int) -> None:
183187
wait_seconds=CFG.archiver.retry_wait,
184188
).run()
185189

186-
def _get_files(
190+
def get_files_matching_pattern(
187191
self,
188192
directory: Path,
189193
host: str | None,
@@ -205,7 +209,7 @@ def _get_files(
205209
include_qq_files (bool): Whether to include qq runtime files. Defaults to False.
206210
207211
Returns:
208-
list[Path]: A list of absolute paths to matching files.
212+
list[Path]: A list of absolute (logical) paths to matching files.
209213
"""
210214
if cycle and is_printf_pattern(pattern):
211215
try:
@@ -248,6 +252,16 @@ def _get_files(
248252
if regex.search(f.stem) and f.suffix not in CFG.suffixes.all_suffixes
249253
]
250254

255+
def create_init_file(self, cycle: int) -> None:
256+
"""
257+
Create an empty init file for the given cycle.
258+
Used as a fallback when no valid archive file is produced, ensuring
259+
the next iteration of the loop job can proceed normally.
260+
Args:
261+
cycle (int): The index of the next cycle of the loop job.
262+
"""
263+
Path(f"{self._archive_format % cycle}.init").touch()
264+
251265
@staticmethod
252266
def _prepare_regex_pattern(pattern: str) -> re.Pattern[str]:
253267
"""

src/qq_lib/batch/interface/interface.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,29 @@ def get_batch_job(cls, job_id: str) -> TBatchJob:
296296
f"get_batch_job method is not implemented for {cls.__name__}"
297297
)
298298

299+
@classmethod
300+
def get_batch_jobs_from_ids(cls, job_ids: list[str]) -> list[TBatchJob]:
301+
"""
302+
Retrieve information about multiple jobs from the batch system.
303+
304+
Batch jobs should be returned in the same order as they appear in `job_ids`.
305+
A TBatchJob object should be returned for each job id, even if the job
306+
no longer exists or its information is unavailable.
307+
308+
Array jobs should NOT be expanded into their individual tasks.
309+
310+
The default implementation is to call `get_batch_job` for each job id.
311+
This implementation may be inefficient for large numbers of job ids and
312+
should be overriden by subclasses.
313+
314+
Args:
315+
job_ids (list[str]): List of job identifiers.
316+
317+
Returns:
318+
list[TBatchJob]: List of TBatchJob objects, one for each job id.
319+
"""
320+
return [cls.get_batch_job(id) for id in job_ids]
321+
299322
@classmethod
300323
def get_unfinished_batch_jobs(
301324
cls, user: str, server: str | None = None

0 commit comments

Comments
 (0)