24 from future
import standard_library
25 standard_library.install_aliases()
26 from past.builtins
import basestring
36 from .
import LogicalLocation, Persistence, Policy, StorageList, Registry, Storage, RepositoryCfg, safeFileIo
39 from .safeFileIo
import SafeFilename
49 self.
log = Log.getLogger(
"daf.persistence.butler")
50 self.
root = parseRes = urllib.parse.urlparse(uri).path
51 if self.
root and not os.path.exists(self.
root):
52 os.makedirs(self.
root)
56 self.
persistence = Persistence.getPersistence(persistencePolicy)
61 return 'PosixStorage(root=%s)' % self.
root
65 """Get a persisted RepositoryCfg
68 parseRes = urllib.parse.urlparse(uri)
69 loc = os.path.join(parseRes.path,
'repositoryCfg.yaml')
70 if os.path.exists(loc):
71 with open(loc,
'r') as f:
72 repositoryCfg = yaml.load(f)
73 if repositoryCfg.root
is None:
74 repositoryCfg.root = uri
79 repositoryCfg = PosixStorage._getRepositoryCfg(uri)
80 if repositoryCfg
is not None:
87 if loc
is None or cfg.root == loc:
95 parseRes = urllib.parse.urlparse(loc)
97 if not os.path.exists(loc):
99 loc = os.path.join(loc,
'repositoryCfg.yaml')
100 with safeFileIo.FileForWriteOnceCompareSame(loc)
as f:
105 """Get the mapper class associated with a repository root.
107 Supports the legacy _parent symlink search (which was only ever posix-only. This should not be used by
108 new code and repositories; they should use the Repository parentCfg mechanism.
110 :param root: the location of a persisted ReositoryCfg is (new style repos), or the location where a
111 _mapper file is (old style repos).
112 :return: a class object or a class instance, depending on the state of the mapper when the repository
118 cfg = PosixStorage._getRepositoryCfg(root)
124 mapperFile =
"_mapper"
125 while not os.path.exists(os.path.join(basePath, mapperFile)):
127 if os.path.exists(os.path.join(basePath,
"_parent")):
128 basePath = os.path.join(basePath,
"_parent")
133 if mapperFile
is not None:
134 mapperFile = os.path.join(basePath, mapperFile)
137 with open(mapperFile,
"r") as f:
138 mapperName = f.readline().strip()
139 components = mapperName.split(".")
140 if len(components) <= 1:
141 raise RuntimeError(
"Unqualified mapper name %s in %s" %
142 (mapperName, mapperFile))
143 pkg = importlib.import_module(
".".join(components[:-1]))
144 return getattr(pkg, components[-1])
150 """For Butler V1 Repositories only, if a _parent symlink exists, get the location pointed to by the
156 A path to the folder on the local filesystem.
161 A path to the parent folder indicated by the _parent symlink, or None if there is no _parent
164 linkpath = os.path.join(root,
'_parent')
165 if os.path.exists(linkpath):
167 return os.readlink(os.path.join(root,
'_parent'))
171 return os.path.join(root,
'_parent')
175 """Get the class object for the mapper specified in the stored repository"""
176 return PosixStorage.getMapperClass(self.
root)
178 def write(self, butlerLocation, obj):
179 """Writes an object to a location and persistence format specified by ButlerLocation
181 :param butlerLocation: the location & formatting for the object to be written.
182 :param obj: the object to be written.
185 self.log.debug(
"Put location=%s obj=%s", butlerLocation, obj)
187 additionalData = butlerLocation.getAdditionalData()
188 storageName = butlerLocation.getStorageName()
189 locations = butlerLocation.getLocations()
191 pythonType = butlerLocation.getPythonType()
192 if pythonType
is not None:
193 if isinstance(pythonType, basestring):
195 pythonTypeTokenList = pythonType.split(
'.')
196 importClassString = pythonTypeTokenList.pop()
197 importClassString = importClassString.strip()
198 importPackage =
".".join(pythonTypeTokenList)
199 importType = __import__(importPackage, globals(), locals(), [importClassString], 0)
200 pythonType = getattr(importType, importClassString)
205 if hasattr(pythonType,
'butlerWrite'):
206 pythonType.butlerWrite(obj, butlerLocation=butlerLocation)
210 logLoc = LogicalLocation(locationString, additionalData)
212 if storageName ==
"PickleStorage":
213 with open(logLoc.locString(),
"wb")
as outfile:
214 pickle.dump(obj, outfile, pickle.HIGHEST_PROTOCOL)
217 if storageName ==
"ConfigStorage":
218 obj.save(logLoc.locString())
221 if storageName ==
"FitsCatalogStorage":
222 flags = additionalData.getInt(
"flags", 0)
223 obj.writeFits(logLoc.locString(), flags=flags)
227 storageList = StorageList()
228 storage = self.persistence.getPersistStorage(storageName, logLoc)
229 storageList.append(storage)
231 if storageName ==
'FitsStorage':
232 self.persistence.persist(obj, storageList, additionalData)
236 if hasattr(obj,
'__deref__'):
238 self.persistence.persist(obj.__deref__(), storageList, additionalData)
240 self.persistence.persist(obj, storageList, additionalData)
242 def read(self, butlerLocation):
243 """Read from a butlerLocation.
245 :param butlerLocation:
246 :return: a list of objects as described by the butler location. One item for each location in
247 butlerLocation.getLocations()
249 additionalData = butlerLocation.getAdditionalData()
251 storageName = butlerLocation.getStorageName()
253 locations = butlerLocation.getLocations()
254 pythonType = butlerLocation.getPythonType()
255 if pythonType
is not None:
256 if isinstance(pythonType, basestring):
258 pythonTypeTokenList = pythonType.split(
'.')
259 importClassString = pythonTypeTokenList.pop()
260 importClassString = importClassString.strip()
261 importPackage =
".".join(pythonTypeTokenList)
262 importType = __import__(importPackage, globals(), locals(), [importClassString], 0)
263 pythonType = getattr(importType, importClassString)
267 if hasattr(pythonType,
'butlerRead'):
268 results = pythonType.butlerRead(butlerLocation=butlerLocation)
271 for locationString
in locations:
272 logLoc = LogicalLocation(locationString, additionalData)
274 if storageName ==
"PafStorage":
275 finalItem = pexPolicy.Policy.createPolicy(logLoc.locString())
276 elif storageName ==
"YamlStorage":
277 finalItem = Policy(filePath=logLoc.locString())
278 elif storageName ==
"PickleStorage":
279 if not os.path.exists(logLoc.locString()):
280 raise RuntimeError(
"No such pickle file: " + logLoc.locString())
281 with open(logLoc.locString(),
"rb")
as infile:
285 if sys.version_info.major >= 3:
286 finalItem = pickle.load(infile, encoding=
"latin1")
288 finalItem = pickle.load(infile)
289 elif storageName ==
"FitsCatalogStorage":
290 if not os.path.exists(logLoc.locString()):
291 raise RuntimeError(
"No such FITS catalog file: " + logLoc.locString())
292 hdu = additionalData.getInt(
"hdu", 0)
293 flags = additionalData.getInt(
"flags", 0)
294 finalItem = pythonType.readFits(logLoc.locString(), hdu, flags)
295 elif storageName ==
"ConfigStorage":
296 if not os.path.exists(logLoc.locString()):
297 raise RuntimeError(
"No such config file: " + logLoc.locString())
298 finalItem = pythonType()
299 finalItem.load(logLoc.locString())
301 storageList = StorageList()
302 storage = self.persistence.getRetrieveStorage(storageName, logLoc)
303 storageList.append(storage)
304 itemData = self.persistence.unsafeRetrieve(
305 butlerLocation.getCppType(), storageList, additionalData)
306 finalItem = pythonType.swigConvert(itemData)
307 results.append(finalItem)
312 """Check if 'location' exists relative to root.
317 return os.path.exists(os.path.join(self.
root, location))
320 """Get the full path to the location.
325 return os.path.join(self.
root, location)
328 """Perform a lookup in the registry"""
329 return self.registry.lookup(*args, **kwargs)
333 """Test if a Version 1 Repository exists.
335 Version 1 Repositories only exist in posix storages and do not have a RepositoryCfg file.
336 To "exist" the folder at root must exist and contain files or folders.
341 A path to a folder on the local filesystem.
346 True if the repository at root exists, else False.
348 return os.path.exists(root)
and bool(os.listdir(root))
351 Storage.registerStorageClass(scheme=
'', cls=PosixStorage)
352 Storage.registerStorageClass(scheme=
'file', cls=PosixStorage)
a container for holding hierarchical configuration data in memory.
Abstract base class for storage implementations.