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

1# IDLSave - a python module to read IDL 'save' files 

2# Copyright (c) 2010 Thomas P. Robitaille 

3 

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). 

7 

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. 

11 

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: 

18 

19# The above copyright notice and this permission notice shall be included in 

20# all copies or substantial portions of the Software. 

21 

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. 

29 

30__all__ = ['readsav'] 

31 

32import struct 

33import numpy as np 

34import tempfile 

35import zlib 

36import warnings 

37 

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'} 

54 

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"} 

70 

71# Define a dictionary to contain structure definitions 

72STRUCT_DICT = {} 

73 

74 

75def _align_32(f): 

76 '''Align to the next 32-bit position in a file''' 

77 

78 pos = f.tell() 

79 if pos % 4 != 0: 

80 f.seek(pos + 4 - pos % 4) 

81 return 

82 

83 

84def _skip_bytes(f, n): 

85 '''Skip `n` bytes''' 

86 f.read(n) 

87 return 

88 

89 

90def _read_bytes(f, n): 

91 '''Read the next `n` bytes''' 

92 return f.read(n) 

93 

94 

95def _read_byte(f): 

96 '''Read a single byte''' 

97 return np.uint8(struct.unpack('>B', f.read(4)[:1])[0]) 

98 

99 

100def _read_long(f): 

101 '''Read a signed 32-bit integer''' 

102 return np.int32(struct.unpack('>l', f.read(4))[0]) 

103 

104 

105def _read_int16(f): 

106 '''Read a signed 16-bit integer''' 

107 return np.int16(struct.unpack('>h', f.read(4)[2:4])[0]) 

108 

109 

110def _read_int32(f): 

111 '''Read a signed 32-bit integer''' 

112 return np.int32(struct.unpack('>i', f.read(4))[0]) 

113 

114 

115def _read_int64(f): 

116 '''Read a signed 64-bit integer''' 

117 return np.int64(struct.unpack('>q', f.read(8))[0]) 

118 

119 

120def _read_uint16(f): 

121 '''Read an unsigned 16-bit integer''' 

122 return np.uint16(struct.unpack('>H', f.read(4)[2:4])[0]) 

123 

124 

125def _read_uint32(f): 

126 '''Read an unsigned 32-bit integer''' 

127 return np.uint32(struct.unpack('>I', f.read(4))[0]) 

128 

129 

130def _read_uint64(f): 

131 '''Read an unsigned 64-bit integer''' 

132 return np.uint64(struct.unpack('>Q', f.read(8))[0]) 

133 

134 

135def _read_float32(f): 

136 '''Read a 32-bit float''' 

137 return np.float32(struct.unpack('>f', f.read(4))[0]) 

138 

139 

140def _read_float64(f): 

141 '''Read a 64-bit float''' 

142 return np.float64(struct.unpack('>d', f.read(8))[0]) 

143 

144 

145class Pointer: 

146 '''Class used to define pointers''' 

147 

148 def __init__(self, index): 

149 self.index = index 

150 return 

151 

152 

153class ObjectPointer(Pointer): 

154 '''Class used to define object pointers''' 

155 pass 

156 

157 

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 

167 

168 

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 

179 

180 

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) 

221 

222 

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 ''' 

228 

229 nrows = array_desc['nelements'] 

230 columns = struct_desc['tagtable'] 

231 

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']) 

243 

244 structure = np.rec.recarray((nrows, ), dtype=dtype) 

245 

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) 

258 

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) 

264 

265 return structure 

266 

267 

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 ''' 

273 

274 if typecode in [1, 3, 4, 5, 6, 9, 13, 14, 15]: 

275 

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 

281 # Read bytes as numpy array 

282 array = np.frombuffer(f.read(array_desc['nbytes']), 

283 dtype=DTYPE_DICT[typecode]) 

284 

285 elif typecode in [2, 12]: 

286 

287 # These are 2 byte types, need to skip every two as they are not packed 

288 

289 array = np.frombuffer(f.read(array_desc['nbytes']*2), 

290 dtype=DTYPE_DICT[typecode])[1::2] 

291 

292 else: 

293 

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) 

300 

301 array = np.array(array, dtype=np.object_) 

