Repository: apache/superset
File: superset/utils/cache_manager.py (Python)

Code before:
    Flask-caching's memoize decorator evaluates hash_method at decoration time
    (module import), but we need to read HASH_ALGORITHM config at function call
    time when the app context is available.

    This class acts like a hashlib function but looks up the configured
    algorithm when called.
    """

    def __call__(self, data: bytes = b"") -> Any:
        """
        Create a hash object using the configured algorithm.

        Args:
            data: Optional initial data to hash

        Returns:
            A hashlib hash object (e.g., sha256 or md5)
        """
        algorithm = current_app.config["HASH_ALGORITHM"]
        hash_func = _HASH_METHODS[algorithm]
        return hash_func(data)


# Singleton instance to use as default hash_method
configurable_hash_method = ConfigurableHashMethod()


class SupersetCache(Cache):
    """
    Cache subclass that uses the configured HASH_ALGORITHM instead of MD5.

    Flask-caching uses MD5 by default for cache key generation, which fails
    in FIPS mode where MD5 is disabled. This class overrides the default
    hash method to use the algorithm specified by HASH_ALGORITHM config.

    Note: Switching hash algorithms will invalidate existing cache keys,
    causing a one-time cache miss on upgrade.
    """

    def memoize(
        self,
        timeout: int | None = None,
        make_name: Callable[..., Any] | None = None,
        unless: Callable[..., bool] | None = None,
        forced_update: Callable[..., bool] | None = None,
        response_filter: Callable[..., Any] | None = None,
        hash_method: Callable[..., Any] = configurable_hash_method,
        cache_none: bool = False,
        source_check: bool | None = None,
        args_to_ignore: Any | None = None,
    ) -> Callable[..., Any]:

Review comment (suggestion):
This will raise a bare KeyError if HASH_ALGORITHM is set to an unsupported value. Consider matching the pattern in hashing.py which uses .get() and raises a ValueError with a descriptive message:  
```                                                                     
  algorithm = current_app.config["HASH_ALGORITHM"]                                                                                      
  hash_func = _HASH_METHODS.get(algorithm)                                                                                              
  if hash_func is None:                                                                                                                 
      raise ValueError(f"Unsupported hash algorithm: {algorithm}")                                                                      
  return hash_func(data)     
```

Code after:
    Flask-caching's memoize decorator evaluates hash_method at decoration time
    (module import), but we need to read HASH_ALGORITHM config at function call
    time when the app context is available.

    This class acts like a hashlib function but looks up the configured
    algorithm when called.
    """

    def __call__(self, data: bytes = b"") -> Any:
        """
        Create a hash object using the configured algorithm.

        Args:
            data: Optional initial data to hash

        Returns:
            A hashlib hash object (e.g., sha256 or md5)

        Raises:
            ValueError: If HASH_ALGORITHM is set to an unsupported value
        """
        algorithm = current_app.config["HASH_ALGORITHM"]
        hash_func = _HASH_METHODS.get(algorithm)
        if hash_func is None:
            raise ValueError(f"Unsupported hash algorithm: {algorithm}")
        return hash_func(data)


# Singleton instance to use as default hash_method
configurable_hash_method = ConfigurableHashMethod()


class SupersetCache(Cache):
    """
    Cache subclass that uses the configured HASH_ALGORITHM instead of MD5.

    Flask-caching uses MD5 by default for cache key generation, which fails
    in FIPS mode where MD5 is disabled. This class overrides the default
    hash method to use the algorithm specified by HASH_ALGORITHM config.

    Note: Switching hash algorithms will invalidate existing cache keys,
    causing a one-time cache miss on upgrade.
    """

    def memoize(
        self,
        timeout: int | None = None,
        make_name: Callable[..., Any] | None = None,
        unless: Callable[..., bool] | None = None,
        forced_update: Callable[..., bool] | None = None,
        response_filter: Callable[..., Any] | None = None,