Skip to main content

An alternate Programmatic Interface for tracing.

Project description

pufftrc Package

last change 10-Feb-2022

An alternate method to interface to tracing.

A different approach to accomplish what is in the standard library documentation as:

Debugging and profiling -> trace -> Programmatic Interface.

Author: Daniel L. Bates (dan.bates)

The pypi module pufftracer

import pufftracer.pufftrc as puff

The singleton class Puff

A singleton class "Puff" to provide interfaces to the code tracing magic in Python. These interfaces to filter tracing to a list of function names. And/Or to show only calls and returns. Or a maximum call depth. To focus on one module at a time, and omit tracing outside that module. A Means of turning the trace On, Off, and resuming it.

A means of communicating a predicate function to Puff to evaluate at every line, and stopping via an assert False when this function triggers by returning True.

A means of providing lists of locals, nonlocals and global variables to be displayed every time they are modified.

A trace that calls attention to any non-sequential change in line number.

A trace object of class Puff has these interfaces:

Trc = puff.Puff()
Trc.focus(module name or module filename)
Trc.On(nest=100, lines=None, funcs=None, callsonly=False)
Trc.Off()
Trc.Resume()
Trc.StopWhen(condition_function)
Trc.Watch(lcls=IsNot, globs=IsNot, nonlcls=IsNot) where IsNot is an internal None

This is Puff the magic Debugging Dragon.

Of Course an example/testing frame Honalee.py is provided.

Where else can a Magic Dragon live than in Honalee.

This is very much through the looking glass.

The Trc.focus member.

Usage pufftrc.Puff().focus(__file__)
or
focus(module.__file__)

Focus the trace (allow the trace) only on one module at a time and that module cannot be pufftrc.

The Trc.On member.

Trc.On(nest=100, lines=None, funcs=None, callsonly=False)

nest is an int which is the maximum level of call nesting to print the trace for. A value of -1 indidcates no limit for call nesting.

lines can be an int representing the single line number to be traced. lines can also be a list of two ints representing a range of line numbers. Line numbers are the most transiant measure to control the trace, but they are available.

funcs is either a function reference or the function name as a string, or a list of function references, or a list of function names as strings.

callsonly is a boolean. Set to True only shows calls/returns in the trace. Set to False means trace everything.

The Trc.Off member.

Trc.Off() 

Stops the trace.

The Trc.Resume member.

Trc.Resume()

continues a trace printing with no changes to the trace parameters in effect on the preceding Trc.Off().

The Trc.StopWhen member.

Trc.StopWhen(stoptestfunc) 

Gives a reference to a predicate function which returns True when an event occurs where you want the state of the program preserved. The function should be optimized for the path that returns False.

This call must be made after the Trc.On().

The function must be valid to execute in any environment. Variables must remain in scope.

The function may be a lambda function, but it need not be. it can take the form:

def StopTheWorld():
    if not <stop the world condition>:
        return False
    <other processing>

Where other processing can be a smart dump of the state or a breakpoint()

The Trc.Watch member.

Trc.Watch(lcls=IsNot, globs=IsNot, nonlcls=IsNot)

Tells puff what variables to watch for modifications to. Ideally variables will not fall out of scope. If they do we do not monitor them.

Variable that are local are in the lcls list, The global variables are in the globs list and nonlocals in the nonlcls list.

Variables must be simple, a good list would be like "x, y, z" This part while useful is not bullet proof.

Sample Output

  "c:\0\honahlee.py"
*** Puff trace focused on module file :
	 C:\0\honahlee.py
***Tracing On, nest = 1 lines =None funcs = None callsonly = False
waiting on you
			*** "foo" called ***
honahlee.py(23):     x = 5
honahlee.py(24):     y = 12
honahlee.py(25):     z = 2*x+y
honahlee.py(26):     baz()
In baz
			*** return from "baz" ***
			*** return from "foo" ***
***Tracing On, nest = 2 lines =None funcs = None callsonly = False
			*** "foo" called ***
honahlee.py(23):     x = 5
honahlee.py(24):     y = 12
honahlee.py(25):     z = 2*x+y
honahlee.py(26):     baz()
			*** "baz" called ***
honahlee.py(29):     print ("In baz")
In baz
			*** return from "baz" ***
			*** return from "foo" ***
			*** "recurse" called ***
honahlee.py(32):     print(n)
5
honahlee.py(33):     if n > 0:
honahlee.py(34):         recurse(n-1)
			*** "recurse" called ***
honahlee.py(32):     print(n)
4
honahlee.py(33):     if n > 0:
honahlee.py(34):         recurse(n-1)
3
2
1
0
Hit Bottom
			*** return from "recurse" ***
			*** return from "recurse" ***
