Source code for statick_tool.plugins.tool.lizard
"""Apply lizard tool and gather results."""
import io
import logging
import re
from contextlib import redirect_stdout
from typing import Match, Optional, Pattern
import lizard
from statick_tool.issue import Issue
from statick_tool.package import Package
from statick_tool.tool_plugin import ToolPlugin
[docs]
class LizardToolPlugin(ToolPlugin):
"""Apply Lizard tool and gather results.
Note: The `-f/--input_file`, `-o/--output_file`, and `-Edumpcomments`
options are unsupported.
"""
[docs]
def get_name(self) -> str:
"""Get name of tool.
Returns:
Name of the tool.
"""
return "lizard"
[docs]
def scan(self, package: Package, level: str) -> Optional[list[Issue]]:
"""Run tool and gather output.
Args:
package: The package to process.
level: The level to run the tool at.
Returns:
List of issues found or None.
"""
if not package.path:
return []
# The following is a modification of lizard.py's main().
raw_user_flags = (
[lizard.__file__] + [package.path] + self.get_user_flags(level)
) # Leading lizard file name is required.
# Make sure we log warnings.
if "-w" not in raw_user_flags:
raw_user_flags += ["-w"]
# Make sure unsupported arguments are not included.
user_flags = self.remove_invalid_flags(raw_user_flags)
options = lizard.parse_args(user_flags)
printer = options.printer or lizard.print_result
schema = lizard.OutputScheme(options.extensions)
schema.patch_for_extensions()
result = lizard.analyze(
options.paths,
options.exclude,
options.working_threads,
options.extensions,
options.languages,
)
lizard_output = io.StringIO()
with redirect_stdout(lizard_output):
printer(result, options, schema, lizard.AllResult)
output = lizard_output.getvalue()
lizard.print_extension_results(options.extensions)
logging.debug("%s", output)
if self.plugin_context and self.plugin_context.args.output_directory:
with open(self.get_name() + ".log", "w", encoding="utf8") as fid:
fid.write(output)
issues: list[Issue] = self.parse_tool_output(output)
return issues
[docs]
def parse_tool_output(self, output: str) -> list[Issue]:
"""Parse tool output and report issues.
Args:
output: The output from the tool.
Returns:
List of issues found.
"""
lizard_re = r"(.+):(\d+):\s(.+):\s(.+)"
parse: Pattern[str] = re.compile(lizard_re)
matches = []
for line in output.splitlines():
match: Optional[Match[str]] = parse.match(line)
if match:
matches.append(match.groups())
issues: list[Issue] = []
for item in matches:
issue = Issue(
item[0], int(item[1]), self.get_name(), item[2], 5, item[3], None
)
if issue not in issues:
issues.append(issue)
return issues
[docs]
def remove_invalid_flags(self, flag_list: list[str]) -> list[str]:
"""Filter out all disabled flags.
Args:
flag_list: List of flags to filter.
Returns:
List of valid flags.
"""
return [x for x in flag_list if self.valid_flag(x)]
[docs]
@classmethod
def valid_flag(cls, flag: str) -> bool:
"""Indicate if passed flag is invalid.
Args:
flag: The flag to check.
Returns:
Boolean indicating if the flag is valid.
"""
disabled_flags = ["-f", "--input_file", "-o", "--output_file", "-Edumpcomments"]
if flag in disabled_flags:
return False
return True