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
Related
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.
import config as regex
except Exception as e:
logging.error('Issue in Calcutaing: '+str(e))
return None
I want to execute this program but it gives the following error.
import config as regex
File "/usr/local/lib/python3.5/dist-packages/config.py", line 733
except Exception, e:
^
In Python 3 the line
except Exception, e:
must be written as
except Exception as e:
Likely, if you want your module to be published, you should also give it a more descriptive name than config.
So I'm building a simple script that backs up certain documents to my second hard-drive (you never know what could happen!). So, I used the shutil.copytree function to replicate my data on the second drive. It works beautifully, and that is not the problem.
I use the shutil.rmtree function to remove the tree if the destination already exists. I'll show you my code:
import shutil
import os
def overwrite(src, dest):
if(not os.path.exists(src)):
print(src, "does not exist, so nothing may be copied.")
return
if(os.path.exists(dest)):
shutil.rmtree(dest)
shutil.copytree(src, dest)
print(dest, "overwritten with data from", src)
print("")
overwrite(r"C:\Users\Centurion\Dropbox\Documents", r"D:\Backup\Dropbox Documents")
overwrite(r"C:\Users\Centurion\Pictures", r"D:\Backup\All Pictures")
print("Press ENTER to continue...")
input()
As you can see, a simple script. Now, when I run the script for the first time, everything is fine. Pictures and Documents copy over to my D: drive just fine. However, when I run for the second time, this is my output:
C:\Users\Centurion\Programming\Python>python cpdocsnpics.py
D:\Backup\Dropbox Documents overwritten with data from C:\Users\Centurion\Dropbox\Documents
Traceback (most recent call last):
File "cpdocsnpics.py", line 17, in <module>
overwrite(r"C:\Users\Centurion\Pictures", r"D:\Backup\All Pictures")
File "cpdocsnpics.py", line 10, in overwrite
shutil.rmtree(dest)
File "C:\Python34\lib\shutil.py", line 477, in rmtree
return _rmtree_unsafe(path, onerror)
File "C:\Python34\lib\shutil.py", line 376, in _rmtree_unsafe
onerror(os.rmdir, path, sys.exc_info())
File "C:\Python34\lib\shutil.py", line 374, in _rmtree_unsafe
os.rmdir(path)
PermissionError: [WinError 5] Access is denied: 'D:\\Backup\\All Pictures'
The error only happens when I copy Pictures after the first time; I'm assuming it has something to do with being a Library.
What should I do?
That's a cross-platform consistency issue.
You've copied files/dirs with readonly attribute. On the first time "dest" not exists, thus rmtree method is not performed. However, when you try run "overwrite" function we can notice that "dest" location exists (and its subtree) but it was copied with readonly access. So here we got a problem.
In order to "fix" issue, you must provide a handler for onerror parameter of shutil.rmtree. As long as your problem is regarding readonly issues the workaround is somewhat like this:
def readonly_handler(func, path, execinfo):
os.chmod(path, 128) #or os.chmod(path, stat.S_IWRITE) from "stat" module
func(path)
As you can see in the python doc onerror must be a callable that accepts three parameters: function, path, and excinfo. For further info, read the docs.
def overwrite(src, dest):
if(not os.path.exists(src)):
print(src, "does not exist, so nothing may be copied.")
return
if(os.path.exists(dest)):
shutil.rmtree(dest, onerror=readonly_handler)
shutil.copytree(src, dest)
print(dest, "overwritten with data from", src)
print("")
Of course, this handler is simple and specific but if other errors occur, new exceptions will be raised and this handler may not be able to fix them!
Note:
Tim Golden (Python for windows contributor) has been patching the shutil.rmtree issue and it seems it will be resolved in Python 3.5 (see issue 19643).
I found a problem other than the read-only file using shutil.rmtree on Windows (testing on Windows 7). I was using a combination of shutil.rmtree and shutil.copytree to create a test fixture in a test suite, so the sequence was being called repeatedly in a short period of tme (<1 sec intervals), and I was seeing unpredictable failures part way through the test suite, with both EACCES and ENOTEMPTY errors reported. The symptoms suggested to me that the shutil.rmtree function had not completed on return to the calling program, and that it was only after some time that the deleted filenames were available for re-use.
TL;DR: the solution isn't pretty - broadly, it renames the directory before deleting it, but there are a number of wrinkles that need to be handled because the Windows file system seems to take some time to catch up with the operations perfumed. The actual code catches a variety of failure conditions and retries a variant of the failed operation after a short delay.
A longer discussion follows, with my final code at the end.
My first thought was to try renaming the directory tree before removing it, so that the original directory name is immediately available for re-use. This does appear to help. To this end, I created a replacement for rmtree whose essence is this:
def removetree(tgt):
def error_handler(func, path, execinfo):
e = execinfo[1]
if e.errno == errno.ENOENT or not os.path.exists(path):
return # path does not exist - treat as success
if func in (os.rmdir, os.remove) and e.errno == errno.EACCES:
os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
func(path) # read-only file; make writable and retry
raise e
tmp = os.path.join(os.path.dirname(tgt),"_removetree_tmp")
os.rename(tgt, tmp)
shutil.rmtree(tmp, onerror=error_handler)
return
I found this logic was an improvement, but it was subject to unpredictable failure of the os.rename operation, with one of several possible errors. So I also added some retry logic around os.rename, thus:
def removetree(tgt):
def error_handler(func, path, execinfo):
# figure out recovery based on error...
e = execinfo[1]
if e.errno == errno.ENOENT or not os.path.exists(path):
return # path does not exist
if func in (os.rmdir, os.remove) and e.errno == errno.EACCES:
os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
func(path) # read-only file; make writable and retry
raise e
# Rename target directory to temporary value, then remove it
count = 0
while count < 10: # prevents indefinite loop
count += 1
tmp = os.path.join(os.path.dirname(tgt),"_removetree_tmp_%d"%(count))
try:
os.rename(tgt, tmp)
shutil.rmtree(tmp, onerror=error_handler)
break
except OSError as e:
time.sleep(1) # Give file system some time to catch up
if e.errno in [errno.EACCES, errno.ENOTEMPTY]:
continue # Try another temp name
if e.errno == errno.EEXIST:
shutil.rmtree(tmp, ignore_errors=True) # Try to clean up old files
continue # Try another temp name
if e.errno == errno.ENOENT:
break # 'src' does not exist(?)
raise # Other error - propagate
return
The above code is not tested, but the general idea here does seem to work. The full code I actually use is below, and uses two functions. It probably contains some unnecessary logic, but does seem to be working more reliably for me (in that my test suite now passes repeatedly on Windows where previously it failed unpredictably on a majority of runs):
def renametree_temp(src):
"""
Rename tree to temporary name, and return that name, or
None if the source directory does not exist.
"""
count = 0
while count < 10: # prevents indefinite loop
count += 1
tmp = os.path.join(os.path.dirname(src),"_removetree_tmp_%d"%(count))
try:
os.rename(src, tmp)
return tmp # Success!
except OSError as e:
time.sleep(1)
if e.errno == errno.EACCES:
log.warning("util.renametree_temp: %s EACCES, retrying"%tmp)
continue # Try another temp name
if e.errno == errno.ENOTEMPTY:
log.warning("util.renametree_temp: %s ENOTEMPTY, retrying"%tmp)
continue # Try another temp name
if e.errno == errno.EEXIST:
log.warning("util.renametree_temp: %s EEXIST, retrying"%tmp)
shutil.rmtree(tmp, ignore_errors=True) # Try to clean up old files
continue # Try another temp name
if e.errno == errno.ENOENT:
log.warning("util.renametree_temp: %s ENOENT, skipping"%tmp)
break # 'src' does not exist(?)
raise # Other error: propagaee
return None
def removetree(tgt):
"""
Work-around for python problem with shutils tree remove functions on Windows.
See:
https://stackoverflow.com/questions/23924223/
https://stackoverflow.com/questions/1213706/
https://stackoverflow.com/questions/1889597/
http://bugs.python.org/issue19643
"""
# shutil.rmtree error handler that attempts recovery from attempts
# on Windows to remove a read-only file or directory (see links above).
def error_handler(func, path, execinfo):
e = execinfo[1]
if e.errno == errno.ENOENT or not os.path.exists(path):
return # path does not exist: nothing to do
if func in (os.rmdir, os.remove) and e.errno == errno.EACCES:
try:
os.chmod(path, stat.S_IRWXU| stat.S_IRWXG| stat.S_IRWXO) # 0777
except Exception as che:
log.warning("util.removetree: chmod failed: %s"%che)
try:
func(path)
except Exception as rfe:
log.warning("util.removetree: 'func' retry failed: %s"%rfe)
if not os.path.exists(path):
return # Gone, assume all is well
raise
if e.errno == errno.ENOTEMPTY:
log.warning("util.removetree: Not empty: %s, %s"%(path, tgt))
time.sleep(1)
removetree(path) # Retry complete removal
return
log.warning("util.removetree: rmtree path: %s, error: %s"%(path, repr(execinfo)))
raise e
# Try renaming to a new directory first, so that the tgt is immediately
# available for re-use.
tmp = renametree_temp(tgt)
if tmp:
shutil.rmtree(tmp, onerror=error_handler)
return
(The above code incorporates a solution to the read-only file problem from What user do python scripts run as in windows?, which according to Deleting directory in Python is tested. I don't think I encounter the read-only file problem, so assume it is not tested in my test suite.)
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")
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.