qq_lib.core.config

Configuration system for qq.

This module defines dataclasses representing all configurable aspects of qq, including file suffixes, environment variables, timeouts, presentation settings, batch-system options, and global defaults.

The Config class loads user configuration from a TOML file (if available) and provides a globally accessible CFG instance.

  1# Released under MIT License.
  2# Copyright (c) 2025-2026 Ladislav Bartos and Robert Vacha Lab
  3
  4"""
  5Configuration system for qq.
  6
  7This module defines dataclasses representing all configurable aspects of qq,
  8including file suffixes, environment variables, timeouts, presentation settings,
  9batch-system options, and global defaults.
 10
 11The `Config` class loads user configuration from a TOML file (if available)
 12and provides a globally accessible `CFG` instance.
 13"""
 14
 15import os
 16import tomllib
 17from dataclasses import dataclass, field, fields, is_dataclass
 18from pathlib import Path
 19from typing import Any, Self
 20
 21
 22@dataclass(frozen=True)
 23class FileSuffixes:
 24    """File suffixes used by qq."""
 25
 26    # Suffix for qq info files.
 27    qq_info: str = ".qqinfo"
 28    # Suffix for qq output files.
 29    qq_out: str = ".qqout"
 30    # Suffix for captured stdout.
 31    stdout: str = ".out"
 32    # Suffix for captured stderr.
 33    stderr: str = ".err"
 34
 35    @property
 36    def all_suffixes(self) -> list[str]:
 37        """List of all file suffixes."""
 38        return [self.qq_info, self.qq_out, self.stdout, self.stderr]
 39
 40
 41@dataclass(frozen=True)
 42class EnvironmentVariables:
 43    """Environment variable names used by qq."""
 44
 45    # Indicates job is running inside the qq environment.
 46    guard: str = "QQ_ENV_SET"
 47    # Enables qq debug mode.
 48    debug_mode: str = "QQ_DEBUG"
 49    # Path to the qq info file for the job.
 50    info_file: str = "QQ_INFO"
 51    # Machine from which the job was submitted.
 52    input_machine: str = "QQ_INPUT_MACHINE"
 53    # Submission directory path.
 54    input_dir: str = "QQ_INPUT_DIR"
 55    # Whether submission was from shared storage.
 56    shared_submit: str = "QQ_SHARED_SUBMIT"
 57    # Name of the batch system used.
 58    batch_system: str = "QQ_BATCH_SYSTEM"
 59    # Current loop-cycle index.
 60    loop_current: str = "QQ_LOOP_CURRENT"
 61    # Starting loop-cycle index.
 62    loop_start: str = "QQ_LOOP_START"
 63    # Final loop-cycle index.
 64    loop_end: str = "QQ_LOOP_END"
 65    # Non-resubmit flag returned by a job script.
 66    no_resubmit: str = "QQ_NO_RESUBMIT"
 67    # Archive filename pattern.
 68    archive_format: str = "QQ_ARCHIVE_FORMAT"
 69    # Scratch directory on Metacentrum clusters.
 70    pbs_scratch_dir: str = "SCRATCHDIR"
 71    # Slurm account used for the job.
 72    slurm_job_account: str = "SLURM_JOB_ACCOUNT"
 73    # Storage type for LUMI scratch.
 74    lumi_scratch_type: str = "LUMI_SCRATCH_TYPE"
 75    # Total CPUs used.
 76    ncpus: str = "QQ_NCPUS"
 77    # Total GPUs used.
 78    ngpus: str = "QQ_NGPUS"
 79    # Total nodes used.
 80    nnodes: str = "QQ_NNODES"
 81    # Walltime in hours.
 82    walltime: str = "QQ_WALLTIME"
 83
 84
 85@dataclass(frozen=True)
 86class TimeoutSettings:
 87    """Timeout settings in seconds."""
 88
 89    # Timeout for SSH in seconds.
 90    ssh: int = 60
 91    # Timeout for rsync in seconds.
 92    rsync: int = 600
 93
 94
 95@dataclass(frozen=True)
 96class RunnerSettings:
 97    """Settings for Runner operations."""
 98
 99    # Maximum number of attempts when retrying an operation.
