24 from past.builtins
import basestring
35 from .
import (LogicalLocation, Policy,
36 StorageInterface, Storage, ButlerLocation,
37 NoRepositroyAtRoot, RepositoryCfg, doImport)
40 from .safeFileIo
import SafeFilename, safeMakeDir
43 __all__ = [
"PosixStorage"]
47 """Defines the interface for a storage location on the local filesystem. 52 URI or path that is used as the storage location. 54 If True a new repository will be created at the root location if it 55 does not exist. If False then a new repository will not be created. 60 If create is False and a repository does not exist at the root 61 specified by uri then NoRepositroyAtRoot is raised. 65 self.
log = Log.getLogger(
"daf.persistence.butler")
67 if self.
root and not os.path.exists(self.
root):
73 return 'PosixStorage(root=%s)' % self.
root 76 def _pathFromURI(uri):
77 """Get the path part of the URI""" 78 return urllib.parse.urlparse(uri).path
82 """Get a relative path from a location to a location. 87 A path at which to start. It can be a relative path or an 90 A target location. It can be a relative path or an absolute path. 95 A relative path that describes the path from fromPath to toPath. 97 fromPath = os.path.realpath(fromPath)
98 return os.path.relpath(toPath, fromPath)
102 """Get an absolute path for the path from fromUri to toUri 106 fromPath : the starting location 107 A location at which to start. It can be a relative path or an 109 relativePath : the location relative to fromPath 115 Path that is an absolute path representation of fromPath + 116 relativePath, if one exists. If relativePath is absolute or if 117 fromPath is not related to relativePath then relativePath will be 120 if os.path.isabs(relativePath):
122 fromPath = os.path.realpath(fromPath)
123 return os.path.normpath(os.path.join(fromPath, relativePath))
127 """Get a persisted RepositoryCfg 131 uri : URI or path to a RepositoryCfg 136 A RepositoryCfg instance or None 138 storage = Storage.makeFromURI(uri)
142 locationList=
'repositoryCfg.yaml',
148 return storage.read(location)
152 storage = Storage.makeFromURI(cfg.root
if loc
is None else loc, create=
True)
156 locationList=
'repositoryCfg.yaml',
162 storage.write(location, cfg)
166 """Get the mapper class associated with a repository root. 168 Supports the legacy _parent symlink search (which was only ever posix-only. This should not be used by 169 new code and repositories; they should use the Repository parentCfg mechanism. 174 The location of a persisted ReositoryCfg is (new style repos), or 175 the location where a _mapper file is (old style repos). 179 A class object or a class instance, depending on the state of the 180 mapper when the repository was created. 185 cfg = PosixStorage.getRepositoryCfg(root)
191 mapperFile =
"_mapper" 192 while not os.path.exists(os.path.join(basePath, mapperFile)):
194 if os.path.exists(os.path.join(basePath,
"_parent")):
195 basePath = os.path.join(basePath,
"_parent")
200 if mapperFile
is not None:
201 mapperFile = os.path.join(basePath, mapperFile)
204 with open(mapperFile,
"r") as f: 205 mapperName = f.readline().strip() 206 components = mapperName.split(".")
207 if len(components) <= 1:
208 raise RuntimeError(
"Unqualified mapper name %s in %s" %
209 (mapperName, mapperFile))
210 pkg = importlib.import_module(
".".join(components[:-1]))
211 return getattr(pkg, components[-1])
217 """For Butler V1 Repositories only, if a _parent symlink exists, get the location pointed to by the 223 A path to the folder on the local filesystem. 228 A path to the parent folder indicated by the _parent symlink, or None if there is no _parent 231 linkpath = os.path.join(root,
'_parent')
232 if os.path.exists(linkpath):
234 return os.readlink(os.path.join(root,
'_parent'))
238 return os.path.join(root,
'_parent')
241 def write(self, butlerLocation, obj):
242 """Writes an object to a location and persistence format specified by 247 butlerLocation : ButlerLocation 248 The location & formatting for the object to be written. 249 obj : object instance 250 The object to be written. 252 self.
log.
debug(
"Put location=%s obj=%s", butlerLocation, obj)
255 if not writeFormatter:
258 writeFormatter(butlerLocation, obj)
261 raise(RuntimeError(
"No formatter for location:{}".
format(butlerLocation)))
263 def read(self, butlerLocation):
264 """Read from a butlerLocation. 268 butlerLocation : ButlerLocation 269 The location & formatting for the object(s) to be read. 273 A list of objects as described by the butler location. One item for 274 each location in butlerLocation.getLocations() 277 if not readFormatter:
280 return readFormatter(butlerLocation)
282 raise(RuntimeError(
"No formatter for location:{}".
format(butlerLocation)))
285 """Implementation of PosixStorage.exists for ButlerLocation objects. 287 storageName = location.getStorageName()
288 if storageName
not in (
'FitsStorage',
'PafStorage',
289 'PickleStorage',
'ConfigStorage',
'FitsCatalogStorage',
290 'YamlStorage',
'ParquetStorage',
'MatplotlibStorage'):
291 self.
log.
warn(
"butlerLocationExists for non-supported storage %s" % location)
293 for locationString
in location.getLocations():
294 logLoc =
LogicalLocation(locationString, location.getAdditionalData()).locString()
301 """Check if location exists. 305 location : ButlerLocation or string 306 A a string or a ButlerLocation that describes the location of an 307 object in this storage. 312 True if exists, else False. 314 if isinstance(location, ButlerLocation):
321 """Get the full path to the location. 326 return os.path.join(self.
root, location)
330 """Test if a Version 1 Repository exists. 332 Version 1 Repositories only exist in posix storages, do not have a 333 RepositoryCfg file, and contain either a registry.sqlite3 file, a 334 _mapper file, or a _parent link. 339 A path to a folder on the local filesystem. 344 True if the repository at root exists, else False. 346 return os.path.exists(root)
and (
347 os.path.exists(os.path.join(root,
"registry.sqlite3"))
or 348 os.path.exists(os.path.join(root,
"_mapper"))
or 349 os.path.exists(os.path.join(root,
"_parent"))
353 """Copy a file from one location to another on the local filesystem. 358 Path and name of existing file. 360 Path and name of new file. 366 shutil.copy(os.path.join(self.
root, fromLocation), os.path.join(self.
root, toLocation))
369 """Get a handle to a local copy of the file, downloading it to a 374 A path the the file in storage, relative to root. 378 A handle to a local copy of the file. If storage is remote it will be 379 a temporary file. If storage is local it may be the original file or 380 a temporary file. The file name can be gotten via the 'name' property 381 of the returned object. 383 p = os.path.join(self.
root, path)
393 """Search for the given path in this storage instance. 395 If the path contains an HDU indicator (a number in brackets before the 396 dot, e.g. 'foo.fits[1]', this will be stripped when searching and so 397 will match filenames without the HDU indicator, e.g. 'foo.fits'. The 398 path returned WILL contain the indicator though, e.g. ['foo.fits[1]']. 403 A filename (and optionally prefix path) to search for within root. 408 The location that was found, or None if no location was found. 413 def search(root, path, searchParents=False):
414 """Look for the given path in the current root. 416 Also supports searching for the path in Butler v1 repositories by 417 following the Butler v1 _parent symlink 419 If the path contains an HDU indicator (a number in brackets, e.g. 420 'foo.fits[1]', this will be stripped when searching and so 421 will match filenames without the HDU indicator, e.g. 'foo.fits'. The 422 path returned WILL contain the indicator though, e.g. ['foo.fits[1]']. 427 The path to the root directory. 429 The path to the file within the root directory. 430 searchParents : bool, optional 431 For Butler v1 repositories only, if true and a _parent symlink 432 exists, then the directory at _parent will be searched if the file 433 is not found in the root repository. Will continue searching the 434 parent of the parent until the file is found or no additional 440 The location that was found, or None if no location was found. 446 while len(rootDir) > 1
and rootDir[-1] ==
'/':
447 rootDir = rootDir[:-1]
449 if path.startswith(rootDir +
"/"):
451 path = path[len(rootDir +
'/'):]
453 elif rootDir ==
"/" and path.startswith(
"/"):
458 pathPrefix = os.path.dirname(path)
459 while pathPrefix !=
"" and pathPrefix !=
"/":
460 if os.path.realpath(pathPrefix) == os.path.realpath(root):
462 pathPrefix = os.path.dirname(pathPrefix)
463 if pathPrefix ==
"/":
465 elif pathPrefix !=
"":
466 path = path[len(pathPrefix)+1:]
472 firstBracket = path.find(
"[")
473 if firstBracket != -1:
474 strippedPath = path[:firstBracket]
475 pathStripped = path[firstBracket:]
479 paths = glob.glob(os.path.join(dir, strippedPath))
481 if pathPrefix != rootDir:
482 paths = [p[len(rootDir+
'/'):]
for p
in paths]
483 if pathStripped
is not None:
484 paths = [p + pathStripped
for p
in paths]
487 dir = os.path.join(dir,
"_parent")
488 if not os.path.exists(dir):
495 """Ask if a storage at the location described by uri exists 500 URI to the the root location of the storage 505 True if the storage exists, false if not 507 return os.path.exists(PosixStorage._pathFromURI(uri))
511 """Read an lsst.pex.config.Config from a butlerLocation. 515 butlerLocation : ButlerLocation 516 The location for the object(s) to be read. 520 A list of objects as described by the butler location. One item for 521 each location in butlerLocation.getLocations() 524 for locationString
in butlerLocation.getLocations():
525 locStringWithRoot = os.path.join(butlerLocation.getStorage().root, locationString)
526 logLoc =
LogicalLocation(locStringWithRoot, butlerLocation.getAdditionalData())
527 if not os.path.exists(logLoc.locString()):
528 raise RuntimeError(
"No such config file: " + logLoc.locString())
529 pythonType = butlerLocation.getPythonType()
530 if pythonType
is not None:
531 if isinstance(pythonType, basestring):
533 finalItem = pythonType()
534 finalItem.load(logLoc.locString())
535 results.append(finalItem)
540 """Writes an lsst.pex.config.Config object to a location specified by 545 butlerLocation : ButlerLocation 546 The location for the object to be written. 547 obj : object instance 548 The object to be written. 550 filename = os.path.join(butlerLocation.getStorage().root, butlerLocation.getLocations()[0])
552 logLoc =
LogicalLocation(locationString, butlerLocation.getAdditionalData())
553 obj.save(logLoc.locString())
557 """Read objects from a FITS file specified by ButlerLocation. 559 The object is read using class or static method 560 ``readFitsWithOptions(path, options)``, if it exists, else 561 ``readFits(path)``. The ``options`` argument is the data returned by 562 ``butlerLocation.getAdditionalData()``. 566 butlerLocation : ButlerLocation 567 The location for the object(s) to be read. 571 A list of objects as described by the butler location. One item for 572 each location in butlerLocation.getLocations() 574 pythonType = butlerLocation.getPythonType()
575 if pythonType
is not None:
576 if isinstance(pythonType, basestring):
578 supportsOptions = hasattr(pythonType,
"readFitsWithOptions")
579 if not supportsOptions:
581 if issubclass(pythonType, (PropertySet, PropertyList)):
583 reader = readMetadata
585 reader = pythonType.readFits
587 additionalData = butlerLocation.getAdditionalData()
588 for locationString
in butlerLocation.getLocations():
589 locStringWithRoot = os.path.join(butlerLocation.getStorage().root, locationString)
593 filePath = re.sub(
r"(\.fits(.[a-zA-Z0-9]+)?)(\[.+\])$",
r"\1", logLoc.locString())
594 if not os.path.exists(filePath):
595 raise RuntimeError(
"No such FITS file: " + logLoc.locString())
597 finalItem = pythonType.readFitsWithOptions(logLoc.locString(), options=additionalData)
599 fileName = logLoc.locString()
600 mat = re.search(
r"^(.*)\[(\d+)\]$", fileName)
602 if mat
and reader == readMetadata:
603 fileName = mat.group(1)
604 hdu =
int(mat.group(2))
606 finalItem = reader(fileName, hdu=hdu)
608 finalItem = reader(fileName)
609 results.append(finalItem)
614 """Writes an object to a FITS file specified by ButlerLocation. 616 The object is written using method 617 ``writeFitsWithOptions(path, options)``, if it exists, else 618 ``writeFits(path)``. The ``options`` argument is the data returned by 619 ``butlerLocation.getAdditionalData()``. 623 butlerLocation : ButlerLocation 624 The location for the object to be written. 625 obj : object instance 626 The object to be written. 628 supportsOptions = hasattr(obj,
"writeFitsWithOptions")
629 additionalData = butlerLocation.getAdditionalData()
630 locations = butlerLocation.getLocations()
631 with
SafeFilename(os.path.join(butlerLocation.getStorage().root, locations[0]))
as locationString:
634 obj.writeFitsWithOptions(logLoc.locString(), options=additionalData)
636 obj.writeFits(logLoc.locString())
640 """Read a catalog from a Parquet file specified by ButlerLocation. 642 The object returned by this is expected to be a subtype 643 of `ParquetTable`, which is a thin wrapper to `pyarrow.ParquetFile` 644 that allows for lazy loading of the data. 648 butlerLocation : ButlerLocation 649 The location for the object(s) to be read. 653 A list of objects as described by the butler location. One item for 654 each location in butlerLocation.getLocations() 657 additionalData = butlerLocation.getAdditionalData()
659 for locationString
in butlerLocation.getLocations():
660 locStringWithRoot = os.path.join(butlerLocation.getStorage().root, locationString)
662 if not os.path.exists(logLoc.locString()):
663 raise RuntimeError(
"No such parquet file: " + logLoc.locString())
665 pythonType = butlerLocation.getPythonType()
666 if pythonType
is not None:
667 if isinstance(pythonType, basestring):
670 filename = logLoc.locString()
674 results.append(pythonType(filename=filename))
680 """Writes pandas dataframe to parquet file. 684 butlerLocation : ButlerLocation 685 The location for the object(s) to be read. 686 obj : `lsst.qa.explorer.parquetTable.ParquetTable` 687 Wrapped DataFrame to write. 690 additionalData = butlerLocation.getAdditionalData()
691 locations = butlerLocation.getLocations()
692 with
SafeFilename(os.path.join(butlerLocation.getStorage().root, locations[0]))
as locationString:
694 filename = logLoc.locString()
699 """Writes an object to a YAML file specified by ButlerLocation. 703 butlerLocation : ButlerLocation 704 The location for the object to be written. 705 obj : object instance 706 The object to be written. 708 additionalData = butlerLocation.getAdditionalData()
709 locations = butlerLocation.getLocations()
710 with
SafeFilename(os.path.join(butlerLocation.getStorage().root, locations[0]))
as locationString:
712 with open(logLoc.locString(),
"w")
as outfile:
713 yaml.dump(obj, outfile)
717 """Read an object from a pickle file specified by ButlerLocation. 721 butlerLocation : ButlerLocation 722 The location for the object(s) to be read. 726 A list of objects as described by the butler location. One item for 727 each location in butlerLocation.getLocations() 731 additionalData = butlerLocation.getAdditionalData()
732 for locationString
in butlerLocation.getLocations():
733 locStringWithRoot = os.path.join(butlerLocation.getStorage().root, locationString)
735 if not os.path.exists(logLoc.locString()):
736 raise RuntimeError(
"No such pickle file: " + logLoc.locString())
737 with open(logLoc.locString(),
"rb")
as infile:
741 if sys.version_info.major >= 3:
742 finalItem = pickle.load(infile, encoding=
"latin1")
744 finalItem = pickle.load(infile)
745 results.append(finalItem)
750 """Writes an object to a pickle file specified by ButlerLocation. 754 butlerLocation : ButlerLocation 755 The location for the object to be written. 756 obj : object instance 757 The object to be written. 759 additionalData = butlerLocation.getAdditionalData()
760 locations = butlerLocation.getLocations()
761 with
SafeFilename(os.path.join(butlerLocation.getStorage().root, locations[0]))
as locationString:
763 with open(logLoc.locString(),
"wb")
as outfile:
764 pickle.dump(obj, outfile, pickle.HIGHEST_PROTOCOL)
768 """Read a catalog from a FITS table specified by ButlerLocation. 772 butlerLocation : ButlerLocation 773 The location for the object(s) to be read. 777 A list of objects as described by the butler location. One item for 778 each location in butlerLocation.getLocations() 780 pythonType = butlerLocation.getPythonType()
781 if pythonType
is not None:
782 if isinstance(pythonType, basestring):
785 additionalData = butlerLocation.getAdditionalData()
786 for locationString
in butlerLocation.getLocations():
787 locStringWithRoot = os.path.join(butlerLocation.getStorage().root, locationString)
789 if not os.path.exists(logLoc.locString()):
790 raise RuntimeError(
"No such FITS catalog file: " + logLoc.locString())
792 if additionalData.exists(
"hdu"):
793 kwds[
"hdu"] = additionalData.getInt(
"hdu")
794 if additionalData.exists(
"flags"):
795 kwds[
"flags"] = additionalData.getInt(
"flags")
796 finalItem = pythonType.readFits(logLoc.locString(), **kwds)
797 results.append(finalItem)
802 """Writes a catalog to a FITS table specified by ButlerLocation. 806 butlerLocation : ButlerLocation 807 The location for the object to be written. 808 obj : object instance 809 The object to be written. 811 additionalData = butlerLocation.getAdditionalData()
812 locations = butlerLocation.getLocations()
813 with
SafeFilename(os.path.join(butlerLocation.getStorage().root, locations[0]))
as locationString:
815 if additionalData.exists(
"flags"):
816 kwds = dict(flags=additionalData.getInt(
"flags"))
819 obj.writeFits(logLoc.locString(), **kwds)
823 """Read from a butlerLocation (always fails for this storage type). 827 butlerLocation : ButlerLocation 828 The location for the object(s) to be read. 832 A list of objects as described by the butler location. One item for 833 each location in butlerLocation.getLocations() 835 raise NotImplementedError(
"Figures saved with MatplotlibStorage cannot be retreived using the Butler.")
839 """Writes a matplotlib.figure.Figure to a location, using the template's 840 filename suffix to infer the file format. 844 butlerLocation : ButlerLocation 845 The location for the object to be written. 846 obj : matplotlib.figure.Figure 847 The object to be written. 849 additionalData = butlerLocation.getAdditionalData()
850 locations = butlerLocation.getLocations()
851 with
SafeFilename(os.path.join(butlerLocation.getStorage().root, locations[0]))
as locationString:
857 _, ext = os.path.splitext(locations[0])
864 obj.savefig(logLoc.locString(), format=ext)
868 """Read a policy from a PAF file specified by a ButlerLocation. 872 butlerLocation : ButlerLocation 873 The location for the object(s) to be read. 877 A list of objects as described by the butler location. One item for 878 each location in butlerLocation.getLocations() 881 for locationString
in butlerLocation.getLocations():
882 logLoc =
LogicalLocation(butlerLocation.getStorage().locationWithRoot(locationString),
883 butlerLocation.getAdditionalData())
884 finalItem = pexPolicy.Policy.createPolicy(logLoc.locString())
885 results.append(finalItem)
890 """Read an object from a YAML file specified by a butlerLocation. 894 butlerLocation : ButlerLocation 895 The location for the object(s) to be read. 899 A list of objects as described by the butler location. One item for 900 each location in butlerLocation.getLocations() 903 for locationString
in butlerLocation.getLocations():
904 logLoc =
LogicalLocation(butlerLocation.getStorage().locationWithRoot(locationString),
905 butlerLocation.getAdditionalData())
906 if not os.path.exists(logLoc.locString()):
907 raise RuntimeError(
"No such YAML file: " + logLoc.locString())
909 if butlerLocation.pythonType ==
'lsst.daf.persistence.RepositoryCfg':
910 finalItem =
Policy(filePath=logLoc.locString())
914 loader = yaml.FullLoader
915 except AttributeError:
917 with open(logLoc.locString(),
"rb")
as infile:
918 finalItem = yaml.load(infile, Loader=loader)
919 results.append(finalItem)
923 PosixStorage.registerFormatters(
"FitsStorage", readFitsStorage, writeFitsStorage)
924 PosixStorage.registerFormatters(
"ParquetStorage", readParquetStorage, writeParquetStorage)
925 PosixStorage.registerFormatters(
"ConfigStorage", readConfigStorage, writeConfigStorage)
926 PosixStorage.registerFormatters(
"PickleStorage", readPickleStorage, writePickleStorage)
927 PosixStorage.registerFormatters(
"FitsCatalogStorage", readFitsCatalogStorage, writeFitsCatalogStorage)
928 PosixStorage.registerFormatters(
"MatplotlibStorage", readMatplotlibStorage, writeMatplotlibStorage)
929 PosixStorage.registerFormatters(
"PafStorage", readFormatter=readPafStorage)
930 PosixStorage.registerFormatters(
"YamlStorage", readYamlStorage, writeYamlStorage)
932 Storage.registerStorageClass(scheme=
'', cls=PosixStorage)
933 Storage.registerStorageClass(scheme=
'file', cls=PosixStorage)
def readMatplotlibStorage(butlerLocation)
def copyFile(self, fromLocation, toLocation)
def readConfigStorage(butlerLocation)
def readPickleStorage(butlerLocation)
def safeMakeDir(directory)
def writeMatplotlibStorage(butlerLocation, obj)
def getWriteFormatter(cls, objType)
Class for logical location of a persisted Persistable instance.
def writePickleStorage(butlerLocation, obj)
def readParquetStorage(butlerLocation)
def relativePath(fromPath, toPath)
def writeFitsCatalogStorage(butlerLocation, obj)
def exists(self, location)
def readPafStorage(butlerLocation)
def butlerLocationExists(self, location)
def search(root, path, searchParents=False)
def writeParquetStorage(butlerLocation, obj)
def writeConfigStorage(butlerLocation, obj)
def readFitsStorage(butlerLocation)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def locationWithRoot(self, location)
def readFitsCatalogStorage(butlerLocation)
def getParentSymlinkPath(root)
def readYamlStorage(butlerLocation)
def absolutePath(fromPath, relativePath)
def instanceSearch(self, path)
def putRepositoryCfg(cfg, loc=None)
def getReadFormatter(cls, objType)
def writeYamlStorage(butlerLocation, obj)
def getLocalFile(self, path)
def read(self, butlerLocation)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
def writeFitsStorage(butlerLocation, obj)
def write(self, butlerLocation, obj)
def __init__(self, uri, create)
def getRepositoryCfg(uri)