ATLAS Offline Software
Loading...
Searching...
No Matches
python.GeneratorSettingsSemantics Namespace Reference

Classes

class  GeneratorSettingsKeep
class  GeneratorSettingsRecord
class  GeneratorSettingsPrecedence
class  GeneratorSettingsLayer
class  GeneratorSettingsValue
class  GeneratorSettingsSemantics

Functions

 _ordered_layers (layers)
 _build_records (layers, separators)
 _parse_assignment (setting_text, separators)
 _normalize_text (value)
 _deduplicate_records (records, keep)
 _build_report (records, kept_records, removed_records)
 _build_conflict_details (records, kept_records)
 _find_conflicts (records)
 _log_report (context, report)

Variables

 genSettingsLog = logging.getLogger("GeneratorSettingsSemantics")

Function Documentation

◆ _build_conflict_details()

_build_conflict_details ( records,
kept_records )
protected
Build one reporting entry per conflicting parsed key.

The source and value order follows the original record order, and the kept
value is marked explicitly in the value list.

Definition at line 441 of file GeneratorSettingsSemantics.py.

441def _build_conflict_details(records, kept_records):
442 """
443 Build one reporting entry per conflicting parsed key.
444
445 The source and value order follows the original record order, and the kept
446 value is marked explicitly in the value list.
447 """
448 kept_value_by_key = {}
449 for record in kept_records:
450 if record["record_kind"] != GeneratorSettingsRecord.PARSED_COMMAND:
451 continue
452 kept_value_by_key[record["normalized_key"]] = record["normalized_value"]
453
454 values_by_key = {}
455 for record in records:
456 if record["record_kind"] != GeneratorSettingsRecord.PARSED_COMMAND:
457 continue
458
459 key = record["normalized_key"]
460 source_name = record["source_name"]
461 value = record["normalized_value"]
462
463 key_entry = values_by_key.setdefault(
464 key,
465 {
466 "sources": [],
467 "source_set": set(),
468 "values": [],
469 "value_set": set(),
470 "source_to_values": {},
471 },
472 )
473 if source_name not in key_entry["source_set"]:
474 key_entry["source_set"].add(source_name)
475 key_entry["sources"].append(source_name)
476 if value not in key_entry["value_set"]:
477 key_entry["value_set"].add(value)
478 key_entry["values"].append(value)
479 key_entry["source_to_values"].setdefault(source_name, set()).add(value)
480
481 conflict_details = []
482 for key, key_entry in values_by_key.items():
483 merged_values = set()
484 for values in key_entry["source_to_values"].values():
485 merged_values.update(values)
486 if len(merged_values) <= 1 or len(key_entry["source_to_values"]) <= 1:
487 continue
488
489 kept_value = kept_value_by_key.get(key)
490 marked_values = []
491 for value in key_entry["values"]:
492 if value == kept_value:
493 marked_values.append(f"{value} (kept)")
494 continue
495 marked_values.append(value)
496
497 conflict_details.append({
498 "key": key,
499 "sources": key_entry["sources"],
500 "values": marked_values,
501 })
502 return conflict_details
503
504
STL class.
bool add(const std::string &hname, TKey *tobj)
Definition fastadd.cxx:55

◆ _build_records()

_build_records ( layers,
separators )
protected
Convert raw command strings into normalized records.
Parsed commands are deduplicated by key. Commands that cannot be parsed as
key/value records are deduplicated by their normalized full text.

Definition at line 289 of file GeneratorSettingsSemantics.py.

289def _build_records(layers, separators):
290 """
291 Convert raw command strings into normalized records.
292 Parsed commands are deduplicated by key. Commands that cannot be parsed as
293 key/value records are deduplicated by their normalized full text.
294 """
295 records = []
296 for layer in layers:
297 for raw_setting in layer.values:
298 key_text, value_text = _parse_assignment(raw_setting, separators)
299 if key_text is None:
300 records.append({
301 "source_name": layer.source,
302 "record_kind": GeneratorSettingsRecord.UNPARSED_COMMAND,
303 "normalized_key": None,
304 "normalized_value": None,
305 "original_setting": raw_setting,
306 "dedup_signature": (
307 GeneratorSettingsRecord.UNPARSED_COMMAND,
308 _normalize_text(raw_setting),
309 ),
310 })
311 continue
312
313 normalized_key = _normalize_text(key_text)
314 records.append({
315 "source_name": layer.source,
316 "record_kind": GeneratorSettingsRecord.PARSED_COMMAND,
317 "normalized_key": normalized_key,
318 "normalized_value": _normalize_text(value_text),
319 "original_setting": raw_setting,
320 "dedup_signature": (
321 GeneratorSettingsRecord.PARSED_COMMAND,
322 normalized_key,
323 ),
324 })
325 return records
326
327

