Skip to content
This repository has been archived by the owner on Jun 27, 2021. It is now read-only.

Commit

Permalink
Added support for python-side class-specific wrappers. This is implem…
Browse files Browse the repository at this point in the history
…ented

similar to the py.types hierarchy, except in reverse: the python package
"mltypes" contains subclasses of mx.Array, and whenever an mx.Array wrapper
is needed an MRO list for the MATLAB object is computed (see mro.m) and
an appropriate class is chosen from that list.

Currently the only subclass is "cell", and it has no specialized behavior.
It will be changed soonish so that its get/setitem hooks use mxGet/SetCell.
  • Loading branch information
Ken Watford committed Nov 18, 2009
1 parent ad8aee5 commit c971522
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -3,3 +3,6 @@
*~
.nfs*
build/*
*.pyc
*.pyo

20 changes: 20 additions & 0 deletions mltypes/__init__.py
@@ -0,0 +1,20 @@
from matlab import mx
import sys

def _findtype(typelist):
for (modstr, clsstr) in typelist:
if len(modstr):
adjusted_modstr = "mltypes.%s" % modstr
else:
adjusted_modstr = "mltypes"
try:
tempmod = __import__(adjusted_modstr, globals(), locals(), [clsstr], 0)
cls = getattr(tempmod, clsstr);
return cls;
except:
pass
# if we got this far and found nothing, just use mx.Array
return mx.Array;


class cell(mx.Array): pass
68 changes: 68 additions & 0 deletions mro.m
@@ -0,0 +1,68 @@
% mrolist = mro(object, addvirtual=true, autosplit=true)
% Determines something similar to the method resolution order for an object.
% Note that this is probably not the actual order, since a cursory inspection
% did not reveal any clues as to what the correct order might be. So this just
% produces a list of all of the object's classes from most to least specific.
%
% If addvirtual is true, a virtual class may be appended to the end of the
% mro list to indicate the nonexistent root of a particular hierarchy.
% The virtual classes are:
% _numeric: Numbers, logicals, and characters.
% _object: MATLAB objects
% _java: Java objects
% _com: COM objects (windows only)
% _interface: COM interface (windows only)
%
% Note that no attempt is made to enumerate the class hierarchy
% of Java classes. We could probably do that, but let's keep it simple for now.
function [mrolist] = mro(object,addvirtual,autosplit)
if nargin < 2, addvirtual = true; end
if nargin < 3, autosplit = true; end
classname = class(object);
mrolist = {classname};
classqueue = {classname};
while ~isempty(classqueue)
mco = meta.class.fromName(classqueue{1});
classqueue = classqueue(2:end);
if isempty(mco)
% MATLAB doesn't abstract Java class reflection,
% so it will return an empty metaclass handle.
continue;
end
supers = mco.SuperClasses;
for i=1:numel(supers)
name = supers{i}.Name;
if ~ismember(name, mrolist)
mrolist = [mrolist name]; %#ok<AGROW>
classqueue = [classqueue name]; %#ok<AGROW>
end
end
end

if addvirtual
if isnumeric(object) || ischar(object) || islogical(object)
mrolist{end+1} = '_numeric';
elseif isobject(object)
mrolist{end+1} = '_object';
elseif isjava(object)
mrolist{end+1} = '_java';
% In case this gets used on Windows...
elseif iscom(object)
mrolist{end+1} = '_com';
elseif isinterface(object)
mrolist{end+1} = '_interface';
end
end

if autosplit
for i=1:numel(mrolist)
classname = mrolist{i};
dots = strfind(classname, '.');
if isempty(dots)
mrolist{i} = {'', classname};
else
d = dots(end);
mrolist{i} = {classname(1:d-1), classname(d+1:end)};
end
end
end
2 changes: 2 additions & 0 deletions pymex.h
Expand Up @@ -79,6 +79,7 @@ bool mxIsPyObject(const mxArray* mxobj);
mxArray* PyObject_to_mxLogical(PyObject* pyobj);
PyObject* mxChar_to_PyBytes(const mxArray* mxchar);
PyObject* mxCell_to_PyTuple(const mxArray* mxobj);
PyObject* mxCell_to_PyTuple_recursive(const mxArray* mxobj);
mxArray* PyBytes_to_mxChar(PyObject* pystr);
mxArray* PyObject_to_mxChar(PyObject* pyobj);
mxArray* PySequence_to_mxCell(PyObject* pyobj);
Expand All @@ -87,6 +88,7 @@ mxArray* PyObject_to_mxLong(PyObject* pyobj);
PyObject* Any_mxArray_to_PyObject(const mxArray* mxobj);
mxArray* Any_PyObject_to_mxArray(PyObject* pyobj);
bool PyMXObj_Check(PyObject* pyobj);
PyObject* Calculate_matlab_mro(mxArray* mxobj);
PyObject* Py_mxArray_New(mxArray* mxobj, bool duplicate);
int Py_mxArray_Check(PyObject* pyobj);
PyObject* mxArray_to_PyArray(const mxArray* mxobj, bool duplicate);
Expand Down
90 changes: 89 additions & 1 deletion sharedfuncs.c
Expand Up @@ -252,6 +252,23 @@ PyObject* mxCell_to_PyTuple(const mxArray* mxobj) {
}
return pyobj;
}

PyObject* mxCell_to_PyTuple_recursive(const mxArray* mxobj) {
mwSize numel = mxGetNumberOfElements(mxobj);
PyObject* pyobj = PyTuple_New(numel);
mwSize i;
for (i=0; i<numel; i++) {
mxArray* item = mxGetCell(mxobj, i);
PyObject* pyitem;
if (mxIsCell(item))
pyitem = mxCell_to_PyTuple_recursive(item);
else
pyitem = Any_mxArray_to_PyObject(item);
PyTuple_SetItem(pyobj, i, pyitem);
}
return pyobj;
}

/*
PyObject* mxNumber_to_PyObject(const mxArray* mxobj, mwIndex index) {
void* ptr = mxGetData(mxobj);
Expand Down Expand Up @@ -362,6 +379,75 @@ mxArray* Any_PyObject_to_mxArray(PyObject* pyobj) {
return boxb(pyobj);
}

/* TODO: Low priority, but this depends on libmex. If we try to make an
external module later, this will need to function to some extent.
It could operate at low capability by using a hardcoded list for
the builtins and assuming that anything else has _object as a super.
*/
PyObject* Calculate_matlab_mro(mxArray* mxobj) {
mxArray* argin[3];
argin[0] = mxobj;
argin[1] = mxCreateLogicalScalar(1); /* addvirtual=true */
argin[2] = mxCreateLogicalScalar(1); /* autosplit=true */
mxArray* argout[1] = {NULL};
mexSetTrapFlag(1);
int err = mexCallMATLAB(1, argout, 3, argin, "mro");
if (err) return PyErr_Format(PyExc_RuntimeError, "MATLAB error while trying to run 'mro' function.");
else {
PyObject* retval = mxCell_to_PyTuple_recursive(argout[0]);
mxDestroyArray(argout[0]);
mxDestroyArray(argin[1]);
mxDestroyArray(argin[2]);
return retval;
}
}

