Make the display of Python module exceptions easier to understand

Recently, I was wondering how to make the display of module exceptions in Python easier to understand, but I found the best way, so I summarized it. First, let's review the general method of defining module exceptions.

General method

As a concrete situation, let's consider the situation of writing the urllib83 module consisting of the function ʻurllib83.urlopen ()` in Python 3.3. urllib83 means the 83rd urllib module, but I'm sorry if a standard module with the same name appears in the future.

Aside from that, let's assume that this module consists of the following three files.

urllib83/__init__.py
         ftp.py
         http.py

If you write ʻurllib83.urlopen () `as much as you can, it should look like this.

__init__.py


from .ftp import ftpopen
from .http import httpopen


def urlopen(url):
    if url.startswith("ftp://"):
        return ftpopen(url)
    else:
        return httpopen(url)

Next, let's assume that this module has exception classes ʻurllib83.FTPError and ʻurllib83.HTTPError, both of which inherit from the base exception class ʻurllib83.Urllib83Error. If you implement these three exception classes obediently, in ʻurllib83 / __ init__.py,

urllib83/__init__.py


class Urllib83Error(Exception):
    pass


class FTPError(Urllib83Error):
    pass


class HTTPError(Urllib83Error):
    pass

I think that it will be defined as, but there is one problem with this method. ʻUrllib83.FTPError is naturally raised in ʻurllib83 / ftp.py, so ftp.py will raise FTPError,

urllib83/ftp.py


from . import FTPError

def ftpopen(url):
    ...
    raise FTPError

It is used by importing like, but as mentioned above, __init__.py importsftpopen ()from ftp.py. At this rate, __init__.py and ftp.py will import each other, causing the problem of circular import.

So, in general, to avoid this problem, add a file called ʻurllib83 / errors.py` and make it as follows.

urllib83/errors.py


class Urllib83Error(Exception):
    pass

class FTPError(Urllib83Error):
    pass

class HTTPError(Urllib83Error):
    pass

