PageRenderTime 38ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/mlabwrap.py

https://bitbucket.org/nikratio/mlabwrap
Python | 630 lines | 534 code | 23 blank | 73 comment | 21 complexity | 104a37f6b79526575af8241ebf7f96ac MD5 | raw file
  1. ##############################################################################
  2. ################## mlabwrap: transparently wraps matlab(tm) ##################
  3. ##############################################################################
  4. ##
  5. ## o author: Alexander Schmolck <a.schmolck@gmx.net>
  6. ## o created: 2002-05-29 21:51:59+00:40
  7. ## o version: see `__version__`
  8. ## o keywords: matlab wrapper
  9. ## o license: MIT
  10. ## o FIXME:
  11. ## - it seems proxies can somehow still 'disappear', maybe in connection
  12. ## with exceptions in the matlab workspace?
  13. ## - add test that defunct proxy-values are culled from matlab workspace
  14. ## (for some reason ipython seems to keep them alive somehwere, even after
  15. ## a zaphist, should find out what causes that!)
  16. ## - add tests for exception handling!
  17. ## - the proxy getitem/setitem only works quite properly for 1D arrays
  18. ## (matlab's moronic syntax also means that 'foo(bar)(watz)' is not the
  19. ## same as 'tmp = foo(bar); tmp(watz)' -- indeed chances are the former
  20. ## will fail (not, apparently however with 'foo{bar}{watz}' (blech)). This
  21. ## would make it quite hard to the proxy thing 'properly' for nested
  22. ## proxies, so things may break down for complicated cases but can be
  23. ## easily fixed manually e.g.: ``mlab._set('tmp', foo(bar));
  24. ## mlab._get('tmp',remove=True)[watz]``
  25. ## - Guess there should be some in principle avoidable problems with
  26. ## assignments to sub-proxies (in addition to the more fundamental,
  27. ## unavoidable problem that ``proxy[index].part = foo`` can't work as
  28. ## expected if ``proxy[index]`` is a marshallable value that doesn't need
  29. ## to be proxied itself; see below for workaround).
  30. ## o XXX:
  31. ## - better support of string 'arrays'
  32. ## - multi-dimensional arrays are unsupported
  33. ## - treatment of lists, tuples and arrays with non-numerical values (these
  34. ## should presumably be wrapped into wrapper classes MlabCell etc.)
  35. ## - should test classes and further improve struct support?
  36. ## - should we transform 1D vectors into row vectors when handing them to
  37. ## matlab?
  38. ## - what should be flattend? Should there be a scalarization opition?
  39. ## - ``autosync_dirs`` is a bit of a hack (and maybe``handle_out``, too)...
  40. ## - is ``global mlab`` in unpickling of proxies OK?
  41. ## - hasattr fun for proxies (__deepcopy__ etc.)
  42. ## - check pickling
  43. ## o TODO:
  44. ## - delattr
  45. ## - better error reporting: test for number of input args etc.
  46. ## - add cloning of proxies.
  47. ## - pickling for nested proxies (and session management for pickling)
  48. ## - more tests
  49. ## o !!!:
  50. ## - matlab complex arrays are intelligently of type 'double'
  51. ## - ``class('func')`` but ``class(var)``
  52. """
  53. mlabwrap
  54. ========
  55. This module implements a powerful and simple to use wrapper that makes using
  56. matlab(tm) from python almost completely transparent. To use simply do:
  57. >>> from mlabwrap import mlab
  58. and then just use whatever matlab command you like as follows:
  59. >>> mlab.plot(range(10), 'ro:')
  60. You can do more than just plotting:
  61. >>> mlab.sort([3,1,2])
  62. array([[ 1., 2., 3.]])
  63. N.B.: The result here is a 1x3 matrix (and not a flat lenght 3 array) of type
  64. double (and not int), as matlab built around matrices of type double (see
  65. ``MlabWrap._flatten_row_vecs``).
  66. Matlab(tm)ab, unlike python has multiple value returns. To emulate calls like
  67. ``[a,b] = sort([3,2,1])`` just do:
  68. >>> mlab.sort([3,1,2], nout=2)
  69. (array([[ 1., 2., 3.]]), array([[ 2., 3., 1.]]))
  70. For names that are reserved in python (like print) do:
  71. >>> mlab.print_()
  72. You can look at the documentation of a matlab function just by using help,
  73. as usual:
  74. >>> help(mlab.sort)
  75. In almost all cases that should be enough -- if you need to do trickier
  76. things, then get raw with ``mlab._do``, or build your child class that
  77. handles what you want.
  78. Fine points and limitations
  79. ---------------------------
  80. - Only 2D matrices are directly supported as return values of matlab
  81. functions (arbitrary matlab classes are supported via proxy objects --
  82. in most cases this shouldn't make much of a difference (as these proxy
  83. objects can be even pickled) -- still this functionality is yet
  84. experimental).
  85. One potential pitfall with structs (which are currently proxied) is that
  86. setting indices of subarrays ``struct.part[index] = value`` might seem
  87. to have no effect (since ``part`` can be directly represented as a
  88. python array which will be modified without an effect on the proxy
  89. ``struct``'s contents); in that case::
  90. some_array[index] = value; struct.part = some_array``
  91. will have the desired effect.
  92. - Matlab doesn't know scalars, or 1D arrays. Consequently all functions
  93. that one might expect to return a scalar or 1D array will return a 1x1
  94. array instead. Also, because matlab(tm) is built around the 'double'
  95. matrix type (which also includes complex matrices), single floats and
  96. integer types will be cast to double. Note that row and column vectors
  97. can be autoconverted automatically to 1D arrays if that is desired (see
  98. ``_flatten_row_vecs``).
  99. - for matlab(tm) function names like ``print`` that are reserved words in
  100. python, so you have to add a trailing underscore (e.g. ``mlab.print_``).
  101. - sometimes you will have to specify the number of return arguments of a
  102. function, e.g. ``a,b,c = mlab.foo(nout=3)``. MlabWrap will normally try to
  103. figure out for you whether the function you call returns 0 or more values
  104. (in which case by default only the first value is returned!). For builtins
  105. this might fail (since unfortunately there seems to be no foolproof way to
  106. find out in matlab), but you can always lend a helping hand::
  107. mlab.foo = mlab._make_mlab_command('foo', nout=3, doc=mlab.help('foo'))
  108. Now ``mlab.foo()`` will by default always return 3 values, but you can still
  109. get only one by doing ``mlab.foo(nout=1)``
  110. - by default the working directory of matlab(tm) is kept in synch with that of
  111. python to avoid unpleasant surprises. In case this behavior does instaed
  112. cause you unpleasant surprises, you can turn it off with::
  113. mlab._autosync_dirs = False
  114. - you can customize how matlab is called by setting the environment variable
  115. ``MLABRAW_CMD_STR`` (e.g. to add useful opitons like '-nojvm'). For the
  116. rather convoluted semantics see
  117. <http://www.mathworks.com/access/helpdesk/help/techdoc/apiref/engopen.html>.
  118. - if you don't want to use numpy arrays, but something else that's fine
  119. too::
  120. >>> import matrix from numpy.core.defmatrix
  121. >>> mlab._array_cast = matrix
  122. >>> mlab.sqrt([[4.], [1.], [0.]])
  123. matrix([[ 2.],
  124. [ 1.],
  125. [ 0.]])
  126. Credits
  127. -------
  128. This is really a wrapper around a wrapper (mlabraw) which in turn is a
  129. modified and bugfixed version of Andrew Sterian's pymat
  130. (http://claymore.engineer.gvsu.edu/~steriana/Python/pymat.html), so thanks go
  131. to him for releasing his package as open source.
  132. See the docu of ``MlabWrap`` and ``MatlabObjectProxy`` for more information.
  133. """
  134. __docformat__ = "restructuredtext en"
  135. __version__ = '1.1'
  136. __author__ = "Alexander Schmolck <a.schmolck@gmx.net>"
  137. import warnings
  138. from pickle import PickleError
  139. import operator
  140. import os, sys, re
  141. import weakref
  142. import atexit
  143. try:
  144. import numpy
  145. ndarray = numpy.ndarray
  146. except ImportError:
  147. import Numeric
  148. ndarray = Numeric.ArrayType
  149. from tempfile import gettempdir
  150. import mlabraw
  151. #XXX: nested access
  152. def _flush_write_stdout(s):
  153. """Writes `s` to stdout and flushes. Default value for ``handle_out``."""
  154. sys.stdout.write(s); sys.stdout.flush()
  155. # XXX I changed this to no longer use weakrefs because it didn't seem 100%
  156. # reliable on second thought; need to check if we need to do something to
  157. # speed up proxy reclamation on the matlab side.
  158. class CurlyIndexer(object):
  159. """A helper class to mimick ``foo{bar}``-style indexing in python."""
  160. def __init__(self, proxy):
  161. self.proxy = proxy
  162. def __getitem__(self, index):
  163. return self.proxy.__getitem__(index, '{}')
  164. def __setitem__(self, index, value):
  165. self.proxy.__setitem__(index, value, '{}')
  166. class MlabObjectProxy(object):
  167. """A proxy class for matlab objects that can't be converted to python
  168. types.
  169. WARNING: There are impedance-mismatch issues between python and matlab
  170. that make designing such a class difficult (e.g. dimensionality, indexing
  171. and ``length`` work fundamentally different in matlab than in python), so
  172. although this class currently tries to transparently support some stuff
  173. (notably (1D) indexing, slicing and attribute access), other operations
  174. (e.g. math operators and in particular __len__ and __iter__) are not yet
  175. supported. Don't depend on the indexing semantics not to change.
  176. Note:
  177. Assigning to parts of proxy objects (e.g. ``proxy[index].part =
  178. [[1,2,3]]``) should *largely* work as expected, the only exception
  179. would be if ``proxy.foo[index] = 3`` where ``proxy.foo[index]`` is some
  180. type that can be converted to python (i.e. an array or string, (or
  181. cell, if cell conversion has been enabled)), because then ``proxy.foo``
  182. returns a new python object. For these cases it's necessary to do::
  183. some_array[index] = 3; proxy.foo = some_array
  184. """
  185. def __init__(self, mlabwrap, name, parent=None):
  186. self.__dict__['_mlabwrap'] = mlabwrap
  187. self.__dict__['_name'] = name
  188. """The name is the name of the proxies representation in matlab."""
  189. self.__dict__['_parent'] = parent
  190. """To fake matlab's ``obj{foo}`` style indexing."""
  191. def __repr__(self):
  192. output = []
  193. mlab._do('disp(%s)' % self._name, nout=0, handle_out=output.append)
  194. rep = "".join(output)
  195. klass = self._mlabwrap._do("class(%s)" % self._name)
  196. ## #XXX what about classes?
  197. ## if klass == "struct":
  198. ## rep = "\n" + self._mlabwrap._format_struct(self._name)
  199. ## else:
  200. ## rep = ""
  201. return "<%s of matlab-class: %r; internal name: %r; has parent: %s>\n%s" % (
  202. type(self).__name__, klass,
  203. self._name, ['yes', 'no'][self._parent is None],
  204. rep)
  205. def __del__(self):
  206. if self._parent is None:
  207. mlabraw.eval(self._mlabwrap._session, 'clear %s;' % self._name)
  208. def _get_part(self, to_get):
  209. if self._mlabwrap._var_type(to_get) in self._mlabwrap._mlabraw_can_convert:
  210. #!!! need assignment to TMP_VAL__ because `mlabraw.get` only works
  211. # with 'atomic' values like ``foo`` and not e.g. ``foo.bar``.
  212. mlabraw.eval(self._mlabwrap._session, "TMP_VAL__=%s" % to_get)
  213. return self._mlabwrap._get('TMP_VAL__', remove=True)
  214. return type(self)(self._mlabwrap, to_get, self)
  215. def _set_part(self, to_set, value):
  216. #FIXME s.a.
  217. if isinstance(value, MlabObjectProxy):
  218. mlabraw.eval(self._mlabwrap._session, "%s = %s;" % (to_set, value._name))
  219. else:
  220. self._mlabwrap._set("TMP_VAL__", value)
  221. mlabraw.eval(self._mlabwrap._session, "%s = TMP_VAL__;" % to_set)
  222. mlabraw.eval(self._mlabwrap._session, 'clear TMP_VAL__;')
  223. def __getattr__(self, attr):
  224. if attr == "_":
  225. return self.__dict__.setdefault('_', CurlyIndexer(self))
  226. else:
  227. return self._get_part("%s.%s" % (self._name, attr))
  228. def __setattr__(self, attr, value):
  229. self._set_part("%s.%s" % (self._name, attr), value)
  230. # FIXME still have to think properly about how to best translate Matlab semantics here...
  231. def __nonzero__(self):
  232. raise TypeError("%s does not yet implement truth testing" % type(self).__name__)
  233. def __len__(self):
  234. raise TypeError("%s does not yet implement __len__" % type(self).__name__)
  235. def __iter__(self):
  236. raise TypeError("%s does not yet implement iteration" % type(self).__name__)
  237. def _matlab_str_repr(s):
  238. if '\n' not in s:
  239. return "'%s'" % s.replace("'","''")
  240. else:
  241. # Matlab's string literals suck. They can't represent all
  242. # strings, so we need to use sprintf
  243. return "sprintf('%s')" % escape(s).replace("'","''").replace("%", "%%")
  244. _matlab_str_repr = staticmethod(_matlab_str_repr)
  245. #FIXME: those two only work ok for 1D indexing
  246. def _convert_index(self, index):
  247. if isinstance(index, int):
  248. return str(index + 1) # -> matlab 1-based indexing
  249. elif isinstance(index, basestring):
  250. return self._matlab_str_repr(index)
  251. elif isinstance(index, slice):
  252. if index == slice(None,None,None):
  253. return ":"
  254. elif index.step not in (None,1):
  255. raise ValueError("Illegal index for a proxy %r" % index)
  256. else:
  257. start = (index.start or 0) + 1
  258. if start == 0: start_s = 'end'
  259. elif start < 0: start_s = 'end%d' % start
  260. else: start_s = '%d' % start
  261. if index.stop is None: stop_s = 'end'
  262. elif index.stop < 0: stop_s = 'end%d' % index.stop
  263. else: stop_s = '%d' % index.stop
  264. return '%s:%s' % (start_s, stop_s)
  265. else:
  266. raise TypeError("Unsupported index type: %r." % type(index))
  267. def __getitem__(self, index, parens='()'):
  268. """WARNING: Semi-finished, semantics might change because it's not yet
  269. clear how to best bridge the matlab/python impedence match.
  270. HACK: Matlab decadently allows overloading *2* different indexing parens,
  271. ``()`` and ``{}``, hence the ``parens`` option."""
  272. index = self._convert_index(index)
  273. return self._get_part("".join([self._name,parens[0],index,parens[1]]))
  274. def __setitem__(self, index, value, parens='()'):
  275. """WARNING: see ``__getitem__``."""
  276. index = self._convert_index(index)
  277. return self._set_part("".join([self._name,parens[0],index,parens[1]]),
  278. value)
  279. class MlabConversionError(Exception):
  280. """Raised when a mlab type can't be converted to a python primitive."""
  281. pass
  282. class MlabWrap(object):
  283. """This class does most of the wrapping work. It manages a single matlab
  284. session (you can in principle have multiple open sessions if you want,
  285. but I can see little use for this, so this feature is largely untested)
  286. and automatically translates all attribute requests (that don't start
  287. with '_') to the appropriate matlab function calls. The details of this
  288. handling can be controlled with a number of instance variables,
  289. documented below."""
  290. __all__ = [] #XXX a hack, so that this class can fake a module; don't mutate
  291. def __init__(self, use_jvm=False, use_display=False):
  292. """Create a new matlab(tm) wrapper object.
  293. """
  294. self._array_cast = None
  295. """specifies a cast for arrays. If the result of an
  296. operation is a numpy array, ``return_type(res)`` will be returned
  297. instead."""
  298. self._autosync_dirs=True
  299. """`autosync_dirs` specifies whether the working directory of the
  300. matlab session should be kept in sync with that of python."""
  301. self._flatten_row_vecs = False
  302. """Automatically return 1xn matrices as flat numeric arrays."""
  303. self._flatten_col_vecs = False
  304. """Automatically return nx1 matrices as flat numeric arrays."""
  305. self._clear_call_args = True
  306. self._closed = False
  307. """Remove the function args from matlab workspace after each function
  308. call. Otherwise they are left to be (partly) overwritten by the next
  309. function call. This saves a function call in matlab but means that the
  310. memory used up by the arguments will remain unreclaimed till
  311. overwritten."""
  312. cmd_str = 'matlab -nodesktop'
  313. if not use_jvm:
  314. cmd_str += ' -nojvm'
  315. if not use_display:
  316. cmd_str += ' -nodisplay'
  317. self._session = mlabraw.open(cmd_str)
  318. atexit.register(self.close)
  319. self._proxies = weakref.WeakValueDictionary()
  320. """Use ``mlab._proxies.values()`` for a list of matlab object's that
  321. are currently proxied."""
  322. self._proxy_count = 0
  323. self._mlabraw_can_convert = ('double', 'char')
  324. """The matlab(tm) types that mlabraw will automatically convert for us."""
  325. self._dont_proxy = {'cell' : False}
  326. """The matlab(tm) types we can handle ourselves with a bit of
  327. effort. To turn on autoconversion for e.g. cell arrays do:
  328. ``mlab._dont_proxy["cell"] = True``."""
  329. def __del__(self):
  330. self.close()
  331. def close(self):
  332. if not self._closed:
  333. self._closed = True
  334. mlabraw.close(self._session)
  335. def _format_struct(self, varname):
  336. res = []
  337. fieldnames = self._do("fieldnames(%s)" % varname)
  338. size = numpy.ravel(self._do("size(%s)" % varname))
  339. return "%dx%d struct array with fields:\n%s" % (
  340. size[0], size[1], "\n ".join([""] + fieldnames))
  341. ## fieldnames
  342. ## fieldvalues = self._do(",".join(["%s.%s" % (varname, fn)
  343. ## for fn in fieldnames]), nout=len(fieldnames))
  344. ## maxlen = max(map(len, fieldnames))
  345. ## return "\n".join(["%*s: %s" % (maxlen, (`fv`,`fv`[:20] + '...')[len(`fv`) > 23])
  346. ## for fv in fieldvalues])
  347. def _var_type(self, varname):
  348. mlabraw.eval(self._session,
  349. "TMP_CLS__ = class(%(x)s); if issparse(%(x)s),"
  350. "TMP_CLS__ = [TMP_CLS__,'-sparse']; end;" % dict(x=varname))
  351. res_type = mlabraw.get(self._session, "TMP_CLS__")
  352. mlabraw.eval(self._session, "clear TMP_CLS__;") # unlikely to need try/finally to ensure clear
  353. return res_type
  354. def _make_proxy(self, varname, parent=None, constructor=MlabObjectProxy):
  355. """Creates a proxy for a variable.
  356. XXX create and cache nested proxies also here.
  357. """
  358. proxy_val_name = "PROXY_VAL%d__" % self._proxy_count
  359. self._proxy_count += 1
  360. mlabraw.eval(self._session, "%s = %s;" % (proxy_val_name, varname))
  361. res = constructor(self, proxy_val_name, parent)
  362. self._proxies[proxy_val_name] = res
  363. return res
  364. def _get_cell(self, varname):
  365. # XXX can currently only handle ``{}`` and 1D cells
  366. mlabraw.eval(self._session,
  367. "TMP_SIZE_INFO__ = \
  368. [all(size(%(vn)s) == 0), \
  369. min(size(%(vn)s)) == 1 & ndims(%(vn)s) == 2, \
  370. max(size(%(vn)s))];" % {'vn':varname})
  371. is_empty, is_rank1, cell_len = map(int,
  372. self._get("TMP_SIZE_INFO__", remove=True).flat)
  373. if is_empty:
  374. return []
  375. elif is_rank1:
  376. cell_bits = (["TMP%i__" % (self.proxy_count+i)
  377. for i in range(cell_len)])
  378. self._proxy_count += cell_len
  379. mlabraw.eval(self._session, '[%s] = deal(%s{:});' %
  380. (",".join(cell_bits), varname))
  381. # !!! this recursive call means we have to take care with
  382. # overwriting temps!!!
  383. return self._get_values(cell_bits)
  384. else:
  385. raise MlabConversionError("Not a 1D cell array")
  386. def _manually_convert(self, varname, vartype):
  387. if vartype == 'cell':
  388. return self._get_cell(varname)
  389. def _get_values(self, varnames):
  390. if not varnames: raise ValueError("No varnames") #to prevent clear('')
  391. res = []
  392. for varname in varnames:
  393. res.append(self._get(varname))
  394. mlabraw.eval(self._session, "clear('%s');" % "','".join(varnames)) #FIXME wrap try/finally?
  395. return res
  396. def _do(self, cmd, *args, **kwargs):
  397. """Semi-raw execution of a matlab command.
  398. Smartly handle calls to matlab, figure out what to do with `args`,
  399. and when to use function call syntax and not.
  400. If no `args` are specified, the ``cmd`` not ``result = cmd()`` form is
  401. used in Matlab -- this also makes literal Matlab commands legal
  402. (eg. cmd=``get(gca, 'Children')``).
  403. If ``nout=0`` is specified, the Matlab command is executed as
  404. procedure, otherwise it is executed as function (default), nout
  405. specifying how many values should be returned (default 1).
  406. **Beware that if you use don't specify ``nout=0`` for a `cmd` that
  407. never returns a value will raise an error** (because assigning a
  408. variable to a call that doesn't return a value is illegal in matlab).
  409. ``cast`` specifies which typecast should be applied to the result
  410. (e.g. `int`), it defaults to none.
  411. XXX: should we add ``parens`` parameter?
  412. """
  413. handle_out = kwargs.get('handle_out', _flush_write_stdout)
  414. #self._session = self._session or mlabraw.open()
  415. # HACK
  416. if self._autosync_dirs:
  417. mlabraw.eval(self._session, "cd('%s');" % os.getcwd().replace("'", "''"))
  418. nout = kwargs.get('nout', 1)
  419. #XXX what to do with matlab screen output
  420. argnames = []
  421. tempargs = []
  422. try:
  423. for count, arg in enumerate(args):
  424. if isinstance(arg, MlabObjectProxy):
  425. argnames.append(arg._name)
  426. else:
  427. nextName = 'arg%d__' % count
  428. argnames.append(nextName)
  429. tempargs.append(nextName)
  430. # have to convert these by hand
  431. ## try:
  432. ## arg = self._as_mlabable_type(arg)
  433. ## except TypeError:
  434. ## raise TypeError("Illegal argument type (%s.:) for %d. argument" %
  435. ## (type(arg), type(count)))
  436. mlabraw.put(self._session, argnames[-1], arg)
  437. if args:
  438. cmd = "%s(%s)%s" % (cmd, ", ".join(argnames),
  439. ('',';')[kwargs.get('show',0)])
  440. # got three cases for nout:
  441. # 0 -> None, 1 -> val, >1 -> [val1, val2, ...]
  442. if nout == 0:
  443. handle_out(mlabraw.eval(self._session, cmd))
  444. return
  445. # deal with matlab-style multiple value return
  446. resSL = ((["RES%d__" % i for i in range(nout)]))
  447. handle_out(mlabraw.eval(self._session, '[%s]=%s;' % (", ".join(resSL), cmd)))
  448. res = self._get_values(resSL)
  449. if nout == 1: res = res[0]
  450. else: res = tuple(res)
  451. if kwargs.has_key('cast'):
  452. if nout == 0: raise TypeError("Can't cast: 0 nout")
  453. return kwargs['cast'](res)
  454. else:
  455. return res
  456. finally:
  457. if len(tempargs) and self._clear_call_args:
  458. mlabraw.eval(self._session, "clear('%s');" %
  459. "','".join(tempargs))
  460. # this is really raw, no conversion of [[]] -> [], whatever
  461. def _get(self, name, remove=False):
  462. r"""Directly access a variable in matlab space.
  463. This should normally not be used by user code."""
  464. # FIXME should this really be needed in normal operation?
  465. if name in self._proxies: return self._proxies[name]
  466. varname = name
  467. vartype = self._var_type(varname)
  468. if vartype in self._mlabraw_can_convert:
  469. var = mlabraw.get(self._session, varname)
  470. if isinstance(var, ndarray):
  471. if self._flatten_row_vecs and numpy.shape(var)[0] == 1:
  472. var.shape = var.shape[1:2]
  473. elif self._flatten_col_vecs and numpy.shape(var)[1] == 1:
  474. var.shape = var.shape[0:1]
  475. if self._array_cast:
  476. var = self._array_cast(var)
  477. else:
  478. var = None
  479. if self._dont_proxy.get(vartype):
  480. # manual conversions may fail (e.g. for multidimensional
  481. # cell arrays), in that case just fall back on proxying.
  482. try:
  483. var = self._manually_convert(varname, vartype)
  484. except MlabConversionError: pass
  485. if var is None:
  486. # we can't convert this to a python object, so we just
  487. # create a proxy, and don't delete the real matlab
  488. # reference until the proxy is garbage collected
  489. var = self._make_proxy(varname)
  490. if remove:
  491. mlabraw.eval(self._session, "clear('%s');" % varname)
  492. return var
  493. def _set(self, name, value):
  494. r"""Directly set a variable `name` in matlab space to `value`.
  495. This should normally not be used in user code."""
  496. if isinstance(value, MlabObjectProxy):
  497. mlabraw.eval(self._session, "%s = %s;" % (name, value._name))
  498. else:
  499. ## mlabraw.put(self._session, name, self._as_mlabable_type(value))
  500. mlabraw.put(self._session, name, value)
  501. def _make_mlab_command(self, name, nout, doc=None):
  502. def mlab_command(*args, **kwargs):
  503. if 'nout' not in kwargs:
  504. kwargs['nout'] = nout
  505. return self._do(name, *args, **kwargs)
  506. mlab_command.__doc__ = "\n" + doc
  507. return mlab_command
  508. # XXX this method needs some refactoring, but only after it is clear how
  509. # things should be done (e.g. what should be extracted from docstrings and
  510. # how)
  511. def __getattr__(self, attr):
  512. """Magically creates a wapper to a matlab function, procedure or
  513. object on-the-fly."""
  514. if re.search(r'\W', attr): # work around ipython <= 0.7.3 bug
  515. raise ValueError("Attributes don't look like this: %r" % attr)
  516. if attr.startswith('__'): raise AttributeError, attr
  517. assert not attr.startswith('_') # XXX
  518. # print_ -> print
  519. if attr[-1] == "_": name = attr[:-1]
  520. else : name = attr
  521. try:
  522. nout = self._do("nargout('%s')" % name)
  523. except mlabraw.error, msg:
  524. typ = numpy.ravel(self._do("exist('%s')" % name))[0]
  525. if typ == 0: # doesn't exist
  526. raise AttributeError("No such matlab object: %s" % name)
  527. else:
  528. warnings.warn(
  529. "Couldn't ascertain number of output args"
  530. "for '%s', assuming 1." % name)
  531. nout = 1
  532. doc = self._do("help('%s')" % name)
  533. # play it safe only return 1st if nout >= 1
  534. # XXX are all ``nout>1``s also useable as ``nout==1``s?
  535. nout = nout and 1
  536. mlab_command = self._make_mlab_command(name, nout, doc)
  537. #!!! attr, *not* name, because we might have python keyword name!
  538. setattr(self, attr, mlab_command)
  539. return mlab_command
  540. mlab = MlabWrap()
  541. MlabError = mlabraw.error
  542. __all__ = ['mlab', 'MlabWrap', 'MlabError']
  543. # Uncomment the following line to make the `mlab` object a library so that
  544. # e.g. ``from mlabwrap.mlab import plot`` will work
  545. ## if not sys.modules.get('mlabwrap.mlab'): sys.modules['mlabwrap.mlab'] = mlab