I want to set breakpoints in different places of my program. However they are fired if I pass a specific argument to my program, I called it STOP_LEVEL
STOP_LEVEL = 0
def mbp(sl=-1):
if sl == STOP_LEVEL:
# I would like to print which function called this
mlog.info("break point at %s, %s", ...)
breakpoint()
Then I can create a breakpoint like
mbp(2)
and if I set stop_level to 2, it's fired.
First, I would like to know are there other standard methods for such functionality? Second, I would like to know where from my mbp function was called, I mean the caller function, filename and line number. How can I include them in my function as logging information?
There isn't built in functionality like that.
The only way to find where your application was called from would be to examine the stack or to pop from your function. After popping the stack (returning from your function) you'll be at the location in your code it was called from.
To examine the stack you can use inspect.stack()
In pdb you can run the command where
I ended up to this function:
import sys
STOP_LEVEL = 0
def mbp(sl=-1):
if sl == STOP_LEVEL:
fname = sys._getframe().f_back.f_code.co_name
line = sys._getframe().f_back.f_lineno
mlog.info("break point at %s line %s",fname, line)
breakpoint()
Related
I want to set a breakpoint on the set.update() function, but when I try, I get an error message.
Example:
ss= set()
ss.update('a')
Breakpoint:
b set.update
b ss.update
Errors:
The specified object 'ss.update' is not a function
or was not found along sys.path.
The specified object 'set.update' is not a function
or was not found along sys.path.
(Note, I also tried with the parentheses at the end, e.g., b set.update(), but still got the error. I didn't print all the permutations of errors.)
Thanks! Using #avasal's answer and Doug Hellmann's pdb webpage, I came up with this:
Since I was trying to catch set.update, I had to edit the sets.py file, but that wasn't enough, since python was using the builtin set class rather than the one I edited. So I overwrote the builtin sets class:
import sets
locals()['__builtins__'].set=sets.Set
Then I could set conditional break points in the debugger:
b set.update, iterable=='a' #successful
b set.update, iterable=='b' #won't stop for ss.update('a')
My entire example file looks like this:
import pdb
import sets
locals()['__builtins__'].set=sets.Set
pdb.set_trace()
ss = set()
ss.update('a')
print "goodbye cruel world"
Then at the debugger prompt, enter this:
b set.update, iterable=='a'
Hope this helps others too.
I have a function in maya that imports in other functions and creates a shelf with buttons for specific functions. I have a function that has a scriptJob command that works fine. if I import that file in manually and not through the shelf button, but gives a NameError when using the shelf script to run it.
This is an example of the script
myShelf.py file:
import loopingFunction
loopingFunction.runThis()
loopingFunction.py file:
import maya.cmds as mc
def setSettings():
#have some settings set before running this
runThis()
def runThis():
print "yay this ran"
evalDeferred(mc.scriptJob(ro=True,ac=["'someMesh.outMesh',runThis"])
if I run this through the shelf function, I get a runThis nameError is not defined and if I try modifying the scriptJob command to loopingFunction.runThis, I get a nameError loopingFunction is not defined(not sure if using loopingFunction.runThis is even correct, to be honest)
Not sure how I can get around this without having to manually import in the function rather than through the shelf file.
Using string references for callback functions like this often leads to scope problems. (More on that, and why not to use strings, here)
If you pass the function directly to the callback as an object, instead of using a string, it should work correctly as along as you have actually imported the code.
in this case you want an evalDeferred -- do you actually need it? -- so it helps to add a little function around the actual code so that the scriptjob creation actually happens later -- otherwise it will get evaluated before the deferral is scheduled.
def runThis():
print "callback ran"
def do_scriptjob():
cmds.scriptJob(ro=True, ac=('someMesh.outMesh', runThis)
cmds.evalDeferred(do_scriptjob)
In both runThis and do_scriptjob I did not add the parens -- we are letting the evalDeferred and the scriptJob have the function objects to call when they are ready. Using the parens would pass the results of the functions which is not what you want here.
Incidentally it looks like you're trying to create a new copy of the scriptJob inside the scriptJob itself. It'd be better to just drop the runOnce flag and leave the scriptJob lying around -- if something in runThis actually affected the someMesh.outMesh attribute, your maya will go into an infinite loop. I did not change the structure in my example, but I would not recommend writing this kind of self-replicating code if you can possibly avoid it.
You have a problem of nested/maya scope variables
mc.scriptJob(ro=True,ac=["'someMesh.outMesh',runThis"]
This line is a maya command string that is evaluated in the main maya scope (like a global)
As your function have a namespace with the import : 'loopingFunction', you need to enforce it in the string command.
import loopingFunction
loopingFunction.runThis()
You should write
evalDeferred(mc.scriptJob(ro=True,ac=["'someMesh.outMesh',loopingFunction.runThis"])
If you want something more general, you can do :
def runThis(ns=''):
print "yay this ran"
if ns != '':
ns += '.'
evalDeferred(mc.scriptJob(ro=True,ac=["'someMesh.outMesh',{}runThis".format(ns)])
and then run in shelf :
import loopingFunction
loopingFunction.runThis('loopingFunction')
like this you can write any formof namepsaces :
import loopingFunction as loopF
loopF.runThis('loopF')
Edit: My first attempt at asking this might be a bit unfocused/poorly worded here's a better explanation of what I'm trying to do:
I'm trying to modify the default behavior of the print function for the entire environment python is running in without having to modify each file that's being run.
I'm attempting to decorate the print function (I know there are many ways to do this such as overriding it but that's not really the question I'm asking) so I can have it print out some debugging information and force it to always flush. I did that like so:
def modify_print(func):
# I made this so that output always gets flushed as it won't by default
# within the environment I'm using, I also wanted it to print out some
# debugging information, doesn't really matter much in the context of this
# question
def modified_print(*args,**kwargs):
return func(f"some debug prefix: ",flush=True,*args,**kwargs)
return modified_print
print = modify_print(print)
print("Hello world") # Prints "some debug prefix Hello World"
However what I'm trying to do is modify this behavior throughout my entire application. I know I can manually decorate/override/import the print function in each file however I'm wondering if there is some way I can globally configure my python environment to decorate this function everywhere. The only way I can think to do this would be to edit the python source code and build the modified version.
EDIT:
Here's the behavior I wanted implemented, thank you Match for your help.
It prints out the line number and filename everywhere you call a print function within your python environment. This means you don't have to import or override anything manually in all of your files.
https://gist.github.com/MichaelScript/444cbe5b74dce2c01a151d60b714ac3a
import site
import os
import pathlib
# Big thanks to Match on StackOverflow for helping me with this
# see https://stackoverflow.com/a/48713998/5614280
# This is some cool hackery to overwrite the default functionality of
# the builtin print function within your entire python environment
# to display the file name and the line number as well as always flush
# the output. It works by creating a custom user script and placing it
# within the user's sitepackages file and then overwriting the builtin.
# You can disable this behavior by running python with the '-s' flag.
# We could probably swap this out by reading the text from a python file
# which would make it easier to maintain larger modifications to builtins
# or a set of files to make this more portable or to modify the behavior
# of more builtins for debugging purposes.
customize_script = """
from inspect import getframeinfo,stack
def debug_printer(func):
# I made this so that output always gets flushed as it won't by default
# within the environment I'm running it in. Also it will print the
# file name and line number of where the print occurs
def debug_print(*args,**kwargs):
frame = getframeinfo(stack()[1][0])
return func(f"{frame.filename} : {frame.lineno} ", flush=True,*args,**kwargs)
return debug_print
__builtins__['print'] = debug_printer(print)
"""
# Creating the user site dir if it doesn't already exist and writing our
# custom behavior modifications
pathlib.Path(site.USER_SITE).mkdir(parents=True, exist_ok=True)
custom_file = os.path.join(site.USER_SITE,"usercustomize.py")
with open(custom_file,'w+') as f:
f.write(customize_script)
You can use usercustomize script from the site module to achieve something like this.
First, find out where your user site-packages directory is:
python3 -c "import site; print(site.USER_SITE)"
/home/foo/.local/lib/python3.6/site-packages
Next, in that directory, create a script called usercustomize.py - this script will now be run first whenever python is run.
One* way to replace print is to override the __builtins__ dict and replace it with a new method - something like:
from functools import partial
old_print = __builtins__['print']
__builtins__['print'] = partial(old_print, "Debug prefix: ", flush=True)
Drop this into the usercustomize.py script and you should see all python scripts from then on being overridden. You can temporarily disable calling this script by calling python with the -s flag.
*(Not sure if this is the correct way of doing this - there may be a better way - but the main point is that you can use usercustomize to deliver whatever method you choose).
There's no real reason to define a decorator here, because you are only intending to apply it to a single, predetermined function. Just define your modified print function directly, wrapping it around __builtins__.print to avoid recursion.
def print(*args, **kwargs):
__builtins.__print(f"some debug prefix: ", flush=True, *args, **kwargs)
print("Hello world") # Prints "some debug prefix Hello World"
You can use functools.partial to simplify this.
import functools
print = functools.partial(__builtins.__print, f"some debug prefix: ", flush=True)
I have a Python script which is returning data by printing them, a function is not used.
I now want to make a function out of the script which works in the same way, but instead of printing the data, it should be returned by the app function.
Of course I could do it manually by writing "def myapp():", making all the indentations, and call it in the last line of the script, but I wonder if there is a tool for that?
Always write your script as one or more functions ending in two "magic" lines. A suitable template is
import sys # if you want a system return code
MY_CONSTANT = "whatever" # a symbolic constant
def a_function( args): # replace with your useful stuff
pass
# def b_function( args): # as many more defs as are useful
# can refer to / use both a_function (above) and c_function (below)
# def c_function()
# etc
def main():
print( "Script starting ...")
# parse arguments, if any parsing needed
# do stuff using the functions defined above
# print output, if needed
print( "End of script")
sys.exit(0) # 0 is Linux success, or other value for $? on exit
# "magic" that executes script_main only if invoked as a script
if __name__ == "__main__": # are we being invoked directly from command line?
main() # if so, run this file as a script.
Why? This file (myfile.py) is also usable as an import, at the interpreter prompt or in another file / script / module. It will define the constants and functions but it will not actually run anything when being imported as below. Either
import myfile
so you can refer to myfile.a_function, myfile.MY_CONSTANT, etc.
Or
from myfile import a_function
and then you can invoke a_function(args) without needing the prefix. You'll often see test or some random name: main() is not special.
I want to set a breakpoint on the set.update() function, but when I try, I get an error message.
Example:
ss= set()
ss.update('a')
Breakpoint:
b set.update
b ss.update
Errors:
The specified object 'ss.update' is not a function
or was not found along sys.path.
The specified object 'set.update' is not a function
or was not found along sys.path.
(Note, I also tried with the parentheses at the end, e.g., b set.update(), but still got the error. I didn't print all the permutations of errors.)
Thanks! Using #avasal's answer and Doug Hellmann's pdb webpage, I came up with this:
Since I was trying to catch set.update, I had to edit the sets.py file, but that wasn't enough, since python was using the builtin set class rather than the one I edited. So I overwrote the builtin sets class:
import sets
locals()['__builtins__'].set=sets.Set
Then I could set conditional break points in the debugger:
b set.update, iterable=='a' #successful
b set.update, iterable=='b' #won't stop for ss.update('a')
My entire example file looks like this:
import pdb
import sets
locals()['__builtins__'].set=sets.Set
pdb.set_trace()
ss = set()
ss.update('a')
print "goodbye cruel world"
Then at the debugger prompt, enter this:
b set.update, iterable=='a'
Hope this helps others too.