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

Require async/await usage in templates #1191

Open
pgjones opened this issue Apr 15, 2020 · 5 comments
Open

Require async/await usage in templates #1191

pgjones opened this issue Apr 15, 2020 · 5 comments
Labels

Comments

@pgjones
Copy link
Member

pgjones commented Apr 15, 2020

The current async mode happily allows coroutines to be awaited without requiring the await keyword. Whilst this is nice, it is actually quite a confusing and a non-obvious behavior. In addition it doesn't always work, for example {% if coroutine %}, and I'm not sure {% async for ... or {% async with ... work. (Edit: The if statement does work, I'm unsure about async for or async with)

I propose that templating engine understand and require the usage of the async and await keywords dropping the current support for non awaited coroutines.

It would also be nice if the engine did not use any asyncio features so as to be event loop agnostic.

@davidism
Copy link
Member

Based on the docs, the intention seems to be that the template designer doesn't need to understand what type of function was passed, they could continue to write templates as they always had. Whether this has actually been beneficial is impossible to measure.

The main benefit I see is a sort of re-wording of that. By not requiring the await keyword, you don't have to care if your templates will be rendered in an async environment or not. For example, a Flask extension could provide a blueprint with some templates that loop over a database query, and they'd continue to work even if the project using the extension was using an async query library.

I can envision making enable_async accept the value "strict" instead of just True. This would enable async behavior but the compiler wouldn't output the auto_await wrapper. Instead, a new await token and node would be added to tell the compiler to output the await keyword. This would probably be more performant since the wrapper wouldn't be called on literally everything, but would not be as portable. The compiler could skip the await in sync mode, but this wouldn't be completely equivalent to the current behavior, as the template author would have to decide what would and would not have to be async in async mode.


As to using asyncio features, I'm not clear what we'd have to do not to, but I'm open to it. It looks like all it's used for right now is to get an event loop when patching some functions.

@pgjones
Copy link
Member Author

pgjones commented Aug 8, 2020

I think an additional "strict" mode is a better solution, as I can see the value in the original intention - although I personally would like to avoid this.

I'm also not sure if asyncio features are required, but aiming to avoid them would certainly help support other event loops.

@nirvana-msu
Copy link

I'm also in favour of adding explicit/strict mode, at least as an option. Perhaps someone maintaining a library with templates might think differently, but as far as your own code where you control both front-end and back-end is concerned, being explicit with await calls is significantly better than being implicit.

@davidism
Copy link
Member

davidism commented Feb 4, 2021

@nirvana-msu It sounds like you've misunderstood the strict mode I'm proposing. It would not add or require the await keyword in templates, it would always generate them behind the scenes when compiling the templates. That makes rendering more efficient when you can guarantee everything will be async. I don't plan to add keywords to the template syntax.

@nirvana-msu
Copy link

@davidism indeed - I misinterpreted what "new await token" meant. It feels to me though that actually supporting await and async for within the templating language itself would have been the best design - otherwise you end up with a kind of "implicit" concurrency as opposed to asyncio philosophy of "explicit" concurrency / context switching. Not having control of when you await a coroutine can significantly complicate implementation in certain [more complex] situations (say, what if you need to pass a coroutine / async generator as an argument to macro.. I believe in current implementation you have to wrap it in another method, otherwise it would be awaited too early.. etc).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants