lsst.daf.persistence g17e5ecfddb+13e059a339
fmtPosixRepositoryCfg.py
Go to the documentation of this file.
2# LSST Data Management System
3# Copyright 2017 LSST Corporation.
4#
5# This product includes software developed by the
6# LSST Project (http://www.lsst.org/).
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the LSST License Statement and
19# the GNU General Public License along with this program. If not,
20# see <http://www.lsstcorp.org/LegalNotices/>.
21#
22
23import copy
24import errno
25import yaml
26import os
27import urllib
28from . import PosixStorage, RepositoryCfg, safeFileIo, ParentsMismatch
29
30
31__all__ = []
32
33try:
34 # PyYAML >=5.1 prefers a different loader
35 # We need to use Unsafe because obs packages do not register
36 # constructors but rely on python object syntax.
37 Loader = yaml.UnsafeLoader
38except AttributeError:
39 Loader = yaml.Loader
40
41
42def _write(butlerLocation, cfg):
43 """Serialize a RepositoryCfg to a location.
44
45 When the location is the same as cfg.root, the RepositoryCfg is to be written at the root location of
46 the repository. In that case, root is not written in the serialized cfg; it is implicit in the
47 location of the cfg. This allows the cfg to move from machine to machine without modification.
48
49 Parameters
50 ----------
51 butlerLocation : ButlerLocation
52 The location to write the RepositoryCfg.
53 cfg : RepositoryCfg instance
54 The RepositoryCfg to be serialized.
55 """
56 def setRoot(cfg, loc):
57 loc = os.path.split(loc)[0] # remove the `repoistoryCfg.yaml` file name
58 if loc is None or cfg.root == loc:
59 cfg = copy.copy(cfg)
60 cfg.root = None
61 return cfg
62
63 # This class supports schema 'file' and also treats no schema as 'file'.
64 # Split the URI and take only the path; remove the schema from loc if it's there.
65 loc = butlerLocation.storage.root
66 parseRes = urllib.parse.urlparse(loc if loc is not None else cfg.root)
67 loc = os.path.join(parseRes.path, butlerLocation.getLocations()[0])
68 try:
69 with safeFileIo.SafeLockedFileForRead(loc) as f:
70 existingCfg = _doRead(f, parseRes.path)
71 if existingCfg == cfg:
72 cfg.dirty = False
73 return
74 except IOError as e:
75 if e.errno != errno.ENOENT: # ENOENT is 'No such file or directory'
76 raise
78 existingCfg = _doRead(f, parseRes.path)
79 if existingCfg is None:
80 cfgToWrite = setRoot(cfg, loc)
81 else:
82 if existingCfg == cfg:
83 cfg.dirty = False
84 return
85 try:
86 existingCfg.extend(cfg)
87 cfgToWrite = setRoot(existingCfg, loc)
88 except ParentsMismatch as e:
89 raise RuntimeError("Can not extend existing repository cfg because: {}".format(e))
90 yaml.dump(cfgToWrite, f)
91 cfg.dirty = False
92
93
94def _doRead(fileObject, uri):
95 """Get a persisted RepositoryCfg from an open file object.
96
97 Parameters
98 ----------
99 fileObject : an open file object
100 the file that contains the RepositoryCfg.
101 uri : string
102 path to the repositoryCfg
103
104 Returns
105 -------
106 A RepositoryCfg instance or None
107 """
108 repositoryCfg = yaml.load(fileObject, Loader=Loader)
109 if repositoryCfg is not None:
110 if repositoryCfg.root is None:
111 repositoryCfg.root = uri
112 return repositoryCfg
113
114
115def _read(butlerLocation):
116 """Deserialize a RepositoryCfg from a location.
117
118 Parameters
119 ----------
120 butlerLocation : ButlerLocation
121 The lcoation from which to read the RepositoryCfg.
122
123 Returns
124 -------
125 RepositoryCfg
126 The deserialized RepoistoryCfg.
127
128 Raises
129 ------
130 IOError
131 Raised if no repositoryCfg exists at the location.
132 """
133 repositoryCfg = None
134 loc = butlerLocation.storage.root
135 fileLoc = os.path.join(loc, butlerLocation.getLocations()[0])
136 try:
137 with safeFileIo.SafeLockedFileForRead(fileLoc) as f:
138 repositoryCfg = _doRead(f, loc)
139 except IOError as e:
140 if e.errno != errno.ENOENT: # ENOENT is 'No such file or directory'
141 raise
142 return repositoryCfg
143
144
145PosixStorage.registerFormatters(RepositoryCfg, _read, _write)