>>> print(f"{d['key'][1]=}")
d['key'][1]='one' >>> print(f"{d['key'][1]=}")
d['key'][1]='one'f-strings's `=` is awesome. I'm overjoyed it was added to Python. I use it all the time.
That said, IceCream does bring more to the table, like:
- Stynax highlighting.
- Pretty prints data structures.
- Returns its value for nesting.
- Integrates with logging via `ic.configureOutput()`.
etcI hope that helps!
One question: in most cases, I get an output like `ic| tmp.py:9 in nonesuch() at 03:23:52.479` What is the "at 03:23:52.479", and how do I turn it off? Looks like it's some sort of timer function, but I don't see it in the readme. Python3.7 on Linux.
In my experience the conditions on the ground are different though. Tooling availability is inconsistent, which is why such approaches don’t always work. Reverse debugging isn’t available on all platforms/languages. Debuggers (or at least the C/C++ ones) are slow in how they inject instrumentation code to evaluate - rather than compiling expressions into native codes to conditionally trap, they seem to trap unconditionally and evaluate the conditional using their introspection language (there are valid reasons why it’s done this way but it has serious performance impacts). In compiled languages, the evaluation of the expression can be difficult/impossible to write because of this (not enough type information available, validation is late binding meaning mistakes are extra expensive, etc.
At the end of the day, when your tools fail you, print debugging is easier to use to accomplish the task rather. Getting tooling to work effectively is more time consuming and frequently not possible.
That said, sometimes you don't want to interrupt execution and see your results in real time.
>>> print(f"{d['key'][1] = }")
d['key'][1] = 'one'
It works with any expression you like, not just variables: >>> print(f'{np.sin(np.pi/4.) = }')
np.sin(np.pi/4.) = 0.7071067811865475Also assignment expressions? :)
>>> print(f"{(yes:='yes')=}")
(yes:='yes')='yes' >>> print(f"{(yes:='yes') = } and now {yes = }")
(yes:='yes') = 'yes' and now yes = 'yes' julia> x = 4711
4711
julia> @show x
x = 4711
4711It's convenient, don't get me wrong, but it's not exactly groundsbreaking. Unlike breakpoint[1].
[0] https://doc.rust-lang.org/std/macro.dbg.html
[1] https://docs.python.org/3/library/functions.html#breakpoint it's not groundsbreaking on its own but the ability to very easily hook into it and replace the breakpoint hook by an arbitrary callable? Super useful.
PYTHONBREAKPOINT=print python
and all your breakpoint() calls will be prints.Which is not super useful, however you can use
PYTHONBREAKPOINT=icecream.ic
and now you get the advantages of TFA without having to import anything anywhere.The only annoyance is that the default hook takes no arguments, so you can't trivially switch between the default and custom implementations, you may need to modify the source depending on the breakpoint hook you're using.
__init__.py
os.environ.setdefault('PYTHONBREAKPOINT', 'tests.utils.raise_or_debug')
tests.utils.raise_or_debug def raise_or_debug(msg=''):
if settings.BREAKPOINT_ON_ERROR:
extype, value, tb = sys.exc_info()
if getattr(sys, 'last_traceback', None):
pdb.pm()
elif tb:
pdb.post_mortem(tb)
else:
pdb.set_trace()
elif msg:
raise AssertionError(msg)
else:
pdb.set_trace()
The reason is so we can leave breakpoints in the tests, and then depending on context run the tests in a context where debug is possible, or simply raise the failure again.Selenium can be very flaky, so this has really sped up iteration and improvements. Standard functions with retries, and smart assertions and timeouts can then be fixed and resumed in dev - and give a good message on fail (for example headless chrome runner on CI).
We have manually overwritten the breakpoint handler to a custom handler that in default mode does this:
- `breakpoint()` calls trigger `pdb.set_trace()` as per usual
- `breakpoint(<str>)` calls trigger AssertionError to fail tests
When `BREAKPOINT_ON_ERROR=TRUE` is turned on:
- `breakpoint()` calls trigger `pdb.set_trace()` as per usual
- `breakpoint(<str>)` call `pdb.set_trace()` so you can try to manually work out how the code is functioning
- if you call `breakpoint(<str>)` from an `except` block it will open the debugger at the position of the exception
Usage example: try:
WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.ID, 'desktop-apply')))
except TimeoutException:
breakpoint('Was not able to click apply button.')icecream, the subject of this post, returns its parameter unmodified without multiple evaluations.
The fine article
> icecream, the subject of this post, returns its parameter unmodified without multiple evaluations.
That would be my point, yes.
python3 myscript.py --enableFstringDebugging=true
So that I could get rid of lots of conditional statements, e.g.: if debug:
print (f"some useful debugging info")