Coverage for pyEDAA/OSVVM/AlertLog.py: 80%

282 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-12 23:17 +0000

1# ==================================================================================================================== # 

2# _____ ____ _ _ ___ ______ ____ ____ __ # 

3# _ __ _ _| ____| _ \ / \ / \ / _ \/ ___\ \ / /\ \ / / \/ | # 

4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | \___ \\ \ / / \ \ / /| |\/| | # 

5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |_| |___) |\ V / \ V / | | | | # 

6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)___/|____/ \_/ \_/ |_| |_| # 

7# |_| |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # 

15# # 

16# Licensed under the Apache License, Version 2.0 (the "License"); # 

17# you may not use this file except in compliance with the License. # 

18# You may obtain a copy of the License at # 

19# # 

20# http://www.apache.org/licenses/LICENSE-2.0 # 

21# # 

22# Unless required by applicable law or agreed to in writing, software # 

23# distributed under the License is distributed on an "AS IS" BASIS, # 

24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

25# See the License for the specific language governing permissions and # 

26# limitations under the License. # 

27# # 

28# SPDX-License-Identifier: Apache-2.0 # 

29# ==================================================================================================================== # 

30# 

31"""A data model for OSVVM's AlertLog YAML file format.""" 

32from datetime import timedelta 

33from enum import Enum, auto 

34from pathlib import Path 

35from typing import Optional as Nullable, Dict, Iterator, Iterable, Callable 

36 

37from ruamel.yaml import YAML, CommentedSeq, CommentedMap 

38from pyTooling.Decorators import readonly, export 

39from pyTooling.MetaClasses import ExtendedType 

40from pyTooling.Common import getFullyQualifiedName 

41from pyTooling.Stopwatch import Stopwatch 

42from pyTooling.Tree import Node 

43from pyTooling.Versioning import SemanticVersion 

44 

45from pyEDAA.OSVVM import OSVVMException 

46 

47 

48@export 

49class AlertLogException(OSVVMException): 

50 """Base-class for all pyEDAA.OSVVM.AlertLog specific exceptions.""" 

51 

52 

53@export 

54class DuplicateItemException(AlertLogException): 

55 """Raised if a duplicate item is detected in the AlertLog hierarchy.""" 

56 

57 

58@export 

59class AlertLogStatus(Enum): 

60 """Status of an :class:`AlertLogItem`.""" 

61 Unknown = auto() 

62 Passed = auto() 

63 Failed = auto() 

64 

65 __MAPPINGS__ = { 

66 "passed": Passed, 

67 "failed": Failed 

68 } 

69 

70 @classmethod 

71 def Parse(self, name: str) -> "AlertLogStatus": 

72 try: 

73 return self.__MAPPINGS__[name.lower()] 

74 except KeyError as ex: 

75 raise AlertLogException(f"Unknown AlertLog status '{name}'.") from ex 

76 

77 def __bool__(self) -> bool: 

78 """ 

79 Convert an *AlertLogStatus* to a boolean. 

80 

81 :returns: Return true, if the status is ``Passed``. 

82 """ 

83 return self is self.Passed 

84 

85 

86@export 

87def _format(node: Node) -> str: 

88 """ 

89 User-defined :external+pyTool:ref:`pyTooling Tree <STRUCT/Tree>` formatting function for nodes referencing :class:`AlertLogItems <AlertLogItem>`. 

90 

91 :param node: Node to format. 

92 :returns: String representation (one-liner) describing an AlertLogItem. 

93 """ 

94 return f"{node['Name']}: {node['TotalErrors']}={node['AlertCountFailures']}/{node['AlertCountErrors']}/{node['AlertCountWarnings']} {node['PassedCount']}/{node['AffirmCount']}" 

95 

96 

97@export 

98class AlertLogItem(metaclass=ExtendedType, slots=True): 

99 """ 

100 An *AlertLogItem* represents an AlertLog hierarchy item. 

101 

102 An item has a reference to its parent item in the AlertLog hierarchy. If the item is the top-most element (root 

103 element), the parent reference is ``None``. 

104 

105 An item can contain further child items. 

106 """ 

107 _parent: "AlertLogItem" #: Reference to the parent item. 

108 _name: str #: Name of the AlertLog item. 

109 _children: Dict[str, "AlertLogItem"] #: Dictionary of child items. 

