←back to thread

620 points tambourine_man | 6 comments | | HN request time: 0.759s | source | bottom
1. pansa2 ◴[] No.43750383[source]
> t-strings evaluate to a new type, `string.templatelib.Template`

> To support processing, `Template`s give developers access to the string and its interpolated values before* they are combined into a final string.*

Are there any use-cases where processing a Template involves something other than (i) process each value, then (ii) recombine the results and the string parts, in their original order, to produce a new string? In other words, is the `process_template` function ever going to be substantially different from this (based on `pig_latin` from the article)?

    def process_template(template: Template) -> str:
        result = []
        for item in template:
            if isinstance(item, str):
                result.append(item)
            else:
                result.append(process_value(item.value))
        return "".join(result)
I haven't seen any examples where the function would be different. But if there aren't any, it's strange that the design requires every Template processing function to include this boilerplate, instead of making, say, a `Template.process` method that accepts a `process_value` function.
replies(3): >>43750478 #>>43753470 #>>43757222 #
2. rfoo ◴[] No.43750478[source]
There are a lot of examples about SQL in comments. In the SQL case you want something like:

  def process_template(template: Template) -> tuple[str, tuple]:
    sql_parts = []
    args = []
    for item in template:
      if isinstance(item, str):
        sql_parts.append(item)
      else:
        sql_parts.append("?")
        args.append(process_value(item.value))
    return "".join(sql_parts), tuple(args)
(of course it would be more nuanced, but I hope you get the point)
replies(1): >>43750614 #
3. pansa2 ◴[] No.43750614[source]
Yes that makes sense, thanks.

Also, my comment was about the amount of boilerplate required, but that can be vastly reduced by writing `process_template` in a more functional style instead of the highly-imperative (Golang-like?) style used in the article. The first `process_template` example is just:

    def process_template(template: Template) -> str:
        return ''.join(interleave_longest(template.strings, map(process_value, template.values)))
And the second is something like:

    def process_template(template: Template) -> tuple[str, tuple]:
        return (
            ''.join(interleave_longest(template.strings, ['?'] * len(template.values))),
            map(process_value, template.values)
        )
4. WorldMaker ◴[] No.43753470[source]
Templates don't even have to be processed into a string. The article shows an example where the Template is processed into an HTML mini-DOM. It's maybe not obvious because the DOM object is immediately stringified to show sample output, but you could imagine manipulating the DOM object in a few more steps before stringifying it, or maybe you are running in WASM in a browser and using that mini-DOM directly as a Virtual DOM passed to JS to work with.

Also, in addition to the other SQL example using "?" to fill in the "holes" for parameters in an SQL friendly way, some DBs also support named parameters, so the "hole" in the string form might be naively replaced with something like `f"@{item.expression}"` and that also forms the key in a dict to pass as parameters. (You'd want to make sure that the expression inside the template is useful as a parameter name, and not something more exotic like {1 + 3} or {thing for thing in some_list}, in which cases you are probably auto-assigning some other parameter name.)

replies(1): >>43754703 #
5. pauleveritt ◴[] No.43754703[source]
Nearly everything you just described is being worked on. It's amazing how accurately you have described it. We hope to demo and explain at PyCon US.
6. zahlman ◴[] No.43757222[source]
>But if there aren't any, it's strange that the design requires every Template processing function to include this boilerplate

Other replies gave examples of other use cases. But the neat thing about Python is that you don't need to "include this boilerplate" for the common cases. It can be wrapped up in a decorator (which could be included in `templatelib`). Or, as you say, in a method on the Template class.

I think I'd implement it as a generator, calling `process_value` (defaulting to `str`) on the Interpolations, so that the caller can still do more with the results (or just `''.join` them).

But these are separate considerations; nothing prevents implementing them later, or indeed adding them to the implementation before the 3.14 release.