clip-caption-reward
copied
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Readme
Files and versions
153 lines
5.3 KiB
153 lines
5.3 KiB
2 years ago
|
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||
|
# Copy from fvcore
|
||
|
|
||
|
import logging
|
||
|
import os
|
||
|
from typing import Any
|
||
|
import yaml
|
||
|
from yacs.config import CfgNode as _CfgNode
|
||
|
|
||
|
import io as PathManager
|
||
|
|
||
|
BASE_KEY = "_BASE_"
|
||
|
|
||
|
|
||
|
class CfgNode(_CfgNode):
|
||
|
"""
|
||
|
Our own extended version of :class:`yacs.config.CfgNode`.
|
||
|
It contains the following extra features:
|
||
|
|
||
|
1. The :meth:`merge_from_file` method supports the "_BASE_" key,
|
||
|
which allows the new CfgNode to inherit all the attributes from the
|
||
|
base configuration file.
|
||
|
2. Keys that start with "COMPUTED_" are treated as insertion-only
|
||
|
"computed" attributes. They can be inserted regardless of whether
|
||
|
the CfgNode is frozen or not.
|
||
|
3. With "allow_unsafe=True", it supports pyyaml tags that evaluate
|
||
|
expressions in config. See examples in
|
||
|
https://pyyaml.org/wiki/PyYAMLDocumentation#yaml-tags-and-python-types
|
||
|
Note that this may lead to arbitrary code execution: you must not
|
||
|
load a config file from untrusted sources before manually inspecting
|
||
|
the content of the file.
|
||
|
"""
|
||
|
|
||
|
@staticmethod
|
||
|
def load_yaml_with_base(filename, allow_unsafe = False):
|
||
|
"""
|
||
|
Just like `yaml.load(open(filename))`, but inherit attributes from its
|
||
|
`_BASE_`.
|
||
|
|
||
|
Args:
|
||
|
filename (str): the file name of the current config. Will be used to
|
||
|
find the base config file.
|
||
|
allow_unsafe (bool): whether to allow loading the config file with
|
||
|
`yaml.unsafe_load`.
|
||
|
|
||
|
Returns:
|
||
|
(dict): the loaded yaml
|
||
|
"""
|
||
|
with PathManager.open(filename, "r") as f:
|
||
|
try:
|
||
|
cfg = yaml.safe_load(f)
|
||
|
except yaml.constructor.ConstructorError:
|
||
|
if not allow_unsafe:
|
||
|
raise
|
||
|
logger = logging.getLogger(__name__)
|
||
|
logger.warning(
|
||
|
"Loading config {} with yaml.unsafe_load. Your machine may "
|
||
|
"be at risk if the file contains malicious content.".format(
|
||
|
filename
|
||
|
)
|
||
|
)
|
||
|
f.close()
|
||
|
with open(filename, "r") as f:
|
||
|
cfg = yaml.unsafe_load(f)
|
||
|
|
||
|
def merge_a_into_b(a, b):
|
||
|
# merge dict a into dict b. values in a will overwrite b.
|
||
|
for k, v in a.items():
|
||
|
if isinstance(v, dict) and k in b:
|
||
|
assert isinstance(
|
||
|
b[k], dict
|
||
|
), "Cannot inherit key '{}' from base!".format(k)
|
||
|
merge_a_into_b(v, b[k])
|
||
|
else:
|
||
|
b[k] = v
|
||
|
|
||
|
if BASE_KEY in cfg:
|
||
|
base_cfg_file = cfg[BASE_KEY]
|
||
|
if base_cfg_file.startswith("~"):
|
||
|
base_cfg_file = os.path.expanduser(base_cfg_file)
|
||
|
if not any(
|
||
|
map(base_cfg_file.startswith, ["/", "https://", "http://"])
|
||
|
):
|
||
|
# the path to base cfg is relative to the config file itself.
|
||
|
base_cfg_file = os.path.join(
|
||
|
os.path.dirname(filename), base_cfg_file
|
||
|
)
|
||
|
base_cfg = CfgNode.load_yaml_with_base(
|
||
|
base_cfg_file, allow_unsafe=allow_unsafe
|
||
|
)
|
||
|
del cfg[BASE_KEY]
|
||
|
|
||
|
merge_a_into_b(cfg, base_cfg)
|
||
|
return base_cfg
|
||
|
return cfg
|
||
|
|
||
|
def merge_from_file(self, cfg_filename, allow_unsafe = False):
|
||
|
"""
|
||
|
Merge configs from a given yaml file.
|
||
|
|
||
|
Args:
|
||
|
cfg_filename: the file name of the yaml config.
|
||
|
allow_unsafe: whether to allow loading the config file with
|
||
|
`yaml.unsafe_load`.
|
||
|
"""
|
||
|
loaded_cfg = CfgNode.load_yaml_with_base(
|
||
|
cfg_filename, allow_unsafe=allow_unsafe
|
||
|
)
|
||
|
loaded_cfg = type(self)(loaded_cfg)
|
||
|
self.merge_from_other_cfg(loaded_cfg)
|
||
|
|
||
|
# Forward the following calls to base, but with a check on the BASE_KEY.
|
||
|
def merge_from_other_cfg(self, cfg_other):
|
||
|
"""
|
||
|
Args:
|
||
|
cfg_other (CfgNode): configs to merge from.
|
||
|
"""
|
||
|
assert (
|
||
|
BASE_KEY not in cfg_other
|
||
|
), "The reserved key '{}' can only be used in files!".format(BASE_KEY)
|
||
|
return super().merge_from_other_cfg(cfg_other)
|
||
|
|
||
|
def merge_from_list(self, cfg_list):
|
||
|
"""
|
||
|
Args:
|
||
|
cfg_list (list): list of configs to merge from.
|
||
|
"""
|
||
|
keys = set(cfg_list[0::2])
|
||
|
assert (
|
||
|
BASE_KEY not in keys
|
||
|
), "The reserved key '{}' can only be used in files!".format(BASE_KEY)
|
||
|
return super().merge_from_list(cfg_list)
|
||
|
|
||
|
def __setattr__(self, name, val):
|
||
|
if name.startswith("COMPUTED_"):
|
||
|
if name in self:
|
||
|
old_val = self[name]
|
||
|
if old_val == val:
|
||
|
return
|
||
|
raise KeyError(
|
||
|
"Computed attributed '{}' already exists "
|
||
|
"with a different value! old={}, new={}.".format(
|
||
|
name, old_val, val
|
||
|
)
|
||
|
)
|
||
|
self[name] = val
|
||
|
else:
|
||
|
super().__setattr__(name, val)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
cfg = CfgNode.load_yaml_with_base('configs/updown_long.yml')
|
||
|
print(cfg)
|