Coverage for python/lsst/daf/butler/json.py: 55%

26 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-05 02:53 -0700

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/>. 

27 

28from __future__ import annotations 

29 

30__all__ = ("to_json_generic", "from_json_generic", "to_json_pydantic", "from_json_pydantic") 

31 

32import json 

33from typing import TYPE_CHECKING, Any, ClassVar, Protocol 

34 

35from pydantic import BaseModel 

36 

37if TYPE_CHECKING: 

38 from .dimensions import DimensionUniverse 

39 from .registry import Registry 

40 

41 

42class SupportsSimple(Protocol): 

43 """Protocol defining the methods required to support the standard 

44 serialization using "simple" methods names. 

45 """ 

46 

47 _serializedType: ClassVar[type[BaseModel]] 

48 

49 def to_simple(self, minimal: bool) -> Any: ... 49 ↛ exitline 49 didn't jump to line 49, because

50 

51 @classmethod 

52 def from_simple( 52 ↛ exitline 52 didn't jump to the function exit

53 cls, simple: Any, universe: DimensionUniverse | None = None, registry: Registry | None = None 

54 ) -> SupportsSimple: ... 

55 

56 

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. 

60 

61 Parameters 

62 ---------- 

63 minimal : `bool` 

64 Return minimal possible representation. 

65 """ 

66 return self.to_simple(minimal=minimal).model_dump_json(exclude_defaults=True, exclude_unset=True) 

67 

68 

69def from_json_pydantic( 

70 cls_: type[SupportsSimple], 

71 json_str: str, 

72 universe: DimensionUniverse | None = None, 

73 registry: Registry | None = None, 

74) -> SupportsSimple: 

75 """Convert from JSON to a pydantic model. 

76 

77 Parameters 

78 ---------- 

79 cls_ : `type` of `SupportsSimple` 

80 The Python type being created. 

81 json_str : `str` 

82 The JSON string representing this object. 

83 universe : `DimensionUniverse` or `None`, optional 

84 The universe required to instantiate some models. Required if 

85 ``registry`` is `None`. 

86 registry : `Registry` or `None`, optional 

87 Registry from which to obtain the dimension universe if an explicit 

88 universe has not been given. 

89 

90 Returns 

91 ------- 

92 model : `SupportsSimple` 

93 Pydantic model constructed from JSON and validated. 

94 """ 

95 simple = cls_._serializedType.model_validate_json(json_str) 

96 try: 

97 return cls_.from_simple(simple, universe=universe, registry=registry) 

98 except AttributeError as e: 

99 raise AttributeError(f"JSON deserialization requires {cls_} has a from_simple() class method") from e 

100 

101 

102def to_json_generic(self: SupportsSimple, minimal: bool = False) -> str: 

103 """Convert this class to JSON form. 

104 

105 The class type is not recorded in the JSON so the JSON decoder 

106 must know which class is represented. 

107 

108 Parameters 

109 ---------- 

110 minimal : `bool`, optional 

111 Use minimal serialization. Requires Registry to convert 

112 back to a full type. 

113 

114 Returns 

115 ------- 

116 json : `str` 

117 The class in JSON string format. 

118 """ 

119 # For now use the core json library to convert a dict to JSON 

120 # for us. 

121 return json.dumps(self.to_simple(minimal=minimal)) 

122 

123 

124def from_json_generic( 

125 cls: type[SupportsSimple], 

126 json_str: str, 

127 universe: DimensionUniverse | None = None, 

128 registry: Registry | None = None, 

129) -> SupportsSimple: 

130 """Return new class from JSON string. 

131 

132 Converts a JSON string created by `to_json` and return 

133 something of the supplied class. 

134 

135 Parameters 

136 ---------- 

137 json_str : `str` 

138 Representation of the dimensions in JSON format as created 

139 by `to_json()`. 

140 universe : `DimensionUniverse`, optional 

141 The special graph of all known dimensions. Passed directly 

142 to `from_simple()`. 

143 registry : `lsst.daf.butler.Registry`, optional 

144 Registry to use to convert simple name of a DatasetType to 

145 a full `DatasetType`. Passed directly to `from_simple()`. 

146 

147 Returns 

148 ------- 

149 constructed : Any 

150 Newly-constructed object. 

151 """ 

152 simple = json.loads(json_str) 

153 try: 

154 return cls.from_simple(simple, universe=universe, registry=registry) 

155 except AttributeError as e: 

156 raise AttributeError(f"JSON deserialization requires {cls} has a from_simple() class method") from e