302 

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) 

308 

309 # Go to next alignment position 

310 _align_32(f) 

311 

312 return array 

313 

314 

315def _read_record(f): 

316 '''Function to read in a full record''' 

317 

318 record = {'rectype': _read_long(f)} 

319 

320 nextrec = _read_uint32(f) 

321 nextrec += _read_uint32(f).astype(np.int64) * 2**32 

322 

323 _skip_bytes(f, 4) 

324 

325 if record['rectype'] not in RECTYPE_DICT: 

326 raise Exception("Unknown RECTYPE: %i" % record['rectype']) 

327 

328 record['rectype'] = RECTYPE_DICT[record['rectype']] 

329 

330 if record['rectype'] in ["VARIABLE", "HEAP_DATA"]: 

331 

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) 

337 

338 rectypedesc = _read_typedesc(f) 

339 

340 if rectypedesc['typecode'] == 0: 

341 

342 if nextrec == f.tell(): 

343 record['data'] = None # Indicates NULL value 

344 else: 

345 raise ValueError("Unexpected type code: 0") 

346 

347 else: 

348 

349 varstart = _read_long(f) 

350 if varstart != 7: 

351 raise Exception("VARSTART is not 7") 

352 

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) 

362 

363 elif record['rectype'] == "TIMESTAMP": 

364 

365 _skip_bytes(f, 4*256) 

366 record['date'] = _read_string(f) 

367 record['user'] = _read_string(f) 

368 record['host'] = _read_string(f) 

369 

370 elif record['rectype'] == "VERSION": 

371 

372 record['format'] = _read_long(f) 

373 record['arch'] = _read_string(f) 

374 record['os'] = _read_string(f) 

375 record['release'] = _read_string(f) 

376 

377 elif record['rectype'] == "IDENTIFICATON": 

378 

379 record['author'] = _read_string(f) 

380 record['title'] = _read_string(f) 

381 record['idcode'] = _read_string(f) 

382 

383 elif record['rectype'] == "NOTICE": 

384 

385 record['notice'] = _read_string(f) 

386 

387 elif record['rectype'] == "DESCRIPTION": 

388 

389 record['description'] = _read_string_data(f) 

390 

391 elif record['rectype'] == "HEAP_HEADER": 

392 

393 record['nvalues'] = _read_long(f) 

394 record['indices'] = [_read_long(f) for _ in range(record['nvalues'])] 

395 

396 elif record['rectype'] == "COMMONBLOCK": 

397 

398 record['nvars'] = _read_long(f) 

399 record['name'] = _read_string(f) 

400 record['varnames'] = [_read_string(f) for _ in range(record['nvars'])] 

401 

402 elif record['rectype'] == "END_MARKER": 

403 

404 record['end'] = True 

405 

406 elif record['rectype'] == "UNKNOWN": 

407 

408 warnings.warn("Skipping UNKNOWN record") 

409 

410 elif record['rectype'] == "SYSTEM_VARIABLE": 

411 

412 warnings.warn("Skipping SYSTEM_VARIABLE record") 

413 

414 else: 

415 

416 raise Exception("record['rectype']=%s not implemented" % 

417 record['rectype']) 

418 

419 f.seek(nextrec) 

420 

421 return record 

422 

423 

424def _read_typedesc(f): 

425 '''Function to read in a type descriptor''' 

426 

427 typedesc = {'typecode': _read_long(f), 'varflags': _read_long(f)} 

428 

429 if typedesc['varflags'] & 2 == 2: 

430 raise Exception("System variables not implemented") 

431 

432 typedesc['array'] = typedesc['varflags'] & 4 == 4 

433 typedesc['structure'] = typedesc['varflags'] & 32 == 32 

434 

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) 

440 

441 return typedesc 

442 

443 

444def _read_arraydesc(f): 

445 '''Function to read in an array descriptor''' 

446 

447 arraydesc = {'arrstart': _read_long(f)} 

448 

449 if arraydesc['arrstart'] == 8: 

450 

451 _skip_bytes(f, 4) 

452 

453 arraydesc['nbytes'] = _read_long(f) 

454 arraydesc['nelements'] = _read_long(f) 