100    retry_tries: int = 3
101    # Wait time (in seconds) between retry attempts.
102    retry_wait: int = 300
103    # Delay (in seconds) between sending SIGTERM and SIGKILL to a job script.
104    sigterm_to_sigkill: int = 5
105    # Interval (in seconds) between successive checks of the running script's state.
106    subprocess_checks_wait_time: int = 2
107    # Default intepreter used to run the submitted scripts in the qq environment.
108    default_interpreter: str = "bash"
109
110
111@dataclass(frozen=True)
112class ResubmitterSettings:
113    """Settings for Resubmitter operations."""
114
115    # Maximum number of attempts when retrying an operation.
116    retry_tries: int = 3
117    # Wait time (in seconds) between retry attempts.
118    retry_wait: int = 300
119    # List of hosts from which job resubmission should be attempted.
120    # If empty, the batch system defaults are used.
121    default_resubmit_hosts: str = ""
122
123
124@dataclass(frozen=True)
125class ArchiverSettings:
126    """Settings for Archiver operations."""
127
128    # Maximum number of attempts when retrying an operation.
129    retry_tries: int = 3
130    # Wait time (in seconds) between retry attempts.
131    retry_wait: int = 300
132
133
134@dataclass(frozen=True)
135class GoerSettings:
136    """Settings for Goer operations."""
137
138    # Interval (in seconds) between successive checks of the job's state
139    # (when waiting for the job to start).
140    wait_time: int = 5
141
142
143@dataclass(frozen=True)
144class LoopJobSettings:
145    """Settings for qq loop jobs."""
146
147    # Pattern used for naming loop jobs.
148    pattern: str = "+%04d"
149    # Pattern used for names of archived files.
150    archive_format: str = "job%04d"
151    # Default name of the archive directory.
152    archive_dir: str = "storage"
153
154
155@dataclass(frozen=True)
156class JobStatusPanelSettings:
157    """Settings for creating a job status panel."""
158
159    # Maximal width of the job status panel.
160    max_width: int | None = None
161    # Minimal width of the job status panel.
162    min_width: int | None = 70
163    # Style of the border lines.
164    border_style: str = "white"
165    # Style of the title.
166    title_style: str = "white bold"
167
168
169@dataclass(frozen=True)
170class FullInfoPanelSettings:
171    """Settings for creating a full info panel."""
172
173    # Maximal width of the job info panel.
174    max_width: int | None = None
175    # Minimal width of the job info panel.
176    min_width: int | None = 80
177    # Style of the border lines.
178    border_style: str = "white"
179    # Style of the title.
180    title_style: str = "white bold"
181    # Style of the separators between individual sections of the panel.
182    rule_style: str = "white"
183
184
185@dataclass(frozen=True)
186class BriefInfoSettings:
187    """Settings for brief info display."""
188
189    # Color of the job ID in brief info.
190    job_id_color: str = "default"
191    # Color of the directory path in brief info.
192    dir_path_color: str = "cyan"
193    # Color of the loop info in brief info.
194    loop_info_color: str = "grey70"
195
196
197@dataclass(frozen=True)
198class PresenterSettings:
199    """Settings for Presenter."""
200
201    # Settings for the job status panel
202    job_status_panel: JobStatusPanelSettings = field(
203        default_factory=JobStatusPanelSettings
204    )
205
206    # Settings for the job info panel
207    full_info_panel: FullInfoPanelSettings = field(
208        default_factory=FullInfoPanelSettings
209    )
210
211    # Settings for the brief info display.
212    brief_info: BriefInfoSettings = field(default_factory=BriefInfoSettings)
213
214    # Style used for the keys in job status/info panel.
215    key_style: str = "default bold"
216    # Style used for values in job status/info panel.
217    value_style: str = "white"
218    # Style used for notes in job status/info panel.
219    notes_style: str = "grey50"
220
221
222@dataclass(frozen=True)
223class JobsPresenterSettings:
224    """Settings for JobsPresenter."""
225
226    # Maximal width of the jobs panel.
227    max_width: int | None = None
228    # Minimal width of the jobs panel.
229    min_width: int | None = 80
230    # Maximum displayed length of a job name before truncation.
231    max_job_name_length: int = 20
232    # Maximum displayed length of working nodes before truncation.
233    max_nodes_length: int = 40
234    # Style used for border lines.
235    border_style: str = "white"
236    # Style used for the title.
237    title_style: str = "white bold"
238    # Style used for the subtitle (server name).
239    subtitle_style: str = "white bold"
240    # Style used for table headers.
241    headers_style: str = "default"
242    # Style used for table values.
243    main_style: str = "white"
244    # Style used for job statistics.
245    secondary_style: str = "grey70"
246    # Style used for extra notes.
247    extra_info_style: str = "grey50"
248    # Style used for strong warning messages.
249    strong_warning_style: str = "bright_red"
250    # Style used for mild warning messages.
251    mild_warning_style: str = "bright_yellow"
252    # List of columns to show in the output.
253    # If not set, the settings for the current batch system will be used.
254    columns_to_show: list[str] | None = None
255
256    # Code used to signify "total jobs".
257    sum_jobs_code: str = "Σ"
258
259
260@dataclass(frozen=True)
261class QueuesPresenterSettings:
262    """Settings for QueuesPresenter."""
263
264    # Maximal width of the queues panel.
265    max_width: int | None = None
266    # Minimal width of the queues panel.
267    min_width: int | None = 80
268    # Style used for border lines.
269    border_style: str = "white"
270    # Style used for the title.
271    title_style: str = "white bold"
272    # Style used for the subtitle (server name).
273    subtitle_style: str = "white bold"
274    # Style used for table headers.
275    headers_style: str = "default"
276
277    # Mark used to denote main queues.
278    main_mark = "●"
279    # Mark used to denote reroutings.
280    rerouted_mark = " ··>"
281
282    # Style used for the mark if the queue is available.
283    available_mark_style: str = "bright_green"
284    # Style used for the mark if the queue is not available.
285    unavailable_mark_style: str = "bright_red"
286    # Style used for the mark if the queue is dangling.
287    dangling_mark_style: str = "bright_yellow"
288
289    # Style used for information about main queues.
290    main_text_style: str = "white"
291    # Style used for information about reroutings.
292    rerouted_text_style: str = "grey50"
293
294    # Code used to signify "other jobs".
295    other_jobs_code: str = "O"
296    # Code used to signify "total jobs".
297    sum_jobs_code: str = "Σ"
298
299
300@dataclass(frozen=True)
301class NodesPresenterSettings:
302    """Settings for NodesPresenter."""
303
304    # Maximal width of the nodes panel.
305    max_width: int | None = None
306    # Minimal width of the nodes panel.
307    min_width: int | None = 80
308    # Maximal width of the shared properties section.
309    max_props_panel_width: int = 40
310    # Style used for border lines.
311    border_style: str = "white"
312    # Style used for the title.
313    title_style: str = "white bold"
314    # Style used for the subtitle (server name).
315    subtitle_style: str = "white bold"
316    # Style used for table headers.
317    headers_style: str = "default"
318    # Style of the separators between individual sections of the panel.
319    rule_style: str = "white"
320    # Name to use for the leftover nodes that were not assigned to any group.
321    others_group_name: str = "other"
322    # Name to use for the group if it contains all nodes.
323    all_nodes_group_name: str = "all nodes"
324
325    # Mark used to denote nodes.
326    state_mark = "●"
327
328    # Style used for main information about the nodes.
329    main_text_style: str = "white"
330    # Style used for statistics and shared properties.
331    secondary_text_style: str = "grey70"
332    # Style used for the mark and resources if the node is free.
333    free_node_style: str = "bright_green bold"
334    # Style used for the mark and resources if the node is partially free.
335    part_free_node_style: str = "green"
336    # Style used for the mark and resources if the node is busy.
337    busy_node_style: str = "blue"
338    # Style used for all information about unavailable nodes.
339    unavailable_node_style = "bright_red"
340
341
342@dataclass(frozen=True)
343class DateFormats:
344    """Date and time format strings."""
345
346    # Standard date format used by qq.
347    standard: str = "%Y-%m-%d %H:%M:%S"
348    # Date format used by PBS Pro.
349    pbs: str = "%a %b %d %H:%M:%S %Y"
350    # Date format used by Slurm.
351    slurm: str = "%Y-%m-%dT%H:%M:%S"
352
353
354@dataclass(frozen=True)
355class ExitCodes:
356    """Exit codes used for various errors."""
357
358    # Returned when a qq script is run outside the qq environment.
359    not_qq_env: int = 90
360    # Default error code for failures of qq commands or most errors in the qq environment.
361    default: int = 91
362    # Returned when a qq job fails and its error state cannot be written to the qq info file.
363    qq_run_fatal: int = 92
364    # Returned when a qq job fails due to a communication error between qq services.
365    qq_run_communication: int = 93
366    # Used by job scripts to signal that a loop job should not be resubmitted.
367    qq_run_no_resubmit: int = 95
368    # Returned on an unexpected or unhandled error.
369    unexpected_error: int = 99
370
371
372@dataclass(frozen=True)
373class StateColors:
374    """Color scheme for RealState display."""
375
376    # Style used for queued jobs.
377    queued: str = "bright_magenta"
378    # Style used for held jobs.
379    held: str = "bright_magenta"
380    # Style used for suspended jobs.
381    suspended: str = "bright_black"
382    # Style used for waiting jobs.
383    waiting: str = "bright_magenta"
384    # Style used for running jobs.
385    running: str = "bright_blue"
386    # Style used for booting jobs.
387    booting: str = "bright_cyan"
388    # Style used for killed jobs.
389    killed: str = "bright_red"
390    # Style used for failed jobs.
391    failed: str = "bright_red"
392    # Style used for finished jobs.
393    finished: str = "bright_green"
394    # Style used for exiting jobs.
395    exiting: str = "bright_yellow"
396    # Style used for jobs in an inconsistent state.
397    in_an_inconsistent_state: str = "grey70"
398    # Style used for jobs in an unknown state.
399    unknown: str = "grey70"
400    # Style used whenever a summary of jobs is provided.
401    sum: str = "white"
402    # Style used for "other" job states.
403    other: str = "grey70"
404
405
406@dataclass(frozen=True)
407class SizeOptions:
408    """Options associated with the Size dataclass."""
409
410    # Maximal relative error acceptable when rounding Size values for display.
411    max_rounding_error: float = 0.1
412
413
414@dataclass(frozen=True)
415class PBSOptions:
416    """Options associated with PBS."""
417
418    # Name of the subdirectory inside SCRATCHDIR used as the job's working directory.
419    scratch_dir_inner: str = "main"
420
421
422@dataclass(frozen=True)
423class SlurmOptions:
424    """Options associated with Slurm."""
425
426    # Maximal number of threads used to collect information about jobs using scontrol.
427    jobs_scontrol_nthreads: int = 8
428
429
430@dataclass(frozen=True)
431class SlurmIT4IOptions:
432    """Options associated with Slurm on IT4I clusters."""
433
434    # Number of attempts when preparing a working directory on scratch.
435    scratch_dir_attempts: int = 3
436
437
438@dataclass(frozen=True)
439class SlurmLumiOptions:
440    """Options associated with Slurm on LUMI."""
441
442    # Number of attempts when preparing a working directory on scratch.
443    scratch_dir_attempts: int = 3
444
445
446@dataclass(frozen=True)
447class TransferFilesOptions:
448    """Options associated with transferring and archiving files."""
449
450    # Default archive mode used for jobs.
451    default_archive_mode: str = "success"
452
453    # Default transfer mode used for jobs.
454    default_transfer_mode: str = "success"
455
456
457@dataclass(frozen=True)
458class BatchServersOptions:
459    """Options associated with selecting and specifying batch servers."""
460
461    # Dictionary mapping known server shortcuts to full server names.
462    known_servers: dict[str, str] = field(
463        default_factory=lambda: {
464            "robox": "robox-pro.ceitec.muni.cz",
465            "sokar": "sokar-pbs.ncbr.muni.cz",
466            "metacentrum": "pbs-m1.metacentrum.cz",
467            "meta": "pbs-m1.metacentrum.cz",
468        }
469    )
470
471    # Dictionary mapping known server names to frontends.
472    known_output_hosts: dict[str, str] = field(
473        default_factory=lambda: {
474            "robox-pro.ceitec.muni.cz": "st1.ceitec.muni.cz",
475            "sokar-pbs.ncbr.muni.cz": "sokar.ncbr.muni.cz",
476            "pbs-m1.metacentrum.cz": "perian.metacentrum.cz",
477        }
478    )
479
480
481@dataclass(frozen=True)
482class ParallelizationOptions:
483    """Options associated with multithreaded execution."""
484
485    # Maximal number of threads used to collect job information.
486    job_info_max_threads: int = 16
487
488    # Maximal number of threads used to submit jobs in parallel.
489    submission_max_threads: int = 8
490
491    # Maximal number of threads used to clear runtime files in parallel.
492    clear_max_threads: int = 8
493
494
495@dataclass(frozen=True)
496class Config:
497    """Main configuration for qq."""
498
499    suffixes: FileSuffixes = field(default_factory=FileSuffixes)
500    env_vars: EnvironmentVariables = field(default_factory=EnvironmentVariables)
501    timeouts: TimeoutSettings = field(default_factory=TimeoutSettings)
502    runner: RunnerSettings = field(default_factory=RunnerSettings)
503    resubmitter: ResubmitterSettings = field(default_factory=ResubmitterSettings)
504    archiver: ArchiverSettings = field(default_factory=ArchiverSettings)
505    goer: GoerSettings = field(default_factory=GoerSettings)
506    presenter: PresenterSettings = field(default_factory=PresenterSettings)
507    loop_jobs: LoopJobSettings = field(default_factory=LoopJobSettings)
508    jobs_presenter: JobsPresenterSettings = field(default_factory=JobsPresenterSettings)
509    queues_presenter: QueuesPresenterSettings = field(
510        default_factory=QueuesPresenterSettings
511    )
512    nodes_presenter: NodesPresenterSettings = field(
513        default_factory=NodesPresenterSettings
514    )
515    date_formats: DateFormats = field(default_factory=DateFormats)
516    exit_codes: ExitCodes = field(default_factory=ExitCodes)
517    state_colors: StateColors = field(default_factory=StateColors)
518    size: SizeOptions = field(default_factory=SizeOptions)
519    pbs_options: PBSOptions = field(default_factory=PBSOptions)
520    slurm_options: SlurmOptions = field(default_factory=SlurmOptions)
521    slurm_it4i_options: SlurmIT4IOptions = field(default_factory=SlurmIT4IOptions)
522    slurm_lumi_options: SlurmLumiOptions = field(default_factory=SlurmLumiOptions)
523    transfer_files_options: TransferFilesOptions = field(
524        default_factory=TransferFilesOptions
525    )
526    batch_servers_options: BatchServersOptions = field(
527        default_factory=BatchServersOptions
528    )
529    parallelization_options: ParallelizationOptions = field(
530        default_factory=ParallelizationOptions
531    )
532
533    # Name of the qq binary.
534    binary_name: str = "qq"
535
536    @classmethod
537    def load(cls, config_path: Path | None = None) -> Self:
538        """
539        Load configuration from TOML file or use defaults.
540
541        Args:
542            config_path: Explicit path to config file. If None, searches standard locations.
543
544        Returns:
545            Config instance with loaded or default values.
546        """
547        if config_path is None:
548            config_path = Config._get_config_path()
549
550        try:
551            if config_path and config_path.exists():
552                with config_path.open("rb") as f:
553                    config_data = tomllib.load(f)
554                return _dict_to_dataclass(cls, config_data)
555        except Exception as e:
556            print(
557                f"[ FATAL CONFIGURATION ERROR ] Could not read qq config '{config_path}': {e}."
558            )
559            print(
560                "[ FATAL CONFIGURATION ERROR ] Falling back to default configuration.\n\n"
561            )
562
563        # no config found - use defaults
564        return cls()
565
566    @staticmethod
567    def _get_config_path() -> Path | None:
568        """
569        Search for config file in standard locations (XDG compliant).
570        Returns the first existing config file, or None.
571        """
572        config_locations: list[Path | None] = [
573            # 1. Explicit environment variable (highest priority)
574            Path(env_path) if (env_path := os.getenv("QQ_CONFIG")) else None,
575            # 2. Current working directory (for development/override)
576            Path.cwd() / "qq_config.toml",
577            # 3. XDG config home (standard user config location)
578            Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
579            / "qq"
580            / "config.toml",
581        ]
582
583        for path in config_locations:
584            if path and path.is_file():
585                return path
586
587        return None
588
589
590def _dict_to_dataclass(cls, data: dict[str, Any]):
591    """
592    Recursively convert a dictionary to a dataclass instance.
593    Handles nested dataclasses properly.
594    """
595    if not is_dataclass(cls):
596        return data
597
598    field_values = {}
599    for field_info in fields(cls):
600        field_name = field_info.name
601        field_type = field_info.type
602
603        if field_name in data:
604            value = data[field_name]
605            if is_dataclass(field_type) and isinstance(value, dict):
606                field_values[field_name] = _dict_to_dataclass(field_type, value)
607            else:
608                field_values[field_name] = value
609
610    return cls(**field_values)
611
612
613# Global configuration for qq.
614CFG = Config.load()
@dataclass(frozen=True)
class FileSuffixes:
23@dataclass(frozen=True)
24class FileSuffixes:
25    """File suffixes used by qq."""
26
27    # Suffix for qq info files.
28    qq_info: str = ".qqinfo"
29    # Suffix for qq output files.
30    qq_out: str = ".qqout"
31    # Suffix for captured stdout.
32    stdout: str = ".out"
33    # Suffix for captured stderr.
34    stderr: str = ".err"
35
36    @property
37    def all_suffixes(self) -> list[str]:
38        """List of all file suffixes."""
39        return [self.qq_info, self.qq_out, self.stdout, self.stderr]

