-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[WIP] 3482 Add detection for circular macro replacement #4029
Open
Nic-Ma
wants to merge
7
commits into
Project-MONAI:dev
Choose a base branch
from
Nic-Ma:3482-detect-circular-macro
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
42a45e0
Merge pull request #19 from Project-MONAI/master
Nic-Ma cd16a13
Merge pull request #32 from Project-MONAI/master
Nic-Ma 6f87afd
Merge pull request #180 from Project-MONAI/dev
Nic-Ma f398298
Merge pull request #214 from Project-MONAI/dev
Nic-Ma 56ee8ce
Merge pull request #394 from Project-MONAI/dev
Nic-Ma ee33571
[DLMED] detect recursive macro replacement
Nic-Ma 1a45c47
[DLMED] fix min test
Nic-Ma 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 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 |
---|---|---|
|
@@ -13,7 +13,7 @@ | |
import re | ||
from copy import deepcopy | ||
from pathlib import Path | ||
from typing import Any, Dict, Optional, Sequence, Tuple, Union | ||
from typing import Any, Dict, Optional, Sequence, Set, Tuple, Union | ||
|
||
from monai.bundle.config_item import ComponentLocator, ConfigComponent, ConfigExpression, ConfigItem | ||
from monai.bundle.reference_resolver import ReferenceResolver | ||
|
@@ -253,7 +253,7 @@ def read_config(self, f: Union[PathLike, Sequence[PathLike], Dict], **kwargs): | |
content.update(self.load_config_files(f, **kwargs)) | ||
self.set(config=content) | ||
|
||
def _do_resolve(self, config: Any, id: str = ""): | ||
def _do_resolve(self, config: Any, id: str = "", waiting_list: Optional[Set[str]] = None): | ||
""" | ||
Recursively resolve `self.config` to replace the relative ids with absolute ids, for example, | ||
`@##A` means `A` in the upper level. and replace the macro tokens with target content, | ||
|
@@ -266,18 +266,32 @@ def _do_resolve(self, config: Any, id: str = ""): | |
go one level further into the nested structures. | ||
Use digits indexing from "0" for list or other strings for dict. | ||
For example: ``"xform#5"``, ``"net#channels"``. ``""`` indicates the entire ``self.config``. | ||
waiting_list: set of macro replacement ids pending to be resolved. | ||
It's used to detect circular references such as: | ||
`{"A": {"dep": "%B"}, "B": {"dep": "%A"}}`. | ||
|
||
""" | ||
if waiting_list is None: | ||
waiting_list = set() | ||
if isinstance(config, (dict, list)): | ||
for k, v in enumerate(config) if isinstance(config, list) else config.items(): | ||
sub_id = f"{id}{ID_SEP_KEY}{k}" if id != "" else k | ||
config[k] = self._do_resolve(v, sub_id) | ||
config[k] = self._do_resolve(v, sub_id, waiting_list) | ||
if isinstance(config, str): | ||
config = self.resolve_relative_ids(id, config) | ||
if config.startswith(MACRO_KEY): | ||
waiting_list.add(id) | ||
path, ids = ConfigParser.split_path_id(config[len(MACRO_KEY) :]) | ||
parser = ConfigParser(config=self.get() if not path else ConfigParser.load_config_file(path)) | ||
return self._do_resolve(config=deepcopy(parser[ids])) | ||
if not path: | ||
# if the target id is in the waiting list, that's circular references | ||
if ids in waiting_list: | ||
raise ValueError(f"detected circular references in macro replacement '{ids}' for id='{id}'.") | ||
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. So this would be |
||
parser = ConfigParser(config=self.get()) | ||
config = self._do_resolve(deepcopy(parser[ids]), ids, waiting_list) | ||
else: | ||
# don't support recursive macro replacement in another config file | ||
config = ConfigParser(config=ConfigParser.load_config_file(path))[ids] | ||
waiting_list.discard(id) | ||
return config | ||
|
||
def resolve_macro_and_relative_ids(self): | ||
|
This file contains 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 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
waiting_list
is actually a list we could look at the previous item in the exception to help the user trace what's happened.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an interesting idea, I think the previous implementation in
ReferenceResolver
is a list and @wyli simplified it toset
later. @wyli What do you think about it?Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think set is more suitable/efficient for
in
checks here...not sure if the previous item is very intuitive when the circular ref is formed by multiple reference a->b->...->c->a