455 arraydesc['ndims'] = _read_long(f) 

456 

457 _skip_bytes(f, 8) 

458 

459 arraydesc['nmax'] = _read_long(f) 

460 

461 arraydesc['dims'] = [_read_long(f) for _ in range(arraydesc['nmax'])] 

462 

463 elif arraydesc['arrstart'] == 18: 

464 

465 warnings.warn("Using experimental 64-bit array read") 

466 

467 _skip_bytes(f, 8) 

468 

469 arraydesc['nbytes'] = _read_uint64(f) 

470 arraydesc['nelements'] = _read_uint64(f) 

471 arraydesc['ndims'] = _read_long(f) 

472 

473 _skip_bytes(f, 8) 

474 

475 arraydesc['nmax'] = 8 

476 

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)) 

483 

484 else: 

485 

486 raise Exception("Unknown ARRSTART: %i" % arraydesc['arrstart']) 

487 

488 return arraydesc 

489 

490 

491def _read_structdesc(f): 

492 '''Function to read in a structure descriptor''' 

493 

494 structdesc = {} 

495 

496 structstart = _read_long(f) 

497 if structstart != 9: 

498 raise Exception("STRUCTSTART should be 9") 

499 

500 structdesc['name'] = _read_string(f) 

501 predef = _read_long(f) 

502 structdesc['ntags'] = _read_long(f) 

503 structdesc['nbytes'] = _read_long(f) 

504 

505 structdesc['predef'] = predef & 1 

506 structdesc['inherits'] = predef & 2 

507 structdesc['is_super'] = predef & 4 

508 

509 if not structdesc['predef']: 

510 

511 structdesc['tagtable'] = [_read_tagdesc(f) 

512 for _ in range(structdesc['ntags'])] 

513 

514 for tag in structdesc['tagtable']: 

515 tag['name'] = _read_string(f) 

516 

517 structdesc['arrtable'] = {tag['name']: _read_arraydesc(f) 

518 for tag in structdesc['tagtable'] 

519 if tag['array']} 

520 

521 structdesc['structtable'] = {tag['name']: _read_structdesc(f) 

522 for tag in structdesc['tagtable'] 

523 if tag['structure']} 

524 

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'])] 

532 

533 STRUCT_DICT[structdesc['name']] = structdesc 

534 

535 else: 

536 

537 if structdesc['name'] not in STRUCT_DICT: 

538 raise Exception("PREDEF=1 but can't find definition") 

539 

540 structdesc = STRUCT_DICT[structdesc['name']] 

541 

542 return structdesc 

543 

544 

545def _read_tagdesc(f): 

546 '''Function to read in a tag descriptor''' 

547 

548 tagdesc = {'offset': _read_long(f)} 

549 

550 if tagdesc['offset'] == -1: 

551 tagdesc['offset'] = _read_uint64(f) 

552 

553 tagdesc['typecode'] = _read_long(f) 

554 tagflags = _read_long(f) 

555 

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 

560 

561 return tagdesc 

562 

563 

564def _replace_heap(variable, heap): 

565 

566 if isinstance(variable, Pointer): 

567 

568 while isinstance(variable, Pointer): 

569 

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 

579 

580 replace, new = _replace_heap(variable, heap) 

581 

582 if replace: 

583 variable = new 

584 

585 return True, variable 

586 

587 elif isinstance(variable, np.rec.recarray): 

588 

589 # Loop over records 

590 for ir, record in enumerate(variable): 

591 

592 replace, new = _replace_heap(record, heap) 

593 

594 if replace: 

595 variable[ir] = new 

596 

597 return False, variable 

598 

599 elif isinstance(variable, np.record): 

600 

601 # Loop over values 

602 for iv, value in enumerate(variable): 

603 

604 replace, new = _replace_heap(value, heap) 

605 

606 if replace: 

607 variable[iv] = new 

608 

609 return False, variable 

610 

611 elif isinstance(variable, np.ndarray): 

612 

613 # Loop over values if type is np.object_ 

614 if variable.dtype.type is np.object_: 

615 

616 for iv in range(variable.size): 

617 

618 replace, new = _replace_heap(variable.item(iv), heap) 

619 

620 if replace: 

