add {% static ... %} template option

This commit is contained in:
Joe Kaufeld 2024-09-16 18:15:39 -04:00
parent 3cc86f0dbe
commit 24109014af
6 changed files with 75 additions and 7 deletions

View File

@ -26,6 +26,8 @@ app = SpiderwebRouter(
staticfiles_dirs=["static_files"], staticfiles_dirs=["static_files"],
append_slash=False, # default append_slash=False, # default
cors_allow_all_origins=True, cors_allow_all_origins=True,
static_url="static_stuff",
debug=True,
) )

View File

@ -20,3 +20,11 @@ class FloatConverter:
def to_python(self, value): def to_python(self, value):
return float(value) return float(value)
class PathConverter:
regex = r".+"
name = "path"
def to_python(self, value):
return str(value)

16
spiderweb/jinja_core.py Normal file
View File

@ -0,0 +1,16 @@
from typing import TYPE_CHECKING
from jinja2 import Environment
if TYPE_CHECKING:
from spiderweb import SpiderwebRouter
class SpiderwebEnvironment(Environment):
# Contains all the normal abilities of the Jinja environment, but with a link
# back to the server for easy access to settings and other server-related
# information.
def __init__(self, server=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.server: "SpiderwebRouter" = server

View File

@ -0,0 +1,19 @@
import posixpath
from jinja2 import nodes
from jinja2.ext import Extension
class StaticFilesExtension(Extension):
# Take things that look like `{% static "file" %}` and replace them with `/static/file`
tags = {"static"}
def parse(self, parser):
token = next(parser.stream)
args = [parser.parse_expression()]
return nodes.Output([self.call_method("_static", args)]).set_lineno(
token.lineno
)
def _static(self, file):
return posixpath.join(f"/{self.environment.server.static_url}", file)

View File

@ -6,10 +6,10 @@ import traceback
import urllib.parse as urlparse import urllib.parse as urlparse
from logging import Logger from logging import Logger
from threading import Thread from threading import Thread
from typing import Optional, Callable, Sequence, LiteralString, Literal from typing import Optional, Callable, Sequence, Literal
from wsgiref.simple_server import WSGIServer from wsgiref.simple_server import WSGIServer
from jinja2 import BaseLoader, Environment, FileSystemLoader from jinja2 import BaseLoader, FileSystemLoader
from peewee import Database, SqliteDatabase from peewee import Database, SqliteDatabase
from spiderweb.middleware import MiddlewareMixin from spiderweb.middleware import MiddlewareMixin
@ -31,6 +31,7 @@ from spiderweb.exceptions import (
NoResponseError, NoResponseError,
SpiderwebNetworkException, SpiderwebNetworkException,
) )
from spiderweb.jinja_core import SpiderwebEnvironment
from spiderweb.local_server import LocalServerMixin from spiderweb.local_server import LocalServerMixin
from spiderweb.request import Request from spiderweb.request import Request
from spiderweb.response import HttpResponse, TemplateResponse, JsonResponse from spiderweb.response import HttpResponse, TemplateResponse, JsonResponse
@ -61,10 +62,12 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
cors_allow_private_network: bool = False, cors_allow_private_network: bool = False,
csrf_trusted_origins: Sequence[str] = None, csrf_trusted_origins: Sequence[str] = None,
db: Optional[Database] = None, db: Optional[Database] = None,
debug: bool = False,
templates_dirs: Sequence[str] = None, templates_dirs: Sequence[str] = None,
middleware: Sequence[str] = None, middleware: Sequence[str] = None,
append_slash: bool = False, append_slash: bool = False,
staticfiles_dirs: Sequence[str] = None, staticfiles_dirs: Sequence[str] = None,
static_url: str = "static",
routes: Sequence[tuple[str, Callable] | tuple[str, Callable, dict]] = None, routes: Sequence[tuple[str, Callable] | tuple[str, Callable, dict]] = None,
error_routes: dict[int, Callable] = None, error_routes: dict[int, Callable] = None,
secret_key: str = None, secret_key: str = None,
@ -87,6 +90,7 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
self.append_slash = append_slash self.append_slash = append_slash
self.templates_dirs = templates_dirs self.templates_dirs = templates_dirs
self.staticfiles_dirs = staticfiles_dirs self.staticfiles_dirs = staticfiles_dirs
self.static_url = static_url
self._middleware: list[str] = middleware or [] self._middleware: list[str] = middleware or []
self.middleware: list[Callable] = [] self.middleware: list[Callable] = []
self.secret_key = secret_key if secret_key else self.generate_key() self.secret_key = secret_key if secret_key else self.generate_key()
@ -109,6 +113,8 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
convert_url_to_regex(i) for i in self._csrf_trusted_origins convert_url_to_regex(i) for i in self._csrf_trusted_origins
] ]
self.debug = debug
self.extra_data = kwargs self.extra_data = kwargs
# session middleware # session middleware
@ -144,13 +150,23 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
if self.error_routes: if self.error_routes:
self.add_error_routes() self.add_error_routes()
template_env_args = {
"server": self,
"extensions": [
"spiderweb.jinja_extensions.StaticFilesExtension",
],
}
if self.templates_dirs: if self.templates_dirs:
self.template_loader = Environment( self.template_loader = SpiderwebEnvironment(
loader=FileSystemLoader(self.templates_dirs) loader=FileSystemLoader(self.templates_dirs),
**template_env_args,
) )
else: else:
self.template_loader = None self.template_loader = None
self.string_loader = Environment(loader=BaseLoader()) self.string_loader = SpiderwebEnvironment(
loader=BaseLoader(), **template_env_args
)
if self.staticfiles_dirs: if self.staticfiles_dirs:
for static_dir in self.staticfiles_dirs: for static_dir in self.staticfiles_dirs:
@ -160,7 +176,14 @@ class SpiderwebRouter(LocalServerMixin, MiddlewareMixin, RoutesMixin, FernetMixi
f"Static files directory '{str(static_dir)}' does not exist." f"Static files directory '{str(static_dir)}' does not exist."
) )
raise ConfigError raise ConfigError
self.add_route(r"/static/<str:filename>", send_file) # noqa: F405 if self.debug:
# We don't need a log message here because this is the expected behavior
self.add_route(rf"/{self.static_url}/<path:filename>", send_file) # noqa: F405
else:
self.log.warning(
"`staticfiles_dirs` is set, but `debug` is set to FALSE. Static"
" files will not be served."
)
# finally, run the startup checks to verify everything is correct and happy. # finally, run the startup checks to verify everything is correct and happy.
self.log.info("Run startup checks...") self.log.info("Run startup checks...")

View File

@ -13,7 +13,7 @@
middleware is working. middleware is working.
</p> </p>
<p> <p>
<img src="/static/aaaaaa.gif" alt="AAAAAAAAAA"> <img src="{% static 'aaaaaa.gif' %}" alt="AAAAAAAAAA">
</p> </p>
<p> <p>
{{ request.META }} {{ request.META }}