LSSTApplications  19.0.0-14-gb0260a2+1009f50558,20.0.0+089c43b26b,20.0.0+126303c00d,20.0.0+2f3d0e5c40,20.0.0+36ef800059,20.0.0+5efbb753f1,20.0.0+bebc1f60e8,20.0.0+c31f88ab0f,20.0.0+e2e26847c2,20.0.0+f8788d280b,20.0.0-1-g10df615+c10c626ef0,20.0.0-1-g253301a+36ef800059,20.0.0-1-g2b7511a+bebc1f60e8,20.0.0-1-g4d801e7+2364cbef1f,20.0.0-1-g5b95a8c+f41a630bc9,20.0.0-1-g660595b+f45b7d88f4,20.0.0-1-gc96f8cb+4c66cd7cf3,20.0.0-1-gd1c87d7+85c46248f3,20.0.0-1-gedffbd8+f975718b49,20.0.0-13-ge9dc5b3+55648be1db,20.0.0-15-ga86d0f6+bdc7c63f7c,20.0.0-15-gf5872d0+4c66cd7cf3,20.0.0-16-g6fe20a1+a77778c47d,20.0.0-2-g4dae9ad+4c66cd7cf3,20.0.0-2-g7818986+85c46248f3,20.0.0-2-gec03fae+ff10c6d78d,20.0.0-25-g38d2b7ae+bde7f0c6ec,20.0.0-3-g4cc78c6+63636aeed8,20.0.0-3-g6a8623c+55648be1db,20.0.0-3-g750bffe+823a89ab62,20.0.0-4-gfea843c+f45b7d88f4,20.0.0-47-g0830830+f5f993f8be,20.0.0-5-g357b56b+f45b7d88f4,20.0.0-5-gfcebe35+8726ad94da,20.0.0-7-gc818c6e6+aaebb51c78,20.0.0-7-gcda7bf1+2a82aa79eb,20.0.0-9-g61a2a9a3d+14f89e4eca,w.2020.38
LSSTDataManagementBasePackage
fitsChanContinued.py
Go to the documentation of this file.
1 __all__ = ["CardType", "FitsChan", "FitsKeyState"]
2 
3 from .fitsChan import CardType, FitsChan, FitsKeyState
4 
5 
6 def _calc_card_pos(self, index):
7  """Convert a python index into a FitsChan position.
8 
9  Parameters
10  ----------
11  self : `FitsChan`
12  The FitsChan to index.
13  index : `int`
14  0-based index into header. If negative, counts from end.
15 
16  Raises
17  ------
18  IndexError
19  Raised if the index exceeds the size of the FitsChan. If the index
20  equals the size of the FitsChan (noting that in 0-based indexing the
21  final card is one less than the size) then this refers to the end of
22  the header.
23  """
24  # Calculate 0-based index
25  nCards = len(self)
26  if index < 0:
27  index = nCards + index
28  elif abs(index) > nCards:
29  # We allow index of one higher to indicate append
30  raise IndexError(f"Index {index} exceeds size of FitsChan ({nCards})")
31 
32  # Convert to 1-based index
33  return index + 1
34 
35 
36 def _get_current_card_value(self):
37  """Retrieve the value of the current card along with the keyword name.
38 
39  Returns
40  -------
41  name : `str`
42  The name of the current card.
43  value : `object`
44  The value in the correct Python type.
45  """
46  # Method look up table for obtaining values
47  typeLut = {CardType.INT: self.getFitsI,
48  CardType.FLOAT: self.getFitsF,
49  CardType.STRING: self.getFitsS,
50  CardType.COMPLEXF: self.getFitsCF,
51  CardType.LOGICAL: self.getFitsL
52  }
53 
54  # Get the data type for this matching card
55  ctype = self.getCardType()
56 
57  # Get the name of the card
58  name = self.getCardName()
59 
60  # Go back one card so we can ask for the value in the correct
61  # data type (getFitsX starts from the next card)
62  # thiscard = self.getCard()
63  # self.setCard(thiscard - 1)
64 
65  if ctype == CardType.UNDEF:
66  value = None
67  elif ctype in typeLut:
68  found = typeLut[ctype]("") # "" indicates current card
69  if found.found:
70  value = found.value
71  else:
72  raise RuntimeError(f"Unexpectedly failed to find card '{name}'")
73  elif ctype == CardType.COMMENT:
74  value = self.getCardComm()
75  else:
76  raise RuntimeError(f"Type, {ctype} of FITS card '{name}' not supported")
77 
78  return name, value
79 
80 
81 def length(self):
82  return self.nCard
83 
84 
85 FitsChan.__len__ = length
86 
87 
88 def iter(self):
89  """The FitsChan is its own iterator, incrementing the card position on
90  each call.
91 
92  The position of the iterator is handled internally in the FitsChan and
93  is moved to the start of the FitsChan by this call.
94  Whilst iterating do not change the internal card position.
95 
96  The iterator will return 80-character header cards.
97  """
98  self.clearCard()
99  return self
100 
101 
102 FitsChan.__iter__ = iter
103 
104 
105 def next(self):
106  """Return each 80-character header card until we run out of cards.
107  """
108  card = self.findFits("%f", True)
109  if not card.found:
110  raise StopIteration
111  return card.value
112 
113 
114 FitsChan.__next__ = next
115 
116 
117 def to_string(self):
118  """A FitsChan string representation is a FITS header with newlines
119  after each 80-character header card.
120  """
121  return "\n".join(c for c in self)
122 
123 
124 FitsChan.__str__ = to_string
125 
126 
127 def contains(self, name):
128  """Returns True if either the supplied name is present in the FitsChan
129  or the supplied integer is acceptable to the FitsChan.
130  """
131  if isinstance(name, int):
132  # index will be zero-based
133  if name >= 0 and name < self.nCard:
134  return True
135  elif isinstance(name, str):
136  currentCard = self.getCard()
137  try:
138  self.clearCard()
139  result = self.findFits(name, False)
140  finally:
141  self.setCard(currentCard)
142  if result.found:
143  return True
144 
145  return False
146 
147 
148 FitsChan.__contains__ = contains
149 
150 
151 def getitem(self, name):
152  """Return a value associated with the supplied name.
153 
154  Parameters
155  ----------
156  name : `str` or `int`
157  If the FitsChan is being accessed by integer index the returned value
158  will be the corresponding 80-character card. Index values are 0-based.
159  A negative index counts from the end of the FitsChan.
160  If the FitsChan is being accessed by string the returned value will
161  be the scalar value associated with the first card that matches the
162  supplied name.
163 
164  Returns
165  -------
166  value : `str`, `int`, `float`, `bool`, or `None`
167  The complete 80-character header card if an integer index is supplied,
168  else the first matching value of the named header.
169 
170  Raises
171  ------
172  IndexError
173  Raised if an integer index is provided and the index is out of range.
174  KeyError
175  Raised if a string is provided and that string is not present in
176  the FitsChan. Also raised if the supplied name is neither an integer
177  not a string.
178  RuntimeError
179  Raised if there is some problem accessing the value in the FitsChan.
180  """
181 
182  # Save current card position
183  currentCard = self.getCard()
184 
185  if isinstance(name, int):
186  # Calculate position in FitsChan (0-based to 1-based)
187  newpos = _calc_card_pos(self, name)
188  self.setCard(newpos)
189  try:
190  result = self.findFits("%f", False)
191  finally:
192  self.setCard(currentCard)
193  if not result.found:
194  raise IndexError(f"No FITS card at index {name}")
195  return result.value
196 
197  elif isinstance(name, str):
198 
199  try:
200  # Rewind FitsChan so we search all cards
201  self.clearCard()
202 
203  # We are only interested in the first matching card
204  result = self.findFits(name, False)
205  if not result.found:
206  raise KeyError(f"{name}'")
207 
208  this_name, value = _get_current_card_value(self)
209  if this_name != name:
210  raise RuntimeError(f"Internal inconsistency in get: {this_name} != {name}")
211 
212  finally:
213  # Reinstate the original card position
214  self.setCard(currentCard)
215 
216  return value
217 
218  raise KeyError(f"Supplied key, '{name}' of unsupported type")
219 
220 
221 FitsChan.__getitem__ = getitem
222 
223 
224 def setitem(self, name, value):
225  """Set a new value.
226 
227  Parameters
228  ----------
229  name : `str` or `int`
230  If the name is an integer index this corresponds to a position within
231  the FitsChan. Index values are 0-based. A negative index counts
232  from the end of the FitsChan. If the index matches the number of
233  cards (e.g. the return value of `len()`) the new value will be
234  appended to the end of the FitsChan.
235  If the name is an empty string or `None`, the value will be inserted
236  at the current card position as a comment card.
237  If the name is a string corresponding to a header card that is already
238  present in the FitsChan, the new value will overwrite the existing
239  value leaving the header name and any comment unchanged.
240  Any other cards matching that name later in the header will
241  be removed. If there is no header with that name, a new card will
242  be inserted at the end of the FitsChan.
243  value : `str`, `int`, `float`, `bool`, `None`
244  The new value to be inserted. If an integer index is given it must be
245  a complete FITS header card. The string will be padded to 80
246  characters.
247 
248  Raises
249  ------
250  IndexError
251  Raised if the supplied integer index is out of range.
252  KeyError
253  Raised if the supplied name is neither a string or an integer.
254  TypeError
255  Raised if an integer index is given but the supplied value is not
256  a string.
257  """
258 
259  if isinstance(name, int):
260  # Calculate position in FitsChan (0-based to 1-based)
261  newpos = _calc_card_pos(self, name)
262  self.setCard(newpos)
263 
264  if not value:
265  value = " "
266 
267  # Overwrite the entire value
268  self.putFits(value, True)
269  return
270 
271  # A blank name results in a comment card being inserted at the
272  # current position
273  if not name:
274  self.setFitsCM(value, False)
275  return
276 
277  if not isinstance(name, str):
278  raise KeyError(f"Supplied key, '{name}' of unsupported type")
279 
280  # Get current card position and rewind
281  currentCard = self.getCard()
282  try:
283  self.clearCard()
284 
285  # Look for a card with the specified name.
286  # We do not care about the result; if nothing is found we will be
287  # at the end of the header.
288  self.findFits(name, False)
289 
290  # pyast seems to want to delete items if the value is None but
291  # astropy and PropertyList think the key should be undefined.
292  if value is None:
293  self.setFitsU(name, overwrite=True)
294  elif isinstance(value, int):
295  self.setFitsI(name, value, overwrite=True)
296  elif isinstance(value, float):
297  self.setFitsF(name, value, overwrite=True)
298  elif isinstance(value, bool):
299  self.setFitsL(name, value, overwrite=True)
300  else:
301  # Force to string.
302  self.setFitsS(name, str(value), overwrite=True)
303 
304  # Delete any later cards with matching keyword
305  while True:
306  found = self.findFits(name, False)
307  if not found.found:
308  break
309  self.delFits()
310  finally:
311  # Try to reinstate the current card
312  self.setCard(currentCard)
313 
314 
315 FitsChan.__setitem__ = setitem
316 
317 
318 def delitem(self, name):
319  """Delete an item from the FitsChan either by index (0-based) or by name.
320 
321  Parameters
322  ----------
323  name : `str` or `int`
324  If the name is an integer index the card at that position will be
325  removed. The index is zero-based. A negative index counts from the
326  end of the FitsChan.
327  If the name is a string all cards matching that name will be removed.
328 
329  Raises
330  ------
331  IndexError
332  Raised if the supplied index is out of range.
333  KeyError
334  Raised if the supplied name is not found in the FitsChan.
335  """
336  if isinstance(name, int):
337  # Correct to 1-based
338  newpos = _calc_card_pos(self, name)
339  if newpos <= 0 or newpos > self.nCard:
340  # AST will ignore this but we raise if the index is out of range
341  raise IndexError(f"No FITS card at index {name}")
342  self.setCard(newpos)
343  self.delFits()
344  return
345 
346  if not isinstance(name, str):
347  raise KeyError(f"Supplied key, '{name}' of unsupported type")
348 
349  self.clearCard()
350  # Delete any cards with matching keyword
351  deleted = False
352  while True:
353  found = self.findFits(name, False)
354  if not found.found:
355  break
356  self.delFits()
357  deleted = True
358 
359  if not deleted:
360  raise KeyError(f"No FITS card named {name}")
361 
362 
363 FitsChan.__delitem__ = delitem
364 
365 
366 def items(self):
367  """Iterate over each card, returning the keyword and value in a tuple.
368 
369  Returns
370  -------
371  key : `str`
372  The key associated with the card. Can be an empty string for some
373  comment styles. The same key name can be returned multiple times
374  and be associated with different values.
375  value : `str`, `int`, `bool`, `float`
376  The value.
377 
378  Notes
379  -----
380  The position of the iterator is internal to the FitsChan. Do not
381  change the card position when iterating.
382  """
383  self.clearCard()
384  nCards = self.nCard
385  thisCard = self.getCard()
386 
387  while thisCard <= nCards:
388  name, value = _get_current_card_value(self)
389  yield name, value
390  self.setCard(thisCard + 1)
391  thisCard = self.getCard()
392 
393 
394 FitsChan.items = items
astshim.fitsChanContinued.next
def next(self)
Definition: fitsChanContinued.py:105
astshim.fitsChanContinued.delitem
def delitem(self, name)
Definition: fitsChanContinued.py:318
lsst::sphgeom::abs
Angle abs(Angle const &a)
Definition: Angle.h:106
astshim.fitsChanContinued.contains
def contains(self, name)
Definition: fitsChanContinued.py:127
astshim.fitsChanContinued.setitem
def setitem(self, name, value)
Definition: fitsChanContinued.py:224
astshim.fitsChanContinued.getitem
def getitem(self, name)
Definition: fitsChanContinued.py:151
astshim.fitsChanContinued.to_string
def to_string(self)
Definition: fitsChanContinued.py:117
astshim.fitsChanContinued.length
def length(self)
Definition: fitsChanContinued.py:81
astshim.fitsChanContinued.items
def items(self)
Definition: fitsChanContinued.py:366
astshim.fitsChanContinued.iter
def iter(self)
Definition: fitsChanContinued.py:88