Add convert() function to string.templatelib (original) (raw)
I have been playing with t-strings and I keep finding myself copy-pasting the convert
function from the PEP:
def convert(value: object, conversion: Literal["a", "r", "s"] | None) -> object:
if conversion == "a":
return ascii(value)
elif conversion == "r":
return repr(value)
elif conversion == "s":
return str(value)
return value
The built-in format
function makes it easy to process the format specification for a replacement field, but there’s no built-in convert
function for handling !a
, !r
, and !s
conversions.
It seems like this function might be useful to have implemented within string.templatelib
, so it won’t need to be re-implemented by all t-string parsing utilities.
AA-Turner (Adam Turner) June 5, 2025, 10:29pm 2
I support this idea, I would also suggest asking the RM to add this to 3.14, if accepted.
A
ilotoki0804 (Ilotoki0804) June 6, 2025, 1:06am 3
We might also consider implementing it as a staticmethod of Interpolation
.
blhsing (Ben Hsing) June 6, 2025, 1:16am 4
Just curious though… It sounds like you’re trying to emulate an f-string with a t-string using format
and convert
, so then for what purpose are you not using an f-string directly?
I do kind of support the idea because it would be useful if/when the logging library supports t-strings as repr
is useful primarily for debugging, but just want to ask for clarification of what the OP sees as use cases because I personally don’t quite see many other scenarios where a template library wouldn’t want to always call str
on the interpolation’s value if stringification is needed so I doubt that the claim that convert
“needs to be re-implemented by all t-string parsing utilities” is remotely true (for example, the talks about adding t-string support for subprocess and DB-API don’t include a mention of the standard convert
logics).
trey (Trey Hunner) June 6, 2025, 2:21am 5
This was for this library I made today.
The dedent
function in that library uses a t-string in a very similar way to an f-string, but delaying interpolation is important for its purpose.
blhsing (Ben Hsing) June 6, 2025, 2:57am 6
Right, the primary use case then is for a library to emulate f-strings while delaying the evaluation of the interpolation’s stringification. Fair enough.
dkp (Dave Peck) June 6, 2025, 2:19pm 7
I’d like to see this too.
Would we prefer it:
- As a function directly in
templatelib
or - As a staticmethod on
Interpolation
?
Would our preference if shipped be for it to:
- Accept a
conversion
ofNone
as in the code above, or - Only accept
Literal[“a”, “r”, “s”]
and always return astr
?
Related: in past discussions there was talk of also adding an Interpolation.format_value(…)
convenience method that invokes both convert()
and the format()
builtin in one go. It might make sense to do both at the same time.
AA-Turner (Adam Turner) June 6, 2025, 2:50pm 8
I think it should be a module-level function, we could see DSLs that use the conversion flag for entirely different things than formatted string literal-like behaviour if we relax the restriction on conversion flag values in the future. I think it should accept None
, though, because that’s the default for an omitted conversion flag.
A
pR0Ps (Carey Metcalfe) June 6, 2025, 4:07pm 9
Unless I’m missing something, can’t the existing string.Formatter.convert_field
already be used for this?
As an example, I’ve implemented a function to format a t-string like an f-string like so:
from string import Formatter
from string.templatelib import Template, Interpolation
_FORMATTER = Formatter()
def tstring_as_fstring(tstring):
return "".join(
(
_FORMATTER.format_field(
_FORMATTER.convert_field(x.value, x.conversion),
x.format_spec
)
) if isinstance(x, Interpolation)
else x
for x in tstring
)
Also of note is that the string.Formatter.convert_field
method doesn’t actually use the bound self arg so it’s possible to implement your function like:
import functools
from string import Formatter
convert = functools.partial(Formatter.convert_value, None)
# or, more verbosely:
def convert(value: object, conversion) -> object:
return Formatter.convert_value(None, value, conversion)
Though it’s probably less likely to break if it’s called “properly” via an actual Formatter
instance like the first example vs. treating it like a static method.
AA-Turner (Adam Turner) June 6, 2025, 5:16pm 10
True, we could suggest that people alias convert = classmethod(string.Formatter.convert_field)
. My slight argument for putting it in string.templatelib
is discoverability and semantics, though.
A
trey (Trey Hunner) June 6, 2025, 5:52pm 11
I hadn’t known about Formatter.convert_field
.
I found that I needed to use either:
from functools import partial
convert = partial(Formatter.convert_field, Formatter)
Or (just classmethod
didn’t work since that provided the descriptor but not the bound method):
from string import Formatter
convert = classmethod(Formatter.convert_field).__get__(Formatter)
gerardw (Gerardwx) June 6, 2025, 6:24pm 12
I also cribbed the PEP code, so having an Interpolation method makes sense to me.
from string.templatelib import Interpolation
from typing import Literal
# dervied from https://raw.githubusercontent.com/davepeck/pep750-examples/refs/heads/main/pep/fstring.py
def iformat(interp: Interpolation) -> str:
"""Format an interpolation like an f-string: apply conversion then format specifier."""
value = interp.value
conv = interp.conversion
# apply conversion (ascii, repr, str) if specified
if conv == "a":
value = ascii(value)
elif conv == "r":
value = repr(value)
elif conv == "s":
value = str(value)
# None or other means leave value as is
# apply the format specifier
return format(value, interp.format_spec)
yoavrv (Yoav Ravid) June 6, 2025, 10:05pm 13
As someone newer to python, I feel there should be an f-string syntax (as it’s the most obvious way to format strings in modern python).
I hoped the inner interpolation method from :format_spec
would work but it doesn’t seem to work for the !conversion
part.
I.e. This is a somewhat common idiom
formatted_value = f'{value:{format_spec}}'
But this doesn’t work
formatted_value = f'{value!{conversion}:{format_spec}}'
Personally I would prefer some kind of f-string version to a stand-alone function on nice-syntax and discoverability grounds, but I guess this would require changes to f-strings which is a whole PEP at least.
(Also this makes me think that maybe the Interpolation
fields should default to ""
rather than None
but that ship has sailed)