Raising builtin exception with default message in python - python

I'm trying to implement a method that returns an error whenever a certain directory does not exist.
Rather than doing raise OSError("Directory does not exist."), however, I want to use the builtint error message from OSError: OSError: [Errno 2] No such file or directory:. This is because I am raising the exception in the beginning of the method call, rather than later (which would invoke the same message from python, without any necessary raise).
Any pointers? (other than manually doing OSError("[Errno 2] No such file or directory: "))

import os
try:
open('foo')
except IOError as err:
print(err)
print(err.args)
print(err.filename)
produces
[Errno 2] No such file or directory: 'foo'
(2, 'No such file or directory')
foo
So, to generate an OSError with a similar message use
raise OSError(2, 'No such file or directory', 'foo')

To get the error message for a given error code, you might want to use os.strerror:
>>> os.strerror(2)
'No such file or directory'
Also, you might want to use errno module to use the standard abbreviations for those errors:
>>> errno.ENOENT
2
>>> os.strerror(errno.ENOENT)
'No such file or directory'

I think that "exception" is the Python language term for what you are calling "error". So use this term as you search for more information.
You might find it useful to read the Python Standard Library documentation, "6. Built-in Exceptions".
OSError is one of the built-in exceptions. It's defined in the "Built-in Exceptions" section, which adds, "The errno attribute is a numeric error code from errno, and the strerror attribute is the corresponding string, as would be printed by the C function perror(). See the module errno, which contains names for the error codes defined by the underlying operating system."
Running this code:
raise OSError(42, "my exception string", "no_such_file.dat")
gives me this result:
Traceback (most recent call last):
Line 1, in <module>
raise OSError(42, "my exception string", "no_such_file.dat")
OSError: [Errno 42] my exception string: 'no_such_file.dat'
So, I think your code could do something like:
raise OSError(2, "No such file or directory", filename)

I think the real problem here is that you are probably doing a bunch of checks beforehand instead of just trying.
try:
[CODE]
except Exception:
[HANDLING CODE]
is much better than:
if [SPECIAL CASE]:
[HANDLING CODE]
elif [special case]:
[SPECIAL CASE]
[CODE]

try:
# ...
except OSError:
raise OSError("your answer")

Related

Is it possible to continue execution after error in Jupyter? [duplicate]

