Python embedded in MATLAB extension module
Switch branches/tags
Nothing to show
Clone or download
Latest commit 5f2aba9 Nov 24, 2013
Permalink
Failed to load latest commit information.
+py Adding py.eval. The caller's workspace will be copied into the eval s… Dec 2, 2009
mltypes Shuffling some code around to get stuff out of mltypes/__init__.py Dec 1, 2009
.gitignore Added support for python-side class-specific wrappers. This is implem… Nov 18, 2009
LICENSE Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
Makefile Maintenance patch. Haven't touched this in a while, and apparently Mar 23, 2010
NEWS.md News: win32 branch. Dec 5, 2009
README.md Update contact information Nov 24, 2013
any2str.m Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
commands.c Fix memory leak in CALL (reference to args tuple not released) Jul 25, 2011
engmodule.c Dropping the "matlab" module. It didn't do anything other than contai… Nov 30, 2009
kw.m Correcting for weird object autogrow bug in 2009b. I really need to w… Dec 2, 2009
matlab.py Can now import matlab functions that have reserved python names be pr… Dec 1, 2009
matlab_example.m Adding some simplistic example usage Dec 1, 2009
matmodule.c Dropping the "matlab" module. It didn't do anything other than contai… Nov 30, 2009
mexmodule.c Moving FIXME to Issue #2 Dec 1, 2009
mro.m Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
mxmodule.c Moving FIXME to Issue #4, plus random cleanup Dec 1, 2009
py.m Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
pybuiltins.m Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
pycall.m Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
pyimport.m Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
pymex.c Maintenance patch. Haven't touched this in a while, and apparently Mar 23, 2010
pymex.h Adding VERSION command Dec 1, 2009
pymex.m Adding LICENSE file (going with MIT license for now) as well Nov 24, 2009
pymexutil.py Shuffling some code around to get stuff out of mltypes/__init__.py Dec 1, 2009
python_example.py adding to python_example.py Dec 1, 2009
sharedfuncs.c Shuffling some code around to get stuff out of mltypes/__init__.py Dec 1, 2009
test_mxmodule.py A little more unit test stuff before I quit for the day Nov 30, 2009
test_numpy.py Maintenance patch. Haven't touched this in a while, and apparently Mar 23, 2010
unpy.m Un-hardcoding unpy converters for strings, cells, tuples, ints, longs… Nov 29, 2009

README.md

README for pymex

pymex embeds a Python interpreter into MATLAB and provides support for interaction between them. It does not (currently) allow interaction with MATLAB through any arbitrary process - see the 'pymat' project for that.

License is currently standard MIT/X11. See the LICENSE file.

If you're not sure how you got this, the git repository can be found at: http://github.com/kw/pymex

See the Issues section near the bottom if you have problems, or send me a message on GitHub, or maybe even send me an e-mail (kwatford@gmail.com)

Requirements

  • MATLAB 2008b or higher.
  • Python 2.6. Probably needs shared rather than static. No Py3k support yet, but I'm trying to minimize porting issues. No NumPy for Py3k yet anyway.
  • NumPy. Not a build requirement, but you'll definitely want it.
  • (optional) nose, for running unit tests.
  • If you're using windows, use the win32 branch, and see the COMPILING_WINDOWS.md file.

