Whether you are developing Python, Java, JavaScript programs, or writing C++, PHP, GO, Ruby code, you cannot do without try … except exception handling.
As for why, it's all for the stable operation of the program/product/project.
If a program project, or to a smaller extent a file, does not have a try-except handling, then this program is vulnerable, unstable, and a program that can easily crash.
In code development, especially when developing framework projects, multiple modules (files) often call each other, either in one direction or bidirectionally. In such cases, if an exception occurs in the program, try-except only prints information based on the current location (I mean Except as e), making it difficult to locate the module call, i.e., the location of the source file. What I’m sharing today is how to locate the source file and line number.
Python try-except example:
In this example, we encapsulate the division operation code into a function named `divide`. The first parameter `a` of the function represents the dividend, and the second parameter `b` represents the divisor. In the function body, we use a try except statement to handle the division-by-zero exception. If the program does not encounter a `ZeroDivisionError` exception, the function will successfully compute the result and return it. If the program encounters a `ZeroDivisionError` exception, we will output the specific error message and return `None`.
```python
def divide(a, b):
try:
c = a / b
return c
except ZeroDivisionError as e:
print(e)
return None
# Call the function to perform division
result = divide(10, 0)
print(result)
```
Output result:
```
Divide by zero error
None
```
Locate the source file (error source)
Most of the time, in large projects or complex programs, simply printing `e` only displays basic error information. Sometimes it is difficult to pinpoint which file or which line of code the error occurred in based solely on this information. In such cases, a crude debugging method is to remove the try-except block and run the program again, letting it crash to reveal the specific details.
A relatively quick and convenient way to locate error information is to pinpoint the specific line and file where the error occurred. In fact, we can utilize the error object `e`, which has relevant properties that can be called.
```python
def divide(a, b):
try:
c = a / b
return c
except ZeroDivisionError as e:
# Print specific file name and line number via e's attribute
print(f"Error Occurred At, File: {e.__traceback__.tb_frame.f_code.co_filename}, Line: {e.__traceback__.tb_lineno}")
print("Divide by zero error")
return None
# Call function to perform division operation
result = divide(10, 0)
print(result)
```
Output result:
```
Error Occurred At, File: file path, Line: error line number
Divide by zero error
None
```
Or you can also use the built-in module `traceback` to achieve this.
```python
import traceback
import sys
def divide(a, b):
try:
c = a / b
return c
except ZeroDivisionError as e:
# Implement with traceback
tb_list = traceback.extract_tb(sys.exc_info()[2])
filename, line_no, func_name, code_str = tb_list[-1]
print(f"Error Occurred At, File: {filename}, Line: {line_no}")
print("Divide by zero error")
return None
#Call function to perform division operation
result = divide(10, 0)
print(result)
```
Advanced Usage
At this point, there are still some issues: if the source error file is a third-party library or built-in library, such as a file located in the site-packages of the Python environment, then printing it out is not very useful. What we want to print is the error location of our own code, not the location information of errors from built-in modules or installed third-party modules. Only by finding the location of the custom code can we optimize and fix it.
You can use `traceback.extract_stack()` to replace `traceback.extract_tb()` in order to exclude the source code locations of third-party libraries.
```python
import traceback
import sys
def divide(a, b):
try:
c = a / b
return c
except ZeroDivisionError as e:
# Implement with traceback
tb_list = traceback.extract_stack()[:-1] # The last item is the location of the current call, not where the exception occurred
filename, line_no, func_name, code_str = tb_list[-1]
print(f"Error Occurred At, File: {filename}, Line: {line_no}")
print("Divide by zero error")
return None
# Call the function to perform division operation
result = divide(10, 0)
print(result)
```
Use the `traceback.extract_stack()` method to obtain the complete stack trace information of where the exception occurred. Then, use list slicing to remove the last element, as this element represents the current call location rather than where the exception occurred. The last element typically contains the location of your calling code, so you do not want to include it. Then, extract the last source code file name, line number, function name, and code line from the available stack information to determine where the exception occurred.
This method will not include the source code locations of third-party libraries and will only extract source code locations related to your code.
You can also use the `currentframe()` function from the `inspect` module in the Python standard library to get the current frame. Then, you can use the `traceback.extract_stack()` method to extract stack information, and then use the stack information to progressively compare source file paths to find the source file path related to the current project.
```python
import os
import inspect
import traceback
def get_project_traceback():
# Get current frame
frame = inspect.currentframe()
# Extract stack information (the first two elements are the positions of filter and get_project_traceback functions themselves)
tb_list = traceback.extract_stack(frame)[2:]
# Find the source file path related to the current project
for filename, line_no, func_name, code_str in reversed(tb_list):
if os.getcwd() in filename:
return filename, line_no, func_name, code_str
# If none are found, return the last source file
filename, line_no, func_name, code_str = tb_list[-1]
return filename, line_no, func_name, code_str
def divide(a, b):
try:
c = a / b
return c
except ZeroDivisionError as e:
filename, line_no, func_name, code_str = get_project_traceback()
print(f"Error Occurred At, File: {filename}, Line: {line_no}")
print("Divide by zero error")
return None
# Call the function to perform division operation
result = divide(10, 0)
print(result)
```
In the above code, the `get_project_traceback` function first uses the `inspect.currentframe()` function to obtain the current frame, then uses the `traceback.extract_stack(frame)` method to extract stack information. The first two elements are the positions of the filter and the `get_project_traceback` function itself, so it starts from the third element. Next, the function uses `os.getcwd()` to get the current project path, and compares each source file path one by one to find the source file path related to the current project. If none are found, it returns the last source file.
In the main code block, call the `get_project_traceback` function to obtain stack information related to the current project. When an exception occurs, this code segment retrieves the exception information and uses the `get_project_traceback` function to print the stack information related to the current project.
This article provides readers with practical tips for handling exceptions in Python, particularly an in-depth discussion on locating the source of errors and improving program stability. By using `try-except` statements and related tools, program crashes can be effectively avoided, thereby enhancing development efficiency and program quality.
