Skip to content

[Detail Bug] Testing: django-webpack-loader ignores overridden Django settings, causing stale config and possible infinite loop #428

@detail-app

Description

@detail-app

Summary

  • Context: Configuration for django-webpack-loader is initialized at module load time in webpack_loader/config.py.
  • Bug: The configuration settings (like CACHE) are statically computed at import time and do not respond to dynamic changes in Django's settings (e.g., via @override_settings), leading to stale configurations and potential infinite loops.
  • Actual vs. expected: Changes to settings.DEBUG or settings.WEBPACK_LOADER should be reflected in the loader's behavior, but they are currently ignored after the module is first imported.
  • Impact: When DEBUG is True but CACHE is also True (due to stale configuration during a test override), get_bundle enters an infinite loop if it encounters a "compile" status in the stats file, hanging the thread or process indefinitely.

Code with bug

# webpack_loader/config.py

8: DEFAULT_CONFIG = {
9:     'DEFAULT': {
10:         'CACHE': not settings.DEBUG,  # <-- BUG 🔴 [Statically computed at import time]
...
31: user_config = getattr(settings, 'WEBPACK_LOADER', DEFAULT_CONFIG)
32: 
33: user_config = dict(
34:     (name, dict(DEFAULT_CONFIG['DEFAULT'], **cfg)) # <-- BUG 🔴 [Static merge at import time]
35:     for name, cfg in user_config.items()
36: )

Evidence

1. Reproduction of Stale Configuration

The following script demonstrates that load_config does not reflect changes made via override_settings.

import os
import django
from django.conf import settings
from django.test import override_settings

if not settings.configured:
    settings.configure(
        DEBUG=True,
        WEBPACK_LOADER={
            "DEFAULT": {"BUNDLE_DIR_NAME": "bundles/"}
        },
        STATIC_URL="/static/",
    )
django.setup()

from webpack_loader.config import load_config

def test_config_override():
    print(f"Initial BUNDLE_DIR_NAME: {load_config('DEFAULT')['BUNDLE_DIR_NAME']}")

    with override_settings(
        WEBPACK_LOADER={"DEFAULT": {"BUNDLE_DIR_NAME": "changed/"}}
    ):
        # The configuration is NOT updated
        print(f"Overridden BUNDLE_DIR_NAME: {load_config('DEFAULT')['BUNDLE_DIR_NAME']}")

if __name__ == "__main__":
    test_config_override()

Output:

Initial BUNDLE_DIR_NAME: bundles/
Overridden BUNDLE_DIR_NAME: bundles/

2. Impact: Infinite Loop

The most severe impact occurs in loaders.py because it dynamically checks settings.DEBUG but uses the stale self.config["CACHE"]. If DEBUG is overridden to True but CACHE remains True (stale from import time), get_assets() will always return the cached assets from the first call. If those assets have a status of "compile", the polling loop in get_bundle will never exit.

# webpack_loader/loaders.py

68:         if self.config["CACHE"]:
...
186:         if settings.DEBUG:
...
190:             while assets["status"] == "compile" and not timed_out:
...
198:                 assets = self.get_assets() # Returns cached assets if CACHE is True

Why has this bug gone undetected?

Most production environments use static settings. This bug primarily affects developers during testing or when using dynamic settings overrides. The project's own test suite avoids this by directly patching the internal loader.config dictionary instead of using override_settings.

Recommended fix

The configuration logic in webpack_loader/config.py should be moved into the load_config function or a similar lazy-loading mechanism that retrieves values from django.conf.settings when called.

# Recommended dynamic approach in webpack_loader/config.py
def load_config(name):
    # Fetch settings dynamically // <-- FIX 🟢
    user_config = getattr(settings, 'WEBPACK_LOADER', DEFAULT_CONFIG)
    # ... perform merge logic ...
    return merged_config

History

This bug was introduced in commit 64971f4 (@hugobessa, 2025-05-12, PR #413). Although the static configuration loading pattern existed since the project's early days, it was briefly fixed and made dynamic in commit 6ac6d58, only to be immediately reverted to the buggy static implementation in 64971f4 as part of a series of changes for Webpack 5 support.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions