diff --git a/webpack_loader/loaders.py b/webpack_loader/loaders.py index f6855739..7a81857d 100644 --- a/webpack_loader/loaders.py +++ b/webpack_loader/loaders.py @@ -14,7 +14,7 @@ ) -class WebpackLoader(object): +class WebpackLoader: _assets = {} def __init__(self, name, config): @@ -82,7 +82,7 @@ def map_chunk_files_to_url(self, chunks): def get_chunk_url(self, chunk_file): public_path = chunk_file.get("publicPath") - if public_path: + if public_path and public_path != "auto": return public_path # Use os.path.normpath for Windows paths @@ -91,26 +91,48 @@ def get_chunk_url(self, chunk_file): ) return staticfiles_storage.url(relpath) - def get_bundle(self, bundle_name): + def wait_for_assets(self): assets = self.get_assets() # poll when debugging and block request until bundle is compiled # or the build times out if settings.DEBUG: timeout = self.config["TIMEOUT"] or 0 - timed_out = False start = time.time() - while assets["status"] == "compile" and not timed_out: + while assets["status"] == "compile": time.sleep(self.config["POLL_INTERVAL"]) if timeout and (time.time() - timeout > start): - timed_out = True + raise WebpackLoaderTimeoutError( + "Timed Out. Webpack took more than {0} " + "seconds to compile.".format(self.config["TIMEOUT"] or 0) + ) + assets = self.get_assets() - if timed_out: - raise WebpackLoaderTimeoutError( - "Timed Out. Bundle `{0}` took more than {1} seconds " - "to compile.".format(bundle_name, timeout) - ) + return assets + + def _process_assets_error(self, assets): + if assets.get("status") == "error": + if "file" not in assets: + assets["file"] = "" + if "error" not in assets: + assets["error"] = "Unknown Error" + if "message" not in assets: + assets["message"] = "" + error = """ + {error} in {file} + {message} + """.format(**assets) + raise WebpackError(error) + + raise WebpackLoaderBadStatsError( + "The stats file does not contain valid data. Make sure " + "webpack-bundle-tracker plugin is enabled and try to run " + "webpack again." + ) + + def get_bundle(self, bundle_name): + assets = self.wait_for_assets() if assets.get("status") == "done": chunks = assets["chunks"].get(bundle_name, None) @@ -130,24 +152,17 @@ def get_bundle(self, bundle_name): return self.map_chunk_files_to_url(filtered_chunks) - elif assets.get("status") == "error": - if "file" not in assets: - assets["file"] = "" - if "error" not in assets: - assets["error"] = "Unknown Error" - if "message" not in assets: - assets["message"] = "" - error = """ - {error} in {file} - {message} - """.format(**assets) - raise WebpackError(error) + self._process_assets_error(assets) - raise WebpackLoaderBadStatsError( - "The stats file does not contain valid data. Make sure " - "webpack-bundle-tracker plugin is enabled and try to run " - "webpack again." - ) + def get_asset_url(self, asset_name): + assets = self.wait_for_assets() + try: + asset_file = assets["assets"][asset_name] + except KeyError: + raise WebpackBundleLookupError( + "Cannot resolve asset {0}.".format(asset_name) + ) + return self.get_chunk_url(asset_file) class FakeWebpackLoader(WebpackLoader): diff --git a/webpack_loader/utils.py b/webpack_loader/utils.py index ca844fac..228aa2e4 100644 --- a/webpack_loader/utils.py +++ b/webpack_loader/utils.py @@ -6,24 +6,24 @@ def import_string(dotted_path): - ''' + """ This is a rough copy of django's import_string, which wasn't introduced until Django 1.7 Once this package's support for Django 1.6 has been removed, this can be safely replaced with `from django.utils.module_loading import import_string` - ''' + """ try: - module_path, class_name = dotted_path.rsplit('.', 1) + module_path, class_name = dotted_path.rsplit(".", 1) module = import_module(module_path) return getattr(module, class_name) except (ValueError, AttributeError, ImportError): - raise ImportError('%s doesn\'t look like a valid module path' % dotted_path) + raise ImportError("%s doesn't look like a valid module path" % dotted_path) def get_loader(config_name): if config_name not in _loaders: config = load_config(config_name) - loader_class = import_string(config['LOADER_CLASS']) + loader_class = import_string(config["LOADER_CLASS"]) _loaders[config_name] = loader_class(config_name, config) return _loaders[config_name] @@ -33,13 +33,13 @@ def get_skip_common_chunks(config_name): # The global default is currently False, whenever that is changed, change # this fallback value as well which is present to provide backwards # compatibility. - return loader.config.get('SKIP_COMMON_CHUNKS', False) + return loader.config.get("SKIP_COMMON_CHUNKS", False) def _filter_by_extension(bundle, extension): - '''Return only files with the given extension''' + """Return only files with the given extension""" for chunk in bundle: - if chunk['name'].endswith('.{0}'.format(extension)): + if chunk["name"].endswith(".{0}".format(extension)): yield chunk @@ -50,14 +50,16 @@ def _get_bundle(loader, bundle_name, extension): return bundle -def get_files(bundle_name, extension=None, config='DEFAULT'): - '''Returns list of chunks from named bundle''' +def get_files(bundle_name, extension=None, config="DEFAULT"): + """Returns list of chunks from named bundle""" loader = get_loader(config) return list(_get_bundle(loader, bundle_name, extension)) -def get_as_tags(bundle_name, extension=None, config='DEFAULT', suffix='', attrs='', is_preload=False): - ''' +def get_as_tags( + bundle_name, extension=None, config="DEFAULT", suffix="", attrs="", is_preload=False +): + """ Get a list of formatted ' - ).format( - ''.join([chunk['url'], suffix]), + tags.append( + ('').format( + "".join([chunk["url"], suffix]), + attrs, + loader.get_integrity_attr(chunk), + ) + ) + elif chunk["name"].endswith((".css", ".css.gz")): + tags.append( + ('').format( + "".join([chunk["url"], suffix]), attrs, + '"stylesheet"' if not is_preload else '"preload" as="style"', loader.get_integrity_attr(chunk), - )) - elif chunk['name'].endswith(('.css', '.css.gz')): - tags.append(( - '' - ).format( - ''.join([chunk['url'], suffix]), - attrs, - '"stylesheet"' if not is_preload else '"preload" as="style"', - loader.get_integrity_attr(chunk), - )) + ) + ) return tags -def get_static(asset_name, config='DEFAULT'): - ''' +def get_static(asset_name, config="DEFAULT"): + """ Equivalent to Django's 'static' look up but for webpack assets. :param asset_name: the name of the asset :param config: (optional) the name of the configuration :return: path to webpack asset as a string - ''' - return "{0}{1}".format( - get_loader(config).get_assets().get( - 'publicPath', getattr(settings, 'STATIC_URL') - ), - asset_name - ) + """ + loader = get_loader(config) + return loader.get_asset_url(asset_name)