-
Notifications
You must be signed in to change notification settings - Fork 13
Error for multiple different variadic concepts with the same name #646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
lukaspie
merged 10 commits into
master
from
643-bug-raise-error-if-there-are-two-variadic-concepts-with-the-same-name
Jun 5, 2025
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
2e27912
log error if there re multiple different variadic concepts with the s…
lukaspie eaba076
fix for groups that are not on the same level
lukaspie 29f290e
remove unneeded enumeration
lukaspie d499cc6
continue if keys has no variable concepts
lukaspie 0225333
use existing docs style
lukaspie 40295fa
only remove conflicting keys if each of them is valid
lukaspie 8d0e62b
Apply suggestions from code review
lukaspie aacf70e
use keys_to_remove from global context
lukaspie 6daffdc
catch case where there are multiple fields in the conflicting groups
lukaspie 14f9562
catch case where a key with a concept name has an equivalent non-vari…
lukaspie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -850,6 +850,127 @@ def recurse_tree( | |
|
|
||
| handling_map.get(child.type, handle_unknown_type)(child, keys, prev_path) | ||
|
|
||
| def find_instance_name_conflicts(mapping: MutableMapping[str, str]) -> None: | ||
| """ | ||
| Detect and log conflicts where the same variadic instance name is reused across | ||
| different concept names. | ||
|
|
||
| This function ensures that a given instance name (e.g., 'my_name') is only used | ||
| for a single concept (e.g., SAMPLE or USER, but not both). Reusing the same instance | ||
| name for different concept names (e.g., SAMPLE[my_name] and USER[my_name]) is | ||
| considered a conflict. | ||
|
|
||
| For example, this is a conflict: | ||
| /ENTRY[entry1]/SAMPLE[my_name]/... | ||
| /ENTRY[entry1]/USER[my_name]/... | ||
|
|
||
| But this is NOT a conflict: | ||
| /ENTRY[entry1]/INSTRUMENT[instrument]/FIELD[my_name]/... | ||
| /ENTRY[entry1]/INSTRUMENT[instrument]/DETECTOR[detector]/FIELD[my_name]/... | ||
|
|
||
| When such conflicts are found, an error is logged indicating the instance name | ||
| and the conflicting concept names. Additionally, all keys involved in the conflict | ||
| are logged and added to the `keys_to_remove` list. | ||
|
|
||
| Args: | ||
| mapping (MutableMapping[str, str]): | ||
| The mapping containing the data to validate. | ||
| This should be a dict of `/` separated paths, such as | ||
| "/ENTRY[entry1]/SAMPLE[sample1]/name". | ||
| keys_to_remove (List[str]): | ||
| List of keys that will be removed from the template. This is extended here | ||
| in the case of conflicts. | ||
|
|
||
| """ | ||
| pattern = re.compile(r"(?P<concept_name>[^\[\]/]+)\[(?P<instance>[^\]]+)\]") | ||
|
|
||
| # Tracks instance usage with respect to their parent group | ||
| instance_usage: Dict[Tuple[str, str], List[Tuple[str, str]]] = defaultdict(list) | ||
|
|
||
| for key in mapping: | ||
| matches = list(pattern.finditer(key)) | ||
| if not matches: | ||
| # The keys contains no concepts with variable name, no need to further check | ||
| continue | ||
| for match in matches: | ||
| concept_name = match.group("concept_name") | ||
| instance_name = match.group("instance") | ||
|
|
||
| # Determine the parent path up to just before this match | ||
| parent_path = key[: match.start()] | ||
| child_path = key[match.start() :].split("/", 1)[-1] | ||
|
|
||
| # Here we check if for this key with a concept name, another valid key | ||
| # with a non-concept name exists. | ||
| non_concept_key = f"{parent_path}{instance_name}/{child_path}" | ||
|
|
||
| if non_concept_key in mapping: | ||
| try: | ||
| node = add_best_matches_for(non_concept_key, tree) | ||
| if node is not None: | ||
| collector.collect_and_log( | ||
| key, | ||
| ValidationProblem.KeysWithAndWithoutConcept, | ||
| non_concept_key, | ||
| concept_name, | ||
| ) | ||
| collector.collect_and_log( | ||
| key, ValidationProblem.KeyToBeRemoved, "key" | ||
| ) | ||
| keys_to_remove.append(key) | ||
| continue | ||
| except TypeError: | ||
| pass | ||
|
|
||
| instance_usage[(instance_name, parent_path)].append((concept_name, key)) | ||
|
|
||
| for (instance_name, parent_path), entries in sorted(instance_usage.items()): | ||
| concept_names = {c for c, _ in entries} | ||
| if len(concept_names) > 1: | ||
| keys = sorted(k for _, k in entries) | ||
| collector.collect_and_log( | ||
| instance_name, | ||
| ValidationProblem.DifferentVariadicNodesWithTheSameName, | ||
| entries, | ||
| ) | ||
| # Now that we have name conflicts, we still need to check that there are | ||
| # at least two valid keys in that conflict. Only then we remove these. | ||
| # This takes care of the example with keys like | ||
| # /ENTRY[my_entry]/USER[some_name]/name and /ENTRY[my_entry]/USERS[some_name]/name, | ||
| # where we only want to keep the first one. | ||
| valid_keys_with_name_conflicts = [] | ||
|
|
||
| for key in keys: | ||
| try: | ||
| node = add_best_matches_for(key, tree) | ||
| if node is not None: | ||
| valid_keys_with_name_conflicts.append(key) | ||
| continue | ||
| except TypeError: | ||
| pass | ||
|
Comment on lines
+949
to
+950
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When do we arrive here?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| collector.collect_and_log( | ||
| key, ValidationProblem.KeyToBeRemoved, "key" | ||
| ) | ||
| keys_to_remove.append(key) | ||
|
|
||
| if len(valid_keys_with_name_conflicts) >= 1: | ||
| # At this point, all invalid keys have been removed. | ||
| # If more than one valid concept still uses the same instance name under the same parent path, | ||
| # this indicates a semantic ambiguity (e.g., USER[alex] and SAMPLE[alex]). | ||
| # We remove these keys as well to avoid conflicts in the writer. | ||
| remaining_concepts = { | ||
| pattern.findall(k)[-1][0] | ||
| for k in valid_keys_with_name_conflicts | ||
| if pattern.findall(k) | ||
| } | ||
| # If multiple valid concept names reuse the same instance name, remove them too | ||
| if len(remaining_concepts) > 1: | ||
| for valid_key in valid_keys_with_name_conflicts: | ||
| collector.collect_and_log( | ||
| valid_key, ValidationProblem.KeyToBeRemoved, "key" | ||
| ) | ||
| keys_to_remove.append(valid_key) | ||
|
|
||
| def check_attributes_of_nonexisting_field( | ||
| node: NexusNode, | ||
| ): | ||
|
|
@@ -1174,10 +1295,11 @@ def check_reserved_prefix( | |
| "choice": handle_choice, | ||
| } | ||
|
|
||
| keys_to_remove = [] | ||
| keys_to_remove: List[str] = [] | ||
|
|
||
| tree = generate_tree_from(appdef) | ||
| collector.clear() | ||
| find_instance_name_conflicts(mapping) | ||
| nested_keys = build_nested_dict_from(mapping) | ||
| not_visited = list(mapping) | ||
| keys = _follow_link(nested_keys, "") | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.