This is an automated email from the ASF dual-hosted git repository.

maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new df05181  Adds the ability to replace/extend caching backend (#7856)
df05181 is described below

commit df051813d59da3423efed45199e7d2fc66b1d7d6
Author: Rob DiCiuccio <[email protected]>
AuthorDate: Fri Jul 12 14:06:56 2019 -0700

    Adds the ability to replace/extend caching backend (#7856)
    
    * Add ability to override cache backend with custom module
    
    * Tests for setup_cache
    
    * Add custom CACHE_CONFIG documentation
    
    * Fix linter errors and documentation link
    
    * Fix black formatting errors
---
 docs/installation.rst  | 18 ++++++++++++++++++
 superset/utils/core.py | 10 ++++++++--
 tests/utils_tests.py   | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/docs/installation.rst b/docs/installation.rst
index 6f27b17..d82f0f7 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -521,6 +521,24 @@ into your global default defined in ``CACHE_CONFIG``.
         'CACHE_REDIS_URL': 'redis://localhost:6379/0',
     }
 
+It is also possible to pass a custom cache initialization function in the
+config to handle additional caching use cases. The function must return an
+object that is compatible with the `Flask-Cache 
<https://pythonhosted.org/Flask-Cache/>`_ API.
+
+.. code-block:: python
+
+    from custom_caching import CustomCache
+
+    def init_cache(app):
+        """Takes an app instance and returns a custom cache backend"""
+        config = {
+            'CACHE_DEFAULT_TIMEOUT': 60 * 60 * 24, # 1 day default (in secs)
+            'CACHE_KEY_PREFIX': 'superset_results',
+        }
+        return CustomCache(app, config)
+
+    CACHE_CONFIG = init_cache
+
 Superset has a Celery task that will periodically warm up the cache based on
 different strategies. To use it, add the following to the `CELERYBEAT_SCHEDULE`
 section in `config.py`:
diff --git a/superset/utils/core.py b/superset/utils/core.py
index 3ad1c62..ebbb082 100644
--- a/superset/utils/core.py
+++ b/superset/utils/core.py
@@ -775,8 +775,14 @@ def choicify(values):
 
 def setup_cache(app: Flask, cache_config) -> Optional[Cache]:
     """Setup the flask-cache on a flask app"""
-    if cache_config and cache_config.get("CACHE_TYPE") != "null":
-        return Cache(app, config=cache_config)
+    if cache_config:
+        if isinstance(cache_config, dict):
+            if cache_config.get("CACHE_TYPE") != "null":
+                return Cache(app, config=cache_config)
+        else:
+            # Accepts a custom cache initialization function,
+            # returning an object compatible with Flask-Caching API
+            return cache_config(app)
 
     return None
 
diff --git a/tests/utils_tests.py b/tests/utils_tests.py
index be2ce79..3094f66 100644
--- a/tests/utils_tests.py
+++ b/tests/utils_tests.py
@@ -20,6 +20,8 @@ import unittest
 from unittest.mock import patch
 import uuid
 
+from flask import Flask
+from flask_caching import Cache
 import numpy
 
 from superset import app
@@ -39,6 +41,7 @@ from superset.utils.core import (
     parse_human_timedelta,
     parse_js_uri_path_item,
     parse_past_timedelta,
+    setup_cache,
     validate_json,
     zlib_compress,
     zlib_decompress_to_string,
@@ -766,6 +769,35 @@ class UtilsTestCase(unittest.TestCase):
         self.assertIsNone(parse_js_uri_path_item(None))
         self.assertIsNotNone(parse_js_uri_path_item("item"))
 
+    def test_setup_cache_no_config(self):
+        app = Flask(__name__)
+        cache_config = None
+        self.assertIsNone(setup_cache(app, cache_config))
+
+    def test_setup_cache_null_config(self):
+        app = Flask(__name__)
+        cache_config = {"CACHE_TYPE": "null"}
+        self.assertIsNone(setup_cache(app, cache_config))
+
+    def test_setup_cache_standard_config(self):
+        app = Flask(__name__)
+        cache_config = {
+            "CACHE_TYPE": "redis",
+            "CACHE_DEFAULT_TIMEOUT": 60,
+            "CACHE_KEY_PREFIX": "superset_results",
+            "CACHE_REDIS_URL": "redis://localhost:6379/0",
+        }
+        assert isinstance(setup_cache(app, cache_config), Cache) is True
+
+    def test_setup_cache_custom_function(self):
+        app = Flask(__name__)
+        CustomCache = type("CustomCache", (object,), {"__init__": lambda 
*args: None})
+
+        def init_cache(app):
+            return CustomCache(app, {})
+
+        assert isinstance(setup_cache(app, init_cache), CustomCache) is True
+
     def test_get_stacktrace(self):
         with app.app_context():
             app.config["SHOW_STACKTRACE"] = True

Reply via email to