File suffixes used by qq.

FileSuffixes( qq_info: str = '.qqinfo', qq_out: str = '.qqout', stdout: str = '.out', stderr: str = '.err')
qq_info: str = '.qqinfo'
qq_out: str = '.qqout'
stdout: str = '.out'
stderr: str = '.err'
all_suffixes: list[str]
36    @property
37    def all_suffixes(self) -> list[str]:
38        """List of all file suffixes."""
39        return [self.qq_info, self.qq_out, self.stdout, self.stderr]

List of all file suffixes.

@dataclass(frozen=True)
class EnvironmentVariables:
42@dataclass(frozen=True)
43class EnvironmentVariables:
44    """Environment variable names used by qq."""
45
46    # Indicates job is running inside the qq environment.
47    guard: str = "QQ_ENV_SET"
48    # Enables qq debug mode.
49    debug_mode: str = "QQ_DEBUG"
50    # Path to the qq info file for the job.
51    info_file: str = "QQ_INFO"
52    # Machine from which the job was submitted.
53    input_machine: str = "QQ_INPUT_MACHINE"
54    # Submission directory path.
55    input_dir: str = "QQ_INPUT_DIR"
56    # Whether submission was from shared storage.
57    shared_submit: str = "QQ_SHARED_SUBMIT"
58    # Name of the batch system used.
59    batch_system: str = "QQ_BATCH_SYSTEM"
60    # Current loop-cycle index.
61    loop_current: str = "QQ_LOOP_CURRENT"
62    # Starting loop-cycle index.
63    loop_start: str = "QQ_LOOP_START"
64    # Final loop-cycle index.
65    loop_end: str = "QQ_LOOP_END"
66    # Non-resubmit flag returned by a job script.
67    no_resubmit: str = "QQ_NO_RESUBMIT"
68    # Archive filename pattern.
69    archive_format: str = "QQ_ARCHIVE_FORMAT"
70    # Scratch directory on Metacentrum clusters.
71    pbs_scratch_dir: str = "SCRATCHDIR"
72    # Slurm account used for the job.
73    slurm_job_account: str = "SLURM_JOB_ACCOUNT"
74    # Storage type for LUMI scratch.
75    lumi_scratch_type: str = "LUMI_SCRATCH_TYPE"
76    # Total CPUs used.
77    ncpus: str = "QQ_NCPUS"
78    # Total GPUs used.
79    ngpus: str = "QQ_NGPUS"
80    # Total nodes used.
81    nnodes: str = "QQ_NNODES"
82    # Walltime in hours.
83    walltime: str = "QQ_WALLTIME"

Environment variable names used by qq.

EnvironmentVariables( guard: str = 'QQ_ENV_SET', debug_mode: str = 'QQ_DEBUG', info_file: str = 'QQ_INFO', input_machine: str = 'QQ_INPUT_MACHINE', input_dir: str = 'QQ_INPUT_DIR', shared_submit: str = 'QQ_SHARED_SUBMIT', batch_system: str = 'QQ_BATCH_SYSTEM', loop_current: str = 'QQ_LOOP_CURRENT', loop_start: str = 'QQ_LOOP_START', loop_end: str = 'QQ_LOOP_END', no_resubmit: str = 'QQ_NO_RESUBMIT', archive_format: str = 'QQ_ARCHIVE_FORMAT', pbs_scratch_dir: str = 'SCRATCHDIR', slurm_job_account: str = 'SLURM_JOB_ACCOUNT', lumi_scratch_type: str = 'LUMI_SCRATCH_TYPE', ncpus: str = 'QQ_NCPUS', ngpus: str = 'QQ_NGPUS', nnodes: str = 'QQ_NNODES', walltime: str = 'QQ_WALLTIME')
guard: str = 'QQ_ENV_SET'
debug_mode: str = 'QQ_DEBUG'
info_file: str = 'QQ_INFO'
input_machine: str = 'QQ_INPUT_MACHINE'
input_dir: str = 'QQ_INPUT_DIR'
shared_submit: str = 'QQ_SHARED_SUBMIT'
batch_system: str = 'QQ_BATCH_SYSTEM'
loop_current: str = 'QQ_LOOP_CURRENT'
loop_start: str = 'QQ_LOOP_START'
loop_end: str = 'QQ_LOOP_END'
no_resubmit: str = 'QQ_NO_RESUBMIT'
archive_format: str = 'QQ_ARCHIVE_FORMAT'
pbs_scratch_dir: str = 'SCRATCHDIR'
slurm_job_account: str = 'SLURM_JOB_ACCOUNT'
lumi_scratch_type: str = 'LUMI_SCRATCH_TYPE'
ncpus: str = 'QQ_NCPUS'
ngpus: str = 'QQ_NGPUS'
nnodes: str = 'QQ_NNODES'
walltime: str = 'QQ_WALLTIME'
@dataclass(frozen=True)
class TimeoutSettings:
86@dataclass(frozen=True)
87class TimeoutSettings:
88    """Timeout settings in seconds."""
89
90    # Timeout for SSH in seconds.
91    ssh: int = 60
92    # Timeout for rsync in seconds.
93    rsync: int = 600

Timeout settings in seconds.

TimeoutSettings(ssh: int = 60, rsync: int = 600)
ssh: int = 60
rsync: int = 600
@dataclass(frozen=True)
class RunnerSettings:
 96@dataclass(frozen=True)
 97class RunnerSettings:
 98    """Settings for Runner operations."""
 99
100    # Maximum number of attempts when retrying an operation.
101    retry_tries: int = 3
102    # Wait time (in seconds) between retry attempts.
103    retry_wait: int = 300
104    # Delay (in seconds) between sending SIGTERM and SIGKILL to a job script.
105    sigterm_to_sigkill: int = 5
106    # Interval (in seconds) between successive checks of the running script's state.
107    subprocess_checks_wait_time: int = 2
108    # Default intepreter used to run the submitted scripts in the qq environment.
109    default_interpreter: str = "bash"

Settings for Runner operations.

RunnerSettings( retry_tries: int = 3, retry_wait: int = 300, sigterm_to_sigkill: int = 5, subprocess_checks_wait_time: int = 2, default_interpreter: str = 'bash')
retry_tries: int = 3
retry_wait: int = 300
sigterm_to_sigkill: int = 5
subprocess_checks_wait_time: int = 2
default_interpreter: str = 'bash'
@dataclass(frozen=True)
class ResubmitterSettings:
112@dataclass(frozen=True)
113class ResubmitterSettings:
114    """Settings for Resubmitter operations."""
115
116    # Maximum number of attempts when retrying an operation.
117    retry_tries: int = 3
118    # Wait time (in seconds) between retry attempts.
119    retry_wait: int = 300
120    # List of hosts from which job resubmission should be attempted.
121    # If empty, the batch system defaults are used.
122    default_resubmit_hosts: str = ""

Settings for Resubmitter operations.

ResubmitterSettings( retry_tries: int = 3, retry_wait: int = 300, default_resubmit_hosts: str = '')
retry_tries: int = 3
retry_wait: int = 300
default_resubmit_hosts: str = ''
@dataclass(frozen=True)
class ArchiverSettings:
125@dataclass(frozen=True)
126class ArchiverSettings:
127    """Settings for Archiver operations."""
128
129    # Maximum number of attempts when retrying an operation.
130    retry_tries: int = 3
131    # Wait time (in seconds) between retry attempts.
132    retry_wait: int = 300

Settings for Archiver operations.

ArchiverSettings(retry_tries: int = 3, retry_wait: int = 300)
retry_tries: int = 3
retry_wait: int = 300
@dataclass(frozen=True)
class GoerSettings:
135@dataclass(frozen=True)
136class GoerSettings:
137    """Settings for Goer operations."""
138
139    # Interval (in seconds) between successive checks of the job's state
140    # (when waiting for the job to start).
141    wait_time: int = 5

Settings for Goer operations.

GoerSettings(wait_time: int = 5)
wait_time: int = 5
@dataclass(frozen=True)
class LoopJobSettings:
144@dataclass(frozen=True)
145class LoopJobSettings:
146    """Settings for qq loop jobs."""
147
148    # Pattern used for naming loop jobs.
149    pattern: str = "+%04d"
150    # Pattern used for names of archived files.
151    archive_format: str = "job%04d"
152    # Default name of the archive directory.
153    archive_dir: str = "storage"

Settings for qq loop jobs.

LoopJobSettings( pattern: str = '+%04d', archive_format: str = 'job%04d', archive_dir: str = 'storage')
pattern: str = '+%04d'
archive_format: str = 'job%04d'
archive_dir: str = 'storage'
@dataclass(frozen=True)
class JobStatusPanelSettings:
156@dataclass(frozen=True)
157class JobStatusPanelSettings:
158    """Settings for creating a job status panel."""
159
160    # Maximal width of the job status panel.
161    max_width: int | None = None
162    # Minimal width of the job status panel.
163    min_width: int | None = 70
164    # Style of the border lines.
165    border_style: str = "white"
166    # Style of the title.
167    title_style: str = "white bold"

Settings for creating a job status panel.