621 variable.reshape(-1)[iv] = new 

622 

623 return False, variable 

624 

625 else: 

626 

627 return False, variable 

628 

629 

630class AttrDict(dict): 

631 ''' 

632 A case-insensitive dictionary with access via item, attribute, and call 

633 notations: 

634 

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 ''' 

655 

656 def __init__(self, init={}): 

657 dict.__init__(self, init) 

658 

659 def __getitem__(self, name): 

660 return super().__getitem__(name.lower()) 

661 

662 def __setitem__(self, key, value): 

663 return super().__setitem__(key.lower(), value) 

664 

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 

671 

672 __setattr__ = __setitem__ 

673 __call__ = __getitem__ 

674 

675 

676def readsav(file_name, idict=None, python_dict=False, 

677 uncompressed_file_name=None, verbose=False): 

678 """ 

679 Read an IDL .sav file. 

680 

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. 

702 

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. 

712 

713 Examples 

714 -------- 

715 >>> from os.path import dirname, join as pjoin 

716 >>> import scipy.io as sio 

717 >>> from scipy.io import readsav 

718 

719 Get the filename for an example .sav file from the tests/data directory. 

720 

721 >>> data_dir = pjoin(dirname(sio.__file__), 'tests', 'data') 

722 >>> sav_fname = pjoin(data_dir, 'array_float32_1d.sav') 

723 

724 Load the .sav file contents. 

725 

726 >>> sav_data = readsav(sav_fname) 

727 

728 Get keys of the .sav file contents. 

729 

730 >>> print(sav_data.keys()) 

731 dict_keys(['array1d']) 

732 

733 Access a content with a key. 

734 

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.] 

742 

743 """ 

744 

745 # Initialize record and variable holders 

746 records = [] 

747 if python_dict or idict: 

748 variables = {} 

749 else: 

750 variables = AttrDict() 

751 

752 # Open the IDL file 

753 f = open(file_name, 'rb') 

754 

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) 

759 

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) 

763 

764 if recfmt == b'\x00\x04': 

765 pass 

766 

767 elif recfmt == b'\x00\x06': 

768 

769 if verbose: 

770 print("IDL Save file is compressed") 

771 

772 if uncompressed_file_name: 

773 fout = open(uncompressed_file_name, 'w+b') 

774 else: 

775 fout = tempfile.NamedTemporaryFile(suffix='.sav') 

776 

777 if verbose: 

778 print(" -> expanding to %s" % fout.name) 

779 

780 # Write header 

781 fout.write(b'SR\x00\x04') 

782 

783 # Cycle through records 

784 while True: 

785 

786 # Read record type 

787 rectype = _read_long(f) 

788 fout.write(struct.pack('>l', int(rectype))) 

789 

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 

793 

794 # Read the unknown 4 bytes 

795 unknown = f.read(4) 

796 

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 

804 

805 # Find current position 

806 pos = f.tell() 

807 

808 # Decompress record 

809 rec_string = zlib.decompress(f.read(nextrec-pos)) 

810 

811 # Find new position of next record 

812 nextrec = fout.tell() + len(rec_string) + 12 

813 

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) 

819 

820 # Close the original compressed file 

821 f.close() 

822 

823 # Set f to be the decompressed file, and skip the first four bytes 

824 f = fout 

825 f.seek(4) 

826 

827 else: 

828 raise Exception("Invalid RECFMT: %s" % recfmt) 

829 

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 

837 

838 # Close the file 

839 f.close() 

840 

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'] 

846 

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'] 

854 

855 if verbose: 

856 

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 

865 

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 

875 

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 

884 

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 

891 

892 print("-"*50) 

893 print("Successfully read %i records of which:" % 

894 (len(records))) 

895 

896 # Create convenience list of record types 

897 rectypes = [r['rectype'] for r in records] 

898 

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) 

903 

904 if 'VARIABLE' in rectypes: 

905 print("Available variables:") 

906 for var in variables: 

907 print(f" - {var} [{type(variables[var])}]") 

908 print("-"*50) 

909 

910 if idict: 

911 for var in variables: 

912 idict[var] = variables[var] 

913 return idict 

914 else: 

915 return variables