chore: rename project coven → hack-house ⛧
Rebrand the Rust client crate (coven/ → hh/, package+binary "hack-house"), README, CLI strings, and branch (coven → hack-house). Gitea repo renamed cmd-chat → hack-house to match. Crypto/server logic unchanged; selftest + golden-vector test still green, binary is now `hack-house`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import wraps
|
||||
from inspect import isawaitable
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
from jinja2 import Environment
|
||||
from sanic.compat import Header
|
||||
from sanic.request import Request
|
||||
from sanic.response import HTTPResponse
|
||||
|
||||
from sanic_ext.extensions.templating.render import (
|
||||
LazyResponse,
|
||||
TemplateResponse,
|
||||
)
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sanic_ext import Config
|
||||
|
||||
|
||||
class Templating:
|
||||
def __init__(self, environment: Environment, config: Config) -> None:
|
||||
self.environment = environment
|
||||
self.config = config
|
||||
|
||||
def template(
|
||||
self,
|
||||
file_name: str,
|
||||
status: int = 200,
|
||||
headers: Optional[Union[Header, dict[str, str]]] = None,
|
||||
content_type: str = "text/html; charset=utf-8",
|
||||
**kwargs,
|
||||
):
|
||||
template = self.environment.get_template(file_name)
|
||||
render = (
|
||||
template.render_async
|
||||
if self.config.TEMPLATING_ENABLE_ASYNC
|
||||
else template.render
|
||||
)
|
||||
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
async def decorated_function(*args, **kwargs):
|
||||
context = f(*args, **kwargs)
|
||||
if isawaitable(context):
|
||||
context = await context
|
||||
if isinstance(context, HTTPResponse) and not isinstance(
|
||||
context, TemplateResponse
|
||||
):
|
||||
return context
|
||||
|
||||
# TODO
|
||||
# - Allow each of these to be a callable that is executed here
|
||||
params = {
|
||||
"status": status,
|
||||
"content_type": content_type,
|
||||
"headers": headers,
|
||||
}
|
||||
|
||||
if isinstance(context, LazyResponse):
|
||||
for attr in ("status", "headers", "content_type"):
|
||||
value = getattr(context, attr, None)
|
||||
if value:
|
||||
params[attr] = value
|
||||
context = context.context
|
||||
|
||||
context["request"] = Request.get_current()
|
||||
|
||||
content = render(**context)
|
||||
if isawaitable(content):
|
||||
content = await content
|
||||
|
||||
return HTTPResponse(content, **params)
|
||||
|
||||
return decorated_function
|
||||
|
||||
return decorator
|
||||
@@ -0,0 +1,57 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from collections import abc
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
from jinja2 import (
|
||||
Environment,
|
||||
FileSystemLoader,
|
||||
__version__,
|
||||
select_autoescape,
|
||||
)
|
||||
|
||||
from sanic_ext.extensions.templating.engine import Templating
|
||||
|
||||
from ..base import Extension
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sanic_ext import Extend
|
||||
|
||||
|
||||
class TemplatingExtension(Extension):
|
||||
name = "templating"
|
||||
|
||||
def startup(self, bootstrap: Extend) -> None:
|
||||
self._add_template_paths_to_reloader(
|
||||
self.config.TEMPLATING_PATH_TO_TEMPLATES
|
||||
)
|
||||
loader = FileSystemLoader(self.config.TEMPLATING_PATH_TO_TEMPLATES)
|
||||
|
||||
if not hasattr(bootstrap, "environment"):
|
||||
bootstrap.environment = Environment(
|
||||
loader=loader,
|
||||
autoescape=select_autoescape(),
|
||||
enable_async=self.config.TEMPLATING_ENABLE_ASYNC,
|
||||
)
|
||||
if not hasattr(bootstrap, "templating"):
|
||||
bootstrap.templating = Templating(
|
||||
environment=bootstrap.environment, config=self.config
|
||||
)
|
||||
bootstrap.templating.environment.globals["url_for"] = self.app.url_for
|
||||
|
||||
def label(self):
|
||||
return f"jinja2=={__version__}"
|
||||
|
||||
def _add_template_paths_to_reloader(
|
||||
self, path: Union[str, os.PathLike, Sequence[Union[str, os.PathLike]]]
|
||||
) -> None:
|
||||
if not isinstance(path, abc.Iterable) or isinstance(path, str):
|
||||
path = [path]
|
||||
|
||||
for item in path:
|
||||
self.app.state.reload_dirs.add(Path(item))
|
||||
@@ -0,0 +1,105 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from inspect import isawaitable
|
||||
from typing import TYPE_CHECKING, Any, Optional, Union
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.compat import Header
|
||||
from sanic.exceptions import SanicException
|
||||
from sanic.request import Request
|
||||
from sanic.response import HTTPResponse
|
||||
|
||||
from sanic_ext.exceptions import ExtensionNotFound
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from jinja2 import Environment
|
||||
|
||||
|
||||
class TemplateResponse(HTTPResponse): ...
|
||||
|
||||
|
||||
class LazyResponse(TemplateResponse):
|
||||
__slots__ = (
|
||||
"body",
|
||||
"status",
|
||||
"content_type",
|
||||
"headers",
|
||||
"_cookies",
|
||||
"context",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
context: dict[str, Any],
|
||||
status: int = 0,
|
||||
headers: Optional[Union[Header, dict[str, str]]] = None,
|
||||
content_type: Optional[str] = None,
|
||||
):
|
||||
super().__init__(
|
||||
content_type=content_type, status=status, headers=headers
|
||||
)
|
||||
self.context = context
|
||||
|
||||
|
||||
async def render(
|
||||
template_name: str = "",
|
||||
status: int = 200,
|
||||
headers: Optional[dict[str, str]] = None,
|
||||
content_type: str = "text/html; charset=utf-8",
|
||||
app: Optional[Sanic] = None,
|
||||
environment: Optional[Environment] = None,
|
||||
context: Optional[dict[str, Any]] = None,
|
||||
*,
|
||||
template_source: str = "",
|
||||
) -> TemplateResponse:
|
||||
if app is None:
|
||||
try:
|
||||
app = Sanic.get_app()
|
||||
except SanicException as e:
|
||||
raise SanicException(
|
||||
"Cannot render template beause locating the Sanic application "
|
||||
"was ambiguous. Please return render(..., app=some_app)."
|
||||
) from e
|
||||
|
||||
if template_name and template_source:
|
||||
raise SanicException(
|
||||
"You must provide template_name OR template_source, not both."
|
||||
)
|
||||
|
||||
if environment is None:
|
||||
try:
|
||||
environment = app.ext.environment
|
||||
except AttributeError:
|
||||
raise ExtensionNotFound(
|
||||
"The Templating extension does not appear to be enabled. "
|
||||
"Perhaps jinja2 is not installed."
|
||||
)
|
||||
|
||||
kwargs = context if context else {}
|
||||
|
||||
kwargs["request"] = Request.get_current()
|
||||
|
||||
if template_name or template_source:
|
||||
template = (
|
||||
environment.get_template(template_name)
|
||||
if template_name
|
||||
else environment.from_string(template_source)
|
||||
)
|
||||
|
||||
render = (
|
||||
template.render_async
|
||||
if app.config.TEMPLATING_ENABLE_ASYNC
|
||||
else template.render
|
||||
)
|
||||
content = render(**kwargs)
|
||||
if isawaitable(content):
|
||||
content = await content # type: ignore
|
||||
|
||||
return TemplateResponse( # type: ignore
|
||||
content, status=status, headers=headers, content_type=content_type
|
||||
)
|
||||
else:
|
||||
return LazyResponse(
|
||||
kwargs, status=status, headers=headers, content_type=content_type
|
||||
)
|
||||
Reference in New Issue
Block a user