Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use AppKey #534

Merged
merged 12 commits into from
Nov 18, 2023
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 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 @@
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.")

Check warning on line 101 in aiohttp_jinja2/__init__.py

View check run for this annotation

Codecov / codecov/patch

aiohttp_jinja2/__init__.py#L100-L101

Added lines #L100 - L101 were not covered by tests


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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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(

Check warning on line 81 in aiohttp_jinja2/helpers.py

View check run for this annotation

Codecov / codecov/patch

aiohttp_jinja2/helpers.py#L81

Added line #L81 was not covered by tests
"'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