Coverage for python / lsst / dax / apdb / config.py: 35%
55 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 08:49 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-22 08:49 +0000
1# This file is part of dax_apdb
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://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 <https://www.gnu.org/licenses/>.
22from __future__ import annotations
24__all__ = ["ApdbConfig"]
26import warnings
27from collections.abc import Mapping
28from typing import Any, ClassVar, cast
30import yaml
31from pydantic import BaseModel, Field
33from lsst.resources import ResourcePath, ResourcePathExpression
35from .apdbIndex import ApdbIndex
36from .factory import config_type_for_name
39class ApdbConfig(BaseModel):
40 """Base class for APDB configuration types.
42 This class contains a set of parameters that are common to all
43 implementations. Implementation-specific parameters are declared in
44 sub-classes.
45 """
47 _implementation_type: ClassVar[str]
49 schema_file: str = Field(
50 default="resource://lsst.sdm.schemas/apdb.yaml",
51 description="Location of (YAML) configuration file with standard APDB schema.",
52 )
54 ss_schema_file: str = Field(
55 default="resource://lsst.sdm.schemas/sso.yaml",
56 description=(
57 "Location of (YAML) configuration file with SSO schema. "
58 "This file is only loaded if SSObject/SSSource tables are not present in APDB schema file. "
59 "Can be set to empty string to avoid loading even if tables are not in APDB schema."
60 ),
61 )
63 schema_name: str = Field(
64 default="ApdbSchema",
65 description="Name of the schema in YAML configuration file (not used and deprecated).",
66 )
68 read_sources_months: int = Field(
69 default=12,
70 description="Number of months of history to read from DiaSource.",
71 )
73 read_forced_sources_months: int = Field(
74 default=12,
75 description="Number of months of history to read from DiaForcedSource",
76 )
78 enable_replica: bool = Field(
79 default=False,
80 description="If True, make and fill additional tables used for replication.",
81 )
83 replica_chunk_seconds: int = Field(
84 default=600,
85 description=(
86 "Time extent for replica chunks, new chunks are created every specified number of seconds."
87 ),
88 )
90 @classmethod
91 def from_uri(cls, uri: ResourcePathExpression) -> ApdbConfig:
92 """Load configuration object from external file.
94 Parameters
95 ----------
96 uri : `~lsst.resources.ResourcePathExpression`
97 Location of the file containing serialized configuration in YAML
98 format.
100 Returns
101 -------
102 config : `ApdbConfig`
103 Apdb configuration object.
104 """
105 if isinstance(uri, str) and uri.startswith("label:"):
106 tag, _, label = uri.partition(":")
107 index = ApdbIndex()
108 # Try to find YAML format first, and pex_config if YAML is not
109 # found. During transitional period we support conversion of
110 # pex_config format to a new pydantic format.
111 try:
112 uri = index.get_apdb_uri(label, "yaml")
113 except ValueError as yaml_exc:
114 try:
115 uri = index.get_apdb_uri(label, "pex_config")
116 except ValueError:
117 # If none is found then re-raise exception from yaml
118 # attempt, but add a note that pex_config is missing too.
119 yaml_exc.add_note(f"Legacy label {label}/pex_config is also missing.")
120 raise yaml_exc from None
122 path = ResourcePath(uri)
123 config_bytes = path.read()
125 # During transitional period we support loading of configurations from
126 # both pex_config and pydantic/YAML formats. We have to look at the
127 # contents for figure out which is which.
128 if config_bytes.startswith(b"import lsst.dax.apdb"):
129 from . import legacy_config
131 pex_config = legacy_config.ApdbConfig.legacy_load(config_bytes)
132 new_config = pex_config.to_model()
133 warnings.warn(
134 (
135 f"APDB is instantiated using legacy pex_config format from file {path}. "
136 "Support for pex_config format will be removed after v29. Please update "
137 "configuration to new YAML format with `apdb-cli convert-legacy-config` command."
138 ),
139 category=FutureWarning,
140 stacklevel=2,
141 )
142 return new_config
144 config_object = yaml.full_load(config_bytes)
145 if not isinstance(config_object, Mapping):
146 raise TypeError("YAML configuration file does not represent valid object")
147 config_dict: dict[str, Any] = dict(config_object)
148 type_name = config_dict.pop("implementation_type", None)
149 if not type_name:
150 raise LookupError("YAML configuration file does not have `implementation_type` key")
151 klass = config_type_for_name(cast(str, type_name))
152 return klass.model_validate(config_dict)
154 def save(self, uri: ResourcePathExpression) -> None:
155 """Save configuration to a specified location in YAML format.
157 Parameters
158 ----------
159 uri : `ResourcePathExpression`
160 Location to save configuration
161 """
162 config_dict = self.model_dump(exclude_unset=True, exclude_defaults=True)
163 config_dict["implementation_type"] = self._implementation_type
164 config_yaml = yaml.dump(config_dict)
166 path = ResourcePath(uri)
167 path.write(config_yaml.encode())