Skip to content

Commit

Permalink
Use AppKey (#534)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
Dreamsorcerer and pre-commit-ci[bot] authored Nov 18, 2023
1 parent 9619811 commit 051c0c3
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 49 deletions.
53 changes: 31 additions & 22 deletions aiohttp_jinja2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,45 @@
Awaitable,
Callable,
Dict,
Final,
Mapping,
Optional,
Protocol,
Sequence,
Tuple,
TypeVar,
Union,
cast,
overload,
)

import jinja2
from aiohttp import web
from aiohttp.abc import AbstractView

from .helpers import GLOBAL_HELPERS
from .helpers import GLOBAL_HELPERS, static_root_key
from .typedefs import Filters

__version__ = "1.5.1"

__all__ = ("setup", "get_env", "render_template", "render_string", "template")


APP_CONTEXT_PROCESSORS_KEY = "aiohttp_jinja2_context_processors"
APP_KEY = "aiohttp_jinja2_environment"
REQUEST_CONTEXT_KEY = "aiohttp_jinja2_context"
__all__ = (
"get_env",
"render_string",
"render_template",
"setup",
"static_root_key",
"template",
)

_TemplateReturnType = Awaitable[Union[web.StreamResponse, Mapping[str, Any]]]
_SimpleTemplateHandler = Callable[[web.Request], _TemplateReturnType]
_ContextProcessor = Callable[[web.Request], Awaitable[Dict[str, Any]]]

APP_CONTEXT_PROCESSORS_KEY: Final = web.AppKey[Sequence[_ContextProcessor]](
"APP_CONTEXT_PROCESSORS_KEY"
)
APP_KEY: Final = web.AppKey[jinja2.Environment]("APP_KEY")
REQUEST_CONTEXT_KEY: Final = "aiohttp_jinja2_context"

_T = TypeVar("_T")
_AbstractView = TypeVar("_AbstractView", bound=AbstractView)

