MATLAB Answers

1

MATLAB Crashes when Using Conda Environment Other than Base

Asked by Peter Cook on 6 Feb 2019
Latest activity Commented on by Julian Hapke on 9 Apr 2019
I am having trouble using a custom conda environment in MATLAB. Here's the code I use to bootstrap a conda environment with a few packages I need from from the web:
% bootstrap a miniconda installation & python 35 environment
% choose a miniconda version,
% !!! avoid version 4.5.12 due to openSSL DLL hell !!!
condaUrl = 'https://repo.anaconda.com/miniconda/';
s = webread(condaUrl, weboptions('ContentType','text'));
v = unique(strcat('Miniconda3',regexp(s,'(?<=>Miniconda3)[^<>]*x86_64\.exe','match')));
vn = regexp(v,'(?<=3-)(\d*\.\d*\.\d*)','match');
verMask = cellfun(@isempty, vn);
v(verMask) = [];
vn(verMask) = [];
whichVer = listdlg('ListString',v,...
'PromptString','Select Miniconda Version',...
'SelectionMode','single');
if isempty(whichVer)
% use the latest and greatest
urlString = 'https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe';
else
% use selected ver
urlString = strcat(condaUrl, v{whichVer});
end
fprintf('Downloading Miniconda3 Installer\n');
targetDir = websave('miniconda3.exe', urlString);
fprintf('Miniconda3 Download Complete\n');
fprintf('Installing Miniconda3\n');
disp('start /wait "" miniconda3.exe /InstallationType=JustMe /RegisterPython=0 /S /D=%UserProfile%\Miniconda')
!start /wait "" miniconda3.exe /InstallationType=JustMe /RegisterPython=0 /S /D=%UserProfile%\Miniconda
fprintf('Miniconda3 Installation Complete\n');
% build a custom conda environment for MATLAB
pyver = 3.5;
myenv = sprintf('myenv%0.0f', pyver*10);
condaCmdPath = fullfile(getenv('USERPROFILE'),'Miniconda\Scripts\activate.bat');
minicondaRoot = fullfile(getenv('USERPROFILE'),'Miniconda');
myenvRoot = fullfile(minicondaRoot, 'envs', myenv);
matlabPythonRoot = fullfile(matlabroot, 'extern', 'engines', 'python');
% write a bat file to run in condaprompt
fptr = fopen('condaSetup.bat','w');
fprintf(fptr, 'CALL %c%s%c %c%s%c\r\n', 34, condaCmdPath, 34, 34, minicondaRoot, 34);
% fprintf(fptr, '%ccomspec%c /k %c%c%s%c %c%s%c%c\r\n', 37, 37, 34, 34, condaCmdPath, 34, 34, minicondaRoot, 34, 34);
fprintf(fptr, 'CALL conda config --add channels conda-forge\r\n');
fprintf(fptr, 'CALL conda config --set proxy_servers.http http://np1prxy801:80\r\n');
fprintf(fptr, 'CALL conda config --set proxy_servers.https https://np1prxy801:80\r\n');
fprintf(fptr, 'CALL conda create -v -y -n %s python=%0.1f numpy scipy scikit-learn sklearn-contrib-py-earth\r\n', myenv, pyver);
fprintf(fptr, 'CALL conda activate %s\r\n', myenv);
fprintf(fptr, 'cd %s\r\n', matlabPythonRoot);
fprintf(fptr, 'python setup.py build -b %s install\r\n', myenvRoot);
fclose(fptr);
% run "makefile"
dos('condaSetup.bat','-echo');
% delete the bat file
delete 'condaSetup.bat'
% add this python environment to startup.m by appending these two lines:
% !%comspec% /c ""C:\Users\HB69954\Miniconda\Scripts\activate.bat" "C:\Users\HB69954\Miniconda\envs\myenv35""
% pyversion c:\users\hb69954\miniconda\envs\myenv35\python.exe
w = which('startup.m');
fptr = fopen(w,'r');
s = fread(fptr, inf, 'uint8=>char')';
fclose(fptr);
activatePyString = '!%comspec% /c ""C:\Users\HB69954\Miniconda\Scripts\activate.bat" "C:\Users\HB69954\Miniconda\envs\myenv35""';
swapPyVersionString = 'pyversion c:\users\hb69954\miniconda\envs\myenv35\python.exe';
fptr = fopen(w,'w');
fprintf(fptr,'%s\r\n%s\r\n%s\r\n', s, activatePyString, swapPyVersionString);
fclose(fptr);
This script downloads Miniconda3, installs it, and creates a conda virtual environment called myenv35.
By default, MATLAB chooses my base environment as the python version:
>> pyversion
version: '3.7'
executable: 'C:\Users\HB69954\Miniconda\python.exe'
library: 'C:\Users\HB69954\Miniconda\python37.dll'
home: 'C:\Users\HB69954\Miniconda'
isloaded: 1
I added these two lines to startup.m to load this python environment instead of the base environment:
!%comspec% /c ""C:\Users\HB69954\Miniconda\Scripts\activate.bat" "C:\Users\HB69954\Miniconda\envs\myenv35""
pyversion c:\users\hb69954\miniconda\envs\myenv35\python.exe
This apparently loads in MATLAB,
>> pyversion
version: '3.5'
executable: 'c:\users\hb69954\miniconda\envs\myenv35\python.exe'
library: 'c:\users\hb69954\miniconda\envs\myenv35\python35.dll'
home: 'c:\users\hb69954\miniconda\envs\myenv35'
isloaded: 0
I can use python standard library functions in this environment, i.e.
>> myList = py.list([1,2,3,0,8,9,7])
myList =
Python list with no properties.
[1.0, 2.0, 3.0, 0.0, 8.0, 9.0, 7.0]
>> myList.sort()
>> myList
myList =
Python list with no properties.
[0.0, 1.0, 2.0, 3.0, 7.0, 8.0, 9.0]
But MATLAB crashes whenever I try to use a library that installed in the virtual environment (i.e. numpy), MATLAB closes without issuing an error or warning.
x = py.numpy.array([1,2,3])
taskmgr doesn't show matlab.exe running in the background, and no crash dump files are created in %USERPROFILE%\AppData\Local\Temp, leading me to believe its not a real crash as much as some file/function is issuing an exit/close command to matlab.exe.
  1. Are python/conda environments supported in MATLAB? I can't think of a reason why they wouldn't be, but I haven't seen any official support documentation for them either.
  2. Could this be related to me installing MATLAB Engine for Python in the same virtual environment?
  3. Is there any other reason why swapping the python environment could be causing MATLAB to crash while running environment packages?

  0 Comments

