Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set up directory for use in ImageFileCollection unit tests #687

Merged
merged 7 commits into from Jul 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion ccdproc/image_collection.py
Expand Up @@ -18,6 +18,7 @@
from astropy.utils.exceptions import AstropyUserWarning

from .ccddata import fits_ccddata_reader, _recognized_fits_file_extensions
from .tests.pytest_fixtures import directory_for_testing

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -754,7 +755,8 @@ def _fits_files_in_directory(self, extensions=None,
files.extend(fnmatch.filter(all_files, '*' + extension))
else:
for infile in all_files:
with open(infile, 'rb') as fp:
inpath = path.join(self.location, infile)
with open(inpath, 'rb') as fp:
# Hmm, first argument to is_fits is not actually used in
# that function. *shrug*
if fits.connect.is_fits('just some junk', infile, fp):
Expand Down Expand Up @@ -970,3 +972,14 @@ def ccds(self, ccd_kwargs=None, **kwd):
return self._generator('ccd', ccd_kwargs=ccd_kwargs, **kwd)
ccds.__doc__ = _generator.__doc__.format(
name='CCDData', default_scaling='True', return_type='astropy.nddata.CCDData')


def sample_directory_with_files():
"""
Returns the path to the small sample directory used
in the tests of ``ImageFileCollection``. Primarily intended
for use in the doctests.
"""

n_test, tmpdir = directory_for_testing()
return tmpdir
2 changes: 1 addition & 1 deletion ccdproc/tests/data/expected_ifc_file_properties.csv
@@ -1,4 +1,4 @@
file,simple,bitpix,naxis,naxis1,extend,bscale,bzero,imagetyp,filter,exptime
file,simple,bitpix,naxis,naxis1,extend,bscale,bzero,imagetyp,filter,exposure
filter_no_object_light.fit,True,16,1,100,True,1,32768,LIGHT,R,1.0
filter_object_light.fit,True,16,1,100,True,1,32768,LIGHT,R,1.0
filter_object_light.fit.gz,True,16,1,100,True,1,32768,LIGHT,R,1.0
Expand Down
30 changes: 19 additions & 11 deletions ccdproc/tests/pytest_fixtures.py
Expand Up @@ -77,12 +77,11 @@ def _make_file_for_testing(file_name='', **kwd):
hdu.writeto(file_name)


@pytest.fixture
def triage_setup(request):
def directory_for_testing():
"""
Set up directory with these contents:

One file with imagetyp BIAS. It has an the keyword EXPTIME in
One file with imagetyp BIAS. It has an the keyword EXPOSURE in
the header, but no others beyond IMAGETYP and the bare minimum
created with the FITS file.

Expand All @@ -95,9 +94,9 @@ def triage_setup(request):
files.

+ One file for each compression type, currently .gz and .fz.
+ ALL of the files will have the keyword EXPTIME
+ ALL of the files will have the keyword EXPOSURE
in the header.
+ Only ONE of them will have the value EXPTIME=15.0.
+ Only ONE of them will have the value EXPOSURE=15.0.
+ All of the files EXCEPT ONE will have the keyword
FILTER with the value 'R'.
+ NONE of the files have the keyword OBJECT
Expand Down Expand Up @@ -127,20 +126,20 @@ def triage_setup(request):

_make_file_for_testing(file_name='no_filter_no_object_bias.fit',
imagetyp='BIAS',
exptime=0.0)
EXPOSURE=0.0)

_make_file_for_testing(file_name='no_filter_no_object_light.fit',
imagetyp='LIGHT',
exptime=1.0)
EXPOSURE=1.0)

_make_file_for_testing(file_name='filter_no_object_light.fit',
imagetyp='LIGHT',
exptime=1.0,
EXPOSURE=1.0,
filter='R')

_make_file_for_testing(file_name='filter_object_light.fit',
imagetyp='LIGHT',
exptime=1.0,
EXPOSURE=1.0,
filter='R')

with open('filter_object_light.fit', 'rb') as f_in:
Expand All @@ -151,11 +150,20 @@ def triage_setup(request):

_make_file_for_testing(file_name='test.fits.fz',
imagetyp='LIGHT',
exptime=15.0,
EXPOSURE=15.0,
filter='R')

os.chdir(original_dir)

return n_test, test_dir


@pytest.fixture
def triage_setup(request):

n_test, test_dir = directory_for_testing()

def teardown():
os.chdir(original_dir)
try:
rmtree(test_dir)
except OSError:
Expand Down
12 changes: 7 additions & 5 deletions ccdproc/tests/test_image_collection.py
Expand Up @@ -145,7 +145,7 @@ def test_filter_fz_files(self, triage_setup):
fn = 'test.fits.fz'
ic = ImageFileCollection(location=triage_setup.test_dir, filenames=fn)
# Get a subset of files with a specific header value
filtered = ic.files_filtered(exptime=15.0)
filtered = ic.files_filtered(exposure=15.0)
assert len(filtered) == 1

def test_filtered_files_have_proper_path(self, triage_setup):
Expand Down Expand Up @@ -471,7 +471,7 @@ def test_fits_summary_when_keywords_are_not_subset(self, triage_setup):
but the latter is not a subset of the former.
"""
ic = ImageFileCollection(triage_setup.test_dir,
keywords=['imagetyp', 'exptime'])
keywords=['imagetyp', 'exposure'])
n_files = len(ic.files)
files_missing_this_key = ic.files_filtered(imagetyp='*',
monkeys=None)
Expand Down Expand Up @@ -899,7 +899,7 @@ def test_image_collection_with_no_location(self, triage_setup):
assert ic.summary['naxis1'].dtype == np.array([5]).dtype

