Coverage for python/lsst/daf/butler/formatters/json.py: 81%
33 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-13 10:56 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-13 10:56 +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__ = ("JsonFormatter",)
32import contextlib
33import dataclasses
34import json
35from typing import Any
37from .file import FileFormatter
40class JsonFormatter(FileFormatter):
41 """Formatter implementation for JSON files."""
43 extension = ".json"
45 unsupportedParameters = None
46 """This formatter does not support any parameters (`frozenset`)"""
48 def _readFile(self, path: str, pytype: type[Any] | None = None) -> Any:
49 """Read a file from the path in JSON format.
51 Parameters
52 ----------
53 path : `str`
54 Path to use to open JSON format file.
55 pytype : `class`, optional
56 Not used by this implementation.
58 Returns
59 -------
60 data : `object`
61 Data as Python object read from JSON file.
62 """
63 with open(path, "rb") as fd:
64 data = self._fromBytes(fd.read(), pytype)
66 return data
68 def _writeFile(self, inMemoryDataset: Any) -> None:
69 """Write the in memory dataset to file on disk.
71 Will look for `_asdict()` method to aid JSON serialization, following
72 the approach of the simplejson module.
74 Parameters
75 ----------
76 inMemoryDataset : `object`
77 Object to serialize.
79 Raises
80 ------
81 Exception
82 The file could not be written.
83 """
84 self.fileDescriptor.location.uri.write(self._toBytes(inMemoryDataset))
86 def _fromBytes(self, serializedDataset: bytes, pytype: type[Any] | None = None) -> Any:
87 """Read the bytes object as a python object.
89 Parameters
90 ----------
91 serializedDataset : `bytes`
92 Bytes object to unserialize.
93 pytype : `class`, optional
94 Not used by this implementation.
96 Returns
97 -------
98 inMemoryDataset : `object`
99 The requested data as a Python object or None if the string could
100 not be read.
101 """
102 try:
103 data = json.loads(serializedDataset)
104 except json.JSONDecodeError:
105 data = None
107 return data
109 def _toBytes(self, inMemoryDataset: Any) -> bytes:
110 """Write the in memory dataset to a bytestring.
112 Parameters
113 ----------
114 inMemoryDataset : `object`
115 Object to serialize
117 Returns
118 -------
119 serializedDataset : `bytes`
120 bytes representing the serialized dataset.
122 Raises
123 ------
124 Exception
125 The object could not be serialized.
126 """
127 # Try different standardized methods for native json.
128 # For example, Pydantic models have a .model_dump_json method.
129 # v1 models without compatibility layer will need .json()
130 with contextlib.suppress(AttributeError):
131 return inMemoryDataset.model_dump_json().encode()
132 with contextlib.suppress(AttributeError): 132 ↛ exitline 132 didn't return from function '_toBytes', because the return on line 133 wasn't executed
133 return inMemoryDataset.json().encode()
135 if dataclasses.is_dataclass(inMemoryDataset):
136 inMemoryDataset = dataclasses.asdict(inMemoryDataset)
137 elif hasattr(inMemoryDataset, "_asdict"):
138 inMemoryDataset = inMemoryDataset._asdict()
139 return json.dumps(inMemoryDataset, ensure_ascii=False).encode()