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.1.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.1-cp310-cp310-macosx_11_0_arm64.whl (291.8 kB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

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

File metadata

  • Download URL: pled-0.1.1.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.1.tar.gz
Algorithm Hash digest
SHA256 318153fabe7cc2f1f57da169428c870a3ecda4735308bcfef857a637121162b5
MD5 af42bdc14cdfe49e4bbf177c5dc67788
BLAKE2b-256 582c1664d9d8fb09d357c852b0afc9d8623d004b3d96d437f4133e024edfe3c7

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pled-0.1.1-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d629e5207f478c12b62e8d5bcf8aff07516869864bb277679075325f5ffc5b03
MD5 e4cf5b9d2825e11ff7b1794dcebc50e6
BLAKE2b-256 9dd5839d1af7d2bc9ed17cbf4f2f553c91c79419ed754d1edda23ade5b4dc2b8

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