Coverage for python/lsst/daf/butler/core/constraints.py : 23%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22"""Code relating to constraints based on `DatasetRef`, `DatasetType`, or
23`StorageClass`."""
25__all__ = ("Constraints", "ConstraintsValidationError", "ConstraintsConfig")
27import logging
28from .config import Config
29from .configSupport import LookupKey, processLookupConfigList
30from .exceptions import ValidationError
32log = logging.getLogger(__name__)
35class ConstraintsValidationError(ValidationError):
36 """Exception thrown when a constraints list has mutually exclusive
37 definitions."""
38 pass
41class ConstraintsConfig(Config):
42 """Configuration information for `Constraints`"""
43 pass
46class Constraints:
47 """Determine whether a `DatasetRef`, `DatasetType`, or `StorageClass`
48 is allowed to be handled.
50 Parameters
51 ----------
52 config : `ConstraintsConfig` or `str`
53 Load configuration. If `None` then this is equivalent to having
54 no restrictions.
55 universe : `DimensionUniverse`
56 The set of all known dimensions, used to normalize any lookup keys
57 involving dimensions.
58 """
60 matchAllKey = LookupKey("all")
61 """Configuration key associated with matching everything."""
63 def __init__(self, config, *, universe):
64 # Default is to accept all and reject nothing
65 self._accept = set()
66 self._reject = set()
68 if config is not None:
69 self.config = ConstraintsConfig(config)
71 if "accept" in self.config:
72 self._accept = processLookupConfigList(self.config["accept"], universe=universe)
73 if "reject" in self.config:
74 self._reject = processLookupConfigList(self.config["reject"], universe=universe)
76 if self.matchAllKey in self._accept and self.matchAllKey in self._reject:
77 raise ConstraintsValidationError("Can not explicitly accept 'all' and reject 'all'"
78 " in one configuration")
80 def __str__(self):
81 # Standard stringification
82 if not self._accept and not self._reject:
83 return "Accepts: all"
85 accepts = ", ".join(str(k) for k in self._accept)
86 rejects = ", ".join(str(k) for k in self._reject)
87 return f"Accepts: {accepts}; Rejects: {rejects}"
89 def isAcceptable(self, entity):
90 """Check whether the supplied entity will be acceptable to whatever
91 this `Constraints` class is associated with.
93 Parameters
94 ----------
95 entity : `DatasetType`, `DatasetRef`, or `StorageClass`
96 Instance to use to look in constraints table.
97 The entity itself reports the `LookupKey` that is relevant.
99 Returns
100 -------
101 allowed : `bool`
102 `True` if the entity is allowed.
103 """
104 # Get the names to use for lookup
105 names = set(entity._lookupNames())
107 # Test if this entity is explicitly mentioned for accept/reject
108 isExplicitlyAccepted = bool(names & self._accept)
110 if isExplicitlyAccepted:
111 return True
113 isExplicitlyRejected = bool(names & self._reject)
115 if isExplicitlyRejected:
116 return False
118 # Now look for wildcard match -- we have to also check for dataId
119 # overrides
121 # Generate a new set of lookup keys that use the wildcard name
122 # but the supplied dimensions
123 wildcards = {k.clone(name=self.matchAllKey.name) for k in names}
125 isWildcardAccepted = bool(wildcards & self._accept)
126 isWildcardRejected = bool(wildcards & self._reject)
128 if isWildcardRejected:
129 return False
131 # If all the wildcard and explicit rejections have failed then
132 # if the accept list is empty, or if a wildcard acceptance worked
133 # we can accept, else reject
134 if isWildcardAccepted or not self._accept:
135 return True
137 return False
139 def getLookupKeys(self):
140 """Retrieve the look up keys for all the constraints entries.
142 Returns
143 -------
144 keys : `set` of `LookupKey`
145 The keys available for determining constraints. Does not include
146 the special "all" lookup key.
147 """
148 all = self._accept | self._accept
149 return set(a for a in all if a.name != self.matchAllKey.name)