Each file other than ʻerrors.py`

__init__.py


from .errors import Urllib83Error, FTPError, HTTPError

__all__ = ['Urllib83Error', 'FTPError', 'HTTPError']

Import the required exception class as in. (note:__all__Needs the line__init__.pyonly. )

__init__.pyAlso imports the exception class, so the side using the urllib83 module also

client.py


from urllib83 import urlopen, Urllib83Error

try:
    urlopen(url)
except Urllib83Error:
    pass

You can use module exceptions like this. There is nothing wrong with this.

##What's wrong?## What I was interested in was that the side using the urllib83 module tried...Without using the except statementurllib83.urlopen() Is called. urllib83.FTPErrorWhen occurs, the traceback is displayed as shown below.

python


>>> import urllib83
>>> urllib83.urlopen("ftp://")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./urllib83/__init__.py", line 10, in urlopen
    return ftpopen(url)
  File "./urllib83/ftp.py", line 4, in ftpopen
    raise FTPError
urllib83.errors.FTPError
>>>

In the last lineurllib83.FTPErrornot,urllib83.errors.FTPErrorIs displayed. This is a problem,urllib83.FTPErrorIt is my opinion that it should be displayed.

However, the general view of Pythonista people is that "I have no idea where the problem lies in this display." The reason is

The documentation for the urllib83 moduleurllib83.FTPErrorWill occur, and the user will say in the except clause,except urllib83.FTPError:You can supplement the exception that occurred by writing. The problem doesn't exist anywhere as it works according to the specs in the documentation

is what they said. On the other hand, my counterargument to that is

Users of the urllib83 moduleurllib83.errors.FTPErrorWhen I actually encountered the indication, it was mentioned in the urllib83 module documentationurllib83.FTPErrorIsn't the cost to know that is too high? From the beginning,urllib83.FTPErrorIs easier to understand and user-friendly, so you should do something about it.

That is.

##Solution## urllib83.FTPErrorI tried various ways to make it displayed, but in the enderrors.pyException class with

errors.py


_SAVED_NAME = __name__
__name__ = __package__
# __name__ = "urllib83" #Click here for Python 3.2


class Urllib83Error(Exception):
    pass


class FTPError(Urllib83Error):
    pass


class HTTPError(Urllib83Error):
    pass

__name__ = _SAVED_NAME

I came to the conclusion that it seems best to define it as.

The point is__name__It is to temporarily rewrite the contents of the attribute and restore it when the definition of the exception class is finished. When an exception occurs, the following is displayed.

python


>>> import urllib83
>>> urllib83.urlopen("ftp://")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./urllib83/__init__.py", line 10, in urlopen
    return ftpopen(url)
  File "./urllib83/ftp.py", line 4, in ftpopen
    raise FTPError
urllib83.FTPError
>>>

Properlyurllib83.FTPErrorIs displayed.

__name__Rewriting attributes is not common, and if you write complex code,urllib83/__init__.pyThere may be a difference compared to when defined in, so__name__We strongly recommend that you avoid complicated processing as much as possible while rewriting attributes. then,__name__Don't forget to undo the attributes.

##Finally## So far, we haven't found any problems with this method, but if you notice any problems, please let us know in the comments. Personally, isn't there something wrong with the integrated development environment? I am particularly worried about that.

Recommended Posts

Make the display of Python module exceptions easier to understand
14 quizzes to understand the surprisingly confusing scope of Python
Color Python errors (stack traces) to make them easier to understand
2015-11-26 python> Display the function list of the module> import math> dir (math)
[Cloudian # 9] Try to display the metadata of the object in Python (boto3)
[python] A note that started to understand the behavior of matplotlib.pyplot
Tips to make Python here-documents easier to read
[Python] How to display random numbers (random module)
[Python3] Understand the basics of Beautiful Soup
Pass the path of the imported python module
Make a relation diagram of Python module
[Python] Understand the content of error messages
Check the path of the Python imported module
[Python3] Understand the basics of file operations
[Python] How to change the date format (display format)
Make a copy of the list in Python
[Python] A rough understanding of the logging module
Try using the collections module (ChainMap) of python3
Knowledge notes needed to understand the Python framework
I want to display the progress in Python!
the zen of Python
Easy way to check the source of Python modules
python beginners tried to predict the number of criminals
The wall of changing the Django service from Python 2.7 to Python 3
Try to make a Python module in C language
How to get the number of digits in Python
A memo to visually understand the axis of pandas.Panel
How to use the Raspberry Pi relay module Python
[python] Get the list of classes defined in the module
[python] option to turn off the output of click.progressbar
I want to fully understand the basics of Bokeh
Let's use the Python version of the Confluence API module.
Write data to KINTONE using the Python requests module
[Python] Summary of how to specify the color of the figure
How to access the global variable of the imported module
Understand the status of data loss --Python vs. R
[Introduction to Python] Basic usage of the library matplotlib
To do the equivalent of Ruby's ObjectSpace._id2ref in Python
Completely translated the site of "The Hitchhiker's Guide to Python"
Python OpenCV tried to display the image in text.
Python Note: The mystery of assigning a variable to a variable
Deploy the management page to production to make maintenance easier.
I tried to summarize the string operations of Python
Tips for Python beginners to use the Scikit-image example for themselves 7 How to make a module
The background of the characters in the text image is overexposed to make it easier to read.
A memo of misunderstanding when trying to load the entire self-made module with Python3
I tried to find the entropy of the image with python
[Python] Change the Cache-Control of the object uploaded to Cloud Storage
Try to get the function list of Python> os package
Towards the retirement of Python2
Try to understand Python self
The first API to make with python Djnago REST framework
Minimum knowledge to get started with the Python logging module
About the Python module venv
You who color the log to make it easier to see
The story of introducing jedi (python auto-completion package) to emacs
Automatic update of Python module
View using the python module of Nifty Cloud mobile backend
[Python] I want to make a 3D scatter plot of the epicenter with Cartopy + Matplotlib!
About the ease of Python
[Super easy! ] How to display the contents of dictionaries and lists including Japanese in Python