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
Helper functions for directory operations, relative URL generation, and file system utilities. These functions support the core freezing operations and provide additional functionality for working with static sites.
Functions for traversing and processing directory structures.
def walk_directory(root, ignore=()):
"""
Walk directory and yield slash-separated paths relative to root.
Used to implement URL generator for static files and file discovery.
Parameters:
- root: Directory path to walk (str or Path)
- ignore: List of fnmatch patterns to ignore
Yields:
str: Relative file paths with forward slashes
Ignore patterns:
- Patterns with slash are matched against full path
- Patterns without slash match individual path components
- Patterns ending with '/' match directories
"""Functions for generating relative URLs and handling URL transformations.
def relative_url_for(endpoint, **values):
"""
Like Flask's url_for but returns relative URLs when possible.
Absolute URLs (with _external=True or different subdomain) are unchanged.
Relative URLs are converted based on current request context path.
URLs ending with '/' get 'index.html' appended for static compatibility.
Parameters:
- endpoint: Flask endpoint name
- **values: URL parameters (same as url_for)
Returns:
str: Relative URL or absolute URL if necessary
Note: Should only be used with Frozen-Flask, not in live Flask apps
"""from flask_frozen import walk_directory
from pathlib import Path
# Basic directory traversal
static_dir = Path('static')
for filepath in walk_directory(static_dir):
print(f"Found file: {filepath}")
# Output: css/style.css, js/app.js, images/logo.png, etc.# Ignore specific files and directories
ignore_patterns = [
'*.pyc', # Ignore Python bytecode
'*.pyo', # Ignore optimized bytecode
'__pycache__/', # Ignore Python cache directories
'node_modules/', # Ignore Node.js dependencies
'*.scss', # Ignore SCSS source files
'src/*', # Ignore entire src directory
'.DS_Store', # Ignore macOS system files
]
for filepath in walk_directory('assets', ignore=ignore_patterns):
print(f"Will be included: {filepath}")# Advanced ignore patterns
patterns = [
# Pattern matching examples:
'*.log', # Matches any .log file
'temp/', # Matches temp directory
'draft-*', # Matches files starting with 'draft-'
'*/cache/*', # Matches cache directories anywhere
'build/*.map', # Matches .map files in build directory
'.git*', # Matches .git, .gitignore, etc.
]
# Custom file filtering
def custom_file_filter(root_dir):
files = []
for filepath in walk_directory(root_dir, ignore=patterns):
# Additional custom filtering
if not filepath.startswith('_'): # Skip files starting with underscore
files.append(filepath)
return filesfrom flask import Flask, request
from flask_frozen import relative_url_for
app = Flask(__name__)
@app.route('/blog/<slug>/')
def blog_post(slug):
return f'Blog post: {slug}'
# In a template or view context
with app.test_request_context('/blog/python-tips/'):
# From /blog/python-tips/ to /blog/flask-guide/
url = relative_url_for('blog_post', slug='flask-guide')
print(url) # Output: ../flask-guide/
# From /blog/python-tips/ to homepage
url = relative_url_for('index')
print(url) # Output: ../../When FREEZER_RELATIVE_URLS is enabled, templates automatically use relative_url_for:
<!-- In Jinja2 template -->
<a href="{{ url_for('blog_post', slug='python-tips') }}">Python Tips</a>
<!-- Generates relative URL like: ../python-tips/ -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<!-- Generates relative URL like: ../../static/css/style.css -->from flask_frozen import walk_directory
import os
def find_assets(asset_type):
"""Find assets of a specific type"""
extensions = {
'images': ['*.jpg', '*.png', '*.gif', '*.svg'],
'styles': ['*.css'],
'scripts': ['*.js'],
'fonts': ['*.woff', '*.woff2', '*.ttf', '*.eot'],
}
if asset_type not in extensions:
return []
# Ignore everything except the desired asset type
ignore_patterns = []
for other_type, exts in extensions.items():
if other_type != asset_type:
ignore_patterns.extend(exts)
assets = []
for filepath in walk_directory('static', ignore=ignore_patterns):
assets.append(filepath)
return assets
# Find all images
images = find_assets('images')
print(f"Found {len(images)} images")from flask_frozen import walk_directory
import json
import hashlib
from pathlib import Path
def create_asset_manifest(static_dir):
"""Create a manifest of all static files with hashes"""
manifest = {}
for filepath in walk_directory(static_dir):
full_path = Path(static_dir) / filepath
# Calculate file hash for cache busting
with open(full_path, 'rb') as f:
file_hash = hashlib.md5(f.read()).hexdigest()[:8]
# Add to manifest
name, ext = filepath.rsplit('.', 1)
hashed_name = f"{name}.{file_hash}.{ext}"
manifest[filepath] = {
'hashed': hashed_name,
'size': full_path.stat().st_size,
'hash': file_hash,
}
return manifest
# Create and save manifest
manifest = create_asset_manifest('static')
with open('build/assets-manifest.json', 'w') as f:
json.dump(manifest, f, indent=2)from flask_frozen import walk_directory
import shutil
from pathlib import Path
def sync_directories(source, target, ignore_patterns=None):
"""Sync directories with ignore patterns"""
if ignore_patterns is None:
ignore_patterns = []
source_path = Path(source)
target_path = Path(target)
target_path.mkdir(parents=True, exist_ok=True)
# Copy files that match criteria
for filepath in walk_directory(source, ignore=ignore_patterns):
src_file = source_path / filepath
dst_file = target_path / filepath
# Create directory if needed
dst_file.parent.mkdir(parents=True, exist_ok=True)
# Copy file
shutil.copy2(src_file, dst_file)
print(f"Copied: {filepath}")
# Example usage
sync_directories(
'assets',
'build/assets',
ignore_patterns=['*.scss', '*/src/*', '.DS_Store']
)These utilities integrate seamlessly with the Freezer class:
from flask import Flask
from flask_frozen import Freezer, walk_directory
app = Flask(__name__)
freezer = Freezer(app)
# Custom static file generator using walk_directory
@freezer.register_generator
def custom_assets():
asset_dirs = ['media', 'uploads', 'downloads']
for asset_dir in asset_dirs:
if Path(asset_dir).exists():
for filepath in walk_directory(asset_dir):
# Generate URL for each asset file
yield f'/{asset_dir}/{filepath}'
# Use relative URLs in frozen site
app.config['FREEZER_RELATIVE_URLS'] = True
freezer.freeze()Install with Tessl CLI
npx tessl i tessl/pypi-frozen-flask