Skip to content

Commit cb43fd0

Browse files
authored
feat: check if input filename is an open HDF5 file object (#89)
* feat: check if input filename is an open HDF5 file object * refactor: use wrapper to importlib for optional dependencies * Update environment.yml
1 parent 089102f commit cb43fd0

34 files changed

+393
-481
lines changed

doc/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies:
2020
- scp
2121
- sphinx
2222
- sphinx_rtd_theme
23+
- timescale
2324
- pip:
2425
- sphinx-argparse>=0.4
2526
- ..

doc/source/api_reference/utilities.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ General Methods
1919

2020
.. autofunction:: icesat2_toolkit.utilities.get_data_path
2121

22+
.. autofunction:: icesat2_toolkit.utilities.import_dependency
23+
2224
.. autofunction:: icesat2_toolkit.utilities.get_hash
2325

2426
.. autofunction:: icesat2_toolkit.utilities.get_git_revision_hash

icesat2_toolkit/convert.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
convert.py
3-
Written by Tyler Sutterley (03/2024)
3+
Written by Tyler Sutterley (05/2024)
44
Utilities for converting ICESat-2 HDF5 files into different formats
55
66
PYTHON DEPENDENCIES:
@@ -17,6 +17,7 @@
1717
https://pandas.pydata.org/
1818
1919
UPDATE HISTORY:
20+
Updated 05/2024: use wrapper to importlib for optional dependencies
2021
Updated 03/2024: use pathlib to define and operate on paths
2122
Updated 12/2022: place some imports behind try/except statements
2223
Updated 06/2022: place zarr and pandas imports behind try/except statements
@@ -35,20 +36,12 @@
3536
import itertools
3637
import posixpath
3738
import numpy as np
39+
from icesat2_toolkit.utilities import import_dependency
3840

3941
# attempt imports
40-
try:
41-
import h5py
42-
except ModuleNotFoundError:
43-
warnings.warn("h5py not available", ImportWarning)
44-
try:
45-
import pandas
46-
except ModuleNotFoundError:
47-
warnings.warn("pandas not available", ImportWarning)
48-
try:
49-
import zarr
50-
except ModuleNotFoundError:
51-
warnings.warn("zarr not available", ImportWarning)
42+
h5py = import_dependency('h5py')
43+
pandas = import_dependency('pandas')
44+
zarr = import_dependency('zarr')
5245

5346
class convert():
5447
np.seterr(invalid='ignore')

icesat2_toolkit/convert_delta_time.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
u"""
3-
convert_delta_time.py (04/2022)
3+
convert_delta_time.py (04/2024)
44
Converts time from delta seconds into Julian and year-decimal
55
66
INPUTS:
@@ -21,6 +21,7 @@
2121
https://pypi.org/project/timescale/
2222
2323
UPDATE HISTORY:
24+
Updated 04/2024: use timescale for temporal operations
2425
Updated 04/2022: updated docstrings to numpy documentation format
2526
Updated 01/2021: time utilities for converting times from JD and to decimal
2627
Updated 08/2020: time utilities for counting leap seconds and JD conversion

icesat2_toolkit/fit.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python
22
u"""
33
fit.py
4-
Written by Tyler Sutterley (12/2021)
4+
Written by Tyler Sutterley (05/2024)
55
Utilities for calculating average fits from ATL03 Geolocated Photon Data
66
77
PYTHON DEPENDENCIES:
@@ -15,6 +15,7 @@
1515
https://github.com/scikit-learn/scikit-learn
1616
1717
UPDATE HISTORY:
18+
Updated 05/2024: use wrapper to importlib for optional dependencies
1819
Updated 12/2022: place some imports behind try/except statements
1920
Updated 04/2022: updated docstrings to numpy documentation format
2021
Written 05/2021
@@ -26,12 +27,10 @@
2627
import scipy.stats
2728
import scipy.signal
2829
import scipy.optimize
30+
from icesat2_toolkit.utilities import import_dependency
2931

3032
# attempt imports
31-
try:
32-
import sklearn.neighbors
33-
except (AttributeError, ImportError, ModuleNotFoundError) as exc:
34-
warnings.warn("scikit-learn not available", ImportWarning)
33+
neighbors = import_dependency('sklearn.neighbors')
3534

3635
# PURPOSE: compress complete list of valid indices into a set of ranges
3736
def compress_list(i,n):
@@ -527,7 +526,7 @@ def reduce_histogram_fit(x, y, z, ind, dt, FIT_TYPE='gaussian',
527526
# using kernel density functions from scikit-learn neighbors
528527
# gaussian kernels will reflect more accurate distributions of the data
529528
# with less sensitivity to sampling width than histograms (tophat kernels)
530-
kde = sklearn.neighbors.KernelDensity(bandwidth=dz,kernel='gaussian')
529+
kde = neighbors.KernelDensity(bandwidth=dz, kernel='gaussian')
531530
kde.fit(z[:,None])
532531
# kde score_samples outputs are normalized log density functions
533532
hist = np.exp(kde.score_samples(z_full[:,None]) + np.log(n_max*dz))
@@ -655,7 +654,7 @@ def reduce_histogram_fit(x, y, z, ind, dt, FIT_TYPE='gaussian',
655654
# using kernel density functions from scikit-learn neighbors
656655
# gaussian kernels will reflect more accurate distributions of the data
657656
# with less sensitivity to sampling width than histograms (tophat kernels)
658-
kde = sklearn.neighbors.KernelDensity(bandwidth=dz,kernel='gaussian')
657+
kde = neighbors.KernelDensity(bandwidth=dz,kernel='gaussian')
659658
kde.fit(z_filt[:,None])
660659
# kde score_samples outputs are normalized log density functions
661660
hist = np.exp(kde.score_samples(z_full[:,None]) + np.log(nz*dz))
@@ -990,7 +989,7 @@ def calc_first_photon_bias(temporal_residuals,n_pulses,n_pixels,dead_time,dt,
990989
# using kernel density functions from scikit-learn neighbors
991990
# gaussian kernels will reflect more accurate distributions of the data
992991
# with less sensitivity to sampling width than histograms (tophat kernels)
993-
kde = sklearn.neighbors.KernelDensity(bandwidth=dt,kernel='gaussian')
992+
kde = neighbors.KernelDensity(bandwidth=dt,kernel='gaussian')
994993
kde.fit(temporal_residuals[:,None])
995994
# kde score_samples outputs are normalized log density functions
996995
hist = np.exp(kde.score_samples(t_full[:,None]) + np.log(cnt*dt))

icesat2_toolkit/io/ATL03.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
u"""
3-
ATL03.py (03/2024)
3+
ATL03.py (05/2024)
44
Read ICESat-2 ATL03 and ATL09 data files to calculate average segment surfaces
55
ATL03 datasets: Global Geolocated Photons
66
ATL09 datasets: Atmospheric Characteristics
@@ -15,6 +15,8 @@
1515
https://www.h5py.org/
1616
1717
UPDATE HISTORY:
18+
Updated 05/2024: use wrapper to importlib for optional dependencies
19+
check if input filename is an open HDF5 file object
1820
Updated 03/2024: use pathlib to define and operate on paths
1921
Updated 11/2023: drop DIMENSION_LIST, CLASS and NAME attributes
2022
Updated 12/2022: place some imports behind try/except statements
@@ -41,12 +43,10 @@
4143
import warnings
4244
import numpy as np
4345
import scipy.interpolate
46+
from icesat2_toolkit.utilities import import_dependency
4447

4548
# attempt imports
46-
try:
47-
import h5py
48-
except ModuleNotFoundError:
49-
warnings.warn("h5py not available", ImportWarning)
49+
h5py = import_dependency('h5py')
5050

5151
# PURPOSE: read ICESat-2 ATL03 HDF5 data files
5252
def read_granule(FILENAME, ATTRIBUTES=False, **kwargs):
@@ -72,6 +72,8 @@ def read_granule(FILENAME, ATTRIBUTES=False, **kwargs):
7272
# Open the HDF5 file for reading
7373
if isinstance(FILENAME, io.IOBase):
7474
fileID = h5py.File(FILENAME, 'r')
75+
elif isinstance(FILENAME, h5py.File):
76+
fileID = FILENAME
7577
else:
7678
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
7779
fileID = h5py.File(FILENAME, 'r')
@@ -299,6 +301,8 @@ def interpolate_ATL09(FILENAME, pfl, dtime, ATTRIBUTES=True, **kwargs):
299301
# Open the HDF5 file for reading
300302
if isinstance(FILENAME, io.IOBase):
301303
fileID = h5py.File(FILENAME, 'r')
304+
elif isinstance(FILENAME, h5py.File):
305+
fileID = FILENAME
302306
else:
303307
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
304308
fileID = h5py.File(FILENAME, 'r')
@@ -368,6 +372,8 @@ def find_beams(FILENAME, **kwargs):
368372
# Open the HDF5 file for reading
369373
if isinstance(FILENAME, io.IOBase):
370374
fileID = h5py.File(FILENAME, 'r')
375+
elif isinstance(FILENAME, h5py.File):
376+
fileID = FILENAME
371377
else:
372378
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
373379
fileID = h5py.File(FILENAME, 'r')
@@ -414,6 +420,8 @@ def read_main(FILENAME, ATTRIBUTES=False, **kwargs):
414420
# Open the HDF5 file for reading
415421
if isinstance(FILENAME, io.IOBase):
416422
fileID = h5py.File(FILENAME, 'r')
423+
elif isinstance(FILENAME, h5py.File):
424+
fileID = FILENAME
417425
else:
418426
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
419427
fileID = h5py.File(FILENAME, 'r')
@@ -586,6 +594,8 @@ def read_beam(FILENAME, gtx, ATTRIBUTES=False, **kwargs):
586594
# Open the HDF5 file for reading
587595
if isinstance(FILENAME, io.IOBase):
588596
fileID = h5py.File(FILENAME, 'r')
597+
elif isinstance(FILENAME, h5py.File):
598+
fileID = FILENAME
589599
else:
590600
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
591601
fileID = h5py.File(FILENAME, 'r')

icesat2_toolkit/io/ATL06.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
u"""
3-
ATL06.py (03/2024)
3+
ATL06.py (05/2024)
44
Read ICESat-2 ATL06 (Land Ice Along-Track Height Product) data files
55
66
OPTIONS:
@@ -16,6 +16,8 @@
1616
https://www.h5py.org/
1717
1818
UPDATE HISTORY:
19+
Updated 05/2024: use wrapper to importlib for optional dependencies
20+
check if input filename is an open HDF5 file object
1921
Updated 03/2024: use pathlib to define and operate on paths
2022
Updated 11/2023: drop DIMENSION_LIST, CLASS and NAME attributes
2123
Updated 05/2023: extract more ancillary data from ATL06 files
@@ -39,12 +41,10 @@
3941
import pathlib
4042
import warnings
4143
import numpy as np
44+
from icesat2_toolkit.utilities import import_dependency
4245

4346
# attempt imports
44-
try:
45-
import h5py
46-
except ModuleNotFoundError:
47-
warnings.warn("h5py not available", ImportWarning)
47+
h5py = import_dependency('h5py')
4848

4949
# PURPOSE: read ICESat-2 ATL06 HDF5 data files
5050
def read_granule(FILENAME, ATTRIBUTES=False, HISTOGRAM=False,
@@ -75,6 +75,8 @@ def read_granule(FILENAME, ATTRIBUTES=False, HISTOGRAM=False,
7575
# Open the HDF5 file for reading
7676
if isinstance(FILENAME, io.IOBase):
7777
fileID = h5py.File(FILENAME, 'r')
78+
elif isinstance(FILENAME, h5py.File):
79+
fileID = FILENAME
7880
else:
7981
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
8082
fileID = h5py.File(FILENAME, 'r')
@@ -287,6 +289,8 @@ def find_beams(FILENAME, **kwargs):
287289
# Open the HDF5 file for reading
288290
if isinstance(FILENAME, io.IOBase):
289291
fileID = h5py.File(FILENAME, 'r')
292+
elif isinstance(FILENAME, h5py.File):
293+
fileID = FILENAME
290294
else:
291295
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
292296
fileID = h5py.File(FILENAME, 'r')
@@ -342,6 +346,8 @@ def read_beam(FILENAME, gtx, ATTRIBUTES=False, **kwargs):
342346
# Open the HDF5 file for reading
343347
if isinstance(FILENAME, io.IOBase):
344348
fileID = h5py.File(FILENAME, 'r')
349+
elif isinstance(FILENAME, h5py.File):
350+
fileID = FILENAME
345351
else:
346352
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
347353
fileID = h5py.File(FILENAME, 'r')

icesat2_toolkit/io/ATL07.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
u"""
3-
ATL07.py (03/2024)
3+
ATL07.py (05/2024)
44
Read ICESat-2 ATL07 (Sea Ice Height) data files
55
66
PYTHON DEPENDENCIES:
@@ -11,6 +11,8 @@
1111
https://www.h5py.org/
1212
1313
UPDATE HISTORY:
14+
Updated 05/2024: use wrapper to importlib for optional dependencies
15+
check if input filename is an open HDF5 file object
1416
Updated 03/2024: use pathlib to define and operate on paths
1517
Updated 11/2023: drop DIMENSION_LIST, CLASS and NAME attributes
1618
Updated 05/2023: extract more ancillary data from ATL07 files
@@ -32,12 +34,10 @@
3234
import pathlib
3335
import warnings
3436
import numpy as np
37+
from icesat2_toolkit.utilities import import_dependency
3538

3639
# attempt imports
37-
try:
38-
import h5py
39-
except ModuleNotFoundError:
40-
warnings.warn("h5py not available", ImportWarning)
40+
h5py = import_dependency('h5py')
4141

4242
# PURPOSE: read ICESat-2 ATL07 HDF5 data files
4343
def read_granule(FILENAME, ATTRIBUTES=False, **kwargs):
@@ -63,6 +63,8 @@ def read_granule(FILENAME, ATTRIBUTES=False, **kwargs):
6363
# Open the HDF5 file for reading
6464
if isinstance(FILENAME, io.IOBase):
6565
fileID = h5py.File(FILENAME, 'r')
66+
elif isinstance(FILENAME, h5py.File):
67+
fileID = FILENAME
6668
else:
6769
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
6870
fileID = h5py.File(FILENAME, 'r')
@@ -230,6 +232,8 @@ def find_beams(FILENAME, **kwargs):
230232
# Open the HDF5 file for reading
231233
if isinstance(FILENAME, io.IOBase):
232234
fileID = h5py.File(FILENAME, 'r')
235+
elif isinstance(FILENAME, h5py.File):
236+
fileID = FILENAME
233237
else:
234238
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
235239
fileID = h5py.File(FILENAME, 'r')
@@ -280,6 +284,8 @@ def read_beam(FILENAME, gtx, ATTRIBUTES=False, **kwargs):
280284
# Open the HDF5 file for reading
281285
if isinstance(FILENAME, io.IOBase):
282286
fileID = h5py.File(FILENAME, 'r')
287+
elif isinstance(FILENAME, h5py.File):
288+
fileID = FILENAME
283289
else:
284290
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
285291
fileID = h5py.File(FILENAME, 'r')

icesat2_toolkit/io/ATL10.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
u"""
3-
ATL10.py (03/2024)
3+
ATL10.py (05/2024)
44
Read ICESat-2 ATL10 (Sea Ice Freeboard) data files
55
66
PYTHON DEPENDENCIES:
@@ -11,6 +11,8 @@
1111
https://www.h5py.org/
1212
1313
UPDATE HISTORY:
14+
Updated 05/2024: use wrapper to importlib for optional dependencies
15+
check if input filename is an open HDF5 file object
1416
Updated 03/2024: use pathlib to define and operate on paths
1517
Updated 11/2023: drop DIMENSION_LIST, CLASS and NAME attributes
1618
Updated 05/2023: extract more ancillary data from ATL10 files
@@ -27,12 +29,10 @@
2729
import pathlib
2830
import warnings
2931
import numpy as np
32+
from icesat2_toolkit.utilities import import_dependency
3033

3134
# attempt imports
32-
try:
33-
import h5py
34-
except ModuleNotFoundError:
35-
warnings.warn("h5py not available", ImportWarning)
35+
h5py = import_dependency('h5py')
3636

3737
# PURPOSE: read ICESat-2 ATL10 HDF5 data files
3838
def read_granule(FILENAME, ATTRIBUTES=False, **kwargs):
@@ -58,6 +58,8 @@ def read_granule(FILENAME, ATTRIBUTES=False, **kwargs):
5858
# Open the HDF5 file for reading
5959
if isinstance(FILENAME, io.IOBase):
6060
fileID = h5py.File(FILENAME, 'r')
61+
elif isinstance(FILENAME, h5py.File):
62+
fileID = FILENAME
6163
else:
6264
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
6365
fileID = h5py.File(FILENAME, 'r')
@@ -228,6 +230,8 @@ def find_beams(FILENAME, **kwargs):
228230
# Open the HDF5 file for reading
229231
if isinstance(FILENAME, io.IOBase):
230232
fileID = h5py.File(FILENAME, 'r')
233+
elif isinstance(FILENAME, h5py.File):
234+
fileID = FILENAME
231235
else:
232236
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
233237
fileID = h5py.File(FILENAME, 'r')
@@ -279,6 +283,8 @@ def read_beam(FILENAME, gtx, ATTRIBUTES=False, **kwargs):
279283
# Open the HDF5 file for reading
280284
if isinstance(FILENAME, io.IOBase):
281285
fileID = h5py.File(FILENAME, 'r')
286+
elif isinstance(FILENAME, h5py.File):
287+
fileID = FILENAME
282288
else:
283289
FILENAME = pathlib.Path(FILENAME).expanduser().absolute()
284290
fileID = h5py.File(FILENAME, 'r')

0 commit comments

Comments
 (0)