Coverage for python/lsst/daf/butler/registry/interfaces/_opaque.py: 66%
40 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-12 02:05 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2023-01-12 02:05 -0800
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/>.
21from __future__ import annotations
23"""Interfaces for the objects that manage opaque (logical) tables within a
24`Registry`.
25"""
27__all__ = ["OpaqueTableStorageManager", "OpaqueTableStorage"]
29from abc import ABC, abstractmethod
30from typing import TYPE_CHECKING, Any, Iterable, Iterator, Optional
32from ...core.ddl import TableSpec
33from ._database import Database, StaticTablesContext
34from ._versioning import VersionedExtension
36if TYPE_CHECKING: 36 ↛ 37line 36 didn't jump to line 37, because the condition on line 36 was never true
37 from ...core.datastore import DatastoreTransaction
40class OpaqueTableStorage(ABC):
41 """An interface that manages the records associated with a particular
42 opaque table in a `Registry`.
44 Parameters
45 ----------
46 name : `str`
47 Name of the opaque table.
48 """
50 def __init__(self, name: str):
51 self.name = name
53 @abstractmethod
54 def insert(self, *data: dict, transaction: DatastoreTransaction | None = None) -> None:
55 """Insert records into the table
57 Parameters
58 ----------
59 *data
60 Each additional positional argument is a dictionary that represents
61 a single row to be added.
62 transaction : `DatastoreTransaction`, optional
63 Transaction object that can be used to enable an explicit rollback
64 of the insert to be registered. Can be ignored if rollback is
65 handled via a different mechanism, such as by a database. Can be
66 `None` if no external transaction is available.
67 """
68 raise NotImplementedError()
70 @abstractmethod
71 def fetch(self, **where: Any) -> Iterator[dict]:
72 """Retrieve records from an opaque table.
74 Parameters
75 ----------
76 **where
77 Additional keyword arguments are interpreted as equality
78 constraints that restrict the returned rows (combined with AND);
79 keyword arguments are column names and values are the values they
80 must have.
82 Yields
83 ------
84 row : `dict`
85 A dictionary representing a single result row.
86 """
87 raise NotImplementedError()
89 @abstractmethod
90 def delete(self, columns: Iterable[str], *rows: dict) -> None:
91 """Remove records from an opaque table.
93 Parameters
94 ----------
95 columns: `~collections.abc.Iterable` of `str`
96 The names of columns that will be used to constrain the rows to
97 be deleted; these will be combined via ``AND`` to form the
98 ``WHERE`` clause of the delete query.
99 *rows
100 Positional arguments are the keys of rows to be deleted, as
101 dictionaries mapping column name to value. The keys in all
102 dictionaries must be exactly the names in ``columns``.
103 """
104 raise NotImplementedError()
106 name: str
107 """The name of the logical table this instance manages (`str`).
108 """
111class OpaqueTableStorageManager(VersionedExtension):
112 """An interface that manages the opaque tables in a `Registry`.
114 `OpaqueTableStorageManager` primarily serves as a container and factory for
115 `OpaqueTableStorage` instances, which each provide access to the records
116 for a different (logical) opaque table.
118 Notes
119 -----
120 Opaque tables are primarily used by `Datastore` instances to manage their
121 internal data in the same database that hold the `Registry`, but are not
122 limited to this.
124 While an opaque table in a multi-layer `Registry` may in fact be the union
125 of multiple tables in different layers, we expect this to be rare, as
126 `Registry` layers will typically correspond to different leaf `Datastore`
127 instances (each with their own opaque table) in a `ChainedDatastore`.
128 """
130 @classmethod
131 @abstractmethod
132 def initialize(cls, db: Database, context: StaticTablesContext) -> OpaqueTableStorageManager:
133 """Construct an instance of the manager.
135 Parameters
136 ----------
137 db : `Database`
138 Interface to the underlying database engine and namespace.
139 context : `StaticTablesContext`
140 Context object obtained from `Database.declareStaticTables`; used
141 to declare any tables that should always be present in a layer
142 implemented with this manager.
144 Returns
145 -------
146 manager : `OpaqueTableStorageManager`
147 An instance of a concrete `OpaqueTableStorageManager` subclass.
148 """
149 raise NotImplementedError()
151 def __getitem__(self, name: str) -> OpaqueTableStorage:
152 """Interface to `get` that raises `LookupError` instead of returning
153 `None` on failure.
154 """
155 r = self.get(name)
156 if r is None:
157 raise LookupError(f"No logical table with name '{name}' found.")
158 return r
160 @abstractmethod
161 def get(self, name: str) -> Optional[OpaqueTableStorage]:
162 """Return an object that provides access to the records associated with
163 an opaque logical table.
165 Parameters
166 ----------
167 name : `str`
168 Name of the logical table.
170 Returns
171 -------
172 records : `OpaqueTableStorage` or `None`
173 The object representing the records for the given table in this
174 layer, or `None` if there are no records for that table in this
175 layer.
177 Notes
178 -----
179 Opaque tables must be registered with the layer (see `register`) by
180 the same client before they can safely be retrieved with `get`.
181 Unlike most other manager classes, the set of opaque tables cannot be
182 obtained from an existing data repository.
183 """
184 raise NotImplementedError()
186 @abstractmethod
187 def register(self, name: str, spec: TableSpec) -> OpaqueTableStorage:
188 """Ensure that this layer can hold records for the given opaque logical
189 table, creating new tables as necessary.
191 Parameters
192 ----------
193 name : `str`
194 Name of the logical table.
195 spec : `TableSpec`
196 Schema specification for the table to be created.
198 Returns
199 -------
200 records : `OpaqueTableStorage`
201 The object representing the records for the given element in this
202 layer.
204 Notes
205 -----
206 This operation may not be invoked within a transaction context block.
207 """
208 raise NotImplementedError()