Skip to main content

print-less debugging

Project description

pled – print-less debugging

pled is a library for debugging Python code so you don't have to paste print() everywhere.

It features no-code instrumentation on your codebase.

Use cases

  • When you are writing a new function/module and want to trace its execution without print-ing or logging everything yourself.
  • When you see a downstream exception but need to understand upstream code that may have caused it.
  • When you need temporary state tracking which you will remove later.
  • When you want to get a visual representation of the execution flow of your code.

Getting started

  1. Install pled as a library

    # With poetry
    poetry add --dev pled
    
    # With uv
    uv add --dev pled
    
  2. Use an executor to execute code and collect traces into a tracer

    # Tracing function `bar(*args, **kwargs)` inside module `foo`
    
    from pled import Executor
    
    executor = Executor("foo")
    tracer = executor.execute_function("bar", *args, **kwargs)
    
  3. Inspect the traces in a tracer

    # Print the traces
    print(tracer.format_traces())
    
    # Or dump into stringified JSON
    json_traces = tracer.dump_json()
    
    # Or generate an HTML report (needs Internet connection as it uses mermaid.js from CDN)
    tracer.dump_report_file("report.html")
    

A sample output involving a your_project.main module and a your_project.dep module:

Click to expand JSON
[
  {
    "type": "FunctionEntry",
    "timestamp": 0.010341791,
    "function_name": "your_project.main.entry",
    "args": [["flag", "2"]]
  },
  {
    "type": "Branch",
    "timestamp": 0.010348791,
    "function_name": "your_project.main.entry",
    "branch_type": "if",
    "condition_expr": "flag > 0",
    "evaluated_values": [["flag", "2"]],
    "condition_result": true
  },
  {
    "type": "FunctionEntry",
    "timestamp": 0.010350833,
    "function_name": "your_project.dep.dep_func",
    "args": [["limit", "2"]]
  },
  {
    "type": "Branch",
    "timestamp": 0.01035325,
    "function_name": "your_project.dep.dep_func",
    "branch_type": "while",
    "condition_expr": "i < limit",
    "evaluated_values": [
      ["i", "0"],
      ["limit", "2"]
    ],
    "condition_result": true
  },
  {
    "type": "Branch",
    "timestamp": 0.01035575,
    "function_name": "your_project.dep.dep_func",
    "branch_type": "while",
    "condition_expr": "i < limit",
    "evaluated_values": [
      ["i", "1"],
      ["limit", "2"]
    ],
    "condition_result": true
  },
  {
    "type": "Branch",
    "timestamp": 0.010358625,
    "function_name": "your_project.dep.dep_func",
    "branch_type": "while",
    "condition_expr": "i < limit",
    "evaluated_values": [
      ["i", "2"],
      ["limit", "2"]
    ],
    "condition_result": false
  },
  {
    "type": "FunctionExit",
    "timestamp": 0.010360333,
    "function_name": "your_project.dep.dep_func",
    "return_value": "3"
  },
  {
    "type": "FunctionExit",
    "timestamp": 0.010429666,
    "function_name": "your_project.main.entry",
    "return_value": null
  }
]
Click to see a screenshot of the HTML report

Report

Types of traces

pled traces the following types of events:

  • FunctionEntry - function entry
    • function_name - fully qualified function name
    • args - the full argument list in name-value pairs
    • timestamp - timestamp of the event
  • FunctionExit - function exit
    • function_name - fully qualified function name
    • return_value - return value
    • timestamp - timestamp of the event
  • Branch - branching
    • function_name - fully qualified function name where the branch is located
    • branch_type - branch type, can be if, while, or except
    • condition_expr - condition expression
    • evaluated_values - evaluated values
    • condition_result - condition result
    • timestamp - timestamp of the event
  • Await - await expression
    • function_name - fully qualified function name where the await is located
    • await_expr - await expression
    • await_value - value being awaited
    • await_result - result of the await
    • timestamp - timestamp of the event
  • Yield - yield expression
    • function_name - fully qualified function name where the yield is located
    • yield_value - value being yielded
    • timestamp - timestamp of the event
  • YieldResume - yield resumption
    • function_name - fully qualified function name where the yield is located
    • send_value - value being sent to the generator
    • timestamp - timestamp of the event

Note: timestamp is a float representing the number of seconds since the start of the execution.

Examples

Tracing a module

Given this module:

# Module: your_project.main

def just_print():
    print("hello")

just_print()  # <-- this module does some work directly

You can trace the execution of this module by running:

from pled import Executor

executor = Executor("your_project.main")
tracer = executor.execute_module()
print(tracer.format_traces())

Tracing a function

Given this module without root-level execution:

# Module: your_project.add

def just_add(a: int, b: int) -> int:
    return a + b

You can trace the execution of a function by running:

from pled import Executor

executor = Executor("your_project.add")
tracer = executor.execute_function("just_add", 1, 2)
print(tracer.format_traces())

Tracing multiple modules

You can trace multiple modules by passing a list of package or module names to the Executor constructor.

Suppose you want to trace everything inside your_project package when executing your_project.add.just_add function.

from pled import Executor

executor = Executor("your_project.add", includes=["your_project"])
tracer = executor.execute_function("just_add", 1, 2)
print(tracer.format_traces())

Tracing a function with background execution

You can run the executor in the background by setting the background option to True.

Given this module:

# Module: your_project.event_loop

def infinite_yield():
    import time

    while True:
        time.sleep(0.5)
        yield 1

def loop():
    for _ in infinite_yield():
        pass

You can run loop() in the background with:

from pled import Executor

executor = Executor("your_project.event_loop", background=True)
tracer = executor.execute_function("loop")
while True:
    time.sleep(1)
    print(tracer.format_traces())

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pled-0.1.2.tar.gz (144.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pled-0.1.2-cp310-cp310-macosx_11_0_arm64.whl (292.0 kB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file pled-0.1.2.tar.gz.

File metadata

  • Download URL: pled-0.1.2.tar.gz
  • Upload date:
  • Size: 144.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.8.1

File hashes

Hashes for pled-0.1.2.tar.gz
Algorithm Hash digest
SHA256 77c2aca813b4803a2f48dbd3d1287e18e32e58220733cf049457fcd6b601880e
MD5 ec749295723174f154f48e8b364bf52e
BLAKE2b-256 88e44256200cc67ceca7cda6dddcd335266a2c1f53734a284d28e228c493401f

See more details on using hashes here.

File details

Details for the file pled-0.1.2-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pled-0.1.2-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e891355b9c3ca8fa567f6be9c9227b7b5eca471ca193f351c2e884b9778fc1c9
MD5 4875c46aa8b77c68ede96f4ce583dcee
BLAKE2b-256 67dbed61727a289af055855a3519e16627d6891a0ac24c0056bc76d4eb84fa75

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page