qq_lib.properties.states

State models for qq jobs across different layers of the system.

This module defines three related enums - NaiveState, BatchState, and RealState - used to represent a job's status as recorded in qq's metadata, reported by the batch system, and interpreted by qq's higher-level logic.

qq often receives partial or inconsistent information (e.g., a job marked as finished locally while still running in the batch system). RealState normalizes these signals into a single coherent state used by qq operators.

  1# Released under MIT License.
  2# Copyright (c) 2025-2026 Ladislav Bartos and Robert Vacha Lab
  3
  4"""
  5State models for qq jobs across different layers of the system.
  6
  7This module defines three related enums - `NaiveState`, `BatchState`, and
  8`RealState` - used to represent a job's status as recorded in qq's metadata,
  9reported by the batch system, and interpreted by qq's higher-level logic.
 10
 11qq often receives partial or inconsistent information (e.g., a job marked as
 12finished locally while still running in the batch system). `RealState`
 13normalizes these signals into a single coherent state used by qq operators.
 14"""
 15
 16from enum import Enum
 17
 18from qq_lib.core.config import CFG
 19from qq_lib.core.logger import get_logger
 20
 21logger = get_logger(__name__)
 22
 23
 24class NaiveState(Enum):
 25    """
 26    Naive state of the job written into qqinfo files.
 27    """
 28
 29    QUEUED = 1
 30    RUNNING = 2
 31    FAILED = 3
 32    FINISHED = 4
 33    KILLED = 5
 34    UNKNOWN = 6
 35
 36    def __str__(self) -> str:
 37        """
 38        Return the lowercase string representation of the enum variant.
 39
 40        Returns:
 41            str: The name of the state in lowercase.
 42        """
 43        return self.name.lower()
 44
 45    @classmethod
 46    def from_str(cls, s: str) -> "NaiveState":
 47        """
 48        Convert a string to the corresponding NaiveState enum variant.
 49
 50        Args:
 51            s (str): String representation of the state (case-insensitive).
 52
 53        Returns:
 54            NaiveState: Corresponding enum variant. Returns UNKNOWN if no match is found.
 55        """
 56        try:
 57            return cls[s.upper()]
 58        except KeyError:
 59            return cls.UNKNOWN
 60
 61
 62class BatchState(Enum):
 63    """
 64    State of the job according to the underlying batch system.
 65    """
 66
 67    RUNNING = 1
 68    QUEUED = 2
 69    FINISHED = 3
 70    FAILED = 4
 71    HELD = 5
 72    EXITING = 6
 73    WAITING = 7
 74    MOVING = 8
 75    SUSPENDED = 9
 76    UNKNOWN = 10
 77
 78    def __str__(self) -> str:
 79        """
 80        Return the lowercase string representation of the enum variant.
 81
 82        Returns:
 83            str: The name of the batch state in lowercase.
 84        """
 85        return self.name.lower()
 86
 87    @classmethod
 88    def _code_to_state(cls) -> dict[str, str]:
 89        """
 90        Internal mapping from one-letter codes to batch state names.
 91
 92        Returns:
 93            dict[str, str]: Mapping of codes to corresponding batch state names.
 94        """
 95        return {
 96            "E": "exiting",
 97            "H": "held",
 98            "Q": "queued",
 99            "R": "running",
100            "T": "moving",
101            "W": "waiting",
102            "S": "suspended",
103            "F": "finished",
104            "X": "failed",
105        }
106
107    @classmethod
108    def from_code(cls, code: str) -> "BatchState":
109        """
110        Convert a one-letter batch system code to a BatchState enum variant.
111
112        Args:
113            code (str): One-letter code representing the batch system state.
114
115        Returns:
116            BatchState: Corresponding enum variant, or UNKNOWN if the code is invalid.
117        """
118        code = code.upper()
119        if code not in cls._code_to_state():
120            return cls.UNKNOWN
121
122        name = cls._code_to_state()[code].upper()
123        return cls[name]
124
125    def to_code(self) -> str:
126        """
127        Return the one-letter code corresponding to this BatchState.
128
129        Returns:
130            str: One-letter code representing the batch state. Returns '?' if unknown.
131        """
132        for k, v in self._code_to_state().items():
133            if v.upper() == self.name:
134                return k
135
136        return "?"
137
138    @property
139    def color(self) -> str:
140        """
141        Return the display color associated with this BatchState.
142
143        Returns:
144            str: A string representing the color for presentation purposes.
145        """
146        if self is BatchState.MOVING:
147            return RealState.QUEUED.color
148        return RealState[self.name].color
149
150
151class RealState(Enum):
152    """
153    Precise state of the job obtained by combining information from NaiveState and BatchState.
154    """
155
156    QUEUED = 1
157    HELD = 2
158    SUSPENDED = 3
159    WAITING = 4
160    RUNNING = 5
161    BOOTING = 6
162    KILLED = 7
163    FAILED = 8
164    FINISHED = 9
165    EXITING = 10
166    IN_AN_INCONSISTENT_STATE = 11
167    UNKNOWN = 12
168
169    def __str__(self) -> str:
170        """
171        Return the human-readable string representation of the state.
172
173        Returns:
174            str: Lowercase string with underscores replaced by spaces.
175        """
176        return self.name.lower().replace("_", " ")
177
178    @classmethod
179    def from_states(
180        cls, naive_state: NaiveState, batch_state: BatchState
181    ) -> "RealState":
182        """
183        Determine the RealState of a job based on its NaiveState and BatchState.
184
185        Args:
186            naive_state (NaiveState): The naive state of the job from qqinfo.
187            batch_state (BatchState): The state of the job according to the batch system.
188
189        Returns:
190            RealState: The corresponding RealState.
191        """
192        logger.debug(
193            f"Converting to RealState from '{naive_state}' and '{batch_state}'."
194        )
195        match (naive_state, batch_state):
196            case (NaiveState.UNKNOWN, _):
197                return cls.UNKNOWN
198
199            case (NaiveState.QUEUED, BatchState.QUEUED | BatchState.MOVING):
200                return cls.QUEUED
201            case (NaiveState.QUEUED, BatchState.HELD):
202                return cls.HELD
203            case (NaiveState.QUEUED, BatchState.SUSPENDED):
204                return cls.SUSPENDED
205            case (NaiveState.QUEUED, BatchState.WAITING):
206                return cls.WAITING
207            case (NaiveState.QUEUED, BatchState.RUNNING):
208                return cls.BOOTING
209            case (NaiveState.QUEUED, _):
210                return cls.IN_AN_INCONSISTENT_STATE
211
212            case (NaiveState.RUNNING, BatchState.RUNNING):
213                return cls.RUNNING
214            case (NaiveState.RUNNING, BatchState.SUSPENDED):
215                return cls.SUSPENDED
216            case (NaiveState.RUNNING, _):
217                return cls.IN_AN_INCONSISTENT_STATE
218
219            case (NaiveState.KILLED, BatchState.RUNNING):
220                return cls.EXITING
221            case (NaiveState.KILLED, _):
222                return cls.KILLED
223
224            case (NaiveState.FINISHED, BatchState.RUNNING):
225                return cls.EXITING
226            case (
227                NaiveState.FINISHED,
228                BatchState.QUEUED
229                | BatchState.WAITING
230                | BatchState.HELD
231                | BatchState.FAILED,
232            ):
233                return cls.IN_AN_INCONSISTENT_STATE
234            case (NaiveState.FINISHED, _):
235                return cls.FINISHED
236
237            case (NaiveState.FAILED, BatchState.RUNNING):
238                return cls.EXITING
239            case (
240                NaiveState.FAILED,
241                BatchState.QUEUED
242                | BatchState.WAITING
243                | BatchState.HELD
244                | BatchState.FINISHED,
245            ):
246                return cls.IN_AN_INCONSISTENT_STATE
247            case (NaiveState.FAILED, _):
248                return cls.FAILED
249
250        return cls.UNKNOWN
251
252    @property
253    def color(self) -> str:
254        """
255        Return the display color associated with this RealState.
256
257        Returns:
258            str: A string representing the color for presentation purposes.
259        """
260        return getattr(CFG.state_colors, self.name.lower())
logger = <Logger qq_lib.properties.states (INFO)>
class NaiveState(enum.Enum):
25class NaiveState(Enum):
26    """
27    Naive state of the job written into qqinfo files.
28    """
29
30    QUEUED = 1
31    RUNNING = 2
32    FAILED = 3
33    FINISHED = 4
34    KILLED = 5
35    UNKNOWN = 6
36
37    def __str__(self) -> str:
38        """
39        Return the lowercase string representation of the enum variant.
40
41        Returns:
42            str: The name of the state in lowercase.
43        """
44        return self.name.lower()
45
46    @classmethod
47    def from_str(cls, s: str) -> "NaiveState":
48        """
49        Convert a string to the corresponding NaiveState enum variant.
50
51        Args:
52            s (str): String representation of the state (case-insensitive).
53
54        Returns:
55            NaiveState: Corresponding enum variant. Returns UNKNOWN if no match is found.
56        """
57        try:
58            return cls[s.upper()]
59        except KeyError:
60            return cls.UNKNOWN

