/
matlab.py
222 lines (193 loc) · 7.69 KB
/
matlab.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
"""Interfaces to run MATLAB scripts."""
import os
from .. import config
from .base import (
CommandLineInputSpec,
InputMultiPath,
isdefined,
CommandLine,
traits,
File,
Directory,
)
def get_matlab_command():
"""Determine whether Matlab is installed and can be executed."""
if "NIPYPE_NO_MATLAB" not in os.environ:
from nipype.utils.filemanip import which
return which(os.getenv("MATLABCMD", "matlab"))
no_matlab = get_matlab_command() is None
class MatlabInputSpec(CommandLineInputSpec):
"""Basic expected inputs to Matlab interface"""
script = traits.Str(
argstr='-r "%s;exit"', desc="m-code to run", mandatory=True, position=-1
)
uses_mcr = traits.Bool(
desc="use MCR interface",
xor=["nodesktop", "nosplash", "single_comp_thread"],
nohash=True,
)
nodesktop = traits.Bool(
True,
argstr="-nodesktop",
usedefault=True,
desc="Switch off desktop mode on unix platforms",
nohash=True,
)
nosplash = traits.Bool(
True,
argstr="-nosplash",
usedefault=True,
desc="Switch of splash screen",
nohash=True,
)
logfile = File(argstr="-logfile %s", desc="Save matlab output to log")
single_comp_thread = traits.Bool(
argstr="-singleCompThread", desc="force single threaded operation", nohash=True
)
# non-commandline options
mfile = traits.Bool(True, desc="Run m-code using m-file", usedefault=True)
script_file = File(
"pyscript.m", usedefault=True, desc="Name of file to write m-code to"
)
paths = InputMultiPath(Directory(), desc="Paths to add to matlabpath")
prescript = traits.List(
["ver,", "try,"], usedefault=True, desc="prescript to be added before code"
)
postscript = traits.List(
[
"\n,catch ME,",
"fprintf(2,'MATLAB code threw an exception:\\n');",
"fprintf(2,'%s\\n',ME.message);",
"if length(ME.stack) ~= 0, fprintf(2,'File:%s\\nName:%s\\nLine:%d\\n',ME.stack.file,ME.stack.name,ME.stack.line);, end;",
"end;",
],
desc="script added after code",
usedefault=True,
)
class MatlabCommand(CommandLine):
"""Interface that runs matlab code
>>> import nipype.interfaces.matlab as matlab
>>> mlab = matlab.MatlabCommand(mfile=False) # don't write script file
>>> mlab.inputs.script = "which('who')"
>>> out = mlab.run() # doctest: +SKIP
"""
_cmd = "matlab"
_default_matlab_cmd = None
_default_mfile = None
_default_paths = None
input_spec = MatlabInputSpec
def __init__(self, matlab_cmd=None, **inputs):
"""initializes interface to matlab
(default 'matlab -nodesktop -nosplash')
"""
super(MatlabCommand, self).__init__(**inputs)
if matlab_cmd and isdefined(matlab_cmd):
self._cmd = matlab_cmd
elif self._default_matlab_cmd:
self._cmd = self._default_matlab_cmd
if self._default_mfile and not isdefined(self.inputs.mfile):
self.inputs.mfile = self._default_mfile
if self._default_paths and not isdefined(self.inputs.paths):
self.inputs.paths = self._default_paths
if not isdefined(self.inputs.single_comp_thread) and not isdefined(
self.inputs.uses_mcr
):
if config.getboolean("execution", "single_thread_matlab"):
self.inputs.single_comp_thread = True
# For matlab commands force all output to be returned since matlab
# does not have a clean way of notifying an error
self.terminal_output = "allatonce"
@classmethod
def set_default_matlab_cmd(cls, matlab_cmd):
"""Set the default MATLAB command line for MATLAB classes.
This method is used to set values for all MATLAB
subclasses. However, setting this will not update the output
type for any existing instances. For these, assign the
<instance>.inputs.matlab_cmd.
"""
cls._default_matlab_cmd = matlab_cmd
@classmethod
def set_default_mfile(cls, mfile):
"""Set the default MATLAB script file format for MATLAB classes.
This method is used to set values for all MATLAB
subclasses. However, setting this will not update the output
type for any existing instances. For these, assign the
<instance>.inputs.mfile.
"""
cls._default_mfile = mfile
@classmethod
def set_default_paths(cls, paths):
"""Set the default MATLAB paths for MATLAB classes.
This method is used to set values for all MATLAB
subclasses. However, setting this will not update the output
type for any existing instances. For these, assign the
<instance>.inputs.paths.
"""
cls._default_paths = paths
def _run_interface(self, runtime):
self.terminal_output = "allatonce"
runtime = super(MatlabCommand, self)._run_interface(runtime)
try:
# Matlab can leave the terminal in a barbbled state
os.system("stty sane")
except:
# We might be on a system where stty doesn't exist
pass
if "MATLAB code threw an exception" in runtime.stderr:
self.raise_exception(runtime)
return runtime
def _format_arg(self, name, trait_spec, value):
if name in ["script"]:
argstr = trait_spec.argstr
if self.inputs.uses_mcr:
argstr = "%s"
return self._gen_matlab_command(argstr, value)
return super(MatlabCommand, self)._format_arg(name, trait_spec, value)
def _gen_matlab_command(self, argstr, script_lines):
"""Generates commands and, if mfile specified, writes it to disk."""
cwd = os.getcwd()
mfile = self.inputs.mfile or self.inputs.uses_mcr
paths = []
if isdefined(self.inputs.paths):
paths = self.inputs.paths
# prescript
prescript = self.inputs.prescript
postscript = self.inputs.postscript
# prescript takes different default value depending on the mfile argument
if mfile:
prescript.insert(
0, "fprintf(1,'Executing %s at %s:\\n',mfilename(),datestr(now));"
)
else:
prescript.insert(0, "fprintf(1,'Executing code at %s:\\n',datestr(now));")
for path in paths:
# addpath() is not available after compilation
# https://www.mathworks.com/help/compiler/ismcc.html
# https://www.mathworks.com/help/compiler/isdeployed.html
prescript.append("if ~(ismcc || isdeployed), addpath('%s'); end;\n" % path)
if not mfile:
# clean up the code of comments and replace newlines with commas
script_lines = ",".join(
[
line
for line in script_lines.split("\n")
if not line.strip().startswith("%")
]
)
script_lines = "\n".join(prescript) + script_lines + "\n".join(postscript)
if mfile:
with open(os.path.join(cwd, self.inputs.script_file), "wt") as mfile:
mfile.write(script_lines)
if self.inputs.uses_mcr:
script = "%s" % (os.path.join(cwd, self.inputs.script_file))
else:
script = "addpath('%s');%s" % (
cwd,
self.inputs.script_file.split(".")[0],
)
else:
script = "".join(script_lines.split("\n"))
return argstr % script