JobStatusPanelSettings( max_width: int | None = None, min_width: int | None = 70, border_style: str = 'white', title_style: str = 'white bold')
max_width: int | None = None
min_width: int | None = 70
border_style: str = 'white'
title_style: str = 'white bold'
@dataclass(frozen=True)
class FullInfoPanelSettings:
170@dataclass(frozen=True)
171class FullInfoPanelSettings:
172    """Settings for creating a full info panel."""
173
174    # Maximal width of the job info panel.
175    max_width: int | None = None
176    # Minimal width of the job info panel.
177    min_width: int | None = 80
178    # Style of the border lines.
179    border_style: str = "white"
180    # Style of the title.
181    title_style: str = "white bold"
182    # Style of the separators between individual sections of the panel.
183    rule_style: str = "white"

Settings for creating a full info panel.

FullInfoPanelSettings( max_width: int | None = None, min_width: int | None = 80, border_style: str = 'white', title_style: str = 'white bold', rule_style: str = 'white')
max_width: int | None = None
min_width: int | None = 80
border_style: str = 'white'
title_style: str = 'white bold'
rule_style: str = 'white'
@dataclass(frozen=True)
class BriefInfoSettings:
186@dataclass(frozen=True)
187class BriefInfoSettings:
188    """Settings for brief info display."""
189
190    # Color of the job ID in brief info.
191    job_id_color: str = "default"
192    # Color of the directory path in brief info.
193    dir_path_color: str = "cyan"
194    # Color of the loop info in brief info.
195    loop_info_color: str = "grey70"

Settings for brief info display.

BriefInfoSettings( job_id_color: str = 'default', dir_path_color: str = 'cyan', loop_info_color: str = 'grey70')
job_id_color: str = 'default'
dir_path_color: str = 'cyan'
loop_info_color: str = 'grey70'
@dataclass(frozen=True)
class PresenterSettings:
198@dataclass(frozen=True)
199class PresenterSettings:
200    """Settings for Presenter."""
201
202    # Settings for the job status panel
203    job_status_panel: JobStatusPanelSettings = field(
204        default_factory=JobStatusPanelSettings
205    )
206
207    # Settings for the job info panel
208    full_info_panel: FullInfoPanelSettings = field(
209        default_factory=FullInfoPanelSettings
210    )
211
212    # Settings for the brief info display.
213    brief_info: BriefInfoSettings = field(default_factory=BriefInfoSettings)
214
215    # Style used for the keys in job status/info panel.
216    key_style: str = "default bold"
217    # Style used for values in job status/info panel.
218    value_style: str = "white"
219    # Style used for notes in job status/info panel.
220    notes_style: str = "grey50"

Settings for Presenter.

PresenterSettings( job_status_panel: JobStatusPanelSettings = <factory>, full_info_panel: FullInfoPanelSettings = <factory>, brief_info: BriefInfoSettings = <factory>, key_style: str = 'default bold', value_style: str = 'white', notes_style: str = 'grey50')
job_status_panel: JobStatusPanelSettings
full_info_panel: FullInfoPanelSettings
brief_info: BriefInfoSettings
key_style: str = 'default bold'
value_style: str = 'white'
notes_style: str = 'grey50'
@dataclass(frozen=True)
class JobsPresenterSettings:
223@dataclass(frozen=True)
224class JobsPresenterSettings:
225    """Settings for JobsPresenter."""
226
227    # Maximal width of the jobs panel.
228    max_width: int | None = None
229    # Minimal width of the jobs panel.
230    min_width: int | None = 80
231    # Maximum displayed length of a job name before truncation.
232    max_job_name_length: int = 20
233    # Maximum displayed length of working nodes before truncation.
234    max_nodes_length: int = 40
235    # Style used for border lines.
236    border_style: str = "white"
237    # Style used for the title.
238    title_style: str = "white bold"
239    # Style used for the subtitle (server name).
240    subtitle_style: str = "white bold"
241    # Style used for table headers.
242    headers_style: str = "default"
243    # Style used for table values.
244    main_style: str = "white"
245    # Style used for job statistics.
246    secondary_style: str = "grey70"
247    # Style used for extra notes.
248    extra_info_style: str = "grey50"
249    # Style used for strong warning messages.
250    strong_warning_style: str = "bright_red"
251    # Style used for mild warning messages.
252    mild_warning_style: str = "bright_yellow"
253    # List of columns to show in the output.
254    # If not set, the settings for the current batch system will be used.
255    columns_to_show: list[str] | None = None
256
257    # Code used to signify "total jobs".
258    sum_jobs_code: str = "Σ"

Settings for JobsPresenter.

JobsPresenterSettings( max_width: int | None = None, min_width: int | None = 80, max_job_name_length: int = 20, max_nodes_length: int = 40, border_style: str = 'white', title_style: str = 'white bold', subtitle_style: str = 'white bold', headers_style: str = 'default', main_style: str = 'white', secondary_style: str = 'grey70', extra_info_style: str = 'grey50', strong_warning_style: str = 'bright_red', mild_warning_style: str = 'bright_yellow', columns_to_show: list[str] | None = None, sum_jobs_code: str = 'Σ')
max_width: int | None = None
min_width: int | None = 80
max_job_name_length: int = 20
max_nodes_length: int = 40
border_style: str = 'white'
title_style: str = 'white bold'
subtitle_style: str = 'white bold'
headers_style: str = 'default'
main_style: str = 'white'
secondary_style: str = 'grey70'
extra_info_style: str = 'grey50'
strong_warning_style: str = 'bright_red'
mild_warning_style: str = 'bright_yellow'
columns_to_show: list[str] | None = None
sum_jobs_code: str = 'Σ'
@dataclass(frozen=True)
class QueuesPresenterSettings:
261@dataclass(frozen=True)
262class QueuesPresenterSettings:
263    """Settings for QueuesPresenter."""
264
265    # Maximal width of the queues panel.
266    max_width: int | None = None
267    # Minimal width of the queues panel.
268    min_width: int | None = 80
269    # Style used for border lines.
270    border_style: str = "white"
271    # Style used for the title.
272    title_style: str = "white bold"
273    # Style used for the subtitle (server name).
274    subtitle_style: str = "white bold"
275    # Style used for table headers.
276    headers_style: str = "default"
277
278    # Mark used to denote main queues.
279    main_mark = "●"
280    # Mark used to denote reroutings.
281    rerouted_mark = " ··>"
282
283    # Style used for the mark if the queue is available.
284    available_mark_style: str = "bright_green"
285    # Style used for the mark if the queue is not available.
286    unavailable_mark_style: str = "bright_red"
287    # Style used for the mark if the queue is dangling.
288    dangling_mark_style: str = "bright_yellow"
289
290    # Style used for information about main queues.
291    main_text_style: str = "white"
292    # Style used for information about reroutings.
293    rerouted_text_style: str = "grey50"
294
295    # Code used to signify "other jobs".
296    other_jobs_code: str = "O"
297    # Code used to signify "total jobs".
298    sum_jobs_code: str = "Σ"

Settings for QueuesPresenter.

