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.
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__.py
only. )
__init__.py
Also 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.FTPError
When 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.FTPError
not,urllib83.errors.FTPError
Is displayed.
This is a problem,urllib83.FTPError
It 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 module
urllib83.FTPError
Will 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 module
urllib83.errors.FTPError
When I actually encountered the indication, it was mentioned in the urllib83 module documentationurllib83.FTPError
Isn't the cost to know that is too high? From the beginning,urllib83.FTPError
Is easier to understand and user-friendly, so you should do something about it.
That is.
##Solution##
urllib83.FTPError
I tried various ways to make it displayed, but in the enderrors.py
Exception 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.FTPError
Is displayed.
__name__
Rewriting attributes is not common, and if you write complex code,urllib83/__init__.py
There 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