Checking code for deprecation warnings

deprecation-warning, python, python-2.7, static-code-analysis

Solution

Since you want to do this statically, you can use the `ast` module to parse the code and then scan it for any occurrence of the deprecated code with a subclass of the `NodeVisitor` class. Like so:

import ast, sys

class UsingMessageAttr(ast.NodeVisitor):

    error_object_names = []

    def visit_Attribute(self, node):
        if (node.attr == 'message' and 
            hasattr(node.value, 'id') and 
            node.value.id in self.error_object_names):

            print("Danger Will Robinson!!")
            sys.exit(1)

        self.generic_visit(node)

    def visit_ExceptHandler(self, node):
        if node.name is not None:
            self.error_object_names.append(node.name)
            self.generic_visit(node)
            self.error_object_names.pop()
        else:
            self.generic_visit(node)

with open('sourcefile.py', 'r') as f:
    UsingMessageAttr().visit(ast.parse(f.read()))

This works by using python parse the source file into an AST and then uses the visitor pattern to walk through the entire file and find any instances of the deprecated attribute. For more information on how this works, see the python documentation on the ast module.

Note that this won't work if you use something clever to refer to the exception object. It simply takes the variable name that the exception object was bound to and checks if a `message` attribute is ever accessed from a variable of the same name inside the body of the exception handler.

Problem

Consider the following sample code: ``` data = [] try: print data[0] except IndexError as error: print error.message ``` There is nothing syntactically wrong (using Python2.7) with the code except that if you run python with warnings turned on, you would see a `DeprecationWarning`: ``` $ python -W always test.py test.py:5: DeprecationWarning: BaseException.message has been deprecated as of Python 2.6 print error.message list index out of range ``` FYI, this is because `.message` was deprecated since python2.6 and removed in python3. Now, I'd like to find all places in the project where `.message` is called on any exception instance by using static code analysis tools. As an end goal, I'm planning to have this check running as a part of a daily build&test&code quality check task and raise an error if the syntax is still used. Is it possible? Is it something that `pylint`, `pyflakes` or other code analysis tools are capable of? I found that `pep8` tool has several similar checks implemented, for instance, `has_key()` usage check: ``` $ cat test.py my_dict = {} print my_dict.has_key('test') $ pep8 test.py test.py:2:14: W601 .has_key() is deprecated, use 'in' ``` As an alternative solution, I can treat all warnings as errors (like suggested here) and make my tests fail but this has its disadvantages: - there are other deprecation warnings coming from third-party packages that I cannot fix - strictly speaking, this requires 100% coverage, which is difficult to maintain

Original source