QueuesPresenterSettings( max_width: int | None = None, min_width: int | None = 80, border_style: str = 'white', title_style: str = 'white bold', subtitle_style: str = 'white bold', headers_style: str = 'default', available_mark_style: str = 'bright_green', unavailable_mark_style: str = 'bright_red', dangling_mark_style: str = 'bright_yellow', main_text_style: str = 'white', rerouted_text_style: str = 'grey50', other_jobs_code: str = 'O', sum_jobs_code: str = 'Σ')
max_width: int | None = None
min_width: int | None = 80
border_style: str = 'white'
title_style: str = 'white bold'
subtitle_style: str = 'white bold'
headers_style: str = 'default'
main_mark = '●'
rerouted_mark = ' ··>'
available_mark_style: str = 'bright_green'
unavailable_mark_style: str = 'bright_red'
dangling_mark_style: str = 'bright_yellow'
main_text_style: str = 'white'
rerouted_text_style: str = 'grey50'
other_jobs_code: str = 'O'
sum_jobs_code: str = 'Σ'
@dataclass(frozen=True)
class NodesPresenterSettings:
301@dataclass(frozen=True)
302class NodesPresenterSettings:
303    """Settings for NodesPresenter."""
304
305    # Maximal width of the nodes panel.
306    max_width: int | None = None
307    # Minimal width of the nodes panel.
308    min_width: int | None = 80
309    # Maximal width of the shared properties section.
310    max_props_panel_width: int = 40
311    # Style used for border lines.
312    border_style: str = "white"
313    # Style used for the title.
314    title_style: str = "white bold"
315    # Style used for the subtitle (server name).
316    subtitle_style: str = "white bold"
317    # Style used for table headers.
318    headers_style: str = "default"
319    # Style of the separators between individual sections of the panel.
320    rule_style: str = "white"
321    # Name to use for the leftover nodes that were not assigned to any group.
322    others_group_name: str = "other"
323    # Name to use for the group if it contains all nodes.
324    all_nodes_group_name: str = "all nodes"
325
326    # Mark used to denote nodes.
327    state_mark = "●"
328
329    # Style used for main information about the nodes.
330    main_text_style: str = "white"
331    # Style used for statistics and shared properties.
332    secondary_text_style: str = "grey70"
333    # Style used for the mark and resources if the node is free.
334    free_node_style: str = "bright_green bold"
335    # Style used for the mark and resources if the node is partially free.
336    part_free_node_style: str = "green"
337    # Style used for the mark and resources if the node is busy.
338    busy_node_style: str = "blue"
339    # Style used for all information about unavailable nodes.
340    unavailable_node_style = "bright_red"

Settings for NodesPresenter.

NodesPresenterSettings( max_width: int | None = None, min_width: int | None = 80, max_props_panel_width: int = 40, border_style: str = 'white', title_style: str = 'white bold', subtitle_style: str = 'white bold', headers_style: str = 'default', rule_style: str = 'white', others_group_name: str = 'other', all_nodes_group_name: str = 'all nodes', main_text_style: str = 'white', secondary_text_style: str = 'grey70', free_node_style: str = 'bright_green bold', part_free_node_style: str = 'green', busy_node_style: str = 'blue')
max_width: int | None = None
min_width: int | None = 80
max_props_panel_width: int = 40
border_style: str = 'white'
title_style: str = 'white bold'
subtitle_style: str = 'white bold'
headers_style: str = 'default'
rule_style: str = 'white'
others_group_name: str = 'other'
all_nodes_group_name: str = 'all nodes'
state_mark = '●'
main_text_style: str = 'white'
secondary_text_style: str = 'grey70'
free_node_style: str = 'bright_green bold'
part_free_node_style: str = 'green'
busy_node_style: str = 'blue'
unavailable_node_style = 'bright_red'
@dataclass(frozen=True)
class DateFormats:
343@dataclass(frozen=True)
344class DateFormats:
345    """Date and time format strings."""
346
347    # Standard date format used by qq.
348    standard: str = "%Y-%m-%d %H:%M:%S"
349    # Date format used by PBS Pro.
350    pbs: str = "%a %b %d %H:%M:%S %Y"
351    # Date format used by Slurm.
352    slurm: str = "%Y-%m-%dT%H:%M:%S"

Date and time format strings.

DateFormats( standard: str = '%Y-%m-%d %H:%M:%S', pbs: str = '%a %b %d %H:%M:%S %Y', slurm: str = '%Y-%m-%dT%H:%M:%S')
standard: str = '%Y-%m-%d %H:%M:%S'
pbs: str = '%a %b %d %H:%M:%S %Y'
slurm: str = '%Y-%m-%dT%H:%M:%S'
@dataclass(frozen=True)
class ExitCodes:
355@dataclass(frozen=True)
356class ExitCodes:
357    """Exit codes used for various errors."""
358
359    # Returned when a qq script is run outside the qq environment.
360    not_qq_env: int = 90
361    # Default error code for failures of qq commands or most errors in the qq environment.
362    default: int = 91
363    # Returned when a qq job fails and its error state cannot be written to the qq info file.
364    qq_run_fatal: int = 92
365    # Returned when a qq job fails due to a communication error between qq services.
366    qq_run_communication: int = 93
367    # Used by job scripts to signal that a loop job should not be resubmitted.
368    qq_run_no_resubmit: int = 95
369    # Returned on an unexpected or unhandled error.
370    unexpected_error: int = 99

Exit codes used for various errors.

ExitCodes( not_qq_env: int = 90, default: int = 91, qq_run_fatal: int = 92, qq_run_communication: int = 93, qq_run_no_resubmit: int = 95, unexpected_error: int = 99)
not_qq_env: int = 90
default: int = 91
qq_run_fatal: int = 92
qq_run_communication: int = 93
qq_run_no_resubmit: int = 95
unexpected_error: int = 99
@dataclass(frozen=True)
class StateColors:
373@dataclass(frozen=True)
374class StateColors:
375    """Color scheme for RealState display."""
376
377    # Style used for queued jobs.
378    queued: str = "bright_magenta"
379    # Style used for held jobs.
380    held: str = "bright_magenta"
381    # Style used for suspended jobs.
382    suspended: str = "bright_black"
383    # Style used for waiting jobs.
384    waiting: str = "bright_magenta"
385    # Style used for running jobs.
386    running: str = "bright_blue"
387    # Style used for booting jobs.
388    booting: str = "bright_cyan"
389    # Style used for killed jobs.
390    killed: str = "bright_red"
391    # Style used for failed jobs.
392    failed: str = "bright_red"
393    # Style used for finished jobs.
394    finished: str = "bright_green"
395    # Style used for exiting jobs.
396    exiting: str = "bright_yellow"
397    # Style used for jobs in an inconsistent state.
398    in_an_inconsistent_state: str = "grey70"
399    # Style used for jobs in an unknown state.
400    unknown: str = "grey70"
401    # Style used whenever a summary of jobs is provided.
402    sum: str = "white"
403    # Style used for "other" job states.
404    other: str = "grey70"

Color scheme for RealState display.

StateColors( queued: str = 'bright_magenta', held: str = 'bright_magenta', suspended: str = 'bright_black', waiting: str = 'bright_magenta', running: str = 'bright_blue', booting: str = 'bright_cyan', killed: str = 'bright_red', failed: str = 'bright_red', finished: str = 'bright_green', exiting: str = 'bright_yellow', in_an_inconsistent_state: str = 'grey70', unknown: str = 'grey70', sum: str = 'white', other: str = 'grey70')
queued: str = 'bright_magenta'
held: str = 'bright_magenta'
suspended: str = 'bright_black'
waiting: str = 'bright_magenta'
running: str = 'bright_blue'
booting: str = 'bright_cyan'
killed: str = 'bright_red'
failed: str = 'bright_red'
finished: str = 'bright_green'
exiting: str = 'bright_yellow'
in_an_inconsistent_state: str = 'grey70'
unknown: str = 'grey70'
sum: str = 'white'
other: str = 'grey70'
@dataclass(frozen=True)
class SizeOptions:
407@dataclass(frozen=True)
408class SizeOptions:
409    """Options associated with the Size dataclass."""
410
411    # Maximal relative error acceptable when rounding Size values for display.
412    max_rounding_error: float = 0.1

Options associated with the Size dataclass.

SizeOptions(max_rounding_error: float = 0.1)
max_rounding_error: float = 0.1
@dataclass(frozen=True)
class PBSOptions:
415@dataclass(frozen=True)
416class PBSOptions:
417    """Options associated with PBS."""
418
419    # Name of the subdirectory inside SCRATCHDIR used as the job's working directory.
420    scratch_dir_inner: str = "main"

Options associated with PBS.

PBSOptions(scratch_dir_inner: str = 'main')
scratch_dir_inner: str = 'main'
@dataclass(frozen=True)
class SlurmOptions:
423@dataclass(frozen=True)
424class SlurmOptions:
425    """Options associated with Slurm."""
426
427    # Maximal number of threads used to collect information about jobs using scontrol.
428    jobs_scontrol_nthreads: int = 8

