From 051c0c3269fc2dd1d7933b6d58ebc5fc22135eac Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Sat, 18 Nov 2023 14:46:33 +0000 Subject: [PATCH] Use AppKey (#534) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- aiohttp_jinja2/__init__.py | 53 +++++++++++++++++++------------- aiohttp_jinja2/helpers.py | 29 ++++++++++++----- docs/index.rst | 9 +++--- requirements.txt | 2 +- setup.py | 2 +- tests/test_context_processors.py | 9 +++--- tests/test_jinja_globals.py | 6 ++-- tests/test_simple_renderer.py | 6 +--- 8 files changed, 67 insertions(+), 49 deletions(-) diff --git a/aiohttp_jinja2/__init__.py b/aiohttp_jinja2/__init__.py index 5d7381f1..b744382b 100644 --- a/aiohttp_jinja2/__init__.py +++ b/aiohttp_jinja2/__init__.py @@ -4,6 +4,7 @@ Awaitable, Callable, Dict, + Final, Mapping, Optional, Protocol, @@ -11,7 +12,6 @@ Tuple, TypeVar, Union, - cast, overload, ) @@ -19,22 +19,30 @@ 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) @@ -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, @@ -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. @@ -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) @@ -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) @@ -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: @@ -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: @@ -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: diff --git a/aiohttp_jinja2/helpers.py b/aiohttp_jinja2/helpers.py index fb367555..756079cd 100644 --- a/aiohttp_jinja2/helpers.py +++ b/aiohttp_jinja2/helpers.py @@ -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 @@ -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, @@ -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'] = ''." - ) 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] = ''." + ) 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("/")) diff --git a/docs/index.rst b/docs/index.rst index f3faec90..1fec8624 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -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:: diff --git a/requirements.txt b/requirements.txt index 4217f7ca..4ce93989 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/setup.py b/setup.py index 4e27e98d..5bca97c3 100644 --- a/setup.py +++ b/setup.py @@ -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, ) diff --git a/tests/test_context_processors.py b/tests/test_context_processors.py index e0206173..bc2967d0 100644 --- a/tests/test_context_processors.py +++ b/tests/test_context_processors.py @@ -1,4 +1,4 @@ -from typing import Dict, Union +from typing import Dict, Tuple, Union import jinja2 from aiohttp import web @@ -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) @@ -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, ) @@ -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, ) diff --git a/tests/test_jinja_globals.py b/tests/test_jinja_globals.py index 355869cf..11547df1 100644 --- a/tests/test_jinja_globals.py +++ b/tests/test_jinja_globals.py @@ -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) @@ -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() @@ -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 diff --git a/tests/test_simple_renderer.py b/tests/test_simple_renderer.py index cbda557b..edfe4e69 100644 --- a/tests/test_simple_renderer.py +++ b/tests/test_simple_renderer.py @@ -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)