◆ _build_report()

_build_report ( records,
kept_records,
removed_records )
protected
Collect duplicate and conflict information for logging/tests.

Definition at line 370 of file GeneratorSettingsSemantics.py.

370def _build_report(records, kept_records, removed_records):
371 """Collect duplicate and conflict information for logging/tests."""
372 removed_duplicates = [
373 {
374 "source": record["source_name"],
375 "duplicate_of_source": record.get("duplicate_of_source_name"),
376 "setting": record["original_setting"],
377 "kept_setting": record.get("kept_original_setting"),
378 "normalized_value": record["normalized_value"],
379 "kept_normalized_value": record.get("kept_normalized_value"),
380 }
381 for record in removed_records
382 ]
383
384 removed_identical = [
385 {
386 "source": record["source"],
387 "duplicate_of_source": record.get("duplicate_of_source"),
388 "setting": record["setting"],
389 "kept_setting": record.get("kept_setting"),
390 }
391 for record in removed_duplicates
392 if record["normalized_value"] == record.get("kept_normalized_value")
393 ]
394 duplicates_in_source = {}
395 duplicates_across_sources = {}
396
397 for record in removed_identical:
398 source_name = record.get("source", "<unknown>")
399 duplicate_of_source = record.get("duplicate_of_source", "<unknown>")
400 if duplicate_of_source == source_name:
401 duplicates_in_source[source_name] = (
402 duplicates_in_source.get(source_name, 0) + 1
403 )
404 continue
405
406 source_pair = (source_name, duplicate_of_source)
407 duplicates_across_sources[source_pair] = (
408 duplicates_across_sources.get(source_pair, 0) + 1
409 )
410
411 conflict_details = _build_conflict_details(records, kept_records)
412
413 return {
414 "removed_duplicates": removed_duplicates,
415 "removed_identical": removed_identical,
416 "conflicting_reassignments": [
417 record
418 for record in removed_duplicates
419 if record["normalized_value"] != record.get("kept_normalized_value")
420 ],
421 "removed_overridden": [
422 record
423 for record in removed_duplicates
424 if record["normalized_value"] != record.get("kept_normalized_value")
425 ],
426 "conflict_details": conflict_details,
427 "conflicts": _find_conflicts(records),
428 "duplicates_in_source": duplicates_in_source,
429 "duplicates_across_sources": [
430 {
431 "source": source_name,
432 "duplicate_of_source": duplicate_of_source,
433 "count": duplicate_count,
434 }
435 for (source_name, duplicate_of_source), duplicate_count
436 in sorted(duplicates_across_sources.items())
437 ],
438 }
439
440

◆ _deduplicate_records()

_deduplicate_records ( records,
keep )
protected
Keep the first or last record for each normalized setting key.

Definition at line 342 of file GeneratorSettingsSemantics.py.

342def _deduplicate_records(records, keep):
343 """Keep the first or last record for each normalized setting key."""
344 keep_last = keep == GeneratorSettingsKeep.LAST
345 records_to_scan = reversed(records) if keep_last else records
346
347 kept_records = []
348 removed_records = []
349 first_seen_by_signature = {}
350 for record in records_to_scan:
351 signature = record["dedup_signature"]
352 if signature in first_seen_by_signature:
353 kept_record = first_seen_by_signature[signature]
354 removed_record = dict(record)
355 removed_record["duplicate_of_source_name"] = kept_record["source_name"]
356 removed_record["kept_normalized_value"] = kept_record["normalized_value"]
357 removed_record["kept_original_setting"] = kept_record["original_setting"]
358 removed_records.append(removed_record)
359 continue
360
361 first_seen_by_signature[signature] = record
362 kept_records.append(record)
363
364 if keep_last:
365 kept_records.reverse()
366 removed_records.reverse()
367 return kept_records, removed_records
368
369

◆ _find_conflicts()

_find_conflicts ( records)
protected
Find keys assigned to multiple values within or across sources.