Options associated with Slurm.

SlurmOptions(jobs_scontrol_nthreads: int = 8)
jobs_scontrol_nthreads: int = 8
@dataclass(frozen=True)
class SlurmIT4IOptions:
431@dataclass(frozen=True)
432class SlurmIT4IOptions:
433    """Options associated with Slurm on IT4I clusters."""
434
435    # Number of attempts when preparing a working directory on scratch.
436    scratch_dir_attempts: int = 3

Options associated with Slurm on IT4I clusters.

SlurmIT4IOptions(scratch_dir_attempts: int = 3)
scratch_dir_attempts: int = 3
@dataclass(frozen=True)
class SlurmLumiOptions:
439@dataclass(frozen=True)
440class SlurmLumiOptions:
441    """Options associated with Slurm on LUMI."""
442
443    # Number of attempts when preparing a working directory on scratch.
444    scratch_dir_attempts: int = 3

Options associated with Slurm on LUMI.

SlurmLumiOptions(scratch_dir_attempts: int = 3)
scratch_dir_attempts: int = 3
@dataclass(frozen=True)
class TransferFilesOptions:
447@dataclass(frozen=True)
448class TransferFilesOptions:
449    """Options associated with transferring and archiving files."""
450
451    # Default archive mode used for jobs.
452    default_archive_mode: str = "success"
453
454    # Default transfer mode used for jobs.
455    default_transfer_mode: str = "success"

Options associated with transferring and archiving files.

TransferFilesOptions( default_archive_mode: str = 'success', default_transfer_mode: str = 'success')
default_archive_mode: str = 'success'
default_transfer_mode: str = 'success'
@dataclass(frozen=True)
class BatchServersOptions:
458@dataclass(frozen=True)
459class BatchServersOptions:
460    """Options associated with selecting and specifying batch servers."""
461
462    # Dictionary mapping known server shortcuts to full server names.
463    known_servers: dict[str, str] = field(
464        default_factory=lambda: {
465            "robox": "robox-pro.ceitec.muni.cz",
466            "sokar": "sokar-pbs.ncbr.muni.cz",
467            "metacentrum": "pbs-m1.metacentrum.cz",
468            "meta": "pbs-m1.metacentrum.cz",
469        }
470    )
471
472    # Dictionary mapping known server names to frontends.
473    known_output_hosts: dict[str, str] = field(
474        default_factory=lambda: {
475            "robox-pro.ceitec.muni.cz": "st1.ceitec.muni.cz",
476            "sokar-pbs.ncbr.muni.cz": "sokar.ncbr.muni.cz",
477            "pbs-m1.metacentrum.cz": "perian.metacentrum.cz",
478        }
479    )

Options associated with selecting and specifying batch servers.

BatchServersOptions( known_servers: dict[str, str] = <factory>, known_output_hosts: dict[str, str] = <factory>)
known_servers: dict[str, str]
known_output_hosts: dict[str, str]
@dataclass(frozen=True)
class ParallelizationOptions:
482@dataclass(frozen=True)
483class ParallelizationOptions:
484    """Options associated with multithreaded execution."""
485
486    # Maximal number of threads used to collect job information.
487    job_info_max_threads: int = 16
488
489    # Maximal number of threads used to submit jobs in parallel.
490    submission_max_threads: int = 8
491
492    # Maximal number of threads used to clear runtime files in parallel.
493    clear_max_threads: int = 8

Options associated with multithreaded execution.

ParallelizationOptions( job_info_max_threads: int = 16, submission_max_threads: int = 8, clear_max_threads: int = 8)
job_info_max_threads: int = 16
submission_max_threads: int = 8
clear_max_threads: int = 8
@dataclass(frozen=True)
class Config:
496@dataclass(frozen=True)
497class Config:
498    """Main configuration for qq."""
499
500    suffixes: FileSuffixes = field(default_factory=FileSuffixes)
501    env_vars: EnvironmentVariables = field(default_factory=EnvironmentVariables)
502    timeouts: TimeoutSettings = field(default_factory=TimeoutSettings)
503    runner: RunnerSettings = field(default_factory=RunnerSettings)
504    resubmitter: ResubmitterSettings = field(default_factory=ResubmitterSettings)
505    archiver: ArchiverSettings = field(default_factory=ArchiverSettings)
506    goer: GoerSettings = field(default_factory=GoerSettings)
507    presenter: PresenterSettings = field(default_factory=PresenterSettings)
508    loop_jobs: LoopJobSettings = field(default_factory=LoopJobSettings)
509    jobs_presenter: JobsPresenterSettings = field(default_factory=JobsPresenterSettings)
510    queues_presenter: QueuesPresenterSettings = field(
511        default_factory=QueuesPresenterSettings
512    )
513    nodes_presenter: NodesPresenterSettings = field(
514        default_factory=NodesPresenterSettings
515    )
516    date_formats: DateFormats = field(default_factory=DateFormats)
517    exit_codes: ExitCodes = field(default_factory=ExitCodes)
518    state_colors: StateColors = field(default_factory=StateColors)
519    size: SizeOptions = field(default_factory=SizeOptions)
520    pbs_options: PBSOptions = field(default_factory=PBSOptions)
521    slurm_options: SlurmOptions = field(default_factory=SlurmOptions)
522    slurm_it4i_options: SlurmIT4IOptions = field(default_factory=SlurmIT4IOptions)
523    slurm_lumi_options: SlurmLumiOptions = field(default_factory=SlurmLumiOptions)
524    transfer_files_options: TransferFilesOptions = field(
525        default_factory=TransferFilesOptions
526    )
527    batch_servers_options: BatchServersOptions = field(
528        default_factory=BatchServersOptions
529    )
530    parallelization_options: ParallelizationOptions = field(
531        default_factory=ParallelizationOptions
532    )
533
534    # Name of the qq binary.
535    binary_name: str = "qq"
536
537    @classmethod
538    def load(cls, config_path: Path | None = None) -> Self:
539        """
540        Load configuration from TOML file or use defaults.
541
542        Args:
543            config_path: Explicit path to config file. If None, searches standard locations.
544
545        Returns:
546            Config instance with loaded or default values.
547        """
548        if config_path is None:
549            config_path = Config._get_config_path()
550
551        try:
552            if config_path and config_path.exists():
553                with config_path.open("rb") as f:
554                    config_data = tomllib.load(f)
555                return _dict_to_dataclass(cls, config_data)
556        except Exception as e:
557            print(
558                f"[ FATAL CONFIGURATION ERROR ] Could not read qq config '{config_path}': {e}."
559            )
560            print(
561                "[ FATAL CONFIGURATION ERROR ] Falling back to default configuration.\n\n"
562            )
563
564        # no config found - use defaults
565        return cls()
566
567    @staticmethod
568    def _get_config_path() -> Path | None:
569        """
570        Search for config file in standard locations (XDG compliant).
571        Returns the first existing config file, or None.
572        """
573        config_locations: list[Path | None] = [
574            # 1. Explicit environment variable (highest priority)
575            Path(env_path) if (env_path := os.getenv("QQ_CONFIG")) else None,
576            # 2. Current working directory (for development/override)
577            Path.cwd() / "qq_config.toml",
578            # 3. XDG config home (standard user config location)
579            Path(os.getenv("XDG_CONFIG_HOME", Path.home() / ".config"))
580            / "qq"
581            / "config.toml",
582        ]
583
584        for path in config_locations:
585            if path and path.is_file():
586                return path
587
588        return None

Main configuration for qq.

