 All right, let's get started. My name is Craig Maloney, and I will be introducing debugging in Python for you. Now, I'm pretty sure it's not controversial to anyone that programming is hard. Programming also can lead to bugs. But we are going to take the spirit of Rear Admiral Grace Murray Hopper, who is responsible for discovering one of the most famous bugs. This is a moth that was in the relay panel of the Mark 2 computer. It's in the Smithsonian. You can take a look at it. Hopefully, our bugs won't be quite as famous as that bug. But not all bugs are as obvious as a giant moth in a relay on an old computer. Sometimes it takes a little bit more. And debugging, too, is not just about fixing bugs in there. Lovely non sequitur there. It's not just about fixing bugs. It's also about figuring out what the code is actually doing in there. Because sometimes you get pieces of code. You're not exactly sure what the heck it's doing. You think it's doing one thing, but it's doing something completely different. So let's do a quick example. What does this code do? I'll give you a second to take a look at it and see what you think it's doing. So let's execute it and see what it does. All right. Well, that's handy. It doesn't really do a whole lot that's visible. You think it's going to do something that's visible, but it doesn't. And you make this face. So what happened in there? Well, what happened is nothing is displayed. It doesn't actually get to this section of code here. And why is that? Well, one way to find out is the Lilly print statement. How many people have used the print statement? Every hand should go up. It's a cheap and cheerful way of getting information out of there. There's no shame. You're among friends. So let's put a print statement right here and figure out what's happening there. And we have our other print statement there for when it's completed. So we can now figure out what's going on. And we realize after that loop is ending, we're only hitting 4.75. We're not hitting the 20 that we might expect it to hit. So we learned a few things. Number one, print. And you have to have the parentheses around it because it's Python 3. We'll show the value of i. i is reset each time that it iterates through. So it's not actually changing the value of i. It's actually being reset each time that the range is generated. So it will never equal 20. We have dead code in there. So we figure that out by just doing a quick print statement. One way to do this, though, is to do logging. Now, instead of doing print where it will print out to the console, you can then have a few more options of where that stuff gets directed to. So we start off by putting the logging initialization up in here. And we have it going to example.log. And then we have the debugging information in here. We put in a few things, beginning the loop and the end of the loop. And when the program has ended. So what does that look like? We're back first off to the silent running that we initially had. So we haven't really changed the output of the code. But now we have additional information about what the results are. So we can see that it started off the loop. And we see all the what was before printed out now is in the log file. And then we see that the loop ended and that it ended our program. So what does that get us? Well, number one, you have a nice, cheap, and cheerful way of turning debugging off and on. You can set log levels in there. So you don't have to modify all the code that's around there in order to figure out where to pull out the log messages. So in production code, some people will put a print statement or something in there. And then suddenly you get a whole bunch of print statements that go out to places where you don't know where they're supposed to go. Now you have control over where those go. You can figure out and filter out which ones are logged and which ones are silently ignored based on the logging level. So in here, I changed this. Instead of logging debug messages, it now logs info-related messages. So these will be turned off when it's all said and done. It is logged in here. But that's a debug. You're getting a little ahead of me on this. So we now have it back to semi-silent running, because the programming began and the programming ended. I'll talk a little bit more about logging and the logging levels in a little bit. But in order to change it back over to showing debug, what I can do is I can change this program. And there's different ways of doing this with configuration files. This is just one way to do this, where you change the program in one spot from info to debug and you get back all the information about what's in that loop again. So then we're back to here's info, here's all the debug messages, and here's the end of the program. So we can turn those on and off, it will. And again, you can do it via configuration files or changing the program itself. It doesn't really matter. You're not actually trying to change the actual code itself and forgetting where you have debug messages and where you have info messages. Let's try another example of this. Now, this is a program that will take and read a sum of numbers from a file and then add them all together. So let's take a look and see what happens here. I run the code and out comes an exception. Comes out and says unsupported operand types for int and string. OK, what's exactly happening here? Something's screwball is going on. So we could log and print all the data that's in there. That could be one way of doing this. However, we take a look at all the list of numbers. We've got 2 million and 1 numbers in there. Logging that would be a real pain in the butt. Number one, our output would be completely off. So there's another way to do this. And that would be to use, but we could print off the first element off of here just to see what exactly is in there. That's one way of doing that. Instead of logging it, just print the first one. Let's see what that does. So we have, this is the first number that's in there, and what looks like a line feed. I'm not entirely sure what's going on in here. But we're still getting our error out of there. So that sort of gave us some information, but not quite. Let's try and do a little bit better. We could throw in and figure out what the type is, and that will tell us, hmm, will that go give us? That will tell us, OK, we're getting a string in here. Well, we kind of knew that already. I mean, in this simple example, we kind of knew, OK, yeah, it's an int and a string that is trying to combine, can't do that. But print is pretty much a blunt instrument. It's only going to show you what the output, what is represented by a Python. So you may or may not get useful information out of there out of print, especially if you're printing objects or something like that. It's not going to give you really good information. Logging isn't going to do a whole lot of good either. So if I throw all this instrumentation in here and then log out the number, what am I going to get? Well, it'll come back and give me a debug. And it's basically the same as if I had printed it. It's just I can throw it into a debug log now. It's like, OK, that sort of gives us some information. But what really will help us out is the debugger. Let me show you how to invoke the debugger. I'm going to go a little fast on this piece, but keep in mind, this is pretty much the rest of the presentation is on the debugger. So I start off the debugger by using dash m, pdb, and then the sum of the numbers. I'm going to insert a breakpoint here where I'm getting the error. The sum is blah, blah, blah. And then I will tell it to continue. See is for continue. So I've listed out the code. This shows where the breakpoint is. You see the little b over here? That's the breakpoint. And then I'll show you what happens after I hit Continue. So it's going to print out the first number off of here. I'm sorry. I'm going to print off the list of the numbers in the first one. And our suspicion is confirmed. It's a string, and it's got a line feed at the very end of it. Now we have something useful. Now there's a cheat sheet if you want to follow along. I put it in a slide with the cheat sheet. Unfortunately, that's really tiny type. So if you wanted to take a look at that real quick and play along, go feel free. But the cheat sheet looks a little something like this. I'm sure all eagle eyes are out there like, yeah. These are all the commands that are available, or at least most of the good commands that are available in the Python debugger. The URI, again, is github.com slash nblock slash pdb dash cheat sheet. All right. So let's talk again about invoking the debugger. There's two good ways to invoke the debugger. You use the Python command, Python 3, whichever you choose, mostly 3, dash m, pdb, and then the file name that you're using. Or in the source code itself, and I'll show you an example of where this gets a little more interesting, you can do import pdb semicolon pdb.settrace. And what that will do, and what's nice about that is that will allow you to, in separate files, like instead of trying to figure out if we're doing breakpoints, which file is this actually in, you can just insert it into the file itself. So it's a cheap and cheerful way of getting a breakpoint pretty easily in other code. Temperature warning. Oh, fantastic. Please don't die. So we put in, instead of putting the breakpoint, we can put the import pdb and then pdb.settrace in the file itself. So I won't have to actually tell it, OK, this is where we're going to break. It will just automatically break. So if I can run the file directly, Python 3, some of numbers, and then boom, it pops over into here, and we're back to our pdb debugger. Back to debugging the code. So let's change this. Instead of just depending on the number, let's impet the integer. That should fix the problem, right? Let's fix this. And when we run it, uh-oh, we're still back and getting a small problem here. So I run it through, tell it to continue, and it comes back with a trace back that says, invalid literal for int with the base 10, Bob. Apparently, Bob signed his work. Thanks a lot, Bob. So this is to show a conditional breakpoint. And what we can do is we can say, OK, break at line 13 and show me when the number equals Bob with a line feed in it. So I continue. It runs through. I can list where exactly it is. Well, first off, I can print off. This is where the number. And yes, indeed, it broke at Bob, Bob with the backslash. And I can list exactly where that code is. So I can show, if someone wants to look over my shoulder and say, hey, why is that breaking out? Well, here's why it's breaking out. This is exactly what's going on in there. And that's inside the debugger itself. So someone left a stray Bob in the data. So how do we work around this? Well, one way to work around this is to put a try and accept in here. So we say, we have a value error. I don't know exactly what to do with a Bob in the data, but I can at least say, move out of that data. Get out of there. So move back over here. And now we can run completely through. And we see that the sum is some large number. Doesn't matter. We finally got our sum out of there. Well, we can also do one better with this. So we had logging before. What we can do with that logging is we can say, warning. OK, it's not necessarily an error. It's a warning. Or we could say it's an error if you wanted to call it an error. But it's a warning that says, receive non-integer input and then say what number, what was in here that caused that problem to occur. And that looks a little like this. We come back and say, oh, we got a warning. We received non-integer input. And we got a Bob in the data. Boom, there's our sum. So we haven't really changed the program materially. We just made it so that it's a little more resilient to errors. And we provided ourselves some breadcrumbs. Hey, take a look. Make sure that Bob doesn't sign the data anymore. And boom, there's our data. OK, that's a lot of Bob. So these are the logging levels that you can use. They're additive. So anything from debug level will do anything from below. Anything, I'm sorry, no, it'll go up. So anything from the debug level will log through. Anything from info level will log through. Warning, et cetera. So you can say, warning level is, I'm sorry, these are the values and whatnot. But it will allow you to determine what you're logging out of here and what you're showing in your log files at that point. Critical, I would say, is for things that are like you actually have screwed up the data, go fix it. Error level is like something is wrong. And this may be a breaking condition. Warning, it's a non-breaking condition. It's just something you need to be aware of. Info, again, info and debug. Not going to go too far into that. So what did we learn? We learned that we can invoke the debugger using dash mpdb, or we can use the import statement here to import pdb and then do a set trace there. We can set the break points in our code. We can use conditional break points in our code. And we can use logging to show helpful warnings and such when code shouldn't be executed. So let's debug some real computer code. Let it sink in a little bit here. This is the Fibonacci sequence. Every coding interview should have one. And what we'll do is we'll see what this code does. And we'll bring up the debugger to do this. And I'll set a break point at line seven, because that's really where a lot of the interesting stuff happens in here. And one thing that we can do with that is we can put repetitious commands in there. So one thing that the Fibonacci sequence has is a lot of recursion. So what I may want to look at is the step every time that it's recursed into it. And I also want to look at the arguments that are passed to that particular function. So I can set a command on the break point number one. Tell it, show the back trace, or where it is. These are synonymous. And then show the arguments that are there. And then end out of that. So this code will be run every time that it enters in there in the debugger. And I'll also set a break point at line five and line three and do the same thing for those. What does that look like? So if I do a list, it will show the break points at each of these positions. If I do a B, it will show these break points as well. And so it says this is where the line is at. It's over there, of course. But it's also enabled, it's keeping it, et cetera. So when I continue on the code behind the temperature warning there, it will print out the back trace and the argument that's been passed along with it. So I'm listing it out. This is where it's broken, or this is where the break point is. This is all the back trace that occurred, the calls that occurred in order to get to this point. And that's the argument that's been passed to it. Time passes. I'm not going to show you every single execution of this. But as we get further on down, this is what n equals 2 is. And there's a whole back trace call of all the levels that it has done prior to that. And I'll show you how to show all those levels later on. But we can continue with this. And as we continue with this, you'll see down here we have the return 1. So we've gotten to the point where n equals 1 and it's returning 1 off of there. I don't think I actually show the arguments that are in here. But that's where it's got to that point. And then you get to the other one where it returns 0. And then all those Fibonacci things come back and show you the sum. So let me show you real quick how to move through the stack. And this is a little easier to see as an interactive piece. So let me break at line 7. I'm not going to actually show the arguments, but I'll do the back trace. So this is the first call for here. And it's args are n equals 10. We can continue on down. So see, this is the line number that's at. The args are 6. If I do a back trace, it'll show you the call stack for each of those calls that have been made to the Fibonacci sequence. What I can do is I can say, OK, I want to go up the stack. See what these arguments are. This is arguments n equals 8. I can go back down the stack. This one is args are equal to 7. And I go back down the stack. And what's nice about this is that as you call different functions, you can see where you came from in that call stack and get back to that particular point. What's handy, too, is if you accidentally, and I'll show you a little bit of this later on, if you accidentally get into Python system calls, you have a way to get back to where you need to go and see where that came from. OK. Let me get out of that. We did the handy demo. So what have we learned so far? We've learned that breakpoints can have commands associated with them using the commands and then the breakpoint number. We've learned that you can see what arguments were used in order to call that particular function. There's a synonymous where that shows the back trace as well. But BT and where are basically the same command. And that'll show you the stack trace. C is for continuing after reaching a particular breakpoint. And U and D will move you up and down through the stack. Let's do a few more commands. All right, this is a pointless loop, as it is so named. What it will do is it'll do a range, the sum of all numbers, basically, till n occurs. OK. So let's talk about next and step. And for that, I'm going to do a quick demo of it. But next runs the next line of code directly below it. Yes, directly below it. And then s will step into a function. So for our pointless loop, and we'll do a demo of that. OK. If I just do a run of this, so let's say I do n for next. It will, first off, go here where it says def n main. So we'll show where the definition is. And it will then run the main call here. And then it will promptly end. So you don't actually see all the execution of that function up there. In order to get that, you need to use a step. So what we can do is use an s to step into main. You can step into the pointless loop section here. And then step through each of the iterations of this pointless loop as we're going through. OK. And then if you wanted to get through all that stuff, you have to basically go through all the iterations of the pointless loop. And we'll show how to get around that in a little easier ways of doing that. One easy way to do that is a conditional break point. So instead of running each of those iterations of the pointless loop, let's say that we wanted to know when the sum of number sum is greater than 100. That's a little more interesting than all the iterations to get up into that point. So what I can do at the top here, so you see the position, I'll say break at line 6 where the sum is added, and show me break at this point when it's greater than 100. So I can also do commands. I'll say pretty print i and pretty print the number sum off of here, and then continue on through. And what you'll see is if I can hit continue, it'll show i, which is at 15, and show the number sum, which is 105, which happens to be greater than 100. Thank you, computer. And then it brings us back to the prompt. So we can continue from that point, and it'll show us, yes, indeed, this is now hitting it. It's continuing. It continues to hit that break point, because number sum continues to be greater than 100, in this case. And right here, if I take a look at the break point itself, it'll show the break point, and it'll also give us additional information. It'll say, stop only if number sum is greater than 100, and that break point has been hit 17 times already. So previously, the 14 other times, it didn't trigger the break point. This time, it did, and continually forward. It will keep hitting that, and show you every iteration of that. You can also disable break points. After a while, some break points get a little painful to deal with. So in order to disable that, you tell it disable, and then the break point number. So in this case, if I disable one, what will that get us? So I keep iterating through. OK, I'm tired of it. 253, yes, indeed. Number sum continues to still be greater than 100. So if I hit disable, it will say disable this break point, and it will show the break point has been already hit 23 times, but it has been disabled. The enable is no. It still keeps it around, but it is no longer enabled. So if I want to, later on, have it re-enabled at some point, I could do so. In this case, it would be kind of pointless to do so, but I could do that if I wanted to. There's also the idea of a temporary break point. So let's say I wanted to, at number sum, hit it only once, and then never hit it again. Don't tell me anymore I've hit my point. Boom, done. So I can do T break at line 6. Tell it number sum greater than 100. Boom. So it will only stop if number sum is 100. It will delete it afterward, still enable, but it will delete when it's all said and done. If I hit continue, it hits that, shows me the number sum. Now, if I take a look at the break points, it no longer exists. I no longer have to keep track of it. So what have we learned so far? Learned about next and step and how to navigate through the code, using next and step. Next being the next line of code, step being step into a function. We learned that break points can be conditionally triggered. We also said that C can also continue from where we left off in the code, so we can continue from further on. You can disable break points so they no longer fire and temporary break points may be set so that they're deleted once they're hit. So Python 3 adds a few goodies to our debugging toolkit. And one of those nice goodies, remember our pointless loop exercise and the number sum, one of those nice goodies is the display command. They changed how display works. In Python 2, I think it would only show you what it was at that particular point. They added some functionality where it acts like watch in the GNU debugger, where it will show you the previous state and then the current state. So if we go into our pointless loop and tell it, so we boom, boom, boom, boom, boom, through, let me get to the point where it actually is doing the display. Hello. There we are. PDB display number sum. At this particular point in the definition, it does not know what number sum is. It has not been defined yet. So it gives us a number error, a name error. It's not been defined quite yet. But if I hit the next line, there's the definition for number sum equals 0. So now it will show that 0 is the current value and then the old value was not defined. So you know at what point it's been defined. And as you traverse through this lovely pointless loop, you can see, OK, this is the 1. The old value was 0. Now it's 3. The old value was 1. So it gives you a really good insight into how variables are changing as it's moving along. Now there's another debugger called PDB++. And what PDB++ gives you is really cool. So I'm going to work at PDB++. Work on PDB++ because I'm not on Rick's computer. And what this is, PDB++ is a module that you install that overlays the Python debugger. And the reason I'm not going to show you how to install it is because A, conference Wi-Fi, and B, all you need to do is do a pip install, pdbpp, and it will install itself. But you invoke it the same way that you invoke PDB, normal PDB. Don't do it on the PDB cheat sheet. Let's do it on pointless code, pointless loop. The first thing that you may notice is that it's polarized. Another thing you'll notice is that it does syntax highlighting as well. The third thing that you will notice, L is for list. Yes? You were in the LL earlier. LL does this. I didn't know it did this. Hello, good morning, where it will show you the line and it will highlight it for you. So yes, LL is a little longer list, I believe. I'm not exactly sure what it's called, but I don't know, I'm just happy that it did that. So let's go into our code here and do display. Why is it not doing? One other thing that it does and that I wish it would do for me is show me, all right, let's start over. What I wish it would show you right now is there's tab completion as well, which is very handy. So you don't have to type things over and over again. Yeah, so display number sum. So it has a nicer way of showing this particular syntax where it shows, OK, this moved over to this particular position. One thing about PDB++ that's a little annoying is you can type in a variable and it will display it like this. However, if you are like me and you use n as a variable name, you will get this, which will display happily the value that's in n. It will not actually move next. So you have to then type next in order to get it to move over. I can't tell you how many times that has bitten me. So, and I think there's a way to, yeah, well, or be more judicious about using the debugger and the variable n. Or just learn how to type next all the time, your choice. Anyways, that's one caveat. Exactly. So here's some helpful links. You have the Python module of the week talked about PDB at length. There's the PDB cheat sheet as well, which I mentioned earlier. There's also documentation for PDB itself at the Python docs. You want to learn more about the logging module, which you should. That is over at the Python module of the week, logging module as well, and also at the docs at Python.org. And PDB++ is over at PyPI, Python.org, or PDBPP. I know I covered a lot. Is there any questions? Yes. I don't know offhand. Oh, sorry. Yes, thank you for reminding me. I know it's going to forget. Can you step into generator expressions, iterators, and lambdas? I don't know offhand if you can do that. I've not used a whole lot of those in practice. I don't know if it would get you that much actually stepping into those. It'd be something to check out, for sure. Yes. The question is, is PDB++ better than IPDB? I have enjoyed PDB++ a little more than IPDB. I used to use a lot of IPDB a lot. IPDB ships with IPython as well. I found PDB++ a little easier to invoke than IPDB. IPDB wants to be invoked as like PDB, and then your Python script. So it's IPDB, and then your Python script. I found that using Python 3, and then dash m, and then the module name, or the file name, a little easier to cope with. So yes. They are available on my GitHub, which is available off of my website under the Projects tab. OK? Yes. The question is, if you have a visual debugger, how does this help you compared to using it on the command line? That's what I'm understanding. The reason I talk about the command line is that a lot of these use the same patterns for the command line and individual debuggers. And the other thing is, I don't know all of the various debuggers. I'm more of a command line person. So they may have some of the same functionality available to you. And they may be in different keystrokes as well. I'm entirely sure. This is something that you can use in pretty much any environment. So if you're in a Linux environment, a Windows environment, Mac, this should be pretty portable as opposed to your IDE. So that's why I'm showing you this way of doing it. Hopefully that will help later on. Yes. How do you attach to a running process? That is a very good question. I don't know offhand. I think there are ways to do that. I would have to check the documentation to see how to do that. Yes? A couple of quick questions with Turbo Pascal at all in the past. Have I worked with Turbo Pascal in the past? I used some of those IDE type things in the past. Yes? So my final question there. The follow-up is how close is it to Turbo Pascal's debugger? In some ways, there's another variant called PUDB, which is much closer to Turbo Pascal and how it uses it because it will show you a graphical end-curses version of where everything is located. If you're much more familiar with that, I'd say that would be one approach to take. This is a little closer to things like GDB, the GNU debugger. It's very close, and a lot of the keystrokes are very similar in that. Any other questions? Yes? Directly to the debugger? Right. Is there a convention for how to do that? The question is, is there a convention for, if you have a failing test, will it automatically bring up the debugger? I think it depends on the testing framework. I think PyTest has built-in support for it. I think NOS also had support for it as well. I don't remember offhand if it does. 15 minutes. Goodness. Sorry, it didn't mean to blow through all this stuff. Any other questions? Yeah? I noticed there are a bunch of mice on your shirt. Yes. I look very familiar. Is it related to that author? OK, so the question is, I have this shirt that has for Python 201. Am I related to the author? No. I just happened to be part of the Kickstarter, and I thought that the cover was cool. Any other questions? Where's the slide for GitHub? So first off, thank you. Second off, the website is at dcafbad.net, d-e-c-a-f b-a-d dot net. Under there, there's a tab for projects. And under there, there's a GitHub repo. But there's also a list of videos. And in those videos, I had a link to the slides under there. So hopefully it's a little obvious to find if it's not some of you know at this email address and say, hey, I'm lost. And hopefully you can find it from there. Any other questions? Yes? It does convert to decimal. Please do not ask me for the actual number. I'd have to look at a calculator for it. But yeah, it would be it's not 0xdcafbad.net. But yes, it does translate to a hex number. Do it, do it, do it. Oh, god. Bring up free 42. All right. Do it in Python. Thanks. Thank you very much. Dance, dance for me. And of course, I'm completely blanking on how to convert this now. All right. Yes, it does convert, though. Any other questions? Great. Thank you very much. I appreciate it.