Sign in to comment.

1 Answer

Answer by Julian Hapke on 19 Mar 2019

Try this:
load your environment in a seperate promt, have a look at the path variable (assuming windows here)
echo %PATH%
unload the environment (back to base), look at the %PATH% again and find out the differences. There should be some paths from the custom evironment added to the path variable.
Add those path inside matlab, for example with:
pyExec = 'C:\software\miniconda3\envs\mlpy\python.exe';
pyRoot = fileparts(pyExec);
p = getenv('PATH');
p = strsplit(p, ';');
addToPath = {
pyRoot
fullfile(pyRoot, 'Library', 'mingw-w64', 'bin')
fullfile(pyRoot, 'Library', 'usr', 'bin')
fullfile(pyRoot, 'Library', 'bin')
fullfile(pyRoot, 'Scripts')
fullfile(pyRoot, 'bin')
};
p = [addToPath(:); p(:)];
p = unique(p, 'stable');
p = strjoin(p, ';');
setenv('PATH', p);
after that MATLAB should be able to use packages from that conda environment.

  2 Comments

Apologies for the late response Julian,
I took your suggestion and see that python appends the following directories to path when I activate my conda environment and removes them when I deactivate :
'C:\Users\HB69954\Miniconda\envs\myenv35'
'C:\Users\HB69954\Miniconda\envs\myenv35\Library\bin'
'C:\Users\HB69954\Miniconda\envs\myenv35\Library\mingw-w64\bin'
'C:\Users\HB69954\Miniconda\envs\myenv35\Library\usr\bin'
'C:\Users\HB69954\Miniconda\envs\myenv35\Scripts'
'C:\Users\HB69954\Miniconda\envs\myenv35\bin'
and when I activate my python base environment, I get these:
'C:\Users\HB69954\Miniconda\Library\mingw-w64\bin'
'C:\Users\HB69954\Miniconda\Library\usr\bin'
'C:\Users\HB69954\Miniconda\bin'
'C:\Program Files\dotnet\'
I do not wish to append the environment specific python variables to PATH with setenv (or py.sys.path() ) permanently, as I use other python environments outside of MATLAB and don't want path shadowing.
But looking into that brought something interesting to light. I noticed multiple entries in path to pywin32_system32. I then looked into a couple python 3 versions of pywin32.pth and found an interesting difference.
Python 3.6:
# .pth file for the PyWin32 extensions
win32
win32\lib
Pythonwin
# Entries needed for a "portable" installations, where the post_install script
# isn't run, which would normally copy the pywin32 core DLL files to either
# the top of the python directory.
# We just stick the source of these DLLs directly on the PATH.
import os;os.environ["PATH"]+=(';'+os.path.join(sitedir,"pywin32_system32"))
Python 3.7:
# .pth file for the PyWin32 extensions
win32
win32\lib
Pythonwin
# This breaks any software involving a feedback loop involving PATH and Python
# One example of such software is the Anaconda Distribition's conda package
# manager, so we do not do it. We fixed this in a slightly better way anyway.
# Entries needed for a "portable" installations, where the post_install script
# isn't run, which would normally copy the pywin32 core DLL files to either
# the top of the python directory.
# We just stick the source of these DLLs directly on the PATH.
# import os;os.environ["PATH"]+=(';'+os.path.join(sitedir,"pywin32_system32"))
Two telling things from the comments in this second find file: 1. We find the cause of the crash and 2. Python 3.7 does not append this (command is muted) to the path whereas python < 3.7 does (possibly also explaining the addition of C:\Program Files\dotnet, a directly full of MSVC DLLs, to path). Since my base environment is python 3.7 this explains the sometimes working, sometimes breaking behavior seen in MATLAB when swapping to the 3.5 environment.
Adding to %PATH% with setenv should only modify the PATH in the current MATLAB Session. Outside of MATLAB everything should remain default, so I don't see a problem there.

Sign in to comment.