Enhanced String Formatting for Truncation with Ellipses

String Formatting with truncating and ellipses

A simple way to format a string that exceeds a certain length by truncating it and adding ellipses.

Example

long_word = Truncate("Supercalifragilisticexpialidocious")
formatted_name = f"A long word: {long_word:t10}"
print(formatted_name)  # Output: "A long word: Supercalif…"

We have good methods to format and round decimals already. (PEP 3101, PEP 378)

Formatting numbers

print(f"Here's a number: {10_000/7:,.2f}")  #Output: "Here's a number: 1,428.57"

Why not have a similar feature for strings? I’ve often run into scenarios where I’m dealing with dynamic data such as names of people or companies that I am printing out to a component or whatever and end up having to make a function that does this—checks if the string exceeds a certain length and, if so, truncates it and adds ellipses (…). It’d be nice if there were a simple string function of string formatting method of doing this inherently.

Demonstrative Example Code

Here’s a demonstration of a class implementing this:

class Truncate:
    def __init__(self, string):
        self.string = string

    def __format__(self, format_spec):
        max_length = int(format_spec[1:]) if format_spec.startswith('t') else None
        if max_length is not None and len(self.string) > max_length:
            return self.string[:max_length] + '…'
        return self.string

# Example usage
long_word = Truncate("Supercalifragilisticexpialidocious")
formatted_name = f"A long word: {long_word:t10}"
print(formatted_name)  # Output: "A long word: Supercalif…"

This example also accepts negative indexing in stride with python’s ability to do such.

formatted_name = f"A long word: {long_word:t-20}"
print(formatted_name)  # Output: "A long word: Supercalifragi…"

The details of exactly how this would work are yet to be determined. But this would be a useful feature and would improve Python’s string formatting usefulness.

Extras

While we’re discussing it, there might also be a use case for truncating from the left side and adding ellipses there instead of the right; e.g.,

formatted_name = f"A long word: {long_word:l5}"
print(formatted_name)  # Output: "A long word: …califragilisticexpialidocious"
1 Like

Does textwrap.shorten suite your needs?

2 Likes

Unfortunately, no. But thanks for the reference because I didn’t know about this library.

While it has some usefulness in this regard, it breaks the text on whitespace and cuts off any words that exceed the width. So, to use the example from my post:

import textwrap

long_word = "Supercalifragilisticexpialidocious"
textwrap.shorten(long_word, width=5, placeholder='…')
# Output: '…'

shorten just removes the whole word because it exceeds the given width and then adds the placeholder.

To further illustrate this:

text = "".join(list("abcdefg"))
textwrap.shorten(text, width=5, placeholder='…')
# Outputs: '…'

But if there are spaces between them:

text = " ".join(list("abcdefg"))
textwrap.shorten(text, width=5, placeholder='…')
# Outputs: 'a b…'

And honestly, this result sort of confuses me because I would have expected 'a b c…'. But anyway, this library doesn’t work as a solution because it cuts on whitespace between words instead of just truncating the string.

Width includes the placeholder.

Chopping the data short and adding a is relatively simple. But in my experience you nearly always want something more sophisticated. textwrap is slightly better, but as you say breaking on words isn’t always ideal. To go much further than this you rapidly get into complexities like word splitting (and hyphenation) algorithms, which are far too specialised for the stdlib.

I think the simple answer to your original question is that while a basic form of truncation is easy enough to add, it’s also easy enough to write for yourself, and it’s not useful enough to warrant a standard format specifier.

For an even simpler version than yours:

def trunc(s, max_len=None):
    if max_len is not None and len(s) > max_len:
        return s[:max_len] + "…"
    return s

long_word = "Supercalifragilisticexpialidocious"
formatted_name = f"A long word: {trunc(long_word, 10)}"

No need for a special class with a format method.

Thank you very much for the feedback!

Although an implementation is fairly simple, I’d argue that the usefulness and simplistic implementation of this feature would be similar to other format specifiers like <, >, =, or ^ (PEP 3101 | Standard Format Specifiers).

Personally, I’ve run into cases needing this several times in the last year—more frequently than needing the other specifiers mentioned.

I see your point here. That’s valid. I still think it’d be worth further consideration since this is a use case with precedent.

Anecdotal: this feature would have been more useful to me in the last year than the other specifiers that already exist. That’s just my experience but it may be worth more consideration.

Thanks again for the feedback.

2 Likes