Now Reading
Tracing Python | Die wunderbare Welt von Isotopp

Tracing Python | Die wunderbare Welt von Isotopp

2023-05-24 01:45:02

Primarily based on a dialogue on IRC and Mastodon: “How can I get entry to the return values of my (Python-) applications features?”
And extra typically, how can I hint perform execution in Python, exhibiting perform parameters and return values?

In fact, you’ll be able to all the time merely activate this within the PyCharm debugger:

PyCharm, Debug Window, Gear Icon, “Present Return Values”

You can too implement such a factor from first ideas, in Python:

import functools

def args_to_str(*args):
    return ", ".be part of(map(str, args))

def kwargs_to_str(**kwargs):
    ret = ""
    for ok, v in kwargs.objects():
        ret += f"{ok}={v},"

    return ret[:-1]

def logging(func):
    func.__indent__ = 0

    def wrapper_logging(*args, **kwargs):
        func_indent = " " * func.__indent__
        func.__indent__ += 2

        func_name = func.__qualname__
        func_args = args_to_str(*args)
        func_kwargs = kwargs_to_str(**kwargs)

        print(f"{func_indent} -> Enter: {func_name}({func_args}", finish="")
        if func_kwargs != "":
            print(f", {func_kwargs}", finish="")

        end result = func(*args, **kwargs)

        print(f"{func_indent} <- Depart: {func_name}({end result})")
        return end result

    return wrapper_logging

def fac(n: int) -> int:
    if n == 1:
        return 1
        return n * fac(n - 1)

if __name__ == '__main__':
    end result = fac(3)
    print(f"{end result=}")

This makes use of @functools.wraps() to outline a decorator

, @logging.
It additionally leverages the actual fact, that something, together with a callable, can have properties, which we’re utilizing to keep up an indent depend in func.__indent__.
That is initialized to 0, after which incremented by 2 for every name within the name stack. Unwinding the decision stack resets the counter, so we don’t have to try this manually.

Now we have two helper features to show the features *args and **kwargs into correct strings, and we entry func.__qualname__ to get the features name

We’re utilizing __qualname__ to deal with internal features correctly right here, even when that’s generally creating much less readable output.

Working the code leads to

Output from the Python program above reveals perform calls and outcomes.

The autologging

package deal makes this easier, even when it lacks the good indentation.

Our code turns into a lot shorter:

import logging
import sys
from autologging import logged, traced, TRACE


class Fac:
    def __init__(self):

    def fac(self, n: int) -> int:

        if n == 1:
            return 1
            return n * self.fac(n - 1)

if __name__ == '__main__':
    f = Fac()

We get to make use of a brand new loglevel TRACE, and import @logged and @traced decorators from the package deal.
After organising a log channel with correct formatting, we are able to mark features with the decorator, get their execution traced and might merely log.

The output:

Output of our program utilizing the autologging package deal.

At this level Andreas Thienemann talked about icecream, which is exceptional snug.
Variations of icecream exist for a lot of programming languages, so this isn’t Python particular.

from icecream import ic


class Fac:
    def __init__(self):

    def fac(self, n: int) -> int:
        if n == 1:
            return ic(1)
            return ic(n * self.fac(n - 1))

if __name__ == '__main__':
    f = Fac()

Icecream outlined a perform ic(), which you’ll be able to name with parameters (ic(n), ic(1), and ic(n * self.fac(n - 1))), or with out (ic()).
The perform will print its parameters, similar to a debug print, or when known as with out parameters, simply log its execution together with supply file and line.
Choices to prettify the output exist.

The package deal additionally supplies a perform set up(), which is able to merely make ic() out there as a builtin:

from icecream import ic
set up()
# this principally does builtin.ic = ic

# ic() is now out there in all of your modules 
# with out having to incorporate it in each submodule.

The output appears like this:

Working our code with icecream produces this output, properly colorized and fairly printed.
We enabled includeContext=True, so we additionally get file names and line numbers.

In fact, this isn’t the tip of it.
The package deal snoop

supplies tracing of 1 or all features,
comes with its personal model of icecream known as pp (PrettyPrint).
Utilizing pp.deep(), it’s going to present expression analysis step-by-step.

from snoop import pp
pp.deep(lambda: x + 1 + max(y + 2, y + 3))


12:34:56.78 LOG:
12:34:56.78 ............ x = 1
12:34:56.78 ........ x + 1 = 2
12:34:56.78 ................ y = 2
12:34:56.78 ............ y + 2 = 4
12:34:56.78 ................ y = 2
12:34:56.78 ............ y + 3 = 5
12:34:56.78 ........ max(y + 2, y + 3) = 5
12:34:56.78 .... x + 1 + max(y + 2, y + 3) = 7

Snoop understands Python:
It reveals not simply file numbers, however precise code name context, parameters.
It handles debugging Decorators, can log Exceptions correctly, and might log name stacks of a configurable depth.

Try the examples within the hyperlink above.


is a continuation of snoop to the intense, and built-in with it.
It captures the identical knowledge as snoop, however logs it to a SQLite file in your $HOME.
It can then let you begin a flask-based Webserver on port 7777 to guage the hint file and replay it step-by-step.

@spy lets you run snoop and birdseye in tandem.

from birdseye import eye
from snoop import snoop, spy

def fac(n: int) -> int:
    if n <= 1:
        return 1
        return n * fac(n-1)

if __name__ == '__main__':
    end result = fac(3)
    print(f"{end result=}")

This can instrument the code to run with snoop, and in addition log into the database file.

The hint is fairly:

Output of snoop working on our check perform. We instrumented with @spy, which may even create a hint file.

Working the Birdseye decoder is straightforward: python -m birdeye will begin it on the default port, 7777.

Beginning the birdseye internet server to serve hint recordsdata. It’s certain to localhost, and listens by default on Port 7777.

The rendered hint appears like this:

See Also

birdseye webserver exhibiting a hint. You may click on via this system execution.

The package deal peepshow

then gives a full scale commandline debugger, which lets you hint program execution, set breakpoints and so forth.

Sadly, it has been deserted (the final commit was in November 2020), and doesn’t assist fashionable Python.

What for those who can not entry the host your code runs on, for instance, as a result of that host is variable and lots of, since you are working in Kubernetes?
On this case, Honeycomb and different OpenTelemetry packages have you ever coated.
They do the identical logging as above, however package deal knowledge in OpenTelemetry Spans, and ship these over the community.

The previous, unique Honeycomb Beeline for Python is documented right here: Python Beeline

The present OTel appropriate implementation is right here: OTel replacement


A medium article dialogue the identical decorator utilization as we do right here, however within the context of OpenTelemetry, is obtainable:
Using Decorators to Instrument Python Code With OpenTelemetry Traces


The same resolution exists for the C programming language since 1987, within the type of the Fred Fish Debug Macros.

See the utilization in MySQL

and seize the source


That is older than time itself, and C is a bit restricted in comparison with Python, but it surely principally does the identical factor, in 36 years previous code.

The code for these samples has been made avialable on GitHub


Source Link

What's Your Reaction?
In Love
Not Sure
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top