pylint syntax error on valid program (raise with three arguments / expressions) - python

I'm looking at this minimal valid(?) program:
import sys
def f():
try:
raise Exception()
except Exception:
raise Exception(), None, sys.exc_info()[2]
f()
This program executes and behaves as expected , preserving the stack trace of the inner exception, as documented by help("raise"). However, when I run pylint on it, this is what I get:
$ pylint program.py
************* Module tmp
E: 7, 0: invalid syntax (<string>, line 7) (syntax-error)
The syntax-error disappears when I remove the second and third expressions to raise.
Is this a bug in pylint, or am I overlooking something?

Your pylint binary testing for Python 3 syntax, your code is valid for Python 2 only. Pylint tests code following the syntax of the Python binary you installed it with (it uses Python's own parser).
In Python 3, you'd use:
raise Exception().with_traceback(sys.exc_info()[2])
See the raise statement documentation for Python 3.
While your syntax may be correct for Python 2, you are technically using raise wrong. When passing in 3 elements, the first must be a class, not an instance. The second is an instance of that class, the third the traceback:
raise Exception, Exception(), sys.exc_info()[2]
or you can pass in None for an empty argument list passed to the first (the class) to create an instance:
raise Exception, None, sys.exc_info()[2]
Your code still happens to work, but only because Python isn't being too strict and takes that first argument as the instance when it is not a class.
If you want to test Python 2 code with pylint, install a copy into your Python 2 binary, and run that version. See Specify which python version pylint should evaluate for

Related

How to catch a custom exception not defined by me in python

I have the following try block:
try:
# depending on conditions, this can generate several types of errors
mymodule.do_stuff()
except Exception as e:
print("something went wrong, the error was :" + type(e).__name__)
I would like to catch potential errors from do_stuff(). After trial and error I was able to generate a list of potential errors that can be triggered by do_stuff() by printing their type(e).__name__ value:
DoStuffInsufficientMemoryException
DoStuffInsufficientCPUException
DoStuffInsufficientDiskException
but if I try do modify my except statement from except Exception as e to except DoStuffInsufficientMemoryException, I will get the error that DoStuffInsufficientMemoryException is not defined.
I tried defining a class that extends Exception for it, as most tutorials / questions in here suggest, basically:
class WAFInvalidParameterException(Exception):
pass
so now that variable is recognized, but since I can't control the error that do_sutff() will raise, I can't really raise this exception in my initial try block.
Ideally I would like to have 1 except block for each error so I would like to have something like:
try:
mymodule.do_stuff()
except DoStuffInsufficientMemoryException:
free_memory()
except DoStuffInsufficientCPUException:
kill_processes()
except DoStuffInsufficientDiskException:
free_disk_space()
but of course this doesn't work as these variables are not defined.
Just like you can't reference do_stuff without its module specifier, you have to specify in which module namespace these exceptions are defined.
try:
mymodule.do_stuff()
except mymodule.DoStuffInsufficientMemoryException:
free_memory()
except mymodule.DoStuffInsufficientCPUException:
kill_processes()
except mymodule.DoStuffInsufficientDiskException:
free_disk_space()
If free_memory is also in the mymodule namespace, of course you need to specify it there as well.
Alternatively, when you import mymodule, you can explicitly import selected symbols into the current namespace:
from mymodule import do_stuff, DoStuffInsufficientMemoryException, ...
and then, because they are in the current package, you can (or indeed must) refer to them without the package prefix mymodule.
A well-designed module will export selected symbols so you can refer to them without the package prefix, but whether this makes sense for your own package depends on its general design and intended audience. Some large packages define a separate subpackage for exceptions so you can say
import bigpackage.exceptions
to import them all. You will probably still want to explore the package's documentation (or, if it's lacking, its source code) to discover which exceptions exist and how they are organized. Many packages define a base exception class from which all its other exceptions are subclasses so that you can easily catch them all with just one symbol, like
try:
bigpackage.heavy_wizardry()
except bigpackage.BigBaseException:
print("you are turned into a frog")
EDIT : you can import other methods instead of creating your own, of course
The try/except block will try to execute the code and if an error is raised and specified in the except statement, it will stop the execution of the code located in the try block and execute the other code located in the except block. So, to catch your custom error, you have to raise it in the first place.
If you didn't know, you can raise errors using the raise statement. Here, I've made a simple chunk of code. I have a custom error, a variable x initialized at 2, and a method that adds 1 to the variable given in argument. The method will raise a CustomError if the variable becomes 3.
# Here, I define the custom error and allow a custom message to be displayed
# using the super() method
class CustomError(Exception):
def __init__(self, msg):
super().__init__(msg)
# I initialize x at 2
x = 2
# I create the method that will add 1 to the variable given in argument
# and raise a CustomError if the variable becomes 3
# This is completely random, and you can make whatever checks you want before raising
# Your custom error
def do_stuff(x):
x += 1
if x == 3:
raise CustomError("x is 3")
# Now, I write the try/except block. I raise e (the CustomError) if it is
# being raised in the method do_stuff(x)
try:
do_stuff(x)
except CustomError as e:
raise e
Feel free to try the code out to get a better understanding of it !
Usually if a function
module.foo()
throws an exception DoStuffInsufficientMemoryException it would be also importable as
from module import DoStuffInsufficientMemoryException
If this results in ImportError then you need the fullname function from this answer; use it with e (it takes an instance and returns the class name). If it gives
foo.bar.DoStuffInsufficientMemoryException
then you can import the exception as
from foo.bar import DoStuffInsufficientMemoryException
The above might not work for all cases. One notable case is Boto 3 AWS client library that does not make the exceptions importable - instead they will be attributes on the client instance.

Handle c library error in python?

