Attribute resolution rules: replacement, merging, omitting, etc. #71
Replies: 11 comments 4 replies
-
|
Yes, we need to get very precise on the rules here. The general idea is that whichever value of a given attribute comes rightmost should win, except for cumulative attributes like But I know that general idea needs to be fleshed out further; things aren't yet fully baked. Getting precise on the rules and updating the implementation and tests has been my highest priority TODO item, so if you have specific suggestions for the rules you'd expect, it would be very much appreciated! Making your exploration even trickier, this appears to be a bug: |
Beta Was this translation helpful? Give feedback.
-
|
I've taken a few stabs at this and its pretty easy to end up in the weeds. I think I almost broke my brain on it TBH. I guess this seems like an okay starting point. Let me know what you think. I will probably tweak things to make this work and then send a draft PR again and we could see which tests broke. A Proposal:
Reducing complexity
Special cases
Special special cases
Components
Improvements
More cutting?
Actual changesI think most of the examples would keep working this is all about just making the rules more strict and trying to pin down all the edge cases. The major changes would be:
|
Beta Was this translation helpful? Give feedback.
-
|
Thanks @ianjosephwilson -- it's really helpful to see your instincts here and compare notes! Informally speaking, and ignoring special-case attributes, do you think it'd be fair to describe your instincts as drawn from python function invocation and unpacking? Specifically, I'm thinking of your instinct that attribute names should not be repeated, with a possible exception in the rules around "dynamic" attributes. Just to spell it out: if we have a function with the signature I suppose my instincts draw more from Python dictionary unpacking into dictionaries, that is syntax like I think this approach also matches the mental model a little better for people decamping from React/JSX. In javascript, For the special individual attributes — My instincts are much fuzzier about handling of the My instincts are also fuzzy when it comes to value removal/clearing, and I like your thoughts there as a place to start. Taking a cue from JSX, if you pass |
Beta Was this translation helpful? Give feedback.
-
|
Here is one of those weird cases that seems wrong but I can't imagine someone would want. html(t'<p qty="1" {dict(qty="2")} qty="3" {dict(qty=4)} qty={None} qty="5"></p>')This brings up a point I already forgot about. The attributes are parsed upfront and clobbered before the spread attributes can be interpolated. I think the last value is retained but the first set call is used for ordering. The attributes should probably be stored in kv tuples anyways. Ie. My older idea was to model a set of sequences after templates like this with the same n seqs and n-1 interpolates and then just iterate and process into the final final attrs dictionary: @dataclass
class AttributesTemplate:
seqs: tuple[tuple[str, str|None], ...]
interpolations: tuple[Interpolation, ...] |
Beta Was this translation helpful? Give feedback.
-
|
Hey @ianjosephwilson nice to see someone from Pyramid/wired hanging out over here. 😀 Some (late) comments: Dave does a good job of framing this as function invocation vs. dict semantics. You're right to call out the mission: simple and fast, or pluggable and featureful? Confession: I'm a chaos monkey that generates too much of the latter. Regarding performance, there probably are a number of techniques we can do longer-term. Obviously the JSX world has multiple generations of lessons on this, moving static stuff out, etc. Dave and I talked about ideas to move some of the work from rendering to parsing. Your first post quietly brings in a case we are uncertain of: should interpolations need a first = html(t"<div>Hello</div>")
second = t"<div>Hello</div>"
result = html(t"<main>{first} and {second}")It sucks repeating |
Beta Was this translation helpful? Give feedback.
-
|
PS @ianjosephwilson and @pauleveritt sorry for my very sluggish reply here. Partly, I've been turning over this conversation in my head this week, and partly I'm just super heads-down on other stuff. I'll be back! :-) |
Beta Was this translation helpful? Give feedback.
-
|
@davepeck No problem. I have been enjoying trying out t-strings probably a little too much. I tried out a different parsing approach I'll try to start another discussion about. After trying out even more approaches I think you're right for now about sticking with the clobbering from left to right strategy. There are some confusing edge cases but I think it is the easiest to implement and to explain (after those are resolved). @pauleveritt The few the proud. Hopefully this whole htmx revival + t-strings will bring back more python web dev or at least keep it alive. Maybe typing is for another discussion but I couldn't find a lot of information about adding metadata to templates. It is kind of a bummer for the library too. If we could mark the templates then we could easily scan for them and process them beforehand. (my original discussion topic) Have you considered using something like from tdom.types import TDom
def get_product_image_t(product_image_do: ProductImageDO) -> TDom:
""" Create a product image html template fragment consumable by the tdom library. """
return t'<div><img alt={truncate(product_image_do.description, 100)}></div>' |
Beta Was this translation helpful? Give feedback.
-
Ah, good to hear it. Even after mulling it over, this is still where my instincts lie too.
We punted on this when we wrote PEP 750 -- I think it's going to take the community a bit of time to decide how all this works. But yes, something like |
Beta Was this translation helpful? Give feedback.
-
|
I guess the final outcomes of attribute resolution (the clobbering left to right and special cases) as I see them would be this:
|
Beta Was this translation helpful? Give feedback.
-
|
Thanks for merging those changes. There are a few more attribute use-cases I think that still need a bit more smoothing out. One immediate issue is that any sort of merging is going to mean that we probably need to store the attributes as a key/value sequence instead of a dictionary so that a static # We have a default margin that is always set and then we conditionally apply one background color or the other.
background_color = {'bg-near-white': item.active, 'bg-gray': not item.active}
t'''<div class="ma2" class="{background_color}">{item.title}</div>'''This is related to the other discussion about having a structured post-parsing structure:
I think keeping all the information together before resolution kind of makes sense anyways because then things could be changed later and we don't have to redo everything, ie. no data was lost when parsing from the t-string into the templated "structure". Specifically something like this: From HTMLParser: So I kind of think this needs to be resolved before we can start on figuring out how to merge at least the |
Beta Was this translation helpful? Give feedback.
-
|
We arrived at a solid stopping point with #89 , maybe need a few tests but I'm closing this discussion. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
What are the rules for how the attributes are resolved? I have been experimenting with different parsing/serializing ideas and I don't quite understand them.
Attribute Sources (Some? of them?)
t"<input disabled>",t'<input value="1">')t"<input disabled={disabled}>")t'<img alt="A picture of {img_alt}">')t"<input {dict(disabled=True)}>")Special cases/kinds (Some? of them?)
styleclassariadataMerging
Should multiple input attributes be merged or replaced or allow omitting an attribute defined previously by another source?
If you have
t'<div style="padding-left: 10px" {dict(style="padding-right: 20px")}></div>'I would think that you'd get something like<div style="padding-left: 10px; padding-right: 10px">.Even more so for the case of classes:
Finally in the case of boolean attributes I would expect to be able to "turn off" attributes with a later falsy value. This seems to work but only by accident because
disabled=Truealso does not render the attribute.Overriding issue
Beta Was this translation helpful? Give feedback.
All reactions