A Python lint tool

main()

The Py7 script invokes main()(Listing 2) automatically unless you do not include source code files as CLI parameters (if there are no parameters, Py7 exits with an error). It reports the Py7 tool version information and then checks for additional CLI parameters. If a help parameter (-h or --help) is present, then usage() is called, with a normal exit.

Listing 2

main()

01 def main():
02   print("")
03   version()
04   print("")
05
06   if len(sys.argv) == 2 and sys.argv[1] in ('-h', '--help'):
07     usage()
08     sys.exit(0)
09
10   if len(sys.argv) < 2:
11     print(":: ")
12     print(":: Error: No Python files given to check!")
13     print(":: ")
14     usage()
15     sys.exit(1)
16
17   check_modules()  # check for and install required modules
18
19   print("::")
20   print(":: Beginning Py7 Lint")
21   print("::")
22   print("   ")
23   for i in range(1, len(sys.argv)):
24     print("\n::--- ... ---::\n")
25     run_lint_tests(sys.argv[i])
26     print("   ")
27   print("::")
28   print(":: Completed Py7 Lint")
29   print("::")
30   input("\nPress any key to continue...\n")
31   sys.exit(0)

The main() function then checks that the various modules are installed in the Python environment by calling check_modules() (line 17) each time Py7 is run.

Then in lines 19-31, main() runs the various lint tools against the Python source code files given as CLI parameters. After checking and verifying the modules are available, main then iterates over the CLI parameters, which are each Python source code file selected for linting. Each file is then passed to run_lint_tets to lint the code.

check_modules()

The check_modules() function (Listing 3) uses a list of the modules, PY_MOD_LIST, which is defined as a global attribute; check_modules() uses a for loop to iterate over PY_MOD_LIST. The find_spec function in the importlib module checks to see if the module exists.

Listing 3

check_modules

01 def main():
02   print("")
03   version()
04   print("")
05
06   if len(sys.argv) == 2 and sys.argv[1] in ('-h', '--help'):
07     usage()
08     sys.exit(0)
09
10   if len(sys.argv) < 2:
11     print(":: ")
12     print(":: Error: No Python files given to check!")
13     print(":: ")
14     usage()
15     sys.exit(1)
16
17   check_modules()  # check for and install required modules
18
19   print("::")
20   print(":: Beginning Py7 Lint")
21   print("::")
22   print("   ")
23   for i in range(1, len(sys.argv)):
24     print("\n::--- ... ---::\n")
25     run_lint_tests(sys.argv[i])
26     print("   ")
27   print("::")
28   print(":: Completed Py7 Lint")
29   print("::")
30   input("\nPress any key to continue...\n")
31   sys.exit(0)01 def check_modules():
02   print("#")
03   print("# Python Linter Tool Script ", __release__)
04   print("# ")
05   print("# Checking for required Python modules...")
06   print("#")
07   for package in PY_MOD_LIST:
08     print("   ")
09     has_mod = importlib.util.find_spec(package)
10     if has_mod:
11       print(f'  #:>The {package} module exists.')
12     else:
13       print(f'  #:>The {package} module absent.')
14       tmp_argv = sys.argv
15       sys.argv = ["pip", "install"] + [ package ]
16       try:
17         run_module("pip", run_name="__main__")
18       except SystemExit as exception:
19         print("exit code: ", exception.code)
20         if exception.code != 0 :
21           print(":: ")
22           print(f':: Error: The {package} module NOT installed; end tests!')
23           print(":: ")
24           sys.exit(2)
25       sys.argv = tmp_argv
26       print("  #:> ")
27       print(f'  #:>The {package} module installed.')
28       print("  #:> ")
29   print("")
30   input("\nPress any key to continue...\n")
31   print("")

If a module is not found when using check_modules, the module is then installed using run_module() in the runpy module within a try statement. First, the system CLI arguments are stored, and then the module to be installed is set as an argument.

The run_module function then invokes the pip module with the __main__ function. The original Py7 tool simply executed pip as a sub-process, but that caused problems in later versions of Python.

The return code from the pip module __main__ function is checked with the exception object of the try statement. If the return code is anything except zero, then the Py7 tool reports an error and exits, because Py7 cannot run without all the tools installed.