Config( suffixes: FileSuffixes = <factory>, env_vars: EnvironmentVariables = <factory>, timeouts: TimeoutSettings = <factory>, runner: RunnerSettings = <factory>, resubmitter: ResubmitterSettings = <factory>, archiver: ArchiverSettings = <factory>, goer: GoerSettings = <factory>, presenter: PresenterSettings = <factory>, loop_jobs: LoopJobSettings = <factory>, jobs_presenter: JobsPresenterSettings = <factory>, queues_presenter: QueuesPresenterSettings = <factory>, nodes_presenter: NodesPresenterSettings = <factory>, date_formats: DateFormats = <factory>, exit_codes: ExitCodes = <factory>, state_colors: StateColors = <factory>, size: SizeOptions = <factory>, pbs_options: PBSOptions = <factory>, slurm_options: SlurmOptions = <factory>, slurm_it4i_options: SlurmIT4IOptions = <factory>, slurm_lumi_options: SlurmLumiOptions = <factory>, transfer_files_options: TransferFilesOptions = <factory>, batch_servers_options: BatchServersOptions = <factory>, parallelization_options: ParallelizationOptions = <factory>, binary_name: str = 'qq')
suffixes: FileSuffixes
timeouts: TimeoutSettings
runner: RunnerSettings
resubmitter: ResubmitterSettings
archiver: ArchiverSettings
goer: GoerSettings
presenter: PresenterSettings
loop_jobs: LoopJobSettings
jobs_presenter: JobsPresenterSettings
queues_presenter: QueuesPresenterSettings
nodes_presenter: NodesPresenterSettings
date_formats: DateFormats
exit_codes: ExitCodes
state_colors: StateColors
size: SizeOptions
pbs_options: PBSOptions
slurm_options: SlurmOptions
slurm_it4i_options: SlurmIT4IOptions
slurm_lumi_options: SlurmLumiOptions
transfer_files_options: TransferFilesOptions
batch_servers_options: BatchServersOptions
parallelization_options: ParallelizationOptions
binary_name: str = 'qq'
@classmethod
def load(cls, config_path: pathlib._local.Path | None = None) -> Self:
537    @classmethod
538    def load(cls, config_path: Path | None = None) -> Self:
539        """
540        Load configuration from TOML file or use defaults.
541
542        Args:
543            config_path: Explicit path to config file. If None, searches standard locations.
544
545        Returns:
546            Config instance with loaded or default values.
547        """
548        if config_path is None:
549            config_path = Config._get_config_path()
550
551        try:
552            if config_path and config_path.exists():
553                with config_path.open("rb") as f:
554                    config_data = tomllib.load(f)
555                return _dict_to_dataclass(cls, config_data)
556        except Exception as e:
557            print(
558                f"[ FATAL CONFIGURATION ERROR ] Could not read qq config '{config_path}': {e}."
559            )
560            print(
561                "[ FATAL CONFIGURATION ERROR ] Falling back to default configuration.\n\n"
562            )
563
564        # no config found - use defaults
565        return cls()

Load configuration from TOML file or use defaults.

Arguments:
  • config_path: Explicit path to config file. If None, searches standard locations.
Returns:

Config instance with loaded or default values.

CFG = Config(suffixes=FileSuffixes(qq_info='.qqinfo', qq_out='.qqout', stdout='.out', stderr='.err'), env_vars=EnvironmentVariables(guard='QQ_ENV_SET', debug_mode='QQ_DEBUG', info_file='QQ_INFO', input_machine='QQ_INPUT_MACHINE', input_dir='QQ_INPUT_DIR', shared_submit='QQ_SHARED_SUBMIT', batch_system='QQ_BATCH_SYSTEM', loop_current='QQ_LOOP_CURRENT', loop_start='QQ_LOOP_START', loop_end='QQ_LOOP_END', no_resubmit='QQ_NO_RESUBMIT', archive_format='QQ_ARCHIVE_FORMAT', pbs_scratch_dir='SCRATCHDIR', slurm_job_account='SLURM_JOB_ACCOUNT', lumi_scratch_type='LUMI_SCRATCH_TYPE', ncpus='QQ_NCPUS', ngpus='QQ_NGPUS', nnodes='QQ_NNODES', walltime='QQ_WALLTIME'), timeouts=TimeoutSettings(ssh=60, rsync=600), runner=RunnerSettings(retry_tries=3, retry_wait=300, sigterm_to_sigkill=5, subprocess_checks_wait_time=2, default_interpreter='bash'), resubmitter=ResubmitterSettings(retry_tries=3, retry_wait=300, default_resubmit_hosts=''), archiver=ArchiverSettings(retry_tries=3, retry_wait=300), goer=GoerSettings(wait_time=5), presenter=PresenterSettings(job_status_panel=JobStatusPanelSettings(max_width=None, min_width=70, border_style='white', title_style='white bold'), full_info_panel=FullInfoPanelSettings(max_width=None, min_width=80, border_style='white', title_style='white bold', rule_style='white'), brief_info=BriefInfoSettings(job_id_color='default', dir_path_color='cyan', loop_info_color='grey70'), key_style='default bold', value_style='white', notes_style='grey50'), loop_jobs=LoopJobSettings(pattern='+%04d', archive_format='job%04d', archive_dir='storage'), jobs_presenter=JobsPresenterSettings(max_width=None, min_width=80, max_job_name_length=20, max_nodes_length=40, border_style='white', title_style='white bold', subtitle_style='white bold', headers_style='default', main_style='white', secondary_style='grey70', extra_info_style='grey50', strong_warning_style='bright_red', mild_warning_style='bright_yellow', columns_to_show=None, sum_jobs_code='Σ'), queues_presenter=QueuesPresenterSettings(max_width=None, min_width=80, border_style='white', title_style='white bold', subtitle_style='white bold', headers_style='default', available_mark_style='bright_green', unavailable_mark_style='bright_red', dangling_mark_style='bright_yellow', main_text_style='white', rerouted_text_style='grey50', other_jobs_code='O', sum_jobs_code='Σ'), nodes_presenter=NodesPresenterSettings(max_width=None, min_width=80, max_props_panel_width=40, border_style='white', title_style='white bold', subtitle_style='white bold', headers_style='default', rule_style='white', others_group_name='other', all_nodes_group_name='all nodes', main_text_style='white', secondary_text_style='grey70', free_node_style='bright_green bold', part_free_node_style='green', busy_node_style='blue'), date_formats=DateFormats(standard='%Y-%m-%d %H:%M:%S', pbs='%a %b %d %H:%M:%S %Y', slurm='%Y-%m-%dT%H:%M:%S'), exit_codes=ExitCodes(not_qq_env=90, default=91, qq_run_fatal=92, qq_run_communication=93, qq_run_no_resubmit=95, unexpected_error=99), state_colors=StateColors(queued='bright_magenta', held='bright_magenta', suspended='bright_black', waiting='bright_magenta', running='bright_blue', booting='bright_cyan', killed='bright_red', failed='bright_red', finished='bright_green', exiting='bright_yellow', in_an_inconsistent_state='grey70', unknown='grey70', sum='white', other='grey70'), size=SizeOptions(max_rounding_error=0.1), pbs_options=PBSOptions(scratch_dir_inner='main'), slurm_options=SlurmOptions(jobs_scontrol_nthreads=8), slurm_it4i_options=SlurmIT4IOptions(scratch_dir_attempts=3), slurm_lumi_options=SlurmLumiOptions(scratch_dir_attempts=3), transfer_files_options=TransferFilesOptions(default_archive_mode='success', default_transfer_mode='success'), batch_servers_options=BatchServersOptions(known_servers={'robox': 'robox-pro.ceitec.muni.cz', 'sokar': 'sokar-pbs.ncbr.muni.cz', 'metacentrum': 'pbs-m1.metacentrum.cz', 'meta': 'pbs-m1.metacentrum.cz'}, known_output_hosts={'robox-pro.ceitec.muni.cz': 'st1.ceitec.muni.cz', 'sokar-pbs.ncbr.muni.cz': 'sokar.ncbr.muni.cz', 'pbs-m1.metacentrum.cz': 'perian.metacentrum.cz'}), parallelization_options=ParallelizationOptions(job_info_max_threads=16, submission_max_threads=8, clear_max_threads=8), binary_name='qq')