Add as_mutable for Jinja templating#88626
Conversation
|
Added docs. |
emontnemery
left a comment
There was a problem hiding this comment.
While the motivation for the PR is clear, the ergonomics is maybe not so great.
Instead of adding the as_mutable filter, could we explore adding our own Jinja SandboxedEnvironment which would modify a copy of the source data instead of modifying it directly?
Another option could be to let Jinja [] and {} operators create _MutableDict and _MutableList respectively so user defined lists and dicts can be modified?
|
Happy to keep iterating. A few thoughts:
So -- that's how I landed on being explicit about making things mutable -- everywhere else it quickly gets confusing and difficult to predict which things will be automatically mutable and what changes may stick. I do like the idea of making user-defined lists and dicts automatically mutable. |
|
@emontnemery Do you have any idea how to override the {} and [] behaviors in jinja? I'm having trouble finding a good hook, but will keep looking. Since they're not operators AFAICT (they're a literal syntax built into the language), I'm not sure how to adjust it. |
|
After a little digging, I couldn't find any way to adjust the behavior of literal creation (I think it's just compiling down to raw Python literals, which also don't look overridable). Happy to keep going back and forth -- marking as Ready for review so it ends up back on your radar, but if that's the wrong thing to do since I haven't actually changed anything, apologies in advance :) |
|
@emontnemery no rush, just thinking it would be nice to get this in with all the other Jinja changes I made in this cycle. Any thoughts on what I mentioned above? Seems easier to follow a world where everything is immutable unless explicitly marked otherwise then one where it's unclear where changes will persist, but definitely interested in any ideas you have. |
|
@depoll thanks for looking into the copy on modify as well as overriding operators 👍 The concern with this PR is still that |
|
Here's an example straight out of my setup. I have a template sensor that I use to aggregate BLE info I'm gathering on a bunch of ESPHome devices (the built in presence tools don't work very well for my use cases). It receives events and updates its state:
But let's just take the last for loop as an example. Here, I'm doing crazy amounts of copying immutable lists to rebuild them item-by-item to potentially replace a single entry. This could instead be rewritten: This turns an O(n^2) operation (because of repeated copying of lists) into an O(n) operation (because there is a single copy which is then modified in place). I have a number of similar examples with dicts where e.g. removing an item requires rebuilding the dict item-by-item, making copies at every iteration, when a single To become this: |
|
You should really write your beacon template as a custom integration in Python and don't use jinja2 for this. |
|
@balloob fair enough, though (at least for me) a custom integration is fairly heavyweight to get started with by comparison to a template sensor and vastly more painful to debug with live data (because every change requires restarting HA instead of just reloading templates). Anyway, the use case exists in far smaller settings -- I have a number of bookkeeping cases where I'm getting an event and doing simple accumulation in a dict based on those events. Both adding and removing items are more painful and resource-intensive than they need to be. |
|
We've discussed this functionality extensively a couple of times with a couple of core developers, and we have decided not to accept this change/feature. It is too confusing to use and explain, and the given use case is very specific (and shouldn't really be a template). Nevertheless, thanks for being willing to contribute @depoll 👍 ../Frenck |
Breaking change
Proposed change
Breaking out #88575
Here, I've added the ability to create a mutable copy of a list or dictionary in jinja that you can directly modify (rather than rebuilding the list/dictionary element-by-element, which can turn an O(1) operation into an O(n) or O(n^2) operation). This requires two additions:
as_mutablefilter so that one can efficiently mutate a list or dictionary without rebuilding it from scratch in a loopdoextension to make it possible to mutate lists/dicts.All of this enables the following code:
To produce the following output:
I'm happy to update the documentation as well but wanted to first clear that this would be a welcome change.
Type of change
Additional information
Checklist
black --fast homeassistant tests)If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest.requirements_all.txt.Updated by running
python3 -m script.gen_requirements_all..coveragerc.To help with the load of incoming pull requests: