Skip to content

Commit 42885b5

Browse files
committed
version 26.1.2
1 parent a7a48d8 commit 42885b5

4 files changed

Lines changed: 276 additions & 13 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The MATLAB® Engine API for Python® provides a package to integrate MATLA
2121
MATLAB Engine API for Python can be installed directly from the Python Package Index.
2222
<!-- MUST_BE_UPDATED_EACH_RELEASE (Search repo for this string) -->
2323
```bash
24-
$ python -m pip install matlabengine==26.1.1
24+
$ python -m pip install matlabengine==26.1.2
2525
```
2626

2727

@@ -46,7 +46,7 @@ setenv LD_LIBRARY_PATH ${LD_LIBRARY_PATH}:<matlabroot>/bin/glnxa64
4646
MATLAB Engine API for Python can be installed directly from the Python Package Index.
4747
<!-- MUST_BE_UPDATED_EACH_RELEASE (Search repo for this string) -->
4848
```bash
49-
$ python -m pip install matlabengine==26.1.1
49+
$ python -m pip install matlabengine==26.1.2
5050
```
5151

5252
### macOS
@@ -70,7 +70,7 @@ setenv DYLD_LIBRARY_PATH ${DYLD_LIBRARY_PATH}:<matlabroot>/bin/maci64
7070
MATLAB Engine API for Python can be installed directly from the Python Package Index.
7171
<!-- MUST_BE_UPDATED_EACH_RELEASE (Search repo for this string) -->
7272
```bash
73-
$ python -m pip install matlabengine==26.1.1
73+
$ python -m pip install matlabengine==26.1.2
7474
```
7575

7676
---

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class _MatlabFinder(build_py):
2424
MATLAB_REL = 'R2026a'
2525

2626
# MUST_BE_UPDATED_EACH_RELEASE (Search repo for this string)
27-
MATLAB_VER = '26.1.1'
27+
MATLAB_VER = '26.1.2'
2828

