Skip to content

Commit

Permalink
Rework SRI flag to be on the urls() method instead of the bundle object.
Browse files Browse the repository at this point in the history
This makes it work better with nested bundles and removes the possibility of mixed settings.

This commit also adds unit tests for the bundle class.

Part of implementing miracle2k#494
  • Loading branch information
VorpalBlade committed May 30, 2019
1 parent b690155 commit 0bb0ec1
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 30 deletions.
27 changes: 15 additions & 12 deletions src/webassets/bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .exceptions import BundleError, BuildError
from .utils import cmp_debug_levels, hash_func
from .env import ConfigurationContext, DictConfigStorage, BaseEnvironment
from .utils import is_url, calculate_sri
from .utils import is_url, calculate_sri_on_file


__all__ = ('Bundle', 'get_all_bundle_files',)
Expand Down Expand Up @@ -118,7 +118,6 @@ def __init__(self, *contents, **options):
self.remove_duplicates = options.pop('remove_duplicates', True)
self.extra = options.pop('extra', {})
self.merge = options.pop('merge', True)
self.calculate_sri = options.pop('calculate_sri', False)

self._config = BundleConfig(self)
self._config.update(options.pop('config', {}))
Expand Down Expand Up @@ -738,6 +737,8 @@ def _urls(self, ctx, extra_filters, *args, **kwargs):
"""Return a list of urls for this bundle, and all subbundles,
and, when it becomes necessary, start a build process.
"""
# Check if we should calculate SRI
calculate_sri = kwargs.pop('calculate_sri', False)

# Look at the debug value to see if this bundle should return the
# source urls (in debug mode), or a single url of the bundle in built
Expand Down Expand Up @@ -765,9 +766,9 @@ def _urls(self, ctx, extra_filters, *args, **kwargs):
if ctx.auto_build:
self._build(ctx, extra_filters=extra_filters, force=False,
*args, **kwargs)
if self.calculate_sri:
if calculate_sri:
return [{'uri': self._make_output_url(ctx),
'sri': calculate_sri(ctx.resolver.resolve_output_to_path(ctx, self.output, self))}]
'sri': calculate_sri_on_file(ctx.resolver.resolve_output_to_path(ctx, self.output, self))}]
else:
return [self._make_output_url(ctx)]
else:
Expand All @@ -780,29 +781,31 @@ def _urls(self, ctx, extra_filters, *args, **kwargs):
urls.extend(org._urls(
wrap(ctx, cnt),
merge_filters(extra_filters, self.filters),
*args, **kwargs))
*args,
calculate_sri=calculate_sri,
**kwargs))
elif is_url(cnt):
# Can't calculate SRI for non file
if self.calculate_sri:
urls.append({'uri': cnt})
if calculate_sri:
urls.append({'uri': cnt, 'sri': None})
else:
urls.append(cnt)
else:
sri = None
try:
url = ctx.resolver.resolve_source_to_url(ctx, cnt, org)
if self.calculate_sri:
sri = calculate_sri(ctx.resolver.resolve_output_to_path(ctx, cnt, org))
if calculate_sri:
sri = calculate_sri_on_file(ctx.resolver.resolve_output_to_path(ctx, cnt, org))
except ValueError:
# If we cannot generate a url to a path outside the
# media directory. So if that happens, we copy the
# file into the media directory.
external = pull_external(ctx, cnt)
url = ctx.resolver.resolve_source_to_url(ctx, external, org)
if self.calculate_sri:
sri = calculate_sri(ctx.resolver.resolve_output_to_path(ctx, external, org))
if calculate_sri:
sri = calculate_sri_on_file(ctx.resolver.resolve_output_to_path(ctx, external, org))

if self.calculate_sri:
if calculate_sri:
urls.append({'uri': url, 'sri': sri})
else:
urls.append(url)
Expand Down
5 changes: 2 additions & 3 deletions src/webassets/ext/jinja2.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,14 @@ def _render_assets(self, filter, output, dbg, depends, files, caller=None):
'output': output,
'filters': filter,
'debug': dbg,
'depends': depends,
'calculate_sri': True,
'depends': depends
}
bundle = self.BundleClass(
*self.resolve_contents(files, env), **bundle_kwargs)

# Retrieve urls (this may or may not cause a build)
with bundle.bind(env):
urls = bundle.urls()
urls = bundle.urls(calculate_sri=True)

# For each url, execute the content of this template tag (represented
# by the macro ```caller`` given to use by Jinja2).
Expand Down
40 changes: 30 additions & 10 deletions src/webassets/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@
set = set


try:
FileNotFoundError
except NameError:
FileNotFoundError = IOError
else:
FileNotFoundError = FileNotFoundError


from webassets.six import StringIO


Expand Down Expand Up @@ -214,16 +222,28 @@ def is_url(s):
return bool(parsed.scheme and parsed.netloc) and len(parsed.scheme) > 1


def calculate_sri(input):
"""Calculate SRI string"""
BUF_SIZE = 65536
def calculate_sri(data):
"""Calculate SRI string for data buffer."""
hash = hashlib.sha384()
with open(input, 'rb') as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
hash.update(data)
hash.update(data)
hash = hash.digest()
hash_base64 = base64.b64encode(hash).decode()
return 'sha384-{}'.format(hash_base64)
return 'sha384-{}'.format(hash_base64)


def calculate_sri_on_file(file_name):
"""Calculate SRI string if file can be found. Otherwise silently return None"""
BUF_SIZE = 65536
hash = hashlib.sha384()
try:
with open(file_name, 'rb') as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
hash.update(data)
hash = hash.digest()
hash_base64 = base64.b64encode(hash).decode()
return 'sha384-{}'.format(hash_base64)
except FileNotFoundError:
return None
Loading

0 comments on commit 0bb0ec1

Please sign in to comment.