Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/scipy/io/_idl.py: 10%
431 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-23 06:43 +0000
1# IDLSave - a python module to read IDL 'save' files
2# Copyright (c) 2010 Thomas P. Robitaille
4# Many thanks to Craig Markwardt for publishing the Unofficial Format
5# Specification for IDL .sav files, without which this Python module would not
6# exist (http://cow.physics.wisc.edu/~craigm/idl/savefmt).
8# This code was developed by with permission from ITT Visual Information
9# Systems. IDL(r) is a registered trademark of ITT Visual Information Systems,
10# Inc. for their Interactive Data Language software.
12# Permission is hereby granted, free of charge, to any person obtaining a
13# copy of this software and associated documentation files (the "Software"),
14# to deal in the Software without restriction, including without limitation
15# the rights to use, copy, modify, merge, publish, distribute, sublicense,
16# and/or sell copies of the Software, and to permit persons to whom the
17# Software is furnished to do so, subject to the following conditions:
19# The above copyright notice and this permission notice shall be included in
20# all copies or substantial portions of the Software.
22# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28# DEALINGS IN THE SOFTWARE.
30__all__ = ['readsav']
32import struct
33import numpy as np
34import tempfile
35import zlib
36import warnings
38# Define the different data types that can be found in an IDL save file
39DTYPE_DICT = {1: '>u1',
40 2: '>i2',
41 3: '>i4',
42 4: '>f4',
43 5: '>f8',
44 6: '>c8',
45 7: '|O',
46 8: '|O',
47 9: '>c16',
48 10: '|O',
49 11: '|O',
50 12: '>u2',
51 13: '>u4',
52 14: '>i8',
53 15: '>u8'}
55# Define the different record types that can be found in an IDL save file
56RECTYPE_DICT = {0: "START_MARKER",
57 1: "COMMON_VARIABLE",
58 2: "VARIABLE",
59 3: "SYSTEM_VARIABLE",
60 6: "END_MARKER",
61 10: "TIMESTAMP",
62 12: "COMPILED",
63 13: "IDENTIFICATION",
64 14: "VERSION",
65 15: "HEAP_HEADER",
66 16: "HEAP_DATA",
67 17: "PROMOTE64",
68 19: "NOTICE",
69 20: "DESCRIPTION"}
71# Define a dictionary to contain structure definitions
72STRUCT_DICT = {}
75def _align_32(f):
76 '''Align to the next 32-bit position in a file'''
78 pos = f.tell()
79 if pos % 4 != 0:
80 f.seek(pos + 4 - pos % 4)
81 return
84def _skip_bytes(f, n):
85 '''Skip `n` bytes'''
86 f.read(n)
87 return
90def _read_bytes(f, n):
91 '''Read the next `n` bytes'''
92 return f.read(n)
95def _read_byte(f):
96 '''Read a single byte'''
97 return np.uint8(struct.unpack('>B', f.read(4)[:1])[0])
100def _read_long(f):
101 '''Read a signed 32-bit integer'''
102 return np.int32(struct.unpack('>l', f.read(4))[0])
105def _read_int16(f):
106 '''Read a signed 16-bit integer'''
107 return np.int16(struct.unpack('>h', f.read(4)[2:4])[0])
110def _read_int32(f):
111 '''Read a signed 32-bit integer'''
112 return np.int32(struct.unpack('>i', f.read(4))[0])
115def _read_int64(f):
116 '''Read a signed 64-bit integer'''
117 return np.int64(struct.unpack('>q', f.read(8))[0])
120def _read_uint16(f):
121 '''Read an unsigned 16-bit integer'''
122 return np.uint16(struct.unpack('>H', f.read(4)[2:4])[0])
125def _read_uint32(f):
126 '''Read an unsigned 32-bit integer'''
127 return np.uint32(struct.unpack('>I', f.read(4))[0])
130def _read_uint64(f):
131 '''Read an unsigned 64-bit integer'''
132 return np.uint64(struct.unpack('>Q', f.read(8))[0])
135def _read_float32(f):
136 '''Read a 32-bit float'''
137 return np.float32(struct.unpack('>f', f.read(4))[0])
140def _read_float64(f):
141 '''Read a 64-bit float'''
142 return np.float64(struct.unpack('>d', f.read(8))[0])
145class Pointer:
146 '''Class used to define pointers'''
148 def __init__(self, index):
149 self.index = index
150 return
153class ObjectPointer(Pointer):
154 '''Class used to define object pointers'''
155 pass
158def _read_string(f):
159 '''Read a string'''
160 length = _read_long(f)
161 if length > 0:
162 chars = _read_bytes(f, length).decode('latin1')
163 _align_32(f)
164 else:
165 chars = ''
166 return chars
169def _read_string_data(f):
170 '''Read a data string (length is specified twice)'''
171 length = _read_long(f)
172 if length > 0:
173 length = _read_long(f)
174 string_data = _read_bytes(f, length)
175 _align_32(f)
176 else:
177 string_data = ''
178 return string_data
181def _read_data(f, dtype):
182 '''Read a variable with a specified data type'''
183 if dtype == 1:
184 if _read_int32(f) != 1:
185 raise Exception("Error occurred while reading byte variable")
186 return _read_byte(f)
187 elif dtype == 2:
188 return _read_int16(f)
189 elif dtype == 3:
190 return _read_int32(f)
191 elif dtype == 4:
192 return _read_float32(f)
193 elif dtype == 5:
194 return _read_float64(f)
195 elif dtype == 6:
196 real = _read_float32(f)
197 imag = _read_float32(f)
198 return np.complex64(real + imag * 1j)
199 elif dtype == 7:
200 return _read_string_data(f)
201 elif dtype == 8:
202 raise Exception("Should not be here - please report this")
203 elif dtype == 9:
204 real = _read_float64(f)
205 imag = _read_float64(f)
206 return np.complex128(real + imag * 1j)
207 elif dtype == 10:
208 return Pointer(_read_int32(f))
209 elif dtype == 11:
210 return ObjectPointer(_read_int32(f))
211 elif dtype == 12:
212 return _read_uint16(f)
213 elif dtype == 13:
214 return _read_uint32(f)
215 elif dtype == 14:
216 return _read_int64(f)
217 elif dtype == 15:
218 return _read_uint64(f)
219 else:
220 raise Exception("Unknown IDL type: %i - please report this" % dtype)
223def _read_structure(f, array_desc, struct_desc):
224 '''
225 Read a structure, with the array and structure descriptors given as
226 `array_desc` and `structure_desc` respectively.
227 '''
229 nrows = array_desc['nelements']
230 columns = struct_desc['tagtable']
232 dtype = []
233 for col in columns:
234 if col['structure'] or col['array']:
235 dtype.append(((col['name'].lower(), col['name']), np.object_))
236 else:
237 if col['typecode'] in DTYPE_DICT:
238 dtype.append(((col['name'].lower(), col['name']),
239 DTYPE_DICT[col['typecode']]))
240 else:
241 raise Exception("Variable type %i not implemented" %
242 col['typecode'])
244 structure = np.rec.recarray((nrows, ), dtype=dtype)
246 for i in range(nrows):
247 for col in columns:
248 dtype = col['typecode']
249 if col['structure']:
250 structure[col['name']][i] = _read_structure(f,
251 struct_desc['arrtable'][col['name']],
252 struct_desc['structtable'][col['name']])
253 elif col['array']:
254 structure[col['name']][i] = _read_array(f, dtype,
255 struct_desc['arrtable'][col['name']])
256 else:
257 structure[col['name']][i] = _read_data(f, dtype)
259 # Reshape structure if needed
260 if array_desc['ndims'] > 1:
261 dims = array_desc['dims'][:int(array_desc['ndims'])]
262 dims.reverse()
263 structure = structure.reshape(dims)
265 return structure
268def _read_array(f, typecode, array_desc):
269 '''
270 Read an array of type `typecode`, with the array descriptor given as
271 `array_desc`.
272 '''
274 if typecode in [1, 3, 4, 5, 6, 9, 13, 14, 15]:
276 if typecode == 1:
277 nbytes = _read_int32(f)
278 if nbytes != array_desc['nbytes']:
279 warnings.warn("Not able to verify number of bytes from header")
281 # Read bytes as numpy array
282 array = np.frombuffer(f.read(array_desc['nbytes']),
283 dtype=DTYPE_DICT[typecode])
285 elif typecode in [2, 12]:
287 # These are 2 byte types, need to skip every two as they are not packed
289 array = np.frombuffer(f.read(array_desc['nbytes']*2),
290 dtype=DTYPE_DICT[typecode])[1::2]
292 else:
294 # Read bytes into list
295 array = []
296 for i in range(array_desc['nelements']):
297 dtype = typecode
298 data = _read_data(f, dtype)
299 array.append(data)
301 array = np.array(array, dtype=np.object_)
303 # Reshape array if needed
304 if array_desc['ndims'] > 1:
305 dims = array_desc['dims'][:int(array_desc['ndims'])]
306 dims.reverse()
307 array = array.reshape(dims)
309 # Go to next alignment position
310 _align_32(f)
312 return array
315def _read_record(f):
316 '''Function to read in a full record'''
318 record = {'rectype': _read_long(f)}
320 nextrec = _read_uint32(f)
321 nextrec += _read_uint32(f).astype(np.int64) * 2**32
323 _skip_bytes(f, 4)
325 if record['rectype'] not in RECTYPE_DICT:
326 raise Exception("Unknown RECTYPE: %i" % record['rectype'])
328 record['rectype'] = RECTYPE_DICT[record['rectype']]
330 if record['rectype'] in ["VARIABLE", "HEAP_DATA"]:
332 if record['rectype'] == "VARIABLE":
333 record['varname'] = _read_string(f)
334 else:
335 record['heap_index'] = _read_long(f)
336 _skip_bytes(f, 4)
338 rectypedesc = _read_typedesc(f)
340 if rectypedesc['typecode'] == 0:
342 if nextrec == f.tell():
343 record['data'] = None # Indicates NULL value
344 else:
345 raise ValueError("Unexpected type code: 0")
347 else:
349 varstart = _read_long(f)
350 if varstart != 7:
351 raise Exception("VARSTART is not 7")
353 if rectypedesc['structure']:
354 record['data'] = _read_structure(f, rectypedesc['array_desc'],
355 rectypedesc['struct_desc'])
356 elif rectypedesc['array']:
357 record['data'] = _read_array(f, rectypedesc['typecode'],
358 rectypedesc['array_desc'])
359 else:
360 dtype = rectypedesc['typecode']
361 record['data'] = _read_data(f, dtype)
363 elif record['rectype'] == "TIMESTAMP":
365 _skip_bytes(f, 4*256)
366 record['date'] = _read_string(f)
367 record['user'] = _read_string(f)
368 record['host'] = _read_string(f)
370 elif record['rectype'] == "VERSION":
372 record['format'] = _read_long(f)
373 record['arch'] = _read_string(f)
374 record['os'] = _read_string(f)
375 record['release'] = _read_string(f)
377 elif record['rectype'] == "IDENTIFICATON":
379 record['author'] = _read_string(f)
380 record['title'] = _read_string(f)
381 record['idcode'] = _read_string(f)
383 elif record['rectype'] == "NOTICE":
385 record['notice'] = _read_string(f)
387 elif record['rectype'] == "DESCRIPTION":
389 record['description'] = _read_string_data(f)
391 elif record['rectype'] == "HEAP_HEADER":
393 record['nvalues'] = _read_long(f)
394 record['indices'] = [_read_long(f) for _ in range(record['nvalues'])]
396 elif record['rectype'] == "COMMONBLOCK":
398 record['nvars'] = _read_long(f)
399 record['name'] = _read_string(f)
400 record['varnames'] = [_read_string(f) for _ in range(record['nvars'])]
402 elif record['rectype'] == "END_MARKER":
404 record['end'] = True
406 elif record['rectype'] == "UNKNOWN":
408 warnings.warn("Skipping UNKNOWN record")
410 elif record['rectype'] == "SYSTEM_VARIABLE":
412 warnings.warn("Skipping SYSTEM_VARIABLE record")
414 else:
416 raise Exception("record['rectype']=%s not implemented" %
417 record['rectype'])
419 f.seek(nextrec)
421 return record
424def _read_typedesc(f):
425 '''Function to read in a type descriptor'''
427 typedesc = {'typecode': _read_long(f), 'varflags': _read_long(f)}
429 if typedesc['varflags'] & 2 == 2:
430 raise Exception("System variables not implemented")
432 typedesc['array'] = typedesc['varflags'] & 4 == 4
433 typedesc['structure'] = typedesc['varflags'] & 32 == 32
435 if typedesc['structure']:
436 typedesc['array_desc'] = _read_arraydesc(f)
437 typedesc['struct_desc'] = _read_structdesc(f)
438 elif typedesc['array']:
439 typedesc['array_desc'] = _read_arraydesc(f)
441 return typedesc
444def _read_arraydesc(f):
445 '''Function to read in an array descriptor'''
447 arraydesc = {'arrstart': _read_long(f)}
449 if arraydesc['arrstart'] == 8:
451 _skip_bytes(f, 4)
453 arraydesc['nbytes'] = _read_long(f)
454 arraydesc['nelements'] = _read_long(f)
455 arraydesc['ndims'] = _read_long(f)
457 _skip_bytes(f, 8)
459 arraydesc['nmax'] = _read_long(f)
461 arraydesc['dims'] = [_read_long(f) for _ in range(arraydesc['nmax'])]
463 elif arraydesc['arrstart'] == 18:
465 warnings.warn("Using experimental 64-bit array read")
467 _skip_bytes(f, 8)
469 arraydesc['nbytes'] = _read_uint64(f)
470 arraydesc['nelements'] = _read_uint64(f)
471 arraydesc['ndims'] = _read_long(f)
473 _skip_bytes(f, 8)
475 arraydesc['nmax'] = 8
477 arraydesc['dims'] = []
478 for d in range(arraydesc['nmax']):
479 v = _read_long(f)
480 if v != 0:
481 raise Exception("Expected a zero in ARRAY_DESC")
482 arraydesc['dims'].append(_read_long(f))
484 else:
486 raise Exception("Unknown ARRSTART: %i" % arraydesc['arrstart'])
488 return arraydesc
491def _read_structdesc(f):
492 '''Function to read in a structure descriptor'''
494 structdesc = {}
496 structstart = _read_long(f)
497 if structstart != 9:
498 raise Exception("STRUCTSTART should be 9")
500 structdesc['name'] = _read_string(f)
501 predef = _read_long(f)
502 structdesc['ntags'] = _read_long(f)
503 structdesc['nbytes'] = _read_long(f)
505 structdesc['predef'] = predef & 1
506 structdesc['inherits'] = predef & 2
507 structdesc['is_super'] = predef & 4
509 if not structdesc['predef']:
511 structdesc['tagtable'] = [_read_tagdesc(f)
512 for _ in range(structdesc['ntags'])]
514 for tag in structdesc['tagtable']:
515 tag['name'] = _read_string(f)
517 structdesc['arrtable'] = {tag['name']: _read_arraydesc(f)
518 for tag in structdesc['tagtable']
519 if tag['array']}
521 structdesc['structtable'] = {tag['name']: _read_structdesc(f)
522 for tag in structdesc['tagtable']
523 if tag['structure']}
525 if structdesc['inherits'] or structdesc['is_super']:
526 structdesc['classname'] = _read_string(f)
527 structdesc['nsupclasses'] = _read_long(f)
528 structdesc['supclassnames'] = [
529 _read_string(f) for _ in range(structdesc['nsupclasses'])]
530 structdesc['supclasstable'] = [
531 _read_structdesc(f) for _ in range(structdesc['nsupclasses'])]
533 STRUCT_DICT[structdesc['name']] = structdesc
535 else:
537 if structdesc['name'] not in STRUCT_DICT:
538 raise Exception("PREDEF=1 but can't find definition")
540 structdesc = STRUCT_DICT[structdesc['name']]
542 return structdesc
545def _read_tagdesc(f):
546 '''Function to read in a tag descriptor'''
548 tagdesc = {'offset': _read_long(f)}
550 if tagdesc['offset'] == -1:
551 tagdesc['offset'] = _read_uint64(f)
553 tagdesc['typecode'] = _read_long(f)
554 tagflags = _read_long(f)
556 tagdesc['array'] = tagflags & 4 == 4
557 tagdesc['structure'] = tagflags & 32 == 32
558 tagdesc['scalar'] = tagdesc['typecode'] in DTYPE_DICT
559 # Assume '10'x is scalar
561 return tagdesc
564def _replace_heap(variable, heap):
566 if isinstance(variable, Pointer):
568 while isinstance(variable, Pointer):
570 if variable.index == 0:
571 variable = None
572 else:
573 if variable.index in heap:
574 variable = heap[variable.index]
575 else:
576 warnings.warn("Variable referenced by pointer not found "
577 "in heap: variable will be set to None")
578 variable = None
580 replace, new = _replace_heap(variable, heap)
582 if replace:
583 variable = new
585 return True, variable
587 elif isinstance(variable, np.rec.recarray):
589 # Loop over records
590 for ir, record in enumerate(variable):
592 replace, new = _replace_heap(record, heap)
594 if replace:
595 variable[ir] = new
597 return False, variable
599 elif isinstance(variable, np.record):
601 # Loop over values
602 for iv, value in enumerate(variable):
604 replace, new = _replace_heap(value, heap)
606 if replace:
607 variable[iv] = new
609 return False, variable
611 elif isinstance(variable, np.ndarray):
613 # Loop over values if type is np.object_
614 if variable.dtype.type is np.object_:
616 for iv in range(variable.size):
618 replace, new = _replace_heap(variable.item(iv), heap)
620 if replace:
621 variable.reshape(-1)[iv] = new
623 return False, variable
625 else:
627 return False, variable
630class AttrDict(dict):
631 '''
632 A case-insensitive dictionary with access via item, attribute, and call
633 notations:
635 >>> from scipy.io._idl import AttrDict
636 >>> d = AttrDict()
637 >>> d['Variable'] = 123
638 >>> d['Variable']
639 123
640 >>> d.Variable
641 123
642 >>> d.variable
643 123
644 >>> d('VARIABLE')
645 123
646 >>> d['missing']
647 Traceback (most recent error last):
648 ...
649 KeyError: 'missing'
650 >>> d.missing
651 Traceback (most recent error last):
652 ...
653 AttributeError: 'AttrDict' object has no attribute 'missing'
654 '''
656 def __init__(self, init={}):
657 dict.__init__(self, init)
659 def __getitem__(self, name):
660 return super().__getitem__(name.lower())
662 def __setitem__(self, key, value):
663 return super().__setitem__(key.lower(), value)
665 def __getattr__(self, name):
666 try:
667 return self.__getitem__(name)
668 except KeyError:
669 raise AttributeError(
670 f"'{type(self)}' object has no attribute '{name}'") from None
672 __setattr__ = __setitem__
673 __call__ = __getitem__
676def readsav(file_name, idict=None, python_dict=False,
677 uncompressed_file_name=None, verbose=False):
678 """
679 Read an IDL .sav file.
681 Parameters
682 ----------
683 file_name : str
684 Name of the IDL save file.
685 idict : dict, optional
686 Dictionary in which to insert .sav file variables.
687 python_dict : bool, optional
688 By default, the object return is not a Python dictionary, but a
689 case-insensitive dictionary with item, attribute, and call access
690 to variables. To get a standard Python dictionary, set this option
691 to True.
692 uncompressed_file_name : str, optional
693 This option only has an effect for .sav files written with the
694 /compress option. If a file name is specified, compressed .sav
695 files are uncompressed to this file. Otherwise, readsav will use
696 the `tempfile` module to determine a temporary filename
697 automatically, and will remove the temporary file upon successfully
698 reading it in.
699 verbose : bool, optional
700 Whether to print out information about the save file, including
701 the records read, and available variables.
703 Returns
704 -------
705 idl_dict : AttrDict or dict
706 If `python_dict` is set to False (default), this function returns a
707 case-insensitive dictionary with item, attribute, and call access
708 to variables. If `python_dict` is set to True, this function
709 returns a Python dictionary with all variable names in lowercase.
710 If `idict` was specified, then variables are written to the
711 dictionary specified, and the updated dictionary is returned.
713 Examples
714 --------
715 >>> from os.path import dirname, join as pjoin
716 >>> import scipy.io as sio
717 >>> from scipy.io import readsav
719 Get the filename for an example .sav file from the tests/data directory.
721 >>> data_dir = pjoin(dirname(sio.__file__), 'tests', 'data')
722 >>> sav_fname = pjoin(data_dir, 'array_float32_1d.sav')
724 Load the .sav file contents.
726 >>> sav_data = readsav(sav_fname)
728 Get keys of the .sav file contents.
730 >>> print(sav_data.keys())
731 dict_keys(['array1d'])
733 Access a content with a key.
735 >>> print(sav_data['array1d'])
736 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
737 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
738 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
739 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
740 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
741 0. 0. 0.]
743 """
745 # Initialize record and variable holders
746 records = []
747 if python_dict or idict:
748 variables = {}
749 else:
750 variables = AttrDict()
752 # Open the IDL file
753 f = open(file_name, 'rb')
755 # Read the signature, which should be 'SR'
756 signature = _read_bytes(f, 2)
757 if signature != b'SR':
758 raise Exception("Invalid SIGNATURE: %s" % signature)
760 # Next, the record format, which is '\x00\x04' for normal .sav
761 # files, and '\x00\x06' for compressed .sav files.
762 recfmt = _read_bytes(f, 2)
764 if recfmt == b'\x00\x04':
765 pass
767 elif recfmt == b'\x00\x06':
769 if verbose:
770 print("IDL Save file is compressed")
772 if uncompressed_file_name:
773 fout = open(uncompressed_file_name, 'w+b')
774 else:
775 fout = tempfile.NamedTemporaryFile(suffix='.sav')
777 if verbose:
778 print(" -> expanding to %s" % fout.name)
780 # Write header
781 fout.write(b'SR\x00\x04')
783 # Cycle through records
784 while True:
786 # Read record type
787 rectype = _read_long(f)
788 fout.write(struct.pack('>l', int(rectype)))
790 # Read position of next record and return as int
791 nextrec = _read_uint32(f)
792 nextrec += _read_uint32(f).astype(np.int64) * 2**32
794 # Read the unknown 4 bytes
795 unknown = f.read(4)
797 # Check if the end of the file has been reached
798 if RECTYPE_DICT[rectype] == 'END_MARKER':
799 modval = np.int64(2**32)
800 fout.write(struct.pack('>I', int(nextrec) % modval))
801 fout.write(struct.pack('>I', int((nextrec - (nextrec % modval)) / modval)))
802 fout.write(unknown)
803 break
805 # Find current position
806 pos = f.tell()
808 # Decompress record
809 rec_string = zlib.decompress(f.read(nextrec-pos))
811 # Find new position of next record
812 nextrec = fout.tell() + len(rec_string) + 12
814 # Write out record
815 fout.write(struct.pack('>I', int(nextrec % 2**32)))
816 fout.write(struct.pack('>I', int((nextrec - (nextrec % 2**32)) / 2**32)))
817 fout.write(unknown)
818 fout.write(rec_string)
820 # Close the original compressed file
821 f.close()
823 # Set f to be the decompressed file, and skip the first four bytes
824 f = fout
825 f.seek(4)
827 else:
828 raise Exception("Invalid RECFMT: %s" % recfmt)
830 # Loop through records, and add them to the list
831 while True:
832 r = _read_record(f)
833 records.append(r)
834 if 'end' in r:
835 if r['end']:
836 break
838 # Close the file
839 f.close()
841 # Find heap data variables
842 heap = {}
843 for r in records:
844 if r['rectype'] == "HEAP_DATA":
845 heap[r['heap_index']] = r['data']
847 # Find all variables
848 for r in records:
849 if r['rectype'] == "VARIABLE":
850 replace, new = _replace_heap(r['data'], heap)
851 if replace:
852 r['data'] = new
853 variables[r['varname'].lower()] = r['data']
855 if verbose:
857 # Print out timestamp info about the file
858 for record in records:
859 if record['rectype'] == "TIMESTAMP":
860 print("-"*50)
861 print("Date: %s" % record['date'])
862 print("User: %s" % record['user'])
863 print("Host: %s" % record['host'])
864 break
866 # Print out version info about the file
867 for record in records:
868 if record['rectype'] == "VERSION":
869 print("-"*50)
870 print("Format: %s" % record['format'])
871 print("Architecture: %s" % record['arch'])
872 print("Operating System: %s" % record['os'])
873 print("IDL Version: %s" % record['release'])
874 break
876 # Print out identification info about the file
877 for record in records:
878 if record['rectype'] == "IDENTIFICATON":
879 print("-"*50)
880 print("Author: %s" % record['author'])
881 print("Title: %s" % record['title'])
882 print("ID Code: %s" % record['idcode'])
883 break
885 # Print out descriptions saved with the file
886 for record in records:
887 if record['rectype'] == "DESCRIPTION":
888 print("-"*50)
889 print("Description: %s" % record['description'])
890 break
892 print("-"*50)
893 print("Successfully read %i records of which:" %
894 (len(records)))
896 # Create convenience list of record types
897 rectypes = [r['rectype'] for r in records]
899 for rt in set(rectypes):
900 if rt != 'END_MARKER':
901 print(" - %i are of type %s" % (rectypes.count(rt), rt))
902 print("-"*50)
904 if 'VARIABLE' in rectypes:
905 print("Available variables:")
906 for var in variables:
907 print(f" - {var} [{type(variables[var])}]")
908 print("-"*50)
910 if idict:
911 for var in variables:
912 idict[var] = variables[var]
913 return idict
914 else:
915 return variables