2929
# MUST_BE_UPDATED_EACH_RELEASE (Search repo for this string)
3030
SUPPORTED_PYTHON_VERSIONS = set(['3.9', '3.10', '3.11', '3.12', '3.13'])
@@ -420,7 +420,7 @@ def run(self):
420420
setup(
421421
name="matlabengine",
422422
# MUST_BE_UPDATED_EACH_RELEASE (Search repo for this string)
423-
version="26.1.1",
423+
version="26.1.2",
424424
description='A module to call MATLAB from Python',
425425
author='MathWorks',
426426
license="LICENSE.txt, located in this repository",

src/matlab/__init__.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ def put_extern_bin_on_py_sys_path(self):
125125
dir_to_search = os.path.join('runtime', self.arch)
126126
trailing_substrings_to_find = [dir_to_search, dir_to_search + os.sep]
127127

128+
# First, look for a path ending with "runtime/<arch>". If found, use it
129+
# to find a path ending with "extern/bin/<arch>".
128130
extern_bin_dir = ''
129131
for elem in path_elements:
130132
for trailing_substring in trailing_substrings_to_find:
@@ -133,6 +135,19 @@ def put_extern_bin_on_py_sys_path(self):
133135
if extern_bin_dir:
134136
break
135137

138+
if not extern_bin_dir:
139+
# Look for a path ending with "extern/bin/<arch>".
140+
dir_to_search = os.path.join('extern', 'bin', self.arch)
141+
trailing_substrings_to_find = [dir_to_search, dir_to_search + os.sep]
142+
143+
extern_bin_dir = ''
144+
for elem in path_elements:
145+
for trailing_substring in trailing_substrings_to_find:
146+
if elem.endswith(trailing_substring):
147+
extern_bin_dir = elem
148+
if extern_bin_dir:
149+
break
150+
136151
if not extern_bin_dir:
137152
format_str = 'Could not find an appropriate directory in {0} from which to read binaries. Details::\n{1}'
138153
raise RuntimeError(format_str.format(self.path_var, path_elements_orig))
@@ -172,6 +187,13 @@ def add_dll_dir_on_win(arch, dir_to_add):
172187
if not dir_to_add:
173188
raise RuntimeError('Cannot add empty DLL directory')
174189
os.add_dll_directory(dir_to_add)
190+
191+
# For the case of Windows, we also need to add the bin/win64 folder to the PATH
192+
if 'PATH' in os.environ:
193+
_env = os.environ['PATH']
194+
os.environ['PATH'] = dir_to_add + os.pathsep + _env
195+
else:
196+
os.environ['PATH'] = dir_to_add
175197

176198
def get_dirs_from_arch_file_without_import():
177199
_PYTHONVERSION = get_python_version()
@@ -207,14 +229,23 @@ def subdir_exists(folder_name, subfolder_name):
207229
if subdir_exists(_package_folder, 'engine'):
208230
success = get_dirs_from_arch_file_without_import()
209231
if not success:
210-
if 'MWE_INSTALL' in os.environ:
211-
mroot_from_env_var = os.environ['MWE_INSTALL']
212-
extern_bin_dir = os.path.join(mroot_from_env_var, 'extern', 'bin', _mpi.arch)
213-
if not os.path.exists(extern_bin_dir):
214-
raise RuntimeError('directory {} does not exist'.format(extern_bin_dir))
215-
sys.path.insert(0, extern_bin_dir)
216-
bin_dir = extern_bin_dir.replace('extern' + os.sep + 'bin', 'bin')
217-
add_dll_dir_on_win(_mpi.arch, bin_dir)
232+
# Use the location of this file (already stored in _package_folder) to construct _bin_dir, _engine_dir, and _extern_bin_dir,
233+
# so that they are added to the Python sys.path and the OS path environment variable as necessary.
234+
235+
#Use _package_folder <matlabroot>\extern\engines\python\dist\matlab to derive <matlabroot>\bin\win64
236+
#<matlabroot>\extern\engines\python\dist\matlab\..\..\..\..\..\bin\win64
237+
_bin_dir = os.path.join(_package_folder, '..', '..', '..', '..' ,'..', 'bin', _mpi.arch)
238+
239+
if os.path.exists(_bin_dir):
240+
add_dll_dir_on_win(_mpi.arch, _bin_dir)
241+
242+
#Use _package_folder <matlabroot>\extern\engines\python\dist\matlab to derive <matlabroot>\extern\engines\python\dist\matlab\engine\win64
243+
_engine_dir = os.path.join(_package_folder, 'engine', _mpi.arch)
244+
sys.path.insert(0,_engine_dir)
245+
246+
#Use _bin_dir <matlabroot>\bin\win64 to derive <matlabroot>\extern\bin\win64
247+
_extern_bin_dir = os.path.join(_bin_dir, '..', '..', 'extern', 'bin', _mpi.arch)
248+
sys.path.insert(0,_extern_bin_dir)
218249
else:
219250
raise RuntimeError('unable to read {}'.format(get_arch_filename()))
220251
else:

src/matlab/__init__.py.bak

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# Copyright 2021-2025 MathWorks, Inc.
2+
"""
3+
Array interface between Python and MATLAB
4+
5+
This package defines classes and exceptions that create and manage
6+
multidimensional arrays in Python that are passed between Python and MATLAB.
7+
8+
Modules
9+
-------
10+
* mcpyarray - type-specific multidimensional array classes for working
11+
with MATLAB, implemented in C++
12+
"""
13+
14+
import os
15+
import platform
16+
import sys
17+
import warnings
18+
from pkgutil import extend_path
19+
__path__ = extend_path(__path__, '__name__')
20+
21+
_package_folder = os.path.dirname(os.path.realpath(__file__))
22+
sys.path.append(_package_folder)
23+
24+
# This code allows us to put the proper extern/bin/<arch> directory on the Python path
25+
# to avoid a situation in which some shared libraries are loaded from a MATLAB while
26+
# others are loaded from a runtime. The first directory on the path that contains
27+
# the string "bin/<arch>" (with the proper directory separator)
28+
# will be checked. If it is "extern/bin/<arch>", it will be used as the
29+
# extern/bin/<arch> directory. Otherwise, we'll go up two directories and down
30+
# to extern/bin/<arch>.
31+
class _MiniPathInitializer(object):
32+
PLATFORM_DICT = {'Windows': 'PATH', 'Linux': 'LD_LIBRARY_PATH', 'Darwin': 'DYLD_LIBRARY_PATH'}
33+
34+
def __init__(self):
35+
self.arch = ''
36+
self.extern_bin_dir = ''
37+
self.path_var = ''
38+
self.system = ''
39+
40+
def get_platform_info(self):
41+
"""Ask Python for the platform and architecture."""
42+
# This will return 'Windows', 'Linux', or 'Darwin' (for Mac).
43+
self.system = platform.system()
44+
if not self.system in _MiniPathInitializer.PLATFORM_DICT:
45+
raise RuntimeError('{0} is not a supported platform.'.format(self.system))
46+
else:
47+
# path_var is the OS-dependent name of the path variable ('PATH', 'LD_LIBRARY_PATH', "DYLD_LIBRARY_PATH')
48+
self.path_var = _MiniPathInitializer.PLATFORM_DICT[self.system]
49+
50+
if self.system == 'Windows':
51+
self.arch = 'win64'
52+
elif self.system == 'Linux':
53+
self.arch = 'glnxa64'
54+
elif self.system == 'Darwin':
55+
# determine ARM or Intel Mac machine
56+
if platform.mac_ver()[-1] == 'arm64':
57+
self.arch = 'maca64'
58+
else:
59+
self.arch = 'maci64'
60+
else:
61+
raise RuntimeError('Operating system {0} is not supported.'.format(self.system))
62+
63+
def get_extern_bin_from_path_element_ending_with_runtime_arch(self, dir_found):
64+
extern_bin_dir = ''
65+
path_components = dir_found.split(os.sep)
66+
67+
if path_components[-1]:
68+
last_path_component = path_components[-1]
69+
possible_extern = -3
70+
else:
71+
# The directory name ended with a slash, so the last item in the list was an empty string. Go back one more.
72+
last_path_component = path_components[-2]
73+
possible_extern = -4
74+
75+
if last_path_component != self.arch:
76+
output_str = ''.join(('To call deployed MATLAB code on a {0} machine, you must run a {0} version of Python, ',
77+
'and your {1} variable must contain an element pointing to "<MR>{2}runtime{2}{0}", ',
78+
'where "<MR>" indicates a MATLAB or MATLAB Runtime root. ',
79+
'Instead, the value found was as follows: {3}'))
80+
raise RuntimeError(output_str.format(self.arch, self.path_var, os.sep, dir_found))
81+
82+
if (len(path_components) + possible_extern) >= 0 and path_components[possible_extern] == 'extern':
83+
extern_bin_dir = dir_found
84+
else:
85+
mroot = os.path.dirname(os.path.dirname(os.path.normpath(dir_found)))
86+
extern_bin_dir = os.path.join(mroot, 'extern', 'bin', self.arch)
87+
88+
return extern_bin_dir
89+
90+
def get_extern_bin_from_py_sys_path(self):
91+
#Retrieve Python sys.path as a single string, and search for the substring "extern/bin/<arch>" (with
92+
#the proper directory separator). If it's already present, assume it's the one we want.
93+
substr_to_find = os.path.join('extern', 'bin', self.arch)
94+
for item in sys.path:
95+
if item.find(substr_to_find) != -1:
96+
return item
97+
return ''
98+
99+
def put_extern_bin_on_py_sys_path(self):
100+
"""
101+
Look through the system path for the first directory ending with "runtime/<arch>" or
102+
"bin/<arch>" (with/without trailing slash). Use this to construct a new path ending
103+
with "extern/bin/<arch>".
104+
"""
105+
106+
path_elements = []
107+
path_elements_orig = ''
108+
if self.path_var in os.environ:
109+
path_elements_orig = os.environ[self.path_var]
110+
# On Windows, some elements of the path may use forward slashes while others use backslashes.
111+
# Make them all backslashes.
112+
if self.system == 'Windows':
113+
path_elements_orig = path_elements_orig.replace('/', '\\')
114+
path_elements = path_elements_orig.split(os.pathsep)
115+
if not path_elements:
116+
if self.system == 'Darwin':
117+
raise RuntimeError('On the Mac, you must run mwpython rather than python ' +
118+
'to start a session or script that imports your package. ' +
119+
'For more details, execute "mwpython -help" or see the package documentation.')
120+
else:
121+
raise RuntimeError('On {0}, you must set the environment variable "{1}" to a non-empty string. {2}'.format(
122+
self.system, self.path_var,
123+
'For more details, see the package documentation.'))
124+
125+
dir_to_search = os.path.join('runtime', self.arch)
126+
trailing_substrings_to_find = [dir_to_search, dir_to_search + os.sep]
127+
128+
extern_bin_dir = ''
129+
for elem in path_elements:
130+
for trailing_substring in trailing_substrings_to_find:
131+
if elem.endswith(trailing_substring):
132+
extern_bin_dir = self.get_extern_bin_from_path_element_ending_with_runtime_arch(elem)
133+
if extern_bin_dir:
134+
break
135+
136+
if not extern_bin_dir:
137+
format_str = 'Could not find an appropriate directory in {0} from which to read binaries. Details::\n{1}'
138+
raise RuntimeError(format_str.format(self.path_var, path_elements_orig))
139+
140+
if not os.path.isdir(extern_bin_dir):
141+
raise RuntimeError('Could not find the directory {0}'.format(extern_bin_dir))
142+
self.extern_bin_dir = extern_bin_dir
143+
sys.path.insert(0, self.extern_bin_dir)
144+
145+
def get_python_version():
146+
# UPDATE_IF_PYTHON_VERSION_ADDED_OR_REMOVED : search for this string in codebase
147+
# when support for a Python version must be added or removed
148+
_supported_versions = ['3_9', '3_10', '3_11', '3_12', '3_13']
149+
_ver = sys.version_info
150+
_version = '{0}_{1}'.format(_ver[0], _ver[1])
151+
newer_than_supported = _ver[1] > 12
152+
153+
_PYTHONVERSION = None
154+
155+
if _version in _supported_versions:
156+
_PYTHONVERSION = _version
157+
elif newer_than_supported:
158+
warnings.warn('Python versions 3.9, 3.10, 3.11, 3.12, and 3.13 are supported, but your version of Python is %s' % _version)
159+
_PYTHONVERSION = _version
160+
else:
161+
raise EnvironmentError("Python %s is not supported." % _version)
162+
163+
return _PYTHONVERSION
164+
165+
def get_arch_filename():
166+
_module_folder = os.path.dirname(os.path.realpath(__file__))
167+
_arch_filename = os.path.join(_module_folder, 'engine', '_arch.txt')
168+
return _arch_filename
169+
170+
def add_dll_dir_on_win(arch, dir_to_add):
171+
if arch == 'win64':
172+
if not dir_to_add:
173+
raise RuntimeError('Cannot add empty DLL directory')
174+
os.add_dll_directory(dir_to_add)
175+
176+
def get_dirs_from_arch_file_without_import():
177+
_PYTHONVERSION = get_python_version()
178+
_arch_filename = get_arch_filename()
179+
firstExceptionMessage = ''
180+
if not os.path.isfile(_arch_filename):
181+
return False
182+
183+
try:
184+
_arch_file = open(_arch_filename,'r')
185+
_lines = _arch_file.readlines()
186+
[_arch, _bin_dir,_engine_dir, _extern_bin_dir] = [x.rstrip() for x in _lines if x.rstrip() != ""]
187+
_arch_file.close()
188+
sys.path.insert(0,_engine_dir)
189+
sys.path.insert(0,_extern_bin_dir)
190+
add_dll_dir_on_win(_arch, _bin_dir)
191+
except Exception as exc:
192+
firstExceptionMessage = 'Please contact MathWorks Technical Support for assistance:\nDetails: {}'.format(
193+
exc)
194+
195+
if firstExceptionMessage:
196+
return False
197+
else:
198+
return True
199+
200+
def subdir_exists(folder_name, subfolder_name):
201+
return os.path.exists(os.path.join(folder_name, subfolder_name))
202+
203+
204+
_mpi = _MiniPathInitializer()
205+
_mpi.get_platform_info()
206+
extern_bin_dir = ''
207+
if subdir_exists(_package_folder, 'engine'):
208+
success = get_dirs_from_arch_file_without_import()
209+
if not success:
210+
if 'MWE_INSTALL' in os.environ:
211+
mroot_from_env_var = os.environ['MWE_INSTALL']
212+
extern_bin_dir = os.path.join(mroot_from_env_var, 'extern', 'bin', _mpi.arch)
213+
if not os.path.exists(extern_bin_dir):
214+
raise RuntimeError('directory {} does not exist'.format(extern_bin_dir))
215+
sys.path.insert(0, extern_bin_dir)
216+
bin_dir = extern_bin_dir.replace('extern' + os.sep + 'bin', 'bin')
217+
add_dll_dir_on_win(_mpi.arch, bin_dir)
218+
else:
219+
raise RuntimeError('unable to read {}'.format(get_arch_filename()))
220+
else:
221+
extern_bin_dir = _mpi.get_extern_bin_from_py_sys_path()
222+
if not extern_bin_dir:
223+
_mpi.put_extern_bin_on_py_sys_path()
224+
extern_bin_dir = _mpi.extern_bin_dir
225+
226+
bin_dir = extern_bin_dir.replace('extern' + os.sep + 'bin', 'bin')
227+
add_dll_dir_on_win(_mpi.arch, bin_dir)
228+
229+
230+
from matlabmultidimarrayforpython import double, single, uint8, int8, uint16, \
231+
int16, uint32, int32, uint64, int64, logical, ShapeError, SizeError
232+

0 commit comments

Comments
 (0)