In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces "how to use pdb for Python debugging". In daily operation, I believe many people have doubts about how to use pdb for Python debugging. The editor consulted all kinds of data and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "how to use pdb for Python debugging". Next, please follow the editor to study!
1. Initial phase: allow a variable value
To start with, let's explore the simplest use of pdb: looking up the value of a variable. First, we write the following statement on a line in a source file:
Import pdb; pdb.set_trace ()
When this line of code is executed, the Python code file is suspended, waiting for you to issue a command to instruct it what to do next. After running the above code, you can see the prompt (Pdb) in the command line interface, which means that the code is stopped, waiting for the command to be entered.
Since Python3.7, the official standard recommends replacing the above code with the standard library internal function breakpoint () (Note: the code attached to this article uses the above code form), which can speed up the running and debugging of the interpreter:
Breakpoint ()
By default, breakpoint () will pour into the pdb module and then call the pdb.set_trace () function, but it will be further encapsulated. However, using breakpoint () is more flexible and allows users to control debugging behavior by calling its API, as well as using the environment variable PYTHONBREAKPOINT. For example, when we set PYTHONBREAKPOINT=0 in our environment, this completely turns off the function of breakpoint (), thus turning off debugging.
In addition, instead of manually adding breakpoint code to the source code file, you only need to set it through parameter passing when you enter the run instruction on the command line, for example:
$python3-m pdb app.py arg1 arg2
So, let's go straight to the content of this section, that is, look at the values of variables in the code, and take a look at the following example, the source file used is codeExample1.py:
#! / usr/bin/env python3filename = _ _ file__import pdb; pdb.set_trace () print (f "path= {filename}")
On your command line interface, run the above Python code and you will get the following output:
$. / codeExample1.py > / code/codeExample1.py (5) ()-> print (f "path= {filename}") (Pdb)
Next, let's type the command p filename to look at the value of the variable filename, and you can see the following result:
(Pdb) p filename ". / codeExample1.py" (Pdb)
Because we are using a command line interface program (command-line interface), pay attention to the characters and format of the output and explain as follows:
The first line tells us the name of the source file we are running. After the name of the source code, the current number of lines of code is contained in parentheses, followed by the name of the function. Here, because we don't need to call any functions, we're at the module level, and what we see is
().
-> the second line at the beginning represents the specific code content corresponding to the current number of lines of code.
(Pdb) is a pdb prompt waiting for the next command to be entered.
We can use the Q command to indicate launch quit.
two。 Print expression
When using the command p, we can also enter an expression and let Python evaluate the value of the expression. If you pass in a variable name, pdb will allow the corresponding value of the current variable. However, we can further investigate the current running state of our application.
In the following case, when the function get_path () is called, in order to see what happens in this function, I have inserted the breakpoint program pdb.set_trace () in advance to block the running of the program. The source code codeExample2.py is as follows:
#! / usr/bin/env python3import osdef get_path (filename): "Return file" s path or empty string if no path. "" head, tail = os.path.split (filename) import pdb; pdb.set_trace () return headfilename = _ _ file__print (f "path = {get_path (filename)}")
If you run this file on the command line, you can get the following output:
$. / codeExample2.py > / code/example2.py (10) get_path ()-> return head (Pdb)
So at this point, what stage are we in:
>: indicates that we are at line 10 of the source file codeExample2.py, where is the function get_path (). If you run the command p, the scalar output is the currently referenced code frame, that is, the scalar in the current context.
->: the running code has been stopped at return head, and this line of code has not been executed. It is located at line 10 of the codeExample2.py file, specifically in the function get_path ().
If you want to see the context of the application's current status code, you can use the command ll (longlist) to view the code content as follows:
(Pdb) ll 6 def get_path (filename): 7 "Return file" s path or empty string if no path. "" 8 head, tail = os.path.split (filename) 9 import pdb Pdb.set_trace () 10-> return head (Pdb) p filename ". / codeExample2.py" (Pdb) p head, tail (".", "codeExample2.py") (Pdb) p "filename:" + filename "filename:. / codeExample2.py" (Pdb) p get_path (Pdb) p getattr (get_path) "_ _ doc__") "Return file" s path or empty string if no path. "(Pdb) p [os.path.split (p) [1] for p in os.path.sys.path] [" pdb-basics "," python36.zip "," python3.6 "," lib-dynload "," site-packages "] (Pdb)
You can type any valid Python expression followed by p to evaluate. This is especially useful when you are debugging and want to test the alternative implementation directly in the application at run time. You can also use the command pp (beautiful printing) to beautifully print expressions. If you want to print variables or expressions with a large number of outputs, such as lists and dictionaries. If you can, beautiful printing keeps objects on one line, and if they don't fit the allowed width, divide them into multiple lines.
3. Debug code
In this part, we mainly use two commands to debug the code, as shown in the following figure:
The difference between the commands n (next) and s (step) is where the pdb stops. With n (next), pdb executes until it runs to the next line of code of the current function or module, that is, if an external function is called, it does not jump to the external function code, which can be understood as "step over". Use s (step) to execute the current code, but if an external function is called, it jumps to the external function, which can be understood as "step into", and if executed to the external jump function, the s command outputs-- Call--.
Both the n (next) and s (step) commands tentatively execute the code when running to the end of the current function and print out-- Return--, below is the codeExample3.py file source code content:
#! / usr/bin/env python3import osdef get_path (filename): "Return file" s path or empty string if no path. "" head, tail = os.path.split (filename) return headfilename = _ _ file__import pdb; pdb.set_trace () filename_path = get_path (filename) print (f "path = {filename_path}")
If you run this file on the command line and enter the command n, you get the following output:
/ codeExample3.py > / code/example3.py (14) ()-> filename_path = get_path (filename) (Pdb) n > / code/example3.py (15) ()-> print (f "path = {filename_path}") (Pdb)
By using the command n (next), we stop at 15 lines of code, and we just don't jump to the function get_path () in this module. Here the function is expressed as (), indicating that we are currently at the module level rather than inside any function.
Let's try the s (step) command again, and the output is as follows:
$. / codeExample3.py > / code/example3.py (14) ()-> filename_path = get_path (filename) (Pdb) s Murray Callmuri-> / code/example3.py (6) get_path ()-> def get_path (filename): (Pdb)
By using the s (step) command, we stop at line 6 inside the function get_path () because the function is called at line 14 in the code file, and notice that it is output after the s command-- Call--, indicates that it is a function call. For convenience, pdb has command memory function. If we want to debug a lot of code, we can type Enter enter to repeat the command.
The following is an example of a mix of n (next) and s (step) commands. First enter s (step) because we want to enter the function get_path (), then debug the code locally with the command n (next), and use the Enter enter key to avoid repeatedly entering the command:
$. / codeExample3.py > / code/codeExample3.py (14) ()-> filename_path = get_path (filename) (Pdb) s Murray Callmuri-> / code/codeExample3.py (6) get_path ()-> def get_path (filename): (Pdb) n > / code/codeExample3.py (8) get_path ()-> head Tail = os.path.split (filename) (Pdb) > / code/codeExample3.py (9) get_path ()-> return head (Pdb)-- Return-- > / code/codeExample3.py (9) get_path ()-> "."-> return head (Pdb) > / code/codeExample3.py (15) ()-> print (f "path = {filename_path}") (Pdb) path =.-- Return-- > / code/codeExample3.py (15) ()-> None -> print (f "path = {filename_path}") (Pdb)
Notice the output-- Call-- and-- Return--,-- this is the pdb output, prompting us to debug the state information, n (next) and s (step) commands will stop after the function returns, that is, we will see the output of-- Return-- information. In addition, pay attention to-> "." At the end of the first-- Return-- output:
-- Return-- > / code/example3.py (9) get_path ()-> "."-> return head (Pdb)
When pdb stops to the end of a function, but has not yet run to return, pdb also prints the contains value, which is "." in the above example.
3.1 display Code
Don't forget that we mentioned the command ll (longlist: show the source code of the current function or frame). This command is very effective when we debug into an unfamiliar code context. We can print out the entire function code, and the sample is shown as follows:
$. / codeExample3.py > / code/codeExample3.py (14) ()-> filename_path = get_path (filename) (Pdb) s Murray head-> / code/codeExample3.py (6) get_path () > def get_path (filename): (Pdb) ll 6-> def get_path (filename): 7 "" Return file "s path or empty string if no path."8 head Tail = os.path.split (filename) 9 return head (Pdb)
If you want to view a short code snippet, we can use the command l (list), which does not need to enter parameters, which will print 11 lines of code near the current code, as in the following example:
$. / codeExample3.py > / code/codeExample3.py (14) ()-> filename_path = get_path (filename) (Pdb) l 9 return head 10 11 12 filename = _ file__ 13 import pdb; pdb.set_trace () 14-> filename_path = get_path (filename) 15 print (f "path = {filename_path}") [EOF] (Pdb) l [EOF] (Pdb) l. 9 return head 10 11 12 filename = _ file__ 13 import pdb; pdb.set_trace () 14-> filename_path = get_path (filename) 15 print (f "path = {filename_path}") [EOF] (Pdb) 4. Breakpoint use
The correct use of breakpoints can save a lot of time in our debugging process. Instead of debugging lines of code step by step, you can simply run to the breakpoint to debug by simply setting a breakpoint where we want to look up. Similarly, we can add conditions to let pdb determine whether a breakpoint needs to be set. We usually use the command b (break) to set a breakpoint. We can specify the number of lines of code, or the name of the function we want to debug. The syntax is as follows:
B (reak) [([filename:] lineno | function) [, condition]]
If filename: is not specified before the number of lines of code, the default is in the current code file. Note that the second optional parameter is b: condition, which is very powerful. Suppose that in a scenario, we want to set a breakpoint under certain conditions. If we pass in the second parameter of an expression seat, pdb will set the breakpoint if the expression is changed to true. We will give an example below. In the following example, using a utility module util.py, let's set a breakpoint in the function get_path (). Here is the content of the code codeExample4.py:
#! / usr/bin/env python3import utilfilename = _ _ file__import pdb; pdb.set_trace () filename_path = util.get_path (filename) print (f "path = {filename_path}")
The following is the contents of the tool module util.py file:
Def get_path (filename): "Return file" s path or empty string if no path. "import os head, tail = os.path.split (filename) return head
First, let's set the breakpoint using the name of the source file and the number of lines of code:
$. / example4.py > / code/example4.py (7) ()-> filename_path = util.get_path (filename) (Pdb) b util:5Breakpoint 1 at / code/util.py:5 (Pdb) c > / code/util.py (5) get_path ()-> return head (Pdb) p filename, head, tail (". / example4.py", "example4.py") (Pdb)
The command c (continue) is a command that continues to run after the breakpoint is stopped. Let's use the function name to set the breakpoint:
$. / codeExample4.py > / code/codeExample4.py (7) ()-> filename_path = util.get_path (filename) (Pdb) b util.get_pathBreakpoint 1 at / code/util.py:1 (Pdb) c > / code/util.py (3) get_path ()-> import os (Pdb) p filename ". / codeExample4.py" (Pdb)
If you enter b without any parameters, you can view all breakpoint information that has been set:
(Pdb) bNum Type Disp Enb Where1 breakpoint keep yes at / code/util.py:1 (Pdb)
You can use the commands disable bpnumber and enable bpnumber to disable and re-enable breakpoints. Bpnumber is the breakpoint number in the first column Num of the breakpoint list. Notice that the value of the Enb column changes:
(Pdb) disable 1Disabled breakpoint 1 at / code/util.py:1 (Pdb) bNum Type Disp Enb Where1 breakpoint keep no at / code/util.py:1 (Pdb) enable 1Enabled breakpoint 1 at / code/util.py:1 (Pdb) bNum Type Disp Enb Where1 breakpoint keep yes at / code/util.py:1 (Pdb)
To delete a breakpoint, use the command cl (clear):
Cl (ear) filename:linenocl (ear) [bpnumber [bpnumber...]]
Now, let's try to enter the expression parameter when setting the breakpoint. In the current case scenario, the get_path () function accepts a relative path, that is, if the path name does not start with /, then the breakpoint will not be set. The specific example is as follows:
/ codeExample4.py > / code/codeExample4.py (7) ()-> filename_path = util.get_path (filename) (Pdb) b util.get_path, not filename.startswith ("/") Breakpoint 1 at / code/util.py:1 (Pdb) c > / code/util.py (3) get_path ()-> import os (Pdb) afilename = ". / codeExample4.py" (Pdb)
If you continue to enter the c (continue) command to run after creating a breakpoint, pdb will only stop running if the expression evaluates to true. The command a (args) prints out the incoming arguments to the current function.
In the above case, if you set the breakpoint by the name of the function rather than the number of lines of code, note that the expression will only use the parameters of the current function or global variables, otherwise, the breakpoint will stop the function from running instead of evaluating the expression. If we still want to evaluate the expression with a variable that is not the current function, that is, the variable we use is not in the current function argument list, then we need to specify the number of lines of code, as shown in the following example:
$. / codeExample4.py > / code/codeExample4.py (7) ()-> filename_path = util.get_path (filename) (Pdb) b util:5, not head.startswith ("/") Breakpoint 1 at / code/util.py:5 (Pdb) c > / code/util.py (5) get_path ()-> return head (Pdb) p head ". (Pdb) afilename =". / codeExample4.py "(Pdb)
5. Continue to execute the code
Currently, we can use the n (next) and s (step) commands to debug and view the code, and then use the commands b (break) and c (continue) to stop or continue running the code, and here is a related command: unt (until). Using the unt command is similar to the c command, but the result is that the next line is larger than the current number of lines of code. Sometimes, unt is more convenient and convenient to use, so let's show it in the following example, first giving the usage syntax:
Depending on whether we enter the number of lines of code parameter the lineno,unt command can be run in the following two ways:
Without lineno, the code can continue to execute to the point where the next number of lines is larger than the current number of lines, which is similar to n (next), which is another form of execution similar to "step over". However, the difference between the command n and unt is that unt only stops where the next number of lines is greater than the current number of lines, while the n command stops at the next logical execution line.
With lineno, the code runs to a point where the next number of lines is larger or equal than the current one, similar to c (continue) followed by a code function parameter.
In both cases, unt commands like n (next) and s (step) only stop at the current frame (or function).
Use unt when you want to continue execution and stop further in the current source file. You can think of it as a mixture of n (next) and b (break), depending on whether the line number parameter is passed or not.
In the following example, there is a function with a loop. Here, we want to continue executing the code and stop after the loop, rather than stepping through each iteration of the loop or setting breakpoints. Here is the contents of the file codeExample4unt.py file:
#! / usr/bin/env python3import osdef get_path (fname): "Return file" s path or empty string if no path. "import pdb; pdb.set_trace () head, tail = os.path.split (fname) for char in tail: pass # Check filename char return headfilename = _ _ file__filename_path = get_path (filename) print (f" path = {filename_path} ")
And the output using unt under the command is as follows:
$. / codeExample4unt.py > / code/codeExample4unt.py (9) get_path ()-> head, tail = os.path.split (fname) (Pdb) ll 6 def get_path (fname): 7 "" Return file "s path or empty string if no path."8 import pdb Pdb.set_trace () 9-> head Tail = os.path.split (fname) 10 for char in tail: 11 pass # Check filename char 12 return head (Pdb) unt > / code/codeExample4unt.py (10) get_path ()-> for char in tail: (Pdb) > / code/codeExample4unt.py (11) get_path () > pass # Check filename char (Pdb) > / code/codeExample4unt.py (12) get_path ()-> return head (Pdb) p char, tail ("y", "codeExample4unt.py")
The ll command is used first to print the source code of the function, then unt. Pdb remembers the command I entered last time, so I just press Enter to repeat the unt command. This continues to execute the code until it reaches a line of source code larger than the current line.
Notice that in the console output above, pdb stopped only once on lines 10 and 11. Because unt is used, execution is stopped only in the first iteration of the loop. However, each iteration of the loop is executed. This can be verified on the last line of the output. The value "y" of the char variable is equal to the last character in the tail value "codeExample4unt.py".
6. Display expression
Similar to the function of printing expressions p and pp, we can use dispaly [expression] to tell pdb to display the value of an expression, and undisplay [expression] to clearly display an expression. Here are some syntax explanations:
Here is an example where the code file is codeExample4display.py, showing the use of a loop:
$. / codeExample4display.py > / code/codeExample4display.py (9) get_path ()-> head, tail = os.path.split (fname) (Pdb) ll 6 def get_path (fname): 7 "" Return file "s path or empty string if no path."8 import pdb Pdb.set_trace () 9-> head Tail = os.path.split (fname) 10 for char in tail: 11 pass # Check filename char 12 return head (Pdb) b 11Breakpoint 1 at / code/codeExample4display.py:11 (Pdb) c > / code/codeExample4display.py (11) get_path ()-> pass # Check filename char (Pdb) display chardisplay char: "e" (Pdb) c > / code/codeExample4display.py (11) get_path ()-> pass # Check filename chardisplay char: "x" [old: "e"] (Pdb) > / code/codeExample4display.py (11) get_path ()-> pass # Check filename chardisplay char: "a" [old: "x"] (Pdb) > / code/codeExample4display.py (11) get_path ()-> pass # Check filename chardisplay char: "m" [old: "a"]
In the above output, pdb automatically displays the value of the char variable, because its value changes each time a breakpoint is encountered. Sometimes this is helpful and exactly what you want, but there is another way to use display.
You can type display multiple times to build an expression watch list. This is easier to use than p. After adding all the expressions you are interested in, simply type display to see the current value:
$. / codeExample4display.py > / code/codeExample4display.py (9) get_path ()-> head, tail = os.path.split (fname) (Pdb) ll 6 def get_path (fname): 7 "" Return file "s path or empty string if no path."8 import pdb Pdb.set_trace () 9-> head Tail = os.path.split (fname) 10 for char in tail: 11 pass # Check filename char 12 return head (Pdb) b 11Breakpoint 1 at / code/codeExample4display.py:11 (Pdb) c > / code/codeExample4display.py (11) get_path ()-> pass # Check filename char (Pdb) display chardisplay char: "e" (Pdb) display fnamedisplay fname: ". / codeExample4display.py" (Pdb) display headdisplay head: ". ) display taildisplay tail: "codeExample4display.py" (Pdb) c > / code/codeExample4display.py (11) get_path ()-> pass # Check filename chardisplay char: "x" [old: "e"] (Pdb) displayCurrently displaying:char: "x" fname: ". / codeExample4display.py" head:. "tail:" codeExample4display.py "
7.Python Caller ID
In the last section, we will demonstrate the use of "call ID" based on what we have learned above. The following is the content of the case code codeExample5.py:
#! / usr/bin/env python3import fileutildef get_file_info (full_fname): file_path = fileutil.get_path (full_fname) return file_pathfilename = _ file__filename_path = get_file_info (filename) print (f "path = {filename_path}")
And the contents of the tool module fileutil.py file:
Def get_path (fname): "Return file" s path or empty string if no path. "import os import pdb; pdb.set_trace () head, tail = os.path.split (fname) return head
In this case, suppose you have a large code base that contains a function from the utility module get_path () that is called with invalid input. However, it is called from many places in different packages.
How do I find out who the caller is?
Use the command w (where) to print a sequence of code stacks that displays all stacks from low to top:
/ codeExample5.py > / code/fileutil.py (5) get_path ()-> head, tail = os.path.split (fname) (Pdb) w / code/codeExample5.py (12) ()-> filename_path = get_file_info (filename) / code/codeExample5.py (7) get_file_info ()-> file_path = fileutil.get_path (full_fname) > / code/fileutil.py (5) get_path ()-> head, tail = os.path.split (fname) (Pdb)
If this seems confusing, or if you're not sure what a stack trace or frame is, don't worry. I will explain these terms below. It's not as difficult as it sounds.
Since the nearest frame is at the bottom, start there and read from the bottom up. Look at the line that starts with->, but skip the first instance, because pdb.set_trace () is used to enter pdb in the function get_path (). In this example, the line of source code that calls function get_path () is:
> file_path = fileutil.get_path (full_fname)
The line package above each-> contains the file name and the number of lines of code (in parentheses), as well as the function name, so the caller is:
/ code/example5.py (7) get_file_info ()-> file_path = fileutil.get_path (full_fname)
Obviously in this simple example, we showed how to find function callers, but imagine a large application where you set a conditional breakpoint to determine the source of the error input value. Let's go further.
What is stack tracing and stack frame content?
The stack trace is just a list of all the frames that Python creates to trace function calls. A framework is a data structure that Python creates when calling a function and deletes when the function returns. The stack is just an ordered list of frames or function calls at any point in time. The stack grows and shrinks throughout the life cycle of the application as the function is called and then returned. When printing, this ordered list of frames, the stack, is called a stack trace. You can view it at any time by typing the command w, just as we found the caller on it.
What does the current stack frame mean?
Treats the current frame as the current function that pdb has stopped executing. In other words, the current frame is where your application is currently paused and is used as a "reference frame" for pdb commands such as p (print). P and other commands will use the current frame as the context as needed. In the case of p, the current frame is used to find and print variable references. When pdb prints a stack trace, the arrow > indicates the current frame.
How to use and switch stack frames?
You can use two commands u (up) and d (down) to change the current frame. Used in conjunction with p, this allows you to call any point in the stack in any frame to check variables and state in the application. The usage syntax is as follows:
Let's look at an example of using the u and d commands. In this case, we want to examine the local variable full_fname of the function get_file_info () in codeExample5.py. To do this, we must use the command u to change the current frame up one level:
/ codeExample5.py > / code/fileutil.py (5) get_path ()-> head, tail = os.path.split (fname) (Pdb) w / code/codeExample5.py (12) ()-> filename_path = get_file_info (filename) / code/codeExample5.py (7) get_file_info ()-> file_path = fileutil.get_path (full_fname) > / code/fileutil.py (5) get_path ()-> head Tail = os.path.split (fname) (Pdb) u > / code/codeExample5.py (7) get_file_info ()-> file_path = fileutil.get_path (full_fname) (Pdb) p full_fname ". / codeExample5.py" (Pdb) d > / code/fileutil.py (5) get_path ()-> head, tail = os.path.split (fname) (Pdb) p fname ". / codeExample5.py" (Pdb)
Because the breakpoint program pdb.set_trace () is set in the fileutil.py file function get_path (), the current frame is set here, as shown below:
> / code/fileutil.py (5) get_path ()
To access and print the variable full_fname in the codeExample5.py file function get_file_info (), the command u implementation moves to the previous stack frame:
(Pdb) u > / code/codeExample5.py (7) get_file_info ()-> file_path = fileutil.get_path (full_fname)
Notice that in the output of u above, pdb prints the arrow > at the beginning of the first line. This is pdb, which lets you know that the frame has changed and that the source location is now the current frame. You can now access the variable full_fname. In addition, it is important to be aware that the source line of line 2 that begins with-> has been executed. Fileutil.get_path () has been called because the frame has been moved onto the stack. With u, we move the stack up (in a sense, back in time) to the function codeExample5.get_file_info () that calls fileutil.get_path ().
Continuing with this example, after printing the full_fname, use d to move the current frame to its original position and print the local variable fname in get_path (). If we like, we can move multiple frames at a time by passing the count parameter to u or d. For example, we can move to the module level in codeExample5.py by typing u 2:
$. / codeExample5.py > / code/fileutil.py (5) get_path ()-> head, tail = os.path.split (fname) (Pdb) u 2 > / code/codeExample5.py (12) ()-> filename_path = get_file_info (filename) (Pdb) p filename ". / codeExample5.py" (Pdb) at this point, the study on "how to use pdb for Python debugging" is over, hoping to solve everyone's doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.