lsst.daf.persistence  13.0-11-gfc17871
 All Classes Namespaces Files Functions Variables Typedefs Friends Macros
repositoryCfg.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 #
4 # LSST Data Management System
5 # Copyright 2016 LSST Corporation.
6 #
7 # This product includes software developed by the
8 # LSST Project (http://www.lsst.org/).
9 #
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
19 #
20 # You should have received a copy of the LSST License Statement and
21 # the GNU General Public License along with this program. If not,
22 # see <http://www.lsstcorp.org/LegalNotices/>.
23 #
24 
25 # -*- python -*-
26 
27 import os
28 import yaml
29 
30 from lsst.daf.persistence import listify, iterify, doImport, Storage
31 from past.builtins import basestring
32 
33 
34 class RepositoryCfg(yaml.YAMLObject):
35  """RepositoryCfg stores the configuration of a repository. Its contents are persisted to the repository
36  when the repository is created in persistent storage. Thereafter the the RepositoryCfg should not change.
37 
38  Parameters
39  ----------
40  mapper : string
41  The mapper associated with the repository. The string should be importable to a class object.
42  mapperArgs : dict
43  Arguments & values to pass to the mapper when initializing it.
44  parents : list of URI
45  URIs to the locaiton of the parent RepositoryCfgs of this repository.
46  policy : dict
47  Policy associated with this repository, overrides all other policy data (which may be loaded from
48  policies in derived packages).
49  deserializing : bool
50  Butler internal use only. This flag is used to indicate to the init funciton that the repository class
51  is being deserialized and should not perform certain operations that normally happen in other uses of
52  init.
53  """
54  yaml_tag = u"!RepositoryCfg_v1"
55 
56  def __init__(self, root, mapper, mapperArgs, parents, policy, deserializing=False):
57  self._root = root
58  self._mapper = mapper
59  self._mapperArgs = mapperArgs
60  # Where possible we mangle the parents so that they are relative to root, for example if the root and
61  # the parents are both in the same PosixStorage. The parents are stored in mangled form; when
62  # deserializing the parents we do not re-mangle them.
63  if deserializing:
64  self._parents = parents
65  else:
66  self._parents = []
67  self.addParents(iterify(parents))
68  self._policy = policy
69 
70  @staticmethod
71  def v1Constructor(loader, node):
72  """Constructor for 'version 1' of the serlized RepositoryCfg.
73 
74  If new parameters are added to RepositoryCfg they will have to be checked for in d; if they are there
75  then their value should be used and if they are not there a default value must be used in place.
76 
77  In case the structure of the serialzed file must be changed in a way that invalidates some of the
78  keys:
79  1. Increment the version number (after _v1) in the yaml_tag of this class.
80  2. Add a new constructor (similar to this one) to deserialze new serializations of this class.
81  3. Registered the new constructor for the new version with yaml, the same way it is done at the bottom
82  of this file.
83  4. All constructors for the older version(s) of persisted RepositoryCfg must be changed to adapt
84  the old keys to their new uses and create the current (new) version of a repository cfg, or raise a
85  RuntimeError in the case that older versions of serialized RepositoryCfgs can not be adapted.
86  There is an example of migrating from a fictitious v0 to v1 in tests/repositoryCfg.py
87  """
88  d = loader.construct_mapping(node)
89  cfg = RepositoryCfg(root=d['_root'], mapper=d['_mapper'], mapperArgs=d['_mapperArgs'],
90  parents=d['_parents'], policy=d.get('_policy', None), deserializing=True)
91  return cfg
92 
93  def __eq__(self, other):
94  if not other:
95  return False
96  return self.root == other.root and \
97  self.mapper == other.mapper and \
98  self.mapperArgs == other.mapperArgs and \
99  self.parents == other.parents
100 
101  def __ne__(self, other):
102  return not self.__eq__(other)
103 
104  @property
105  def root(self):
106  return self._root
107 
108  @root.setter
109  def root(self, root):
110  if root is not None and self._root is not None:
111  raise RuntimeError("Explicity clear root (set to None) before changing the value of root.")
112  self._root = root
113 
114  @property
115  def mapper(self):
116  return self._mapper
117 
118  @mapper.setter
119  def mapper(self, mapper):
120  if self._mapper is not None:
121  raise RuntimeError("Should not set mapper over previous not-None value.")
122  self._mapper = mapper
123 
124  @property
125  def mapperArgs(self):
126  return self._mapperArgs
127 
128  @mapperArgs.setter
129  def mapperArgs(self, newDict):
130  self._mapperArgs = newDict
131 
132  @property
133  def parents(self):
134  return [Storage.absolutePath(self.root, p) for p in self._parents]
135 
136  def addParents(self, newParents):
137  newParents = listify(newParents)
138  for newParent in newParents:
139  newParent = Storage.relativePath(self.root, newParent)
140  if newParent not in self._parents:
141  self._parents.append(newParent)
142 
143  @property
144  def policy(self):
145  return self._policy
146 
147  @staticmethod
148  def makeFromArgs(repositoryArgs, parents):
149  cfg = RepositoryCfg(root=repositoryArgs.root,
150  mapper=repositoryArgs.mapper,
151  mapperArgs=repositoryArgs.mapperArgs,
152  parents=parents,
153  policy=repositoryArgs.policy)
154  return cfg
155 
156  def matchesArgs(self, repositoryArgs):
157  """Checks that a repositoryArgs instance will work with this repositoryCfg. This is useful
158  when loading an already-existing repository that has a persisted cfg, to ensure that the args that are
159  passed into butler do not conflict with the persisted cfg."""
160  if repositoryArgs.root is not None and self._root != repositoryArgs.root:
161  return False
162 
163  repoArgsMapper = repositoryArgs.mapper
164  cfgMapper = self._mapper
165  if isinstance(repoArgsMapper, basestring):
166  repoArgsMapper = doImport(repoArgsMapper)
167  if isinstance(cfgMapper, basestring):
168  cfgMapper = doImport(cfgMapper)
169  if repoArgsMapper is not None and repoArgsMapper != cfgMapper:
170  return False
171  # check mapperArgs for any keys in common and if their value does not match then return false.
172  if self._mapperArgs is not None and repositoryArgs.mapperArgs is not None:
173  for key in set(self._mapperArgs.keys()) & set(repositoryArgs.mapperArgs):
174  if self._mapperArgs[key] != repositoryArgs.mapperArgs[key]:
175  return False
176  if repositoryArgs.policy and repositoryArgs.policy != self._policy:
177  return False
178 
179  return True
180 
181  def __repr__(self):
182  return "%s(root=%r, mapper=%r, mapperArgs=%r, parents=%s, policy=%s)" % (
183  self.__class__.__name__,
184  self._root,
185  self._mapper,
186  self._mapperArgs,
187  self._parents,
188  self._policy)
189 
190 yaml.add_constructor(u"!RepositoryCfg_v1", RepositoryCfg.v1Constructor)