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.4.4, created at 2024-03-22 06:44 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-03-22 06:44 +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",
280 stacklevel=3)
282 # Read bytes as numpy array
283 array = np.frombuffer(f.read(array_desc['nbytes']),
284 dtype=DTYPE_DICT[typecode])
286 elif typecode in [2, 12]:
288 # These are 2 byte types, need to skip every two as they are not packed
290 array = np.frombuffer(f.read(array_desc['nbytes']*2),
291 dtype=DTYPE_DICT[typecode])[1::2]
293 else:
295 # Read bytes into list
296 array = []
297 for i in range(array_desc['nelements']):
298 dtype = typecode
299 data = _read_data(f, dtype)
300 array.append(data)
302 array = np.array(array, dtype=np.object_)
304 # Reshape array if needed
305 if array_desc['ndims'] > 1:
306 dims = array_desc['dims'][:int(array_desc['ndims'])]
307 dims.reverse()
308 array = array.reshape(dims)
310 # Go to next alignment position
311 _align_32(f)
313 return array
316def _read_record(f):
317 '''Function to read in a full record'''
319 record = {'rectype': _read_long(f)}
321 nextrec = _read_uint32(f)
322 nextrec += _read_uint32(f).astype(np.int64) * 2**32
324 _skip_bytes(f, 4)
326 if record['rectype'] not in RECTYPE_DICT:
327 raise Exception("Unknown RECTYPE: %i" % record['rectype'])
329 record['rectype'] = RECTYPE_DICT[record['rectype']]
331 if record['rectype'] in ["VARIABLE", "HEAP_DATA"]:
333 if record['rectype'] == "VARIABLE":
334 record['varname'] = _read_string(f)
335 else:
336 record['heap_index'] = _read_long(f)
337 _skip_bytes(f, 4)
339 rectypedesc = _read_typedesc(f)
341 if rectypedesc['typecode'] == 0:
343 if nextrec == f.tell():
344 record['data'] = None # Indicates NULL value
345 else:
346 raise ValueError("Unexpected type code: 0")
348 else:
350 varstart = _read_long(f)
351 if varstart != 7:
352 raise Exception("VARSTART is not 7")
354 if rectypedesc['structure']:
355 record['data'] = _read_structure(f, rectypedesc['array_desc'],
356 rectypedesc['struct_desc'])
357 elif rectypedesc['array']:
358 record['data'] = _read_array(f, rectypedesc['typecode'],
359 rectypedesc['array_desc'])
360 else:
361 dtype = rectypedesc['typecode']
362 record['data'] = _read_data(f, dtype)
364 elif record['rectype'] == "TIMESTAMP":
366 _skip_bytes(f, 4*256)
367 record['date'] = _read_string(f)
368 record['user'] = _read_string(f)
369 record['host'] = _read_string(f)
371 elif record['rectype'] == "VERSION":
373 record['format'] = _read_long(f)
374 record['arch'] = _read_string(f)
375 record['os'] = _read_string(f)
376 record['release'] = _read_string(f)
378 elif record['rectype'] == "IDENTIFICATON":
380 record['author'] = _read_string(f)
381 record['title'] = _read_string(f)
382 record['idcode'] = _read_string(f)
384 elif record['rectype'] == "NOTICE":
386 record['notice'] = _read_string(f)
388 elif record['rectype'] == "DESCRIPTION":
390 record['description'] = _read_string_data(f)
392 elif record['rectype'] == "HEAP_HEADER":
394 record['nvalues'] = _read_long(f)
395 record['indices'] = [_read_long(f) for _ in range(record['nvalues'])]
397 elif record['rectype'] == "COMMONBLOCK":
399 record['nvars'] = _read_long(f)
400 record['name'] = _read_string(f)
401 record['varnames'] = [_read_string(f) for _ in range(record['nvars'])]
403 elif record['rectype'] == "END_MARKER":
405 record['end'] = True
407 elif record['rectype'] == "UNKNOWN":
409 warnings.warn("Skipping UNKNOWN record", stacklevel=3)
411 elif record['rectype'] == "SYSTEM_VARIABLE":
413 warnings.warn("Skipping SYSTEM_VARIABLE record", stacklevel=3)
415 else:
417 raise Exception(f"record['rectype']={record['rectype']} not implemented")
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", stacklevel=3)
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 stacklevel=3)
579 variable = None
581 replace, new = _replace_heap(variable, heap)
583 if replace:
584 variable = new
586 return True, variable
588 elif isinstance(variable, np.rec.recarray):
590 # Loop over records
591 for ir, record in enumerate(variable):
593 replace, new = _replace_heap(record, heap)
595 if replace:
596 variable[ir] = new
598 return False, variable
600 elif isinstance(variable, np.record):
602 # Loop over values
603 for iv, value in enumerate(variable):
605 replace, new = _replace_heap(value, heap)
607 if replace:
608 variable[iv] = new
610 return False, variable
612 elif isinstance(variable, np.ndarray):
614 # Loop over values if type is np.object_
615 if variable.dtype.type is np.object_:
617 for iv in range(variable.size):
619 replace, new = _replace_heap(variable.item(iv), heap)
621 if replace:
622 variable.reshape(-1)[iv] = new
624 return False, variable
626 else:
628 return False, variable
631class AttrDict(dict):
632 '''
633 A case-insensitive dictionary with access via item, attribute, and call
634 notations:
636 >>> from scipy.io._idl import AttrDict
637 >>> d = AttrDict()
638 >>> d['Variable'] = 123
639 >>> d['Variable']
640 123
641 >>> d.Variable
642 123
643 >>> d.variable
644 123
645 >>> d('VARIABLE')
646 123
647 >>> d['missing']
648 Traceback (most recent error last):
649 ...
650 KeyError: 'missing'
651 >>> d.missing
652 Traceback (most recent error last):
653 ...
654 AttributeError: 'AttrDict' object has no attribute 'missing'
655 '''
657 def __init__(self, init={}):
658 dict.__init__(self, init)
660 def __getitem__(self, name):
661 return super().__getitem__(name.lower())
663 def __setitem__(self, key, value):
664 return super().__setitem__(key.lower(), value)
666 def __getattr__(self, name):
667 try:
668 return self.__getitem__(name)
669 except KeyError:
670 raise AttributeError(
671 f"'{type(self)}' object has no attribute '{name}'") from None
673 __setattr__ = __setitem__
674 __call__ = __getitem__
677def readsav(file_name, idict=None, python_dict=False,
678 uncompressed_file_name=None, verbose=False):
679 """
680 Read an IDL .sav file.
682 Parameters
683 ----------
684 file_name : str
685 Name of the IDL save file.
686 idict : dict, optional
687 Dictionary in which to insert .sav file variables.
688 python_dict : bool, optional
689 By default, the object return is not a Python dictionary, but a
690 case-insensitive dictionary with item, attribute, and call access
691 to variables. To get a standard Python dictionary, set this option
692 to True.
693 uncompressed_file_name : str, optional
694 This option only has an effect for .sav files written with the
695 /compress option. If a file name is specified, compressed .sav
696 files are uncompressed to this file. Otherwise, readsav will use
697 the `tempfile` module to determine a temporary filename
698 automatically, and will remove the temporary file upon successfully
699 reading it in.
700 verbose : bool, optional
701 Whether to print out information about the save file, including
702 the records read, and available variables.
704 Returns
705 -------
706 idl_dict : AttrDict or dict
707 If `python_dict` is set to False (default), this function returns a
708 case-insensitive dictionary with item, attribute, and call access
709 to variables. If `python_dict` is set to True, this function
710 returns a Python dictionary with all variable names in lowercase.
711 If `idict` was specified, then variables are written to the
712 dictionary specified, and the updated dictionary is returned.
714 Examples
715 --------
716 >>> from os.path import dirname, join as pjoin
717 >>> import scipy.io as sio
718 >>> from scipy.io import readsav
720 Get the filename for an example .sav file from the tests/data directory.
722 >>> data_dir = pjoin(dirname(sio.__file__), 'tests', 'data')
723 >>> sav_fname = pjoin(data_dir, 'array_float32_1d.sav')
725 Load the .sav file contents.
727 >>> sav_data = readsav(sav_fname)
729 Get keys of the .sav file contents.
731 >>> print(sav_data.keys())
732 dict_keys(['array1d'])
734 Access a content with a key.
736 >>> print(sav_data['array1d'])
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. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.
742 0. 0. 0.]
744 """
746 # Initialize record and variable holders
747 records = []
748 if python_dict or idict:
749 variables = {}
750 else:
751 variables = AttrDict()
753 # Open the IDL file
754 f = open(file_name, 'rb')
756 # Read the signature, which should be 'SR'
757 signature = _read_bytes(f, 2)
758 if signature != b'SR':
759 raise Exception("Invalid SIGNATURE: %s" % signature)
761 # Next, the record format, which is '\x00\x04' for normal .sav
762 # files, and '\x00\x06' for compressed .sav files.
763 recfmt = _read_bytes(f, 2)
765 if recfmt == b'\x00\x04':
766 pass
768 elif recfmt == b'\x00\x06':
770 if verbose:
771 print("IDL Save file is compressed")
773 if uncompressed_file_name:
774 fout = open(uncompressed_file_name, 'w+b')
775 else:
776 fout = tempfile.NamedTemporaryFile(suffix='.sav')
778 if verbose:
779 print(" -> expanding to %s" % fout.name)
781 # Write header
782 fout.write(b'SR\x00\x04')
784 # Cycle through records
785 while True:
787 # Read record type
788 rectype = _read_long(f)
789 fout.write(struct.pack('>l', int(rectype)))
791 # Read position of next record and return as int
792 nextrec = _read_uint32(f)
793 nextrec += _read_uint32(f).astype(np.int64) * 2**32
795 # Read the unknown 4 bytes
796 unknown = f.read(4)
798 # Check if the end of the file has been reached
799 if RECTYPE_DICT[rectype] == 'END_MARKER':
800 modval = np.int64(2**32)
801 fout.write(struct.pack('>I', int(nextrec) % modval))
802 fout.write(
803 struct.pack('>I', int((nextrec - (nextrec % modval)) / modval))
804 )
805 fout.write(unknown)
806 break
808 # Find current position
809 pos = f.tell()
811 # Decompress record
812 rec_string = zlib.decompress(f.read(nextrec-pos))
814 # Find new position of next record
815 nextrec = fout.tell() + len(rec_string) + 12
817 # Write out record
818 fout.write(struct.pack('>I', int(nextrec % 2**32)))
819 fout.write(struct.pack('>I', int((nextrec - (nextrec % 2**32)) / 2**32)))
820 fout.write(unknown)
821 fout.write(rec_string)
823 # Close the original compressed file
824 f.close()
826 # Set f to be the decompressed file, and skip the first four bytes
827 f = fout
828 f.seek(4)
830 else:
831 raise Exception("Invalid RECFMT: %s" % recfmt)
833 # Loop through records, and add them to the list
834 while True:
835 r = _read_record(f)
836 records.append(r)
837 if 'end' in r:
838 if r['end']:
839 break
841 # Close the file
842 f.close()
844 # Find heap data variables
845 heap = {}
846 for r in records:
847 if r['rectype'] == "HEAP_DATA":
848 heap[r['heap_index']] = r['data']
850 # Find all variables
851 for r in records:
852 if r['rectype'] == "VARIABLE":
853 replace, new = _replace_heap(r['data'], heap)
854 if replace:
855 r['data'] = new
856 variables[r['varname'].lower()] = r['data']
858 if verbose:
860 # Print out timestamp info about the file
861 for record in records:
862 if record['rectype'] == "TIMESTAMP":
863 print("-"*50)
864 print("Date: %s" % record['date'])
865 print("User: %s" % record['user'])
866 print("Host: %s" % record['host'])
867 break
869 # Print out version info about the file
870 for record in records:
871 if record['rectype'] == "VERSION":
872 print("-"*50)
873 print("Format: %s" % record['format'])
874 print("Architecture: %s" % record['arch'])
875 print("Operating System: %s" % record['os'])
876 print("IDL Version: %s" % record['release'])
877 break
879 # Print out identification info about the file
880 for record in records:
881 if record['rectype'] == "IDENTIFICATON":
882 print("-"*50)
883 print("Author: %s" % record['author'])
884 print("Title: %s" % record['title'])
885 print("ID Code: %s" % record['idcode'])
886 break
888 # Print out descriptions saved with the file
889 for record in records:
890 if record['rectype'] == "DESCRIPTION":
891 print("-"*50)
892 print("Description: %s" % record['description'])
893 break
895 print("-"*50)
896 print("Successfully read %i records of which:" %
897 (len(records)))
899 # Create convenience list of record types
900 rectypes = [r['rectype'] for r in records]
902 for rt in set(rectypes):
903 if rt != 'END_MARKER':
904 print(" - %i are of type %s" % (rectypes.count(rt), rt))
905 print("-"*50)
907 if 'VARIABLE' in rectypes:
908 print("Available variables:")
909 for var in variables:
910 print(f" - {var} [{type(variables[var])}]")
911 print("-"*50)
913 if idict:
914 for var in variables:
915 idict[var] = variables[var]
916 return idict
917 else:
918 return variables