Safemotion Lib
Loading...
Searching...
No Matches
config.py
Go to the documentation of this file.
1# encoding: utf-8
2"""
3@author: l1aoxingyu
4@contact: sherlockliao01@gmail.com
5"""
6
7import logging
8import os
9from typing import Any
10
11import yaml
12from yacs.config import CfgNode as _CfgNode
13
14from ..utils.file_io import PathManager
15
16BASE_KEY = "_BASE_"
17
18
19class CfgNode(_CfgNode):
20 """
21 Our own extended version of :class:`yacs.config.CfgNode`.
22 It contains the following extra features:
23 1. The :meth:`merge_from_file` method supports the "_BASE_" key,
24 which allows the new CfgNode to inherit all the attributes from the
25 base configuration file.
26 2. Keys that start with "COMPUTED_" are treated as insertion-only
27 "computed" attributes. They can be inserted regardless of whether
28 the CfgNode is frozen or not.
29 3. With "allow_unsafe=True", it supports pyyaml tags that evaluate
30 expressions in config. See examples in
31 https://pyyaml.org/wiki/PyYAMLDocumentation#yaml-tags-and-python-types
32 Note that this may lead to arbitrary code execution: you must not
33 load a config file from untrusted sources before manually inspecting
34 the content of the file.
35 """
36
37 @staticmethod
38 def load_yaml_with_base(filename: str, allow_unsafe: bool = False):
39 """
40 Just like `yaml.load(open(filename))`, but inherit attributes from its
41 `_BASE_`.
42 Args:
43 filename (str): the file name of the current config. Will be used to
44 find the base config file.
45 allow_unsafe (bool): whether to allow loading the config file with
46 `yaml.unsafe_load`.
47 Returns:
48 (dict): the loaded yaml
49 """
50 with PathManager.open(filename, "r") as f:
51 try:
52 cfg = yaml.safe_load(f)
53 except yaml.constructor.ConstructorError:
54 if not allow_unsafe:
55 raise
56 logger = logging.getLogger(__name__)
57 logger.warning(
58 "Loading config {} with yaml.unsafe_load. Your machine may "
59 "be at risk if the file contains malicious content.".format(
60 filename
61 )
62 )
63 f.close()
64 with open(filename, "r") as f:
65 cfg = yaml.unsafe_load(f)
66
67 def merge_a_into_b(a, b):
68 # merge dict a into dict b. values in a will overwrite b.
69 for k, v in a.items():
70 if isinstance(v, dict) and k in b:
71 assert isinstance(
72 b[k], dict
73 ), "Cannot inherit key '{}' from base!".format(k)
74 merge_a_into_b(v, b[k])
75 else:
76 b[k] = v
77
78 if BASE_KEY in cfg:
79 base_cfg_file = cfg[BASE_KEY]
80 if base_cfg_file.startswith("~"):
81 base_cfg_file = os.path.expanduser(base_cfg_file)
82 if not any(
83 map(base_cfg_file.startswith, ["/", "https://", "http://"])
84 ):
85 # the path to base cfg is relative to the config file itself.
86 base_cfg_file = os.path.join(
87 os.path.dirname(filename), base_cfg_file
88 )
89 base_cfg = CfgNode.load_yaml_with_base(
90 base_cfg_file, allow_unsafe=allow_unsafe
91 )
92 del cfg[BASE_KEY]
93
94 merge_a_into_b(cfg, base_cfg)
95 return base_cfg
96 return cfg
97
98 def merge_from_file(self, cfg_filename: str, allow_unsafe: bool = False):
99 """
100 Merge configs from a given yaml file.
101 Args:
102 cfg_filename: the file name of the yaml config.
103 allow_unsafe: whether to allow loading the config file with
104 `yaml.unsafe_load`.
105 """
106 loaded_cfg = CfgNode.load_yaml_with_base(
107 cfg_filename, allow_unsafe=allow_unsafe
108 )
109 loaded_cfg = type(self)(loaded_cfg)
110 self.merge_from_other_cfg(loaded_cfg)
111
112 # Forward the following calls to base, but with a check on the BASE_KEY.
113 def merge_from_other_cfg(self, cfg_other):
114 """
115 Args:
116 cfg_other (CfgNode): configs to merge from.
117 """
118 assert (
119 BASE_KEY not in cfg_other
120 ), "The reserved key '{}' can only be used in files!".format(BASE_KEY)
121 return super().merge_from_other_cfg(cfg_other)
122
123 def merge_from_list(self, cfg_list: list):
124 """
125 Args:
126 cfg_list (list): list of configs to merge from.
127 """
128 keys = set(cfg_list[0::2])
129 assert (
130 BASE_KEY not in keys
131 ), "The reserved key '{}' can only be used in files!".format(BASE_KEY)
132 return super().merge_from_list(cfg_list)
133
134 def __setattr__(self, name: str, val: Any):
135 if name.startswith("COMPUTED_"):
136 if name in self:
137 old_val = self[name]
138 if old_val == val:
139 return
140 raise KeyError(
141 "Computed attributed '{}' already exists "
142 "with a different value! old={}, new={}.".format(
143 name, old_val, val
144 )
145 )
146 self[name] = val
147 else:
148 super().__setattr__(name, val)
149
150
151def get_cfg() -> CfgNode:
152 """
153 Get a copy of the default config.
154 Returns:
155 a fastreid CfgNode instance.
156 """
157 from .defaults import _C
158
159 return _C.clone()
__setattr__(self, str name, Any val)
Definition config.py:134
merge_from_list(self, list cfg_list)
Definition config.py:123
merge_from_other_cfg(self, cfg_other)
Definition config.py:113
merge_from_file(self, str cfg_filename, bool allow_unsafe=False)
Definition config.py:98
load_yaml_with_base(str filename, bool allow_unsafe=False)
Definition config.py:38