parse
TOC
- Functions:
- Classes:
- 🅲 ConfigDict - A specialized dictionary used for parsing and managing configuration data.
Functions
🅵 _dict2node
_dict2node
def _dict2node(
module_type: SpecialFlag, base: str, _dict: dict
) -> dict[str, ModuleNode]:
ModuleType = _dispatch_module_node[module_type]
return {
name: ModuleType.from_base_name(base, name, v)
for name, v in _dict.items()
}
🅵 _parse_param_name
def _parse_param_name(name: str) -> tuple[str, list[tuple[str, str]]]:
names = re.split(f"([{''.join(HOOK_FLAGS)}])", name)
return names.pop(0), list(zip(names[::2], names[1::2]))
🅵 _flatten_list
_flatten_list
def _flatten_list(
lis: Sequence[ConfigNode | list[ConfigNode]],
) -> Sequence[ConfigNode]:
new_lis = []
for i in lis:
if isinstance(i, list):
new_lis.extend(i)
else:
new_lis.append(i)
return new_lis
🅵 set_primary_fields
set_primary_fields
def set_primary_fields(cfg) -> None:
primary_fields = cfg.primary_fields
primary_to_registry = cfg.primary_to_registry
if hasattr(ConfigDict, "primary_fields"):
logger.ex("`primary_fields` will be reset to {}", primary_fields)
if primary_fields:
ConfigDict.set_primary_fields(primary_fields, primary_to_registry)
Classes
🅲 ConfigDict
ConfigDict
class ConfigDict(dict):
primary_fields: list = None
primary_to_registry: dict[str, str] = None
registered_fields: list = None
all_fields: set[str] = None
scratchpads_fields: set[str] = set()
current_field: str | None = None
reused_caches: dict[str, ReusedNode] = None
A specialized dictionary used for parsing and managing configuration data.
It extends the functionality of the standard Python dictionary to include methods for parsing configuration nodes, handling special parameters, and managing primary and registered fields.
Attributes primary_fields: A list of primary field names. primary_to_registry: A dictionary mapping primary field names to their corresponding registries. registered_fields: A list of registered field names. all_fields: A set containing all field names. scratchpads_fields: A set containing scratchpad field names. current_field: The current field being processed (can be None). reused_caches: A dictionary for caching reused nodes.
🅼 __new__
__new__
def __new__(cls) -> Self:
if not hasattr(cls, "primary_fields"):
raise RuntimeError("Call `set_primary_fields` before `load`")
class ConfigDictImpl(ConfigDict):
primary_fields = ConfigDict.primary_fields
primary_to_registry = ConfigDict.primary_to_registry
scratchpads_fields = ConfigDict.scratchpads_fields
reused_caches = {}
inst = super().__new__(ConfigDictImpl)
return inst
🅼 set_primary_fields
set_primary_fields
@classmethod
def set_primary_fields(
cls, primary_fields: Sequence[str], primary_to_registry: dict[str, str]
) -> None:
cls.primary_fields = list(primary_fields)
cls.primary_to_registry = primary_to_registry
Sets the `primary_fields` attribute to the specified list of module names,
and `registered_fields` attributes based on the current state of the `Registry` object.
Note that `set_primary_fields` must be called before `config.load`.
🅼 parse
parse
def parse(self) -> None:
models.IS_PARSING = True
self._parse_primary_modules()
self._parse_isolated_obj()
self._parse_inter_modules()
self._wrap()
self._clean()
Parsing config into some `ModuleNode`s, the procedures are as following:
-
Convert config nodes in `primary_fields` to `ModuleNode` at first, the field will be regarded as a set of nodes, e.g. `self[Model]` consists of two models.
i. If the field is registered, the base registry is set to field; ii. If not, search `Registry` to get the base registry of each node.
-
Convert isolated objects to `ModuleNode`;
i. If the field is registered, search `Registry` to get the base registry of each node. Raising error if it cannot find; ii. If not, regard the field as a single node, and search `Registry`. iii. If search fail, regard the field as a scratchpads.
-
Parse all the `ModuleNode` to target type of Node, e.g. `ReusedNode` and `ClassNode`.
Visit the top level of config dict: i. If the name is in `primary_fields` or `scratchpads_fields`, parse each node of field; ii. Else if the node is instance of `ModuleNode`, parse it to target node.
-
Wrap all the nodes of `primary_fields` into ModuleWrapper.
-
Clean some remain non-primary nodes. Only keep the primary nodes.
-
In the 3rd step, it will parse every parameter of each module node
if its name has a special prefix, e.g. `!`, `@` or `$`. The special parameter should be a string, a list of string or a dict of string. They will be parsed to target module nodes in given format(alone, list or dict).
According to given string parameters, e.g. `['ResNet', 'SegHead']`,
- It will firstly search from the top level of config.
- If it dose not exist, it will search from `primary_fields`, `scratchpads_fields` and `registered_fields`.
- If it still dose not exist, it will be regraded as a implicit module, which must have non-required parameters.
For the first two situations, the node will be convert to target type of node, then it will be set back to config for cache according to the priority. For the last situation, it will only be set back when target module type is `ReusedNode`. But if the target module type is `ClassNode`, it will not be set back.
NOTE: Set converted nodes back to config is necceary for `ReusedNode`.
NOTE: use `export EXCORE_DEBUG=1` to enable excore debug to get more information when parsing.
🅼 _wrap
def _wrap(self) -> None:
for name in self.primary_keys():
self[name] = ModuleWrapper(self[name])
🅼 _clean
def _clean(self) -> None:
for name in self.non_primary_keys():
if name in self.registered_fields or isinstance(self[name], ModuleNode):
self.pop(name)
🅼 primary_keys
def primary_keys(self) -> Generator[str, None, None]:
for name in self.primary_fields:
if name in self:
yield name