Home Tutorials Standard Library

Standard Library

Python's tempfile Module: Safe Temporary Files and Directories

Pyford Notes July 6, 2026 6 min read
Key points
  • tempfile guarantees a unique filename and secure permissions — hand-rolling a name from a timestamp risks collisions and, on multi-user systems, information leaks.
  • NamedTemporaryFile(delete=True) (the default) deletes the file the moment it's closed, which on some platforms means it can't be reopened by name while still open — a frequent source of confusion on Windows.
  • TemporaryDirectory() is a context manager that recursively deletes the entire directory tree on exit, even if files were added to it after creation.
  • mkstemp()/mkdtemp() are lower-level: they return a name and leave cleanup to the caller, useful when a context manager's lifetime doesn't match the file's actual lifetime.

Why not just make up a filename

A tempting shortcut is building a path from the process ID or the current time. This has two real problems: the name can collide with another process using a similar naming scheme, and on a shared multi-user system, predictable names in a world-writable directory are a known vector for symlink attacks, where another user pre-creates a symlink at the guessed path pointing somewhere sensitive. tempfile solves both by generating names that are unpredictable and by creating files with permissions that only the owner can read or write:

import tempfile

with tempfile.NamedTemporaryFile(mode="w", suffix=".csv") as f:
    f.write("id,name\n1,Ada\n")
    f.flush()
    process_file(f.name)

NamedTemporaryFile and the delete gotcha

NamedTemporaryFile gives back a file object with a real path (.name) on disk, which matters when another function or subprocess needs a path string rather than an open file object. The catch is the default delete=True behaviour: the file is removed the instant it's closed, and on Windows specifically, an open temp file typically can't be opened a second time by another process while the first handle is still held, because Windows locks open files more aggressively than POSIX systems do. The portable fix is delete=False, combined with manually removing the file afterward:

f = tempfile.NamedTemporaryFile(suffix=".txt", delete=False)
try:
    f.write(b"data")
    f.close()
    subprocess.run(["some-tool", f.name])
finally:
    os.unlink(f.name)

This is one of the more common cross-platform bugs in code that was only ever tested on Linux or macOS.

TemporaryDirectory: a self-cleaning folder

When a task needs to create several related files — extracted archive contents, intermediate build artifacts — TemporaryDirectory() creates an empty directory and recursively deletes everything inside it when the with block ends, regardless of what got added afterward:

with tempfile.TemporaryDirectory() as tmpdir:
    extract_path = os.path.join(tmpdir, "extracted")
    shutil.unpack_archive("data.zip", extract_path)
    process_directory(extract_path)
# tmpdir and everything inside it is gone here

This is meaningfully safer than manually calling shutil.rmtree() in a finally block, since the cleanup happens automatically even if an exception is raised partway through populating the directory, and there's no risk of forgetting the cleanup call in one code path.

Lower-level: mkstemp and mkdtemp

Both context-manager wrappers are built on lower-level functions that hand back a name (or an open file descriptor) and leave cleanup entirely to the caller:

fd, path = tempfile.mkstemp(suffix=".log")
os.close(fd)   # mkstemp returns a raw file descriptor, not a file object
# ... use path ...
os.unlink(path)

tmpdir_path = tempfile.mkdtemp()
# ... use tmpdir_path ...
shutil.rmtree(tmpdir_path)

These are the right tools when a temp file or directory needs to outlive a single with block — for example, a path that gets handed off to a background task and cleaned up much later by different code. mkstemp() returning a raw OS-level file descriptor rather than a Python file object is the detail most likely to trip someone moving from NamedTemporaryFile: it needs os.fdopen(fd) or os.close(fd), not fd.write().

Finding the temp directory location

tempfile.gettempdir() returns the directory all of the above use by default, chosen from a platform-specific list of candidates (environment variables like TMPDIR, then OS-specific fallback paths). Every function above also accepts an explicit dir= argument to override this, which matters when the default temp filesystem is too small, too slow, or on a different disk than where the eventual output needs to land — copying a large temp file across filesystems afterward can cost more time than choosing the right dir= up front.