Naive state of the job written into qqinfo files.

QUEUED = <NaiveState.QUEUED: 1>
RUNNING = <NaiveState.RUNNING: 2>
FAILED = <NaiveState.FAILED: 3>
FINISHED = <NaiveState.FINISHED: 4>
KILLED = <NaiveState.KILLED: 5>
UNKNOWN = <NaiveState.UNKNOWN: 6>
@classmethod
def from_str(cls, s: str) -> NaiveState:
46    @classmethod
47    def from_str(cls, s: str) -> "NaiveState":
48        """
49        Convert a string to the corresponding NaiveState enum variant.
50
51        Args:
52            s (str): String representation of the state (case-insensitive).
53
54        Returns:
55            NaiveState: Corresponding enum variant. Returns UNKNOWN if no match is found.
56        """
57        try:
58            return cls[s.upper()]
59        except KeyError:
60            return cls.UNKNOWN

Convert a string to the corresponding NaiveState enum variant.

Arguments:
  • s (str): String representation of the state (case-insensitive).
Returns:

NaiveState: Corresponding enum variant. Returns UNKNOWN if no match is found.

class BatchState(enum.Enum):
 63class BatchState(Enum):
 64    """
 65    State of the job according to the underlying batch system.
 66    """
 67
 68    RUNNING = 1
 69    QUEUED = 2
 70    FINISHED = 3
 71    FAILED = 4
 72    HELD = 5
 73    EXITING = 6
 74    WAITING = 7
 75    MOVING = 8
 76    SUSPENDED = 9
 77    UNKNOWN = 10
 78
 79    def __str__(self) -> str:
 80        """
 81        Return the lowercase string representation of the enum variant.
 82
 83        Returns:
 84            str: The name of the batch state in lowercase.
 85        """
 86        return self.name.lower()
 87
 88    @classmethod
 89    def _code_to_state(cls) -> dict[str, str]:
 90        """
 91        Internal mapping from one-letter codes to batch state names.
 92
 93        Returns:
 94            dict[str, str]: Mapping of codes to corresponding batch state names.
 95        """
 96        return {
 97            "E": "exiting",
 98            "H": "held",
 99            "Q": "queued",
100            "R": "running",
101            "T": "moving",
102            "W": "waiting",
103            "S": "suspended",
104            "F": "finished",
105            "X": "failed",
106        }
107
108    @classmethod
109    def from_code(cls, code: str) -> "BatchState":
110        """
111        Convert a one-letter batch system code to a BatchState enum variant.
112
113        Args:
114            code (str): One-letter code representing the batch system state.
115
116        Returns:
117            BatchState: Corresponding enum variant, or UNKNOWN if the code is invalid.
118        """
119        code = code.upper()
120        if code not in cls._code_to_state():
121            return cls.UNKNOWN
122
123        name = cls._code_to_state()[code].upper()
124        return cls[name]
125
126    def to_code(self) -> str:
127        """
128        Return the one-letter code corresponding to this BatchState.
129
130        Returns:
131            str: One-letter code representing the batch state. Returns '?' if unknown.
132        """
133        for k, v in self._code_to_state().items():
134            if v.upper() == self.name:
135                return k
136
137        return "?"
138
139    @property
140    def color(self) -> str:
141        """
142        Return the display color associated with this BatchState.
143
144        Returns:
145            str: A string representing the color for presentation purposes.
146        """
147        if self is BatchState.MOVING:
148            return RealState.QUEUED.color
149        return RealState[self.name].color