Installation

  1. Clone or untar this somewhere.
  2. Ensure that MATLAB's mex command is configured. Run mex -setup if it isn't. (this can be done from within MATLAB or using the mex script in MATLAB's bin dir)
  3. Run make in that directory. If your configuration is not as expected you can specify some of the environment variables, like make PYTHON=~/bin/python2.7
  4. If by some miracle that actually worked and you've got nose, try make test. Note that you can't just run nosetests because the necessary modules only exist within MATLAB. On some systems this may fail rather hard -- see issue #1
  5. Add the pymex directory to MATLAB's path. pymex will add itself to your Python path.

Usage

For some examples, see python_example.py and matlab_example.py.

help pymex might be helpful.

On the MATLAB side, pyimport and pybuiltins give us access to Python modules and builtins. To import a module,

pyimport numpy

or

np = pyimport('numpy')

If you use the "command form" to import a submodule, it will change the dots into underscores. I'd recommend using the expression form instead.

pyimport mltypes.containers  % produces the variable "mltypes_containers"

Running pybuiltins with no arguments tries to import all of Python's builtins, which is probably undesirable. Provide string arguments to request specific builtins:

pybuiltins list tuple dict

or

[list, tuple, dict] = pybuiltins('list', 'tuple', 'dict');

Some shortcuts are provided in the 'py' package:

py.tuple(42, 'spam', @plot)
py.list(1, 2, 3)
py.dict('spam', 'eggs', 'foo', 'bar')

(note that these shortcuts have different calling conventions than the builtins. YMMV)

MATLAB has no keyword arguments, so to pass those to a python function, use the kw class:

v = myfunc(a, b, kw('keyword1', 42, 'keyword2', py.None, ...))

You can provide multiple instances of kw if necessary.

To evaluate a python expression using the variables in the current MATLAB workspace, use py.eval:

>> x = {'spam', 42, struct('foo','bar'), containers.Map}
x = 
    'spam'    [42]    [1x1 struct]    [0x1 containers.Map]
>> py.eval('[(type(a), a) for a in x]')
ans = 
[(<type 'str'>, 'spam'), (<class 'mltypes._builtins._numeric'>, 42), (<class 'mltypes._builtins.struct'>, 
 <struct at 0x7f12c84b7fd0>), (<class 'mltypes.containers.Map'>, <containers.Map at 0x7f12c84ba738>)]

Python objects tend to have a lot of attributes whose names begin with underscores. That's not valid in MATLAB, but we can do it anyway using MATLAB's dynamic field access syntax:

>> l = py.list(1,2,3)
l = 
[1, 2, 3]
>> l.pop()           % normal attribute access
ans = 
3
>> l.('__len__')()   % underscore attribute access
ans = 
2

This is ugly, but reinforces the idea that you probably shouldn't be accessing them.

Python functions and expressions do not automatically convert outputs to MATLAB objects, even if they're just wrapped MATLAB objects. Use the unpy method to coerce.

On the Python side:

from matlab import cell, fprintf, plot, max

MATLAB functions (currently) coerce all arguments to MATLAB types when possible, since presumably MATLAB functions don't want Python inputs. To request multiple outputs, use the 'nargout' keyword:

x = numpy.array([1, 2, 4, 0])
val, ind = matlab.max(x, nargout=2) # ind is 1-based

Wrappers

Wrapper classes are provided for both sides of the river. When an object needs to be wrapped, its method resolution order is used to find a list of classes in order of specificity. Then a specific hierarchy is checked for matching classes, and the most specific match found wins.

On the MATLAB side, python classes are wrapped using the py.types package, and the default type is py.types.builtin.object (even for objects that aren't "object" instances). The only other wrapper present at time of writing is py.types.numpy.ndarray.

On the Python side, MATLAB classes are wrapped using the mltypes package. The base type is mx.Array (mx being the embedded module representing MATLAB's libmx), but most of the basic types have wrappers. mro.m provides the method resolution order for an object, which includes some "virtual" classes I've added whose names begin with underscores. The prime example of this is the _numeric class, which all MATLAB numeric arrays are subclasses of.

The _object class doesn't work particularly well for general MATLAB objects, but see mltypes.containers.Map for an example of a wrapped MATLAB class:

>> map = containers.Map;
>> map('foo') = 'bar';
>> pymap = py(map)
pymap = 
  containers.Map handle
    Package: containers

  Properties:
        Count: 1
      KeyType: 'char'
    ValueType: 'any'
  Methods, Events, Superclasses
>> pymap{'foo'}
ans = 
bar
>> pymap{'spam'} = 'eggs';
>> map('spam')
ans =
eggs
>> 

NumPy support

The aforementioned _numeric class doesn't really do much because normal MATLAB numeric arrays work so well with NumPy:

>> x = py.asarray(magic(5)) % or pyimport numpy; numpy.asarray(...)
x = 
[[ 17.  24.   1.   8.  15.]
 [ 23.   5.   7.  14.  16.]
 [  4.   6.  13.  20.  22.]
 [ 10.  12.  19.  21.   3.]
 [ 11.  18.  25.   2.   9.]]
>> x{0,0} = 42
x = 
[[ 42.  24.   1.   8.  15.]
 [ 23.   5.   7.  14.  16.]
 [  4.   6.  13.  20.  22.]
 [ 10.  12.  19.  21.   3.]
 [ 11.  18.  25.   2.   9.]]
>> unpy(x)
ans =
    42    24     1     8    15
    23     5     7    14    16
     4     6    13    20    22
    10    12    19    21     3
    11    18    25     2     9
>> x.base
ans = 
[17 24 1 8 15;23 5 7 14 16;4 6 13 20 22;10 12 19 21 3;11 18 25 2 9]
>> type(x.base)
ans= 
<class 'mltypes._builtins._numeric'>

Issues

Issue tracker is on GitHub. Please report problems there. A few of note, so that you don't have to go looking for them:

  • As mentioned, unit tests might not work. See Issue #1.
  • MATLAB is not thread safe. See Issue #2.
  • There is presently no support for complex or sparse matrices. See issues #5 and #6
  • I presently have no way to generate an actual Python REPL in pymex. MATLAB does weird things with its stdin/stdout in its Desktop gui. If you start it up with -nodesktop then stdout works, but stdin doesn't seem to work properly (I've started Python's standard REPL but it doesn't seem to accept input). I've also tried simply starting IDLE, but it crashes somewhere within Tk/CoreFoundation (this is on my Mac) Maybe someone will have better luck, or just write a module based on MATLAB's fprintf and input functions.