11"""Wrappers for the interface between RATapi and MATLAB custom files."""
2- from contextlib import contextmanager
2+
33import os
44import pathlib
55import platform
66import shutil
77import subprocess
8+ from contextlib import contextmanager
89from typing import Callable
910
10- import numpy as np
1111from numpy .typing import ArrayLike
1212
1313import RATapi .rat_core
1414
15-
1615MATLAB_PATH_FILE = os .path .join (os .path .dirname (os .path .realpath (__file__ )), "matlab.txt" )
1716
1817
1918@contextmanager
20- def cd (newdir ):
19+ def cd (new_dir ):
20+ """Context manager to change to a given directory and return to current directory on exit.
21+
22+ Parameters
23+ ----------
24+ new_dir : string
25+ The path to change to
26+ """
2127 prev_dir = os .getcwd ()
22- os .chdir (os .path .expanduser (newdir ))
28+ os .chdir (os .path .expanduser (new_dir ))
2329 try :
2430 yield
2531 finally :
2632 os .chdir (prev_dir )
2733
2834
29- def set_matlab_path (matlab_path ):
30- if not matlab_path :
31- return
32-
33- if platform .system () == "Windows" :
34- process = subprocess .Popen (f'"{ matlab_path } " -batch "comserver(\' register\' )"' )
35+ def set_matlab_path (matlab_exe_path ):
36+ """Set the path of MATLAB to use for custom functions.
37+
38+ This will also register the MATLAB COM server on Windows OS which could be slow.
39+
40+ Parameters
41+ ----------
42+ matlab_path : string
43+ The full path of the MATLAB executable
44+ """
45+ if not matlab_exe_path :
46+ return
47+
48+ if platform .system () == "Windows" :
49+ process = subprocess .Popen (f'"{ matlab_exe_path } " -batch "comserver(\' register\' )"' )
3550 process .wait ()
36-
3751
3852 with open (MATLAB_PATH_FILE , "w" ) as path_file :
39- path_file .write (matlab_path )
53+ path_file .write (matlab_exe_path )
54+
4055
56+ def get_matlab_paths (matlab_exe_path ):
57+ """Return paths for loading MATLAB engine C interface dynamic libraries.
4158
42- def get_matlab_paths (exe_path ):
43- if not exe_path :
59+ Parameters
60+ ----------
61+ matlab_exe_path : string
62+ The full path of the MATLAB executable
63+
64+ Returns
65+ -------
66+ paths : Tuple[string, string]
67+ The path of the MATLAB bin and the DLL location
68+ """
69+ if not matlab_exe_path :
4470 raise FileNotFoundError ()
45-
46- bin_path = pathlib .Path (exe_path ).parent
47- if bin_path .stem != ' bin' :
71+
72+ bin_path = pathlib .Path (matlab_exe_path ).parent
73+ if bin_path .stem != " bin" :
4874 raise FileNotFoundError ()
49-
50- if platform .system () == "Windows" :
51- arch = "win64"
52- elif platform .system () == "Darwin" :
53- arch = "maca64" if platform .mac_ver ()[- 1 ] == ' arm64' else "maci64"
54- else :
75+
76+ if platform .system () == "Windows" :
77+ arch = "win64"
78+ elif platform .system () == "Darwin" :
79+ arch = "maca64" if platform .mac_ver ()[- 1 ] == " arm64" else "maci64"
80+ else :
5581 arch = "glnxa64"
56-
82+
5783 dll_path = bin_path / arch
5884 if not dll_path .exists ():
5985 raise FileNotFoundError (f"The expected MATLAB folders were in found at the path: { dll_path } " )
60-
61- return f'{ bin_path .as_posix ()} /' , f'{ dll_path .as_posix ()} /'
86+
87+ return f"{ bin_path .as_posix ()} /" , f"{ dll_path .as_posix ()} /"
88+
6289
6390def start_matlab ():
64- """Start MATLAB asynchronously and returns a future to retrieve the engine later .
91+ """Load MATLAB engine dynamic libraries and creates wrapper object .
6592
6693 Returns
6794 -------
68- future : RATapi.rat_core.MatlabEngine
69- A custom matlab engine wrapper.
95+ engine : Optional[ RATapi.rat_core.MatlabEngine]
96+ A matlab engine object
7097
7198 """
7299 try :
73100 with open (MATLAB_PATH_FILE ) as path_file :
74101 matlab_path = path_file .read ()
75102 except FileNotFoundError :
76103 matlab_path = ""
77-
104+
78105 if not matlab_path :
79106 matlab_path = shutil .which ("matlab" )
80107 if matlab_path is None :
@@ -85,17 +112,16 @@ def start_matlab():
85112 if temp .is_symlink ():
86113 matlab_path = temp .readlink ().as_posix ()
87114 set_matlab_path (matlab_path )
88-
115+
89116 if matlab_path :
90117 bin_path , dll_path = get_matlab_paths (matlab_path )
91118 os .environ ["MATLAB_DLL_PATH" ] = dll_path
92- print (bin_path , dll_path )
93- if platform .system () == "Windows" :
119+ if platform .system () == "Windows" :
94120 os .environ ["PATH" ] = dll_path + os .pathsep + os .environ ["PATH" ]
95-
121+
96122 with cd (bin_path ):
97123 engine = RATapi .rat_core .MatlabEngine ()
98-
124+
99125 return engine
100126
101127
@@ -107,12 +133,15 @@ class MatlabWrapper:
107133 filename : string
108134 The path of the file containing MATLAB function
109135 """
136+
110137 engine = start_matlab ()
111-
138+
112139 def __init__ (self , filename ) -> None :
113140 if self .engine is None :
114- raise ValueError ("MATLAB is not found. Please use `set_matlab_path` to set the location of your MATLAB installation" ) from None
115-
141+ raise ValueError (
142+ "MATLAB is not found. Please use `set_matlab_path` to set the location of your MATLAB installation"
143+ ) from None
144+
116145 path = pathlib .Path (filename )
117146 self .engine .cd (str (path .parent ))
118147 self .engine .setFunction (path .stem )
@@ -133,7 +162,6 @@ def handle(*args):
133162 return handle
134163
135164
136-
137165class DylibWrapper :
138166 """Creates a python callback for a function in dynamic library.
139167
0 commit comments