-
-
Notifications
You must be signed in to change notification settings - Fork 584
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
Adds support for file-handlers and file-type in Map and TimeSeries #5193
Changes from all commits
76b716f
9a50c12
4678884
fa27a82
b906b5b
ac82e7f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Added support for file-object and file-type in `~sunpy.map.Map` and `~sunpy.timeseries.TimeSeries`. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
""" | ||
This module provides a generic file reader. | ||
""" | ||
import io | ||
import os | ||
import re | ||
import pathlib | ||
from itertools import chain | ||
|
||
try: | ||
from . import fits | ||
|
@@ -59,7 +61,7 @@ def read_file(filepath, filetype=None, **kwargs): | |
|
||
Parameters | ||
---------- | ||
filepath : path-like | ||
filepath : `str` or file-like | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does a pathlib.Path not work? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no it works, |
||
The file to be read. | ||
filetype : `str`, optional | ||
Supported reader or extension to manually specify the filetype. | ||
|
@@ -77,19 +79,24 @@ def read_file(filepath, filetype=None, **kwargs): | |
----- | ||
Other keyword arguments are passed to the reader used. | ||
""" | ||
filepath = str(pathlib.Path(filepath)) | ||
_filepath = filepath.name if isinstance(filepath, io.IOBase) else filepath | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not all objects which implement >>> import io
>>> isinstance(io.BytesIO(), io.IOBase)
True
>>> io.BytesIO().name
AttributeError: '_io.BytesIO' object has no attribute 'name' I don't think we should do this at all when dealing with io objects, I think it would be better to work out the type from the magic bytes at the start of the file and not any reference to the filename. (Which already happens if this filename doesn't match anything I think). |
||
_filepath = pathlib.Path(_filepath) | ||
|
||
# Use the explicitly passed filetype | ||
if filetype is not None: | ||
if filetype: | ||
if filetype not in chain(*_known_extensions): | ||
raise UnrecognizedFileTypeError( | ||
"The requested filetype is not currently supported by SunPy.") | ||
return _readers[filetype].read(filepath, **kwargs) | ||
|
||
# Go through the known extensions | ||
for extension, readername in _known_extensions.items(): | ||
if filepath.endswith(extension) or filetype in extension: | ||
if _filepath.suffix.endswith(extension): | ||
return _readers[readername].read(filepath, **kwargs) | ||
|
||
# If filetype is not apparent from the extension, attempt to detect it | ||
readername = _detect_filetype(filepath) | ||
return _readers[readername].read(filepath, **kwargs) | ||
readername = _detect_filetype(_filepath) | ||
return _readers[readername].read(_filepath, **kwargs) | ||
|
||
|
||
def read_file_header(filepath, filetype=None, **kwargs): | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,6 +1,7 @@ | ||||||
""" | ||||||
This module provides a JPEG 2000 file reader. | ||||||
""" | ||||||
import io | ||||||
import collections | ||||||
from xml.etree import cElementTree as ET | ||||||
|
||||||
|
@@ -26,6 +27,9 @@ def read(filepath, **kwargs): | |||||
pairs : `list` | ||||||
A list of (data, header) tuples. | ||||||
""" | ||||||
if isinstance(filepath, io.IOBase): | ||||||
raise TypeError("Reader does not support file-handler") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
# Put import here to speed up sunpy.io import time | ||||||
from glymur import Jp2k | ||||||
header = get_header(filepath) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,15 @@ def test_read_file_fits(self): | |
assert all([isinstance(p[1], | ||
sunpy.io.header.FileHeader) for p in pairs]) | ||
|
||
# Test file-object | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a test here to check it works as expected with other file objects, something like this: with open(AIA_171_IMAGE, 'rb') as fd:
fits_bytes = fd.read()
sunpy.io.read_file(io.BytesIO(fits_bytes))
... |
||
with open(AIA_171_IMAGE, 'rb') as fd: | ||
aiapair = sunpy.io.read_file(fd) | ||
assert isinstance(aiapair, list) | ||
assert len(aiapair) == 1 | ||
assert len(aiapair[0]) == 2 | ||
assert isinstance(aiapair[0][0], np.ndarray) | ||
assert isinstance(aiapair[0][1], sunpy.io.header.FileHeader) | ||
|
||
def test_read_file_fits_gzip(self): | ||
# Test read gzipped fits file | ||
for fits_extension in [".fts", ".fit", ".fits"]: | ||
|
@@ -59,22 +68,43 @@ def test_read_file_fits_gzip(self): | |
@skip_glymur | ||
def test_read_file_jp2(self): | ||
# Test read jp2 | ||
pair = sunpy.io.read_file(os.path.join(sunpy.data.test.rootdir, | ||
"2013_06_24__17_31_30_84__SDO_AIA_AIA_193.jp2")) | ||
|
||
sdo_aia_jp2 = os.path.join(sunpy.data.test.rootdir, "2013_06_24__17_31_30_84__SDO_AIA_AIA_193.jp2") | ||
# Test filepath | ||
pair = sunpy.io.read_file(sdo_aia_jp2) | ||
assert isinstance(pair, list) | ||
assert len(pair) == 1 | ||
assert len(pair[0]) == 2 | ||
assert isinstance(pair[0][0], np.ndarray) | ||
assert isinstance(pair[0][1], sunpy.io.header.FileHeader) | ||
|
||
# Test exception for file object | ||
with pytest.raises(TypeError, match="Reader does not support file-handler"): | ||
with open(sdo_aia_jp2, 'rb') as fd: | ||
pair = sunpy.io.read_file(fd) | ||
|
||
def test_read_file_header_fits(self): | ||
# Test FITS | ||
hlist = sunpy.io.read_file_header(AIA_171_IMAGE) | ||
assert isinstance(hlist, list) | ||
assert len(hlist) == 1 | ||
assert isinstance(hlist[0], sunpy.io.header.FileHeader) | ||
|
||
def test_read_file_exceptions(self): | ||
# Test invalid filetype | ||
with pytest.raises(sunpy.io.file_tools.UnrecognizedFileTypeError, | ||
match="The requested filetype is not currently supported by SunPy"): | ||
sunpy.io.read_file(AIA_171_IMAGE, "invalid_extension") | ||
|
||
def test_detect_filetype(self): | ||
# Test the detection logic | ||
assert sunpy.io.detect_filetype(AIA_171_IMAGE) == "fits" | ||
|
||
sdo_aia_jp2 = os.path.join(sunpy.data.test.rootdir, "2013_06_24__17_31_30_84__SDO_AIA_AIA_193.jp2") | ||
assert sunpy.io.detect_filetype(sdo_aia_jp2) == "jp2" | ||
|
||
goes_truncated = os.path.join(sunpy.data.test.rootdir, 'goes_truncated_test_goes15.nc') | ||
assert sunpy.io.detect_filetype(goes_truncated) == "hdf5" | ||
|
||
@skip_glymur | ||
def test_read_file_header_jp2(self): | ||
# Test jp2 | ||
|
@@ -110,13 +140,21 @@ def test_write_file_fits_bytes(self): | |
|
||
@skip_ana | ||
def test_read_file_ana(self): | ||
ana_data = sunpy.io.read_file(os.path.join(sunpy.data.test.rootdir, "test_ana.fz")) | ||
# Test read ana | ||
ana_test_file = os.path.join(sunpy.data.test.rootdir, "test_ana.fz") | ||
# Test filepath | ||
ana_data = sunpy.io.read_file(ana_test_file) | ||
assert isinstance(ana_data, list) | ||
assert len(ana_data) == 1 | ||
assert len(ana_data[0]) == 2 | ||
assert isinstance(ana_data[0][0], np.ndarray) | ||
assert isinstance(ana_data[0][1], sunpy.io.header.FileHeader) | ||
|
||
# Test file object | ||
with pytest.raises(TypeError, match="Reader does not support file-handler"): | ||
with open(ana_test_file, 'rb') as fd: | ||
ana_data = sunpy.io.read_file(fd) | ||
|
||
@skip_ana | ||
def test_read_file__header_ana(self): | ||
ana_data = sunpy.io.read_file_header(os.path.join(sunpy.data.test.rootdir, "test_ana.fz")) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,6 +80,10 @@ def test_patterns(self): | |
eitmap = sunpy.map.Map(a_fname) | ||
assert isinstance(eitmap, sunpy.map.GenericMap) | ||
|
||
# File name with file-type | ||
eitmap = sunpy.map.Map(a_fname, filetype=pathlib.Path(a_fname).suffix[1:]) | ||
assert isinstance(eitmap, sunpy.map.GenericMap) | ||
Comment on lines
+84
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You aren't really testing this properly here as you are giving it a filename which it can detect the type for. You would be better off giving it a random filename and the type. (Although it will also use magic bytes to detect file type if the filename doesn't pan out so I am not sure exactly how to make this test fail.) |
||
|
||
# Directory | ||
directory = pathlib.Path(filepath, "EIT") | ||
maps = sunpy.map.Map(os.fspath(directory)) | ||
|
@@ -98,6 +102,11 @@ def test_patterns(self): | |
assert isinstance(maps, list) | ||
assert ([isinstance(amap, sunpy.map.GenericMap) for amap in maps]) | ||
|
||
# File-like object | ||
with open(a_fname, "rb") as fd: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if you pass random binary data to map? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it returns a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a test for this in this file? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not currently, should I add one? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you could. |
||
fdmap = sunpy.map.Map(fd) | ||
assert isinstance(fdmap, sunpy.map.GenericMap) | ||
|
||
# Glob | ||
pattern = os.path.join(filepath, "EIT", "*") | ||
maps = sunpy.map.Map(pattern) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.