110 

111 _status: AlertLogStatus #: AlertLog item's status 

112 _totalErrors: int #: Total number of warnings, errors and failures. 

113 _alertCountWarnings: int #: Warning count. 

114 _alertCountErrors: int #: Error count. 

115 _alertCountFailures: int #: Failure count. 

116 _passedCount: int #: Passed affirmation count. 

117 _affirmCount: int #: Overall affirmation count (incl. failed affirmations). 

118 _requirementsPassed: int #: Count of passed requirements. 

119 _requirementsGoal: int #: Overall expected requirements. 

120 _disabledAlertCountWarnings: int #: Count of disabled warnings. 

121 _disabledAlertCountErrors: int #: Count of disabled errors. 

122 _disabledAlertCountFailures: int #: Count of disabled failures. 

123 

124 def __init__( 

125 self, 

126 name: str, 

127 status: AlertLogStatus = AlertLogStatus.Unknown, 

128 totalErrors: int = 0, 

129 alertCountWarnings: int = 0, 

130 alertCountErrors: int = 0, 

131 alertCountFailures: int = 0, 

132 passedCount: int = 0, 

133 affirmCount: int = 0, 

134 requirementsPassed: int = 0, 

135 requirementsGoal: int = 0, 

136 disabledAlertCountWarnings: int = 0, 

137 disabledAlertCountErrors: int = 0, 

138 disabledAlertCountFailures: int = 0, 

139 children: Iterable["AlertLogItem"] = None, 

140 parent: Nullable["AlertLogItem"] = None 

141 ) -> None: 

142 self._name = name 

143 self._parent = parent 

144 if parent is not None: 

145 if not isinstance(parent, AlertLogItem): 145 ↛ 146line 145 didn't jump to line 146 because the condition on line 145 was never true

146 ex = TypeError(f"Parameter 'parent' is not an AlertLogItem.") 

147 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") 

148 raise ex 

149 elif name in parent._children: 149 ↛ 150line 149 didn't jump to line 150 because the condition on line 149 was never true

150 raise DuplicateItemException(f"AlertLogItem '{name}' already exists in '{parent._name}'.") 

151 

152 parent._children[name] = self 

153 

154 self._children = {} 

155 if children is not None: 

156 for child in children: 

157 if not isinstance(child, AlertLogItem): 157 ↛ 158line 157 didn't jump to line 158 because the condition on line 157 was never true

158 ex = TypeError(f"Item in parameter 'children' is not an AlertLogItem.") 

159 ex.add_note(f"Got type '{getFullyQualifiedName(child)}'.") 

160 raise ex 

161 elif child._name in self._children: 161 ↛ 162line 161 didn't jump to line 162 because the condition on line 161 was never true

162 raise DuplicateItemException(f"AlertLogItem '{child._name}' already exists in '{self._name}'.") 

163 elif child._parent is not None: 163 ↛ 164line 163 didn't jump to line 164 because the condition on line 163 was never true

164 raise AlertLogException(f"AlertLogItem '{child._name}' is already part of another AlertLog hierarchy ({child._parent._name}).") 

165 

166 self._children[child._name] = child 

167 child._parent = self 

168 

169 self._status = status 

170 self._totalErrors = totalErrors 

171 self._alertCountWarnings = alertCountWarnings 

172 self._alertCountErrors = alertCountErrors 

173 self._alertCountFailures = alertCountFailures 

174 self._passedCount = passedCount 

175 self._affirmCount = affirmCount 

176 self._requirementsPassed = requirementsPassed 

177 self._requirementsGoal = requirementsGoal 

178 self._disabledAlertCountWarnings = disabledAlertCountWarnings 

179 self._disabledAlertCountErrors = disabledAlertCountErrors 

180 self._disabledAlertCountFailures = disabledAlertCountFailures 

181 

182 @property 

183 def Parent(self) -> Nullable["AlertLogItem"]: 

184 """ 

185 Property to access the parent item of this item (:attr:`_parent`). 

186 

187 :returns: The item's parent item. ``None``, if it's the top-most item (root). 

188 """ 

189 return self._parent 

190 

191 @Parent.setter 

192 def Parent(self, value: Nullable["AlertLogItem"]) -> None: 

193 if value is None: 193 ↛ 194line 193 didn't jump to line 194 because the condition on line 193 was never true

