A Python lint tool
Running a Lint Check
Each lint tool is called using the run() function from the sub-process module. If the return code is zero, the tool reports no problems and provides output, confirming that the lint tool ran (as opposed to only providing output if there is an error). If the return code indicates a failure, then the lint tool's captured output is reported to the user.
This linting process continues until all seven lint tests are performed on the specified Python source code files. After the lint checks are done, the Py7 tool indicates the end of the lint checks, reports all problems, and asks the user to acknowledge completion.
Instead of output going to a logfile or trace file, all output goes to standard output. If you want to capture the output, you can use the magic of pipes, redirection, and utilities. For example, you can capture output using the tee utility by piping output to a text file and redirecting the standard error to the standard output. In Bash, use:
./py7.py py7.py 2>&1 | tee py7.out.txt
Undoubtedly, there are other means to capture Py7's output. For instance, you could also annotate other information, such as line numbers, via the cat -n' utility.
Putting Py7 to Work
To show Py7 at work, I have run it on Py7 itself, a Python shell, and other Python source code files. First, I tested Py7 on itself by running the tool on the Py7 Python source code file. Doing so allowed me to find and fix several possible issues as I developed, debugged, and optimized the Python code. After completing this test, I was presented with two remaining possible issues found by the Bandit lint tool as shown in Listing 5. Each is an issue with a sub-process call's security. In some instances, this would be significant, but not in the context of the Py7 tool. Here, it is a false positive because Py7 does not modify the Python source code and only invokes other tools via the sub-process. As mentioned earlier, Py7's reported issues are guidelines or suggestions.
Listing 5
Security Issues Found by Bandit
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 >> Issue: [B404:blacklist] Consider possible security implications associated with the subprocess module.
02 Severity: Low Confidence: High
03 CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
04 More Info: https://bandit.readthedocs.io/en/1.8.3/blacklists/blacklist_imports.html#b404-import-subprocess
05 Location: ./py7.py:44:0
06 43from runpy import run_module
07 44from subprocess import PIPE, run
08 45
09 --------------------------------------------------
10
11 >> Issue: [B602:subprocess_popen_with_shell_equals_true] subprocess call with shell=True identified, security issue.
12 Severity: High Confidence: High
13 CWE: CWE-78 (https://cwe.mitre.org/data/definitions/78.html)
14 More Info: https://bandit.readthedocs.io/en/1.8.3/plugins/b602_subprocess_popen_with_shell_equals_true.html
15 Location: ./py7.py:86:17
16 85 result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True,
17 86 shell=True, check=False)
18 87
19 88 if len(result.stdout) == 0:
20 --------------------------------------------------
I also ran Py7 on the psh [11] Python shell (about 80 lines of code), written by Danish Praka, which resulted in multiple lint reports of problems. Py7's output for psh, along with the Python source code file, is available in Py7's GitHub repository [3]. Listing 6 shows the significant results with the duplicates removed.
Listing 6
Running Py7 on psh (Duplicates Removed)
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 psh.py:82:0: C0305: Trailing newlines (trailing-newlines)
02 psh.py:4:0: W0105: String statement has no effect (pointless-string-statement)
03 psh.py:6:0: C0413: Import "import os" should be placed at the top of the module (wrong-import-position)
04 psh.py:7:0: C0413: Import "import subprocess" should be placed at the top of the module (wrong-import-position)
05 psh.py:50:11: W0718: Catching too general exception Exception (broad-exception-caught)
06 psh.py:39:20: W1510: 'subprocess.run' used without explicitly defining the value for 'check'. (subprocess-run-check)
07 psh.py:41:26: C0209: Formatting a regular string which could be an f-string (consider-using-f-string)
08 psh.py:62:0: C0116: Missing function or method docstring (missing-function-docstring)
09 psh.py:70:8: R1723: Unnecessary "elif" after "break", remove the leading "el" from "elif" (no-else-break)
10 -----------------------------------
11 Your code has been rated at 6.88/10
Finally, I ran Py7 against the Tinycat BASIC programming language interpreter (basic.py) [12] and a two-line Python interpreter [13]. You can find the output and results for both on the Py7 GitHub page [3].
Perhaps more Python source code files will be tested with the py7.py tool.
Room for Improvement
Because Py7 is open source, it has the potential to be improved upon by others. Perhaps Py7 could be used as a plugin inside a source code editor to lint Python code or as an external tool to invoke the source code file for editing and revision, making it both an editor and a linter. Additionally, P7 could be used in a source code repository to lint Python source code as it's being checked into the archive or after a merge with a branch. By adding an option for non-interactive operation, Py7 could be run against the specified Python source code files as command-line parameters, with the lint results output to the standard console and a default logfile.
« Previous 1 2 3 4 Next »
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
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.
News
-
CIQ Releases Compatibility Catalog for Rocky Linux
The company behind Rocky Linux is making an open catalog available to developers, hobbyists, and other contributors, so they can verify and publish compatibility with the CIQ lineup.
-
KDE Gets Some Resuscitation
KDE is bringing back two themes that vanished a few years ago, putting a bit more air under its wings.
-
Ubuntu 26.04 Beta Arrives with Some Surprises
Ubuntu 26.04 is almost here, but the beta version has been released, and it might surprise some people.
-
Ubuntu MATE Dev Leaving After 12 years
Martin Wimpress, the maintainer of Ubuntu MATE, is now searching for his successor. Are you the next in line?
-
Kali Linux Waxes Nostalgic with BackTrack Mode
For those who've used Kali Linux since its inception, the changes with the new release are sure to put a smile on your face.
-
Gnome 50 Smooths Out NVIDIA GPU Issues
Gamers rejoice, your favorite pastime just got better with Gnome 50 and NVIDIA GPUs.
-
System76 Retools Thelio Desktop
The new Thelio Mira has landed with improved performance, repairability, and front-facing ports alongside a high-quality tempered glass facade.
-
Some Linux Distros Skirt Age Verification Laws
After California introduced an age verification law recently, open source operating system developers have had to get creative with how they deal with it.
-
UN Creates Open Source Portal
In a quest to strengthen open source collaboration, the United Nations Office of Information and Communications Technology has created a new portal.
-
Latest Linux Kernel RC Contains Changes Galore
Linux kernel 7.0-rc3 includes more changes than have been made in a single release in recent history.
