1
.. currentmodule:: pyfits
3
.. _header-transition-guide:
5
*********************************
6
Header Interface Transition Guide
7
*********************************
9
PyFITS v3.1 included an almost complete rewrite of the :class:`Header`
10
interface. Although the new interface is largely compatible with the old
11
interace (whether due to similarities in the design, or backwards-compatibility
12
support), there are enough differences that a full explanation of the new
15
The Trac ticket discussing the initial motivation and changes to be made to the
16
:class:`Header` class is `#64`_. It may be worth reading for some of the
17
background to this work, though this document contains a more complete
18
description of the "final" product (which will continue to evolve).
20
.. _#64: https://trac.assembla.com/pyfits/ticket/64
26
Prior to 3.1, PyFITS users interacted with FITS headers by way of three
27
different classes: :class:`Card`, :class:`CardList`, and :class:`Header`.
29
The Card class represents a single header card with a keyword, value, and
30
comment. It also contains all the machinery for parsing FITS header cards,
31
given the 80 character string, or "card image" read from the header.
33
The CardList class is actually a subclass of Python's `list` built-in. It was
34
meant to represent the actual list of cards that make up a header. That is, it
35
represents an ordered list of cards in the physical order that they appear in
36
the header. It supports the usual list methods for inserting and appending new
37
cards into the list. It also supports `dict`-like keyword access, where
38
``cardlist['KEYWORD']`` would return the first card in the list with the given
41
A lot of the functionality for manipulating headers was actually buried in the
42
CardList class. The Header class was more of a wrapper around CardList that
43
added a little bit of abstraction. It also implemented a partial dict-like
44
interface, though for Headers a keyword lookup returned the header value
45
associated with that keyword, and not the Card object. Though almost every
46
method on the Header class was just performing some operations on the
49
The problem is that there were certain things one could *only* do by directly
50
accessing the CardList, such as look up the comments on a card, or access cards
51
that have duplicate keywords, such as HISTORY. Another long-standing
52
misfeature that slicing a Header object actually returned a CardList object,
53
rather than a new Header. For all but the most simple use cases, working with
54
CardList objects was largely unavoidable.
56
But it was realized that CardList is really an implementation detail
57
not representing any element of a FITS file distinct from the header itself.
58
Users familiar with the FITS format know what a header is, but it's not clear
59
how a "card list" is distinct from that, or why operations go through the
60
Header object, while some have to be performed through the CardList.
62
So the primary goal of this redesign was eliminate the :class:`CardList` class
63
altogether, and make it possible for users to perform all header manipulations
64
directly through :class:`Header` objects. It also tries to present headers as
65
similar as possible to more a more familiar data structure--an ordered mapping
66
(or :class:`~collections.OrderedDict` in Python) for ease of use by new users
67
less familiar with the FITS format. Though there are still many added
68
complexities for dealing with the idiosyncracies of the FITS format.
74
A few old methods on the :class:`Header` class have been marked as deprecated,
75
either because they have been renamed to a more `PEP 8`_-compliant name, or
76
because have become redundant due to new features. To check if your code is
77
using any deprecated methods or features, run your code with ``python -Wd``.
78
This will output any deprecation warnings to the console.
80
Two of the most common deprecation warnings related to Headers are for:
82
- :meth:`Header.has_key`--this has actually been deprecated since PyFITS 3.0,
83
just as Python's `dict.has_key` is deprecated. For checking a key's presence
84
in a mapping object like `dict` or :class:`Header`, use the ``key in d``
85
syntax. This has long been the preference in Python.
87
- :meth:`Header.ascardlist()` and :attr:`Header.ascard`--these were used to
88
access the :class:`CardList` object underlying a header. They should still
89
work, and return a skeleton CardList implementation that should support most
90
of the old CardList functionality. But try removing as much of this as
91
possible. If direct access to the :class:`Card` objects making up a header
92
is necessary, use :attr:`Header.cards`, which returns an iterator over the
93
cards. More on that below.
95
.. _PEP 8: http://www.python.org/dev/peps/pep-0008/
100
The new :class:`Header` class is designed to work as a drop-in replacement for
101
a `dict` via `duck typing`_. That is, although it is not a subclass of `dict`,
102
it implements all the same methods and interfaces. In particular, it is
103
similar to an :class:`~collections.OrderedDict` in that the order of insertions
104
is preserved. However, Header also supports many additional features and
105
behaviors specific to the FITS format. It should also be noted that while the
106
old Header implementation also had a dict-like interface, it did not implement
107
the *entire* dict interface as the new Header does.
109
Although the new Header is used like a dict/mapping in most cases, it also
110
supports a `list` interface. The list-like interace is a bit idiosyncratic in
111
that in some contexts the Header acts like a list of values, in some like a
112
list of keywords, and in a few contexts like a list of :class:`Cards`. This
113
may be the most difficult aspect of the new design, but there is logic to it.
115
As with the old Header implementation, integer index access is supported:
116
``header[0]`` returns the value of the first keyword. However, the
117
:meth:`Header.index` method treats the header as though it's a list of
118
keywords, and returns the index of a given keyword. For example::
120
>>> header.index('BITPIX')
123
:meth:`Header.count` is similar to `list.count`, and also takes a keyword as
126
>>> header.count('HISTORY')
129
A good rule of thumb is that any item access using square brackets `[]` returns
130
*value* in the header, whether using keyword or index lookup. Methods like
131
:meth:`~Header.index` and :meth:`~Header.count` that deal with the order and
132
quantity of items in the Header generally work on keywords. Finally, methods
133
like :meth:`~Header.insert` and :meth:`~Header.append` that add new items to
134
the header work on cards.
136
Aside from the list-like methods, the new Header class works very similarly to
137
the old implementation for most basic use cases and should not present too many
138
surprises. There are differences, however:
140
- As before, the Header() initializer can take a list of :class:`Card` objects
141
with which to fill the header. However, now any iterable may be used. It is
142
also important to note that *any* Header method that accepts :class:`Card`
143
objects can also accept 2-tuples or 3-tuples in place of Cards. That is,
144
either a `(keyword, value, comment)` tuple or a `(keyword, value)` tuple
145
(comment is assumed blank) may be used anywhere in place of a Card object.
146
This is even preferred, as it simply involves less typing. For example::
148
>>> header = pyfits.Header([('A', 1), ('B', 2), ('C', 3, 'A comment')])
154
- As demonstrated in the previous example, the ``repr()`` for a Header, that is
155
the text that is displayed when entering a Header object in the Python
156
console as an expression, shows the header as it would appear in a FITS file.
157
This inserts newlines after each card so that it is easily readable
158
regardless of terminal width. It is *not* necessary to use ``print header``
159
to view this. Simply entering ``header`` displays the header contents as it
160
would appear in the file (sans the END card).
162
- ``len(header)`` is now supported (previously it was necessary to do
163
``len(header.ascard)``. This returns the total number of cards in the
164
header, including blank cards, but excluding the END card.
166
- FITS supports having duplicate keywords, although they are generally in error
167
except for commentary keywords like COMMENT and HISTORY. PyFITS now supports
168
reading, updating, and deleting duplicate keywords: Instead of using the
169
keyword by itself, use a ``(keyword, index)`` tuple. For example
170
``('HISTORY', 0)`` represents the first HISTORY card, ``('HISTORY', 1)``
171
represents the second HISTORY card, and so on. In fact, when a keyword is
172
used by itself, it's really just shorthand for ``(keyword, 0)``. Its is now
173
possible to delete an accidental duplicate like so::
175
>>> del header[('NAXIS', 1)]
177
This will remove an accdential duplicate NAXIS card from the header.
179
- Even if there are duplicate keywords, keyword lookups like
180
``header['NAXIS']`` will always return the value associated with the first
181
copy of that keyword, with one exception: Commentary keywords like COMMENT
182
and HISTORY are expected to have duplicates. So ``header['HISTORY']``, for
183
example, returns the whole sequence of HISTORY values in the correct order.
184
This list of values can be sliced arbitrarily. For example, to view the last
185
3 history entries in a header::
187
>>> hdulist[0].header['HISTORY'][-3:]
188
reference table oref$laf13367o_pct.fits
189
reference table oref$laf13369o_apt.fits
190
Heliocentric correction = 16.225 km/s
192
- Subscript assignment can now be used to add new keywords to the header. Just
193
as with a normal `dict`, ``header['NAXIS'] = 1`` will either update the NAXIS
194
keyword if it already exists, or add a new NAXIS keyword with a value of
195
``1`` if it does not exist. In the old interface this would return a
196
``KeyError`` if NAXIS did not exist, and the only way to add a new keyword
197
was through the update() method.
199
By default, new keywords added in this manner are added to the end of the
200
header, with a few FITS-specific exceptions:
202
* If the header contains extra blank cards at the end, new keywords are added
205
* If the header ends with a list of commentary cards--for example a sequence
206
of HISTORY cards--those are kept at the end, and new keywords are inserted
207
before the commentary cards.
209
* If the keyword is a commentary keyword like COMMENT or HISTORY (or an empty
210
string for blank keywords), a *new* commentary keyword is always added, and
211
appended to the last commentary keyword of the same type. For example,
212
HISTORY keywords are always placed after the last history keyword::
214
>>> header = pyfits.Header()
215
>>> header['COMMENT'] = 'Comment 1'
216
>>> header['HISTORY'] = 'History 1'
217
>>> header['COMMENT'] = 'Comment 2'
218
>>> header['HISTORY'] = 'History 2'
225
These behaviors represent a sensible default behavior for keyword assignment,
226
and represents the same behavior as :meth:`~Header.update` in the old Header
227
implementation. The default behaviors may still be bypassed through the use
228
of other assignment methods like :meth:`Header.set` and :meth:`Header.append`
231
- It is now also possible to assign a value and a comment to a keyword
232
simultaneously using a tuple::
234
>>> header['NAXIS'] = (2, 'Number of axis')
236
This will update the value and comment of an existing keyword, or add a new
237
keyword with the given value and comment.
239
- There is a new :attr:`Header.comments` attribute which lists all the comments
240
associated with keywords in the header (not to be confused with COMMENT
241
cards). This allows viewing and updating the comments on specific cards::
243
>>> header.comments['NAXIS']
245
>>> header.comments['NAXIS'] = 'Number of axes'
246
>>> header.comments['NAXIS']
249
- When deleting a keyword from a header, don't assume that the keyword already
250
exists. In the old Header implementation this would just silently do
251
nothing. For backwards-compatibility it is still okay to delete a
252
non-existent keyword, but a warning will be raised. In the future this
253
*will* be changed so that trying to delete a non-existent keyword raises a
254
`KeyError`. This is for consistency with the behavior of Python dicts. So
255
unless you know for certain that a keyword exists before deleting it, it's
256
best to do something like::
259
... del header['BITPIX']
263
Or if you prefer to look before you leap::
265
>>> if 'BITPIX' in header:
266
... del header['BITPIX']
268
- ``del header`` now supports slices. For example, to delete the last three
269
keywords from a header::
273
- Two headers can now be compared for equality--previously no two Header
274
objects were the same. Now they compare as equal if they contain the exact
275
same content. That is, this requires strict equality.
277
- Two headers can now be added with the '+' operator, which returns a copy of
278
the left header extended by the right header with :meth:`~Header.extend`.
279
Assignment addition is also possible.
281
- The Header.update() method used commonly with the old Header API has been
282
renamed to :meth:`Header.set`. The primary reason for this change is very
283
simple: Header implements the `dict` interface, which already has a method
284
called update(), but that behaves differently from the old Header.update().
286
The details of the new update() can be read in the API docs, but it is very
287
similar to `dict.update`. It also supports backwards compatibility with the
288
old update() by analysis of the arguments passed to it, so existing code will
289
not break immediately. However, this *will* cause a deprecation warning to
290
be output if they're enabled. It is best, for starters, to replace all
291
update() calls with set(). Recall, also, that direct assignment is now
292
possible for adding new keywords to a header. So by and large the only
293
reason to prefer using :meth:`Header.set` is its capability of inserting or
294
moving a keyword to a specific location using the ``before`` or ``after``
297
- Slicing a Header with a slice index returns a new Header containing only
298
those cards contained in the slice. As mentioned earlier, it used to be that
299
slicing a Header returned a card list--something of a misfeature. In
300
general, objects that support slicing ought to return an object of the same
301
type when you slice them.
303
Likewise, wildcard keywords used to return a CardList object. Now they
304
return a new Header--similarly to a slice. For example::
308
returns a new header containing only the NAXIS and NAXISn cards from the
311
.. _duck typing: http://en.wikipedia.org/wiki/Duck_typing
317
The above may seem like a lot, but the majority of existing code using PyFITS
318
to manipulate headers should not need to be updated, at least not immediately.
319
The most common operations still work the same.
321
As mentioned above, it would be helpful to run your code with ``python -Wd`` to
322
enable deprecation warnings--that should be a good idea of where to look to
325
If your code needs to be able to support older versions of PyFITS
326
simultaneously with PyFITS 3.1, things are slightly trickier, but not by
327
much--the deprecated interfaces will not be removed for several more versions
330
- The first change worth making, which is supported by any PyFITS version in
331
the last several years, is remove any use of :meth:`Header.has_key` and
332
replace it with ``keyword in header`` syntax. It's worth making this change
333
for any dict as well, since `dict.has_key` is deprecated. Running the
334
following regular expression over your code may help with most (but not all)
337
s/([^ ]+)\.has_key\(([^)]+)\)/\2 in \1/
339
- If possible, replace any calls to Header.update() with Header.set() (though
340
don't bother with this if you need to support older PyFITS versions). Also,
341
if you have any calls to Header.update() that can be replaced with simple
342
subscript assignments (eg. ``header['NAXIS'] = (2, 'Number of axes')``) do
343
that too, if possible.
345
- Find any code that uses ``header.ascard`` or ``header.ascardlist()``. First
346
ascertain whether that code really needs to work directly on Card objects.
347
If that is definitely the case, go ahead and replace those with
348
``header.cards``--that should work without too much fuss. If you do need to
349
support older versions, you may keep using ``header.ascard`` for now.
351
- In the off chance that you have any code that slices a header, it's best to
352
take the result of that and create a new Header object from it. For
355
>>> new_header = pyfits.Header(old_header[2:])
357
This avoids the problem that in PyFITS <= 3.0 slicing a Header returns a
358
CardList by using the result to initialize a new Header object. This will
359
work in both cases (in PyFITS 3.1, initializing a Header with an existing
360
Header just copies it, a la `list`).
362
- As mentioned earlier, locate any code that deletes keywords with ``del``, and
363
make sure they either look before they leap (``if keyword in header:``) or
364
ask forgiveness (``try/except KeyError:``).