33 """Arguments passed into a Butler that are used to instantiate a repository. This includes arguments that
34 can be used to create a new repository (cfgRoot, root, mapper, mapperArgs, policy) and are persisted along
35 with the new repository's configuration file. These arguments can also describe how a new or existing
36 repository are to be used (cfgRoot or root, tags, mode). When indicating an existing repository it is
37 better to not specify unnecessary arguments, as if they conflict with the persisted repository
38 configuration then a RuntimeError will be raised during Butler init.
40 A RepositoryArgs class can be initialized from a dict, if the first argument to the initializer is a dict.
44 cfgRoot : URI or dict, optional
45 If dict, the initalizer is re-called with the expanded dict.
46 If URI, this is the location where the RepositoryCfg should be found (existing repo) or put (new repo)
48 If different than cfgRoot then this is the location where the repository should exist. A RepositoryCfg
49 will be put at cfgRoot and its root will be a path to root.
50 mapper : string or class object, optional
51 The mapper to use with this repository. If string, should refer an importable object. If class object,
52 should be a mapper to be instantiated by the Butler during Butler init.
54 Arguments & values to pass to the mapper when initializing it.
55 tags : list or object, optional
56 One or more unique identifiers to uniquely identify this repository and its parents when performing
58 mode : string, optional
59 should be one of 'r', 'w', or 'rw', for 'read', 'write', or 'read-write'. Can be omitted; input
60 repositories will default to 'r', output repositories will default to 'w'. 'w' on an input repository
61 will raise a RuntimeError during Butler init, although 'rw' works and is equivalent to 'r'. Output
62 repositories may be 'r' or 'rw', 'r' for an output repository will raise a RuntimeError during Butler
65 Policy associated with this repository, overrides all other policy data (which may be loaded from
66 policies in derived packages).
68 def __init__(self, cfgRoot=None, root=None, mapper=None, mapperArgs=None, tags=None,
69 mode=None, policy=None):
74 self.
_root = Storage.absolutePath(os.getcwd(), root.rstrip(os.sep))
if root
else root
75 self.
_cfgRoot = Storage.absolutePath(os.getcwd(), cfgRoot.rstrip(os.sep))
if cfgRoot
else cfgRoot
80 self.
policy =
Policy(policy)
if policy
is not None else None
83 return "%s(root=%r, cfgRoot=%r, mapper=%r, mapperArgs=%r, tags=%s, mode=%r, policy=%s)" % (
93 if mapper
is not None and self.
_mapper:
94 raise RuntimeError(
"Explicity clear mapper (set to None) before changing its value.")
110 def outputRepo(storage, mapper=None, mapperArgs=None, tags=None, mode=None):
114 """add a tag to the repository cfg"""
115 if isinstance(tag, str):
119 self.
tags.update(tag)
125 """Represents a repository of persisted data and has methods to access that data.
129 """Initialize a Repository with parameters input via RepoData.
134 Object that contains the parameters with which to init the Repository.
136 self.
_storage = Storage.makeFromURI(repoData.cfg.root)
137 if repoData.cfg.dirty
and not repoData.isV1Repository
and repoData.cfgOrigin !=
'nested':
138 self.
_storage.putRepositoryCfg(repoData.cfg, repoData.cfgRoot)
142 def _initMapper(self, repoData):
143 '''Initialize and keep the mapper in a member var.
148 The RepoData with the properties of this Repository.
155 mapper = repoData.cfg.mapper
158 if isinstance(mapper, str):
161 if inspect.isclass(mapper):
162 mapperArgs = copy.copy(repoData.cfg.mapperArgs)
163 if mapperArgs
is None:
165 if 'root' not in mapperArgs:
166 mapperArgs[
'root'] = repoData.cfg.root
167 mapper =
mapper(parentRegistry=repoData.parentRegistry,
168 repositoryCfg=repoData.cfg,
173 def write(self, butlerLocation, obj):
174 """Write a dataset to Storage.
176 :param butlerLocation: Contains the details needed to find the desired dataset.
177 :param dataset: The dataset to be written.
180 butlerLocationStorage = butlerLocation.getStorage()
181 if butlerLocationStorage:
182 return butlerLocationStorage.write(butlerLocation, obj)
186 def read(self, butlerLocation):
187 """Read a dataset from Storage.
189 :param butlerLocation: Contains the details needed to find the desired dataset.
190 :return: An instance of the dataset requested by butlerLocation.
192 butlerLocationStorage = butlerLocation.getStorage()
193 if butlerLocationStorage:
194 return butlerLocationStorage.read(butlerLocation)
205 """Get the registry from the mapper
210 The registry from the mapper or None if the mapper does not have one.
218 Get the keys available in the repository/repositories.
221 :return: A dict of {key:valueType}
229 def map(self, *args, **kwargs):
230 """Find a butler location for the given arguments.
231 See mapper.map for more information about args and kwargs.
233 :param args: arguments to be passed on to mapper.map
234 :param kwargs: keyword arguments to be passed on to mapper.map
235 :return: The type of item is dependent on the mapper being used but is typically a ButlerLocation.
238 raise RuntimeError(
"No mapper assigned to Repository")
242 loc.setRepository(self)
246 """Gets possible values for keys given a partial data id.
248 See mapper documentation for more explanation about queryMetadata.
250 :param args: arguments to be passed on to mapper.queryMetadata
251 :param kwargs: keyword arguments to be passed on to mapper.queryMetadata
252 :return:The type of item is dependent on the mapper being used but is typically a set that contains
253 available values for the keys in the format input argument.
261 """Perform mapper.backup.
263 See mapper.backup for more information about args and kwargs.
265 :param args: arguments to be passed on to mapper.backup
266 :param kwargs: keyword arguments to be passed on to mapper.backup
274 """Get the default level of the mapper.
276 This is typically used if no level is passed into butler methods that call repository.getKeys and/or
277 repository.queryMetadata. There is a bug in that code because it gets the default level from this
278 repository but then uses that value when searching all repositories. If this and other repositories
279 have dissimilar data, the default level value will be nonsensical. A good example of this issue is in
280 Butler.subset; it needs refactoring.
286 return self.
_mapper.getDefaultLevel()
289 """Check if location exists in storage.
293 location : ButlerLocation
294 Desrcibes a location in storage to look for.
299 True if location exists, False if not.
301 butlerLocationStorage = location.getStorage()
302 if butlerLocationStorage:
303 return butlerLocationStorage.exists(location)