If I'm using a lib written in c in python, such as zlib, and I get an error like:
error: Error -3 while decompressing: invalid distance too far back
How do I handle this exception is python? presumably the exception is defined in the c module and there is no exception class to catch on like except SomeException?
To call a C function from Python, the C function must be wrapped by a Python function. For CPython, the wrapper can be written in C with the help of a package such as Swig, in Cython, or in Python with the help of the ctypes module. The wrapper must translate Python inputs to C values, define C output varibles, call the C function, check the return code, and either translate the C output to Python and return it or translate the C code to a Python exception and raise it.
It appears that you are using Python's zlib, which wraps a C zlib. It defines a Python exception class error.
>>> import zlib
>>> zlib.error
<class 'zlib.error'>
>>> issubclass(zlib.error, Exception)
True
You catch it like any other exception.
>>> try:
raise zlib.error('Error 99: unknown')
except zlib.error as e:
print(e)
Error 99: unknown
To be consistent with the exception naming convention, I would have named itZlibError.

how can I use assertRaises() in python's unittest to catch syntaxerror?

For example, I want to test a function which has a syntax, in my unittest class's method, can I use code as the following?
self.assertRaises(SyntaxError, my_function)
When I use this, it just appears traceback of syntax error rather than showing how many tests have passed.
In order for the test to run, the code must be byte compiled by the Python interpreter. This happens when the module containing your function is imported, before the function is ever run. It is during the compilation that the SyntaxError is generated.
In your test module, you could wrap the import statement in a try/except:
raised = False
try:
import foo
except SyntaxError:
# A syntax error was generated during the import...
raised = True
self.assert_(raised, "'import foo' failed to raise a SyntaxError.")
or use one of the methods suggested by #alecxe, which look simpler and cleaner.
Following Warren Weckesser's explanation, you can test that an import function is throwing an error:
self.assertRaises(SyntaxError, __import__, "error_library")
For Python 2.7 and above, importlib.import_module() can/should be used instead:
self.assertRaises(SyntaxError, importlib.import_module, "error_library")

Can I have a method called print (or such) in my class?

When using PyCharm (community edition) on my Windows 7, I can have a class like so:
class X:
def __init__(self):
self.x = 0
def print(self):
print('x:', x)
And use it normally:
>>> x = X()
>>> x.print()
0
This code runs with no problems.
However, when I run the same code on my Ubuntu using gedit and terminal (python x_file.py), I get an error:
$ python main.py
File "main.py", line 6
def print(self):
^
SyntaxError: invalid syntax
Why the difference, am I allowed to have a method called print in a class, or is this just one of PyCharm's features?
In python3 there is absolutely no problem, however in python2 there was a print statement which precludes you from using it as an identifier, including a method name.
It seems like you have python2 on Ubuntu and python3 on Windows, hence the difference.
If you want to avoid the print statement in python2 add:
from __future__ import print_function
At the top of your file and you should obtain python3's print function, thus allowing you to define a method called print, (see this documentation).
Also note that without the __future__ import and in python3 this line:
print('x:', x)
Outputs:
x: <value for x>
However in python2 without the special import you get:
('x:', <value for x>)
Because the (...) does not specify the arguments to the function but is interpreted as a tuple to be printed by the statement.
By any chance your ubuntu python version is below 3 ? since Python 2 uses:
print "test"
and Python 3 uses:
print("test")
In python 2+ print is a special statement like break, return, raise and so on. You cannot override these by creating objects with the same name. In python 3+ print is a function and the argument is the string between parentheses. Builtin functions can be overridden in python so that's why you can define your own print (even outside your class) in python 3.
In your particular example PyCharm is probably based on python 3 and on your ubuntu machine you have '2.something'.
Off-topic: overriding builtins is usually not a good idea.

Why isn't assertRaises catching my Attribute Error using python unittest?

I'm trying to run this test: self.assertRaises(AttributeError, branch[0].childrennodes), and branch[0] does not have an attribute childrennodes, so it should be throwing an AttributeError, which the assertRaises should catch, but when I run the test, the test fails because it is throwing an AttributeError.
Traceback (most recent call last):
File "/home/tttt/../tttt/tests.py", line 504, in test_get_categories_branch
self.assertRaises(AttributeError, branch[0].children_nodes)
AttributeError: 'Category' object has no attribute 'children_nodes'
Any ideas?
When the test is running, before calling self.assertRaises, Python needs to find the value of all the method's arguments. In doing so, it evaluates branch[0].children_nodes, which raises an AttributeError. Since we haven't invoked assertRaises yet, this exception is not caught, causing the test to fail.
The solution is to wrap branch[0].children_nodes in a function or a lambda:
self.assertRaises(AttributeError, lambda: branch[0].children_nodes)
assertRaises can also be used as a context manager (Since Python 2.7, or in PyPI package 'unittest2'):
with self.assertRaises(AttributeError):
branch[0].children_nodes
# etc
This is nice because it can be used on arbitrary blocks of code in the middle of a test, rather than having to create a new function just to define the block of code to which it applies.
It can give you access to the raised exception for further processing, if needed:
with self.assertRaises(AttributeError) as cm:
branch[0].children_nodes
self.assertEquals(cm.exception.special_attribute, 123)
I think its because assert raises only accepts a callable. It evalutes to see if the callable raises an exception, not if the statement itself does.
self.assertRaises(AttributeError, getattr, branch[0], "childrennodes")
should work.
EDIT:
As THC4k correctly says it gathers the statements at collection time and will error then, not at testing time.
Also this is a reason why I like nose, it has a decorator (raises) that is useful and clearer for these kind of tests.
#raises(AttributeError)
def test_1(self)
branch[0].childrennodes
pytest also has a similar context manager:
from pytest import raises
def test_raising():
with raises(AttributeError):
branch[0].childrennodes

Categories

Resources