***Tracing On, nest = 100 lines =None funcs = ['foo'] callsonly = False
waiting on you A !
			*** "foo" called ***
honahlee.py(23):     x = 5
honahlee.py(24):     y = 12
honahlee.py(25):     z = 2*x+y
honahlee.py(26):     baz()
In baz
***Tracing On, nest = 100 lines =None funcs = ['baz'] callsonly = False
waiting on you B !
			*** "baz" called ***
honahlee.py(29):     print ("In baz")
In baz
			*** return from "baz" ***
***Tracing On, nest = 100 lines =None funcs = ['foo', 'baz'] callsonly = False
waiting on you C !
			*** "foo" called ***
honahlee.py(23):     x = 5
honahlee.py(24):     y = 12
honahlee.py(25):     z = 2*x+y
honahlee.py(26):     baz()
			*** "baz" called ***
honahlee.py(29):     print ("In baz")
In baz
			*** return from "baz" ***
			*** return from "foo" ***
5
4
3
2
1
0
Hit Bottom
***Tracing Off
***Tracing Resumed
waiting on you D !
			*** "foo" called ***
honahlee.py(23):     x = 5
honahlee.py(24):     y = 12
honahlee.py(25):     z = 2*x+y
honahlee.py(26):     baz()
			*** "baz" called ***
honahlee.py(29):     print ("In baz")
In baz
			*** return from "baz" ***
			*** return from "foo" ***
***Tracing On, nest = 100 lines =None funcs = None callsonly = False
waiting on you E !
	***** Only tracing line 21
			*** "foo" called ***
honahlee.py(23):     x = 5
honahlee.py(24):     y = 12
honahlee.py(25):     z = 2*x+y
honahlee.py(26):     baz()
			*** "baz" called ***
honahlee.py(29):     print ("In baz")
In baz
			*** return from "baz" ***
			*** return from "foo" ***
***Tracing On, nest = 100 lines =None funcs = ['jack', 'recurse'] callsonly = False
waiting on you F !
			*** "jack" called ***
honahlee.py(39):     recurse(3)
			*** "recurse" called ***
honahlee.py(32):     print(n)
3
honahlee.py(33):     if n > 0:
honahlee.py(34):         recurse(n-1)
			*** "recurse" called ***
honahlee.py(32):     print(n)
2
honahlee.py(33):     if n > 0:
honahlee.py(34):         recurse(n-1)
			*** "recurse" called ***
honahlee.py(32):     print(n)
1
honahlee.py(33):     if n > 0:
honahlee.py(34):         recurse(n-1)
			*** "recurse" called ***
honahlee.py(32):     print(n)
0
honahlee.py(33):     if n > 0:
Line number sequence break	...
honahlee.py(36):         print('Hit Bottom')
Hit Bottom
			*** return from "recurse" ***
			*** return from "recurse" ***
			*** return from "recurse" ***
			*** return from "recurse" ***
			*** return from "jack" ***
***Tracing On, nest = 100 lines =None funcs = None callsonly = True
waiting on you G  !
			*** "jack" called ***
			*** "recurse" called ***
3
			*** "recurse" called ***
2
			*** "recurse" called ***
1
			*** "recurse" called ***
0
Hit Bottom
			*** return from "recurse" ***
			*** return from "recurse" ***
			*** return from "recurse" ***
			*** return from "recurse" ***
			*** return from "jack" ***
*** Watch Puff.lcls={'x': IsNot, 'y': IsNot, 'z': IsNot}
*** Watch Puff.globs={'idx': IsNot}
***Tracing On, nest = 100 lines =None funcs = None callsonly = False
			*** "jill" called ***
	>>>>>>> global idx initially set to 0
honahlee.py(43):     print("Entered jill")
Entered jill
honahlee.py(44):     oldidx = idx
honahlee.py(45):     for x in range(3):
	>>>>>>> local x initially set to 0
honahlee.py(46):         idx += 1
	>>>>>>> global idx was 0 is now set to 1
honahlee.py(47):         for y in range(3):
	>>>>>>> local y initially set to 0
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z initially set to 100
Line number sequence break	...
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 0 is now set to 1
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 100 is now set to 101
Line number sequence break	...
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 1 is now set to 2
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 101 is now set to 102
Line number sequence break	...
honahlee.py(47):         for y in range(3):
Line number sequence break	...
honahlee.py(45):     for x in range(3):
	>>>>>>> local x was 0 is now set to 1
