Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# (c) Stefan Countryman 2016-2018 

2 

3""" 

4FileHandlers that make calls to MATLAB in order to generate their data. You 

5will need to specify the directory containing MATLAB functions to call with the 

6environmental variable ``MATLABDIR``. 

7""" 

8 

9import os 

10from abc import abstractproperty 

11import subprocess 

12from glob import glob 

13import logging 

14import distutils.spawn 

15from llama.classes import optional_env_var 

16from llama.utils import LOGDIR 

17from llama.filehandler import ( 

18 GenerationError, 

19 FileHandler, 

20) 

21LOGGER = logging.getLogger(__name__) 

22 

23MATLAB_LOGFILE = os.path.join(LOGDIR, 'llama.matlab.log') 

24MATLABDIR = optional_env_var( 

25 ['MATLABDIR'], 

26 f""" 

27 Specify path to directory containing MATLAB commands using 

28 environmental variable ``MATLABDIR`` in order to generate MATLAB 

29 ``FileHandler`` instances. No ``MATLABDIR`` specified; defaulting to 

30 CWD: {os.getcwd()}. 

31 """, 

32)[0] 

33 

34 

35 

36def matlabpath(): 

37 """Get a path to a ``matlab`` executable (if it exists). If something called 

38 ``matlab`` is in your ``$PATH``, this will be used. Otherwise, if you have 

39 specified ``$MATLAB_EXECUTABLE_PATH`` as an environmental variable, that path 

40 will be used. If both of these checks fail, the most recent version of 

41 ``MATLAB`` matching the pattern 

42 ``/Applications/MATLAB_R20*.app/bin/matlab`` will be used (only applicable 

43 on MacOS).""" 

44 matlab = distutils.spawn.find_executable('matlab') 

45 if matlab is not None: 

46 return matlab 

47 if 'MATLAB_EXECUTABLE_PATH' in os.environ: 

48 return os.environ['MATLAB_EXECUTABLE_PATH'] 

49 matches = sorted(glob("/Applications/MATLAB_R20*.app/bin/matlab")) 

50 if matches: 

51 return matches[-1] 

52 raise OSError("Could not find a MATLAB executable.") 

53 

54 

55def matlab_eval(matcmd, cli_flags=None): 

56 """Run a command in a headless instance of MATLAB. By default, changes to 

57 the Neutrinos directory of the repo directory, which is where all LLAMA 

58 MATLAB code is currently stored. NOTE that your 

59 command can have multiple statements, but it CANNOT have newlines; it 

60 must have semicolons separating valid statements (See example below). 

61 

62 You can also provide a list of command line flags like "-nojvm" to modify 

63 MATLAB's behavior. *This implementation will be removed if we switch to 

64 MATLAB Engine, since MATLAB is not called through the command line 

65 interface when using the MATLAB Engine interface.* 

66  

67 Examples 

68 -------- 

69 Print "foo" and "bar" on separate lines, checking first if MATLAB is 

70 installed: 

71 >>> try: 

72 ... matpath = matlabpath() 

73 ... matlab_eval("disp('foo');disp('bar')") 

74 ... except OSError: 

75 ... # put your cleanup code here 

76 ... pass 

77 """ 

78 if cli_flags is None: 

79 cli_flags = list() 

80 # write output to the MATLAB logfile. TODO implement true logging. 

81 with open(MATLAB_LOGFILE, 'a') as logfile: 

82 # retrieve the path to the MATLAB executable from the environment 

83 if '\n' in matcmd: 

84 raise ValueError('MATLAB command cannot contain a newline.') 

85 cmd = [ 

86 matlabpath(), '-nodesktop', '-nosplash', '-noFigureWindows', '-r', 

87 "cd {}; ".format(MATLABDIR) + 

88 "try {}; ".format(matcmd) + 

89 "catch err; " + 

90 " fprintf(2, getReport(err)); " + 

91 " exit(64); " + 

92 "end; " + 

93 "disp('done'); exit"] 

94 cmd += cli_flags 

95 proc = subprocess.Popen(cmd, stdout=logfile, stderr=logfile) 

96 proc.communicate() 

97 if proc.returncode != 0: 

98 raise Exception(("Something went wrong. See debug output in: " + 

99 MATLAB_LOGFILE)) 

100 

101 

102class MATLABCallingFileHandler(FileHandler): 

103 """ 

104 A class that is generated by calling a MATLAB function. The generator 

105 just calls the matlab function specified by the ``matlab_command`` property 

106 using MATLAB command-line options specified by ``matlab_options``. 

107 """ 

108 

109 def _generate(self): # pylint: disable=W0221 

110 LOGGER.debug('Running MATLAB command:') 

111 LOGGER.debug(self.matlab_options) 

112 try: 

113 matlab_eval(self.matlab_command, self.matlab_options) 

114 except Exception: 

115 import traceback 

116 LOGGER.error('MATLAB failed to generate %s', self) 

117 LOGGER.error(traceback.format_exc()) 

118 raise GenerationError('matlab_exec failed while generating %s', 

119 self) 

120 

121 # command-line options to pass to MATLAB 

122 matlab_options = [] 

123 

124 @abstractproperty 

125 def matlab_command(self): 

126 """Return the command that MATLAB will eval in order to generate this 

127 file."""