Media asset management for Python, with glue code for various web frameworks
Comprehensive command-line tools for building assets, managing bundles, and integrating with deployment workflows through the webassets command.
Main interface class for command-line asset management operations.
class CommandLineEnvironment:
def __init__(self, env, log_level='INFO'):
"""
Command-line interface for webassets.
Parameters:
- env: Environment instance
- log_level: Logging level for output
"""
def build(self, bundles=None, force=False, no_cache=False, production=False):
"""
Build bundles from command line.
Parameters:
- bundles: Specific bundles to build (None for all)
- force: Force rebuild even if cache is valid
- no_cache: Disable caching for this build
- production: Use production settings
"""
def watch(self, bundles=None):
"""
Watch files and rebuild automatically.
Parameters:
- bundles: Specific bundles to watch (None for all)
"""
def clean(self):
"""Clean generated files and cache."""
def check(self):
"""Check bundle configuration for errors."""Foundation classes for implementing command-line operations.
class Command:
name = None
help = None
def add_parser(self, subparsers):
"""Add command parser to argument parser."""
def run(self, args, env):
"""
Execute command.
Parameters:
- args: Parsed command arguments
- env: Environment instance
"""
class BuildCommand(Command):
name = 'build'
help = 'Build asset bundles'
def run(self, args, env):
"""Execute build command."""
class WatchCommand(Command):
name = 'watch'
help = 'Watch files and rebuild automatically'
def run(self, args, env):
"""Execute watch command."""
class CleanCommand(Command):
name = 'clean'
help = 'Clean generated files'
def run(self, args, env):
"""Execute clean command."""
class CheckCommand(Command):
name = 'check'
help = 'Check bundle configuration'
def run(self, args, env):
"""Execute check command."""Primary function for command-line interface execution.
def main(argv=None):
"""
Main entry point for command-line interface.
Parameters:
- argv: Command line arguments (defaults to sys.argv)
Returns:
Exit code (0 for success, non-zero for error)
"""Exception handling for command-line operations.
class CommandError(Exception):
"""Raised by command-line operations."""# Build all bundles
webassets build
# Build specific bundles
webassets build js_all css_all
# Force rebuild (ignore cache)
webassets build --force
# Build without cache
webassets build --no-cache
# Production build
webassets build --production
# Watch for changes and rebuild
webassets watch
# Watch specific bundles
webassets watch js_all
# Clean generated files
webassets clean
# Check configuration
webassets check# Build with custom environment file
webassets -c assets.yaml build
# Build with verbose output
webassets -v build
# Build with specific log level
webassets --log-level DEBUG build
# Build and specify output directory
webassets --env-directory ./dist build
# Build with custom configuration
webassets --env-url /static --env-debug false buildCreate an assets.yaml configuration file:
directory: ./static
url: /static
debug: false
cache: filesystem
auto_build: false
bundles:
js_all:
contents:
- js/app.js
- js/utils.js
filters: uglifyjs
output: gen/app.js
css_all:
contents:
- css/main.css
- css/layout.css
filters: cssmin
output: gen/app.cssThen use it:
# Use configuration file
webassets -c assets.yaml build
# Override configuration options
webassets -c assets.yaml --env-debug buildfrom webassets.script import CommandLineEnvironment
from webassets import Environment
# Create environment
env = Environment('./static', '/static')
# Register bundles
env.register('js_all', 'app.js', 'utils.js', filters='jsmin', output='gen/app.js')
env.register('css_all', 'style.css', filters='cssmin', output='gen/style.css')
# Create command-line interface
cmdline = CommandLineEnvironment(env)
# Build all bundles
cmdline.build()
# Build specific bundles
cmdline.build(['js_all'])
# Force rebuild
cmdline.build(force=True)
# Clean generated files
cmdline.clean()
# Check configuration
cmdline.check()from webassets.script import Command
class DeployCommand(Command):
name = 'deploy'
help = 'Deploy assets to CDN'
def add_parser(self, subparsers):
parser = subparsers.add_parser(self.name, help=self.help)
parser.add_argument('--cdn-url', help='CDN base URL')
parser.add_argument('--aws-profile', help='AWS profile to use')
return parser
def run(self, args, env):
# Build assets first
cmdline = CommandLineEnvironment(env)
cmdline.build(production=True)
# Deploy to CDN
import boto3
s3 = boto3.client('s3', profile_name=args.aws_profile)
for bundle_name, bundle in env:
output_files = bundle.urls()
for file_url in output_files:
local_path = os.path.join(env.directory, file_url.lstrip('/'))
s3_key = file_url.lstrip('/')
s3.upload_file(local_path, 'my-assets-bucket', s3_key)
print(f"Deployed {file_url} to CDN")
# Register custom command
from webassets.script import main
import sys
def custom_main():
# Add custom command to available commands
from webassets.script import commands
commands.append(DeployCommand())
# Run main CLI
return main()
if __name__ == '__main__':
sys.exit(custom_main())# Makefile
SHELL := /bin/bash
.PHONY: assets assets-watch assets-clean assets-check
assets:
webassets build
assets-watch:
webassets watch
assets-clean:
webassets clean
assets-check:
webassets check
# Production build
assets-prod:
webassets build --production
# Development setup
dev-setup: assets-clean assets
# Full deployment
deploy: assets-clean assets-prod
# Additional deployment steps{
"scripts": {
"build": "webassets build",
"build:prod": "webassets build --production",
"watch": "webassets watch",
"clean": "webassets clean",
"check": "webassets check"
}
}FROM python:3.11
# Install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy assets configuration
COPY assets.yaml .
# Copy source assets
COPY static/ ./static/
# Build assets
RUN webassets -c assets.yaml build --production
# Copy application code
COPY . .
CMD ["python", "app.py"]# .github/workflows/assets.yml
name: Build Assets
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-assets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
- name: Check asset configuration
run: webassets check
- name: Build assets
run: webassets build --production
- name: Upload assets
uses: actions/upload-artifact@v3
with:
name: built-assets
path: static/gen/pipeline {
agent any
stages {
stage('Install Dependencies') {
steps {
sh 'pip install -r requirements.txt'
}
}
stage('Check Assets') {
steps {
sh 'webassets check'
}
}
stage('Build Assets') {
steps {
sh 'webassets build --production'
}
}
stage('Archive Assets') {
steps {
archiveArtifacts artifacts: 'static/gen/**/*', fingerprint: true
}
}
}
}# Development
export WEBASSETS_ENV=development
webassets build
# Staging
export WEBASSETS_ENV=staging
webassets build --production
# Production
export WEBASSETS_ENV=production
webassets build --production --no-cacheWith corresponding configuration files:
# assets.py
import os
env_name = os.environ.get('WEBASSETS_ENV', 'development')
if env_name == 'development':
directory = './src/static'
debug = True
cache = False
elif env_name == 'staging':
directory = './build/static'
debug = False
cache = True
versions = 'timestamp'
elif env_name == 'production':
directory = './dist/static'
debug = False
cache = True
versions = 'hash'
manifest = 'json:manifest.json'#!/usr/bin/env python
"""
Automated asset pipeline script
"""
import os
import sys
from webassets.script import CommandLineEnvironment
from webassets.loaders import YAMLLoader
def build_assets(config_file='assets.yaml', environment='production'):
"""Build assets with specified configuration."""
# Load configuration
loader = YAMLLoader(config_file)
env = loader.load_environment()
# Override environment settings
if environment == 'production':
env.debug = False
env.cache = True
env.versions = 'hash'
env.manifest = 'json:manifest.json'
elif environment == 'development':
env.debug = True
env.cache = False
env.versions = False
# Create command line interface
cmdline = CommandLineEnvironment(env, log_level='INFO')
# Check configuration
print("Checking asset configuration...")
cmdline.check()
# Clean previous build
print("Cleaning previous build...")
cmdline.clean()
# Build assets
print(f"Building assets for {environment}...")
cmdline.build(production=(environment == 'production'))
print("Asset build completed successfully!")
if __name__ == '__main__':
env = sys.argv[1] if len(sys.argv) > 1 else 'production'
build_assets(environment=env)Usage:
python build_assets.py development
python build_assets.py productionInstall with Tessl CLI
npx tessl i tessl/pypi-webassets