Ultimate Guide to Python Debugging

栏目: IT技术 · 发布时间: 3年前

内容简介:Even if you write clear and readable code, even if you cover your code with tests, even if you are very experienced developer, weird bugs will inevitably appear and you will need to debug them in some way. Lots of people resort to just using bunch ofIf you

Let’s explore the Art of debugging using Python logging, tracebacks, decorators and more…

Jun 1 ·6min read

Even if you write clear and readable code, even if you cover your code with tests, even if you are very experienced developer, weird bugs will inevitably appear and you will need to debug them in some way. Lots of people resort to just using bunch of print statements to see what's happening in their code. This approach is far from ideal and there are much better ways to find out what's wrong with your code, some of which we will explore in this article.

Ultimate Guide to Python Debugging

Photo by Danny Mc on Unsplash

Logging is a Must

If you write application without some sort of logging setup you will eventually come to regret it. Not having any logs from your application can make it very difficult to troubleshoot any bugs. Luckily — in Python — setting up basic logger is very simple:

This is all you need to start writing logs to file which will look something like this (you can find path to the file using logging.getLoggerClass().root.handlers[0].baseFilename ):

This setup might seem like it’s good enough (and often it is), but having well configured, formatted, readable logs can make your life so much easier. One way to improve and expand the config is to use .ini or .yaml file that gets read by logger. As an example of what you could do in your config:

Having this kind of extensive config inside you python code would be hard to navigate, edit and maintain. Keeping things in YAML file makes it much easier to set up and tweak multiple loggers with very specific settings like the ones above.

If you are wondering where all these config fields came from, these are documented here and most of them are just keyword arguments as shown in the first example.

So, having the config in the file now, means that we need to load is somehow. The simplest way to do so with YAML files:

Python logger doesn’t actually support YAML files directly, but it supports dictionary configs, which can be easily created from YAML using yaml.safe_load . If you are inclined to rather use old .ini files, then I just want to point out that using dictionary configs is the recommended approach for new application as per docs . For more examples, check out the logging cookbook .

Logging Decorators

Continuing with the previous logging tip, you might get into a situation where you need log calls of some buggy function. Instead of modifying the body of said function, you could employ logging decorator which would log every function call with specific log level and an optional message. Let’s look at the decorator:

Not gonna lie, this one might take a bit to wrap your head around (you might want to just copy-paste it and use it). The idea here is that log function takes the arguments and makes them available to inner wrapper function. These arguments are then made adjustable by adding the accessor functions, which are attached to the decorator. As for the functools.wraps decorator - if we didn't use it here, name of the function ( func.__name__ ) would get overwritten by name of the decorator. But that's a problem because we want to print the name. This gets solved by functools.wraps as it copies function name, docstring and arguments list onto the decorator function.

Anyway, this is the output of above code. Pretty neat, right?

__repr__ For More Readable Logs

Easy improvement to your code that makes it more debuggable is adding __repr__ method to your classes. In case you're not familiar with this method - all it does is return string representation of an instance of a class. Best practice with __repr__ method is to output text that could be used to recreate the instance. For example:

If representing object as shown above is not desirable or not possible, good alternative is to use representation using <...> , e.g. <_io.TextIOWrapper name='somefile.txt' mode='w' encoding='UTF-8'> .

Apart from __repr__ , it's also a good idea to implement __str__ method which is by default used when print(instance) is called. With these 2 methods you can get lots of information just by printing your variables.

__missing__ Dunder Method For Dictionaries

If you for whatever reason need to implement custom dictionary class, then you can expect some bugs arising from KeyError s when you try to access some key that doesn't actually exist. To avoid having to poke around in the code and see which key is missing, you could implement special __missing__ method, which is called every time KeyError is raised.

The implementation above is very simple and only returns and logs message with the missing key , but you could also log other valuable information to give you more context as to what went wrong in the code.

Debugging Crashing Application

If your application crashes before you get a chance to see what is going on in it, you might find this trick quite useful.

Running the application with -i argument ( python3 -i app.py ) causes it to start interactive shell as soon as the program exits. At that point you can inspect variables and functions.

If that’s not good enough, you can bring a bigger hammer — pdb - Python Debugger . pdb has quite a few features which would warrant an article on its own. But here is an example and a rundown of the most important bits. Let's first see our little crashing script:

Now, if we run it with -i argument, we get a chance to debug it:

Debugging session above shows very briefly what you could do with pdb . After program terminates we enter interactive debugging session. First, we import pdb and start the debugger. At that point, we can use all the pdb commands. As an example above, we print variable using p command and list code using l command. Most of the time you would probably want to set breakpoint which you can do with b LINE_NO and run the program until the breakpoint is hit ( c ) and then continue stepping through the function with s , optionally maybe printing stacktrace with w . For a full listing of commands you can go over to pdb docs .

Inspecting Stack Traces

Let’s say your code is for example Flask or Django application running on remote server where you can’t get interactive debugging session. In that case you can use traceback and sys packages to get more insight on what's failing in your code:

When run, the code above will print the last exception that was raised. Apart from printing exceptions, you can also use traceback package to print stacktrace ( traceback.print_stack() ) or extract raw stack frame, format it and inspect it further ( traceback.format_list(traceback.extract_stack()) ).

Reloading Modules During Debugging

Sometimes you might be debugging or experimenting with some function in interactive shell and making frequent changes to it. To make the cycle of running/testing and modifying easier, you can run importlib.reload(module) to avoid having to restart the interactive session after every change:

This tip is more about efficiency than debugging. It’s always nice to be able to skip a few unnecessary steps and make your workflow faster and more efficient. In general, reloading modules from time to time is a good idea, as it can help you avoid trying to debug code that was already modified a bunch of times in the meantime.

Debugging is an Art.

Conclusion

Most of the time, what programming really is — is just a lot of trial and error. Debugging, on the other hand, is — in my opinion — an Art and becoming good at it takes time and experience — the more you know the libraries or framework you use, the easier it gets. Tips and tricks listed above can make your debugging a bit more efficient and faster, but apart from these Python-specific tools, you might want to familiarize yourself with general approaches to debugging — for example, The Art of Debugging by Remy Sharp.

If you liked this article you should check out other of my Python articles below!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

产品经理手册(原书第4版)(白金版)

产品经理手册(原书第4版)(白金版)

[美] 琳达·哥乔斯(Linda Gorchels) / 祝亚雄、冯华丽、金骆彬 / 机械工业出版社 / 2017-8 / 65.00

产品经理的职责起点是新产品开发,贯穿产品生命周期的全过程。本书按上下游产品管理进行组织。 在上游的新产品开发流程中,作者阐述了如何从市场、产品、行业、公司的角度规划企划方案,并获得老板、销售部、运营部的资源支持,推进新产品的项目流程,实现所有目标,制定和实施新产品发布。 下游产品的管理核心在于生命周期的管理,营销更是生命周期管理的重中之重。产品经理如何让产品满足客户需求,让客户获得对产......一起来看看 《产品经理手册(原书第4版)(白金版)》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具