blues
blues

Reputation: 5195

Use contextmanager inside init

In the code below I don't understand why the with super().__init__(*args, **kwargs): line in MyFileIO2 is throwing an error about missing __exit__ while everything works perfectly fine with the MyFileIO class. I don't really understand what exactly the difference between doing the with inside or outside of the init is. Can someone enlighten me what is going on here?

import io

class MyFileIO(io.FileIO):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def __enter__(self, *args, **kwargs):
        f = super().__enter__(*args, **kwargs)
        print('first byte of file: ', f.read(1))
        return f

class MyFileIO2(io.FileIO):
    def __enter__(self, *args, **kwargs):
        f = super().__enter__(*args, **kwargs)
        print('first byte of file: ', f.read(1))
        return f

    def __init__(self, *args, **kwargs):
        with super().__init__(*args, **kwargs): # AttributeError: __exit__
            pass

path = 'some_file.bin'

with MyFileIO(path, 'rb'):
    pass

MyFileIO2(path, 'rb')

Upvotes: 0

Views: 952

Answers (1)

cs95
cs95

Reputation: 403278

You will need to call the context manager on self, because __init__ doesn't actually return anything.

class MyFileIO2(io.FileIO):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        with self:
             pass

    def __enter__(self, *args, **kwargs):
        f = super().__enter__(*args, **kwargs)
        print('First byte of file: ', f.read(1))
        return f

For testing, I created a binary file having the contents "hello world".

_ = MyFileIO2(path, 'rb')    
# First byte of file:  b'h'

What happens is the return value of super().__init__ is being passed through the context manager, so you effectively have this:

with None:
     pass

AttributeError: __enter__

The context manager tries calling the __enter__ method on the NoneType object, but that is an invalid operation.

Upvotes: 1

Related Questions