OSError's filename attribute unavailable? - python

I have the following code:
except(OSError) as (errno, strerror, filename):
print "OSError [%d]: %s at %s" % (errno, strerror, filename)
It runs great unless it meets OSError num. 123 (The file name, directory name, or volume label syntax is incorrect). then I get the following error at the except code line:
ValueError: need more than 2 values to unpack
It is solved by not using the filename attribute. However my requirements prevent me from not using this attribute.
Is there another way?

I have not seen this kind of Exception handling where you are passing the Exception object's attributes to the as clause.
Normally you handle except ExceptionObject as e and handle the attributes as one would normally handle the attributes of an object.
OSError contains a errno attribute is a numeric error code from errno, and the strerror attribute is the corresponding string and for exceptions that involve a file system path (such as chdir() or unlink()), the exception instance will contain a third attribute, filename, which is the file name passed to the function.
import os
try:
os.chdir('somenonexistingdir')
except OSError as e:
print e.errno
print e.filename
print e.strerror

Related

How to handle OSError: [Errno 36] File name too long

When handling the errors that occur when trying to create an existing file or trying to use a file that doesn't exist the OSErrors that get thrown have a subclass (FileExistsError, FileNotFoundError). I couldn't find that subclass for the special case when the filename is too long.
The exact error message is:
OSError: [Errno 36] File name too long: 'filename'
I would like to catch the OSError that occurs when the filename is too long, but only when the filename is too long. I do not want to catch other OSErrors that might occur. Is there a way to achieve this?
Edit: I know that I could check the filename against a length but the maximum filename length varies too much depending on the OS and the filesystem and I don't see a "clean" solution that way.
Simply check errno attribute of caught exception.
try:
do_something()
except OSError as exc:
if exc.errno == 36:
handle_filename_too_long()
else:
raise # re-raise previously caught exception
For readability you may consider using appropriate constant from errno built-in module instead of hardcoded constant.
You can specify just how you want to catch a specific error such as errno.ENAMETOOLONG:
Specific to your question...
try:
# try stuff
except OSError as oserr:
if oserr.errno != errno.ENAMETOOLONG:
# ignore
else:
# caught...now what?
Specific to your comments...
try:
# try stuff
except Exception as err:
# get the name attribute from the exception class
errname = type(err).__name__
# get the errno attribute from the exception class
errnum = err.errno
if (errname == 'OSError') and (errnum == errno.ENAMETOOLONG):
# handle specific to OSError [Errno 36]
else if (errname == 'ExceptionNameHere' and ...:
# handle specific to blah blah blah
.
.
.
else:
raise # if you want to re-raise; otherwise code your ignore
This will grab all exceptions caused by errors in the try. Then it checks if the __name__ matches any specific exception and any additional conditions you want to specify.
You should know there is no getting around the except if an error is encountered unless you specific a concrete exception.

proper way to get nice string from exception

I want to generate a one-line string from an Exception which tells me what happened where (don't need a full backtrace). The following information would be nice:
filename / linenumber
exception type
exception description (what you get from str(e))
nice to have: function/method/class
Currently I do the following:
import os
...
try:
os.nonexisting()
except Exception as e:
t = e.__traceback__
tbf = e.__traceback__.tb_frame
print('%s:%d: %s in %s(): "%s" ' %
os.path.basename(tbf.f_code.co_filename),
t.tb_lineno,
e.__class__.__name__,
tbf.f_code.co_name, e))
which gives me:
foo.py:203: AttributeError in foo(): "'module' object has no attribute 'nonexisting'"
Is there a more elegant way to print out the details given in this example? I'm thinking about s.th. like
print(e.format('%f: %l: %t %F: "%w"'))
I'd like to avoid importing extra modules except there is one exactly for this purpose.
I think traceback.format_exception_only does exactly what you want.
try:
os.nonexisting()
except Exception as e:
print(traceback.format_exception_only(e.__class__, e))

intercept exception with raise

I have function, which looks for special Element if project files:
def csproj_tag_finder(mod_proj_file):
"""Looking for 'BuildType' element in each module's csproj file passed in `mod_proj_file`
ard return it's value (CLOUD, MAIN, NGUI, NONE)"""
try:
tree = ET.ElementTree(file=mod_proj_file)
root = tree.getroot()
for element in root.iterfind('.//'):
if ('BuildType') in element.tag:
return element.text
except IOError as e:
# print 'WARNING: cant find file: %s' % e
If no file found - it prints 'WARNING: cant find file: %s' % e.
This function called from another one:
def parser(modename, mod_proj_file):
...
# module's tag's from project file in <BuildType> elements, looks like CLOUD
mod_tag_from_csproj = csproj_tag_finder(mod_proj_file)
if not mod_tag_from_csproj:
print('WARNING: module %s have not <BuildType> elements in file %s!' % (modename, mod_proj_file))
...
So - when file doesn't found - csproj_tag_finder()return None type, and print WARNING. Second function - parser() find empty mod_tag_from_csproj variable, and also print WARNING. This is harmless, so I want make csproj_tag_finder() raise special Exception, so parser() except it and pass == check, instead of print text.
I tried add something like:
...
except IOError as e:
# print 'WARNING: cant find file: %s' % e
raise Exception('NoFile')
to csproj_tag_finder() to catch it later in parser() - but it's interrupt next steps immediately.
P.S. Later if not mod_tag_from_csproj: will call another function to add new Element. This task can be solved with just return 'NoFile' and then catch with if/else - but it seems to me that raise will more correct way here. Or not?
raise interrupting the next steps immediately is exactly what it's supposed to do. In fact, that's the whole point of exceptions.
But then return also interrupts the next steps immediately, because returning early is also the whole point of return.
If you want to save an error until later, continue doing some other work, and then raise it at the end, you have to do that explicitly. For example:
def spam():
error = None
try:
do_some_stuff()
except IOError as e:
print 'WARNING: cant find file %s' % e
error = Exception('NoFile')
try:
do_some_more_stuff()
except OtherError as e:
print 'WARNING: cant frob the glotz %s' % e
error = Exception('NoGlotz')
# etc.
if error:
raise error
Now, as long as there's no unexpected exception that you forgot to handle, whatever failed last will be in error, and it'll be raised at the end.
As a side note, instead of raising Exception('NoFile'), then using == to test the exception string later, you probably want to create a NoFileException subclass; then you don't need to test it, you can just handle it with except NoFileException:. And that means you can carry some other useful information (the actual exception, the filename, etc.) in your exception without it getting in the way, too. If this sounds scary to implement, it's not. It's literally a one-liner:
class NoFileException(Exception): pass

obtaining error number of an error

I need to obtain the error number from an error that has occurred in Python.
Ex; When trying to transfer a directory via the Paramiko package, an error is caught with this piece of code:
try:
sftp.put(local_path,target_path)
except (IOError,OSError),errno:
print "Error:",errno
For which I get the output,
Error: [Errno 21] Is a directory
I want to utilize the error number to go into some more code to transfer the directory and the directory contents.
Thanks for clarifying your question.
Most Exceptions in Python don't have "error numbers". One exception (no pun intended) are HTTPError exceptions, for example:
import urllib2
try:
page = urllib2.urlopen("some url")
except urllib2.HTTPError, err:
if err.code == 404:
print "Page not found!"
else:
...
Another exception (as noted by bobince) are EnvironmentErrors:
import os
try:
f=open("hello")
except IOError, err:
print err
print err.errno
print err.strerror
print err.filename
outputs
[Errno 2] No such file or directory: 'hello'
2
No such file or directory
hello
If you're talking about errno.h error numbers, you can get them from the errno property on the exception object, but only on EnvironmentError (which includes OSError, IOError and WindowsError).
Specifically on WindowsError you'll also get a winerror property with a Windows-specific error number. (You don't often see one of these though, as Python uses the Win32 API directly relatively rarely.)
There is also the errno package, which allows working with error codes with out having to handle magic numbers in the code. See an example here: Are Python error numbers associated with IOError stable?

Find module name of the originating exception in Python

Example:
>>> try:
... myapp.foo.doSomething()
... except Exception, e:
... print 'Thrown from:', modname(e)
Thrown from: myapp.util.url
In the above example, the exception was actually thrown at myapp/util/url.py module. Is there a way to get the __name__ of that module?
My intention is to use this in logging.getLogger function.
This should work:
import inspect
try:
some_bad_code()
except Exception, e:
frm = inspect.trace()[-1]
mod = inspect.getmodule(frm[0])
print 'Thrown from', mod.__name__
EDIT: Stephan202 mentions a corner case. In this case, I think we could default to the file name.
import inspect
try:
import bad_module
except Exception, e:
frm = inspect.trace()[-1]
mod = inspect.getmodule(frm[0])
modname = mod.__name__ if mod else frm[1]
print 'Thrown from', modname
The problem is that if the module doesn't get loaded (because an exception was thrown while reading the code in that file), then the inspect.getmodule call returns None. So, we just use the name of the file referenced by the offending frame. (Thanks for pointing this out, Stephan202!)
You can use the traceback module, along with sys.exc_info(), to get the traceback programmatically:
try:
myapp.foo.doSomething()
except Exception, e:
exc_type, exc_value, exc_tb = sys.exc_info()
filename, line_num, func_name, text = traceback.extract_tb(exc_tb)[-1]
print 'Thrown from: %s' % filename
This should do the trick:
import inspect
def modname():
t=inspect.trace()
if t:
return t[-1][1]
Python's logging package already supports this - check the documentation. You just have to specify %(module)s in the format string. However, this gives you the module where the exception was caught - not necessarily the same as the one where it was raised. The traceback, of course, gives you the precise location where the exception was raised.
I have a story about how CrashKit computes class names and package names from Python stack traces on the company blog: “Python stack trace saga”. Working code included.

Categories

Resources