/* Attempts to locate an appropriate subclass of mx.Array using the mltypes package.
If for some reason this fails, mx.Array is returned instead.
*/
PyObject* Find_mltype_for(mxArray* mxobj) {
#define GET_MX_ARRAY_CLASS PyObject_GetAttrString(mxmodule, "Array")
PyObject *newclass, *mrolist, *mltypes, *findtype;
newclass = mrolist = mltypes = findtype = NULL;
mrolist = Calculate_matlab_mro(mxobj);
if (!mrolist) {
mexPrintf("failed to get mro list\n");
PyErr_Clear();
newclass = GET_MX_ARRAY_CLASS;
goto findtypes_error;
}
mltypes = PyImport_ImportModule("mltypes");
if (!mltypes) {
mexPrintf("failed to import mltypes\n");
PyErr_Clear();
newclass = GET_MX_ARRAY_CLASS;
goto findtypes_error;
}
findtype = PyObject_GetAttrString(mltypes, "_findtype");
if (!findtype) {
mexPrintf("failed to find _findtype\n");
PyErr_Clear();
newclass = GET_MX_ARRAY_CLASS;
goto findtypes_error;
}
/* I'm not sure why we need *another* tuple around this, but apparently
CallFunction kills the outermost tuple here */
mrolist = PyTuple_Pack(1, mrolist);
newclass = PyObject_CallFunction(findtype, "O", mrolist);
if (!newclass) {
mexPrintf("failed to call _findtype\n");
PyErr_Clear();
newclass = GET_MX_ARRAY_CLASS;
goto findtypes_error;
}
findtypes_error:
Py_XDECREF(findtype);
Py_XDECREF(mrolist);
Py_XDECREF(mltypes);
return newclass;
}


PyObject* Py_mxArray_New(mxArray* mxobj, bool duplicate) {
mxArray* copy;
if (duplicate) {
Expand All @@ -372,9 +458,11 @@ PyObject* Py_mxArray_New(mxArray* mxobj, bool duplicate) {
copy = mxobj;
}
PyObject* mxptr = mxArrayPtr_New(copy);
PyObject* arraycls = Find_mltype_for(copy);
/* TODO: There is probably a better way to do this... */
PyObject* ret = PyObject_CallMethod(mxmodule, "Array", "O", mxptr);
PyObject* ret = PyObject_CallFunction(arraycls, "O", mxptr);
Py_DECREF(mxptr);
Py_DECREF(arraycls);
return ret;
}

Expand Down

0 comments on commit c971522

Please sign in to comment.