The state of static analysis on Python
Here is a program that fails with UnboundLocalError three out of four times:
"""Module to demonstrate an error.""" import random def zero_or_one(): """Returns either 0 or 1 with equal probability.""" return random.choice([0, 1]) def main(): """Function to demonstrate an error.""" if zero_or_one(): first = 42 for _ in range(zero_or_one()): second = 42 print first, second if __name__ == '__main__': main()
Note that line 15 uses the variables first and second, which are defined only if zero_or_one() returned 1 both times.
(Condensed from a real bug where, because of additional indentation, a variable assignment happened as the last line inside a loop, instead of the first line after it.)
I know of three tools that are popular: pychecker, pyflakes, and pylint. None of them say a single thing about this program. It is questionable whether ever (and if so, how often) code like the above is what the programmer intended.
The first of these, pychecker, is not on pip, and requires you to download a file from Sourceforge and run “python setup.py install”. Anyway, this is the output from the three programs:
/tmp% pychecker test.py Processing module test (test.py)... Warnings... None /tmp% pyflakes test.py /tmp% pylint test.py No config file found, using default configuration Report ====== 12 statements analysed. Statistics by type ------------------ +---------+-------+-----------+-----------+------------+---------+ |type |number |old number |difference |%documented |%badname | +=========+=======+===========+===========+============+=========+ |module |1 |1 |= |100.00 |0.00 | +---------+-------+-----------+-----------+------------+---------+ |class |0 |0 |= |0 |0 | +---------+-------+-----------+-----------+------------+---------+ |method |0 |0 |= |0 |0 | +---------+-------+-----------+-----------+------------+---------+ |function |2 |2 |= |100.00 |0.00 | +---------+-------+-----------+-----------+------------+---------+ Raw metrics ----------- +----------+-------+------+---------+-----------+ |type |number |% |previous |difference | +==========+=======+======+=========+===========+ |code |11 |73.33 |11 |= | +----------+-------+------+---------+-----------+ |docstring |3 |20.00 |3 |= | +----------+-------+------+---------+-----------+ |comment |0 |0.00 |0 |= | +----------+-------+------+---------+-----------+ |empty |1 |6.67 |1 |= | +----------+-------+------+---------+-----------+ Duplication ----------- +-------------------------+------+---------+-----------+ | |now |previous |difference | +=========================+======+=========+===========+ |nb duplicated lines |0 |0 |= | +-------------------------+------+---------+-----------+ |percent duplicated lines |0.000 |0.000 |= | +-------------------------+------+---------+-----------+ Messages by category -------------------- +-----------+-------+---------+-----------+ |type |number |previous |difference | +===========+=======+=========+===========+ |convention |0 |0 |= | +-----------+-------+---------+-----------+ |refactor |0 |0 |= | +-----------+-------+---------+-----------+ |warning |0 |0 |= | +-----------+-------+---------+-----------+ |error |0 |0 |= | +-----------+-------+---------+-----------+ Global evaluation ----------------- Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00) /tmp%
Leave a Reply