194 del self._parent._children[self._name] 

195 else: 

196 if not isinstance(value, AlertLogItem): 196 ↛ 197line 196 didn't jump to line 197 because the condition on line 196 was never true

197 ex = TypeError(f"Parameter 'value' is not an AlertLogItem.") 

198 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

199 raise ex 

200 elif self._name in value._children: 200 ↛ 201line 200 didn't jump to line 201 because the condition on line 200 was never true

201 raise DuplicateItemException(f"AlertLogItem '{self._name}' already exists in '{value._name}'.") 

202 

203 value._children[self._name] = self 

204 

205 self._parent = value 

206 

207 @readonly 

208 def Name(self) -> str: 

209 """ 

210 Read-only property to access the AlertLog item's name (:attr:`_name`). 

211 

212 :returns: AlertLog item's name. 

213 """ 

214 return self._name 

215 

216 @readonly 

217 def Status(self) -> AlertLogStatus: 

218 """ 

219 Read-only property to access the AlertLog item's status (:attr:`_status`). 

220 

221 :returns: AlertLog item's status. 

222 """ 

223 return self._status 

224 

225 @readonly 

226 def TotalErrors(self) -> int: 

227 """ 

228 Read-only property to access the AlertLog item's total error count (:attr:`_totalErrors`). 

229 

230 :returns: AlertLog item's total errors. 

231 """ 

232 return self._totalErrors 

233 

234 @readonly 

235 def AlertCountWarnings(self) -> int: 

236 """ 

237 Read-only property to access the AlertLog item's warning count (:attr:`_alertCountWarnings`). 

238 

239 :returns: AlertLog item's warning count. 

240 """ 

241 return self._alertCountWarnings 

242 

243 @readonly 

244 def AlertCountErrors(self) -> int: 

245 """ 

246 Read-only property to access the AlertLog item's error count (:attr:`_alertCountErrors`). 

247 

248 :returns: AlertLog item's error count. 

249 """ 

250 return self._alertCountErrors 

251 

252 @readonly 

253 def AlertCountFailures(self) -> int: 

254 """ 

255 Read-only property to access the AlertLog item's failure count (:attr:`_alertCountFailures`). 

256 

257 :returns: AlertLog item's failure count. 

258 """ 

259 return self._alertCountFailures 

260 

261 @readonly 

262 def PassedCount(self) -> int: 

263 """ 

264 Read-only property to access the AlertLog item's passed affirmation count (:attr:`_alertCountFailures`). 

265 

266 :returns: AlertLog item's passed affirmations. 

267 """ 

268 return self._passedCount 

269 

270 @readonly 

271 def AffirmCount(self) -> int: 

272 """ 

273 Read-only property to access the AlertLog item's overall affirmation count (:attr:`_affirmCount`). 

274 

275 :returns: AlertLog item's overall affirmations. 

276 """ 

277 return self._affirmCount 

278 

279 @readonly 

280 def RequirementsPassed(self) -> int: 

281 return self._requirementsPassed 

282 

283 @readonly 

284 def RequirementsGoal(self) -> int: 

285 return self._requirementsGoal 

286 

287 @readonly 

288 def DisabledAlertCountWarnings(self) -> int: 

289 """ 

290 Read-only property to access the AlertLog item's count of disabled warnings (:attr:`_disabledAlertCountWarnings`). 

291 

292 :returns: AlertLog item's count of disabled warnings. 

293 """ 

294 return self._disabledAlertCountWarnings 

295 

296 @readonly 

297 def DisabledAlertCountErrors(self) -> int: 

298 """ 

299 Read-only property to access the AlertLog item's count of disabled errors (:attr:`_disabledAlertCountErrors`). 

300 

301 :returns: AlertLog item's count of disabled errors. 

302 """ 

303 return self._disabledAlertCountErrors 

304 

305 @readonly 

306 def DisabledAlertCountFailures(self) -> int: 

307 """ 

308 Read-only property to access the AlertLog item's count of disabled failures (:attr:`_disabledAlertCountFailures`). 

309 

310 :returns: AlertLog item's count of disabled failures. 

311 """ 

312 return self._disabledAlertCountFailures 

313 

314 @readonly 

315 def Children(self) -> Dict[str, "AlertLogItem"]: 

316 return self._children 

317 

318 def __iter__(self) -> Iterator["AlertLogItem"]: 

