Source code for coaster.assets

# -*- coding: utf-8 -*-

import re
from collections import defaultdict
# Version is not used here but is made available for others to import from
from semantic_version import Version, Spec
from webassets import Bundle

_VERSION_SPECIFIER_RE = re.compile('[<=>!]')

__all__ = ['Version', 'Spec', 'VersionedAssets']


def split_namespec(namespec):
    find_mark = _VERSION_SPECIFIER_RE.search(namespec)
    if find_mark is None:
        name = namespec
        spec = Spec()
    else:
        name = namespec[:find_mark.start()]
        spec = Spec(namespec[find_mark.start():])
    return name, spec


[docs]class VersionedAssets(defaultdict): """ Semantic-versioned assets. Usage:: >>> assets = VersionedAssets() Schema: ``assets['name'] = {'version': Bundle(), ...}`` Also: ``assets['name'] = {'version': (dependency, ..., Bundle()), ...}`` where 'dependency' is in the form 'jquery>=1.7.0', etc Use ``assets.require(name+version_spec)`` to retrieve the required version bundle """ def __init__(self): # Override dict's __init__ to prevent parameters super(VersionedAssets, self).__init__(dict) def _require_recursive(self, *namespecs): asset_versions = {} # Name: version bundles = [] for namespec in namespecs: name, spec = split_namespec(namespec) version = spec.select(self[name].keys()) if version: if name in asset_versions: if asset_versions[name] not in spec: raise ValueError("%s does not match already requested asset %s==%s" % ( namespec, name, asset_versions[name])) else: asset = self[name][version] if isinstance(asset, (list, tuple)): # We have (requires, bundle). Get requirements requires = asset[:-1] provides = [] bundle = asset[-1] elif isinstance(asset, dict): requires = asset.get('requires', []) provides = asset.get('provides', []) if isinstance(provides, basestring): provides = [provides] bundle = asset['bundle'] else: provides = [] requires = [] bundle = asset filtered_requires = [] for req in requires: req_name, req_spec = split_namespec(req) if req_name in asset_versions: if asset_versions[req_name] not in req_spec: # The version asked for conflicts with a version currently used. raise ValueError("%s is not compatible with already requested version %s" % ( req, asset_versions[req_name])) else: filtered_requires.append(req) # Get these requirements req_bundles = self._require_recursive(*filtered_requires) bundles.extend(req_bundles) # Save list of provided assets for provided in provides: if provided not in asset_versions: asset_versions[provided] = version for req_name, req_version, req_bundle in req_bundles: asset_versions[req_name] = req_version if bundle is not None: bundles.append((name, version, bundle)) return bundles
[docs] def require(self, *namespec): return Bundle(*[bundle for name, version, bundle in self._require_recursive(*namespec)])

Related Topics