Skip to content Skip to sidebar Skip to footer

Custom 'with Open()' Statement In Python: Generator Didn't Yield Error

I have a class for a file, from which you can parse data, write data etc. I want to use it from any application like this: f = MyFileClass() # __init__ method puts a lot of default

Solution 1:

Try this:

@contextmanagerdefopen(self):
    try:
        yieldopen(os.path.join(self.directory, self.name), self.flags)
    except Exception as e:
        print(e.__traceback__)

Context managers are generators, not functions.

Solution 2:

Was going to comment but things turn out to be too complicated to leave there, but I do have an answer.

The corrected version of code can be reduced to essentially this

@contextmanagerdefmyopen(path):
    try:
        yieldopen(path)
    except Exception as e:
        print(e.__traceback__)

Before we try, let's get a count of open file handles using this:

>>> os.listdir('/proc/self/fd')
['0', '1', '2', '3']

Now use our context manager

>>> with myopen('/tmp/a_file') as f:
... print(f.read())
... print(os.listdir('/proc/self/fd'))
... 
Contents of file

['0', '1', '2', '3', '4']

Yup, file descriptor count increased, but now that we are out of our context manager, let's see

>>> print(os.listdir('/proc/self/fd'))
['0', '1', '2', '3', '4']

Uh that defeats the purpose of having a context manager for the file (we want to use the default autoclosing function, so restart the interpreter, and try this.

@contextmanagerdefmyopen(path):
    try:
        withopen(path) as f:
            yield f
    except Exception as e:
        print(e.__traceback__)

Rerun our tests

>>> with myopen('/tmp/a_file') as f:
... print(f.read())
... print(os.listdir('/proc/self/fd'))
... 
Contents of file

['0', '1', '2', '3', '4']

Now outside the context manager

>>> print(os.listdir('/proc/self/fd'))
['0', '1', '2', '3']

Yay, looks like it worked (file is successfully closed), but what about a path that does not exist to see that exception handling?

>>> with myopen('/tmp/no_path') as f:
...     print(f.read())
... 
<traceback object at 0x7f6b64b618c8>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.4/contextlib.py", line 61, in __enter__
    raise RuntimeError("generator didn't yield") from None
RuntimeError: generator didn't yield

The exception block did fire, note that traceback object, but then since the context manager did not correctly yield, a different exception is raised like what you saw before. I don't know what to recommend, but what I recommend is log the error (using a logger) and then reraise the exception. You could consider returning a dummy object of some kind that will then raise the exception when read, or return nothing, but you need to decide on what exactly is best for your case.

Post a Comment for "Custom 'with Open()' Statement In Python: Generator Didn't Yield Error"