319 """ 

320 Iterate all child AlertLog items. 

321 

322 :returns: An iterator of child items. 

323 """ 

324 return iter(self._children.values()) 

325 

326 def __len__(self) -> int: 

327 """ 

328 Returns number of child AlertLog items. 

329 

330 :returns: The number of nested AlertLog items. 

331 """ 

332 return len(self._children) 

333 

334 def __getitem__(self, name: str) -> "AlertLogItem": 

335 """Index access for returning child AlertLog items. 

336 

337 :param name: The child's name. 

338 :returns: The referenced child. 

339 :raises KeyError: When the child referenced by parameter 'name' doesn't exist. 

340 """ 

341 return self._children[name] 

342 

343 def ToTree(self, format: Callable[[Node], str] = _format) -> Node: 

344 """ 

345 Convert the AlertLog hierarchy starting from this AlertLog item to a :external+pyTool:ref:`pyTooling Tree <STRUCT/Tree>`. 

346 

347 :params format: A user-defined :external+pyTool:ref:`pyTooling Tree <STRUCT/Tree>` formatting function. 

348 :returns: A tree of nodes referencing an AlertLog item. 

349 """ 

350 node = Node( 

351 value=self, 

352 keyValuePairs={ 

353 "Name": self._name, 

354 "TotalErrors": self._totalErrors, 

355 "AlertCountFailures": self._alertCountFailures, 

356 "AlertCountErrors": self._alertCountErrors, 

357 "AlertCountWarnings": self._alertCountWarnings, 

358 "PassedCount": self._passedCount, 

359 "AffirmCount": self._affirmCount 

360 }, 

361 children=(child.ToTree() for child in self._children.values()), 

362 format=format 

363 ) 

364 

365 return node 

366 

367 

368@export 

369class Settings(metaclass=ExtendedType, mixin=True): 

370 _externalWarningCount: int 

371 _externalErrorCount: int 

372 _externalFailureCount: int 

373 _failOnDisabledErrors: bool 

374 _failOnRequirementErrors: bool 

375 _failOnWarning: bool 

376 

377 def __init__(self) -> None: 

378 self._externalWarningCount = 0 

379 self._externalErrorCount = 0 

380 self._externalFailureCount = 0 

381 self._failOnDisabledErrors = False 

382 self._failOnRequirementErrors = True 

383 self._failOnWarning = False 

384 

385 

386# TODO: Derive from common Document class? 

387@export 

388class Document(AlertLogItem, Settings): 

389 """ 

390 An *AlertLog Document* represents an OSVVM AlertLog report document (YAML file). 

391 

392 The document inherits :class:`AlertLogItem` and represents the AlertLog hierarchy's root element. 

393 

394 When analyzing and converting the document, the YAML analysis duration as well as the model conversion duration gets 

395 captured. 

396 """ 

397 

398 _path: Path #: Path to the YAML file. 

399 _yamlDocument: Nullable[YAML] #: Internal YAML document instance. 

400 _version: Nullable[SemanticVersion] #: YAML data structure version. 

401 

402 _analysisDuration: Nullable[timedelta] #: YAML file analysis duration in seconds. 

403 _modelConversionDuration: Nullable[timedelta] #: Data structure conversion duration in seconds. 

404 

405 def __init__(self, filename: Path, analyzeAndConvert: bool = False) -> None: 

406 """ 

407 Initializes an AlertLog YAML document. 

408 

409 :param filename: Path to the YAML file. 

410 :param analyzeAndConvert: If true, analyze the YAML document and convert the content to an AlertLog data model instance. 

411 """ 

412 super().__init__("", parent=None) 

413 Settings.__init__(self) 

414 

415 self._path = filename 

416 self._yamlDocument = None 

417 self._version = None 

418 

419 self._analysisDuration = None 

420 self._modelConversionDuration = None 

421 

422 if analyzeAndConvert: 

423 self.Analyze() 

424 self.Parse() 

425 

426 @property 

427 def Path(self) -> Path: 

428 """ 

429 Read-only property to access the path to the YAML file of this document (:attr:`_path`). 

430 

431 :returns: The document's path to the YAML file. 

432 """ 

433 return self._path 

434 

435 @readonly 

436 def AnalysisDuration(self) -> timedelta: 

