Skip to main content

h-assets

Pyramid views for serving collections of compiled static assets (eg. bundles of JavaScript and CSS).

Compared to Pyramid's builtin static asset functionality , this provides a convenient way to serve assets based on certain assumptions about how assets are generated and opinions about how they should be served:

  • The assets are assumed to be compiled artefacts in an output directory populated by frontend build tooling, rather than source files inside the Python package. Typically Hypothesis applications use a build directory in the root of the repository.
  • Cache busting is always enabled and is done via query strings. These query strings are checked, if present, when serving a request to avoid responses being stored under the wrong keys in downstream caches.
  • It is assumed that compressing bytes (eg. with gzip or Brotli) will be handled by a service like Cloudflare, not the Python application.

Additionally h-assets provides a way to define collections (bundles) of assets and methods to generate cache-busted URLs for all assets in the bundle. This is useful for example to render all the <script> or <style> tags that are needed by a certain part of a site.

Usage

Using h-assets in a Pyramid project involves three steps:

  1. Prepare the compiled assets for use with h-assets
  2. During Pyramid application configuration, create an asset Environment to handle asset URL generation and register a view to serve assets from that environment
  3. Expose the URL-generation methods from the asset Environment to your templating system so that templates can generate asset URLs

Preparing assets for use with h-assets

  1. Set up a process to compile or copy assets from source files into an output directory. Conventionally Hypothesis projects use a folder called build in the repository root.

  2. In the output directory generate a JSON manifest file (eg. manifest.json) that maps asset paths to URLs with cache-busting query strings. Example content:

    {
      "scripts/app.bundle.js": "scripts/app.bundle.js?abcdef",
      "scripts/vendor.bundle.js": "scripts/vendor.bundle.js?xyz123"
    }
    

    Any format is allowed for the cache-buster. Hypothesis projects typically use the first few characters of a hash (eg. SHA-1) of the file's contents.

  3. Create an INI file (eg. assets.ini) that defines collections ("bundles") of assets that are used together. Example content:

    [bundles]
    
    frontend_apps_js =
      scripts/browser_check.bundle.js
      scripts/frontend_apps.bundle.js
    
    frontend_apps_css =
      styles/frontend_apps.css
    

Registering a Pyramid view to serve assets

To serve assets using h-assets, a Pyramid view needs to be created using the assets_view function.

In the Pyramid app configuration, define a route where the URL is a base URL followed by a *subpath:

def includeme(config):
    config.add_route("assets", "/assets/*subpath")

To register the view associated with this route, first create an Environment to handle generation of asset URLs. Then use assets_view to create the view callable for use with config.add_view:

import os.path

from h_assets import Environment, assets_view


def includeme(config):
    # This assumes the following repository structure:
    #   build/ - Compiled frontend assets
    #     manifest.json
    #   projectname/
    #     assets.py - This module
    #     routes.py - Route definitions
    #     assets.ini
    root_dir = os.path.dirname(__file__)

    assets_env = Environment(
        assets_base_url="/assets",
        bundle_config_path="{root_dir}/assets.ini",
        manifest_path=f"{root_dir}/../build/manifest.json",
    )

    # Store asset environment in registry for use in registering `asset_urls`
    # Jinja2 helper in `app.py`.
    config.registry["assets_env"] = assets_env

    config.add_view(route_name="assets", view=assets_view(assets_env))

Referencing assets in templates

To get a list of asset URLs for assets in a bundle, use the urls method of the asset Environment. A common pattern is to expose these methods as global helpers in the templating environment being used to generate HTML responses. For example, in a project using pyramid_jinja2:

jinja2_env = config.get_jinja2_environment()
jinja2_env.globals["asset_url"] = config.registry["assets_env"].url
jinja2_env.globals["asset_urls"] = config.registry["assets_env"].urls

Then a template can generate URLs using:

{% for url in asset_urls("frontend_apps_js") %}
  <script async defer src="{{ url }}"></script>
{% endfor %}

Setting up Your h-assets Development Environment

First you'll need to install:

  • Git. On Ubuntu: sudo apt install git, on macOS: brew install git.
  • GNU Make. This is probably already installed, run make --version to check.
  • pyenv. Follow the instructions in pyenv's README to install it. The Homebrew method works best on macOS. The Basic GitHub Checkout method works best on Ubuntu. You don't need to set up pyenv's shell integration ("shims"), you can use pyenv without shims.

Then to set up your development environment:

git clone https://github.com/hypothesis/h-assets.git
cd h-assets
make help

Releasing a New Version of the Project

  1. First, to get PyPI publishing working you need to go to: https://github.com/organizations/hypothesis/settings/secrets/actions/PYPI_TOKEN and add h-assets to the PYPI_TOKEN secret's selected repositories.

  2. Now that the h-assets project has access to the PYPI_TOKEN secret you can release a new version by just creating a new GitHub release. Publishing a new GitHub release will automatically trigger a GitHub Actions workflow that will build the new version of your Python package and upload it to https://pypi.org/project/h-assets.

Changing the Project's Python Versions

To change what versions of Python the project uses:

  1. Change the Python versions in the cookiecutter.json file. For example:

    "python_versions": "3.10.4, 3.9.12",
    
  2. Re-run the cookiecutter template:

    make template
    
  3. Commit everything to git and send a pull request

Changing the Project's Python Dependencies

To change the production dependencies in the setup.cfg file:

  1. Change the dependencies in the .cookiecutter/includes/setuptools/install_requires file. If this file doesn't exist yet create it and add some dependencies to it. For example:

    pyramid
    sqlalchemy
    celery
    
  2. Re-run the cookiecutter template:

    make template
    
  3. Commit everything to git and send a pull request

To change the project's formatting, linting and test dependencies:

  1. Change the dependencies in the .cookiecutter/includes/tox/deps file. If this file doesn't exist yet create it and add some dependencies to it. Use tox's factor-conditional settings to limit which environment(s) each dependency is used in. For example:

    lint: flake8,
    format: autopep8,
    lint,tests: pytest-faker,
    
  2. Re-run the cookiecutter template:

    make template
    
  3. Commit everything to git and send a pull request

Metadata

Release files for h-assets 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for h-assets 1.1.0
File Size Uploaded
h_assets-1.1.0.tar.gz 23.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for h-assets 1.1.0
File Interpreter ABI Platform
h_assets-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 32.0 kB

Release files / h_assets-1.1.0.tar.gz

Download URL h_assets-1.1.0.tar.gz
Size 23.1 kB
Tags Source
SHA-256 checksum
How to use checksums
83a34cbe8b7eaab25d4fb0661cf32c7c473c816bf37c1f8737460da198a538f7
BLAKE2b-256 checksum
How to use checksums
a54aa984b38d11126147149541e3f6ee6690c9f272d332bf60ba25c0dada9d2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release files / h_assets-1.1.0-py3-none-any.whl

Download URL h_assets-1.1.0-py3-none-any.whl
Size 8.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
62de6c81a991c040b9ebed040ea00c3b5c40a4a37aaf7ba977d95cf99a58a6dd
BLAKE2b-256 checksum
How to use checksums
fcb992d8160594782ce7750c1806102739ddcfbebe2c9c77a651b90286e1f329
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page