Temporary files need careful handling—security, cleanup, and cross-platform compatibility. The tempfile module handles all of this.

Temporary Files with Auto-Cleanup

import tempfile
 
# File deleted when closed
with tempfile.TemporaryFile(mode='w+') as f:
    f.write('temporary data')
    f.seek(0)
    print(f.read())
# File automatically deleted here
 
# Named temporary file (can be accessed by name)
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt') as f:
    print(f.name)  # /tmp/tmpxyz123.txt
    f.write('data')
# Also auto-deleted

Keeping Temporary Files

import tempfile
from pathlib import Path
 
# Don't delete on close
with tempfile.NamedTemporaryFile(delete=False, suffix='.json') as f:
    temp_path = Path(f.name)
    f.write(b'{"key": "value"}')
 
# File persists, clean up manually
print(temp_path.read_text())
temp_path.unlink()  # Delete when done

Temporary Directories

import tempfile
from pathlib import Path
 
# Auto-cleanup directory
with tempfile.TemporaryDirectory() as tmpdir:
    tmp_path = Path(tmpdir)
    
    # Create files in temp dir
    (tmp_path / 'file1.txt').write_text('content 1')
    (tmp_path / 'file2.txt').write_text('content 2')
    
    # Process files...
    print(list(tmp_path.iterdir()))
# Entire directory deleted here
 
# Keep directory
tmpdir = tempfile.mkdtemp(prefix='myapp_')
print(tmpdir)  # /tmp/myapp_xyz123
# Clean up manually later

Secure File Creation

import tempfile
import os
 
# mkstemp returns (file_descriptor, path)
fd, path = tempfile.mkstemp(suffix='.key', prefix='secret_')
 
try:
    # Write using file descriptor
    os.write(fd, b'secret data')
finally:
    os.close(fd)
    os.unlink(path)
 
# mkdtemp creates secure directory
secure_dir = tempfile.mkdtemp(prefix='private_')
print(oct(os.stat(secure_dir).st_mode))  # 0o700 (owner-only)

Custom Temp Directory

import tempfile
 
# Use specific directory
with tempfile.NamedTemporaryFile(dir='/custom/path') as f:
    print(f.name)  # /custom/path/tmpxyz123
 
# Change default for all operations
tempfile.tempdir = '/my/temp/location'
 
# Check current temp directory
print(tempfile.gettempdir())  # /my/temp/location

SpooledTemporaryFile

Small data stays in memory, spills to disk when large:

import tempfile
 
# max_size: bytes before spilling to disk
with tempfile.SpooledTemporaryFile(max_size=1024*1024, mode='w+') as f:
    # Small data stays in memory
    f.write('small data')
    
    # Check if rolled over to disk
    print(f._rolled)  # False
    
    # Force rollover
    f.rollover()
    print(f._rolled)  # True
    
    f.seek(0)
    print(f.read())

Processing Large Downloads

import tempfile
import urllib.request
from pathlib import Path
 
def download_to_temp(url: str) -> Path:
    """Download file to temp location."""
    suffix = Path(url).suffix or '.tmp'
    
    with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as f:
        temp_path = Path(f.name)
        
        with urllib.request.urlopen(url) as response:
            while chunk := response.read(8192):
                f.write(chunk)
    
    return temp_path
 
# Usage
path = download_to_temp('https://example.com/file.zip')
try:
    process_file(path)
finally:
    path.unlink()

Atomic File Writes

Write to temp, then rename for atomicity:

import tempfile
import os
from pathlib import Path
 
def atomic_write(path: str, content: str) -> None:
    """Write file atomically using temp file."""
    target = Path(path)
    
    # Create temp file in same directory
    fd, temp_path = tempfile.mkstemp(
        dir=target.parent,
        prefix='.tmp_'
    )
    
    try:
        os.write(fd, content.encode())
        os.close(fd)
        
        # Atomic rename
        os.replace(temp_path, path)
    except:
        os.close(fd)
        os.unlink(temp_path)
        raise
 
# Usage - other processes see complete file or nothing
atomic_write('config.json', '{"key": "value"}')

Testing with Temp Files

import tempfile
import pytest
from pathlib import Path
 
@pytest.fixture
def temp_workspace():
    """Provide temporary directory for tests."""
    with tempfile.TemporaryDirectory() as tmpdir:
        yield Path(tmpdir)
 
def test_file_processing(temp_workspace):
    # Create test file
    input_file = temp_workspace / 'input.txt'
    input_file.write_text('test data')
    
    # Run processing
    output_file = temp_workspace / 'output.txt'
    process(input_file, output_file)
    
    # Verify
    assert output_file.exists()
    assert output_file.read_text() == 'processed: test data'
    # Cleanup automatic

Temp File Context Manager

import tempfile
import os
from contextlib import contextmanager
from pathlib import Path
 
@contextmanager
def temp_file_with_content(content: str, suffix: str = '.txt'):
    """Create temp file with content, auto-cleanup."""
    with tempfile.NamedTemporaryFile(
        mode='w', 
        suffix=suffix, 
        delete=False
    ) as f:
        f.write(content)
        temp_path = Path(f.name)
    
    try:
        yield temp_path
    finally:
        temp_path.unlink()
 
# Usage
with temp_file_with_content('test data', suffix='.json') as path:
    process_file(path)
# File cleaned up

Generating Unique Names

import tempfile
import os
 
# Get unique name without creating file
name = tempfile.mktemp(suffix='.txt')  # Deprecated, race condition risk
print(name)
 
# Better: use NamedTemporaryFile with delete=False
# or generate name and create atomically
 
def unique_path(directory: str, prefix: str, suffix: str) -> str:
    """Generate unique path in directory."""
    import secrets
    while True:
        name = f"{prefix}{secrets.token_hex(8)}{suffix}"
        path = os.path.join(directory, name)
        if not os.path.exists(path):
            return path
 
path = unique_path('/tmp', 'data_', '.csv')

Cross-Platform Considerations

import tempfile
import sys
 
# Get system temp directory
print(tempfile.gettempdir())
# Linux/macOS: /tmp
# Windows: C:\Users\...\AppData\Local\Temp
 
# Set custom temp directory
if sys.platform == 'win32':
    tempfile.tempdir = 'D:\\Temp'
else:
    tempfile.tempdir = '/var/tmp'

Cleanup on Crash

import tempfile
import atexit
import os
 
# Track temp files for emergency cleanup
_temp_files = []
 
def create_tracked_temp(suffix=''):
    """Create temp file that's cleaned up on exit."""
    fd, path = tempfile.mkstemp(suffix=suffix)
    _temp_files.append(path)
    return fd, path
 
def cleanup_all():
    """Clean up all tracked temp files."""
    for path in _temp_files:
        try:
            os.unlink(path)
        except OSError:
            pass
 
atexit.register(cleanup_all)

Best Practices

  1. Always use context managers: Ensures cleanup
  2. Use NamedTemporaryFile for subprocesses: They need file paths
  3. Same filesystem for atomic rename: os.replace() requires this
  4. Set appropriate permissions: Default is secure (600)
  5. Don't use mktemp(): Race condition vulnerability

Temporary files seem simple but have subtle pitfalls. The tempfile module handles security, cleanup, and cross-platform concerns—use it instead of rolling your own.

React to this post: