Coverage for python/lsst/daf/butler/json.py: 67%
26 statements
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-07 09:50 +0000
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-07 09:50 +0000
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 software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28from __future__ import annotations
30__all__ = ("from_json_generic", "from_json_pydantic", "to_json_generic", "to_json_pydantic")
32import json
33from typing import TYPE_CHECKING, Any, ClassVar, Protocol
35from pydantic import BaseModel
37if TYPE_CHECKING:
38 from .dimensions import DimensionUniverse
39 from .registry import Registry
42class SupportsSimple(Protocol):
43 """Protocol defining the methods required to support the standard
44 serialization using "simple" methods names.
45 """
47 _serializedType: ClassVar[type[BaseModel]]
49 def to_simple(self, minimal: bool) -> Any: ... 49 ↛ exitline 49 didn't return from function 'to_simple' because
51 @classmethod
52 def from_simple( 52 ↛ exitline 52 didn't return from function 'from_simple' because
53 cls, simple: Any, universe: DimensionUniverse | None = None, registry: Registry | None = None
54 ) -> SupportsSimple: ...
57def to_json_pydantic(self: SupportsSimple, minimal: bool = False) -> str:
58 """Convert this class to JSON assuming that the ``to_simple()`` returns
59 a pydantic model.
61 Parameters
62 ----------
63 self : `SupportsSimple`
64 The object being serialized.
65 minimal : `bool`
66 Return minimal possible representation.
67 """
68 return self.to_simple(minimal=minimal).model_dump_json(exclude_defaults=True, exclude_unset=True)
71def from_json_pydantic(
72 cls_: type[SupportsSimple],
73 json_str: str | bytes | bytearray,
74 universe: DimensionUniverse | None = None,
75 registry: Registry | None = None,
76) -> SupportsSimple:
77 """Convert from JSON to a pydantic model.
79 Parameters
80 ----------
81 cls_ : `type` of `SupportsSimple`
82 The Python type being created.
83 json_str : `str` or `bytes` or `bytearray`
84 The JSON string representing this object.
85 universe : `DimensionUniverse` or `None`, optional
86 The universe required to instantiate some models. Required if
87 ``registry`` is `None`.
88 registry : `Registry` or `None`, optional
89 Registry from which to obtain the dimension universe if an explicit
90 universe has not been given.
92 Returns
93 -------
94 model : `SupportsSimple`
95 Pydantic model constructed from JSON and validated.
96 """
97 simple = cls_._serializedType.model_validate_json(json_str)
98 try:
99 return cls_.from_simple(simple, universe=universe, registry=registry)
100 except AttributeError as e:
101 raise AttributeError(f"JSON deserialization requires {cls_} has a from_simple() class method") from e
104def to_json_generic(self: SupportsSimple, minimal: bool = False) -> str:
105 """Convert this class to JSON form.
107 The class type is not recorded in the JSON so the JSON decoder
108 must know which class is represented.
110 Parameters
111 ----------
112 self : `SupportsSimple`
113 The object being serialized.
114 minimal : `bool`, optional
115 Use minimal serialization. Requires Registry to convert
116 back to a full type.
118 Returns
119 -------
120 json : `str`
121 The class in JSON string format.
122 """
123 # For now use the core json library to convert a dict to JSON
124 # for us.
125 return json.dumps(self.to_simple(minimal=minimal))
128def from_json_generic(
129 cls: type[SupportsSimple],
130 json_str: str,
131 universe: DimensionUniverse | None = None,
132 registry: Registry | None = None,
133) -> SupportsSimple:
134 """Return new class from JSON string.
136 Converts a JSON string created by ``to_json`` and return
137 something of the supplied class.
139 Parameters
140 ----------
141 cls : `type` of `SupportsSimple`
142 The Python type being created.
143 json_str : `str`
144 Representation of the dimensions in JSON format as created
145 by ``to_json()``.
146 universe : `DimensionUniverse`, optional
147 The special graph of all known dimensions. Passed directly
148 to ``from_simple``.
149 registry : `lsst.daf.butler.Registry`, optional
150 Registry to use to convert simple name of a DatasetType to
151 a full `DatasetType`. Passed directly to ``from_simple``.
153 Returns
154 -------
155 constructed : Any
156 Newly-constructed object.
157 """
158 simple = json.loads(json_str)
159 try:
160 return cls.from_simple(simple, universe=universe, registry=registry)
161 except AttributeError as e:
162 raise AttributeError(f"JSON deserialization requires {cls} has a from_simple() class method") from e