29 from . 
import iterify, doImport, Storage, ParentsMismatch
 
   33     """RepositoryCfg stores the configuration of a repository. Its contents are persisted to the repository 
   34     when the repository is created in persistent storage. Thereafter the the RepositoryCfg should not change. 
   39         The mapper associated with the repository. The string should be importable to a class object. 
   41         Arguments & values to pass to the mapper when initializing it. 
   43         URIs to the locaiton of the parent RepositoryCfgs of this repository. 
   45         Policy associated with this repository, overrides all other policy data (which may be loaded from 
   46         policies in derived packages). 
   48         Butler internal use only. This flag is used to indicate to the init funciton that the repository class 
   49         is being deserialized and should not perform certain operations that normally happen in other uses of 
   52     yaml_tag = 
u"!RepositoryCfg_v1" 
   54     def __init__(self, root, mapper, mapperArgs, parents, policy):
 
   57         self.
_mapperArgs = {} 
if mapperArgs 
is None else mapperArgs
 
   65         """Constructor for 'version 1' of the serlized RepositoryCfg. 
   67         If new parameters are added to RepositoryCfg they will have to be checked for in d; if they are there 
   68         then their value should be used and if they are not there a default value must be used in place. 
   70         In case the structure of the serialzed file must be changed in a way that invalidates some of the 
   72         1. Increment the version number (after _v1) in the yaml_tag of this class. 
   73         2. Add a new constructor (similar to this one) to deserialze new serializations of this class. 
   74         3. Registered the new constructor for the new version with yaml, the same way it is done at the bottom 
   76         4. All constructors for the older version(s) of persisted RepositoryCfg must be changed to adapt 
   77            the old keys to their new uses and create the current (new) version of a repository cfg, or raise a 
   78            RuntimeError in the case that older versions of serialized RepositoryCfgs can not be adapted. 
   79         There is an example of migrating from a fictitious v0 to v1 in tests/repositoryCfg.py 
   81         d = loader.construct_mapping(node)
 
   82         cfg = 
RepositoryCfg(root=d[
'_root'], mapper=d[
'_mapper'], mapperArgs=d[
'_mapperArgs'],
 
   83                             parents=[], policy=d.get(
'_policy', 
None))
 
   87         cfg._parents = d[
'_parents']
 
   88         if cfg._parents 
is None:
 
   96         return self.
root == other.root 
and \
 
  103         return not self.
__eq__(other)
 
  106         """Extend this RepositoryCfg with extendable values from the other RepositoryCfg. 
  108         Currently the only extendable value is parents; see `extendParents` for more detials about extending 
  113         other : RepositoryCfg 
  114             A RepositoryCfg instance to update values from. 
  119             If non-extendable parameters do not match a RuntimeError will be raised. 
  120             (If this RepositoryCfg's parents can not be extended with the parents of the other repository, 
  121             extendParents will raise). 
  123         if (self.
root != other.root
 
  124                 or self.
mapper != other.mapper
 
  126                 or self.
policy != other.policy):
 
  127             raise RuntimeError(
"{} can not be extended with cfg:{}".
format(self, other))
 
  130     def _extendsParents(self, newParents):
 
  131         """Query if a list of parents starts with the same list of parents as this RepositoryCfg's parents, 
  132         with new parents at the end. 
  136         newParents : list of string and/or RepositoryCfg 
  137             A list of parents that contains all the parents that would be in this RepositoryCfg. 
  138             This must include parents that may already be in this RepositoryCfg's parents list. Paths must be 
  139             in absolute form (not relative). 
  144             True if the beginning of the new list matches this RepositoryCfg's parents list, False if not. 
  146         doesExtendParents = 
False 
  147         return doesExtendParents
 
  150         """Determine if a parents list matches our parents list, with extra items at the end. If a list of 
  151         parents does not match but the mismatch is because of new parents at the end of the list, then they 
  152         can be added to the cfg. 
  156         newParents : list of string 
  157             A list of parents that contains all the parents that are to be recorded into this RepositoryCfg. 
  158             This must include parents that may already be in this RepositoryCfg's parents list 
  168             if all(x == y 
for (x, y) 
in zip(self.
_parents, newParents)):
 
  169                 if len(self.
_parents) < len(newParents):
 
  172                 elif len(self.
_parents) == len(newParents):
 
  179             raise ParentsMismatch((
"The beginning of the passed-in parents list: {} does not match the " 
  180                                   "existing parents list in this RepositoryCfg: {}").
format(
 
  189         if root 
is not None and self.
_root is not None:
 
  190             raise RuntimeError(
"Explicity clear root (set to None) before changing the value of root.")
 
  200             raise RuntimeError(
"Should not set mapper over previous not-None value.")
 
  218     def _normalizeParents(root, newParents):
 
  219         """Eliminate symlinks in newParents and get the relative path (if one exists) from root to each parent 
  224         newParents : list containing strings and RepoistoryCfg instances 
  225             Same as in `addParents`. 
  229         list of strings and RepositoryCfg instances. 
  230             Normalized list of parents 
  232         newParents = 
iterify(newParents)
 
  233         for i 
in range(len(newParents)):
 
  234             if isinstance(newParents[i], RepositoryCfg):
 
  235                 newParents[i] = copy.copy(newParents[i])
 
  236                 parentRoot = newParents[i].root
 
  237                 newParents[i].root = 
None 
  238                 newParents[i].root = Storage.relativePath(root, parentRoot)
 
  240                 newParents[i] = Storage.relativePath(root, newParents[i])
 
  244     def _denormalizeParents(root, parents):
 
  245         def getAbs(root, parent):
 
  246             if isinstance(parent, RepositoryCfg):
 
  247                 parentRoot = parent.root
 
  249                 parent.root = Storage.absolutePath(root, parentRoot)
 
  251                 parent = Storage.absolutePath(root, parent)
 
  253         return [getAbs(root, parent) 
for parent 
in parents]
 
  256         """Add a parent or list of parents to this RepositoryCfg 
  260         newParents : string or RepositoryCfg instance, or list of these. 
  261             If string, newParents should be a path or URI to the parent 
  262             repository. If RepositoryCfg, newParents should be a RepositoryCfg 
  263             that describes the parent repository in part or whole. 
  266         for newParent 
in newParents:
 
  278                             mapper=repositoryArgs.mapper,
 
  279                             mapperArgs=repositoryArgs.mapperArgs,
 
  281                             policy=repositoryArgs.policy)
 
  285         """Checks that a repositoryArgs instance will work with this repositoryCfg. This is useful 
  286         when loading an already-existing repository that has a persisted cfg, to ensure that the args that are 
  287         passed into butler do not conflict with the persisted cfg.""" 
  288         if repositoryArgs.root 
is not None and self.
_root != repositoryArgs.root:
 
  291         repoArgsMapper = repositoryArgs.mapper
 
  293         if isinstance(repoArgsMapper, str):
 
  294             repoArgsMapper = 
doImport(repoArgsMapper)
 
  295         if isinstance(cfgMapper, str):
 
  297         if repoArgsMapper 
is not None and repoArgsMapper != cfgMapper:
 
  300         if self.
_mapperArgs is not None and repositoryArgs.mapperArgs 
is not None:
 
  302                 if self.
_mapperArgs[key] != repositoryArgs.mapperArgs[key]:
 
  304         if repositoryArgs.policy 
and repositoryArgs.policy != self.
_policy:
 
  310         return "%s(root=%r, mapper=%r, mapperArgs=%r, parents=%s, policy=%s)" % (
 
  311             self.__class__.__name__,
 
  319 loaderList = [yaml.Loader, ]
 
  321     loaderList.append(yaml.FullLoader)
 
  322 except AttributeError:
 
  325     loaderList.append(yaml.UnsafeLoader)
 
  326 except AttributeError:
 
  329 for loader 
in loaderList:
 
  330     yaml.add_constructor(
u"!RepositoryCfg_v1", RepositoryCfg.v1Constructor, Loader=loader)