Skip to content

Commit 536968a

Browse files
committed
cache and asyncify web module loading
1 parent 4bcdeb5 commit 536968a

File tree

6 files changed

+72
-12
lines changed

6 files changed

+72
-12
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,18 @@ You may configure additional options as well:
103103
```python
104104
# the base URL for all IDOM-releated resources
105105
IDOM_BASE_URL: str = "_idom/"
106+
107+
# Set cache size limit for loading JS files for IDOM.
108+
# Only applies when not using Django's caching framework (see below).
109+
IDOM_WEB_MODULE_LRU_CACHE_SIZE: int | None = None
110+
111+
# Configure a cache for loading JS files
112+
CACHES = {
113+
# Configure a cache for loading JS files for IDOM
114+
"idom_web_modules": {"BACKEND": ...},
115+
# If the above cache is not configured, then we'll use the "default" instead
116+
"default": {"BACKEND": ...},
117+
}
106118
```
107119

108120
## `urls.py`

noxfile.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@
1515

1616

1717
@nox.session(reuse_venv=True)
18-
def test_app(session: Session) -> None:
18+
def manage(session: Session) -> None:
1919
"""Run a manage.py command for tests/test_app"""
20-
session.install("-r", "requirements.txt")
20+
session.install("-r", "requirements/test-env.txt")
2121
session.install("idom[stable]")
2222
session.install("-e", ".")
2323
session.chdir("tests")
24-
25-
build_js_on_commands = ["runserver"]
26-
if set(session.posargs).intersection(build_js_on_commands):
27-
session.run("python", "manage.py", "build_js")
28-
2924
session.run("python", "manage.py", *session.posargs)
3025

3126

src/django_idom/config.py

+16
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,26 @@
22

33
from django.conf import settings
44
from idom.core.proto import ComponentConstructor
5+
from django.core.cache import DEFAULT_CACHE_ALIAS
56

67

78
IDOM_REGISTERED_COMPONENTS: Dict[str, ComponentConstructor] = {}
89

910
IDOM_BASE_URL = getattr(settings, "IDOM_BASE_URL", "_idom/")
1011
IDOM_WEBSOCKET_URL = IDOM_BASE_URL + "websocket/"
1112
IDOM_WEB_MODULES_URL = IDOM_BASE_URL + "web_module/"
13+
14+
_CACHES = getattr(settings, "CACHES", {})
15+
if _CACHES:
16+
if "idom_web_modules" in getattr(settings, "CACHES", {}):
17+
IDOM_WEB_MODULE_CACHE = "idom_web_modules"
18+
else:
19+
IDOM_WEB_MODULE_CACHE = DEFAULT_CACHE_ALIAS
20+
else:
21+
IDOM_WEB_MODULE_CACHE = None
22+
23+
24+
# the LRU cache size for the route serving IDOM_WEB_MODULES_DIR files
25+
IDOM_WEB_MODULE_LRU_CACHE_SIZE = getattr(
26+
settings, "IDOM_WEB_MODULE_LRU_CACHE_SIZE", None
27+
)

src/django_idom/templates/idom/view.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{% load static %}
22
<div id="{{ idom_mount_uuid }}"></div>
33
<script type="module" crossorigin="anonymous">
4-
import { mountViewToElement } from "{% static 'js/idom.js' %}";
4+
import { mountViewToElement } from "{% static 'js/django-idom-client.js' %}";
55
const mountPoint = document.getElementById("{{ idom_mount_uuid }}");
66
mountViewToElement(
77
mountPoint,

src/django_idom/views.py

+40-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,44 @@
1+
import os
2+
import asyncio
3+
import functools
4+
15
from django.http import HttpRequest, HttpResponse
26
from idom.config import IDOM_WED_MODULES_DIR
7+
from django.core.cache import caches
8+
9+
from .config import IDOM_WEB_MODULE_CACHE, IDOM_WEB_MODULE_LRU_CACHE_SIZE
10+
11+
12+
if IDOM_WEB_MODULE_CACHE is None:
13+
14+
def async_lru_cache(*lru_cache_args, **lru_cache_kwargs):
15+
def async_lru_cache_decorator(async_function):
16+
@functools.lru_cache(*lru_cache_args, **lru_cache_kwargs)
17+
def cached_async_function(*args, **kwargs):
18+
coroutine = async_function(*args, **kwargs)
19+
return asyncio.ensure_future(coroutine)
20+
21+
return cached_async_function
22+
23+
return async_lru_cache_decorator
24+
25+
@async_lru_cache(IDOM_WEB_MODULE_LRU_CACHE_SIZE)
26+
async def web_modules_file(request: HttpRequest, file: str) -> HttpResponse:
27+
file_path = IDOM_WED_MODULES_DIR.current.joinpath(*file.split("/"))
28+
return HttpResponse(file_path.read_text(), content_type="text/javascript")
29+
30+
31+
else:
32+
_web_module_cache = caches[IDOM_WEB_MODULE_CACHE]
33+
34+
async def web_modules_file(request: HttpRequest, file: str) -> HttpResponse:
35+
file = IDOM_WED_MODULES_DIR.current.joinpath(*file.split("/")).absolute()
36+
last_modified_time = os.stat(file).st_mtime
37+
cache_key = f"{file}:{last_modified_time}"
338

39+
response = _web_module_cache.get(cache_key)
40+
if response is None:
41+
response = HttpResponse(file.read_text(), content_type="text/javascript")
42+
_web_module_cache.set(cache_key, response, timeout=None)
443

5-
def web_modules_file(request: HttpRequest, file: str) -> HttpResponse:
6-
file_path = IDOM_WED_MODULES_DIR.current.joinpath(*file.split("/"))
7-
return HttpResponse(file_path.read_text(), content_type="text/javascript")
44+
return response

src/js/rollup.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { PRODUCTION } = process.env;
77
export default {
88
input: "src/index.js",
99
output: {
10-
file: "../django_idom/static/js/idom.js",
10+
file: "../django_idom/static/js/django-idom-client.js",
1111
format: "esm",
1212
},
1313
plugins: [

0 commit comments

Comments
 (0)