InPeerReview commited on
Commit
b0481db
·
verified ·
1 Parent(s): c1651d2

Upload 7 files

Browse files
utils/__pycache__/build.cpython-38.pyc ADDED
Binary file (700 Bytes). View file
 
utils/__pycache__/config.cpython-38.pyc ADDED
Binary file (32.3 kB). View file
 
utils/__pycache__/util.cpython-38.pyc ADDED
Binary file (633 Bytes). View file
 
utils/build.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from utils.config import Config, ConfigDict
2
+ from rscd.models.backbones import *
3
+ from rscd.models.decoderheads import *
4
+
5
+ from rscd.losses import *
6
+
7
+ def build_from_cfg(cfg):
8
+ if not isinstance(cfg, (dict, ConfigDict, Config)):
9
+ raise TypeError(
10
+ f'cfg should be a dict, ConfigDict or Config, but got {type(cfg)}')
11
+ if 'type' not in cfg:
12
+ raise KeyError(
13
+ '`cfg` must contain the key "type", '
14
+ f'but got {cfg}')
15
+ obj_type = cfg.pop('type')
16
+ obj_cls = eval(obj_type)
17
+ obj = obj_cls(**cfg)
18
+ return obj
utils/config.py ADDED
@@ -0,0 +1,1030 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) OpenMMLab. All rights reserved.
2
+ import ast
3
+ import copy
4
+ import os
5
+ import os.path as osp
6
+ import platform
7
+ import shutil
8
+ import sys
9
+ import tempfile
10
+ import types
11
+ import uuid
12
+ import warnings
13
+ from argparse import Action, ArgumentParser, Namespace
14
+ from collections import OrderedDict, abc
15
+ from contextlib import contextmanager
16
+ from pathlib import Path
17
+ from typing import Any, Optional, Sequence, Tuple, Union
18
+
19
+ from addict import Dict
20
+ from utils.util import check_file_exist
21
+ import re
22
+
23
+ BASE_KEY = '_base_'
24
+ DELETE_KEY = '_delete_'
25
+ DEPRECATION_KEY = '_deprecation_'
26
+ RESERVED_KEYS = ['filename', 'text', 'pretty_text', 'env_variables']
27
+
28
+
29
+ class ConfigDict(Dict):
30
+
31
+ def __missing__(self, name):
32
+ raise KeyError(name)
33
+
34
+ def __getattr__(self, name):
35
+ try:
36
+ value = super().__getattr__(name)
37
+ except KeyError:
38
+ raise AttributeError(f"'{self.__class__.__name__}' object has no "
39
+ f"attribute '{name}'")
40
+ except Exception as e:
41
+ raise e
42
+ else:
43
+ return value
44
+
45
+ def __deepcopy__(self, memo):
46
+ other = self.__class__()
47
+ memo[id(self)] = other
48
+ for key, value in super().items():
49
+ other[copy.deepcopy(key, memo)] = copy.deepcopy(value, memo)
50
+ return other
51
+
52
+ def __copy__(self):
53
+ other = self.__class__()
54
+ for key, value in super().items():
55
+ other[key] = value
56
+ return other
57
+
58
+ copy = __copy__
59
+
60
+
61
+ def merge(self, other: dict):
62
+ """Merge another dictionary into current dictionary.
63
+
64
+ Args:
65
+ other (dict): Another dictionary.
66
+ """
67
+ default = object()
68
+
69
+ def _merge_a_into_b(a, b):
70
+ if isinstance(a, dict):
71
+ if not isinstance(b, dict):
72
+ a.pop(DELETE_KEY, None)
73
+ return a
74
+ if a.pop(DELETE_KEY, False):
75
+ b.clear()
76
+ all_keys = list(b.keys()) + list(a.keys())
77
+ return {
78
+ key:
79
+ _merge_a_into_b(a.get(key, default), b.get(key, default))
80
+ for key in all_keys if key != DELETE_KEY
81
+ }
82
+ else:
83
+ return a if a is not default else b
84
+
85
+ merged = _merge_a_into_b(copy.deepcopy(other), copy.deepcopy(self))
86
+ self.clear()
87
+ for key, value in merged.items():
88
+ self[key] = value
89
+
90
+ class RemoveAssignFromAST(ast.NodeTransformer):
91
+ """Remove Assign node if the target's name match the key.
92
+
93
+ Args:
94
+ key (str): The target name of the Assign node.
95
+ """
96
+
97
+ def __init__(self, key):
98
+ self.key = key
99
+
100
+ def visit_Assign(self, node):
101
+ if (isinstance(node.targets[0], ast.Name)
102
+ and node.targets[0].id == self.key):
103
+ return None
104
+ else:
105
+ return node
106
+
107
+ class Config:
108
+ """A facility for config and config files.
109
+
110
+ It supports common file formats as configs: python/json/yaml.
111
+ ``Config.fromfile`` can parse a dictionary from a config file, then
112
+ build a ``Config`` instance with the dictionary.
113
+ The interface is the same as a dict object and also allows access config
114
+ values as attributes.
115
+
116
+ Args:
117
+ cfg_dict (dict, optional): A config dictionary. Defaults to None.
118
+ cfg_text (str, optional): Text of config. Defaults to None.
119
+ filename (str or Path, optional): Name of config file.
120
+ Defaults to None.
121
+ format_python_code (bool): Whether to format Python code by yapf.
122
+ Defaults to True.
123
+
124
+ Here is a simple example:
125
+
126
+ Examples:
127
+ >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1])))
128
+ >>> cfg.a
129
+ 1
130
+ >>> cfg.b
131
+ {'b1': [0, 1]}
132
+ >>> cfg.b.b1
133
+ [0, 1]
134
+ >>> cfg = Config.fromfile('tests/data/config/a.py')
135
+ >>> cfg.filename
136
+ "/home/username/projects/mmengine/tests/data/config/a.py"
137
+ >>> cfg.item4
138
+ 'test'
139
+ >>> cfg
140
+ "Config [path: /home/username/projects/mmengine/tests/data/config/a.py]
141
+ :"
142
+ "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}"
143
+
144
+ You can find more advance usage in the `config tutorial`_.
145
+
146
+ .. _config tutorial: https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html
147
+ """ # noqa: E501
148
+
149
+ def __init__(
150
+ self,
151
+ cfg_dict: dict = None,
152
+ cfg_text: Optional[str] = None,
153
+ filename: Optional[Union[str, Path]] = None,
154
+ env_variables: Optional[dict] = None,
155
+ format_python_code: bool = True,
156
+ ):
157
+ filename = str(filename) if isinstance(filename, Path) else filename
158
+ if cfg_dict is None:
159
+ cfg_dict = dict()
160
+ elif not isinstance(cfg_dict, dict):
161
+ raise TypeError('cfg_dict must be a dict, but '
162
+ f'got {type(cfg_dict)}')
163
+ for key in cfg_dict:
164
+ if key in RESERVED_KEYS:
165
+ raise KeyError(f'{key} is reserved for config file')
166
+
167
+ if not isinstance(cfg_dict, ConfigDict):
168
+ cfg_dict = ConfigDict(cfg_dict)
169
+ super().__setattr__('_cfg_dict', cfg_dict)
170
+ super().__setattr__('_filename', filename)
171
+ super().__setattr__('_format_python_code', format_python_code)
172
+ if not hasattr(self, '_imported_names'):
173
+ super().__setattr__('_imported_names', set())
174
+
175
+ if cfg_text:
176
+ text = cfg_text
177
+ elif filename:
178
+ with open(filename, encoding='utf-8') as f:
179
+ text = f.read()
180
+ else:
181
+ text = ''
182
+ super().__setattr__('_text', text)
183
+ if env_variables is None:
184
+ env_variables = dict()
185
+ super().__setattr__('_env_variables', env_variables)
186
+
187
+ @staticmethod
188
+ def fromfile(filename: Union[str, Path],
189
+ use_predefined_variables: bool = True,
190
+ import_custom_modules: bool = True,
191
+ use_environment_variables: bool = True,
192
+ format_python_code: bool = True) -> 'Config':
193
+ """Build a Config instance from config file.
194
+
195
+ Args:
196
+ filename (str or Path): Name of config file.
197
+ use_predefined_variables (bool, optional): Whether to use
198
+ predefined variables. Defaults to True.
199
+ import_custom_modules (bool, optional): Whether to support
200
+ importing custom modules in config. Defaults to None.
201
+ lazy_import (bool): Whether to load config in `lazy_import` mode.
202
+ If it is `None`, it will be deduced by the content of the
203
+ config file. Defaults to None.
204
+ format_python_code (bool): Whether to format Python code by yapf.
205
+ Defaults to True.
206
+
207
+ Returns:
208
+ Config: Config instance built from config file.
209
+ """
210
+ filename = str(filename) if isinstance(filename, Path) else filename
211
+
212
+ cfg_dict, cfg_text, env_variables = Config._file2dict(
213
+ filename, use_predefined_variables, use_environment_variables)
214
+
215
+ return Config(
216
+ cfg_dict,
217
+ cfg_text=cfg_text,
218
+ filename=filename,
219
+ env_variables=env_variables)
220
+
221
+
222
+ @staticmethod
223
+ def _get_base_modules(nodes: list) -> list:
224
+ """Get base module name from parsed code.
225
+
226
+ Args:
227
+ nodes (list): Parsed code of the config file.
228
+
229
+ Returns:
230
+ list: Name of base modules.
231
+ """
232
+
233
+ def _get_base_module_from_with(with_nodes: list) -> list:
234
+ """Get base module name from if statement in python file.
235
+
236
+ Args:
237
+ with_nodes (list): List of if statement.
238
+
239
+ Returns:
240
+ list: Name of base modules.
241
+ """
242
+ base_modules = []
243
+ for node in with_nodes:
244
+ assert isinstance(node, ast.ImportFrom), (
245
+ 'Illegal syntax in config file! Only '
246
+ '`from ... import ...` could be implemented` in '
247
+ 'with read_base()`')
248
+ assert node.module is not None, (
249
+ 'Illegal syntax in config file! Syntax like '
250
+ '`from . import xxx` is not allowed in `with read_base()`')
251
+ base_modules.append(node.level * '.' + node.module)
252
+ return base_modules
253
+
254
+ for idx, node in enumerate(nodes):
255
+ if (isinstance(node, ast.Assign)
256
+ and isinstance(node.targets[0], ast.Name)
257
+ and node.targets[0].id == BASE_KEY):
258
+ raise RuntimeError(
259
+ 'The configuration file type in the inheritance chain '
260
+ 'must match the current configuration file type, either '
261
+ '"lazy_import" or non-"lazy_import". You got this error '
262
+ f'since you use the syntax like `_base_ = "{node.targets[0].id}"` ' # noqa: E501
263
+ 'in your config. You should use `with read_base(): ... to` ' # noqa: E501
264
+ 'mark the inherited config file. See more information '
265
+ 'in https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html' # noqa: E501
266
+ )
267
+
268
+ if not isinstance(node, ast.With):
269
+ continue
270
+
271
+ expr = node.items[0].context_expr
272
+ if (not isinstance(expr, ast.Call)
273
+ or not expr.func.id == 'read_base' or # type: ignore
274
+ len(node.items) > 1):
275
+ raise RuntimeError(
276
+ 'Only `read_base` context manager can be used in the '
277
+ 'config')
278
+
279
+ # The original code:
280
+ # ```
281
+ # with read_base():
282
+ # from .._base_.default_runtime import *
283
+ # ```
284
+ # The processed code:
285
+ # ```
286
+ # from .._base_.default_runtime import *
287
+ # ```
288
+ # As you can see, the if statement is removed and the
289
+ # from ... import statement will be unindent
290
+ for nested_idx, nested_node in enumerate(node.body):
291
+ nodes.insert(idx + nested_idx + 1, nested_node)
292
+ nodes.pop(idx)
293
+ return _get_base_module_from_with(node.body)
294
+ return []
295
+
296
+ @staticmethod
297
+ def _validate_py_syntax(filename: str):
298
+ """Validate syntax of python config.
299
+
300
+ Args:
301
+ filename (str): Filename of python config file.
302
+ """
303
+ with open(filename, encoding='utf-8') as f:
304
+ content = f.read()
305
+ try:
306
+ ast.parse(content)
307
+ except SyntaxError as e:
308
+ raise SyntaxError('There are syntax errors in config '
309
+ f'file {filename}: {e}')
310
+
311
+ @staticmethod
312
+ def _substitute_predefined_vars(filename: str, temp_config_name: str):
313
+ """Substitute predefined variables in config with actual values.
314
+ """
315
+ file_dirname = osp.dirname(filename)
316
+ file_basename = osp.basename(filename)
317
+ file_basename_no_extension = osp.splitext(file_basename)[0]
318
+ file_extname = osp.splitext(filename)[1]
319
+ support_templates = dict(
320
+ fileDirname=file_dirname,
321
+ fileBasename=file_basename,
322
+ fileBasenameNoExtension=file_basename_no_extension,
323
+ fileExtname=file_extname)
324
+ with open(filename, encoding='utf-8') as f:
325
+ config_file = f.read()
326
+ for key, value in support_templates.items():
327
+ regexp = r'\{\{\s*' + str(key) + r'\s*\}\}'
328
+ value = value.replace('\\', '/')
329
+ config_file = re.sub(regexp, value, config_file)
330
+ with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file:
331
+ tmp_config_file.write(config_file)
332
+
333
+ @staticmethod
334
+ def _substitute_env_variables(filename: str, temp_config_name: str):
335
+ """Substitute environment variables in config with actual values.
336
+
337
+ Sometimes, we want to change some items in the config with environment
338
+ variables. For examples, we expect to change dataset root by setting
339
+ ``DATASET_ROOT=/dataset/root/path`` in the command line. This can be
340
+ easily achieved by writing lines in the config as follows
341
+
342
+ .. code-block:: python
343
+
344
+ data_root = '{{$DATASET_ROOT:/default/dataset}}/images'
345
+
346
+
347
+ Here, ``{{$DATASET_ROOT:/default/dataset}}`` indicates using the
348
+ environment variable ``DATASET_ROOT`` to replace the part between
349
+ ``{{}}``. If the ``DATASET_ROOT`` is not set, the default value
350
+ ``/default/dataset`` will be used.
351
+
352
+ Environment variables not only can replace items in the string, they
353
+ can also substitute other types of data in config. In this situation,
354
+ we can write the config as below
355
+
356
+ .. code-block:: python
357
+
358
+ model = dict(
359
+ bbox_head = dict(num_classes={{'$NUM_CLASSES:80'}}))
360
+
361
+
362
+ For details, Please refer to docs/zh_cn/tutorials/config.md .
363
+
364
+ Args:
365
+ filename (str): Filename of config.
366
+ temp_config_name (str): Temporary filename to save substituted
367
+ config.
368
+ """
369
+ with open(filename, encoding='utf-8') as f:
370
+ config_file = f.read()
371
+ regexp = r'\{\{[\'\"]?\s*\$(\w+)\s*\:\s*(\S*?)\s*[\'\"]?\}\}'
372
+ keys = re.findall(regexp, config_file)
373
+ env_variables = dict()
374
+ for var_name, value in keys:
375
+ regexp = r'\{\{[\'\"]?\s*\$' + var_name + r'\s*\:\s*' \
376
+ + value + r'\s*[\'\"]?\}\}'
377
+ if var_name in os.environ:
378
+ value = os.environ[var_name]
379
+ env_variables[var_name] = value
380
+ if not value:
381
+ raise KeyError(f'`{var_name}` cannot be found in `os.environ`.'
382
+ f' Please set `{var_name}` in environment or '
383
+ 'give a default value.')
384
+ config_file = re.sub(regexp, value, config_file)
385
+
386
+ with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file:
387
+ tmp_config_file.write(config_file)
388
+ return env_variables
389
+
390
+ @staticmethod
391
+ def _pre_substitute_base_vars(filename: str,
392
+ temp_config_name: str) -> dict:
393
+ """Preceding step for substituting variables in base config with actual
394
+ value.
395
+
396
+ Args:
397
+ filename (str): Filename of config.
398
+ temp_config_name (str): Temporary filename to save substituted
399
+ config.
400
+
401
+ Returns:
402
+ dict: A dictionary contains variables in base config.
403
+ """
404
+ with open(filename, encoding='utf-8') as f:
405
+ config_file = f.read()
406
+ base_var_dict = {}
407
+ regexp = r'\{\{\s*' + BASE_KEY + r'\.([\w\.]+)\s*\}\}'
408
+ base_vars = set(re.findall(regexp, config_file))
409
+ for base_var in base_vars:
410
+ randstr = f'_{base_var}_{uuid.uuid4().hex.lower()[:6]}'
411
+ base_var_dict[randstr] = base_var
412
+ regexp = r'\{\{\s*' + BASE_KEY + r'\.' + base_var + r'\s*\}\}'
413
+ config_file = re.sub(regexp, f'"{randstr}"', config_file)
414
+ with open(temp_config_name, 'w', encoding='utf-8') as tmp_config_file:
415
+ tmp_config_file.write(config_file)
416
+ return base_var_dict
417
+
418
+ @staticmethod
419
+ def _substitute_base_vars(cfg: Any, base_var_dict: dict,
420
+ base_cfg: dict) -> Any:
421
+ """Substitute base variables from strings to their actual values.
422
+
423
+ Args:
424
+ Any : Config dictionary.
425
+ base_var_dict (dict): A dictionary contains variables in base
426
+ config.
427
+ base_cfg (dict): Base config dictionary.
428
+
429
+ Returns:
430
+ Any : A dictionary with origin base variables
431
+ substituted with actual values.
432
+ """
433
+ cfg = copy.deepcopy(cfg)
434
+
435
+ if isinstance(cfg, dict):
436
+ for k, v in cfg.items():
437
+ if isinstance(v, str) and v in base_var_dict:
438
+ new_v = base_cfg
439
+ for new_k in base_var_dict[v].split('.'):
440
+ new_v = new_v[new_k]
441
+ cfg[k] = new_v
442
+ elif isinstance(v, (list, tuple, dict)):
443
+ cfg[k] = Config._substitute_base_vars(
444
+ v, base_var_dict, base_cfg)
445
+ elif isinstance(cfg, tuple):
446
+ cfg = tuple(
447
+ Config._substitute_base_vars(c, base_var_dict, base_cfg)
448
+ for c in cfg)
449
+ elif isinstance(cfg, list):
450
+ cfg = [
451
+ Config._substitute_base_vars(c, base_var_dict, base_cfg)
452
+ for c in cfg
453
+ ]
454
+ elif isinstance(cfg, str) and cfg in base_var_dict:
455
+ new_v = base_cfg
456
+ for new_k in base_var_dict[cfg].split('.'):
457
+ new_v = new_v[new_k]
458
+ cfg = new_v
459
+
460
+ return cfg
461
+
462
+ @staticmethod
463
+ def _file2dict(
464
+ filename: str,
465
+ use_predefined_variables: bool = True,
466
+ use_environment_variables: bool = True) -> Tuple[dict, str, dict]:
467
+ """Transform file to variables dictionary.
468
+
469
+ Args:
470
+ filename (str): Name of config file.
471
+ use_predefined_variables (bool, optional): Whether to use
472
+ predefined variables. Defaults to True.
473
+
474
+ Returns:
475
+ Tuple[dict, str]: Variables dictionary and text of Config.
476
+ """
477
+ filename = osp.abspath(osp.expanduser(filename))
478
+ check_file_exist(filename)
479
+ fileExtname = osp.splitext(filename)[1]
480
+ if fileExtname not in ['.py']:
481
+ raise OSError('Only py type are supported now!')
482
+ try:
483
+ with tempfile.TemporaryDirectory() as temp_config_dir:
484
+ temp_config_file = tempfile.NamedTemporaryFile(
485
+ dir=temp_config_dir, suffix=fileExtname, delete=False)
486
+ if platform.system() == 'Windows':
487
+ temp_config_file.close()
488
+
489
+ # Substitute predefined variables
490
+ if use_predefined_variables:
491
+ Config._substitute_predefined_vars(filename,
492
+ temp_config_file.name)
493
+ else:
494
+ shutil.copyfile(filename, temp_config_file.name)
495
+ # Substitute environment variables
496
+ env_variables = dict()
497
+ if use_environment_variables:
498
+ env_variables = Config._substitute_env_variables(
499
+ temp_config_file.name, temp_config_file.name)
500
+ # Substitute base variables from placeholders to strings
501
+ base_var_dict = Config._pre_substitute_base_vars(
502
+ temp_config_file.name, temp_config_file.name)
503
+
504
+ # Handle base files
505
+ base_cfg_dict = ConfigDict()
506
+ cfg_text_list = list()
507
+ for base_cfg_path in Config._get_base_files(
508
+ temp_config_file.name):
509
+ base_cfg_path, scope = Config._get_cfg_path(
510
+ base_cfg_path, filename)
511
+ _cfg_dict, _cfg_text, _env_variables = Config._file2dict(
512
+ filename=base_cfg_path,
513
+ use_predefined_variables=use_predefined_variables,
514
+ use_environment_variables=use_environment_variables)
515
+ cfg_text_list.append(_cfg_text)
516
+ env_variables.update(_env_variables)
517
+ duplicate_keys = base_cfg_dict.keys() & _cfg_dict.keys()
518
+ if len(duplicate_keys) > 0:
519
+ raise KeyError(
520
+ 'Duplicate key is not allowed among bases. '
521
+ f'Duplicate keys: {duplicate_keys}')
522
+
523
+ # _dict_to_config_dict will do the following things:
524
+ # 1. Recursively converts ``dict`` to :obj:`ConfigDict`.
525
+ # 2. Set `_scope_` for the outer dict variable for the base
526
+ # config.
527
+ # 3. Set `scope` attribute for each base variable.
528
+ # Different from `_scope_`, `scope` is not a key of base
529
+ # dict, `scope` attribute will be parsed to key `_scope_`
530
+ # by function `_parse_scope` only if the base variable is
531
+ # accessed by the current config.
532
+ _cfg_dict = Config._dict_to_config_dict(_cfg_dict, scope)
533
+ base_cfg_dict.update(_cfg_dict)
534
+
535
+ with open(temp_config_file.name, encoding='utf-8') as f:
536
+ parsed_codes = ast.parse(f.read())
537
+ parsed_codes = RemoveAssignFromAST(BASE_KEY).visit(
538
+ parsed_codes)
539
+ codeobj = compile(parsed_codes, '', mode='exec')
540
+ # Support load global variable in nested function of the
541
+ # config.
542
+ global_locals_var = {BASE_KEY: base_cfg_dict}
543
+ ori_keys = set(global_locals_var.keys())
544
+ eval(codeobj, global_locals_var, global_locals_var)
545
+ cfg_dict = {
546
+ key: value
547
+ for key, value in global_locals_var.items()
548
+ if (key not in ori_keys and not key.startswith('__'))
549
+ }
550
+ # close temp file
551
+ for key, value in list(cfg_dict.items()):
552
+ if isinstance(value,
553
+ (types.FunctionType, types.ModuleType)):
554
+ cfg_dict.pop(key)
555
+ temp_config_file.close()
556
+
557
+ # If the current config accesses a base variable of base
558
+ # configs, The ``scope`` attribute of corresponding variable
559
+ # will be converted to the `_scope_`.
560
+ Config._parse_scope(cfg_dict)
561
+ except Exception as e:
562
+ if osp.exists(temp_config_dir):
563
+ shutil.rmtree(temp_config_dir)
564
+ raise e
565
+
566
+
567
+ cfg_text = filename + '\n'
568
+ with open(filename, encoding='utf-8') as f:
569
+ # Setting encoding explicitly to resolve coding issue on windows
570
+ cfg_text += f.read()
571
+
572
+ # Substitute base variables from strings to their actual values
573
+ cfg_dict = Config._substitute_base_vars(cfg_dict, base_var_dict,
574
+ base_cfg_dict)
575
+ cfg_dict.pop(BASE_KEY, None)
576
+
577
+ cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict)
578
+ cfg_dict = {
579
+ k: v
580
+ for k, v in cfg_dict.items() if not k.startswith('__')
581
+ }
582
+
583
+ # merge cfg_text
584
+ cfg_text_list.append(cfg_text)
585
+ cfg_text = '\n'.join(cfg_text_list)
586
+
587
+ return cfg_dict, cfg_text, env_variables
588
+
589
+ @staticmethod
590
+ def _dict_to_config_dict_lazy(cfg: dict):
591
+ """Recursively converts ``dict`` to :obj:`ConfigDict`. The only
592
+ difference between ``_dict_to_config_dict_lazy`` and
593
+ ``_dict_to_config_dict_lazy`` is that the former one does not consider
594
+ the scope, and will not trigger the building of ``LazyObject``.
595
+
596
+ Args:
597
+ cfg (dict): Config dict.
598
+
599
+ Returns:
600
+ ConfigDict: Converted dict.
601
+ """
602
+ # Only the outer dict with key `type` should have the key `_scope_`.
603
+ if isinstance(cfg, dict):
604
+ cfg_dict = ConfigDict()
605
+ for key, value in cfg.items():
606
+ cfg_dict[key] = Config._dict_to_config_dict_lazy(value)
607
+ return cfg_dict
608
+ if isinstance(cfg, (tuple, list)):
609
+ return type(cfg)(
610
+ Config._dict_to_config_dict_lazy(_cfg) for _cfg in cfg)
611
+ return cfg
612
+
613
+ @staticmethod
614
+ def _dict_to_config_dict(cfg: dict,
615
+ scope: Optional[str] = None,
616
+ has_scope=True):
617
+ """Recursively converts ``dict`` to :obj:`ConfigDict`.
618
+
619
+ Args:
620
+ cfg (dict): Config dict.
621
+ scope (str, optional): Scope of instance.
622
+ has_scope (bool): Whether to add `_scope_` key to config dict.
623
+
624
+ Returns:
625
+ ConfigDict: Converted dict.
626
+ """
627
+ # Only the outer dict with key `type` should have the key `_scope_`.
628
+ if isinstance(cfg, dict):
629
+ if has_scope and 'type' in cfg:
630
+ has_scope = False
631
+ if scope is not None and cfg.get('_scope_', None) is None:
632
+ cfg._scope_ = scope # type: ignore
633
+ cfg = ConfigDict(cfg)
634
+ dict.__setattr__(cfg, 'scope', scope)
635
+ for key, value in cfg.items():
636
+ cfg[key] = Config._dict_to_config_dict(
637
+ value, scope=scope, has_scope=has_scope)
638
+ elif isinstance(cfg, tuple):
639
+ cfg = tuple(
640
+ Config._dict_to_config_dict(_cfg, scope, has_scope=has_scope)
641
+ for _cfg in cfg)
642
+ elif isinstance(cfg, list):
643
+ cfg = [
644
+ Config._dict_to_config_dict(_cfg, scope, has_scope=has_scope)
645
+ for _cfg in cfg
646
+ ]
647
+ return cfg
648
+
649
+ @staticmethod
650
+ def _parse_scope(cfg: dict) -> None:
651
+ """Adds ``_scope_`` to :obj:`ConfigDict` instance, which means a base
652
+ variable.
653
+
654
+ If the config dict already has the scope, scope will not be
655
+ overwritten.
656
+
657
+ Args:
658
+ cfg (dict): Config needs to be parsed with scope.
659
+ """
660
+ if isinstance(cfg, ConfigDict):
661
+ cfg._scope_ = cfg.scope
662
+ elif isinstance(cfg, (tuple, list)):
663
+ [Config._parse_scope(value) for value in cfg]
664
+ else:
665
+ return
666
+
667
+ @staticmethod
668
+ def _get_base_files(filename: str) -> list:
669
+ """Get the base config file.
670
+
671
+ Args:
672
+ filename (str): The config file.
673
+
674
+ Raises:
675
+ TypeError: Name of config file.
676
+
677
+ Returns:
678
+ list: A list of base config.
679
+ """
680
+ file_format = osp.splitext(filename)[1]
681
+ if file_format == '.py':
682
+ Config._validate_py_syntax(filename)
683
+ with open(filename, encoding='utf-8') as f:
684
+ parsed_codes = ast.parse(f.read()).body
685
+
686
+ def is_base_line(c):
687
+ return (isinstance(c, ast.Assign)
688
+ and isinstance(c.targets[0], ast.Name)
689
+ and c.targets[0].id == BASE_KEY)
690
+
691
+ base_code = next((c for c in parsed_codes if is_base_line(c)),
692
+ None)
693
+ if base_code is not None:
694
+ base_code = ast.Expression( # type: ignore
695
+ body=base_code.value) # type: ignore
696
+ base_files = eval(compile(base_code, '', mode='eval'))
697
+ else:
698
+ base_files = []
699
+ else:
700
+ raise TypeError('The config type should be py, but got {file_format}')
701
+ base_files = base_files if isinstance(base_files,
702
+ list) else [base_files]
703
+ return base_files
704
+
705
+ @staticmethod
706
+ def _get_cfg_path(cfg_path: str,
707
+ filename: str) -> Tuple[str, Optional[str]]:
708
+ """Get the config path from the current or external package.
709
+
710
+ Args:
711
+ cfg_path (str): Relative path of config.
712
+ filename (str): The config file being parsed.
713
+
714
+ Returns:
715
+ Tuple[str, str or None]: Path and scope of config. If the config
716
+ is not an external config, the scope will be `None`.
717
+ """
718
+ cfg_dir = osp.dirname(filename)
719
+ cfg_path = osp.join(cfg_dir, cfg_path)
720
+ return cfg_path, None
721
+
722
+ @staticmethod
723
+ def _merge_a_into_b(a: dict,
724
+ b: dict,
725
+ allow_list_keys: bool = False) -> dict:
726
+ """merge dict ``a`` into dict ``b`` (non-inplace).
727
+
728
+ Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid
729
+ in-place modifications.
730
+
731
+ Args:
732
+ a (dict): The source dict to be merged into ``b``.
733
+ b (dict): The origin dict to be fetch keys from ``a``.
734
+ allow_list_keys (bool): If True, int string keys (e.g. '0', '1')
735
+ are allowed in source ``a`` and will replace the element of the
736
+ corresponding index in b if b is a list. Defaults to False.
737
+
738
+ Returns:
739
+ dict: The modified dict of ``b`` using ``a``.
740
+
741
+ Examples:
742
+ # Normally merge a into b.
743
+ >>> Config._merge_a_into_b(
744
+ ... dict(obj=dict(a=2)), dict(obj=dict(a=1)))
745
+ {'obj': {'a': 2}}
746
+
747
+ # Delete b first and merge a into b.
748
+ >>> Config._merge_a_into_b(
749
+ ... dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1)))
750
+ {'obj': {'a': 2}}
751
+
752
+ # b is a list
753
+ >>> Config._merge_a_into_b(
754
+ ... {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True)
755
+ [{'a': 2}, {'b': 2}]
756
+ """
757
+ b = b.copy()
758
+ for k, v in a.items():
759
+ if allow_list_keys and k.isdigit() and isinstance(b, list):
760
+ k = int(k)
761
+ if len(b) <= k:
762
+ raise KeyError(f'Index {k} exceeds the length of list {b}')
763
+ b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys)
764
+ elif isinstance(v, dict):
765
+ if k in b and not v.pop(DELETE_KEY, False):
766
+ allowed_types: Union[Tuple, type] = (
767
+ dict, list) if allow_list_keys else dict
768
+ if not isinstance(b[k], allowed_types):
769
+ raise TypeError(
770
+ f'{k}={v} in child config cannot inherit from '
771
+ f'base because {k} is a dict in the child config '
772
+ f'but is of type {type(b[k])} in base config. '
773
+ f'You may set `{DELETE_KEY}=True` to ignore the '
774
+ f'base config.')
775
+ b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys)
776
+ else:
777
+ b[k] = ConfigDict(v)
778
+ else:
779
+ b[k] = v
780
+ return b
781
+
782
+ @property
783
+ def filename(self) -> str:
784
+ """get file name of config."""
785
+ return self._filename
786
+
787
+ @property
788
+ def text(self) -> str:
789
+ """get config text."""
790
+ return self._text
791
+
792
+ @property
793
+ def env_variables(self) -> dict:
794
+ """get used environment variables."""
795
+ return self._env_variables
796
+
797
+ @property
798
+ def pretty_text(self) -> str:
799
+ """get formatted python config text."""
800
+
801
+ indent = 4
802
+
803
+ def _indent(s_, num_spaces):
804
+ s = s_.split('\n')
805
+ if len(s) == 1:
806
+ return s_
807
+ first = s.pop(0)
808
+ s = [(num_spaces * ' ') + line for line in s]
809
+ s = '\n'.join(s)
810
+ s = first + '\n' + s
811
+ return s
812
+
813
+ def _format_basic_types(k, v, use_mapping=False):
814
+ if isinstance(v, str):
815
+ v_str = repr(v)
816
+ else:
817
+ v_str = str(v)
818
+
819
+ if use_mapping:
820
+ k_str = f"'{k}'" if isinstance(k, str) else str(k)
821
+ attr_str = f'{k_str}: {v_str}'
822
+ else:
823
+ attr_str = f'{str(k)}={v_str}'
824
+ attr_str = _indent(attr_str, indent)
825
+
826
+ return attr_str
827
+
828
+ def _format_list_tuple(k, v, use_mapping=False):
829
+ if isinstance(v, list):
830
+ left = '['
831
+ right = ']'
832
+ else:
833
+ left = '('
834
+ right = ')'
835
+
836
+ v_str = f'{left}\n'
837
+ # check if all items in the list are dict
838
+ for item in v:
839
+ if isinstance(item, dict):
840
+ v_str += f'dict({_indent(_format_dict(item), indent)}),\n'
841
+ elif isinstance(item, tuple):
842
+ v_str += f'{_indent(_format_list_tuple(None, item), indent)},\n' # noqa: 501
843
+ elif isinstance(item, list):
844
+ v_str += f'{_indent(_format_list_tuple(None, item), indent)},\n' # noqa: 501
845
+ elif isinstance(item, str):
846
+ v_str += f'{_indent(repr(item), indent)},\n'
847
+ else:
848
+ v_str += str(item) + ',\n'
849
+ if k is None:
850
+ return _indent(v_str, indent) + right
851
+ if use_mapping:
852
+ k_str = f"'{k}'" if isinstance(k, str) else str(k)
853
+ attr_str = f'{k_str}: {v_str}'
854
+ else:
855
+ attr_str = f'{str(k)}={v_str}'
856
+ attr_str = _indent(attr_str, indent) + right
857
+ return attr_str
858
+
859
+ def _contain_invalid_identifier(dict_str):
860
+ contain_invalid_identifier = False
861
+ for key_name in dict_str:
862
+ contain_invalid_identifier |= \
863
+ (not str(key_name).isidentifier())
864
+ return contain_invalid_identifier
865
+
866
+ def _format_dict(input_dict, outest_level=False):
867
+ r = ''
868
+ s = []
869
+
870
+ use_mapping = _contain_invalid_identifier(input_dict)
871
+ if use_mapping:
872
+ r += '{'
873
+ for idx, (k, v) in enumerate(input_dict.items()):
874
+ is_last = idx >= len(input_dict) - 1
875
+ end = '' if outest_level or is_last else ','
876
+ if isinstance(v, dict):
877
+ v_str = '\n' + _format_dict(v)
878
+ if use_mapping:
879
+ k_str = f"'{k}'" if isinstance(k, str) else str(k)
880
+ attr_str = f'{k_str}: dict({v_str}'
881
+ else:
882
+ attr_str = f'{str(k)}=dict({v_str}'
883
+ attr_str = _indent(attr_str, indent) + ')' + end
884
+ elif isinstance(v, (list, tuple)):
885
+ attr_str = _format_list_tuple(k, v, use_mapping) + end
886
+ else:
887
+ attr_str = _format_basic_types(k, v, use_mapping) + end
888
+
889
+ s.append(attr_str)
890
+ r += '\n'.join(s)
891
+ if use_mapping:
892
+ r += '}'
893
+ return r
894
+
895
+ cfg_dict = self.to_dict()
896
+ text = _format_dict(cfg_dict, outest_level=True)
897
+ return text
898
+
899
+ def __repr__(self):
900
+ return f'Config (path: {self.filename}): {self._cfg_dict.__repr__()}'
901
+
902
+ def __len__(self):
903
+ return len(self._cfg_dict)
904
+
905
+ def __getattr__(self, name: str) -> Any:
906
+ return getattr(self._cfg_dict, name)
907
+
908
+ def __getitem__(self, name):
909
+ return self._cfg_dict.__getitem__(name)
910
+
911
+ def __setattr__(self, name, value):
912
+ if isinstance(value, dict):
913
+ value = ConfigDict(value)
914
+ self._cfg_dict.__setattr__(name, value)
915
+
916
+ def __setitem__(self, name, value):
917
+ if isinstance(value, dict):
918
+ value = ConfigDict(value)
919
+ self._cfg_dict.__setitem__(name, value)
920
+
921
+ def __iter__(self):
922
+ return iter(self._cfg_dict)
923
+
924
+ def __getstate__(self) -> Tuple[dict, Optional[str], Optional[str], dict]:
925
+ return (self._cfg_dict, self._filename, self._text,
926
+ self._env_variables)
927
+
928
+ def __deepcopy__(self, memo):
929
+ cls = self.__class__
930
+ other = cls.__new__(cls)
931
+ memo[id(self)] = other
932
+
933
+ for key, value in self.__dict__.items():
934
+ super(Config, other).__setattr__(key, copy.deepcopy(value, memo))
935
+
936
+ return other
937
+
938
+ def __copy__(self):
939
+ cls = self.__class__
940
+ other = cls.__new__(cls)
941
+ other.__dict__.update(self.__dict__)
942
+ super(Config, other).__setattr__('_cfg_dict', self._cfg_dict.copy())
943
+
944
+ return other
945
+
946
+ copy = __copy__
947
+
948
+ def __setstate__(self, state: Tuple[dict, Optional[str], Optional[str],
949
+ dict]):
950
+ _cfg_dict, _filename, _text, _env_variables = state
951
+ super().__setattr__('_cfg_dict', _cfg_dict)
952
+ super().__setattr__('_filename', _filename)
953
+ super().__setattr__('_text', _text)
954
+ super().__setattr__('_text', _env_variables)
955
+
956
+ def merge_from_dict(self,
957
+ options: dict,
958
+ allow_list_keys: bool = True) -> None:
959
+ """Merge list into cfg_dict.
960
+
961
+ Merge the dict parsed by MultipleKVAction into this cfg.
962
+
963
+ Args:
964
+ options (dict): dict of configs to merge from.
965
+ allow_list_keys (bool): If True, int string keys (e.g. '0', '1')
966
+ are allowed in ``options`` and will replace the element of the
967
+ corresponding index in the config if the config is a list.
968
+ Defaults to True.
969
+
970
+ Examples:
971
+ >>> from mmengine import Config
972
+ >>> # Merge dictionary element
973
+ >>> options = {'model.backbone.depth': 50, 'model.backbone.with_cp': True}
974
+ >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet'))))
975
+ >>> cfg.merge_from_dict(options)
976
+ >>> cfg._cfg_dict
977
+ {'model': {'backbone': {'type': 'ResNet', 'depth': 50, 'with_cp': True}}}
978
+ >>> # Merge list element
979
+ >>> cfg = Config(
980
+ >>> dict(pipeline=[dict(type='LoadImage'),
981
+ >>> dict(type='LoadAnnotations')]))
982
+ >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')})
983
+ >>> cfg.merge_from_dict(options, allow_list_keys=True)
984
+ >>> cfg._cfg_dict
985
+ {'pipeline': [{'type': 'SelfLoadImage'}, {'type': 'LoadAnnotations'}]}
986
+ """ # noqa: E501
987
+ option_cfg_dict: dict = {}
988
+ for full_key, v in options.items():
989
+ d = option_cfg_dict
990
+ key_list = full_key.split('.')
991
+ for subkey in key_list[:-1]:
992
+ d.setdefault(subkey, ConfigDict())
993
+ d = d[subkey]
994
+ subkey = key_list[-1]
995
+ d[subkey] = v
996
+
997
+ cfg_dict = super().__getattribute__('_cfg_dict')
998
+ super().__setattr__(
999
+ '_cfg_dict',
1000
+ Config._merge_a_into_b(
1001
+ option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys))
1002
+
1003
+ def _to_lazy_dict(self, keep_imported: bool = False) -> dict:
1004
+ """Convert config object to dictionary and filter the imported
1005
+ object."""
1006
+ res = self._cfg_dict._to_lazy_dict()
1007
+ if hasattr(self, '_imported_names') and not keep_imported:
1008
+ res = {
1009
+ key: value
1010
+ for key, value in res.items()
1011
+ if key not in self._imported_names
1012
+ }
1013
+ return res
1014
+
1015
+ def to_dict(self, keep_imported: bool = False):
1016
+ """Convert all data in the config to a builtin ``dict``.
1017
+
1018
+ Args:
1019
+ keep_imported (bool): Whether to keep the imported field.
1020
+ Defaults to False
1021
+
1022
+ If you import third-party objects in the config file, all imported
1023
+ objects will be converted to a string like ``torch.optim.SGD``
1024
+ """
1025
+ return self._cfg_dict.to_dict()
1026
+
1027
+ if __name__ == "__main__":
1028
+ file_path = "/train-syncdata/xiaowen.ma/mycode/rssegmentation/configs/ssnet.py"
1029
+ cfg = Config.fromfile(file_path)
1030
+ print(cfg.model_config)
utils/registry.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Code simplified from https://github.com/open-mmlab/mmengine/blob/main/mmengine/registry/registry.py
2
+ # Copyright (c) OpenMMLab. All rights reserved.
3
+
4
+ from collections.abc import Callable
5
+ from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union
6
+ from rich.table import Table
7
+ from rich.console import Console
8
+
9
+
10
+ class Registry:
11
+ """A registry to map strings to classes or functions.
12
+
13
+ Registered object could be built from registry. Meanwhile, registered
14
+ functions could be called from registry.
15
+ """
16
+
17
+ def __init__(self,
18
+ name: str,
19
+ build_func: Optional[Callable] = None):
20
+ self._name = name
21
+ self._module_dict: Dict[str, Type] = dict()
22
+ self._imported = False
23
+
24
+ self.build_func: Callable
25
+ if build_func is None:
26
+ self.build_func = build_from_cfg
27
+ else:
28
+ self.build_func = build_func
29
+
30
+ def __len__(self):
31
+ return len(self._module_dict)
32
+
33
+ def __repr__(self):
34
+ table = Table(title=f'Registry of {self._name}')
35
+ table.add_column('Names', justify='left', style='cyan')
36
+ table.add_column('Objects', justify='left', style='green')
37
+
38
+ for name, obj in sorted(self._module_dict.items()):
39
+ table.add_row(name, str(obj))
40
+
41
+ console = Console()
42
+ with console.capture() as capture:
43
+ console.print(table, end='')
44
+
45
+ return capture.get()
46
+
47
+ @property
48
+ def name(self):
49
+ return self._name
50
+
51
+ @property
52
+ def module_dict(self):
53
+ return self._module_dict
54
+
55
+ def build(self, cfg: dict, *args, **kwargs) -> Any:
56
+ """Build an instance.
57
+ Build an instance by calling :attr:`build_func`.
58
+ """
59
+ return self.build_func(cfg, *args, **kwargs, registry=self)
60
+
61
+ def _register_module(self,
62
+ module: Type,
63
+ module_name: Optional[Union[str, List[str]]] = None) -> None:
64
+ """Register a module.
65
+
66
+ Args:
67
+ module (type): Module to be registered. Typically a class or a
68
+ function, but generally all ``Callable`` are acceptable.
69
+ module_name (str or list of str, optional): The module name to be
70
+ registered. If not specified, the class name will be used.
71
+ Defaults to None.
72
+ force (bool): Whether to override an existing class with the same
73
+ name. Defaults to False.
74
+ """
75
+ if not callable(module):
76
+ raise TypeError(f'module must be Callable, but got {type(module)}')
77
+
78
+ if module_name is None:
79
+ module_name = module.__name__
80
+ if isinstance(module_name, str):
81
+ module_name = [module_name]
82
+ for name in module_name:
83
+ if name in self._module_dict:
84
+ existed_module = self.module_dict[name]
85
+ raise KeyError(f'{name} is already registered in {self.name} '
86
+ f'at {existed_module.__module__}')
87
+ self._module_dict[name] = module
88
+
89
+ def register_module(
90
+ self,
91
+ name: Optional[Union[str, List[str]]] = None,
92
+ module: Optional[Type] = None) -> Union[type, Callable]:
93
+ """Register a module.
94
+
95
+ A record will be added to ``self._module_dict``, whose key is the class
96
+ name or the specified name, and value is the class itself.
97
+ It can be used as a decorator or a normal function.
98
+ """
99
+
100
+ # raise the error ahead of time
101
+ if not (name is None or isinstance(name, str)):
102
+ raise TypeError(
103
+ 'name must be None, an instance of str, '
104
+ f'but got {type(name)}')
105
+
106
+ # use it as a normal method: x.register_module(module=SomeClass)
107
+ if module is not None:
108
+ self._register_module(module=module, module_name=name)
109
+ return module
110
+
111
+ # use it as a decorator: @x.register_module()
112
+ def _register(module):
113
+ self._register_module(module=module, module_name=name)
114
+ return module
115
+
116
+ return _register
117
+
118
+
119
+ def build_from_cfg(
120
+ cfg: dict,
121
+ registry: Registry,
122
+ default_args: Optional[Union[dict, ConfigDict, Config]] = None) -> Any:
123
+ """Build a module from config dict when it is a class configuration, or
124
+ call a function from config dict when it is a function configuration.
125
+ """
126
+ # Avoid circular import
127
+
128
+ if not isinstance(cfg, (dict, ConfigDict, Config)):
129
+ raise TypeError(
130
+ f'cfg should be a dict, ConfigDict or Config, but got {type(cfg)}')
131
+
132
+ if 'type' not in cfg:
133
+ if default_args is None or 'type' not in default_args:
134
+ raise KeyError(
135
+ '`cfg` or `default_args` must contain the key "type", '
136
+ f'but got {cfg}\n{default_args}')
137
+
138
+ if not isinstance(registry, Registry):
139
+ raise TypeError('registry must be a mmengine.Registry object, '
140
+ f'but got {type(registry)}')
141
+
142
+ if not (isinstance(default_args,
143
+ (dict, ConfigDict, Config)) or default_args is None):
144
+ raise TypeError(
145
+ 'default_args should be a dict, ConfigDict, Config or None, '
146
+ f'but got {type(default_args)}')
147
+
148
+ args = cfg.copy()
149
+ if default_args is not None:
150
+ for name, value in default_args.items():
151
+ args.setdefault(name, value)
152
+
153
+ # Instance should be built under target scope, if `_scope_` is defined
154
+ # in cfg, current default scope should switch to specified scope
155
+ # temporarily.
156
+ scope = args.pop('_scope_', None)
157
+ with registry.switch_scope_and_registry(scope) as registry:
158
+ obj_type = args.pop('type')
159
+ if isinstance(obj_type, str):
160
+ obj_cls = registry.get(obj_type)
161
+ if obj_cls is None:
162
+ raise KeyError(
163
+ f'{obj_type} is not in the {registry.name} registry. '
164
+ f'Please check whether the value of `{obj_type}` is '
165
+ 'correct or it was registered as expected. More details '
166
+ 'can be found at '
167
+ 'https://mmengine.readthedocs.io/en/latest/advanced_tutorials/config.html#import-the-custom-module' # noqa: E501
168
+ )
169
+ # this will include classes, functions, partial functions and more
170
+ elif callable(obj_type):
171
+ obj_cls = obj_type
172
+ else:
173
+ raise TypeError(
174
+ f'type must be a str or valid type, but got {type(obj_type)}')
175
+
176
+ try:
177
+ # If `obj_cls` inherits from `ManagerMixin`, it should be
178
+ # instantiated by `ManagerMixin.get_instance` to ensure that it
179
+ # can be accessed globally.
180
+ if inspect.isclass(obj_cls) and \
181
+ issubclass(obj_cls, ManagerMixin): # type: ignore
182
+ obj = obj_cls.get_instance(**args) # type: ignore
183
+ else:
184
+ obj = obj_cls(**args) # type: ignore
185
+
186
+ if (inspect.isclass(obj_cls) or inspect.isfunction(obj_cls)
187
+ or inspect.ismethod(obj_cls)):
188
+ print_log(
189
+ f'An `{obj_cls.__name__}` instance is built from ' # type: ignore # noqa: E501
190
+ 'registry, and its implementation can be found in '
191
+ f'{obj_cls.__module__}', # type: ignore
192
+ logger='current',
193
+ level=logging.DEBUG)
194
+ else:
195
+ print_log(
196
+ 'An instance is built from registry, and its constructor '
197
+ f'is {obj_cls}',
198
+ logger='current',
199
+ level=logging.DEBUG)
200
+ return obj
201
+
202
+ except Exception as e:
203
+ # Normal TypeError does not print class name.
204
+ cls_location = '/'.join(
205
+ obj_cls.__module__.split('.')) # type: ignore
206
+ raise type(e)(
207
+ f'class `{obj_cls.__name__}` in ' # type: ignore
208
+ f'{cls_location}.py: {e}')
utils/util.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import os.path as osp
3
+
4
+ def check_file_exist(filename, msg_tmpl='file "{}" does not exist'):
5
+ if not osp.isfile(filename):
6
+ raise FileNotFoundError(msg_tmpl.format(filename))
7
+
8
+
9
+ def mkdir_or_exist(dir_name, mode=0o777):
10
+ if dir_name == '':
11
+ return
12
+ dir_name = osp.expanduser(dir_name)
13
+ os.makedirs(dir_name, mode=mode, exist_ok=True)