run_lint_tests()

After the lint tools are verified as installed, main() runs each test on each Python source file. Using a for loop, each file listed as a CLI parameter is passed in a call to run_lint_tests() (Listing 4) on the Python source code file.

Listing 4

run_lint_tests()

01 def main():
02   print("")
03   version()
04   print("")
05
06   if len(sys.argv) == 2 and sys.argv[1] in ('-h', '--help'):
07     usage()
08     sys.exit(0)
09
10   if len(sys.argv) < 2:
11     print(":: ")
12     print(":: Error: No Python files given to check!")
13     print(":: ")
14     usage()
15     sys.exit(1)
16
17   check_modules()  # check for and install required modules
18
19   print("::")
20   print(":: Beginning Py7 Lint")
21   print("::")
22   print("   ")
23   for i in range(1, len(sys.argv)):
24     print("\n::--- ... ---::\n")
25     run_lint_tests(sys.argv[i])
26     print("   ")
27   print("::")
28   print(":: Completed Py7 Lint")
29   print("::")
30   input("\nPress any key to continue...\n")
31   sys.exit(0)01 def check_modules():
02   print("#")
03   print("# Python Linter Tool Script ", __release__)
04   print("# ")
05   print("# Checking for required Python modules...")
06   print("#")
07   for package in PY_MOD_LIST:
08     print("   ")
09     has_mod = importlib.util.find_spec(package)
10     if has_mod:
11       print(f'  #:>The {package} module exists.')
12     else:
13       print(f'  #:>The {package} module absent.')
14       tmp_argv = sys.argv
15       sys.argv = ["pip", "install"] + [ package ]
16       try:
17         run_module("pip", run_name="__main__")
18       except SystemExit as exception:
19         print("exit code: ", exception.code)
20         if exception.code != 0 :
21           print(":: ")
22           print(f':: Error: The {package} module NOT installed; end tests!')
23           print(":: ")
24           sys.exit(2)
25       sys.argv = tmp_argv
26       print("  #:> ")
27       print(f'  #:>The {package} module installed.')
28       print("  #:> ")
29   print("")
30   input("\nPress any key to continue...\n")
31   print("")01 def run_lint_tests(file): #25-LOC
02   for py_mod in PY_MOD_LIST:
03     has_file = os.path.isfile(file)
04     if not has_file:
05       print(":: ")
06       print(f':: Error: File {file} not found; end tests!')
07       print(":: ")
08       return
09     if py_mod == "mccabe":
10       cmd = py_mod + '--min ' + MCCABE_MIN + ' ' + file
11     else:
12       cmd = py_mod + ' ' + file
13     print("  ::>")
14     print(f'  ::> {py_mod} on file: {file}')
15     print("  ::>")
16     result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True,
17               shell=True, check=False)
18     if len(result.stdout) == 0:
19       print("  ::> ")
20       print("  ::> [\nNo problems detected.", "\n  ::> ]")
21       print("  ::> ")
22     else:
23       print("  ::> ")
24       print("  ::> [\n", result.stdout, "\n  ::> ]")
25       print("  ::> ")

The run_lint_tests() function uses a for loop to iterate over PY_MOD_LIST and invokes each test on the Python source file, capturing the output that is reported to the user. If the Python source file does not exist, the function returns. For the McCabe lint check, the McCabe complexity's artificial CLI parameter is created using the Python script attribute MCCABE_MIN, which sets the minimum McCabe complexity threshold for an error in the check.

Buy this article as PDF

Download Article PDF now with Express Checkout
Price $2.95
(incl. VAT)

Buy Linux Magazine

Related content

  • Practical Python in Linux

    We’ll introduce you to Python, an easy-to-learn scripting language, and also help you get started creating your own practical Python scripts.

  • Python 3

    What do Python 2.x programmers need to know about Python 3?

  • Exploring /proc

    The Linux /proc virtual filesystem offers a window into a running system – look inside for information on processes and kernel activity.

  • RFID over SPI

    Inexpensive components for the SPI interface let you upgrade a Raspberry Pi 4 to a display system for zero-contact RFID-based data acquisition.

  • Adding Arguments with argparse

    Parse arguments at the command line with this powerful Python module.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News