How do I print the error/exception in the except: block?
try:
...
except:
print(exception)
For Python 2.6 and later and Python 3.x:
except Exception as e: print(e)
For Python 2.5 and earlier, use:
except Exception,e: print str(e)
The traceback module provides methods for formatting and printing exceptions and their tracebacks, e.g. this would print exception like the default handler does:
import traceback
try:
1/0
except Exception:
traceback.print_exc()
Output:
Traceback (most recent call last):
File "C:\scripts\divide_by_zero.py", line 4, in <module>
1/0
ZeroDivisionError: division by zero
In Python 2.6 or greater it's a bit cleaner:
except Exception as e: print(e)
In older versions it's still quite readable:
except Exception, e: print e
Python 3: logging
Instead of using the basic print() function, the more flexible logging module can be used to log the exception. The logging module offers a lot extra functionality, for example, logging messages...
into a given log file, or
with timestamps and additional information about where the logging happened.
For more information check out the official documentation.
Usage
Logging an exception can be done with the module-level function logging.exception() like so:
import logging
try:
1/0
except BaseException:
logging.exception("An exception was thrown!")
Output
ERROR:root:An exception was thrown!
Traceback (most recent call last):
File ".../Desktop/test.py", line 4, in <module>
1/0
ZeroDivisionError: division by zero
Notes
the function logging.exception() should only be called from an exception handler
the logging module should not be used inside a logging handler to avoid a RecursionError (thanks #PrakharPandey)
Alternative log-levels
It's also possible to log the exception with another log level but still show the exception details by using the keyword argument exc_info=True, like so:
logging.critical("An exception was thrown!", exc_info=True)
logging.error ("An exception was thrown!", exc_info=True)
logging.warning ("An exception was thrown!", exc_info=True)
logging.info ("An exception was thrown!", exc_info=True)
logging.debug ("An exception was thrown!", exc_info=True)
# or the general form
logging.log(level, "An exception was thrown!", exc_info=True)
Name and description only
Of course, if you don't want the whole traceback but only some specific information (e.g., exception name and description), you can still use the logging module like so:
try:
1/0
except BaseException as exception:
logging.warning(f"Exception Name: {type(exception).__name__}")
logging.warning(f"Exception Desc: {exception}")
Output
WARNING:root:Exception Name: ZeroDivisionError
WARNING:root:Exception Desc: division by zero
(I was going to leave this as a comment on #jldupont's answer, but I don't have enough reputation.)
I've seen answers like #jldupont's answer in other places as well. FWIW, I think it's important to note that this:
except Exception as e:
print(e)
will print the error output to sys.stdout by default. A more appropriate approach to error handling in general would be:
except Exception as e:
print(e, file=sys.stderr)
(Note that you have to import sys for this to work.) This way, the error is printed to STDERR instead of STDOUT, which allows for the proper output parsing/redirection/etc. I understand that the question was strictly about 'printing an error', but it seems important to point out the best practice here rather than leave out this detail that could lead to non-standard code for anyone who doesn't eventually learn better.
I haven't used the traceback module as in Cat Plus Plus's answer, and maybe that's the best way, but I thought I'd throw this out there.
In case you want to pass error strings, here is an example from Errors and Exceptions (Python 2.6)
>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print type(inst) # the exception instance
... print inst.args # arguments stored in .args
... print inst # __str__ allows args to printed directly
... x, y = inst # __getitem__ allows args to be unpacked directly
... print 'x =', x
... print 'y =', y
...
<type 'exceptions.Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
One has pretty much control on which information from the traceback to be displayed/logged when catching exceptions.
The code
with open("not_existing_file.txt", 'r') as text:
pass
would produce the following traceback:
Traceback (most recent call last):
File "exception_checks.py", line 19, in <module>
with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'
Print/Log the full traceback
As others already mentioned, you can catch the whole traceback by using the traceback module:
import traceback
try:
with open("not_existing_file.txt", 'r') as text:
pass
except Exception as exception:
traceback.print_exc()
This will produce the following output:
Traceback (most recent call last):
File "exception_checks.py", line 19, in <module>
with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'
You can achieve the same by using logging:
try:
with open("not_existing_file.txt", 'r') as text:
pass
except Exception as exception:
logger.error(exception, exc_info=True)
Output:
__main__: 2020-05-27 12:10:47-ERROR- [Errno 2] No such file or directory: 'not_existing_file.txt'
Traceback (most recent call last):
File "exception_checks.py", line 27, in <module>
with open("not_existing_file.txt", 'r') as text:
FileNotFoundError: [Errno 2] No such file or directory: 'not_existing_file.txt'
Print/log error name/message only
You might not be interested in the whole traceback, but only in the most important information, such as Exception name and Exception message, use:
try:
with open("not_existing_file.txt", 'r') as text:
pass
except Exception as exception:
print("Exception: {}".format(type(exception).__name__))
print("Exception message: {}".format(exception))
Output:
Exception: FileNotFoundError
Exception message: [Errno 2] No such file or directory: 'not_existing_file.txt'
Expanding off of the "except Exception as e:" solution here is a nice one liner which includes some additional info like the type of error and where it occurred.
try:
1/0
except Exception as e:
print(f"{type(e).__name__} at line {e.__traceback__.tb_lineno} of {__file__}: {e}")
Output:
ZeroDivisionError at line 48 of /Users/.../script.py: division by zero
Try this
try:
print("Hare Krishna!")
except Exception as er:
print(er)
One liner error raising can be done with assert statements if that's what you want to do. This will help you write statically fixable code and check errors early.
assert type(A) is type(""), "requires a string"
I would recommend using a try-except statement. Also, rather than using a print statement, a logging exception logs a message with level ERROR on the logger, which I find is more effective than a print output. This method should only be called from an exception handler, as it is here:
import logging
try:
*code goes here*
except BaseException:
logging.exception("*Error goes here*")
There's good documentation on this python page if you want to learn more about logging and debugging.

os.walk error of unhandled stopIteration

I have written a python script and wanted to debug it using eric ide. When I was running it, an error popped up saying unhandled StopIteration
My code snippet:
datasetname='../subdataset'
dirs=sorted(next(os.walk(datasetname))[1])
I am new to python and so, I don't really know how to fix this. Why is this error popping up and how do I fix it?
os.walk will generate file names in a directory tree walking it down. It will return the contents for every directory. Since it is a generator it will raise StopIteration exception when there's no more directories to iterate. Typically when you're using it in the for loop you don't see the exception but here you're calling next directly.
If you pass non-existing directory to it will immediately raise the the exception:
>>> next(os.walk('./doesnt-exist'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
You could modify your code to use for loop instead of next so that you wouldn't have to worry about the exception:
import os
for path, dirs, files in os.walk('./doesnt-exist'):
dirs = sorted(dirs)
break
The other option is to use try/except to catch the exception:
import os
try:
dirs = sorted(next(os.walk('./doesnt-exist')))
except StopIteration:
pass # Some error handling here

What is a good solution to a bogus OSError, 13 (EACCES) using Python on Windows

Here is the code:
def make_dir(dir_name):
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
try:
os.makedirs(dir_name)
except OSError, e:
print "ErrorNo: %s (%s)" % (e.errno, errno.errorcode[e.errno])
raise
IFF the directory already exists, I get the following:
ErrorNo: 13 (EACCES)
Traceback (most recent call last):
File "run_pnoise.py", line 167, in <module>
make_dir("prep_dat")
File "run_pnoise.py", line 88, in make_dir
os.makedirs(dir_name)
File "c:\Program Files (x86)\Python27\lib\os.py", line 157, in makedirs
mkdir(name, mode)
WindowsError: [Error 5] Access is denied: 'prep_dat'
If I run the program again, it works, indicating that the program indeed does have access to the directory(s), since the shutil.rmtree call is obviously working. I have come up with a workaround which I will post. However, is there a better explanation and/or workaround?
My assumption is that the shutil.rmtree call is returning before the OS is totally done deleting all of the files and subdirectories. Also, since the shutil.rmtree call is not throwing an exception, any EACCESS (13) error on the makedirs call is likely bogus. My attempt (as modified after Apalala's comment):
def make_dir(dir_name):
retry = True
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
while retry:
try:
# per Apalala, sleeping before the makedirs() eliminates the exception!
time.sleep(0.001)
os.makedirs(dir_name)
except OSError, e:
#time.sleep(0.001) # moved to before the makedirs() call
#print "ErrorNo: %s (%s)" % (e.errno, errno.errorcode[e.errno])
if e.errno != 13: # eaccess
raise
else:
retry = False
This seems to work reliably. There is the race condition problem mentioned in other posts, however that seems unlikely, and would probably result in a different exception.
I had the same problem, and this looks similar to my solution, except I was sleeping (0.1).
Can't you simply use an «except» statement ?
def make_dir(dir_name):
retry = True
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
while retry:
try:
os.makedirs(dir_name)
except OSError, e:
time.sleep(0.001)
if e.errno != 13: # eaccess
raise
except WindowsError:
# (do some stuff)
else:
retry = False
It should work, no ?!
In your previous post, you said the program raised a «WindowsError» exception:
WindowsError: [Error 5] Access is denied: 'prep_dat'
Your code can handle «OSError» exceptions using the «except» statement, but it cannot handle «WindowsError» exceptions... if you want to handle «WindowsError» exceptions, you must use the «except» statement like this:
except WindowsError:
# (do some stuff)
Note that you can handle any exception like this:
except Exception, e:
# this code will catch all raised exceptions. The variable «e» contains an instance of the raised exception.

Handling Windows-specific exceptions in platform-independent way

Consider the following Python exception:
[...]
f.extractall()
File "C:\Python26\lib\zipfile.py", line 935, in extractall
self.extract(zipinfo, path, pwd)
File "C:\Python26\lib\zipfile.py", line 923, in extract
return self._extract_member(member, path, pwd)
File "C:\Python26\lib\zipfile.py", line 957, in _extract_member
os.makedirs(upperdirs)
File "C:\Python26\lib\os.py", line 157, in makedirs
mkdir(name, mode)
WindowsError: [Error 267] The directory name is invalid: 'C:\\HOME\\as\
\pypm-infinitude\\scratch\\b\\slut-0.9.0.zip.work\\slut-0.9\\aux'
I want to handle this particular exception - i.e., WindowsError with error number 267. However, I cannot simply do the following:
try:
do()
except WindowsError, e:
...
Because that would not work on Unix systems where WindowsError is not even defined in the exceptions module.
Is there an elegant way to handle this error?
If you need to catch an exception with a name that might not always exist, then create it:
if not getattr(__builtins__, "WindowsError", None):
class WindowsError(OSError): pass
try:
do()
except WindowsError, e:
print "error"
If you're on Windows, you'll use the real WindowsError class and catch the exception. If you're not, you'll create a WindowsError class that will never be raised, so the except clause doesn't cause any errors, and the except clause will never be invoked.
Here's my current solution, but I slightly despise using non-trivial code in a except block:
try:
f.extractall()
except OSError, e:
# http://bugs.python.org/issue6609
if sys.platform.startswith('win'):
if isinstance(e, WindowsError) and e.winerror == 267:
raise InvalidFile, ('uses Windows special name (%s)' % e)
raise
#Glenn Maynard 's answer does not allow debugging with pdb as the WindowsError builtin is not available in the debugger. This code block will work inside of the Python debugger and during normal execution:
import exceptions
if not getattr(exceptions, "WindowsError", None):
class WindowsError(OSError): pass
This solution also works and avoids string literals and an import of the full exceptions library:
try:
from exceptions import WindowsError
except ImportError:
class WindowsError(OSError): pass

Is there a cross-platform way of getting information from Python's OSError?

On a simple directory creation operation for example, I can make an OSError like this:
(Ubuntu Linux)
>>> import os
>>> os.mkdir('foo')
>>> os.mkdir('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: [Errno 17] File exists: 'foo'
Now I can catch that error like this:
>>> import os
>>> os.mkdir('foo')
>>> try:
... os.mkdir('foo')
... except OSError, e:
... print e.args
...
(17, 'File exists')
Is there a cross-platform way that I can know that that the 17 or the 'File Exists' will always mean the same thing so that I can act differently depending on the situation?
(This came up during another question.)
The errno attribute on the error should be the same on all platforms. You will get WindowsError exceptions on Windows, but since this is a subclass of OSError the same "except OSError:" block will catch it. Windows does have its own error codes, and these are accessible as .winerror, but the .errno attribute should still be present, and usable in a cross-platform way.
Symbolic names for the various error codes can be found in the errno module.
For example,
import os, errno
try:
os.mkdir('test')
except OSError, e:
if e.errno == errno.EEXIST:
# Do something
You can also perform the reverse lookup (to find out what code you should be using) with errno.errorcode. That is:
>>> errno.errorcode[17]
'EEXIST'

Categories