英文:
Why does my context manager not exit on exception
问题
以下是您要翻译的部分:
Original Implementation:
原始实现:
@contextmanager
def open_read(path: str):
f = open(path, 'r')
print('open')
yield f
f.close()
print('closed')
def foo():
try:
with open_read('main.py') as f:
print(f.readline())
raise Exception('oopsie')
except Exception:
pass
print(f.readline())
foo()
I expect this code to print:
我期望这段代码输出:
open
<line 1 of a.txt>
closed
ValueError: I/O operation on closed file.
But instead it prints:
但实际上它输出:
open
<line 1 of a.txt>
<line 2 of a.txt>
It didn't close the file!
它没有关闭文件!
This seems to contradict python's docs which state that __exit__
will be called whether the with
statement exited successfully or with an exception:
这似乎与Python的文档相矛盾,文档中指出__exit__
将被调用,无论with
语句是成功退出还是出现异常退出。
object.exit(self, exc_type, exc_value, traceback)
Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None.
Interestingly, when I reimplemented the context manager as shown below, it worked as expected:
有趣的是,当我按照下面所示重新实现上下文管理器时,它按预期工作:
class open_read(ContextDecorator):
def __init__(self, path: str):
self.path = path
self.f = None
def __enter__(self):
self.f = open(self.path, 'r')
print('open')
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
print('closed')
Why didn't my original implementation work?
为什么我的原始实现不起作用?
英文:
I am learning about context managers and was trying to build one myself. The following is a dummy context manager that opens a file in read mode (I know I can just do with open(...): ...
. this is just an example I built to help me understand how to make my own context managers):
@contextmanager
def open_read(path: str):
f = open(path, 'r')
print('open')
yield f
f.close()
print('closed')
def foo():
try:
with open_read('main.py') as f:
print(f.readline())
raise Exception('oopsie')
except Exception:
pass
print(f.readline())
foo()
I expect this code to print:
open
<line 1 of a.txt>
closed
ValueError: I/O operation on closed file.
But instead it prints:
open
<line 1 of a.txt>
<line 2 of a.txt>
It didn't close the file!
This seems to contradict python's docs which state that __exit__
will be called whether the with
statement exited successfully or with an exception:
> object.exit(self, exc_type, exc_value, traceback)
>
> Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None.
Interestingly, when I reimplemented the context manager as shown below, it worked as expected:
class open_read(ContextDecorator):
def __init__(self, path: str):
self.path = path
self.f = None
def __enter__(self):
self.f = open(self.path, 'r')
print('open')
return self.f
def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()
print('closed')
Why didn't my original implementation work?
答案1
得分: 5
The line f.close()
is never reached (we exit that frame early due to unhandled exception), and then the exception was "handled" in the outer frame (i.e. within foo
).
If you want it to close regardless, you'll have to implement it like that:
@contextmanager
def open_read(path: str):
f = open(path, 'r')
try:
print('open')
yield f
finally:
f.close()
print('closed')
However, I'd like to point out that the built-in open
is already returning a context-manager, and you may be reinventing stdlib contextlib.closing
.
英文:
The line f.close()
is never reached (we exit that frame early due to unhandled exception), and then the exception was "handled" in the outer frame (i.e. within foo
).
If you want it to close regardless, you'll have to implement it like that:
@contextmanager
def open_read(path: str):
f = open(path, 'r')
try:
print('open')
yield f
finally:
f.close()
print('closed')
However, I'd like to point out that that the built-in open
is already returning a context-manager, and you may be reinventing stdlib contextlib.closing
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论