19
19
import os.path as osp
24
from spyderlib.py3compat import pickle, to_text_string, getcwd
31
from spyderlib.py3compat import pickle, to_text_string, getcwd, PY2
34
class MatlabStruct(dict):
36
Matlab style struct, enhanced.
38
Supports dictionary and attribute style access. Can be pickled,
39
and supports code completion in a REPL.
43
>>> from spyderlib.utils.iofuncs import MatlabStruct
44
>>> a = MatlabStruct()
45
>>> a.b = 'spam' # a["b"] == 'spam'
46
>>> a.c["d"] = 'eggs' # a.c.d == 'eggs'
48
{'c': {'d': 'eggs'}, 'b': 'spam'}
51
def __getattr__(self, attr):
52
"""Access the dictionary keys for unknown attributes."""
56
msg = "'MatlabStruct' object has no attribute %s" % attr
57
raise AttributeError(msg)
59
def __getitem__(self, attr):
61
Get a dict value; create a MatlabStruct if requesting a submember.
63
Do not create a key if the attribute starts with an underscore.
65
if attr in self.keys() or attr.startswith('_'):
66
return dict.__getitem__(self, attr)
67
frame = inspect.currentframe()
68
# step into the function that called us
69
if frame.f_back.f_back and self._is_allowed(frame.f_back.f_back):
70
dict.__setitem__(self, attr, MatlabStruct())
71
elif self._is_allowed(frame.f_back):
72
dict.__setitem__(self, attr, MatlabStruct())
73
return dict.__getitem__(self, attr)
75
def _is_allowed(self, frame):
76
"""Check for allowed op code in the calling frame"""
77
allowed = [dis.opmap['STORE_ATTR'], dis.opmap['LOAD_CONST'],
78
dis.opmap.get('STOP_CODE', 0)]
79
bytecode = frame.f_code.co_code
80
instruction = bytecode[frame.f_lasti + 3]
81
instruction = ord(instruction) if PY2 else instruction
82
return instruction in allowed
84
__setattr__ = dict.__setitem__
85
__delattr__ = dict.__delitem__
89
"""Allow for code completion in a REPL"""
93
def get_matlab_value(val):
95
Extract a value from a Matlab file
97
From the oct2py project, see
98
http://pythonhosted.org/oct2py/conversions.html
101
if not isinstance(val, np.ndarray):
104
if "'|O" in str(val.dtype) or "O'" in str(val.dtype):
105
data = MatlabStruct()
106
for key in val.dtype.fields.keys():
107
data[key] = get_matlab_value(val[key][0])
110
if val.dtype == np.object:
113
if "'|O" in str(val.dtype) or "O'" in str(val.dtype):
114
val = get_matlab_value(val)
115
if isinstance(val, MatlabStruct):
119
if val.dtype == np.object:
120
if len(val.shape) > 2:
122
val = np.array([get_matlab_value(val[i].T)
123
for i in range(val.shape[0])])
124
if len(val.shape) > 1:
125
if len(val.shape) == 2:
128
return val.astype(val[0][0].dtype)
130
# dig into the cell type
131
for row in range(val.shape[0]):
132
for i in range(val[row].size):
133
if not np.isscalar(val[row][i]):
134
if val[row][i].size > 1:
135
val[row][i] = val[row][i].squeeze()
137
val[row][i] = val[row][i][0]
139
val = np.array([get_matlab_value(val[i])
140
for i in range(val.size)])
141
if len(val.shape) == 1 or val.shape[0] == 1 or val.shape[1] == 1:
145
if hasattr(val, 'flatten'):
146
val = val.flatten()[0]
147
if isinstance(val, MatlabStruct) and isinstance(val.size, MatlabStruct):
35
161
import scipy.io as spio # analysis:ignore
36
162
def load_matlab(filename):
38
out = spio.loadmat(filename, struct_as_record=True,
164
out = spio.loadmat(filename)
40
165
for key, value in list(out.items()):
41
if isinstance(value, np.ndarray):
43
out[key] = value.tolist()
44
# The following would be needed if squeeze_me=False:
45
# elif value.shape == (1,):
47
# elif value.shape == (1, 1):
48
# out[key] = value[0][0]
166
out[key] = get_matlab_value(value)
50
168
except Exception as error:
51
169
return None, str(error)
330
475
save_funcs[ext] = savefunc
331
476
load_filters.insert(0, to_text_string(_("Supported files")+" (*"+\
332
477
" *".join(load_ext)+")"))
478
load_filters.append(to_text_string(_("All files (*.*)")))
333
479
self.load_filters = "\n".join(load_filters)
334
480
self.save_filters = "\n".join(save_filters)
335
481
self.load_funcs = load_funcs
336
482
self.save_funcs = save_funcs
337
483
self.load_extensions = load_extensions
338
484
self.save_extensions = save_extensions
340
486
def get_internal_funcs(self):
342
488
('.spydata', _("Spyder data files"),
343
489
load_dictionary, save_dictionary),
344
490
('.npy', _("NumPy arrays"), load_array, None),
491
('.npz', _("NumPy zip arrays"), load_array, None),
345
492
('.mat', _("Matlab files"), load_matlab, save_matlab),
346
493
('.csv', _("CSV text files"), 'import_wizard', None),
347
494
('.txt', _("Text files"), 'import_wizard', None),
349
496
('.png', _("PNG images"), load_image, None),
350
497
('.gif', _("GIF images"), load_image, None),
351
498
('.tif', _("TIFF images"), load_image, None),
499
('.pkl', _("Pickle files"), load_pickle, None),
500
('.pickle', _("Pickle files"), load_pickle, None),
501
('.json', _("JSON files"), load_json, None),
354
504
def get_3rd_party_funcs(self):
356
506
from spyderlib.otherplugins import get_spyderplugins_mods
361
511
except AttributeError as error:
362
512
print("%s: %s" % (mod, str(error)), file=STDERR)
363
513
return other_funcs
365
515
def save(self, data, filename):
366
516
ext = osp.splitext(filename)[1].lower()
367
517
if ext in self.save_funcs:
368
518
return self.save_funcs[ext](data, filename)
370
520
return _("<b>Unsupported file type '%s'</b>") % ext
372
522
def load(self, filename):
373
523
ext = osp.splitext(filename)[1].lower()
374
524
if ext in self.load_funcs: