Media asset management for Python, with glue code for various web frameworks
Extensions and adapters for integrating WebAssets with popular Python web frameworks, enabling seamless asset management within existing applications.
Native integration with Jinja2 templating engine for asset management in templates.
class AssetsExtension:
def __init__(self, environment):
"""
Jinja2 template extension for assets.
Parameters:
- environment: Jinja2 Environment instance
"""
def parse(self, parser):
"""Parse {% assets %} template tags."""
def call_method(self, method, kwargs):
"""Handle method calls from templates."""
class Jinja2Loader:
def __init__(self, environment):
"""
Load bundles from Jinja2 templates.
Parameters:
- environment: Jinja2 Environment instance
"""
def load_bundles(self):
"""Load bundles defined in templates."""
def assets(*args, **kwargs):
"""
Template function for asset handling.
Parameters:
- *args: Asset arguments
- **kwargs: Asset options
Returns:
Asset URLs for template rendering
"""{# Load assets extension #}
{% extends "base.html" %}
{# Define and use assets in templates #}
{% assets "css_all" %}
<link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}" />
{% endassets %}
{% assets "js_all" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
{# Inline asset definition #}
{% assets filters="jsmin", output="gen/inline.js" %}
<script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}
{# Multiple files #}
{% assets "js/app.js", "js/utils.js", filters="uglifyjs", output="gen/combined.js" %}
<script src="{{ ASSET_URL }}"></script>
{% endassets %}from flask import Flask
from webassets import Environment, Bundle
from webassets.ext.jinja2 import AssetsExtension
app = Flask(__name__)
# Configure assets
assets = Environment(app)
assets.url = app.static_url_path
assets.directory = app.static_folder
# Add Jinja2 extension
assets.init_app(app)
app.jinja_env.add_extension(AssetsExtension)
app.jinja_env.assets_environment = assets
# Define bundles
js_bundle = Bundle(
'js/jquery.js',
'js/app.js',
filters='jsmin',
output='gen/packed.js'
)
css_bundle = Bundle(
'css/common.css',
'css/forms.css',
filters='cssmin',
output='gen/packed.css'
)
assets.register('js_all', js_bundle)
assets.register('css_all', css_bundle)
@app.route('/')
def index():
return render_template('index.html')Flask template usage:
<!DOCTYPE html>
<html>
<head>
{% assets "css_all" %}
<link rel="stylesheet" href="{{ ASSET_URL }}" />
{% endassets %}
</head>
<body>
<h1>My Flask App</h1>
{% assets "js_all" %}
<script src="{{ ASSET_URL }}"></script>
{% endassets %}
</body>
</html># settings.py
import os
from webassets import Environment, Bundle
# WebAssets configuration
ASSETS_ROOT = os.path.join(BASE_DIR, 'static')
ASSETS_URL = '/static/'
# Create assets environment
assets_env = Environment(ASSETS_ROOT, ASSETS_URL)
# Configure for production
if not DEBUG:
assets_env.cache = True
assets_env.versions = 'hash'
assets_env.manifest = 'json:manifest.json'
# Define bundles
js_bundle = Bundle(
'js/app.js',
'js/utils.js',
filters='jsmin',
output='gen/app.js'
)
css_bundle = Bundle(
'scss/main.scss',
filters=['libsass', 'cssmin'],
output='gen/app.css'
)
assets_env.register('js_app', js_bundle)
assets_env.register('css_app', css_bundle)
# Template context processor
def assets_context(request):
return {'assets_env': assets_env}
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'myapp.context_processors.assets_context',
],
},
},
]Django template usage:
<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
{% for url in assets_env.css_app.urls %}
<link rel="stylesheet" href="{{ url }}">
{% endfor %}
</head>
<body>
{% block content %}{% endblock %}
{% for url in assets_env.js_app.urls %}
<script src="{{ url }}"></script>
{% endfor %}
</body>
</html>from bottle import Bottle, static_file, jinja2_template
from webassets import Environment, Bundle
from webassets.ext.jinja2 import AssetsExtension
import jinja2
app = Bottle()
# Setup Jinja2 with assets
jinja_env = jinja2.Environment(
loader=jinja2.FileSystemLoader('templates')
)
jinja_env.add_extension(AssetsExtension)
# Configure assets
assets = Environment('./static', '/static')
jinja_env.assets_environment = assets
# Define bundles
js_bundle = Bundle(
'js/app.js',
'js/lib.js',
filters='jsmin',
output='gen/app.js'
)
assets.register('js_all', js_bundle)
@app.route('/')
def index():
template = jinja_env.get_template('index.html')
return template.render()
@app.route('/static/<filename:path>')
def static(filename):
return static_file(filename, root='./static')
if __name__ == '__main__':
app.run(host='localhost', port=8080, debug=True)from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from webassets import Environment, Bundle
from webassets.ext.jinja2 import AssetsExtension
app = FastAPI()
# Mount static files
app.mount("/static", StaticFiles(directory="static"), name="static")
# Setup templates with assets
templates = Jinja2Templates(directory="templates")
templates.env.add_extension(AssetsExtension)
# Configure assets
assets = Environment('./static', '/static')
templates.env.assets_environment = assets
# Define bundles
css_bundle = Bundle(
'css/bootstrap.css',
'css/app.css',
filters='cssmin',
output='gen/app.css'
)
js_bundle = Bundle(
'js/vue.js',
'js/app.js',
filters='jsmin',
output='gen/app.js'
)
assets.register('css_all', css_bundle)
assets.register('js_all', js_bundle)
@app.get("/")
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request})from pyramid.config import Configurator
from pyramid.response import Response
from webassets import Environment, Bundle
def assets_tween_factory(handler, registry):
"""Tween to add assets to request."""
def assets_tween(request):
# Add assets to request
request.assets = registry.settings['assets_env']
return handler(request)
return assets_tween
def main(global_config, **settings):
config = Configurator(settings=settings)
# Configure assets
assets = Environment('./static', '/static')
# Define bundles
js_bundle = Bundle(
'js/app.js',
filters='jsmin',
output='gen/app.js'
)
assets.register('js_all', js_bundle)
# Store in settings
config.registry.settings['assets_env'] = assets
# Add assets tween
config.add_tween('myapp.assets_tween_factory')
# Add routes
config.add_route('home', '/')
config.add_view(home_view, route_name='home', renderer='templates/home.pt')
# Add static view
config.add_static_view('static', './static')
return config.make_wsgi_app()
def home_view(request):
return {'assets': request.assets}from webassets import Environment
class FlaskAssetsEnvironment(Environment):
"""Flask-specific assets environment."""
def __init__(self, app=None):
self.app = app
super().__init__()
if app:
self.init_app(app)
def init_app(self, app):
"""Initialize with Flask app."""
self.app = app
# Configure from Flask settings
self.directory = app.static_folder or './static'
self.url = app.static_url_path or '/static'
self.debug = app.debug
# Use Flask's instance folder for cache
if hasattr(app, 'instance_path'):
self.cache = f"filesystem:{app.instance_path}/.webassets-cache"
# Add to app extensions
if not hasattr(app, 'extensions'):
app.extensions = {}
app.extensions['webassets'] = self
# Add template globals
app.jinja_env.globals['assets'] = self
# Add CLI commands
self._add_cli_commands(app)
def _add_cli_commands(self, app):
"""Add Flask CLI commands."""
@app.cli.command()
def build_assets():
"""Build all asset bundles."""
from webassets.script import CommandLineEnvironment
cmdline = CommandLineEnvironment(self)
cmdline.build()
print("Assets built successfully!")
@app.cli.command()
def clean_assets():
"""Clean built assets."""
from webassets.script import CommandLineEnvironment
cmdline = CommandLineEnvironment(self)
cmdline.clean()
print("Assets cleaned successfully!")
# Usage
from flask import Flask
app = Flask(__name__)
assets = FlaskAssetsEnvironment(app)
# Define bundles
assets.register('js_all',
'js/app.js',
'js/utils.js',
filters='jsmin',
output='gen/app.js'
)class WebAssetsMiddleware:
"""WSGI middleware for WebAssets."""
def __init__(self, app, assets_env):
self.app = app
self.assets_env = assets_env
def __call__(self, environ, start_response):
# Add assets to environ
environ['webassets.env'] = self.assets_env
# Auto-build in debug mode
if self.assets_env.debug:
self._auto_build()
return self.app(environ, start_response)
def _auto_build(self):
"""Auto-build assets in debug mode."""
from webassets.script import CommandLineEnvironment
cmdline = CommandLineEnvironment(self.assets_env)
try:
cmdline.build()
except Exception as e:
print(f"Asset build failed: {e}")
# Usage with any WSGI app
from webassets import Environment, Bundle
assets = Environment('./static', '/static', debug=True)
assets.register('js_all', 'app.js', filters='jsmin', output='gen/app.js')
# Wrap WSGI app
app = WebAssetsMiddleware(wsgi_app, assets)def create_asset_helpers(assets_env):
"""Create template helper functions."""
def asset_url(bundle_name):
"""Get single asset URL."""
bundle = assets_env[bundle_name]
urls = bundle.urls()
return urls[0] if urls else ''
def asset_urls(bundle_name):
"""Get all asset URLs."""
bundle = assets_env[bundle_name]
return bundle.urls()
def inline_asset(bundle_name):
"""Get asset content for inlining."""
bundle = assets_env[bundle_name]
bundle.build()
output_path = bundle.resolve_output()
full_path = os.path.join(assets_env.directory, output_path)
try:
with open(full_path, 'r') as f:
return f.read()
except IOError:
return ''
def asset_tag(bundle_name, tag_type='auto'):
"""Generate HTML tag for asset."""
urls = asset_urls(bundle_name)
tags = []
for url in urls:
if tag_type == 'auto':
if url.endswith('.css'):
tags.append(f'<link rel="stylesheet" href="{url}">')
elif url.endswith('.js'):
tags.append(f'<script src="{url}"></script>')
elif tag_type == 'css':
tags.append(f'<link rel="stylesheet" href="{url}">')
elif tag_type == 'js':
tags.append(f'<script src="{url}"></script>')
return '\n'.join(tags)
return {
'asset_url': asset_url,
'asset_urls': asset_urls,
'inline_asset': inline_asset,
'asset_tag': asset_tag
}
# Flask usage
app.jinja_env.globals.update(create_asset_helpers(assets))
# Template usage
# {{ asset_tag('css_all') }}
# {{ asset_tag('js_all') }}
# <style>{{ inline_asset('critical_css') }}</style>class FrameworkConfig:
"""Framework-specific configuration management."""
def __init__(self, framework='flask'):
self.framework = framework
self.bundles = {}
self.settings = {}
def configure_for_framework(self, app):
"""Configure assets for specific framework."""
if self.framework == 'flask':
return self._configure_flask(app)
elif self.framework == 'django':
return self._configure_django(app)
elif self.framework == 'fastapi':
return self._configure_fastapi(app)
def _configure_flask(self, app):
from webassets import Environment
assets = Environment(
directory=app.static_folder,
url=app.static_url_path,
debug=app.debug
)
# Framework-specific settings
if not app.debug:
assets.cache = f"filesystem:{app.instance_path}/.webassets-cache"
assets.versions = 'hash'
return assets
def _configure_django(self, settings):
from webassets import Environment
assets = Environment(
directory=settings.STATIC_ROOT,
url=settings.STATIC_URL,
debug=settings.DEBUG
)
if not settings.DEBUG:
assets.cache = 'filesystem'
assets.versions = 'hash'
assets.manifest = 'json:manifest.json'
return assets
def _configure_fastapi(self, app):
from webassets import Environment
assets = Environment(
directory='./static',
url='/static',
debug=True # Determine from app config
)
return assets
# Usage
config = FrameworkConfig('flask')
assets = config.configure_for_framework(app)Install with Tessl CLI
npx tessl i tessl/pypi-webassets