Coverage for python/lsst/daf/butler/registry/attributes.py : 100%

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/>.
21from __future__ import annotations
22"""The default concrete implementation of the class that manages
23attributes for `Registry`.
24"""
26__all__ = ["DefaultButlerAttributeManager"]
28from typing import (
29 ClassVar,
30 Iterable,
31 Optional,
32 Tuple,
33)
35import sqlalchemy
37from ..core.ddl import TableSpec, FieldSpec
38from .interfaces import (
39 Database,
40 ButlerAttributeExistsError,
41 ButlerAttributeManager,
42 StaticTablesContext,
43 VersionTuple
44)
47# This manager is supposed to have super-stable schema that never changes
48# but there may be cases when we need data migration on this table so we
49# keep version for it as well.
50_VERSION = VersionTuple(0, 1, 0)
53class DefaultButlerAttributeManager(ButlerAttributeManager):
54 """An implementation of `ButlerAttributeManager` that stores attributes
55 in a database table.
57 Parameters
58 ----------
59 db : `Database`
60 Database engine interface for the namespace in which this table lives.
61 table : `sqlalchemy.schema.Table`
62 SQLAlchemy representation of the table that stores attributes.
63 """
64 def __init__(self, db: Database, table: sqlalchemy.schema.Table):
65 self._db = db
66 self._table = table
68 _TABLE_NAME: ClassVar[str] = "butler_attributes"
70 _TABLE_SPEC: ClassVar[TableSpec] = TableSpec(
71 fields=[
72 FieldSpec("name", dtype=sqlalchemy.String, length=1024, primaryKey=True),
73 FieldSpec("value", dtype=sqlalchemy.String, length=65535, nullable=False),
74 ],
75 )
77 @classmethod
78 def initialize(cls, db: Database, context: StaticTablesContext) -> ButlerAttributeManager:
79 # Docstring inherited from ButlerAttributeManager.
80 table = context.addTable(cls._TABLE_NAME, cls._TABLE_SPEC)
81 return cls(db=db, table=table)
83 def get(self, name: str, default: Optional[str] = None) -> Optional[str]:
84 # Docstring inherited from ButlerAttributeManager.
85 sql = sqlalchemy.sql.select([self._table.columns.value]).where(
86 self._table.columns.name == name
87 )
88 row = self._db.query(sql).fetchone()
89 if row is not None:
90 return row[0]
91 return default
93 def set(self, name: str, value: str, *, force: bool = False) -> None:
94 # Docstring inherited from ButlerAttributeManager.
95 if not name or not value:
96 raise ValueError("name and value cannot be empty")
97 if force:
98 self._db.replace(self._table, {
99 "name": name,
100 "value": value,
101 })
102 else:
103 try:
104 self._db.insert(self._table, {
105 "name": name,
106 "value": value,
107 })
108 except sqlalchemy.exc.IntegrityError as exc:
109 raise ButlerAttributeExistsError(f"attribute {name} already exists") from exc
111 def delete(self, name: str) -> bool:
112 # Docstring inherited from ButlerAttributeManager.
113 numRows = self._db.delete(self._table, ["name"], {"name": name})
114 return numRows > 0
116 def items(self) -> Iterable[Tuple[str, str]]:
117 # Docstring inherited from ButlerAttributeManager.
118 sql = sqlalchemy.sql.select([
119 self._table.columns.name,
120 self._table.columns.value,
121 ])
122 for row in self._db.query(sql):
123 yield row[0], row[1]
125 def empty(self) -> bool:
126 # Docstring inherited from ButlerAttributeManager.
127 sql = sqlalchemy.sql.select([sqlalchemy.sql.func.count()]).select_from(self._table)
128 row = self._db.query(sql).fetchone()
129 return row[0] == 0
131 @classmethod
132 def currentVersion(cls) -> Optional[VersionTuple]:
133 # Docstring inherited from VersionedExtension.
134 return _VERSION
136 def schemaDigest(self) -> Optional[str]:
137 # Docstring inherited from VersionedExtension.
138 return self._defaultSchemaDigest([self._table], self._db.dialect)