24 from builtins
import str
25 from past.builtins
import basestring
26 from future.utils
import with_metaclass
27 from future.standard_library
import install_aliases
37 from yaml.representer
import Representer
38 yaml.add_representer(collections.defaultdict, Representer.represent_dict)
40 import lsst.pex.policy
as pexPolicy
48 if sys.version_info[0] >= 3:
49 class _PolicyMeta(type(collections.UserDict), type(yaml.YAMLObject)):
55 class _PolicyBase(collections.UserDict, yaml.YAMLObject):
60 """Policy implements a datatype that is used by Butler for configuration parameters.
61 It is essentially a dict with key/value pairs, including nested dicts (as values). In fact, it can be
62 initialized with a dict. The only caveat is that keys may NOT contain dots ('.'). This is explained next:
63 Policy extends the dict api so that hierarchical values may be accessed with dot-delimited notiation.
64 That is, foo.getValue('a.b.c') is the same as foo['a']['b']['c'] is the same as foo['a.b.c'], and either
65 of these syntaxes may be used.
67 Storage formats supported:
68 - yaml: read and write is supported.
69 - pex policy: read is supported, although this is deprecated and will at some point be removed.
73 """Initialize the Policy. Other can be used to initialize the Policy in a variety of ways:
74 other (string) Treated as a path to a policy file on disk. Must end with '.paf' or '.yaml'.
75 other (Pex Policy) Initializes this Policy with the values in the passed-in Pex Policy.
76 other (Policy) Copies the other Policy's values into this one.
77 other (dict) Copies the values from the dict into this Policy.
79 collections.UserDict.__init__(self)
84 if isinstance(other, collections.Mapping):
86 elif isinstance(other, Policy):
87 self.
data = copy.deepcopy(other.data)
88 elif isinstance(other, basestring):
91 elif isinstance(other, pexPolicy.Policy):
96 raise RuntimeError(
"A Policy could not be loaded from other:%s" % other)
99 """helper function for debugging, prints a policy out in a readable way in the debugger.
101 use: pdb> print myPolicyObject.pprint()
102 :return: a prettyprint formatted string representing the policy
105 return pprint.pformat(self.
data, indent=2, width=1)
108 return self.data.__repr__()
110 def __initFromFile(self, path):
111 """Load a file from path. If path is a list, will pick one to use, according to order specified
112 by extensionPreference.
114 :param path: string or list of strings, to a persisted policy file.
115 :param extensionPreference: the order in which to try to open files. Will use the first one that
120 if path.endswith(
'yaml'):
122 elif path.endswith(
'paf'):
123 policy = pexPolicy.Policy.createPolicy(path)
126 raise RuntimeError(
"Unhandled policy file type:%s" % path)
128 def __initFromPexPolicy(self, pexPolicy):
129 """Load values from a pex policy.
134 names = pexPolicy.names()
137 if pexPolicy.getValueType(name) == pexPolicy.POLICY:
143 if pexPolicy.isArray(name):
144 self[name] = pexPolicy.getArray(name)
146 self[name] = pexPolicy.get(name)
149 def __initFromYamlFile(self, path):
150 """Opens a file at a given path and attempts to load it in from yaml.
155 with open(path,
'r') as f:
158 def __initFromYaml(self, stream):
159 """Loads a YAML policy from any readable stream that contains one.
165 self.
data = yaml.load(stream)
170 for key
in name.split(
'.'):
177 if isinstance(data, collections.Mapping):
182 if isinstance(value, collections.Mapping):
183 keys = name.split(
'.')
186 for key
in keys[0:-1]:
189 cur[keys[-1]] = value
192 keys = name.split(
'.')
193 for key
in keys[0:-1]:
194 data = data.setdefault(key, {})
195 data[keys[-1]] = value
199 keys = key.split(
'.')
209 """Get the path to a default policy file.
211 Determines a directory for the product specified by productName. Then Concatenates
212 productDir/relativePath/fileName (or productDir/fileName if relativePath is None) to find the path
213 to the default Policy file
215 @param productName (string) The name of the product that the default policy is installed as part of
216 @param fileName (string) The name of the policy file. Can also include a path to the file relative to
217 the directory where the product is installed.
218 @param relativePath (string) The relative path from the directior where the product is installed to
219 the location where the file (or the path to the file) is found. If None
220 (default), the fileName argument is relative to the installation
223 basePath = lsst.utils.getPackageDir(productName)
225 raise RuntimeError(
"No product installed for productName: %s" % basePath)
226 if relativePath
is not None:
227 basePath = os.path.join(basePath, relativePath)
228 fullFilePath = os.path.join(basePath, fileName)
232 """Like dict.update, but will add or modify keys in nested dicts, instead of overwriting the nested
235 For example, for the given code:
236 foo = {'a': {'b': 1}}
237 foo.update({'a': {'c': 2}})
239 If foo is a dict, then after the update foo == {'a': {'c': 2}}
240 But if foo is a Policy, then after the update foo == {'a': {'b': 1, 'c': 2}}
243 for k, v
in u.items():
244 if isinstance(d, collections.Mapping):
245 if isinstance(v, collections.Mapping):
246 r = doUpdate(d.get(k, {}), v)
253 doUpdate(self.
data, other)
256 """Like Policy.update, but will add keys & values from other that DO NOT EXIST in self. Keys and
257 values that already exist in self will NOT be overwritten.
262 otherCopy = copy.deepcopy(other)
263 otherCopy.update(self)
264 self.
data = otherCopy.data
266 def names(self, topLevelOnly=False):
267 """Get the dot-delimited name of all the keys in the hierarchy.
268 NOTE: this is different than the built-in method dict.keys, which will return only the first level
272 return list(self.keys())
274 def getKeys(d, keys, base):
277 levelKey = base +
'.' + key
if base
is not None else key
278 keys.append(levelKey)
279 if isinstance(val, collections.Mapping):
280 getKeys(val, keys, levelKey)
282 getKeys(self.
data, keys,
None)
286 """Get a value as an array. May contain one or more elements.
292 if isinstance(val, basestring):
294 elif not isinstance(val, collections.Container):
302 """Get the value for a parameter name/key. See class notes about dot-delimited access.
305 :return: the value for the given name.
307 warnings.warn_explicit(
"Deprecated. Use []", DeprecationWarning)
311 """Set the value for a parameter name/key. See class notes about dot-delimited access.
316 warnings.warn(
"Deprecated. Use []", DeprecationWarning)
320 """For any keys in other that are not present in self, sets that key and its value into self.
322 :param other: another Policy
325 warnings.warn(
"Deprecated. Use .merge()", DeprecationWarning)
329 """Query if a key exists in this Policy
332 :return: True if the key exists, else false.
334 warnings.warn(
"Deprecated. Use 'key in object'", DeprecationWarning)
338 """Get the string value of a key.
341 :return: the value for key
343 warnings.warn(
"Deprecated. Use []", DeprecationWarning)
344 return str(self[key])
347 """Get the value of a key.
350 :return: the value for key
352 warnings.warn(
"Deprecated. Use []", DeprecationWarning)
353 return bool(self[key])
361 warnings.warn(
"Deprecated. Use []", DeprecationWarning)
365 """Get a value as an array. May contain one or more elements.
370 warnings.warn(
"Deprecated. Use asArray()", DeprecationWarning)
372 if isinstance(val, basestring):
374 elif not isinstance(val, collections.Container):
379 if isinstance(other, Policy):
381 return self.
data < other
384 if isinstance(other, Policy):
386 return self.
data <= other
389 if isinstance(other, Policy):
391 return self.
data == other
394 if isinstance(other, Policy):
396 return self.
data != other
399 if isinstance(other, Policy):
401 return self.
data > other
404 if isinstance(other, Policy):
406 return self.
data >= other
412 """Writes the policy to a yaml stream.
420 data = copy.copy(self.
data)
421 keys = [
'defects',
'needCalibRegistry',
'levels',
'defaultLevel',
'defaultSubLevels',
'camera',
422 'exposures',
'calibrations',
'datasets']
425 yaml.dump({key: data.pop(key)}, output, default_flow_style=
False)
430 yaml.dump(data, output, default_flow_style=
False)
433 """Writes the policy to a file.
438 with open(path,
'w')
as f: