I have a python package and I would like to use its classes and methods in Matlab. I know that this can be done directly since Matlab 2014b. I mean all you have to do is add py.
in the beginning of your statements. So far so good, however, I couldn't figure out how to deal with context managers through MATLAB, which are invoked using the with
statement. For instance, assume that we have the following class in a module called app.py,
class App(object):
def __init__(self, input):
self._input = input
self._is_open = False
def __enter__(self):
self._is_open = True
# many other stuff going after this but not relevant to this problem
In Matlab, I can call this as
app = py.app.App(input);
py.getattr(app, '_is_open')
ans =
logical
0
and I see an instance of App in my workspace. However, as expected only __init__
is invoked this way but not __enter__
.
So, is there a way to invoke __enter__
from Matlab, as if we are calling it like with App(input) as app:
in Python?
Note: I am using Python 3.5.1 and Matlab 2017b
I don't believe there is any way to invoke the __enter__
method of a Python class from MATLAB, but the __exit__
method might be implicitly called (I'll address this further below).
It's important to first consider the purpose of context managers (via the __enter__
and __exit__
methods), which is to provide a way to allocate and release resources in a scope-limited fashion, whether or not that scope is exited normally or via an error. MATLAB has a more limited means of "scoping": each function has its own workspace, and control structures like loops, conditional statements, etc. within that function all share that workspace (unlike many languages in which these control structures have their own sub-scopes).
When a workspace is exited in MATLAB, the variables it contains are cleared, but any resources that were allocated may still need to be released. This can be achieved with onCleanup
objects. When they are cleared from memory, they invoke a given function for managing existing resources. An example would be opening and reading from a file:
function openFileSafely(fileName)
fid = fopen(fileName, 'r');
c = onCleanup(@() fclose(fid));
s = fread(fid);
...
end
Here, a file is opened and subsequently read from. An onCleanup
object c
is created that will close the file when c
is cleared from memory upon exit from the function. If the file were simply closed with fclose(fid)
at the end of the function, then an error exit from the function (such as during the course of reading data) would cause the file to still remain opened. Using an onCleanup
object ensures that the file will be closed regardless of how the function exits. Here's an example of how this could be handled in Python:
with open('some_file', 'w') as opened_file:
opened_file.write('Hola!')
Since MATLAB has a different means of "context management" than Python, this may explain why it's not possible to access the __enter__
method. I tried with a class I knew had one: the io.FileIO
class. I first looked for help:
>> py.help('io.FileIO.__enter__')
Help on method_descriptor in io.FileIO:
io.FileIO.__enter__ = __enter__(...)
It finds some help text. It's not particularly helpful, but it's there. However, when I create an object and look at its methods list, neither __enter__
nor __exit__
(nor a clear equivalent) is there:
>> fio = py.io.FileIO('test.txt');
>> methods(fio)
Methods for class py._io.FileIO:
FileIO eq ge le read readinto seek truncate writelines
char fileno gt lt readable readline seekable writable
close flush isatty ne readall readlines tell write
Methods of py._io.FileIO inherited from handle.
Methods for class handle:
addlistener eq findprop gt le ne
delete findobj ge isvalid lt notify
I did notice something interesting when I cleared the fio
object, though. While the fio
object still existed (with the file open), I couldn't delete or move the file, as expected. However, after issuing the command clear fio
, without first closing the file, I was able to interact with the file normally. This implies that the file was automatically closed. This makes me wonder if the __exit__
method might be getting implicitly invoked, but I have yet to determine it for certain.