Freezes a Flask application into a set of static files.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive configuration system for controlling build behavior, file handling, error processing, and output customization. Frozen-Flask automatically sets sensible defaults on your Flask application's configuration.
All configuration options are set on the Flask app's config object and prefixed with FREEZER_.
Control where and how static files are generated.
# Build destination directory (relative to app.root_path)
app.config['FREEZER_DESTINATION'] = 'build' # default
# Files to ignore in destination directory
app.config['FREEZER_DESTINATION_IGNORE'] = [] # default
# Remove files from previous build not in current build
app.config['FREEZER_REMOVE_EXTRA_FILES'] = True # defaultConfiguration for URL generation and path processing.
# Base URL for the site (affects url_for calls)
app.config['FREEZER_BASE_URL'] = None # default
# Use relative URLs in templates
app.config['FREEZER_RELATIVE_URLS'] = False # defaultControl static file discovery and processing.
# Static files to ignore during freezing
app.config['FREEZER_STATIC_IGNORE'] = [] # defaultConfiguration for content type processing and validation.
# Default MIME type for files with unknown extensions
app.config['FREEZER_DEFAULT_MIMETYPE'] = 'application/octet-stream' # default
# Skip MIME type mismatch warnings
app.config['FREEZER_IGNORE_MIMETYPE_WARNINGS'] = False # defaultControl how errors and edge cases are processed.
# Ignore 404 responses (useful during development)
app.config['FREEZER_IGNORE_404_NOT_FOUND'] = False # default
# How to handle redirects: 'follow' or 'ignore'
app.config['FREEZER_REDIRECT_POLICY'] = 'follow' # defaultSettings for build performance and incremental updates.
# Skip files that haven't changed (boolean or callable)
app.config['FREEZER_SKIP_EXISTING'] = False # defaultfrom flask import Flask
from flask_frozen import Freezer
app = Flask(__name__)
# Configure build destination
app.config['FREEZER_DESTINATION'] = 'dist'
app.config['FREEZER_REMOVE_EXTRA_FILES'] = True
freezer = Freezer(app)# Production build with relative URLs for CDN deployment
app.config.update({
'FREEZER_DESTINATION': 'public',
'FREEZER_RELATIVE_URLS': True,
'FREEZER_BASE_URL': 'https://mysite.com/subpath/',
'FREEZER_REMOVE_EXTRA_FILES': True,
'FREEZER_IGNORE_MIMETYPE_WARNINGS': True,
})# Development build with lenient error handling
app.config.update({
'FREEZER_DESTINATION': 'dev-build',
'FREEZER_IGNORE_404_NOT_FOUND': True,
'FREEZER_REDIRECT_POLICY': 'ignore',
'FREEZER_REMOVE_EXTRA_FILES': False,
})# Ignore specific files and patterns
app.config.update({
'FREEZER_DESTINATION_IGNORE': [
'.DS_Store',
'*.log',
'temp/*',
'draft-*',
],
'FREEZER_STATIC_IGNORE': [
'*.scss',
'*.coffee',
'src/*',
'node_modules/*',
],
})# Skip unchanged files for faster rebuilds
def should_skip_file(url, filepath):
# Custom logic for determining if file should be skipped
import os
from datetime import datetime, timedelta
if not os.path.exists(filepath):
return False
# Skip if modified less than 1 hour ago
mtime = datetime.fromtimestamp(os.path.getmtime(filepath))
return datetime.now() - mtime < timedelta(hours=1)
app.config['FREEZER_SKIP_EXISTING'] = should_skip_fileimport os
class Config:
FREEZER_DESTINATION = 'build'
FREEZER_REMOVE_EXTRA_FILES = True
class DevelopmentConfig(Config):
FREEZER_IGNORE_404_NOT_FOUND = True
FREEZER_REDIRECT_POLICY = 'ignore'
class ProductionConfig(Config):
FREEZER_DESTINATION = 'public'
FREEZER_RELATIVE_URLS = True
FREEZER_IGNORE_MIMETYPE_WARNINGS = True
FREEZER_BASE_URL = os.environ.get('SITE_BASE_URL', 'https://example.com')
# Apply configuration based on environment
if app.config.get('ENV') == 'production':
app.config.from_object(ProductionConfig)
else:
app.config.from_object(DevelopmentConfig)# Configuration for deploying to CDN with relative URLs
app.config.update({
'FREEZER_RELATIVE_URLS': True,
'FREEZER_BASE_URL': 'https://cdn.example.com/mysite/',
'FREEZER_DESTINATION': 'cdn-build',
'FREEZER_STATIC_IGNORE': [
'*.map', # Ignore source maps
'dev/*', # Ignore dev-only assets
],
})# Configuration for building multiple site variants
def configure_for_site(site_name):
site_configs = {
'main': {
'FREEZER_DESTINATION': 'build/main',
'FREEZER_BASE_URL': 'https://example.com',
},
'blog': {
'FREEZER_DESTINATION': 'build/blog',
'FREEZER_BASE_URL': 'https://blog.example.com',
},
'docs': {
'FREEZER_DESTINATION': 'build/docs',
'FREEZER_BASE_URL': 'https://docs.example.com',
'FREEZER_RELATIVE_URLS': True,
},
}
app.config.update(site_configs[site_name])
# Build different site variants
for site in ['main', 'blog', 'docs']:
configure_for_site(site)
freezer.freeze()Frozen-Flask validates configuration automatically, but you can add custom validation:
def validate_freezer_config():
"""Validate Frozen-Flask configuration"""
config = app.config
# Ensure destination is set
assert config.get('FREEZER_DESTINATION'), "FREEZER_DESTINATION must be set"
# Validate redirect policy
policy = config.get('FREEZER_REDIRECT_POLICY', 'follow')
assert policy in ['follow', 'ignore'], f"Invalid redirect policy: {policy}"
# Warn about potentially problematic settings
if config.get('FREEZER_RELATIVE_URLS') and not config.get('FREEZER_BASE_URL'):
import warnings
warnings.warn("FREEZER_RELATIVE_URLS is True but FREEZER_BASE_URL is not set")
# Validate before freezing
validate_freezer_config()
freezer.freeze()Install with Tessl CLI
npx tessl i tessl/pypi-frozen-flask