437 """ 

438 Read-only property to access the time spent for YAML file analysis (:attr:`_analysisDuration`). 

439 

440 :returns: The YAML file analysis duration. 

441 """ 

442 if self._analysisDuration is None: 

443 raise AlertLogException(f"Document '{self._path}' was not analyzed.") 

444 

445 return self._analysisDuration 

446 

447 @readonly 

448 def ModelConversionDuration(self) -> timedelta: 

449 """ 

450 Read-only property to access the time spent for data structure to AlertLog hierarchy conversion (:attr:`_modelConversionDuration`). 

451 

452 :returns: The data structure conversion duration. 

453 """ 

454 if self._modelConversionDuration is None: 

455 raise AlertLogException(f"Document '{self._path}' was not converted.") 

456 

457 return self._modelConversionDuration 

458 

459 def Analyze(self) -> None: 

460 """ 

461 Analyze the YAML file (specified by :attr:`_path`) and store the YAML document in :attr:`_yamlDocument`. 

462 

463 :raises AlertLogException: If YAML file doesn't exist. 

464 :raises AlertLogException: If YAML file can't be opened. 

465 """ 

466 if not self._path.exists(): 466 ↛ 467line 466 didn't jump to line 467 because the condition on line 466 was never true

467 raise AlertLogException(f"OSVVM AlertLog YAML file '{self._path}' does not exist.") \ 

468 from FileNotFoundError(f"File '{self._path}' not found.") 

469 

470 with Stopwatch() as sw: 

471 try: 

472 yamlReader = YAML() 

473 self._yamlDocument = yamlReader.load(self._path) 

474 except Exception as ex: 

475 raise AlertLogException(f"Couldn't open '{self._path}'.") from ex 

476 

477 self._analysisDuration = timedelta(seconds=sw.Duration) 

478 

479 def Parse(self) -> None: 

480 """ 

481 Convert the YAML data structure to a hierarchy of :class:`AlertLogItem` instances. 

482 

483 :raises AlertLogException: If YAML file was not analyzed. 

484 """ 

485 if self._yamlDocument is None: 485 ↛ 486line 485 didn't jump to line 486 because the condition on line 485 was never true

486 ex = AlertLogException(f"OSVVM AlertLog YAML file '{self._path}' needs to be read and analyzed by a YAML parser.") 

487 ex.add_note(f"Call 'Document.Analyze()' or create the document using 'Document(path, parse=True)'.") 

488 raise ex 

489 

490 with Stopwatch() as sw: 

491 self._version = SemanticVersion.Parse(self._yamlDocument["Version"]) 

492 if not (self._version >> "0.1"): 492 ↛ 493line 492 didn't jump to line 493 because the condition on line 492 was never true

493 ex = AlertLogException(f"Unsupported YAML data structure version {self._version} for file '{self._path}'.") 

494 ex.add_note("Supported versions are: 1.0") 

495 raise ex 

496 

497 self._name = self._ParseStrFieldFromYAML(self._yamlDocument, "Name") 

498 self._status = AlertLogStatus.Parse(self._ParseStrFieldFromYAML(self._yamlDocument, "Status")) 

499 for child in self._ParseSequenceFromYAML(self._yamlDocument, "Children"): 

500 _ = self._ParseAlertLogItem(child, self) 

501 

502 self._modelConversionDuration = timedelta(seconds=sw.Duration) 

503 

504 @staticmethod 

505 def _ParseSequenceFromYAML(node: CommentedMap, fieldName: str) -> Nullable[CommentedSeq]: 

506 try: 

507 value = node[fieldName] 

508 except KeyError as ex: 

509 newEx = OSVVMException(f"Sequence field '{fieldName}' not found in node starting at line {node.lc.line + 1}.") 

510 newEx.add_note(f"Available fields: {', '.join(key for key in node)}") 

511 raise newEx from ex 

512 

513 if value is None: 513 ↛ 514line 513 didn't jump to line 514 because the condition on line 513 was never true

514 return () 

515 elif not isinstance(value, CommentedSeq): 515 ↛ 516line 515 didn't jump to line 516 because the condition on line 515 was never true

516 ex = AlertLogException(f"Field '{fieldName}' is not a sequence.") # TODO: from TypeError?? 

517 ex.add_note(f"Found type {value.__class__.__name__} at line {node._yaml_line_col.data[fieldName][0] + 1}.") 

518 raise ex 

519 

520 return value 

521 

522 @staticmethod 

523 def _ParseMapFromYAML(node: CommentedMap, fieldName: str) -> Nullable[CommentedMap]: 

524 try: 

525 value = node[fieldName] 

526 except KeyError as ex: 

527 newEx = OSVVMException(f"Dictionary field '{fieldName}' not found in node starting at line {node.lc.line + 1}.") 

528 newEx.add_note(f"Available fields: {', '.join(key for key in node)}") 

529 raise newEx from ex 

530 

531 if value is None: 531 ↛ 532line 531 didn't jump to line 532 because the condition on line 531 was never true

532 return {} 

533 elif not isinstance(value, CommentedMap): 533 ↛ 534line 533 didn't jump to line 534 because the condition on line 533 was never true

534 ex = AlertLogException(f"Field '{fieldName}' is not a list.") # TODO: from TypeError?? 

535 ex.add_note(f"Type mismatch found for line {node._yaml_line_col.data[fieldName][0] + 1}.") 

536 raise ex 

537 return value 

538 

539 @staticmethod 

540 def _ParseStrFieldFromYAML(node: CommentedMap, fieldName: str) -> Nullable[str]: 

541 try: 

542 value = node[fieldName] 

543 except KeyError as ex: 

544 newEx = OSVVMException(f"String field '{fieldName}' not found in node starting at line {node.lc.line + 1}.") 

545 newEx.add_note(f"Available fields: {', '.join(key for key in node)}") 

546 raise newEx from ex 

547 

548 if not isinstance(value, str): 548 ↛ 549line 548 didn't jump to line 549 because the condition on line 548 was never true

549 raise AlertLogException(f"Field '{fieldName}' is not of type str.") # TODO: from TypeError?? 

550 

551 return value 

552 

553 @staticmethod 

554 def _ParseIntFieldFromYAML(node: CommentedMap, fieldName: str) -> Nullable[int]: 

555 try: 

556 value = node[fieldName] 

557 except KeyError as ex: 

558 newEx = OSVVMException(f"Integer field '{fieldName}' not found in node starting at line {node.lc.line + 1}.") 

559 newEx.add_note(f"Available fields: {', '.join(key for key in node)}") 

560 raise newEx from ex 

561 

562 if not isinstance(value, int): 562 ↛ 563line 562 didn't jump to line 563 because the condition on line 562 was never true

563 raise AlertLogException(f"Field '{fieldName}' is not of type int.") # TODO: from TypeError?? 

564 

565 return value 

566 

567 def _ParseAlertLogItem(self, child: CommentedMap, parent: Nullable[AlertLogItem] = None) -> AlertLogItem: 

568 results = self._ParseMapFromYAML(child, "Results") 

569 yamlAlertCount = self._ParseMapFromYAML(results, "AlertCount") 

570 yamlDisabledAlertCount = self._ParseMapFromYAML(results, "DisabledAlertCount") 

571 alertLogItem = AlertLogItem( 

572 self._ParseStrFieldFromYAML(child, "Name"), 

573 AlertLogStatus.Parse(self._ParseStrFieldFromYAML(child, "Status")), 

574 self._ParseIntFieldFromYAML(results, "TotalErrors"), 

575 self._ParseIntFieldFromYAML(yamlAlertCount, "Warning"), 

576 self._ParseIntFieldFromYAML(yamlAlertCount, "Error"), 

577 self._ParseIntFieldFromYAML(yamlAlertCount, "Failure"), 

578 self._ParseIntFieldFromYAML(results, "PassedCount"), 

579 self._ParseIntFieldFromYAML(results, "AffirmCount"), 

580 self._ParseIntFieldFromYAML(results, "RequirementsPassed"), 

581 self._ParseIntFieldFromYAML(results, "RequirementsGoal"), 

582 self._ParseIntFieldFromYAML(yamlDisabledAlertCount, "Warning"), 

583 self._ParseIntFieldFromYAML(yamlDisabledAlertCount, "Error"), 

584 self._ParseIntFieldFromYAML(yamlDisabledAlertCount, "Failure"), 

585 children=(self._ParseAlertLogItem(ch) for ch in self._ParseSequenceFromYAML(child, "Children")), 

586 parent=parent 

587 ) 

588 

589 return alertLogItem