Filename and line number in f-string debugging output

(Karthikeyan Singaravelan) #1

I have been playing around with f-string debugging feature. There are some cases where I print a certain variable in different parts of a program and I thought adding filename:lineno would help. This was mostly inspired by Rust dbg! macro : https://doc.rust-lang.org/beta/std/macro.dbg.html

dbg!(a * 2) // [src/main.rs:2] a * 2 = 4
# prog.py
name = "karthikeyan"
age = 25

print(f"{name = }")
print(f"{age = }")

age += 1

print(f"{age = }")
print(f"{name = :-^20}")
print(f"{str.capitalize(name) = :-^20}")
➜  cpython git:(master) ✗ ./python.exe prog.py
name = 'karthikeyan'
age = 25
age = 26
name = ----karthikeyan-----
str.capitalize(name) = ----Karthikeyan-----

Adding filename and lineno support in my branch by prepending it to expr_text in Python/ast.c .

➜  cpython git:(fstring-filename) ✗ ./python.exe prog.py
[prog.py:4] name = 'karthikeyan'
[prog.py:5] age = 25
[prog.py:9] age = 26
[prog.py:10] name = ----karthikeyan-----
[prog.py:11] str.capitalize(name) = ----Karthikeyan-----

At this point using a logger would be recommended. The issue and python-ideas discussion was focused more on keeping it as simple as possible with just repr but having it in built since it’s for debugging looks nice to me. I am okay with it being rejected but personally I think it’s nice for debugging.

@ericvsmith @larry Thoughts?

(Paul Moore) #2

-1 on this being included by default. I have plenty of use cases that wouldn’t be described as debugging (in fact, they are probably 99-100% of my use cases!) and for those cases, adding the location data would be wrong.

I don’t mind if it’s added as an opt-in feature that people have to request to get it - but personally I doubt I’d ever use it, so I’m fine with not having it.

(Tzu-ping Chung) #3

Like… import logging?

1 Like
(Paul Moore) #4

Precisely.

import logging

l = logging.Logger("")
h = logging.StreamHandler()
f = logging.Formatter(fmt="[{filename}:{lineno}] {msg}", style="{")
h.setFormatter(f)
l.addHandler(h)
l.info("Hello")

Output:

>py .\t.py
[t.py:8] Hello

(Nobody ever said the logging module was terse, but all of that could be put into a utility module).

(Eric V. Smith) #5

I think the file name and line number are too much magic (says the guy who just added “=“!).

If I were really going to do this (which I am definitely not suggesting), I would add something that just expands to the file name, and something else that just expands to the line number. To add it to “=“, even optionally, seems like you’re conflating things.

1 Like
(Serhiy Storchaka) #6

This may be implemented as combination of f-string debugging and special function which prints the filename and line number of the caller (using sys._getframe()).

dbg(f"{name = }")

We now have the breakpoint() builtin, so may add also a builting for debugging print.

1 Like
(Serhiy Storchaka) #7

Other option – use the trace module with the tracer which prints the filename and the line number for lines containing print, f"{...=}" or special comment.

(Karthikeyan Singaravelan) #8

This looks like a better idea than adding it to =. I must admit my initial proposal was just to emulate dbg! macro with f-string but as per the arguments I think there are broader use cases with f-string syntax where filename and line number could be a noise.

Having it as a separate function seems good where users can explicitly opt in for it instead of print() to use it for debugging. There are pypi packages like icecream [0] that implement this type of utility function and could be a good one to have in core.

[0] https://github.com/gruns/icecream