18
18
19
19
from contextlib import contextmanager
20
20
21
- import os
22
21
import fitsio
23
22
import galsim
24
23
import numpy as np
25
24
26
25
27
- class Reader :
28
- """An interface for reading that abstracts the serialization format .
26
+ class FitsReader :
27
+ """A reader object that reads from to multiple FITS HDUs .
29
28
30
- The `open` static method should be used to obtain the right reader for
31
- a particular file based on its extension, but specific subclasses can be
32
- constructed directly as well.
33
-
34
- A `Reader` subclass is always paired with a `Writer` subclass, and the
35
- methods of `Reader` each directly correspond to a method of `Writer`.
36
-
37
- All `Reader` and `Writer` methods take a ``name`` argument that is
38
- associated with the low-level data structured being saved. When writing,
39
- an object can chose not to write a subobject at all with a given name, and
40
- then check to see if the corresponding `Reader` method returns `None`, but
41
- a `Reader` cannot be used to see what type of data structure was saved with
42
- a given name; if this can change, the write implementation must store this
43
- explicitly and use it when invoking the `Reader`.
44
- """
45
-
46
- @staticmethod
47
- @contextmanager
48
- def open (file_name ):
49
- """Return a context manager that yields a `Reader` appropriate for the
50
- given filename.
51
-
52
- :param filename: Name of the file to open (`str`).
53
-
54
- :returns: A context manager that yields a `Reader`.
55
- """
56
- _ , ext = os .path .splitext (file_name )
57
- if ext == ".fits" or ext == ".piff" :
58
- with FitsReader ._open (file_name ) as reader :
59
- yield reader
60
- return
61
- else :
62
- raise NotImplementedError (f"No reader for extension { ext !r} ." )
63
-
64
- def read_struct (self , name ):
65
- """Load a simple flat dictionary.
66
-
67
- :param name: Name used to save this struct in `Writer.write_struct`.
68
-
69
- :returns: A `dict` with `str` keys and `int`, `float`, `str`, `bool`,
70
- or `None` values. If nothing was stored with the given
71
- name, `None` is returned.
72
- """
73
- raise NotImplementedError ()
74
-
75
- def read_table (self , name , metadata = None ):
76
- """Load a table as a numpy array.
77
-
78
- :param name: Name used to save this table in `Writer.write_table`.
79
- :param metadata: If not `None`, a `dict` to be filled with any
80
- metadata associated with the table on write. Key
81
- case may not be preserved!
82
-
83
- :returns: A numpy array with a structured dtype, or `None` if no
84
- object with this name was saved.
85
- """
86
- raise NotImplementedError ()
87
-
88
- def read_array (self , name , metadata = None ):
89
- """Load a regular a numpy array that does not have a structured dtype.
90
-
91
- :param name: Name used to save this array in `Writer.write_array`.
92
- :param metadata: If not `None`, a `dict` to be filled with any
93
- metadata associated with the array on write. Key
94
- case may not be preserved!
95
-
96
- :returns: A numpy array, or `None` if no object with this name was saved.
97
- """
98
- raise NotImplementedError ()
99
-
100
- def read_wcs_map (self , name , logger ):
101
- """Load a map of WCS objects and an optional pointing coord.
102
-
103
- :param name: Name used to save this map in `Writer.write_array`.
104
- :param logger: A logger object for logging debug info.
105
-
106
- :returns: A 2-element tuple, where the first entry is a `dict` with
107
- `int` (chipnum) keys and `galsim.BaseWCS` values, and the
108
- second entry is a `galsim.CelestialCoord` object. Both
109
- entries may be `None`, and if the WCS `dict` is `None` the
110
- pointing coord is always `None`.
111
- """
112
- raise NotImplementedError ()
113
-
114
- def nested (self , name ):
115
- """Return a context manager that yields a new `Reader` that reads
116
- content written by a similarly nested `Writer`.
117
-
118
- :param name: Base name for all objects read with the returned object.
119
-
120
- :returns: A context manager that yields a nested reader object.
121
- """
122
- raise NotImplementedError ()
123
-
124
- def get_full_name (self , name ):
125
- """Return the full name of a data structure saved with the given name,
126
- combining it with any base names added if this `Reader` was created by
127
- the `nested` method.
128
- """
129
- raise NotImplementedError ()
130
-
131
-
132
- class FitsReader (Reader ):
133
- """A `Reader` implementation that reads from to multiple FITS HDUs.
134
-
135
- This reader is intended to read files written by Piff before the `Reader`
136
- and `Writer` abstractions were added.
137
-
138
- `FitsReader.nested` yields a reader that reads from the same file and
139
- prepends all names with its base name to form the EXTNAME, concatenating
140
- them with ``_``.
29
+ This reader is intended to read files written by `FitsWriter`.
141
30
142
31
:param fits: An already-open `fitsio.FITS` object.
143
32
:param base_name: Base name to prepend to all object names, or `None`.
@@ -148,7 +37,7 @@ def __init__(self, fits, base_name):
148
37
149
38
@classmethod
150
39
@contextmanager
151
- def _open (cls , file_name ):
40
+ def open (cls , file_name ):
152
41
"""Return a context manager that opens the given FITS file and yields
153
42
a `FitsReader` object.
154
43
@@ -160,7 +49,14 @@ def _open(cls, file_name):
160
49
yield cls (f , base_name = None )
161
50
162
51
def read_struct (self , name ):
163
- # Docstring inherited.
52
+ """Load a simple flat dictionary.
53
+
54
+ :param name: Name used to save this struct in `FitsWriter.write_struct`.
55
+
56
+ :returns: A `dict` with `str` keys and `int`, `float`, `str`, `bool`,
57
+ or `None` values. If nothing was stored with the given
58
+ name, `None` is returned.
59
+ """
164
60
extname = self .get_full_name (name )
165
61
if extname not in self ._fits :
166
62
return None
@@ -185,11 +81,28 @@ def read_struct(self, name):
185
81
return struct
186
82
187
83
def read_table (self , name , metadata = None ):
188
- # Docstring inherited.
84
+ """Load a table as a numpy array.
85
+
86
+ :param name: Name used to save this table in `FitsWriter.write_table`.
87
+ :param metadata: If not `None`, a `dict` to be filled with any
88
+ metadata associated with the table on write. Key
89
+ case may not be preserved!
90
+
91
+ :returns: A numpy array with a structured dtype, or `None` if no
92
+ object with this name was saved.
93
+ """
189
94
return self .read_array (name , metadata )
190
95
191
96
def read_array (self , name , metadata = None ):
192
- # Docstring inherited.
97
+ """Load a regular a numpy array that does not have a structured dtype.
98
+
99
+ :param name: Name used to save this array in `FitsWriter.write_array`.
100
+ :param metadata: If not `None`, a `dict` to be filled with any
101
+ metadata associated with the array on write. Key
102
+ case may not be preserved!
103
+
104
+ :returns: A numpy array, or `None` if no object with this name was saved.
105
+ """
193
106
extname = self .get_full_name (name )
194
107
if extname not in self ._fits :
195
108
return None
@@ -198,7 +111,17 @@ def read_array(self, name, metadata=None):
198
111
return self ._fits [extname ].read ()
199
112
200
113
def read_wcs_map (self , name , logger ):
201
- # Docstring inherited.
114
+ """Load a map of WCS objects and an optional pointing coord.
115
+
116
+ :param name: Name used to save this map in `FitsWriter.write_wcs_map`.
117
+ :param logger: A logger object for logging debug info.
118
+
119
+ :returns: A 2-element tuple, where the first entry is a `dict` with
120
+ `int` (chipnum) keys and `galsim.BaseWCS` values, and the
121
+ second entry is a `galsim.CelestialCoord` object. Both
122
+ entries may be `None`, and if the WCS `dict` is `None` the
123
+ pointing coord is always `None`.
124
+ """
202
125
import base64
203
126
import pickle
204
127
@@ -262,9 +185,21 @@ def read_wcs_map(self, name, logger):
262
185
263
186
@contextmanager
264
187
def nested (self , name ):
265
- # Docstring inherited.
188
+ """Return a context manager that yields a new `FitsReader` that reads
189
+ content written by a similarly nested `FitsWriter`.
190
+
191
+ :param name: Base name for all objects read with the returned object.
192
+
193
+ :returns: A context manager that yields a nested reader object.
194
+ """
266
195
yield FitsReader (self ._fits , base_name = self .get_full_name (name ))
267
196
268
197
def get_full_name (self , name : str ) -> str :
269
- # Docstring inherited.
198
+ """Return the full name of a data structure saved with the given name,
199
+ combining it with any base names added if this `FitsReader` was created
200
+ by the `nested` method.
201
+
202
+ The FITS implementation concatenates with ``_`` as a delimiter, and
203
+ uses the full name as the EXTNAME header value.
204
+ """
270
205
return name if self ._base_name is None else f"{ self ._base_name } _{ name } "
0 commit comments