honahlee.py(46):         idx += 1
	>>>>>>> global idx was 1 is now set to 2
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 2 is now set to 0
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 102 is now set to 210
Line number sequence break	...
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 0 is now set to 1
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 210 is now set to 211
Line number sequence break	...
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 1 is now set to 2
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 211 is now set to 212
Line number sequence break	...
honahlee.py(47):         for y in range(3):
Line number sequence break	...
honahlee.py(45):     for x in range(3):
	>>>>>>> local x was 1 is now set to 2
honahlee.py(46):         idx += 1
	>>>>>>> global idx was 2 is now set to 3
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 2 is now set to 0
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 212 is now set to 320
Line number sequence break	...
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 0 is now set to 1
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 320 is now set to 321
Line number sequence break	...
honahlee.py(47):         for y in range(3):
	>>>>>>> local y was 1 is now set to 2
honahlee.py(48):             z = idx * 100 + x * 10 + y
	>>>>>>> local z was 321 is now set to 322
Line number sequence break	...
honahlee.py(47):         for y in range(3):
Line number sequence break	...
honahlee.py(45):     for x in range(3):
Line number sequence break	...
honahlee.py(49):     idx = oldidx
			*** return from "jill" ***
	>>>>>>> global idx was 3 is now set to 0
*** Puff not watching variables
***Tracing Off
***Tracing On, nest = 100 lines =None funcs = ['opossum'] callsonly = False
waiting on you H  !
***Trace and target execution stop when condition is True
			*** "opossum" called ***
honahlee.py(19):     for idx in range(10):
honahlee.py(20):         print("opossum on iteration", idx)
opossum on iteration 0
Line number sequence break	...
honahlee.py(19):     for idx in range(10):
honahlee.py(20):         print("opossum on iteration", idx)
opossum on iteration 1
Line number sequence break	...
honahlee.py(19):     for idx in range(10):
honahlee.py(20):         print("opossum on iteration", idx)
opossum on iteration 2
Line number sequence break	...
honahlee.py(19):     for idx in range(10):
honahlee.py(20):         print("opossum on iteration", idx)
opossum on iteration 3
Line number sequence break	...
honahlee.py(19):     for idx in range(10):
honahlee.py(20):         print("opossum on iteration", idx)
opossum on iteration 4
Line number sequence break	...
honahlee.py(19):     for idx in range(10):
honahlee.py(20):         print("opossum on iteration", idx)
***Trace and execution ends because of StopWhen
Traceback (most recent call last):
  File "C:\0\honahlee.py", line 111, in <module>
    showall()
  File "C:\0\honahlee.py", line 105, in showall
    opossum()
  File "C:\0\honahlee.py", line 20, in opossum
    print("opossum on iteration", idx)
  File "C:\0\honahlee.py", line 20, in opossum
    print("opossum on iteration", idx)
  File "C:\Users\Dan Bates\AppData\Local\Programs\Python\Python310\lib\site-packages\pufftracer\pufftrc.py", line 195, in smalltrace
    assert False, "***Puff had a StopWhen return True"
AssertionError: ***Puff had a StopWhen return True

Watch For:

This topic is doscussed on more detail in the chapter on Debugging in a book I have written. I will update this with information when the book is published.

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

pufftracer-0.1.11.tar.gz (10.7 kB view details)

Uploaded Source

Built Distribution

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

pufftracer-0.1.11-py3-none-any.whl (10.6 kB view details)

Uploaded Python 3

File details

Details for the file pufftracer-0.1.11.tar.gz.

File metadata

  • Download URL: pufftracer-0.1.11.tar.gz
  • Upload date:
  • Size: 10.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.27.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.10.1

File hashes

Hashes for pufftracer-0.1.11.tar.gz
Algorithm Hash digest
SHA256 d020443e4e6549321e8f099cf69ed32d75bc0e18318e30e75968d1630db294dc
MD5 bad10e28013ece4149e12dffe3524f5f
BLAKE2b-256 ff0ad2d8011ca5592412dc0e5494c1ddda9a06292019086f3dbe11e1e06edccb

See more details on using hashes here.

File details

Details for the file pufftracer-0.1.11-py3-none-any.whl.

File metadata

  • Download URL: pufftracer-0.1.11-py3-none-any.whl
  • Upload date:
  • Size: 10.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.27.1 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.10.1

File hashes

Hashes for pufftracer-0.1.11-py3-none-any.whl
Algorithm Hash digest
SHA256 97da7e9808954e769910121e2a1f6e49a8ccaed8cc645022c9ac77594c9cd2aa
MD5 b48128e18e528f2da89ca0ce0a24f1b4
BLAKE2b-256 42308184dc86b69075b34481ba737923e2ebbe081782068d9062e1142227c968

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