State of the job according to the underlying batch system.

RUNNING = <BatchState.RUNNING: 1>
QUEUED = <BatchState.QUEUED: 2>
FINISHED = <BatchState.FINISHED: 3>
FAILED = <BatchState.FAILED: 4>
HELD = <BatchState.HELD: 5>
EXITING = <BatchState.EXITING: 6>
WAITING = <BatchState.WAITING: 7>
MOVING = <BatchState.MOVING: 8>
SUSPENDED = <BatchState.SUSPENDED: 9>
UNKNOWN = <BatchState.UNKNOWN: 10>
@classmethod
def from_code(cls, code: str) -> BatchState:
108    @classmethod
109    def from_code(cls, code: str) -> "BatchState":
110        """
111        Convert a one-letter batch system code to a BatchState enum variant.
112
113        Args:
114            code (str): One-letter code representing the batch system state.
115
116        Returns:
117            BatchState: Corresponding enum variant, or UNKNOWN if the code is invalid.
118        """
119        code = code.upper()
120        if code not in cls._code_to_state():
121            return cls.UNKNOWN
122
123        name = cls._code_to_state()[code].upper()
124        return cls[name]

Convert a one-letter batch system code to a BatchState enum variant.

Arguments:
  • code (str): One-letter code representing the batch system state.
