Custom resolvers

The resolver is a pluggable object that webassets uses to find the contents of a Bundle on the filesystem, as well as to generate the correct urls to these files.

For example, the default resolver searches the Environment.load_path, or looks within Environment.directory. The webassets Django integration will use Django’s staticfile finders to look for files.

For normal usage, you will not need to write your own resolver, or indeed need to know how they work. However, if you want to integrate webassets with another framework, or if your application is complex enough that it requires custom file referencing, read on.

The API as webassets sees it

webassets expects to find the resolver via the Environment.resolver property, and expects this object to provide the following methods:

Resolver.resolve_source(ctx, item)

Given item from a Bundle’s contents, this has to return the final value to use, usually an absolute filesystem path.

Note

It is also allowed to return urls and bundle instances (or generally anything else the calling Bundle instance may be able to handle). Indeed this is the reason why the name of this method does not imply a return type.

The incoming item is usually a relative path, but may also be an absolute path, or a url. These you will commonly want to return unmodified.

This method is also allowed to resolve item to multiple values, in which case a list should be returned. This is commonly used if item includes glob instructions (wildcards).

Note

Instead of this, subclasses should consider implementing search_for_source() instead.

Resolver.resolve_output_to_path(ctx, target, bundle)

Given target, this has to return the absolute filesystem path to which the output file of bundle should be written.

target may be a relative or absolute path, and is usually taking from the Bundle.output property.

If a version-placeholder is used (%(version)s, it is still unresolved at this point.

Resolver.resolve_source_to_url(ctx, filepath, item)

Given the absolute filesystem path in filepath, as well as the original value from Bundle.contents which resolved to this path, this must return the absolute url through which the file is to be referenced.

Depending on the use case, either the filepath or the item argument will be more helpful in generating the url.

This method should raise a ValueError if the url cannot be determined.

Resolver.resolve_output_to_url(ctx, target)

Given target, this has to return the url through which the output file can be referenced.

target may be a relative or absolute path, and is usually taking from the Bundle.output property.

This is different from resolve_source_to_url() in that you do not passed along the result of resolve_output_to_path(). This is because in many use cases, the filesystem is not available at the point where the output url is needed (the media server may on a different machine).

Methods to overwrite

However, in practice, you will usually want to override the builtin Resolver, and customize it’s behaviour where necessary. The default resolver already splits what is is doing into multiple methods; so that you can either override them, or refer to them in your own implementation, as makes sense.

Instead of the official entrypoints above, you may instead prefer to override the following methods of the default resolver class:

Resolver.search_for_source(ctx, item)

Called by resolve_source() after determining that item is a relative filesystem path.

You should always overwrite this method, and let resolve_source() deal with absolute paths, urls and other types of items that a bundle may contain.

Resolver.search_load_path(ctx, item)

This is called by search_for_source() when a Environment.load_path is set.

If you want to change how the load path is processed, overwrite this method.

Helpers to use

The following methods of the default resolver class you may find useful as helpers while implementing your subclass:

Resolver.consider_single_directory(directory, item)

Searches for item within directory. Is able to resolve glob instructions.

Subclasses can call this when they have narrowed done the location of a bundle item to a single directory.

Resolver.glob(basedir, expr)

Evaluates a glob expression. Yields a sorted list of absolute filenames.

Resolver.query_url_mapping(ctx, filepath)

Searches the environment-wide url mapping (based on the urls assigned to each directory in the load path). Returns the correct url for filepath.

Subclasses should be sure that they really want to call this method, instead of simply falling back to super().

Example: A prefix resolver

The following is a simple resolver implementation that searches for files in a different directory depending on the first directory part.

from webassets.env import Resolver

class PrefixResolver(Resolver):

    def __init__(self, prefixmap):
        self.map = prefixmap

    def search_for_source(self, ctx, item):
        parts = item.split('/', 1)
        if len(parts) < 2:
            raise ValueError(
                '"%s" not valid; a static path requires a prefix.' % item)

        prefix, name = parts
        if not prefix in self.map:
            raise ValueError(('Prefix "%s" of static path "%s" is not '
                              'registered') % (prefix, item))

        # For the rest, defer to base class method, which provides
        # support for things like globbing.
        return self.consider_single_directory(self.map[prefix], name)

Using it:

env = webassets.Environment(path, url)
env.resolver = PrefixResolver({
    'app1': '/var/www/app1/static',
    'app2': '/srv/deploy/media/app2',
})
bundle = Bundle(
   'app2/scripts/jquery.js',
   'app1/*.js',
)

Other implementations