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-deletedKeeping 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 doneTemporary 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 laterSecure 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/locationSpooledTemporaryFile
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 automaticTemp 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 upGenerating 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
- Always use context managers: Ensures cleanup
- Use NamedTemporaryFile for subprocesses: They need file paths
- Same filesystem for atomic rename:
os.replace()requires this - Set appropriate permissions: Default is secure (600)
- 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: