HTTPS SSH

README

python-exception-inference

This is a pylint plugin to explore python code exception handling.

  • myexcchk.py : exception checker plugin

Which exceptions could reach a given function?

This code

class FromDecorator(Exception):
    pass

def rand():
    return

def decorator(f):
    def new_f(*args, **kw):
        if rand():
            raise FromDecorator()
        return f(*args, **kw)
    return new_f

@decorator
def decorated():
    pass

Returns:

C:\Users\aweil\repos\python-exception-inference>\Python27\Scripts\pylint.exe -r n --rcfile=pylintrc.txt sample_decorator.py
************* Module sample_decorator
W:  8, 4: Exception found: FromDecorator at ':decorator:new_f' (exception-found)
W: 15, 0: Exception found: FromDecorator at ':sample_decorator:decorated' (exception-found)
2017-09-10 12:14:50,894 - root - INFO - -----------------------------------------------------------------

How does it work?

For that, we basically explore every Function/Method/Code to:

  1. get code calls-in and calls-out
  2. find Exception raises
  3. keep track on calls/raises of try/except catching

After that, for any Function/Method and triggered code we collect the info from 1, 2 & 3 obtaining what we wanted.

Pylint analysis is based on messages, and we use two messages for that:

  • exception-point : it's a message used internally, and setup on for every raise statement. Ignore these.
  • exception-found : it's a message used to report a given exception may reach the specified function/method.

I've prepared a pylint's rc-file which enables only exception-found messages to make it easy to play with this.

Some example below..

How do I get set up?

  • install pylint

Usage

  • windows:
    • add myexcchk.py to PYTHONPATH
    • run: pylint --rcfile=pylintrc.txt abc_inst.py
  • unix:
    • PYTHONPATH=. pylint -r n --rcfile=pylintrc.txt abc_inst.py

Output will be somethin like this:

W: 12, 4: Exception found: Exception at 'abc_inst:B:__len__' (exception-found)
W: 16, 0: Exception found: Exception at 'abc_inst:None:testlen' (exception-found)

2017-08-26 06:01:36,658 - root - INFO - -----------------------------------------------------------------
(None, None, '<module-load>')                     <co:   0  ci:0  e:0 None:None:<module-load>>
('exceptions', 'Exception', '__init__')           <co:   0  ci:1  e:0 exceptions:Exception:__init__>
('abc_inst', 'B', '__len__')                      <co:   1  ci:0  e:1 abc_inst:B:__len__>
('abc_inst', None, 'testlen')                     <co:   1  ci:0  e:0 abc_inst:None:testlen>
('abc_inst', None, 'main')                        <co:   0  ci:0  e:0 abc_inst:None:main>
2017-08-26 06:01:36,665 - root - INFO - -----------------------------------------------------------------

First part, is the actual messages reported to pylint, ie: the exceptions found and where. In the show example, exception raised class was "Exception", and it was found in package: abc_inst in: - class B / method __len__ - function testlen

After that, there still is some debug information showing: - Functions processed: where / co = calls-out / ci = calls-in / exception-points in it.

  • tests:

    C:\Users\aweil\repos\python-exception-inference>python myexcchk_test.py -v test_10_catch_superclass (main.MyAstroidCheckerTC) ... ok test_11_raise_indirect (main.MyAstroidCheckerTC) ... ok test_12_raise_indirect2 (main.MyAstroidCheckerTC) ... ok test_13_raise_indirect3 (main.MyAstroidCheckerTC) ... ok test_14_raise_indirect4 (main.MyAstroidCheckerTC) ... ok test_1_basic_message (main.MyAstroidCheckerTC) Learn how to test this things.. ... ok test_2_basic_raise (main.MyAstroidCheckerTC) ... ok test_30_full_inference (main.MyAstroidCheckerTC) ... ok test_3_direct_raise (main.MyAstroidCheckerTC) ... ok test_4_catch_all (main.MyAstroidCheckerTC) ... ok test_5_catch_set (main.MyAstroidCheckerTC) ... ok test_6_catch_multi (main.MyAstroidCheckerTC) ... ok test_7_catch_superclass (main.MyAstroidCheckerTC) ... ok


    Ran 13 tests in 0.075s

    OK

Notes

Pylint information messages use to show in which exact line is the message happening. Although I think it is possible to keep that information (exception trigger line nr) in our analysis, I haven't had time to take care about it, but still consider the actually retrieved information enough for many uses.

old roadmap

To Do for v1

  • add built-in/related thrown exceptions (medium)
  • setup default entry-points to main function (easy)
  • add command-line switch/option to allow specify different entry-points (easy)
  • remove explicit exceptions message (easy)
  • recursively track/process dependent sources (hard)

already done

  • identify exception throwing code (done)
  • create exception filtering code / handling exception handling (done)
  • identify function dependency / call graph (done)
  • attach to function dependency, related exception filtering (done)
  • retrieve code exceptions recursively (done)
  • add indirect/implicit calls (done)