Source code for mlx.warnings.warnings_checker
import abc
import os
import re
from string import Template
from .exceptions import WarningsConfigError
[docs]class WarningsChecker:
name = 'checker'
def __init__(self, verbose=False):
''' Constructor
Args:
name (str): Name of the checker
verbose (bool): Enable/disable verbose logging
'''
self.verbose = verbose
self.count = 0
self.warn_min = 0
self.warn_max = 0
self._counted_warnings = []
self.cq_findings = []
self.cq_enabled = False
self.cq_default_path = '.gitlab-ci.yml'
self._cq_description_template = Template('$description')
self.exclude_patterns = []
self.include_patterns = []
@property
def counted_warnings(self):
''' List: list of counted warnings (str) '''
return self._counted_warnings
@property
def cq_description_template(self):
''' Template: string.Template instance based on the configured template string '''
return self._cq_description_template
@cq_description_template.setter
def cq_description_template(self, template_obj):
try:
template_obj.template = template_obj.substitute(os.environ, description='$description')
except KeyError as err:
raise WarningsConfigError(f"Failed to find environment variable from configuration value "
f"'cq_description_template': {err}") from err
self._cq_description_template = template_obj
[docs] @abc.abstractmethod
def check(self, content):
''' Function for counting the number of warnings in a specific text
Args:
content (str): The content to parse
'''
return
[docs] def add_patterns(self, regexes, pattern_container):
''' Adds regexes as patterns to the specified container
Args:
regexes (list[str]|None): List of regexes to add
pattern_container (list[re.Pattern]): Target storage container for patterns
'''
if regexes:
if not isinstance(regexes, list):
raise TypeError("Expected a list value for exclude key in configuration file; got {}"
.format(regexes.__class__.__name__))
for regex in regexes:
pattern_container.append(re.compile(regex))
[docs] def set_maximum(self, maximum):
''' Setter function for the maximum amount of warnings
Args:
maximum (int): maximum amount of warnings allowed
Raises:
ValueError: Invalid argument (min limit higher than max limit)
'''
if self.warn_min > maximum:
raise ValueError("Invalid argument: maximum limit must be higher than minimum limit ({min}); cannot "
"set {max}.".format(max=maximum, min=self.warn_min))
self.warn_max = maximum
[docs] def get_maximum(self):
''' Getter function for the maximum amount of warnings
Returns:
int: Maximum amount of warnings
'''
return self.warn_max
[docs] def set_minimum(self, minimum):
''' Setter function for the minimum amount of warnings
Args:
minimum (int): minimum amount of warnings allowed
Raises:
ValueError: Invalid argument (min limit higher than max limit)
'''
if minimum > self.warn_max:
raise ValueError("Invalid argument: minimum limit must be lower than maximum limit ({max}); cannot "
"set {min}.".format(min=minimum, max=self.warn_max))
self.warn_min = minimum
[docs] def get_minimum(self):
''' Getter function for the minimum amount of warnings
Returns:
int: Minimum amount of warnings
'''
return self.warn_min
[docs] def return_count(self):
''' Getter function for the amount of warnings found
Returns:
int: Number of warnings found
'''
print("{0.count} {0.name} warnings found".format(self))
return self.count
[docs] def return_check_limits(self):
''' Function for checking whether the warning count is within the configured limits
Returns:
int: 0 if the amount of warnings is within limits, the count of warnings otherwise
(or 1 in case of a count of 0 warnings)
'''
if self.count > self.warn_max or self.count < self.warn_min:
return self._return_error_code()
elif self.warn_min == self.warn_max and self.count == self.warn_max:
print("Number of warnings ({0.count}) is exactly as expected. Well done."
.format(self))
else:
print("Number of warnings ({0.count}) is between limits {0.warn_min} and {0.warn_max}. Well done."
.format(self))
return 0
def _return_error_code(self):
''' Function for determining the return code and message on failure
Returns:
int: The count of warnings (or 1 in case of a count of 0 warnings)
'''
if self.count > self.warn_max:
error_reason = "higher than the maximum limit ({0.warn_max})".format(self)
else:
error_reason = "lower than the minimum limit ({0.warn_min})".format(self)
error_code = self.count
if error_code == 0:
error_code = 1
print("Number of warnings ({0.count}) is {1}. Returning error code {2}."
.format(self, error_reason, error_code))
return error_code
[docs] def print_when_verbose(self, message):
''' Prints message only when verbose mode is enabled.
Args:
message (str): Message to conditionally print
'''
if self.verbose:
print(message)
[docs] def parse_config(self, config):
self.set_maximum(int(config['max']))
self.set_minimum(int(config['min']))
self.add_patterns(config.get("exclude"), self.exclude_patterns)
if 'cq_default_path' in config:
self.cq_default_path = config['cq_default_path']
if 'cq_description_template' in config:
self.cq_description_template = Template(config['cq_description_template'])
def _is_excluded(self, content):
''' Checks if the specific text must be excluded based on the configured regexes for exclusion and inclusion.
Inclusion has priority over exclusion.
Args:
content (str): The content to parse
Returns:
bool: True for exclusion, False for inclusion
'''
matching_exclude_pattern = self._search_patterns(content, self.exclude_patterns)
if not self._search_patterns(content, self.include_patterns) and matching_exclude_pattern:
self.print_when_verbose("Excluded {!r} because of configured regex {!r}"
.format(content, matching_exclude_pattern))
return True
return False
@staticmethod
def _search_patterns(content, patterns):
''' Returns the regex of the first pattern that matches specified content, None if nothing matches '''
for pattern in patterns:
if pattern.search(content):
return pattern.pattern
return None