Str.format for type

My error messages most often simply print out the code that failed:

a = 1
if not isinstance(a, str | tuple):
    raise TypeError(f'not isinstance({a=}, {str | tuple})')
# 'not isinstance(a=1, str | tuple)'

The issue is that f'{a=}' doesn’t completely describe the value. If both: repr and type were printed it would add much to clarity of the message.

So what I would like to have is:

'not isinstance((int)a=1, str | tuple)'

Then I could write:

if not isinstance(a, str | tuple):
    raise TypeError(f'not isinstance(({type(a).__name__}){a=}, {str | tuple})')

But I ended up writing so many of these that I could really use a more convenient way.

So, I would like to inquire if anyone else would find string formatting for type name useful.

The best I came up with is:

a = 1
print('{a!t}')    # int

This way, full message would look like:

if not isinstance(a, str | tuple):
    raise TypeError(f'not isinstance(({a!t}){a=}, {str | tuple})')

Would anyone else find this convenient?

2 Likes

If you find yourself requiring to include the repr and type of an object in an error message, what is stopping you from using a function like below?

from typing import Any


def describer(obj: Any) -> str:
  return f'{repr(obj)} of type {type(obj).__name__}'

a = 1 
if not isinstance(a, str | tuple):
  print(f'{describer(a)} is not a string or tuple')

Python already has enough special symbols for strings. This proposal would add nothing of value and would just confuse users with new syntax.

2 Likes

Personally, I just keep a couple of functions around: r(obj) which
returns f'{type(obj).__name__}:{obj!r}' and a similar s(obj) which
uses str instead of repr. Then I can just use {r(obj)} as desired.
I generally don’t care about the variable name - that will often be
obvious in the code, and I can always mention it if it seems useful.

So I just go:

 from cs.lex import r, s

and get on with my life.

I would not be in favour of a bunch of special purpose format thingies.
There’s too many tiny things we’d all bikeshed about and it is pretty
easy to roll things which get you 90% of the way there.

3 Likes

It is a suggestion if more people are in need of this.

There is one issue with external function.

One can not use f"{arg=}", so extra argument is needed to input the name.

{obj!?} has only 3 options now. If there was something useful for a lot of people, I don’t see why a sensible addition can not be considered.

Do not use a feature purposed for debugging to format a user facing error message.

5 Likes

What are the reasons for not doing that? Are there any risks?

You expose an implementation detail (variable name). After refactoring the behavior of your program will change.

1 Like

I think it is a thin line here.

E.g. function argument name is the name of the variable, but it is reported in error messages.

Also, refactoring will change the variable name and error message will change with it, I don’t see what is wrong with it as long as it is a sensible variable name to report.

Also, the end user of software will never see these messages anyway. This is for low level code, which ideally should be as performant as possible.

Thus, writing complicated wrappers for these is not an option as it is often used in frameworks that extensively make use of error catching.

But this is my case and I can always make optimal compromise with existing features. And it is going to be ok. But with this it could be simpler and faster. So just thought maybe it could be of help to others too.

I realise now that it is a bit much - 1 function call and 1 method call. Nevertheless, I think this might be in at least top 5 of most used constructs in string formatting.

E.g. all __repr__ methods:

def __repr__(self):
    return f'{type(self).__name__}(...)'
# TO->
def __repr__(self):
    return f'{self:t}(...)'

Also see Enhance type name formatting when raising an exception: add %T format in C, and add type.__fullyqualname__

Much thanks, so what I have proposed is implemented and merged into main.

And apologies. This is lack of research on my part.

1 Like

Is it merged? I thought that part of the proposal was rejected.

No, it seems you are right. It is C only.

1 Like

Of course one can write {type(var).__name__}:{var} or use a helper, but take a look from the opposite perspective. People don’t do that every time they should. Introducing any short and easy way for printing the type could help to create “the one and obvious way” to print an offending variable in all messages like this:

TypeError(f"Expected an integer, but got {arg}")
1 Like

I really like this proposal. The type(...).__name__ is used a lot. According to GitHub search results, it’s used 1.23 times more than !r and 8.14 times more than !a, so I think !t is worth it.

The code search results show many different ways to use type(...).__name__. It might be worth checking them out.

In addition, I think we should also implement !tr and !ta, which would be repr(type(...).__name__) and ascii(type(...).__name__) respectively.

1 Like

Your a! search has many false positives:
This might be more accurate

Isn’t writing f"{type(var).__name__}:{var}" or using a helper function already obvious though? I think most IDEs would allow developers to infer that the dunder name attribute is the name of a type. Even if the attribute wasn’t too intuitive, couldn’t one also write `f"{var=} of {type(var}“, which is equivalent to f”{var=} of <class ‘{type(a).name}’>". If some developers didn’t see the benefits of using the obvious solutions, they wouldn’t use the less obvious solution that is the proposed syntax. I also think that the developers who found they needed such solutions would find using functions and/or f-string expressions, the pre-existing solutions, to be more obvious than using some new specialized syntax.

@GotoRoto Isn’t f"{repr(...)}" also quite obvious? Your argument seems to be more of a questioning of the ! syntax itself.

I suppose that expression is also another obvious, socially established option. You’re correct in saying that my argument questions the ! syntax. The expression also strengthens my argument, which is that the proposed ! syntax currently doesn’t have a lot of usecases because they’re already taken up by the more obvious, socially established solutions.