This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: NamedTemporaryFile deleted before enclosing context manager exit
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.9
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Brian McCutchon, zach.ware
Priority: normal Keywords:

Created on 2021-11-17 21:22 by Brian McCutchon, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (2)
msg406494 - (view) Author: Brian McCutchon (Brian McCutchon) Date: 2021-11-17 21:22
Consider the following code:

# Copyright 2021 Google LLC.
# SPDX-License-Identifier: Apache-2.0
import contextlib
import os

@contextlib.contextmanager
def my_tmp_file():
  with tempfile.NamedTemporaryFile('w') as f:
    yield f

os.stat(my_tmp_file().__enter__().name)  # File not found
os.stat(contextlib.ExitStack().enter_context(my_tmp_file()).name)  # Same

I would expect the file to still exist, as __exit__ has not been called and I can't see why the file would have been closed. Also, it performs as expected when using NamedTemporaryFile directly, but not when it is nested in another context manager. It also performs as expected when my_tmp_file() or contextlib.ExitStack() is used in a "with" statement.
msg406504 - (view) Author: Zachary Ware (zach.ware) * (Python committer) Date: 2021-11-17 22:58
It's a bit convoluted, but the file is actually deleted before the `os.stat` call.  Because there are no references to anything but the `name` (which is just a string), the `_GeneratorContextManager` (result of `my_tmp_file`) and the `_TemporaryFileWrapper` (result of `my_tmp_file().__enter__()`) are both destroyed.  Because `NamedTemporaryFile` is called with `delete=True` (default), the `_TemporaryFileWrapper` has a `_closer` attribute which is a `_TemporaryFileCloser`, which calls `self.close()` in `__del__`, which deletes the file.

If a reference to the result of `my_tmp_file()` is saved anywhere along the way, none of the objects are destroyed and the file still exists.  This also wouldn't happen in an implementation without reference counting.
History
Date User Action Args
2022-04-11 14:59:52adminsetgithub: 89991
2021-11-22 22:11:49zach.waresetstatus: pending -> closed
stage: resolved
2021-11-17 22:58:01zach.waresetstatus: open -> pending

nosy: + zach.ware
messages: + msg406504

resolution: not a bug
2021-11-17 21:22:11Brian McCutchoncreate