# and the default float dtype
assert ic.summary['exptime'].dtype == np.array([5.0]).dtype
assert ic.summary['exposure'].dtype == np.array([5.0]).dtype

expected_heads = (actual['imagetyp'] == 'LIGHT').sum()

Expand All @@ -925,9 +925,11 @@ def test_force_detect_fits_files_finds_fits_files(self, triage_setup):
# Making a copy of *every* file means we can just double the expected
# number of files as part of the tests.
path = Path(triage_setup.test_dir)

for idx, p in enumerate(path.iterdir()):
new_name = 'no_extension{}'.format(idx)
(path / new_name).write_bytes(p.read_bytes())
new_path = path / new_name
new_path.write_bytes(p.read_bytes())

ic = ImageFileCollection(location=str(path),
find_fits_by_reading=True)
Expand All @@ -944,7 +946,7 @@ def test_force_detect_fits_files_finds_fits_files(self, triage_setup):

# Only one file in the original set of test files has exposure time
# 15, so there should be two now.
assert len(ic.files_filtered(exptime=15.0)) == 2
assert len(ic.files_filtered(exposure=15.0)) == 2

# Try one of the generators
expected_heads = (2 * triage_setup.n_test['light'] -
Expand Down
27 changes: 15 additions & 12 deletions docs/image_management.rst
Expand Up @@ -23,13 +23,15 @@ list of FITS keywords you want the
example initialization looks like::

>>> from ccdproc import ImageFileCollection
>>> from ccdproc.image_collection import sample_directory_with_files
>>> keys = ['imagetyp', 'object', 'filter', 'exposure']
>>> ic1 = ImageFileCollection('.', keywords=keys) # only keep track of keys
>>> dir = sample_directory_with_files()
>>> ic1 = ImageFileCollection(dir, keywords=keys) # only keep track of keys

You can use the wildcard ``*`` in place of a list to indicate you want the
collection to use all keywords in the headers::

>>> ic_all = ImageFileCollection('.', keywords='*')
>>> ic_all = ImageFileCollection(dir, keywords='*')

Normally identification of FITS files is done by looking at the file extension
and including all files with the correct extension.
Expand All @@ -38,13 +40,13 @@ If the files are not compressed (e.g. not gzipped) then you can force the image
collection to open each file and check from its contents whether it is FITS by
using the ``find_fits_by_reading`` argument::

>> ic_from_content = ImageFileCollection('.', find_fits_by_reading=True)
>> ic_from_content = ImageFileCollection(dir, find_fits_by_reading=True)

You can indicate filename patterns to include or exclude using Unix shell-style
expressions. For example, to include all filenames that begin with ``1d_`` but
not ones that include the word ``bad``, you could do::

>>> ic_all = ImageFileCollection('.', glob_include='1d_*',
>>> ic_all = ImageFileCollection(dir, glob_include='1d_*',
... glob_exclude='*bad*')

Alternatively, you can create the collection with an explicit list of file names::
Expand All @@ -69,16 +71,16 @@ Selecting files
Selecting the files that match a set of criteria, for example all images in
the I band with exposure time less than 60 seconds you could do::

>>> matches = (ic1.summary['filter'] == 'I') & (ic1.summary['exposure'] < 60) # doctest: +SKIP
>>> my_files = ic1.summary['file'][matches] # doctest: +SKIP
>>> matches = (ic1.summary['filter'] == 'R') & (ic1.summary['exposure'] < 15)
>>> my_files = ic1.summary['file'][matches]

The column ``file`` is added automatically when the image collection is created.

For more simple selection, when you just want files whose keywords exactly
match particular values, say all I band images with exposure time of 30
seconds, there is a convenience method ``.files_filtered``::

>>> my_files = ic1.files_filtered(filter='I', exposure=30) # doctest: +SKIP
>>> my_files = ic1.files_filtered(filter='R', exposure=15)

The optional arguments to ``files_filtered`` are used to filter the list of
files.
Expand Down Expand Up @@ -109,7 +111,7 @@ next to each other. To do this, the images in a collection can be sorted with
the ``sort`` method using the fits header keys in the same way you would sort a
:class:`~astropy.table.Table`::

>>> ic1.sort(['object', 'filter']) # doctest: +SKIP
>>> ic1.sort(['exposure', 'imagetyp'])

Iterating over hdus, headers, data, or ccds
-------------------------------------------
Expand All @@ -121,9 +123,10 @@ For example, to iterate over all of the I band images with exposure of
30 seconds, performing some basic operation on the data (very contrived
example)::

>>> for hdu in ic1.hdus(imagetyp='LiGhT', filter='I', exposure=30): # doctest: +SKIP
>>> for hdu in ic1.hdus(imagetyp='LiGhT', filter='R', exposure=15):
... hdu.header['exposure']
... new_data = hdu.data - hdu.data.mean()
15.0

Note that the names of the arguments to ``hdus`` here are the names of FITS
keywords in the collection and the values are the values of those keywords you
Expand All @@ -135,7 +138,7 @@ All of them have the option to also provide the file name in addition to the
hdu (or header or data)::

>>> for hdu, fname in ic1.hdus(return_fname=True,
... imagetyp='LiGhT', filter='I', exposure=30): # doctest: +SKIP
... imagetyp='LiGhT', filter='R', exposure=15):
... hdu.header['meansub'] = True
... hdu.data = hdu.data - hdu.data.mean()
... hdu.writeto(fname + '.new')
Expand All @@ -155,7 +158,7 @@ example below has (almost) the same effect of the example above, subtracting
the mean from each image and saving to a new file::

>>> for hdu in ic1.hdus(save_with_name='_new',
... imagetyp='LiGhT', filter='I', exposure=30): # doctest: +SKIP
... imagetyp='LiGhT', filter='R', exposure=15):
... hdu.header['meansub'] = True
... hdu.data = hdu.data - hdu.data.mean()

Expand Down Expand Up @@ -183,7 +186,7 @@ preserves no backup. The example below replaces each of the I band images
with 30 second exposure with a file that has had the mean subtracted::

>>> for hdu in ic1.hdus(overwrite=True,
... imagetyp='LiGhT', filter='I', exposure=30): # doctest: +SKIP
... imagetyp='LiGhT', filter='R', exposure=15): # doctest: +SKIP
... hdu.header['meansub'] = True
... hdu.data = hdu.data - hdu.data.mean()

Expand Down