Given a Python file containing a Python script written with bad style, this script will output a review adressing its problems.
RESERVED_KEYWORDS=['abs','dict','help','min','setattr','all','dir','hex','next','slice',
'any','divmod','id','object','sorted','ascii','enumerate','input','oct',
'staticmethod','bin','eval','int','open','str','bool','exec','isinstance',
'ord','sum','bytearray','filter','issubclass','pow','super','bytes','float',
'iter','print','tuple','callable','format','len','property','type','chr',
'frozenset','list','range','vars','classmethod','getattr','locals','repr','zip',
'compile','globals','map','reversed',
'__import__','complex','hasattr','max','round','delattr','hash','memoryview','set']
FILENAME = "code_with_bad_style.py"
BULTIN_REASSIGNED_ERROR = """You wrote:
{} = "something"
That is not good because {} is a built-in in Python
and you should never re-assign new values to the
built-ins, in case you are wondering wheter a word is a builtin or
not go to https://docs.python.org/3/library/functions.html to read the
complete list"""
NAME_NOT_USED_ERROR="""You should use
if __name__ == "__name__":
main()
So that your file is going to usable as both
a stand-alone programme and an importable programme.
"""
NO_DOCS_ERROR = """You should consider using some docstrings.
Docstrings are multiline comments that explain what a function does,
they are of great help for the reader. They look like the following:
def function(a, b):
\"\"\"Do X and return a list.\"\"\"
"""
USE_LIST_COMPREHENSION_ERROR = """In python there is
a very powerful language feature called [list comprehension][https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions].
The following:
result = []
for i in lst:
if foo(i):
result.append(bar(i))
should be replaced with:
result = [bar(i) for i in lst if foo(i)]
"""
USE_WITH_ERROR = """There is a very convenient way of handling files in python:
the with statement. It handles closing automatically so you do not
have to worry about it. It is very simple to use, look at the following example:
with open("x.txt") as f:
data = f.read()
# do something with data
"""
PRINT_BREAKING_PYTHON_3_ERROR = """You should use parenthesis with your print
statement (in Python 3 it is a function) to keep compatibility with python 3"""
IMPORT_STAR_ERROR = """You should avoid using:
from long_long_long_name import *
because people will not know where you are taking your functions from.
Instead use:
import long_long_long_name as short
"""
SEPARATOR = """
----------
"""
def nextline(line,lines):
return lines[lines.index(line) + 1]
def reassign_built_in_error(code):
for built_in in RESERVED_KEYWORDS:
if built_in + " =" in code or built_in + "=" in code:
return BULTIN_REASSIGNED_ERROR.format(built_in,built_in)
def if_name_error(code):
if "__name__" not in code:
return NAME_NOT_USED_ERROR
def no_docs_error(code):
for line in code.splitlines():
if line.startswith("def") or line.startswith("class"):
if '"""' not in nextline(line,code):
return NO_DOCS_ERROR
def use_list_comprehension_error(code):
if "append" in code:
return USE_LIST_COMPREHENSION_ERROR
def with_not_used_error(code):
if ".close()" in code and ".open()" in code:
return USE_WITH_ERROR
def print_breaking_3_error(code):
for line in code.splitlines():
if line.startswith("print") and "(" not in line:
return PRINT_BREAKING_PYTHON_3_ERROR
def import_star_error(code):
for line in code.splitlines():
if line.startswith("import") and "*" not in line:
return IMPORT_STAR_ERROR
def main():
ALL_ANALYSIS = [reassign_built_in_error,if_name_error,no_docs_error,
use_list_comprehension_error,with_not_used_error,
print_breaking_3_error,import_star_error]
with open(FILENAME) as f:
code = f.read()
for analysis in ALL_ANALYSIS:
result = analysis(code)
if result:
print(result)
print(SEPARATOR)
if __name__ == "__main__":
main()