Skip to content

Commit a1cd362

Browse files
authored
Merge pull request #99 from LSSTDESC/issue/96/gzipped_files_includeobj
handle gzipped instance catalogs and includeobj entries Closes #96
2 parents d98ccd2 + c62b293 commit a1cd362

File tree

5 files changed

+114
-4
lines changed

5 files changed

+114
-4
lines changed

python/desc/imsim/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
from .camera_readout import *
99
from .focalplane_info import *
1010
from .skyModel import *
11+
from .fopen import *

python/desc/imsim/fopen.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Utilities to read in gzipped instance catalogs with includeobj directives.
3+
"""
4+
import os
5+
import contextlib
6+
import gzip
7+
8+
__all__ = ["fopen"]
9+
10+
11+
@contextlib.contextmanager
12+
def fopen(filename, **kwds):
13+
"""
14+
Return a file descriptor-like object that closes the underlying
15+
file descriptor when used with the with-statement.
16+
17+
Parameters
18+
----------
19+
filename: str
20+
Filename of the instance catalog.
21+
**kwds: dict
22+
Keyword arguments to pass to the gzip.open or open functions.
23+
24+
Returns
25+
-------
26+
generator: file descriptor-like generator object that can be iterated
27+
over to return the lines in a file.
28+
"""
29+
abspath = os.path.split(os.path.abspath(filename))[0]
30+
try:
31+
if filename.endswith('.gz'):
32+
fd = gzip.open(filename, **kwds)
33+
else:
34+
fd = open(filename, **kwds)
35+
yield fopen_generator(fd, abspath, **kwds)
36+
finally:
37+
fd.close()
38+
39+
40+
def fopen_generator(fd, abspath, **kwds):
41+
"""
42+
Return a generator for the provided file descriptor that knows how
43+
to recursively read in instance catalogs specified by the
44+
includeobj directive.
45+
"""
46+
with fd as input_:
47+
for line in input_:
48+
if not line.startswith('includeobj'):
49+
yield line
50+
else:
51+
filename = os.path.join(abspath, line.strip().split()[-1])
52+
with fopen(filename, **kwds) as my_input:
53+
for line in my_input:
54+
yield line

python/desc/imsim/imSim.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
from lsst.sims.GalSimInterface import GalSimCelestialObject
3737
from lsst.sims.photUtils import BandpassDict, Sed, getImsimFluxNorm
3838
from lsst.sims.utils import defaultSpecMap
39-
from desc.imsim import CosmicRays
39+
from .cosmic_rays import CosmicRays
40+
from .fopen import fopen
4041

4142
_POINT_SOURCE = 1
4243
_SERSIC_2D = 2
@@ -103,7 +104,7 @@ def metadata_from_file(file_name):
103104
InstanceCatalog.
104105
"""
105106
input_params = {}
106-
with open(file_name, 'r') as in_file:
107+
with fopen(file_name, mode='rt') as in_file:
107108
for line in in_file:
108109
if line[0] == '#':
109110
continue
@@ -181,7 +182,7 @@ def sources_from_file(file_name, obs_md, phot_params, numRows=None):
181182

182183
num_objects = 0
183184
ct_rows = 0
184-
with open(file_name, 'r') as input_file:
185+
with fopen(file_name, mode='rt') as input_file:
185186
for line in input_file:
186187
ct_rows += 1
187188
params = line.strip().split()
@@ -214,7 +215,7 @@ def sources_from_file(file_name, obs_md, phot_params, numRows=None):
214215
object_type = np.zeros(num_objects, dtype=int)
215216

216217
i_obj = -1
217-
with open(file_name, 'r') as input_file:
218+
with fopen(file_name, mode='rt') as input_file:
218219
for line in input_file:
219220
params = line.strip().split()
220221
if params[0] != 'object':

tests/test_fopen.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"Unit tests for fopen function"
2+
3+
import os
4+
import unittest
5+
import gzip
6+
import desc.imsim
7+
8+
class FopenTestCase(unittest.TestCase):
9+
"TestCase class for fopen unit tests."
10+
def setUp(self):
11+
self.test_dir = 'fopen_dir'
12+
os.makedirs(self.test_dir, exist_ok=True)
13+
self.fopen_test_file \
14+
= os.path.join(self.test_dir, 'fopen_test_file.txt')
15+
self.fopen_include_file1 \
16+
= os.path.join(self.test_dir, 'fopen_include_file1.txt.gz')
17+
self.fopen_include_file2 \
18+
= os.path.join(self.test_dir, 'fopen_include_file2.txt.gz')
19+
self.lines = 'line1 line2 line3 line4'.split()
20+
with open(self.fopen_test_file, 'w') as output:
21+
output.write('%s\n' % self.lines[0])
22+
output.write('%s\n' % self.lines[1])
23+
output.write('includeobj %s\n'
24+
% os.path.basename(self.fopen_include_file1))
25+
output.write('includeobj %s\n'
26+
% os.path.basename(self.fopen_include_file2))
27+
with gzip.open(self.fopen_include_file1, 'wt') as output:
28+
output.write('%s\n' % self.lines[2])
29+
with gzip.open(self.fopen_include_file2, 'wt') as output:
30+
output.write('%s\n' % self.lines[3])
31+
32+
def tearDown(self):
33+
for item in (self.fopen_test_file, self.fopen_include_file1,
34+
self.fopen_include_file2):
35+
try:
36+
os.remove(item)
37+
except OSError:
38+
pass
39+
try:
40+
os.rmdir(self.test_dir)
41+
except OSError:
42+
pass
43+
44+
def test_fopen(self):
45+
"Test the fopen function."
46+
with desc.imsim.fopen(self.fopen_test_file, mode='rt') as input_:
47+
for i, line in enumerate(input_):
48+
expected = self.lines[i]
49+
self.assertEqual(line.strip(), expected)
50+
# Make sure all of the expected lines have been processed.
51+
self.assertEqual(self.lines[-1], expected)
52+
53+
if __name__ == '__main__':
54+
unittest.main()

tests/test_skyModel.py

100755100644
File mode changed.

0 commit comments

Comments
 (0)