Skip to content

Commit

Permalink
Merge a69b5a8 into 964a104
Browse files Browse the repository at this point in the history
  • Loading branch information
bsipocz committed Jun 3, 2016
2 parents 964a104 + a69b5a8 commit 202ebd3
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 36 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -209,6 +209,9 @@ New Features
- New function ``convenience.table_to_hdu`` to allow creating a FITS
HDU object directly from an astropy ``Table``. [#4778]

- A new optional arguments ``ignore_missing`` and ``remove_all`` are added
to ``astropy.io.fits.header.remove()``. [#5020]

- ``astropy.io.misc``

- ``astropy.io.registry``
Expand Down Expand Up @@ -370,6 +373,9 @@ API changes

- ``astropy.io.fits``

- Two optional boolean arguments ``ignore_missing`` and ``remove_all`` are
added to ``Header.remove``. [#5020]

- ``astropy.io.misc``

- ``astropy.io.votable``
Expand Down
57 changes: 23 additions & 34 deletions astropy/io/fits/header.py
Expand Up @@ -4,10 +4,8 @@

import collections
import copy
import inspect
import itertools
import re
import sys
import warnings

from .card import Card, CardList, _pad, KEYWORD_LENGTH
Expand Down Expand Up @@ -224,21 +222,11 @@ def __delitem__(self, key):
indices = self._rvkc_indices

if key not in indices:
if _is_astropy_internal():
# All internal code is designed to assume that this will
# raise a KeyError, so go ahead and do so
raise KeyError("Keyword '%s' not found." % key)
# Warn everyone else.
# TODO: Remove this warning and make KeyError the default after
# a couple versions (by 3.3, say)
warnings.warn(
'Deletion of non-existent keyword %r: '
'In a future Astropy version Header.__delitem__ may be '
'changed so that this raises a KeyError just like a dict '
'would. Please update your code so that KeyErrors are '
'caught and handled when deleting non-existent keywords.' %
key, AstropyDeprecationWarning)
return
# if keyword is not present raise KeyError.
# To delete keyword without caring if they were present,
# Header.remove(Keyword) can be used with optional argument ignore_missing as True
raise KeyError("Keyword '%s' not found." % key)

for idx in reversed(indices[key]):
# Have to copy the indices list since it will be modified below
del self[idx]
Expand Down Expand Up @@ -1530,19 +1518,34 @@ def insert(self, key, card, useblanks=True, after=False):

self._modified = True

def remove(self, keyword):
def remove(self, keyword, ignore_missing=False, remove_all=False):
"""
Removes the first instance of the given keyword from the header similar
to `list.remove` if the Header object is treated as a list of keywords.
Parameters
----------
keyword : str
The keyword of which to remove the first instance in the header
The keyword of which to remove the first instance in the header.
ignore_missing : bool, optional
When True, ignores missing keywords. Otherwise, if the keyword
is not present in the header a KeyError is raised.
remove_all : bool, optional
When True, all instances of keyword will be removed.
Otherwise only the first instance of the given keyword is removed.
"""
keyword = Card.normalize_keyword(keyword)
if keyword in self._keyword_indices:
del self[self._keyword_indices[keyword][0]]
if remove_all:
while keyword in self._keyword_indices:
del self[self._keyword_indices[keyword][0]]
elif not ignore_missing:
raise KeyError("Keyword '%s' not found." % keyword)

del self[self.index(keyword)]

def rename_keyword(self, oldkeyword, newkeyword, force=False):
"""
Expand Down Expand Up @@ -2293,20 +2296,6 @@ def __setitem__(self, item, value):
self._header[(self._keyword, item)] = value


def _is_astropy_internal():
"""
Returns True if the stack frame this is called from is in code internal to
the astropy package.
This is used in a few places where hacks are employed for backwards
compatibility with the old header API, but where we want to avoid using
those hacks internally.
"""

calling_mod = inspect.getmodule(sys._getframe(2))
return calling_mod and calling_mod.__name__.startswith('astropy.')


def _block_size(sep):
"""
Determine the size of a FITS header block if a non-blank separator is used
Expand Down
28 changes: 26 additions & 2 deletions astropy/io/fits/tests/test_header.py
Expand Up @@ -1402,8 +1402,32 @@ def test_header_insert_before_keyword(self):
assert list(header.keys())[-3] == 'TEST1'

def test_remove(self):
# TODO: Test the Header.remove() method; add support for ignore_missing
pass
header = fits.Header([('A', 'B'), ('C', 'D')])

# When keyword is present in the header it should be removed.
header.remove('C')
assert len(header) == 1
assert list(header) == ['A']
assert 'C' not in header

# When keyword is not present in the header and ignore_missing is
# False, KeyError should be raised
with pytest.raises(KeyError):
header.remove('F')

# When keyword is not present and ignore_missing is True, KeyError
# will be ignored
header.remove('F', ignore_missing=True)
assert len(header) == 1

# Test for removing all instances of a keyword
header = fits.Header([('A', 'B'), ('C', 'D'), ('A', 'F')])
header.remove('A', remove_all=True)
assert 'A' not in header
assert len(header) == 1
assert list(header) == ['C']
assert header[0] == 'D'


def test_header_comments(self):
header = fits.Header([('A', 'B', 'C'), ('DEF', 'G', 'H')])
Expand Down

0 comments on commit 202ebd3

Please sign in to comment.