Package: h-assets
h-assetsPyramid views for serving collections of compiled static assets (eg. bundles of JavaScript and CSS).Compared to Pyramid's builtinstatic 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 abuilddirectory 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.UsageUsing h-assets in a Pyramid project involves three steps:Prepare the compiled assets for use with h-assetsDuring Pyramid application configuration, create an assetEnvironmentto handle asset URL generation and register a view to serve assets from that
environmentExpose the URL-generation methods from the assetEnvironmentto your
templating system so that templates can generate asset URLsPreparing assets for use with h-assetsSet up a process to compile or copy assets from source files into an
output directory. Conventionally Hypothesis projects use a folder calledbuildin the repository root.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.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.jsscripts/frontend_apps.bundle.jsfrontend_apps_css=styles/frontend_apps.cssRegistering a Pyramid view to serve assetsTo serve assets using h-assets, a Pyramid view needs to be created using theassets_viewfunction.In the Pyramid app configuration, define a route where the URL is a base URL
followed by a*subpath:defincludeme(config):config.add_route("assets","/assets/*subpath")To register the view associated with this route, first create anEnvironmentto handle generation of asset URLs. Then useassets_viewto create the view
callable for use withconfig.add_view:importos.pathfromh_assetsimportEnvironment,assets_viewdefincludeme(config):# This assumes the following repository structure:#   build/ - Compiled frontend assets#     manifest.json#   projectname/#     assets.py - This module#     routes.py - Route definitions#     assets.iniroot_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_envconfig.add_view(route_name="assets",view=assets_view(assets_env))Referencing assets in templatesTo get a list of asset URLs for assets in a bundle, use theurlsmethod of the
assetEnvironment. 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 usingpyramid_jinja2:jinja2_env=config.get_jinja2_environment()jinja2_env.globals["asset_url"]=config.registry["assets_env"].urljinja2_env.globals["asset_urls"]=config.registry["assets_env"].urlsThen 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 EnvironmentFirst you'll need to install:Git.
On Ubuntu:sudo apt install git, on macOS:brew install git.GNU Make.
This is probably already installed, runmake --versionto check.pyenv.
Follow the instructions in pyenv's README to install it.
TheHomebrewmethod works best on macOS.
TheBasic GitHub Checkoutmethod works best on Ubuntu.
Youdon'tneed to set up pyenv's shell integration ("shims"), you canuse pyenv without shims.Then to set up your development environment:git clone https://github.com/hypothesis/h-assets.git
cd h-assets
make helpReleasing a New Version of the ProjectFirst, to get PyPI publishing working you need to go to:https://github.com/organizations/hypothesis/settings/secrets/actions/PYPI_TOKENand add h-assets to thePYPI_TOKENsecret's selected
repositories.Now that the h-assets project has access to thePYPI_TOKENsecret
you can release a new version by justcreating a new GitHub release.
Publishing a new GitHub release will automatically triggera GitHub Actions workflowthat will build the new version of your Python package and upload it tohttps://pypi.org/project/h-assets.Changing the Project's Python VersionsTo change what versions of Python the project uses:Change the Python versions in thecookiecutter.jsonfile. For example:"python_versions":"3.10.4, 3.9.12",Re-run the cookiecutter template:make templateCommit everything to git and send a pull requestChanging the Project's Python DependenciesTo change the production dependencies in thesetup.cfgfile:Change the dependencies in the.cookiecutter/includes/setuptools/install_requiresfile.
If this file doesn't exist yet create it and add some dependencies to it.
For example:pyramid
sqlalchemy
celeryRe-run the cookiecutter template:make templateCommit everything to git and send a pull requestTo change the project's formatting, linting and test dependencies:Change the dependencies in the.cookiecutter/includes/tox/depsfile.
If this file doesn't exist yet create it and add some dependencies to it.
Use tox'sfactor-conditional settingsto limit which environment(s) each dependency is used in.
For example:lint: flake8,
format: autopep8,
lint,tests: pytest-faker,Re-run the cookiecutter template:make templateCommit everything to git and send a pull request