Expand Down Expand Up @@ -62,7 +70,7 @@ def __call__(
def setup(
app: web.Application,
*args: Any,
app_key: str = APP_KEY,
app_key: web.AppKey[jinja2.Environment] = APP_KEY,
context_processors: Sequence[_ContextProcessor] = (),
filters: Optional[Filters] = None,
default_helpers: bool = True,
Expand All @@ -84,23 +92,24 @@ def setup(
return env


def get_env(app: web.Application, *, app_key: str = APP_KEY) -> jinja2.Environment:
return cast(jinja2.Environment, app.get(app_key))
def get_env(
app: web.Application, *, app_key: web.AppKey[jinja2.Environment] = APP_KEY
) -> jinja2.Environment:
try:
return app[app_key]
except KeyError:
raise RuntimeError("aiohttp_jinja2.setup(...) must be called first.")


def _render_string(
template_name: str,
request: web.Request,
context: Mapping[str, Any],
app_key: str,
app_key: web.AppKey[jinja2.Environment],
) -> Tuple[jinja2.Template, Mapping[str, Any]]:
env = request.config_dict.get(app_key)
if env is None:
text = (
"Template engine is not initialized, "
"call aiohttp_jinja2.setup(..., app_key={}) first"
"".format(app_key)
)
text = "Template engine is not initialized, call aiohttp_jinja2.setup() first"
# in order to see meaningful exception message both: on console
# output and rendered page we add same message to *reason* and
# *text* arguments.
Expand All @@ -124,7 +133,7 @@ def render_string(
request: web.Request,
context: Mapping[str, Any],
*,
app_key: str = APP_KEY,
app_key: web.AppKey[jinja2.Environment] = APP_KEY,
) -> str:
template, context = _render_string(template_name, request, context, app_key)
return template.render(context)
Expand All @@ -135,7 +144,7 @@ async def render_string_async(
request: web.Request,
context: Mapping[str, Any],
*,
app_key: str = APP_KEY,
app_key: web.AppKey[jinja2.Environment] = APP_KEY,
) -> str:
template, context = _render_string(template_name, request, context, app_key)
return await template.render_async(context)
Expand All @@ -159,7 +168,7 @@ def render_template(
request: web.Request,
context: Optional[Mapping[str, Any]],
*,
app_key: str = APP_KEY,
app_key: web.AppKey[jinja2.Environment] = APP_KEY,
encoding: str = "utf-8",
status: int = 200,
) -> web.Response:
Expand All @@ -173,7 +182,7 @@ async def render_template_async(
request: web.Request,
context: Optional[Mapping[str, Any]],
*,
app_key: str = APP_KEY,
app_key: web.AppKey[jinja2.Environment] = APP_KEY,
encoding: str = "utf-8",
status: int = 200,
) -> web.Response:
Expand All @@ -187,7 +196,7 @@ async def render_template_async(
def template(
template_name: str,
*,
app_key: str = APP_KEY,
app_key: web.AppKey[jinja2.Environment] = APP_KEY,
encoding: str = "utf-8",
status: int = 200,
) -> _TemplateWrapper:
Expand Down
29 changes: 21 additions & 8 deletions aiohttp_jinja2/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
useful context functions, see
http://jinja.pocoo.org/docs/dev/api/#jinja2.contextfunction
"""
import warnings
from typing import Dict, Optional, TypedDict, Union

import jinja2
Expand All @@ -13,6 +14,9 @@ class _Context(TypedDict, total=False):
app: web.Application


static_root_key = web.AppKey("static_root_key", str)


@jinja2.pass_context
def url_for(
context: _Context,
Expand Down Expand Up @@ -55,21 +59,30 @@ def url_for(
def static_url(context: _Context, static_file_path: str) -> str:
"""Filter for generating urls for static files.
NOTE: you'll need
to set app['static_root_url'] to be used as the root for the urls returned.
NOTE: you'll need to set app[aiohttp_jinja2.static_root_key] to be used as the
root for the urls returned.
Usage: {{ static('styles.css') }} might become
"/static/styles.css" or "http://mycdn.example.com/styles.css"
"""
app = context["app"]
try:
static_url = app["static_root_url"]
static_url = app[static_root_key]
except KeyError:
raise RuntimeError(
"app does not define a static root url "
"'static_root_url', you need to set the url root "
"with app['static_root_url'] = '<static root>'."
) from None
try:
# TODO (aiohttp 3.10+): Remove this fallback
static_url = app["static_root_url"]
except KeyError:
raise RuntimeError(
"app does not define a static root url, you need to set the url root "
"with app[aiohttp_jinja2.static_root_key] = '<static root>'."
) from None
else:
warnings.warn(
"'static_root_url' is deprecated, use aiohttp_jinja2.static_root_key.",
category=DeprecationWarning,
stacklevel=2,
)
return "{}/{}".format(static_url.rstrip("/"), static_file_path.lstrip("/"))


Expand Down
9 changes: 4 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,13 @@ This is useful as it would allow your static path to switch in
deployment or testing with just one line.

The ``static`` function has similar usage, except it requires you to
set ``static_root_url`` on the app
set ``app[aiohttp_jinja2.static_root_key]``.

.. code-block:: ruby
.. code-block:: python
app = web.Application()
aiohttp_jinja2.setup(app,
loader=jinja2.FileSystemLoader('/path/to/templates/folder'))
app['static_root_url'] = '/static'
aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader("/path/to/templates/folder"))
app[aiohttp_jinja2.static_root_key] = "/static"
Then in the template::

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-e .
aiohttp==3.8.6
aiohttp==3.9.0
alabaster>=0.6.2
coverage==7.2.7
jinja2==3.1.2
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,6 @@ def read(f):
license="Apache 2",
packages=["aiohttp_jinja2"],
python_requires=">=3.8",
install_requires=("aiohttp>=3.6.3", "jinja2>=3.0.0"),
install_requires=("aiohttp>=3.9.0", "jinja2>=3.0.0"),
include_package_data=True,
)
9 changes: 5 additions & 4 deletions tests/test_context_processors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Union
from typing import Dict, Tuple, Union

import jinja2
from aiohttp import web
Expand All @@ -22,10 +22,11 @@ async def func(request):
async def processor(request: web.Request) -> Dict[str, Union[str, int]]:
return {"foo": 1, "bar": "should be overwriten"}

app["aiohttp_jinja2_context_processors"] = (
f: Tuple[aiohttp_jinja2._ContextProcessor, ...] = (
aiohttp_jinja2.request_processor,
processor,
)
app[aiohttp_jinja2.APP_CONTEXT_PROCESSORS_KEY] = f

app.router.add_get("/", func)

Expand Down Expand Up @@ -56,7 +57,7 @@ async def func(request):
async def subprocessor(request):
return {"foo": 1, "bar": "should be overwriten"}

subapp["aiohttp_jinja2_context_processors"] = (
subapp[aiohttp_jinja2.APP_CONTEXT_PROCESSORS_KEY] = (
aiohttp_jinja2.request_processor,
subprocessor,
)
Expand All @@ -69,7 +70,7 @@ async def subprocessor(request):
async def processor(request):
return {"baz": 5}

app["aiohttp_jinja2_context_processors"] = (
app[aiohttp_jinja2.APP_CONTEXT_PROCESSORS_KEY] = (
aiohttp_jinja2.request_processor,
processor,
)
Expand Down
6 changes: 3 additions & 3 deletions tests/test_jinja_globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ async def index(request):
app, loader=jinja2.DictLoader({"tmpl.jinja2": "{{ static('whatever.js') }}"})
)

app["static_root_url"] = "/static"
app[aiohttp_jinja2.static_root_key] = "/static"
app.router.add_route("GET", "/", index)
client = await aiohttp_client(app)

Expand All @@ -153,7 +153,7 @@ async def index(request):

async def test_static_var_missing(aiohttp_client, caplog):
async def index(request):
with pytest.raises(RuntimeError, match="static_root_url"):
with pytest.raises(RuntimeError, match="static_root_key"):
aiohttp_jinja2.render_template("tmpl.jinja2", request, {})
return web.Response()

Expand All @@ -166,4 +166,4 @@ async def index(request):
client = await aiohttp_client(app)

resp = await client.get("/")
assert 200 == resp.status # static_root_url is not set
assert 200 == resp.status # static_root_key is not set
6 changes: 1 addition & 5 deletions tests/test_simple_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,7 @@ async def func(request: web.Request) -> web.Response:
app.router.add_route("GET", "/", func)

req = make_mocked_request("GET", "/", app=app)
msg = (
"Template engine is not initialized, "
"call aiohttp_jinja2.setup(..., app_key={}"
") first".format(aiohttp_jinja2.APP_KEY)
)
msg = "Template engine is not initialized, call aiohttp_jinja2.setup() first"

with pytest.raises(web.HTTPInternalServerError) as ctx:
await func(req)
Expand Down

0 comments on commit 051c0c3

Please sign in to comment.