-
-
Notifications
You must be signed in to change notification settings - Fork 29.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
f-strings: Add a !d conversion for ease of debugging #80955
Comments
I originally propsed this here: https://mail.python.org/pipermail/python-ideas/2018-October/053956.html, and there has also been some discussion on discourse. The proposal is to add a !d "type conversion" to f-strings, to make for simpler "print-based debugging". The idea is that !d would be like !r, etc., except that the resulting string would be:
So this code: Would set result to 's="A string"'. Note that the text of the expression is exactly what's between the Would set result to 's ="A string"'. Note the space before the equal sign. I can't decide if I'm going to allow a format specifier. If I do, it would apply to the entire resulting string. So: Would set result to 's="A string"'. But I might reserve format specs for further use, perhaps to apply to the value of the expression instead of the resulting string. Of course this is of most value for more complex expressions. This: Would set result to 'v*9+15=42'. I'd expect this mostly to be used with print() or logging, but it's a general f-string feature. I have a patch almost ready. |
On Thu, May 02, 2019 at 11:40:45AM +0000, Eric V. Smith wrote:
I don't want to see this added as a special case to f-strings. I think
Please don't limit this useful feature to f-strings. "Debugging strings" |
I support a general mechanism. But I think that if it's much more than the 2 characters that this would take, then I don't want to use it for f-strings. The whole point here (as with all f-string features) is a concise way to get the string you're after. But if you have some ideas, I'm willing to entertain them. I want this feature to land in 3.8, for which we're rapidly running out of time. |
for reference, the discourse thread: https://discuss.python.org/t/f-string-debug-conversion/99/14 |
Steven: We shouldn't block this immediately useful feature from going in for f-strings on waiting for some much broader first class access to expressions feature. !d would be practical today. |
hallway conversation with Eric: neither of us have immediately good thoughts on a spelling other than !d for this. !! or != seem esoteric and/or unparseable, using a capital letter like !D could be new and odd and might be useful to differentiate types of debug output (!d vs !D, etc) so lets not start with caps. thus I'm not suggesting any alternative bikeshed color. |
!d sounds good and makes teaching easier. Adding additional symbol sort of clutters the experience in f'{name!!}'. I think it will be a good thing to have it in 3.8 since it serves one of the common things in debugging f'name = {name}' and perhaps it could be expanded out of f-strings after feedback. Rust also introduced similar feature with https://doc.rust-lang.org/beta/std/macro.dbg.html and received very positive feedback . This will be a very exciting thing to look forward in 3.8 :) |
To implement converting printf-style string formatting into f-string expressions (see bpo-28307) I need to add new convertors: 'd': int(x) where x is a number, used for %d, %u, %i They may be private, only exposed in the AST between optimizer and code generator. But they can also be supported by Python grammar and str.format() if there is a use case for this. If 'd' be used for other purposes, I will need to find other character for converting a number to integer (with possible truncation). Any suggestions? |
regarding bpo-28307 - It is not always correct to convert a %d %u %i used to render v into f'{int(v)}'. That'd lose the TypeError when v is not an integer. If you are looking at making internal private conversions for the purposes of that issue, I think they should be things that'll never conflict with a normal public API token that might be used in the future. (ie: don't reserve 'd' 'i' or 'f' for internal only use - use special values) i'm being vague on purpose here as i don't know how f-strings are parsed and tokenized. |
+1 from me. It's something I'd find useful, and it's a natural extension of the f-string syntax.
The only useful interpretation IMO would be for {expr!d:fmt} to expand to expr={expr:fmt}. If you're not willing to include that in the initial implementation, I'd rather see :fmt reserved for now, with the intention that it's implemented like this at a later date. Having :fmt apply to the whole string including the "expr=" bit would be basically useless to me. For a motivating example, consider f"{datetime.now()!d:%Y-%m-%d}", which is something I could easily imagine using. Steven D'Aprano:
I have no problem with something like this, but I don't think it precludes the proposed f-string extension. The use cases are sufficiently different that I'd expect the two features to live happily together - there's no need to block the f-string extension for a proposal like this. |
Re: format specs and what they apply to. The problem is that you want f"{expr!d}" to expand to f"expr={repr(expr)}". And I think you want that because it's the most useful behavior for strings. Without the repr it's not very useful for strings. But you want to have the format spec apply to the expression, not the repr of the expression. So maybe f"{expr!d:spec}" should expand to f"expr={repr(format(expr, spec))}" So Do we want the repr of the resulting string here (the single quotes around 2019-05-02)? I think probably no (think float formatting). So the question is: how do you get repr by default, but allow the format spec? The only thing I've come up with is:
I think this is the most useful version. But is it too complex to explain? |
Agreed, this is the most useful version. Not only do I not think it's If asked, I'd explain it as:
using spec>". If ":spec" is omitted, repr() is used. That seems simple enough to me - the key is that we're just saying "if |
The most recent version of the patch implements the conditional repr/format behavior. I'm pretty happy with it. Other than docs, I think this is done. I'm going to discuss it with a few more people at PyCon, then commit it. There's a slight optimization I'm considering (pre-append the '=' to the expression text at compile time, instead of at runtime), but I'll do that later, if ever. |
This conversion is very special. For all other conversions {value:!c:format_spec} is equivalent to format(conv(value), format_spec), but not this conversion. It is also specific for f-strings, and is not particularly useful in format strings. Would not be better to use more special syntax for it? For example "!=" or "!!"? Letters can be reserved for future "normal" conversions. It may be we even allowed to register new converters by name. |
!= would be my preference, but it can't work. f'{0!=1}' is already legal. I'm not crazy about !!. I think that will be too confusing. |
I hear you, and after giving it much more thought I agree. |
Serhiy's point about how special this is is very valid. It's so special that I can't figure out where to document it. f-strings are really only documented in Doc/reference/lexical_analysis.rst, and !d details, especially the format/repr distinction, seems like too much information for that document. But I could be wrong about that. And since this feature can't be used in str.format(), it can't be documented in Doc/library/string.rst. In fact, it should contain a note about !d not applying. Rather than make the documentation worse, I think I'll just open a separate issue for it when I commit this. Maybe someone else will have some ideas. |
And for those who *really* want to be able to apply a format spec to the result of the entire !d expression, you can always use nested f-strings: >>> for x in [3.1415, 0.5772156649, 100]:
... print(f'{f"{x!d:.1f}":*^20}')
...
*******x=3.1********
*******x=0.6********
******x=100.0******* Not that I recommend this, but at least it's possible. |
See bpo-36817 for an alternative proposal. |
After discussing this with Guido, we're going to go with a tweaked version of the '=' version of this in bpo-36817. So, I'm going to close this and continue the discussion there. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: