I want to move a file, but in the case it is not found I should just ignore it. In all other cases the exception should be propagated. I have the following piece of Python code:
try:
shutil.move(old_path, new_path)
except IOError as e:
if e.errno != 2: raise e
errno == 2 is the one, that has 'No such file or directory' description. I wonder if this is stable across Python versions and platforms, and etc.
It is better to use values from the errno module instead of hardcoding the value 2:
try:
shutil.move(old_path, new_path)
except IOError as e:
if e.errno != errno.ENOENT: raise e
This makes your code less likely to break in case the integer error value changes (although that is unlikely to occur).
Related
I want to explicitly fail the step in behave when I encounter an exception
eg. I am writing the code according to behave documentation -
from behave import *
#when('verify test fails.*?(?P<param_dict>.*)')
def test_logger(context, param_dict):
try:
logger.info("testing the logger. this is info message")
logger.info(1/0)
except Exception as e:
logger.error("arrived at exception: "+str(e))
fail("failed with exception: "+str(e))
but it throws this error:
NameError: name 'fail' is not defined
I tried other ways too, but nothing works
eg. context.failed = True (did not work either)
If I do not try to fail explicitly, final test result becomes PASS even if it goes in exception block ...which is weird.
context.failed is only an attribute set by Behave and doesn't do anything as is. It's an information attribute, and while you can use it to determine a fail-case and throw an assertion error, it will not do anything on it's own. See context.failed
As for the fail method you've mentioned, it is probably from the unittest module, as seen here. This module is used in Behave's development tests (see their Github) as well. I'll agree though, that this should be clarified in their documentation.
To fix your error you'd need to import the unittest module. To explicitly fail the step, you'd just raise the exception after you've logged your message, something like this:
except Exception as e:
logger.error("arrived at exception: "+str(e))
fail("failed with exception: "+str(e))
raise
As #Verv mentioned in their answer, a behave step will be marked as failed whenever an exception is thrown, so you could just re-raise the exception.
However, behave will show the backtrace for normal exceptions, whereas you probably just want to show a specific failure message.
For that, raise an AssertionError. In your code, that would look like this:
except Exception as e:
logger.error("arrived at exception: " + str(e))
raise AssertionError("failed with exception: " + str(e))
If behave encounters an AssertionError, it will fail the step, display the message you instantiate the error with, but not display other exception stuff.
Right now, I catch the exception in the except Exception: clause, and do print(exception). The result provides no information since it always prints <class 'Exception'>. I knew this used to work in python 2, but how do I do it in python3?
I'm guessing that you need to assign the Exception to a variable. As shown in the Python 3 tutorial:
def fails():
x = 1 / 0
try:
fails()
except Exception as ex:
print(ex)
To give a brief explanation, as is a pseudo-assignment keyword used in certain compound statements to assign or alias the preceding statement to a variable.
In this case, as assigns the caught exception to a variable allowing for information about the exception to stored and used later, instead of needing to be dealt with immediately.
(This is discussed in detail in the Python 3 Language Reference: The try Statement.)
There are other compound statements that use as. The first is the with statement:
#contextmanager
def opening(filename):
f = open(filename)
try:
yield f
finally:
f.close()
with opening(filename) as f:
# ...read data from f...
Here, with statements are used to wrap the execution of a block with methods defined by context managers. This functions like an extended try...except...finally statement in a neat generator package, and the as statement assigns the generator-produced result from the context manager to a variable for extended use.
(This is discussed in detail in the Python 3 Language Reference: The with Statement.)
As of Python 3.10, match statements also use as:
from random import randint
match randint(0, 2):
case 0|1 as low:
print(f"{low} is a low number")
case _:
print("not a low number")
match statements take an expression (in this case, randint(0, 2)) and compare its value to each case branch one at a time until one of them succeeds, at which point it executes that branch's block. In a case branch, as can be used to assign the value of the branch to a variable if that branch succeeds. If it doesn't succeed, it is not bound.
(The match statement is covered by the tutorial and discussed in detail in the Python 3 Language Reference: match Statements.)
Finally, as can be used when importing modules, to alias a module to a different (usually shorter) name:
import foo.bar.baz as fbb
This is discussed in detail in the Python 3 Language Reference: The import Statement.
These are the changes since python 2:
try:
1 / 0
except Exception as e: # (as opposed to except Exception, e:)
# ^ that will just look for two classes, Exception and e
# for the repr
print(repr(e))
# for just the message, or str(e), since print calls str under the hood
print(e)
# the arguments that the exception has been called with.
# the first one is usually the message. (OSError is different, though)
print(e.args)
You can look into the standard library module traceback for fancier stuff.
Try
try:
print(undefined_var)
except Exception as e:
print(e)
this will print the representation given by e.__str__():
"name 'undefined_var' is not defined"
you can also use:
print(repr(e))
which will include the Exception class name:
"NameError("name 'undefined_var' is not defined",)"
Here is the way I like that prints out all of the error stack.
import logging
try:
1 / 0
except Exception as _e:
# any one of the follows:
# print(logging.traceback.format_exc())
logging.error(logging.traceback.format_exc())
The output looks as the follows:
ERROR:root:Traceback (most recent call last):
File "/PATH-TO-YOUR/filename.py", line 4, in <module>
1 / 0
ZeroDivisionError: division by zero
LOGGING_FORMAT :
LOGGING_FORMAT = '%(asctime)s\n File "%(pathname)s", line %(lineno)d\n %(levelname)s [%(message)s]'
Although if you want a code that is compatible with both python2 and python3 you can use this:
import logging
try:
1/0
except Exception as e:
if hasattr(e, 'message'):
logging.warning('python2')
logging.error(e.message)
else:
logging.warning('python3')
logging.error(e)
[In Python3]
Let's say you want to handle an IndexError and print the traceback, you can do the following:
from traceback import print_tb
empty_list = []
try:
x = empty_list[100]
except IndexError as index_error:
print_tb(index_error.__traceback__)
Note: You can use the format_tb function instead of print_tb to get the traceback as a string for logging purposes.
Hope this helps.
Don't use print(e), since that won't print a stack trace, which is a nightmare for debugging. traceback.print_exception is what you're looking for:
import traceback
try:
assert False
except Exception as e:
traceback.print_exception(e)
I've use this :
except (socket.timeout, KeyboardInterrupt) as e:
logging.debug("Exception : {}".format(str(e.__str__).split(" ")[3]))
break
Let me know if it does not work for you !!
You can do:
with self.assertRaisesMessage(ValueError, 'invalid literal for int()'):
int('a')
Reference
I am attempting to convert the following pseudo-code to Python:
If <directory> does not exist:
Create all subdirectories for <directory>
Create a file in <directory>
This sounds simple enough to accomplish with os.makedirs and os.path.isdir:
if not os.path.isdir('/some/path'):
os.makedirs('/some/path')
open('/some/path/test.txt', 'w')
However, upon further inspection there is clearly a race condition present. Consider the following timeline:
the specified directory (/some/path) does not exist
the Python interpreter executes the first line, which evaluates to True
another process creates the directory (/some/path)
makedirs raises an OSError exception since the directory already exists
There are also problems if the directory does initially exist but is removed by another process before the final line is executed.
When it comes to Python, "it's easier to ask for forgiveness than permission." With that in mind, the fragment above could be better written:
try:
os.makedirs('/some/path')
except OSError:
pass
open('/some/path/test.txt', 'w')
This solves the two problems described above but creates a third: os.makedirs raises an OSError exception when one of the following conditions occurs:
the directory already exists
the directory could not be created
This means that there is no way to determine which of the two conditions caused the exception to be raised. In other words, actual failures will be silently ignored, which is not what I want.
How can I work around this problem?
I'll note that all of this is quite a bit more sane in python 3; FileExistsError and PermissionError are separate (subclass of OSError) exceptions that you can catch, and os.makedirs even has a exist_ok kwarg to suppress the former when you're ok with the directory already existing.
If you want to inspect the reason for the OSError, that info is in a tuple in e.args (or optionally e.errno if you just want to look at the error code):
try:
os.makedirs('/etc/python')
except OSError as e:
print e.args
(17, 'File exists')
try:
os.makedirs('/etc/stuff')
except OSError as e:
print e.args
(13, 'Permission denied')
try:
os.makedirs('/etc/stuff')
except OSError as e:
print e.errno
13
So you'll have to do a bit of introspection and handle the two error codes differently in your except block.
Currently I have the problem that I get a NameError for the following code:
try:
# some network programming raising an exception
except ConnectionError:
# some error handling
This does not work, because you have to import ConnectionError from some module, which is not mentioned in the documentation (maybe I'm just blind?).
All I found is this, but it refers to another request library.
All of the exceptions in the standard library that are expected to be "generally usable" are built-ins, and are documented in the Built-in Exceptions part of the library reference.
In 3.3, that includes this one:
exception ConnectionError
A base class for connection-related issues.
Subclasses are BrokenPipeError, ConnectionAbortedError, ConnectionRefusedError and ConnectionResetError.
But this is a built-in. So this should work:
except ConnectionError:
In 3.0-3.2, there is no such exception as ConnectionError. Nothing in the stdlib raises anything of that name. So there's no point in trying to handle it. (See PEP 3151 for an explanation of how OSError and IOError were reorganized between 3.2 and 3.3.)
The 3.2 equivalent of ConnectionError is OSError with certain errno values. So, what you want is something like:
except OSError as e:
if e.errno not in (EPIPE, ESHUTDOWN, ECONNABORTED, ECONNREFUSED, ECONNRESET):
raise
# whatever you wanted to do for ConnectionError.
Meanwhile, in the future, when you don't know what kind of exception you need to handle, it's pretty easy to test. First, write some test code that handles any exception by logging the qualified name of the exception type. Then take the type out of the log and use that in your real code.
try:
code_that_raises()
except Exception as e:
print(type(e), type(e).__qualname__, whatever_else_looks_useful(e))
I wrote a method that does some stuff and catches bad filenames. what should happen is if the path doesn't exist, it throws an IOError. however, it thinks my exception handling is bad syntax... why??
def whatever():
try:
# do stuff
# and more stuff
except IOError:
# do this
pass
whatever()
but before it even gets to calling whatever(), it prints the following:
Traceback (most recent call last):
File "", line 1, in
File "getquizzed.py", line 55
except IOError:
^
SyntaxError: invalid syntax
when imported...help?!
Check your indenting. This unhelpful SyntaxError error has fooled me before. :)
From the deleted question:
I'd expect this to be a duplicate, but I couldn't find it.
Here's Python code, expected outcome of which should be obvious:
x = {1: False, 2: True} # no 3
for v in [1,2,3]:
try:
print x[v]
except Exception, e:
print e
continue
I get the following exception: SyntaxError: 'continue' not properly in loop.
I'd like to know how to avoid this error, which doesn't seem to be
explained by the continue documentation.
I'm using Python 2.5.4 and 2.6.1 on Mac OS X, in Django.
Thank you for reading
there's 1 more possible if you're privileged to have an older installation
and
you're using the 'as' syntax:
except IOError as ioe:
and
the parser's getting tripped up on 'as'.
Using as is the preferred syntax in Python 2.6 and better.
It's a syntax error in Python 2.5 and older. For pre-2.6, use this:
except IOError, ioe:
Just missing something in your try block, i.e. pass or anything, otherwise it gives an indentation error.
You will get a syntax error if you don't put something in side the try block.
You can put pass just to hold the space:
try:
# do stuff
# and more stuff
pass
except IOError:
# do this
pass