Returns:

BatchState: Corresponding enum variant, or UNKNOWN if the code is invalid.

def to_code(self) -> str:
126    def to_code(self) -> str:
127        """
128        Return the one-letter code corresponding to this BatchState.
129
130        Returns:
131            str: One-letter code representing the batch state. Returns '?' if unknown.
132        """
133        for k, v in self._code_to_state().items():
134            if v.upper() == self.name:
135                return k
136
137        return "?"

Return the one-letter code corresponding to this BatchState.

Returns:

str: One-letter code representing the batch state. Returns '?' if unknown.

color: str
139    @property
140    def color(self) -> str:
141        """
142        Return the display color associated with this BatchState.
143
144        Returns:
145            str: A string representing the color for presentation purposes.
146        """
147        if self is BatchState.MOVING:
148            return RealState.QUEUED.color
149        return RealState[self.name].color

Return the display color associated with this BatchState.

Returns:

str: A string representing the color for presentation purposes.

class RealState(enum.Enum):
152class RealState(Enum):
153    """
154    Precise state of the job obtained by combining information from NaiveState and BatchState.
155    """
156
157    QUEUED = 1
158    HELD = 2
159    SUSPENDED = 3
160    WAITING = 4
161    RUNNING = 5
162    BOOTING = 6
163    KILLED = 7
164    FAILED = 8
165    FINISHED = 9
166    EXITING = 10
167    IN_AN_INCONSISTENT_STATE = 11
168    UNKNOWN = 12
169
170    def __str__(self) -> str:
171        """
172        Return the human-readable string representation of the state.
173
174        Returns:
175            str: Lowercase string with underscores replaced by spaces.
176        """
177        return self.name.lower().replace("_", " ")
178
179    @classmethod
180    def from_states(
181        cls, naive_state: NaiveState, batch_state: BatchState
182    ) -> "RealState":
183        """
184        Determine the RealState of a job based on its NaiveState and BatchState.
185
186        Args:
187            naive_state (NaiveState): The naive state of the job from qqinfo.
188            batch_state (BatchState): The state of the job according to the batch system.
189
190        Returns:
191            RealState: The corresponding RealState.
192        """
193        logger.debug(
194            f"Converting to RealState from '{naive_state}' and '{batch_state}'."
195        )
196        match (naive_state, batch_state):
197            case (NaiveState.UNKNOWN, _):
198                return cls.UNKNOWN
199
200            case (NaiveState.QUEUED, BatchState.QUEUED | BatchState.MOVING):
201                return cls.QUEUED
202            case (NaiveState.QUEUED, BatchState.HELD):
203                return cls.HELD
204            case (NaiveState.QUEUED, BatchState.SUSPENDED):
205                return cls.SUSPENDED
206            case (NaiveState.QUEUED, BatchState.WAITING):
207                return cls.WAITING
208            case (NaiveState.QUEUED, BatchState.RUNNING):
209                return cls.BOOTING
210            case (NaiveState.QUEUED, _):
211                return cls.IN_AN_INCONSISTENT_STATE
212
213            case (NaiveState.RUNNING, BatchState.RUNNING):
214                return cls.RUNNING
215            case (NaiveState.RUNNING, BatchState.SUSPENDED):
216                return cls.SUSPENDED
217            case (NaiveState.RUNNING, _):
218                return cls.IN_AN_INCONSISTENT_STATE
219
220            case (NaiveState.KILLED, BatchState.RUNNING):
221                return cls.EXITING
222            case (NaiveState.KILLED, _):
223                return cls.KILLED
224
225            case (NaiveState.FINISHED, BatchState.RUNNING):
226                return cls.EXITING
227            case (
228                NaiveState.FINISHED,
229                BatchState.QUEUED
230                | BatchState.WAITING
231                | BatchState.HELD
232                | BatchState.FAILED,
233            ):
234                return cls.IN_AN_INCONSISTENT_STATE
235            case (NaiveState.FINISHED, _):
236                return cls.FINISHED
237
238            case (NaiveState.FAILED, BatchState.RUNNING):
239                return cls.EXITING
240            case (
241                NaiveState.FAILED,
242                BatchState.QUEUED
243                | BatchState.WAITING
244                | BatchState.HELD
245                | BatchState.FINISHED,
246            ):
247                return cls.IN_AN_INCONSISTENT_STATE
248            case (NaiveState.FAILED, _):
249                return cls.FAILED
250
251        return cls.UNKNOWN
252
253    @property
254    def color(self) -> str:
255        """
256        Return the display color associated with this RealState.
257
258        Returns:
259            str: A string representing the color for presentation purposes.
260        """
261        return getattr(CFG.state_colors, self.name.lower())

