# Add convert() function to string.templatelib

**URL:** <https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569>\
**Category:** Ideas\
**Created:** [June 5, 2025, 9:56pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569 "2025-06-05T21:56:38Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![trey](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/trey/32/20518_2.png) [@trey](https://discuss.python.org/u/trey)\
**Post date:** [June 5, 2025, 9:56pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/1 "2025-06-05T21:56:38Z")

</div>

I have been playing with t-strings and I keep finding myself copy-pasting the `convert` function [from the PEP](https://peps.python.org/pep-0750/#example-implementing-f-strings-with-t-strings):

```python
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.

@dkp

---

<div class="post-metadata">

**Author:** ![AA-Turner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/aa-turner/32/4979_2.png) [@AA-Turner](https://discuss.python.org/u/AA-Turner)\
**Post date:** [June 5, 2025, 10:29pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/2 "2025-06-05T22:29:50Z")

</div>

I support this idea, I would also suggest asking the RM to add this to 3.14, if accepted.

A

---

<div class="post-metadata">

**Author:** ![ilotoki0804](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ilotoki0804/32/14237_2.png) [@ilotoki0804](https://discuss.python.org/u/ilotoki0804)\
**Post date:** [June 6, 2025, 1:06am UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/3 "2025-06-06T01:06:42Z")

</div>

We might also consider implementing it as a staticmethod of `Interpolation`.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [June 6, 2025, 1:16am UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/4 "2025-06-06T01:16:47Z")

</div>

> [@trey](#):
>
> 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.

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).

---

<div class="post-metadata">

**Author:** ![trey](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/trey/32/20518_2.png) [@trey](https://discuss.python.org/u/trey)\
**Post date:** [June 6, 2025, 2:21am UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/5 "2025-06-06T02:21:53Z")

</div>

This was for [this library I made today](https://github.com/treyhunner/better-dedent).

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.

---

<div class="post-metadata">

**Author:** ![blhsing](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/blhsing/32/25812_2.png) [@blhsing](https://discuss.python.org/u/blhsing)\
**Post date:** [June 6, 2025, 2:57am UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/6 "2025-06-06T02:57:30Z")

</div>

> [@trey](#):
>
> 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.

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.

---

<div class="post-metadata">

**Author:** ![dkp](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dkp/32/20413_2.png) [@dkp](https://discuss.python.org/u/dkp)\
**Post date:** [June 6, 2025, 2:19pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/7 "2025-06-06T14:19:29Z")

</div>

I’d like to see this too.

Would we prefer it:

1. As a function directly in `templatelib` or
2. As a staticmethod on `Interpolation`?

Would our preference if shipped be for it to:

1. Accept a `conversion` of `None` as in the code above, or
2. Only accept `Literal[“a”, “r”, “s”]` and always return a `str`?

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.

---

<div class="post-metadata">

**Author:** ![AA-Turner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/aa-turner/32/4979_2.png) [@AA-Turner](https://discuss.python.org/u/AA-Turner)\
**Post date:** [June 6, 2025, 2:50pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/8 "2025-06-06T14:50:34Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![pR0Ps](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pr0ps/32/28045_2.png) [@pR0Ps](https://discuss.python.org/u/pR0Ps)\
**Post date:** [June 6, 2025, 4:07pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/9 "2025-06-06T16:07:33Z")

</div>

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:

```python
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](https://github.com/python/cpython/blob/f00512db20561370faad437853f6ecee0eec4856/Lib/string/ __init__.py#L277-L287) so it’s possible to implement your function like:

```python
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.

---

<div class="post-metadata">

**Author:** ![AA-Turner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/aa-turner/32/4979_2.png) [@AA-Turner](https://discuss.python.org/u/AA-Turner)\
**Post date:** [June 6, 2025, 5:16pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/10 "2025-06-06T17:16:11Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![trey](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/trey/32/20518_2.png) [@trey](https://discuss.python.org/u/trey)\
**Post date:** [June 6, 2025, 5:52pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/11 "2025-06-06T17:52:34Z")

</div>

I hadn’t known about `Formatter.convert_field`.

I found that I needed to use either:

```python
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):

```python
from string import Formatter

convert = classmethod(Formatter.convert_field). __get__ (Formatter)

```

---

<div class="post-metadata">

**Author:** ![gerardw](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gerardw/32/8739_2.png) [@gerardw](https://discuss.python.org/u/gerardw)\
**Post date:** [June 6, 2025, 6:24pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/12 "2025-06-06T18:24:16Z")

</div>

I also [cribbed](https://github.com/Gerardwx/tstring-util/blob/main/src/tstring/interpolation.py) the PEP code, so having an Interpolation method makes sense to me.

```python
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)

```

---

<div class="post-metadata">

**Author:** ![yoavrv](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/yoavrv/32/28295_2.png) [@yoavrv](https://discuss.python.org/u/yoavrv)\
**Post date:** [June 6, 2025, 10:05pm UTC](https://discuss.python.org/t/add-convert-function-to-string-templatelib/94569/13 "2025-06-06T22:05:37Z")

</div>

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

```python
formatted_value = f'{value:{format_spec}}'

```

But this doesn’t work

```python
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)
