Initial commit
This commit is contained in:
202
env/lib/python3.10/site-packages/wagtail/contrib/frontend_cache/utils.py
vendored
Normal file
202
env/lib/python3.10/site-packages/wagtail/contrib/frontend_cache/utils.py
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
import logging
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from urllib.parse import urlsplit, urlunsplit
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
from wagtail.coreutils import get_content_languages
|
||||
|
||||
logger = logging.getLogger("wagtail.frontendcache")
|
||||
|
||||
|
||||
class InvalidFrontendCacheBackendError(ImproperlyConfigured):
|
||||
pass
|
||||
|
||||
|
||||
def get_backends(backend_settings=None, backends=None):
|
||||
# Get backend settings from WAGTAILFRONTENDCACHE setting
|
||||
if backend_settings is None:
|
||||
backend_settings = getattr(settings, "WAGTAILFRONTENDCACHE", None)
|
||||
|
||||
# Fallback to using WAGTAILFRONTENDCACHE_LOCATION setting (backwards compatibility)
|
||||
if backend_settings is None:
|
||||
cache_location = getattr(settings, "WAGTAILFRONTENDCACHE_LOCATION", None)
|
||||
|
||||
if cache_location is not None:
|
||||
backend_settings = {
|
||||
"default": {
|
||||
"BACKEND": "wagtail.contrib.frontend_cache.backends.HTTPBackend",
|
||||
"LOCATION": cache_location,
|
||||
},
|
||||
}
|
||||
|
||||
# No settings found, return empty list
|
||||
if backend_settings is None:
|
||||
return {}
|
||||
|
||||
backend_objects = {}
|
||||
|
||||
for backend_name, _backend_config in backend_settings.items():
|
||||
if backends is not None and backend_name not in backends:
|
||||
continue
|
||||
|
||||
backend_config = _backend_config.copy()
|
||||
backend = backend_config.pop("BACKEND")
|
||||
|
||||
# Try to import the backend
|
||||
try:
|
||||
backend_cls = import_string(backend)
|
||||
except ImportError as e:
|
||||
raise InvalidFrontendCacheBackendError(
|
||||
f"Could not find backend '{backend}': {e}"
|
||||
)
|
||||
|
||||
backend_objects[backend_name] = backend_cls(backend_config)
|
||||
|
||||
return backend_objects
|
||||
|
||||
|
||||
def purge_url_from_cache(url, backend_settings=None, backends=None):
|
||||
purge_urls_from_cache([url], backend_settings=backend_settings, backends=backends)
|
||||
|
||||
|
||||
def purge_urls_from_cache(urls, backend_settings=None, backends=None):
|
||||
# Convert each url to urls one for each managed language (WAGTAILFRONTENDCACHE_LANGUAGES setting).
|
||||
# The managed languages are common to all the defined backends.
|
||||
# This depends on settings.USE_I18N
|
||||
# If WAGTAIL_I18N_ENABLED is True, this defaults to WAGTAIL_CONTENT_LANGUAGES
|
||||
wagtail_i18n_enabled = getattr(settings, "WAGTAIL_I18N_ENABLED", False)
|
||||
content_languages = get_content_languages() if wagtail_i18n_enabled else {}
|
||||
languages = getattr(
|
||||
settings, "WAGTAILFRONTENDCACHE_LANGUAGES", list(content_languages.keys())
|
||||
)
|
||||
if settings.USE_I18N and languages:
|
||||
langs_regex = "^/(%s)/" % "|".join(languages)
|
||||
new_urls = []
|
||||
|
||||
# Purge the given url for each managed language
|
||||
for isocode in languages:
|
||||
for url in urls:
|
||||
up = urlsplit(url)
|
||||
new_url = urlunsplit(
|
||||
(
|
||||
up.scheme,
|
||||
up.netloc,
|
||||
re.sub(langs_regex, "/%s/" % isocode, up.path),
|
||||
up.query,
|
||||
up.fragment,
|
||||
)
|
||||
)
|
||||
|
||||
# Check for best performance. True if re.sub found no match
|
||||
# It happens when i18n_patterns was not used in urls.py to serve content for different languages from different URLs
|
||||
if new_url in new_urls:
|
||||
continue
|
||||
|
||||
new_urls.append(new_url)
|
||||
|
||||
urls = new_urls
|
||||
|
||||
urls_by_hostname = defaultdict(list)
|
||||
|
||||
for url in urls:
|
||||
urls_by_hostname[urlsplit(url).netloc].append(url)
|
||||
|
||||
backends = get_backends(backend_settings, backends)
|
||||
|
||||
for hostname, urls in urls_by_hostname.items():
|
||||
backends_for_hostname = {
|
||||
backend_name: backend
|
||||
for backend_name, backend in backends.items()
|
||||
if backend.invalidates_hostname(hostname)
|
||||
}
|
||||
|
||||
if not backends_for_hostname:
|
||||
logger.info("Unable to find purge backend for %s", hostname)
|
||||
continue
|
||||
|
||||
for backend_name, backend in backends_for_hostname.items():
|
||||
for url in urls:
|
||||
logger.info("[%s] Purging URL: %s", backend_name, url)
|
||||
|
||||
backend.purge_batch(urls)
|
||||
|
||||
|
||||
def _get_page_cached_urls(page):
|
||||
page_url = page.full_url
|
||||
if page_url is None: # nothing to be done if the page has no routable URL
|
||||
return []
|
||||
|
||||
return [page_url + path.lstrip("/") for path in page.specific.get_cached_paths()]
|
||||
|
||||
|
||||
def purge_page_from_cache(page, backend_settings=None, backends=None):
|
||||
purge_pages_from_cache([page], backend_settings=backend_settings, backends=backends)
|
||||
|
||||
|
||||
def purge_pages_from_cache(pages, backend_settings=None, backends=None):
|
||||
urls = []
|
||||
for page in pages:
|
||||
urls.extend(_get_page_cached_urls(page))
|
||||
|
||||
if urls:
|
||||
purge_urls_from_cache(urls, backend_settings, backends)
|
||||
|
||||
|
||||
class PurgeBatch:
|
||||
"""Represents a list of URLs to be purged in a single request"""
|
||||
|
||||
def __init__(self, urls=None):
|
||||
self.urls = []
|
||||
|
||||
if urls is not None:
|
||||
self.add_urls(urls)
|
||||
|
||||
def add_url(self, url):
|
||||
"""Adds a single URL"""
|
||||
self.urls.append(url)
|
||||
|
||||
def add_urls(self, urls):
|
||||
"""
|
||||
Adds multiple URLs from an iterable
|
||||
|
||||
This is equivalent to running ``.add_url(url)`` on each URL
|
||||
individually
|
||||
"""
|
||||
self.urls.extend(urls)
|
||||
|
||||
def add_page(self, page):
|
||||
"""
|
||||
Adds all URLs for the specified page
|
||||
|
||||
This combines the page's full URL with each path that is returned by
|
||||
the page's `.get_cached_paths` method
|
||||
"""
|
||||
self.add_urls(_get_page_cached_urls(page))
|
||||
|
||||
def add_pages(self, pages):
|
||||
"""
|
||||
Adds multiple pages from a QuerySet or an iterable
|
||||
|
||||
This is equivalent to running ``.add_page(page)`` on each page
|
||||
individually
|
||||
"""
|
||||
for page in pages:
|
||||
self.add_page(page)
|
||||
|
||||
def purge(self, backend_settings=None, backends=None):
|
||||
"""
|
||||
Performs the purge of all the URLs in this batch
|
||||
|
||||
This method takes two optional keyword arguments: backend_settings and backends
|
||||
|
||||
- backend_settings can be used to override the WAGTAILFRONTENDCACHE setting for
|
||||
just this call
|
||||
|
||||
- backends can be set to a list of backend names. When set, the invalidation request
|
||||
will only be sent to these backends
|
||||
"""
|
||||
purge_urls_from_cache(self.urls, backend_settings, backends)
|
||||
Reference in New Issue
Block a user