PageRenderTime 60ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/spyderlib/utils/iofuncs.py

https://code.google.com/p/spyderlib/
Python | 569 lines | 560 code | 1 blank | 8 comment | 0 complexity | 4c3584508f839e9c6752a4ec954742be MD5 | raw file
Possible License(s): CC-BY-3.0
  1. # -*- coding:utf-8 -*-
  2. #
  3. # Copyright © 2009-2010 Pierre Raybaut
  4. # Licensed under the terms of the MIT License
  5. # (see spyderlib/__init__.py for details)
  6. """
  7. Input/Output Utilities
  8. Note: 'load' functions has to return a dictionary from which a globals()
  9. namespace may be updated
  10. """
  11. from __future__ import print_function
  12. import sys
  13. import os
  14. import tarfile
  15. import os.path as osp
  16. import shutil
  17. import warnings
  18. import json
  19. import inspect
  20. import dis
  21. try:
  22. import pandas as pd
  23. except ImportError:
  24. pd = None
  25. # Local imports
  26. from spyderlib.py3compat import pickle, to_text_string, getcwd, PY2
  27. class MatlabStruct(dict):
  28. """
  29. Matlab style struct, enhanced.
  30. Supports dictionary and attribute style access. Can be pickled,
  31. and supports code completion in a REPL.
  32. Examples
  33. ========
  34. >>> from spyderlib.utils.iofuncs import MatlabStruct
  35. >>> a = MatlabStruct()
  36. >>> a.b = 'spam' # a["b"] == 'spam'
  37. >>> a.c["d"] = 'eggs' # a.c.d == 'eggs'
  38. >>> print(a)
  39. {'c': {'d': 'eggs'}, 'b': 'spam'}
  40. """
  41. def __getattr__(self, attr):
  42. """Access the dictionary keys for unknown attributes."""
  43. try:
  44. return self[attr]
  45. except KeyError:
  46. msg = "'MatlabStruct' object has no attribute %s" % attr
  47. raise AttributeError(msg)
  48. def __getitem__(self, attr):
  49. """
  50. Get a dict value; create a MatlabStruct if requesting a submember.
  51. Do not create a key if the attribute starts with an underscore.
  52. """
  53. if attr in self.keys() or attr.startswith('_'):
  54. return dict.__getitem__(self, attr)
  55. frame = inspect.currentframe()
  56. # step into the function that called us
  57. if frame.f_back.f_back and self._is_allowed(frame.f_back.f_back):
  58. dict.__setitem__(self, attr, MatlabStruct())
  59. elif self._is_allowed(frame.f_back):
  60. dict.__setitem__(self, attr, MatlabStruct())
  61. return dict.__getitem__(self, attr)
  62. def _is_allowed(self, frame):
  63. """Check for allowed op code in the calling frame"""
  64. allowed = [dis.opmap['STORE_ATTR'], dis.opmap['LOAD_CONST'],
  65. dis.opmap.get('STOP_CODE', 0)]
  66. bytecode = frame.f_code.co_code
  67. instruction = bytecode[frame.f_lasti + 3]
  68. instruction = ord(instruction) if PY2 else instruction
  69. return instruction in allowed
  70. __setattr__ = dict.__setitem__
  71. __delattr__ = dict.__delitem__
  72. @property
  73. def __dict__(self):
  74. """Allow for code completion in a REPL"""
  75. return self.copy()
  76. def get_matlab_value(val):
  77. """
  78. Extract a value from a Matlab file
  79. From the oct2py project, see
  80. http://pythonhosted.org/oct2py/conversions.html
  81. """
  82. import numpy as np
  83. if not isinstance(val, np.ndarray):
  84. return val
  85. # check for objects
  86. if "'|O" in str(val.dtype) or "O'" in str(val.dtype):
  87. data = MatlabStruct()
  88. for key in val.dtype.fields.keys():
  89. data[key] = get_matlab_value(val[key][0])
  90. return data
  91. # handle cell arrays
  92. if val.dtype == np.object:
  93. if val.size == 1:
  94. val = val[0]
  95. if "'|O" in str(val.dtype) or "O'" in str(val.dtype):
  96. val = get_matlab_value(val)
  97. if isinstance(val, MatlabStruct):
  98. return val
  99. if val.size == 1:
  100. val = val.flatten()
  101. if val.dtype == np.object:
  102. if len(val.shape) > 2:
  103. val = val.T
  104. val = np.array([get_matlab_value(val[i].T)
  105. for i in range(val.shape[0])])
  106. if len(val.shape) > 1:
  107. if len(val.shape) == 2:
  108. val = val.T
  109. try:
  110. return val.astype(val[0][0].dtype)
  111. except ValueError:
  112. # dig into the cell type
  113. for row in range(val.shape[0]):
  114. for i in range(val[row].size):
  115. if not np.isscalar(val[row][i]):
  116. if val[row][i].size > 1:
  117. val[row][i] = val[row][i].squeeze()
  118. else:
  119. val[row][i] = val[row][i][0]
  120. else:
  121. val = np.array([get_matlab_value(val[i])
  122. for i in range(val.size)])
  123. if len(val.shape) == 1 or val.shape[0] == 1 or val.shape[1] == 1:
  124. val = val.flatten()
  125. val = val.tolist()
  126. elif val.size == 1:
  127. if hasattr(val, 'flatten'):
  128. val = val.flatten()[0]
  129. if isinstance(val, MatlabStruct) and isinstance(val.size, MatlabStruct):
  130. del val['size']
  131. del val['dtype']
  132. return val
  133. try:
  134. import numpy as np
  135. try:
  136. with warnings.catch_warnings():
  137. warnings.simplefilter("ignore")
  138. import scipy.io as spio
  139. except AttributeError:
  140. # Python 2.5: warnings.catch_warnings was introduced in Python 2.6
  141. import scipy.io as spio # analysis:ignore
  142. def load_matlab(filename):
  143. try:
  144. out = spio.loadmat(filename)
  145. for key, value in list(out.items()):
  146. out[key] = get_matlab_value(value)
  147. return out, None
  148. except Exception as error:
  149. return None, str(error)
  150. def save_matlab(data, filename):
  151. try:
  152. spio.savemat(filename, data, oned_as='row')
  153. except Exception as error:
  154. return str(error)
  155. except ImportError:
  156. load_matlab = None
  157. save_matlab = None
  158. try:
  159. import numpy as np # analysis:ignore
  160. def load_array(filename):
  161. try:
  162. name = osp.splitext(osp.basename(filename))[0]
  163. data = np.load(filename)
  164. if hasattr(data, 'keys'):
  165. return data, None
  166. else:
  167. return {name: data}, None
  168. except Exception as error:
  169. return None, str(error)
  170. def __save_array(data, basename, index):
  171. """Save numpy array"""
  172. fname = basename + '_%04d.npy' % index
  173. np.save(fname, data)
  174. return fname
  175. except ImportError:
  176. load_array = None
  177. try:
  178. from spyderlib.pil_patch import Image
  179. if sys.byteorder == 'little':
  180. _ENDIAN = '<'
  181. else:
  182. _ENDIAN = '>'
  183. DTYPES = {
  184. "1": ('|b1', None),
  185. "L": ('|u1', None),
  186. "I": ('%si4' % _ENDIAN, None),
  187. "F": ('%sf4' % _ENDIAN, None),
  188. "I;16": ('|u2', None),
  189. "I;16S": ('%si2' % _ENDIAN, None),
  190. "P": ('|u1', None),
  191. "RGB": ('|u1', 3),
  192. "RGBX": ('|u1', 4),
  193. "RGBA": ('|u1', 4),
  194. "CMYK": ('|u1', 4),
  195. "YCbCr": ('|u1', 4),
  196. }
  197. def __image_to_array(filename):
  198. img = Image.open(filename)
  199. try:
  200. dtype, extra = DTYPES[img.mode]
  201. except KeyError:
  202. raise RuntimeError("%s mode is not supported" % img.mode)
  203. shape = (img.size[1], img.size[0])
  204. if extra is not None:
  205. shape += (extra,)
  206. return np.array(img.getdata(), dtype=np.dtype(dtype)).reshape(shape)
  207. def load_image(filename):
  208. try:
  209. name = osp.splitext(osp.basename(filename))[0]
  210. return {name: __image_to_array(filename)}, None
  211. except Exception as error:
  212. return None, str(error)
  213. except ImportError:
  214. load_image = None
  215. def load_pickle(filename):
  216. """Load a pickle file as a dictionary"""
  217. try:
  218. if pd:
  219. return pd.read_pickle(data), None
  220. else:
  221. with open(filename, 'rb') as fid:
  222. data = pickle.load(fid)
  223. return data, None
  224. except Exception as err:
  225. return None, str(err)
  226. def load_json(filename):
  227. """Load a json file as a dictionary"""
  228. try:
  229. with open(filename, 'rb') as fid:
  230. data = json.load(fid)
  231. return data, None
  232. except Exception as err:
  233. return None, str(err)
  234. def save_dictionary(data, filename):
  235. """Save dictionary in a single file .spydata file"""
  236. filename = osp.abspath(filename)
  237. old_cwd = getcwd()
  238. os.chdir(osp.dirname(filename))
  239. error_message = None
  240. try:
  241. saved_arrays = {}
  242. if load_array is not None:
  243. # Saving numpy arrays with np.save
  244. arr_fname = osp.splitext(filename)[0]
  245. for name in list(data.keys()):
  246. if isinstance(data[name], np.ndarray) and data[name].size > 0:
  247. # Saving arrays at data root
  248. fname = __save_array(data[name], arr_fname,
  249. len(saved_arrays))
  250. saved_arrays[(name, None)] = osp.basename(fname)
  251. data.pop(name)
  252. elif isinstance(data[name], (list, dict)):
  253. # Saving arrays nested in lists or dictionaries
  254. if isinstance(data[name], list):
  255. iterator = enumerate(data[name])
  256. else:
  257. iterator = iter(list(data[name].items()))
  258. to_remove = []
  259. for index, value in iterator:
  260. if isinstance(value, np.ndarray) and value.size > 0:
  261. fname = __save_array(value, arr_fname,
  262. len(saved_arrays))
  263. saved_arrays[(name, index)] = osp.basename(fname)
  264. to_remove.append(index)
  265. for index in sorted(to_remove, reverse=True):
  266. data[name].pop(index)
  267. if saved_arrays:
  268. data['__saved_arrays__'] = saved_arrays
  269. pickle_filename = osp.splitext(filename)[0]+'.pickle'
  270. with open(pickle_filename, 'wb') as fdesc:
  271. pickle.dump(data, fdesc, 2)
  272. tar = tarfile.open(filename, "w")
  273. for fname in [pickle_filename]+[fn for fn in list(saved_arrays.values())]:
  274. tar.add(osp.basename(fname))
  275. os.remove(fname)
  276. tar.close()
  277. if saved_arrays:
  278. data.pop('__saved_arrays__')
  279. except (RuntimeError, pickle.PicklingError, TypeError) as error:
  280. error_message = to_text_string(error)
  281. os.chdir(old_cwd)
  282. return error_message
  283. def load_dictionary(filename):
  284. """Load dictionary from .spydata file"""
  285. filename = osp.abspath(filename)
  286. old_cwd = getcwd()
  287. os.chdir(osp.dirname(filename))
  288. data = None
  289. error_message = None
  290. try:
  291. tar = tarfile.open(filename, "r")
  292. tar.extractall()
  293. pickle_filename = osp.splitext(filename)[0]+'.pickle'
  294. try:
  295. # Old format (Spyder 2.0-2.1 for Python 2)
  296. with open(pickle_filename, 'U') as fdesc:
  297. data = pickle.loads(fdesc.read())
  298. except (pickle.PickleError, TypeError):
  299. # New format (Spyder >=2.2 for Python 2 and Python 3)
  300. with open(pickle_filename, 'rb') as fdesc:
  301. data = pickle.loads(fdesc.read())
  302. saved_arrays = {}
  303. if load_array is not None:
  304. # Loading numpy arrays saved with np.save
  305. try:
  306. saved_arrays = data.pop('__saved_arrays__')
  307. for (name, index), fname in list(saved_arrays.items()):
  308. arr = np.load( osp.join(osp.dirname(filename), fname) )
  309. if index is None:
  310. data[name] = arr
  311. elif isinstance(data[name], dict):
  312. data[name][index] = arr
  313. else:
  314. data[name].insert(index, arr)
  315. except KeyError:
  316. pass
  317. for fname in [pickle_filename]+[fn for fn in list(saved_arrays.values())]:
  318. os.remove(fname)
  319. except (EOFError, ValueError) as error:
  320. error_message = to_text_string(error)
  321. os.chdir(old_cwd)
  322. return data, error_message
  323. from spyderlib.baseconfig import get_conf_path, STDERR
  324. SAVED_CONFIG_FILES = ('inspector', 'onlinehelp', 'path', 'pylint.results',
  325. 'spyder.ini', 'temp.py', 'temp.spydata', 'template.py',
  326. 'history.py', 'history_internal.py', 'workingdir',
  327. '.projects', '.spyderproject', '.ropeproject',
  328. 'monitor.log', 'monitor_debug.log', 'rope.log')
  329. def reset_session():
  330. """Remove all config files"""
  331. print("*** Reset Spyder settings to defaults ***", file=STDERR)
  332. for fname in SAVED_CONFIG_FILES:
  333. cfg_fname = get_conf_path(fname)
  334. if osp.isfile(cfg_fname):
  335. os.remove(cfg_fname)
  336. elif osp.isdir(cfg_fname):
  337. shutil.rmtree(cfg_fname)
  338. else:
  339. continue
  340. print("removing:", cfg_fname, file=STDERR)
  341. def save_session(filename):
  342. """Save Spyder session"""
  343. local_fname = get_conf_path(osp.basename(filename))
  344. filename = osp.abspath(filename)
  345. old_cwd = getcwd()
  346. os.chdir(get_conf_path())
  347. error_message = None
  348. try:
  349. tar = tarfile.open(local_fname, "w")
  350. for fname in SAVED_CONFIG_FILES:
  351. if osp.isfile(fname):
  352. tar.add(fname)
  353. tar.close()
  354. shutil.move(local_fname, filename)
  355. except Exception as error:
  356. error_message = to_text_string(error)
  357. os.chdir(old_cwd)
  358. return error_message
  359. def load_session(filename):
  360. """Load Spyder session"""
  361. filename = osp.abspath(filename)
  362. old_cwd = getcwd()
  363. os.chdir(osp.dirname(filename))
  364. error_message = None
  365. renamed = False
  366. try:
  367. tar = tarfile.open(filename, "r")
  368. extracted_files = tar.getnames()
  369. # Rename original config files
  370. for fname in extracted_files:
  371. orig_name = get_conf_path(fname)
  372. bak_name = get_conf_path(fname+'.bak')
  373. if osp.isfile(bak_name):
  374. os.remove(bak_name)
  375. if osp.isfile(orig_name):
  376. os.rename(orig_name, bak_name)
  377. renamed = True
  378. tar.extractall()
  379. for fname in extracted_files:
  380. shutil.move(fname, get_conf_path(fname))
  381. except Exception as error:
  382. error_message = to_text_string(error)
  383. if renamed:
  384. # Restore original config files
  385. for fname in extracted_files:
  386. orig_name = get_conf_path(fname)
  387. bak_name = get_conf_path(fname+'.bak')
  388. if osp.isfile(orig_name):
  389. os.remove(orig_name)
  390. if osp.isfile(bak_name):
  391. os.rename(bak_name, orig_name)
  392. finally:
  393. # Removing backup config files
  394. for fname in extracted_files:
  395. bak_name = get_conf_path(fname+'.bak')
  396. if osp.isfile(bak_name):
  397. os.remove(bak_name)
  398. os.chdir(old_cwd)
  399. return error_message
  400. from spyderlib.baseconfig import _
  401. class IOFunctions(object):
  402. def __init__(self):
  403. self.load_extensions = None
  404. self.save_extensions = None
  405. self.load_filters = None
  406. self.save_filters = None
  407. self.load_funcs = None
  408. self.save_funcs = None
  409. def setup(self):
  410. iofuncs = self.get_internal_funcs()+self.get_3rd_party_funcs()
  411. load_extensions = {}
  412. save_extensions = {}
  413. load_funcs = {}
  414. save_funcs = {}
  415. load_filters = []
  416. save_filters = []
  417. load_ext = []
  418. for ext, name, loadfunc, savefunc in iofuncs:
  419. filter_str = to_text_string(name + " (*%s)" % ext)
  420. if loadfunc is not None:
  421. load_filters.append(filter_str)
  422. load_extensions[filter_str] = ext
  423. load_funcs[ext] = loadfunc
  424. load_ext.append(ext)
  425. if savefunc is not None:
  426. save_extensions[filter_str] = ext
  427. save_filters.append(filter_str)
  428. save_funcs[ext] = savefunc
  429. load_filters.insert(0, to_text_string(_("Supported files")+" (*"+\
  430. " *".join(load_ext)+")"))
  431. load_filters.append(to_text_string(_("All files (*.*)")))
  432. self.load_filters = "\n".join(load_filters)
  433. self.save_filters = "\n".join(save_filters)
  434. self.load_funcs = load_funcs
  435. self.save_funcs = save_funcs
  436. self.load_extensions = load_extensions
  437. self.save_extensions = save_extensions
  438. def get_internal_funcs(self):
  439. return [
  440. ('.spydata', _("Spyder data files"),
  441. load_dictionary, save_dictionary),
  442. ('.npy', _("NumPy arrays"), load_array, None),
  443. ('.npz', _("NumPy zip arrays"), load_array, None),
  444. ('.mat', _("Matlab files"), load_matlab, save_matlab),
  445. ('.csv', _("CSV text files"), 'import_wizard', None),
  446. ('.txt', _("Text files"), 'import_wizard', None),
  447. ('.jpg', _("JPEG images"), load_image, None),
  448. ('.png', _("PNG images"), load_image, None),
  449. ('.gif', _("GIF images"), load_image, None),
  450. ('.tif', _("TIFF images"), load_image, None),
  451. ('.pkl', _("Pickle files"), load_pickle, None),
  452. ('.pickle', _("Pickle files"), load_pickle, None),
  453. ('.json', _("JSON files"), load_json, None),
  454. ]
  455. def get_3rd_party_funcs(self):
  456. other_funcs = []
  457. from spyderlib.otherplugins import get_spyderplugins_mods
  458. for mod in get_spyderplugins_mods(prefix='io_', extension='.py'):
  459. try:
  460. other_funcs.append((mod.FORMAT_EXT, mod.FORMAT_NAME,
  461. mod.FORMAT_LOAD, mod.FORMAT_SAVE))
  462. except AttributeError as error:
  463. print("%s: %s" % (mod, str(error)), file=STDERR)
  464. return other_funcs
  465. def save(self, data, filename):
  466. ext = osp.splitext(filename)[1].lower()
  467. if ext in self.save_funcs:
  468. return self.save_funcs[ext](data, filename)
  469. else:
  470. return _("<b>Unsupported file type '%s'</b>") % ext
  471. def load(self, filename):
  472. ext = osp.splitext(filename)[1].lower()
  473. if ext in self.load_funcs:
  474. return self.load_funcs[ext](filename)
  475. else:
  476. return None, _("<b>Unsupported file type '%s'</b>") % ext
  477. iofunctions = IOFunctions()
  478. iofunctions.setup()
  479. def save_auto(data, filename):
  480. """Save data into filename, depending on file extension"""
  481. pass
  482. if __name__ == "__main__":
  483. from spyderlib.py3compat import u
  484. import datetime
  485. testdict = {'d': 1, 'a': np.random.rand(10, 10), 'b': [1, 2]}
  486. testdate = datetime.date(1945, 5, 8)
  487. example = {'str': 'kjkj kj k j j kj k jkj',
  488. 'unicode': u('éù'),
  489. 'list': [1, 3, [4, 5, 6], 'kjkj', None],
  490. 'tuple': ([1, testdate, testdict], 'kjkj', None),
  491. 'dict': testdict,
  492. 'float': 1.2233,
  493. 'array': np.random.rand(4000, 400),
  494. 'empty_array': np.array([]),
  495. 'date': testdate,
  496. 'datetime': datetime.datetime(1945, 5, 8),
  497. }
  498. import time
  499. t0 = time.time()
  500. save_dictionary(example, "test.spydata")
  501. print(" Data saved in %.3f seconds" % (time.time()-t0))
  502. t0 = time.time()
  503. example2, ok = load_dictionary("test.spydata")
  504. print("Data loaded in %.3f seconds" % (time.time()-t0))
  505. # for key in example:
  506. # print key, ":", example[key] == example2[key]
  507. a = MatlabStruct()
  508. a.b = 'spam'
  509. assert a["b"] == 'spam'
  510. a.c["d"] = 'eggs'
  511. assert a.c.d == 'eggs'
  512. assert a == {'c': {'d': 'eggs'}, 'b': 'spam'}