Module 4
Software Engineering Project Management
Unit 10
Software Quality Monitoring in Python
Learning Outcomes
- Use Python linters to implement quality code.
- Describe how the concept of software quality has evolved over time.
- Make suggestions on techniques to recover a software engineering project which has been considered to have failed.
e-Portfolio Activity: Using Linters to Achieve Python Code Quality
Copy the following Python code into a file named
code_with_lint.py
. Now run the code against a variety
of linters to test the code quality:
- pylint code_with_lint.py
- pyflakes code_with_lint.py
- pycodestyle code_with_lint.py
- pydocstyle code_with_lint.py
Compare the effectiveness of each tool in defining and identifying code quality. What can you conclude about the effectiveness of each approach?
Code:
import io
from math import *
from time import time
some_global_var = 'GLOBAL VAR NAMES SHOULD BE IN ALL_CAPS_WITH_UNDERSCOES'
def multiply(x, y):
"""
This returns the result of a multiplation of the inputs
"""
some_global_var = 'this is actually a local variable...'
result = x* y
return result
if result == 777:
print("jackpot!")
def is_sum_lucky(x, y):
"""This returns a string describing whether or not the sum of input is lucky
This function first makes sure the inputs are valid and then calculates the
sum. Then, it will determine a message to return based on whether or not
that sum should be considered "lucky"
"""
if x != None:
if y is not None:
result = x+y;
if result == 7:
return 'a lucky number!'
else:
return( 'an unlucky number!')
return ('just a normal number')
class SomeClass:
def __init__(self, some_arg, some_other_arg, verbose = False):
self.some_other_arg = some_other_arg
self.some_arg = some_arg
list_comprehension = [((100/value)*pi) for value in some_arg if value != 0]
time = time()
from datetime import datetime
date_and_time = datetime.now()
return
Pylint
************* Module code_with_lint
code_with_lint.py:27:0: W0301: Unnecessary semicolon (unnecessary-semicolon)
code_with_lint.py:31:0: C0325: Unnecessary parens after 'return' keyword (superfluous-parens)
code_with_lint.py:33:0: C0325: Unnecessary parens after 'return' keyword (superfluous-parens)
code_with_lint.py:1:0: C0114: Missing module docstring (missing-module-docstring)
code_with_lint.py:2:0: W0622: Redefining built-in 'pow' (redefined-builtin)
code_with_lint.py:2:0: W0401: Wildcard import math (wildcard-import)
code_with_lint.py:7:0: C0103: Constant name "some_global_var" doesn't conform to UPPER_CASE naming style (invalid-name)
code_with_lint.py:13:4: W0621: Redefining name 'some_global_var' from outer scope (line 7) (redefined-outer-name)
code_with_lint.py:16:4: W0101: Unreachable code (unreachable)
code_with_lint.py:13:4: W0612: Unused variable 'some_global_var' (unused-variable)
code_with_lint.py:25:7: C0121: Comparison 'x != None' should be 'x is not None' (singleton-comparison)
code_with_lint.py:28:12: R1705: Unnecessary "else" after "return", remove the "else" and de-indent the code inside it (no-else-return)
code_with_lint.py:19:0: R1710: Either all return statements in a function should return an expression, or none of them should. (inconsistent-return-statements)
code_with_lint.py:35:0: C0115: Missing class docstring (missing-class-docstring)
code_with_lint.py:41:8: W0621: Redefining name 'time' from outer scope (line 5) (redefined-outer-name)
code_with_lint.py:41:15: E0601: Using variable 'time' before assignment (used-before-assignment)
code_with_lint.py:42:8: C0415: Import outside toplevel (datetime.datetime) (import-outside-toplevel)
code_with_lint.py:37:4: R1711: Useless return at end of function or method (useless-return)
code_with_lint.py:37:50: W0613: Unused argument 'verbose' (unused-argument)
code_with_lint.py:40:8: W0612: Unused variable 'list_comprehension' (unused-variable)
code_with_lint.py:43:8: W0612: Unused variable 'date_and_time' (unused-variable)
code_with_lint.py:35:0: R0903: Too few public methods (0/2) (too-few-public-methods)
code_with_lint.py:1:0: W0611: Unused import io (unused-import)
code_with_lint.py:5:0: W0611: Unused time imported from time (unused-import)
code_with_lint.py:2:0: W0614: Unused import(s) acos, acosh, asin, asinh, atan, atan2, atanh, cbrt, ceil, comb, copysign, cos, cosh, degrees, dist, e, erf, erfc, exp, exp2, expm1, fabs, factorial, floor, fmod, frexp, fsum, gamma, gcd, hypot, inf, isclose, isfinite, isinf, isnan, isqrt, lcm, ldexp, lgamma, log, log10, log1p, log2, modf, nan, nextafter, perm, pow, prod, radians, remainder, sin, sinh, sqrt, sumprod, tan, tanh, tau, trunc and ulp from wildcard import of math (unused-wildcard-import)
------------------------------------------------------------------
Your code has been rated at 0.00/10 (previous run: 0.00/10, +0.00)
Pyflakes
.\code_with_lint.py:1:1: 'io' imported but unused
.\code_with_lint.py:2:1: 'from math import *' used; unable to detect undefined names
.\code_with_lint.py:13:5: local variable 'some_global_var' is assigned to but never used
.\code_with_lint.py:40:44: 'pi' may be undefined, or defined from star imports: math
.\code_with_lint.py:40:9: local variable 'list_comprehension' is assigned to but never used
.\code_with_lint.py:41:16: local variable 'time' defined in enclosing scope on line 5 referenced before assignment
.\code_with_lint.py:41:9: local variable 'time' is assigned to but never used
.\code_with_lint.py:43:9: local variable 'date_and_time' is assigned to but never used
Pycodestyle
.\code_with_lint.py:9:1: E302 expected 2 blank lines, found 1
.\code_with_lint.py:14:15: E225 missing whitespace around operator
.\code_with_lint.py:19:1: E302 expected 2 blank lines, found 1
.\code_with_lint.py:20:80: E501 line too long (80 > 79 characters)
.\code_with_lint.py:25:10: E711 comparison to None should be 'if cond is not None:'
.\code_with_lint.py:27:25: E703 statement ends with a semicolon
.\code_with_lint.py:31:23: E275 missing whitespace after keyword
.\code_with_lint.py:31:24: E201 whitespace after '('
.\code_with_lint.py:35:1: E302 expected 2 blank lines, found 1
.\code_with_lint.py:37:58: E251 unexpected spaces around keyword / parameter equals
.\code_with_lint.py:37:60: E251 unexpected spaces around keyword / parameter equals
.\code_with_lint.py:38:28: E221 multiple spaces before operator
.\code_with_lint.py:38:31: E222 multiple spaces after operator
.\code_with_lint.py:39:22: E221 multiple spaces before operator
.\code_with_lint.py:39:31: E222 multiple spaces after operator
.\code_with_lint.py:40:80: E501 line too long (83 > 79 characters)
Pydocstyle
.\code_with_lint.py:1 at module level:
D100: Missing docstring in public module
.\code_with_lint.py:10 in public function `multiply`:
D200: One-line docstring should fit on one line with quotes (found 3)
.\code_with_lint.py:10 in public function `multiply`:
D400: First line should end with a period (not 's')
.\code_with_lint.py:10 in public function `multiply`:
D401: First line should be in imperative mood; try rephrasing (found 'This')
.\code_with_lint.py:20 in public function `is_sum_lucky`:
D205: 1 blank line required between summary line and description (found 0)
.\code_with_lint.py:20 in public function `is_sum_lucky`:
D400: First line should end with a period (not 'y')
.\code_with_lint.py:20 in public function `is_sum_lucky`:
D401: First line should be in imperative mood; try rephrasing (found 'This')
.\code_with_lint.py:35 in public class `SomeClass`:
D101: Missing docstring in public class
.\code_with_lint.py:37 in public method `__init__`:
D107: Missing docstring in __init__
Among the linters tested, pylint
proved to be the
most comprehensive and effective in identifying a wide range of
issues, from stylistic inconsistencies and naming convention
violations to logical errors such as unreachable code and
redefined variables. It provided a detailed breakdown, even
assigning a score to measure overall code quality.
pyflakes
, while efficient, focused primarily on
unused variables and imports but lacked deeper style analysis.
pycodestyle
effectively highlighted formatting
issues, such as missing spaces and excessive line lengths, but
did not catch logical problems. pydocstyle
, on the
other hand, strictly enforced documentation standards, pointing
out missing or improperly formatted docstrings.
Reflection
- Unit 10 introduced me to a range of Python linters. While previously familiar with only Pylint, I realised the existence of diverse linters tailored for specific purposes.
- I continued to progress on the Unit 11 assessment. My presentation deck and transcript are now complete. With minor revisions, I anticipate being fully prepared.