Definition at line 505 of file GeneratorSettingsSemantics.py.

505def _find_conflicts(records):
506 """Find keys assigned to multiple values within or across sources."""
507 values_by_key_and_source = {}
508 for record in records:
509 if record["record_kind"] != GeneratorSettingsRecord.PARSED_COMMAND:
510 continue
511
512 key = record["normalized_key"]
513 source_name = record["source_name"]
514 value = record["normalized_value"]
515 values_by_key_and_source.setdefault(key, {}).setdefault(
516 source_name,
517 set(),
518 ).add(value)
519
520 conflicts = []
521 for key, source_to_values in values_by_key_and_source.items():
522 for source_name, values in source_to_values.items():
523 if len(values) > 1:
524 conflicts.append({
525 "type": "intra_source_conflict",
526 "key": key,
527 "source": source_name,
528 "values": sorted(values),
529 })
530
531 merged_values = set()
532 for values in source_to_values.values():
533 merged_values.update(values)
534 if len(merged_values) > 1 and len(source_to_values) > 1:
535 conflicts.append({
536 "type": "inter_source_conflict",
537 "key": key,
538 "sources": sorted(source_to_values.keys()),
539 "values": sorted(merged_values),
540 })
541 return conflicts
542
543

◆ _log_report()

_log_report ( context,
report )
protected
Print warnings from the structured report.

Definition at line 544 of file GeneratorSettingsSemantics.py.

544def _log_report(context, report):
545 """Print warnings from the structured report."""
546 issue_prefix = "Potential issue with generator settings"
547
548 duplicates = report.get("removed_identical", [])
549 conflict_details = report.get("conflict_details", [])
550 if not duplicates and not conflict_details:
551 return
552
553 genSettingsLog.warning(
554 f"{issue_prefix} [{context}]: found {len(duplicates)} duplicate "
555 f"setting(s) across sources and {len(conflict_details)} conflicting "
556 f"setting key(s)"
557 )
558
559 for entry in sorted(
560 duplicates,
561 key=lambda record: (
562 record.get("source", ""),
563 record.get("duplicate_of_source", ""),
564 record.get("setting", ""),
565 ),
566 ):
567 source_name = entry.get("source", "<unknown>")
568 duplicate_of_source = entry.get("duplicate_of_source", "<unknown>")
569 setting = entry.get("setting", "<unknown>")
570 source_list = [source_name]
571 if duplicate_of_source != source_name:
572 source_list.append(duplicate_of_source)
573 genSettingsLog.warning(
574 f"{issue_prefix} [{context}]: duplicate setting from sources "
575 f"[{', '.join(source_list)}]: {setting}"
576 )
577
578 for entry in conflict_details:
579 key = entry.get("key", "<unknown>")
580 sources = ", ".join(entry.get("sources", []))
581 values = ", ".join(entry.get("values", []))
582 genSettingsLog.warning(
583 f"{issue_prefix} [{context}]: conflicting setting '{key}' across "
584 f"sources [{sources}] -> [{values}]"
585 )

◆ _normalize_text()

_normalize_text ( value)
protected

Definition at line 337 of file GeneratorSettingsSemantics.py.

337def _normalize_text(value):
338 text = str(value).strip()
339 return " ".join(text.split())
340
341

◆ _ordered_layers()

_ordered_layers ( layers)
protected
Apply precedence before deduplication.

Definition at line 277 of file GeneratorSettingsSemantics.py.

277def _ordered_layers(layers):
278 """Apply precedence before deduplication."""
279 return sorted(
280 layers,
281 key=lambda layer: (
282 int(layer.precedence),
283 layer.source,
284 repr(layer.values),
285 ),
286 )
287
288

◆ _parse_assignment()

_parse_assignment ( setting_text,
separators )
protected

Definition at line 328 of file GeneratorSettingsSemantics.py.

328def _parse_assignment(setting_text, separators):
329 text = str(setting_text).strip()
330 for separator in separators:
331 if separator in text:
332 key_text, value_text = text.split(separator, 1)
333 return key_text.strip(), value_text.strip()
334 return None, None
335
336

Variable Documentation

◆ genSettingsLog

python.GeneratorSettingsSemantics.genSettingsLog = logging.getLogger("GeneratorSettingsSemantics")

Definition at line 9 of file GeneratorSettingsSemantics.py.