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()

python.GeneratorSettingsSemantics._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 439 of file GeneratorSettingsSemantics.py.

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

◆ _build_records()

python.GeneratorSettingsSemantics._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 287 of file GeneratorSettingsSemantics.py.

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

◆ _build_report()

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

Definition at line 368 of file GeneratorSettingsSemantics.py.

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

◆ _deduplicate_records()

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

Definition at line 340 of file GeneratorSettingsSemantics.py.

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

◆ _find_conflicts()

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

Definition at line 503 of file GeneratorSettingsSemantics.py.

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

◆ _log_report()

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

Definition at line 542 of file GeneratorSettingsSemantics.py.

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

◆ _normalize_text()

python.GeneratorSettingsSemantics._normalize_text ( value)
protected

Definition at line 335 of file GeneratorSettingsSemantics.py.

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

◆ _ordered_layers()

python.GeneratorSettingsSemantics._ordered_layers ( layers)
protected
Apply precedence before deduplication.

Definition at line 275 of file GeneratorSettingsSemantics.py.

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

◆ _parse_assignment()

python.GeneratorSettingsSemantics._parse_assignment ( setting_text,
separators )
protected

Definition at line 326 of file GeneratorSettingsSemantics.py.

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

Variable Documentation

◆ genSettingsLog

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

Definition at line 9 of file GeneratorSettingsSemantics.py.