Precise state of the job obtained by combining information from NaiveState and BatchState.

QUEUED = <RealState.QUEUED: 1>
HELD = <RealState.HELD: 2>
SUSPENDED = <RealState.SUSPENDED: 3>
WAITING = <RealState.WAITING: 4>
RUNNING = <RealState.RUNNING: 5>
BOOTING = <RealState.BOOTING: 6>
KILLED = <RealState.KILLED: 7>
FAILED = <RealState.FAILED: 8>
FINISHED = <RealState.FINISHED: 9>
EXITING = <RealState.EXITING: 10>
IN_AN_INCONSISTENT_STATE = <RealState.IN_AN_INCONSISTENT_STATE: 11>
UNKNOWN = <RealState.UNKNOWN: 12>
@classmethod
def from_states( cls, naive_state: NaiveState, batch_state: BatchState) -> RealState:
179    @classmethod
180    def from_states(
181        cls, naive_state: NaiveState, batch_state: BatchState
182    ) -> "RealState":
183        """
184        Determine the RealState of a job based on its NaiveState and BatchState.
185
186        Args:
187            naive_state (NaiveState): The naive state of the job from qqinfo.
188            batch_state (BatchState): The state of the job according to the batch system.
189
190        Returns:
191            RealState: The corresponding RealState.
192        """
193        logger.debug(
194            f"Converting to RealState from '{naive_state}' and '{batch_state}'."
195        )
196        match (naive_state, batch_state):
197            case (NaiveState.UNKNOWN, _):
198                return cls.UNKNOWN
199
200            case (NaiveState.QUEUED, BatchState.QUEUED | BatchState.MOVING):
201                return cls.QUEUED
202            case (NaiveState.QUEUED, BatchState.HELD):
203                return cls.HELD
204            case (NaiveState.QUEUED, BatchState.SUSPENDED):
205                return cls.SUSPENDED
206            case (NaiveState.QUEUED, BatchState.WAITING):
207                return cls.WAITING
208            case (NaiveState.QUEUED, BatchState.RUNNING):
209                return cls.BOOTING
210            case (NaiveState.QUEUED, _):
211                return cls.IN_AN_INCONSISTENT_STATE
212
213            case (NaiveState.RUNNING, BatchState.RUNNING):
214                return cls.RUNNING
215            case (NaiveState.RUNNING, BatchState.SUSPENDED):
216                return cls.SUSPENDED
217            case (NaiveState.RUNNING, _):
218                return cls.IN_AN_INCONSISTENT_STATE
219
220            case (NaiveState.KILLED, BatchState.RUNNING):
221                return cls.EXITING
222            case (NaiveState.KILLED, _):
223                return cls.KILLED
224
225            case (NaiveState.FINISHED, BatchState.RUNNING):
226                return cls.EXITING
227            case (
228                NaiveState.FINISHED,
229                BatchState.QUEUED
230                | BatchState.WAITING
231                | BatchState.HELD
232                | BatchState.FAILED,
233            ):
234                return cls.IN_AN_INCONSISTENT_STATE
235            case (NaiveState.FINISHED, _):
236                return cls.FINISHED
237
238            case (NaiveState.FAILED, BatchState.RUNNING):
239                return cls.EXITING
240            case (
241                NaiveState.FAILED,
242                BatchState.QUEUED
243                | BatchState.WAITING
244                | BatchState.HELD
245                | BatchState.FINISHED,
246            ):
247                return cls.IN_AN_INCONSISTENT_STATE
248            case (NaiveState.FAILED, _):
249                return cls.FAILED
250
251        return cls.UNKNOWN

Determine the RealState of a job based on its NaiveState and BatchState.

Arguments:
  • naive_state (NaiveState): The naive state of the job from qqinfo.
  • batch_state (BatchState): The state of the job according to the batch system.
Returns:

RealState: The corresponding RealState.

color: str
253    @property
254    def color(self) -> str:
255        """
256        Return the display color associated with this RealState.
257
258        Returns:
259            str: A string representing the color for presentation purposes.
260        """
261        return getattr(CFG.state_colors, self.name.lower())

Return the display color associated with this RealState.

Returns:

str: A string representing the color for presentation purposes.