Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 36 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ This should make the python-matlab-bridge import-able.

## Usage

To use the pymatbridge you need to connect your python interperter to a Matlab
To use the pymatbridge you need to connect your python interpreter to a Matlab
session. This is done in the following manner:

from pymatbridge import Matlab
Expand All @@ -72,12 +72,12 @@ This creates a matlab session class instance, into which you will be able to
inject code and variables, and query for results. By default, when you use
`start`, this will open whatever gets called when you type `matlab`
in your Terminal, but you can also specify the location of your Matlab
application when initialzing your matlab session class:
application when initializing your matlab session class:

mlab = Matlab(matlab='/Applications/MATLAB_R2011a.app/bin/matlab')
mlab = Matlab(executable='/Applications/MATLAB_R2011a.app/bin/matlab')

You can then start the Matlab server, which will kick off your matlab session,
and create the connection between your Python interperter and this session:
and create the connection between your Python interpreter and this session:

mlab.start()

Expand All @@ -99,7 +99,7 @@ the `get_variable` method:

mlab.get_variable('a')

You can run any MATLAB functions contained within a .m file of the
You can run any MATLAB functions contained within a .m file of the
same name. For example, to call the function jk in jk.m:

%% MATLAB
Expand All @@ -123,6 +123,37 @@ You can shut down the MATLAB server by calling:
Tip: you can execute MATLAB code at the beginning of each of your matlab
sessions by adding code to the `~/startup.m` file.

### Octave support & caveats

A `pymatbridge.Octave` class is provided with exactly the same interface
as `pymatbridge.Matlab`:

from pymatbridge import Octave
octave = Octave()

Rather than looking for `matlab` at the shell, this will look for `octave`.
As with `pymatbridge.Matlab`, you can override this by specifying the
`executable` keyword argument.

There are a few caveats to note about Octave support:

* `pymatbridge.Matlab` invokes MATLAB with command line options that suppress
the display of figures -- these are instead saved to image files, accessible
via `results['content']['figures']` in the results dict. No such mechanism
seems to be available for Octave, so when drawing figures you'll likely see
them briefly pop up and disappear. (If you know of a way around this, feel
free to send a pull request).
* The zmq messenger used to communicate with the Octave session is written in C
and needs to be compiled. For MATLAB various prebuilt binaries are provided
and added to MATLAB's runtime path, but as of this writing the same isn't
true for Octave. You'll need to compile the messenger (in
`messenger/src/messenger.c`) using an incantation like
`mkoctfile --mex -lzmq messenger.c` and place the resulting `messenger.mex`
file somewhere in Octave's runtime path.

Finally, rather than `~/startup.m`, Octave looks for an `~/.octaverc` file for
commands to execute before every session. (This is a good place to manipulate
the runtime path, for example).

### Matlab magic:

Expand Down
8 changes: 4 additions & 4 deletions messenger/src/messenger.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ void mexFunction(int nlhs, mxArray *plhs[],

if (!initialized) {
if (!initialize(socket_addr)) {
p[0] = true;
p[0] = 1;
mexPrintf("Socket created at: %s\n", socket_addr);
} else {
p[0] = false;
p[0] = 0;
mexErrMsgTxt("Socket creation failed.");
}
} else {
Expand Down Expand Up @@ -137,9 +137,9 @@ void mexFunction(int nlhs, mxArray *plhs[],
p = mxGetLogicals(plhs[0]);

if (msglen == respond(msg_out, msglen)) {
p[0] = true;
p[0] = 1;
} else {
p[0] = false;
p[0] = 0;
mexErrMsgTxt("Failed to send message due to ZMQ error");
}

Expand Down
4 changes: 2 additions & 2 deletions pymatbridge/matlab/matlabserver.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ function matlabserver(socket_address)
% over the socket. I then enters the listen-respond mode until it gets an
% "exit" command

json.startup
json_startup
messenger('init', socket_address);

while(1)
msg_in = messenger('listen');
req = json.load(msg_in);
req = json_load(msg_in);

switch(req.cmd)
case {'connect'}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function str = dump(value, varargin)
function str = json_dump(value, varargin)
%DUMP Encode matlab value into a JSON string.
%
% SYNOPSIS
Expand Down Expand Up @@ -64,7 +64,7 @@
%
% See also json.load json.write

json.startup('WarnOnAddpath', true);
json_startup('WarnOnAddpath', true);
options = get_options_(varargin{:});
obj = dump_data_(value, options);
if isempty(options.indent)
Expand Down Expand Up @@ -95,11 +95,12 @@
function obj = dump_data_(value, options)
%DUMP_DATA_
if ischar(value) && (isvector(value) || isempty(value))
obj = java.lang.String(value);
obj = javaObject('java.lang.String', value);
elseif isempty(value) && isnumeric(value)
obj = org.json.JSONObject.NULL;
json_object = javaObject('org.json.JSONObject');
obj = json_object.NULL;
elseif ~isscalar(value)
obj = org.json.JSONArray();
obj = javaObject('org.json.JSONArray');

if ndims(value) > 2
split_value = num2cell(value, 1:ndims(value)-1);
Expand All @@ -124,13 +125,13 @@
end
end
elseif iscell(value)
obj = org.json.JSONArray();
obj = javaObject('org.json.JSONArray');
for i = 1:numel(value)
obj.put(dump_data_(value{i}, options));
end
elseif isnumeric(value)
if isreal(value)
obj = java.lang.Double(value);
obj = javaObject('java.lang.Double', value);
% Encode complex number as a struct
else
complex_struct = struct;
Expand All @@ -139,14 +140,14 @@
obj = dump_data_(complex_struct, options);
end
elseif islogical(value)
obj = java.lang.Boolean(value);
obj = javaObject('java.lang.Boolean', value);
elseif isstruct(value)
obj = org.json.JSONObject();
obj = javaObject('org.json.JSONObject');
keys = fieldnames(value);
for i = 1:length(keys)
obj.put(keys{i},dump_data_(value.(keys{i}), options));
end
else
error('json:typeError', 'Unsupported data type: %s', class(value));
end
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function value = load(str, varargin)
function value = json_load(str, varargin)
%LOAD Load matlab value from a JSON string.
%
% SYNOPSIS
Expand Down Expand Up @@ -57,7 +57,7 @@
%
% See also json.dump json.read

json.startup('WarnOnAddpath', true);
json_startup('WarnOnAddpath', true);
options = get_options_(varargin{:});
singleton = false;

Expand All @@ -66,11 +66,11 @@
error('json:invalidString','Invalid JSON string');
end
if str(1)=='{'
node = org.json.JSONObject(java.lang.String(str));
node = javaObject('org.json.JSONObject', javaObject('java.lang.String', str));
else
singleton = str(1) ~= '[' && str(end) ~= ']';
if singleton, str = ['[',str,']']; end
node = org.json.JSONArray(java.lang.String(str));
node = javaObject('org.json.JSONArray', javaObject('java.lang.String', str));
end
value = parse_data_(node, options);
if singleton, value = value{:}; end
Expand Down Expand Up @@ -119,15 +119,16 @@
warning('json:fieldNameConflict', ...
'Field %s renamed to %s', field, safe_field);
end
value.(safe_field) = parse_data_(node.get(java.lang.String(key)), ...
value.(safe_field) = parse_data_(node.get(javaObject('java.lang.String', key)), ...
options);
end
% Check if the struct just decoded represents a complex number
if isfield(value,'real') && isfield(value, 'imag')
complex_value = complex(value.real, value.imag);
value = complex_value;
end
elseif isa(node, 'org.json.JSONObject$Null')
% In MATLAB, nested classes end up with a $ in the name, in Octave it's a .
elseif isa(node, 'org.json.JSONObject$Null') || isa(node, 'org.json.JSONObject.Null')
value = [];
else
error('json:typeError', 'Unknown data type: %s', class(node));
Expand Down Expand Up @@ -196,4 +197,4 @@
fields = fieldnames(value);
vec = [vec, uint8([fields{:}])];
end
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function value = read(filename, varargin)
function value = json_read(filename, varargin)
%READ Load a matlab value from a JSON file.
%
% SYNOPSIS
Expand All @@ -19,7 +19,7 @@
fid = 0;
try
fid = fopen(filename, 'r');
value = json.load(fscanf(fid, '%c', inf));
value = json_load(fscanf(fid, '%c', inf));
fclose(fid);
catch e
if fid ~= 0, fclose(fid); end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function startup(varargin)
function json_startup(varargin)
%STARTUP Initialize runtime environment.
%
% SYNOPSIS
Expand Down Expand Up @@ -42,4 +42,4 @@ function startup(varargin)
]);
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function write(value, filename, varargin)
function json_write(value, filename, varargin)
%WRITE Write a matlab value into a JSON file.
%
% SYNOPSIS
Expand All @@ -19,7 +19,7 @@ function write(value, filename, varargin)
fid = 0;
try
fid = fopen(filename, 'w');
fprintf(fid, '%s', json.dump(value, varargin{:}));
fprintf(fid, '%s', json_dump(value, varargin{:}));
fclose(fid);
catch e
if fid ~= 0, fclose(fid); end
Expand Down
4 changes: 2 additions & 2 deletions pymatbridge/matlab/util/pymat_eval.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

if ~code_check
response.message = 'No code provided as POST parameter';
json_response = json.dump(response);
json_response = json_dump(response);
return;
end

Expand Down Expand Up @@ -71,6 +71,6 @@

response.content.code = code;

json_response = json.dump(response);
json_response = json_dump(response);

end %function
4 changes: 2 additions & 2 deletions pymatbridge/matlab/util/pymat_feval.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

if ~func_path_check
response.message = 'No function given as func_path POST parameter';
json_response = json.dump(response);
json_response = json_dump(response);
return
end

Expand All @@ -35,7 +35,7 @@
response.success = 'true';
response.message = 'Successfully completed request';

json_response = json.dump(response);
json_response = json_dump(response);

return

Expand Down
4 changes: 2 additions & 2 deletions pymatbridge/matlab/util/pymat_get_variable.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

if ~varname_check
response.message = 'No variable name provided as input argument';
json_response = json.dump(response);
json_response = json_dump(response);
return
end

Expand All @@ -26,7 +26,7 @@

response.var = evalin('base', varname);

json_response = json.dump(response);
json_response = json_dump(response);

return
end
5 changes: 2 additions & 3 deletions pymatbridge/matlab_magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def eval(self, line):
"""
Parse and evaluate a single line of matlab
"""
run_dict = self.Matlab.run_code(line, maxtime=self.Matlab.maxtime)
run_dict = self.Matlab.run_code(line)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove the time-out?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The maxtime parameter wasn't actually being used in run_func, run_code or get_variable, so I removed it and then fixed the call sites. I guess we could implement a timeout in those methods, but even then it might make more sense to just use self.maxtime rather than a parameter.


if run_dict['success'] == 'false':
raise MatlabInterperterError(line, run_dict['content']['stdout'])
Expand All @@ -127,8 +127,7 @@ def set_matlab_var(self, name, value):
Set up a variable in Matlab workspace
"""
run_dict = self.Matlab.run_func("pymat_set_variable.m",
{'name':name, 'value':value},
maxtime=self.Matlab.maxtime)
{'name':name, 'value':value})

if run_dict['success'] == 'false':
raise MatlabInterperterError(line, run_dict['content']['stdout'])
Expand Down
Loading