/
opt
/
cloudlinux
/
venv
/
lib
/
python3.11
/
site-packages
/
prospector
/
config
/
Upload Filee
HOME
import os import re import sys from pathlib import Path from typing import Dict, List, Union try: # Python >= 3.11 import re._constants as sre_constants except ImportError: import sre_constants from prospector import tools from prospector.autodetect import autodetect_libraries from prospector.compat import is_relative_to from prospector.config import configuration as cfg from prospector.message import Message from prospector.profiles import AUTO_LOADED_PROFILES from prospector.profiles.profile import BUILTIN_PROFILE_PATH, CannotParseProfile, ProfileNotFound, ProspectorProfile from prospector.tools import DEFAULT_TOOLS, DEPRECATED_TOOL_NAMES class ProspectorConfig: # There are several methods on this class which could technically # be functions (they don't use the 'self' argument) but that would # make this module/class a bit ugly. # Also the 'too many instance attributes' warning is ignored, as this # is a config object and its sole purpose is to hold many properties! def __init__(self, workdir: Path = None): self.config, self.arguments = self._configure_prospector() self.paths = self._get_work_path(self.config, self.arguments) self.explicit_file_mode = all(p.is_file for p in self.paths) self.workdir = workdir or Path.cwd() self.profile, self.strictness = self._get_profile(self.workdir, self.config) self.libraries = self._find_used_libraries(self.config, self.profile) self.tools_to_run = self._determine_tool_runners(self.config, self.profile) self.ignores = self._determine_ignores(self.config, self.profile, self.libraries) self.configured_by: Dict[str, str] = {} self.messages: List[Message] = [] def make_exclusion_filter(self): # Only close over the attributes required by the filter, rather # than the entire self, because ProspectorConfig can't be pickled # because of the config attribute, which would break parallel # pylint. ignores, workdir = self.ignores, self.workdir def _filter(path: Path): for ignore in ignores: # first figure out where the path is, relative to the workdir # ignore-paths/patterns will usually be relative to a repository # root or the CWD, but the path passed to prospector may not be path = path.resolve().absolute() if is_relative_to(path, workdir): path = path.relative_to(workdir) if ignore.match(str(path)): return True return False return _filter def get_tools(self, found_files): self.configured_by = {} runners = [] for tool_name in self.tools_to_run: tool = tools.TOOLS[tool_name]() config_result = tool.configure(self, found_files) if config_result is None: configured_by = None messages = [] else: configured_by, messages = config_result if messages is None: messages = [] self.configured_by[tool_name] = configured_by self.messages += messages runners.append(tool) return runners def replace_deprecated_tool_names(self) -> List[str]: # pep8 was renamed pycodestyle ; pep257 was renamed pydocstyle # for backwards compatibility, these have been deprecated but will remain until prospector v2 deprecated_found = [] replaced = [] for tool_name in self.tools_to_run: if tool_name in DEPRECATED_TOOL_NAMES: replaced.append(DEPRECATED_TOOL_NAMES[tool_name]) deprecated_found.append(tool_name) else: replaced.append(tool_name) self.tools_to_run = replaced return deprecated_found def get_output_report(self): # Get the output formatter if self.config.output_format is not None: output_report = self.config.output_format else: output_report = [(self.profile.output_format, self.profile.output_target)] for index, report in enumerate(output_report): if not all(report): output_report[index] = (report[0] or "grouped", report[1] or []) return output_report def _configure_prospector(self): # first we will configure prospector as a whole mgr = cfg.build_manager() config = mgr.retrieve(*cfg.build_default_sources()) return config, mgr.arguments def _get_work_path(self, config, arguments) -> List[Path]: # Figure out what paths we're prospecting if config["path"]: paths = [Path(self.config["path"])] elif arguments["checkpath"]: paths = [Path(p) for p in arguments["checkpath"]] else: paths = [Path.cwd()] return [p.resolve() for p in paths] def _get_profile(self, workdir: Path, config): # Use the specified profiles profile_provided = False if len(config.profiles) > 0: profile_provided = True cmdline_implicit = [] # if there is a '.prospector.ya?ml' or a '.prospector/prospector.ya?ml' or equivalent landscape config # file then we'll include that profile_name: Union[None, str, Path] = None if not profile_provided: for possible_profile in AUTO_LOADED_PROFILES: prospector_yaml = os.path.join(workdir, possible_profile) if os.path.exists(prospector_yaml) and os.path.isfile(prospector_yaml): profile_provided = True profile_name = possible_profile break strictness = None if profile_provided: if profile_name is None: profile_name = config.profiles[0] extra_profiles = config.profiles[1:] else: extra_profiles = config.profiles strictness = "from profile" else: # Use the preconfigured prospector profiles profile_name = "default" extra_profiles = [] if config.doc_warnings is not None and config.doc_warnings: cmdline_implicit.append("doc_warnings") if config.test_warnings is not None and config.test_warnings: cmdline_implicit.append("test_warnings") if config.no_style_warnings is not None and config.no_style_warnings: cmdline_implicit.append("no_pep8") if config.full_pep8 is not None and config.full_pep8: cmdline_implicit.append("full_pep8") if config.member_warnings is not None and config.member_warnings: cmdline_implicit.append("member_warnings") # Use the strictness profile only if no profile has been given if config.strictness is not None and config.strictness: cmdline_implicit.append("strictness_%s" % config.strictness) strictness = config.strictness # the profile path is # * anything provided as an argument # * a directory called .prospector in the check path # * the check path # * prospector provided profiles profile_path = [Path(path).absolute() for path in config.profile_path] prospector_dir = workdir / ".prospector" if os.path.exists(prospector_dir) and os.path.isdir(prospector_dir): profile_path.append(prospector_dir) profile_path.append(workdir) profile_path.append(BUILTIN_PROFILE_PATH) try: forced_inherits = cmdline_implicit + extra_profiles profile = ProspectorProfile.load(profile_name, profile_path, forced_inherits=forced_inherits) except CannotParseProfile as cpe: sys.stderr.write( "Failed to run:\nCould not parse profile %s as it is not valid YAML\n%s\n" % (cpe.filepath, cpe.get_parse_message()) ) sys.exit(1) except ProfileNotFound as nfe: search_path = ":".join(map(str, nfe.profile_path)) profile = nfe.name.split(":")[0] sys.stderr.write( f"""Failed to run: Could not find profile {nfe.name}. Search path: {search_path}, or in module 'prospector_profile_{profile}' """ ) sys.exit(1) else: return profile, strictness def _find_used_libraries(self, config, profile): libraries = [] # Bring in adaptors that we automatically detect are needed if config.autodetect and profile.autodetect is True: for found_dep in autodetect_libraries(self.workdir): libraries.append(found_dep) # Bring in adaptors for the specified libraries for name in set(config.uses + profile.uses): if name not in libraries: libraries.append(name) return libraries def _determine_tool_runners(self, config, profile): if config.tools is None: # we had no command line settings for an explicit list of # tools, so we use the defaults to_run = set(DEFAULT_TOOLS) # we can also use any that the profiles dictate for tool in tools.TOOLS: if profile.is_tool_enabled(tool): to_run.add(tool) else: to_run = set(config.tools) # profiles have no say in the list of tools run when # a command line is specified for tool in config.with_tools: to_run.add(tool) for tool in config.without_tools: if tool in to_run: to_run.remove(tool) # if config.tools is None and len(config.with_tools) == 0 and len(config.without_tools) == 0: for tool in tools.TOOLS: enabled = profile.is_tool_enabled(tool) if enabled is None: enabled = tool in DEFAULT_TOOLS if tool in to_run and not enabled and tool not in config.with_tools and tool not in (config.tools or []): # if this is not enabled in a profile but is asked for in a command line arg, we keep it, otherwise # remove it from the list to run to_run.remove(tool) return sorted(list(to_run)) def _determine_ignores(self, config, profile, libraries): # Grab ignore patterns from the options ignores = [] for pattern in config.ignore_patterns + profile.ignore_patterns: if pattern is None: # this can happen if someone has a profile with an empty ignore-patterns value, eg: # # ignore-patterns: # uses: django continue try: ignores.append(re.compile(pattern)) except sre_constants.error: pass # Convert ignore paths into patterns boundary = r"(^|/|\\)%s(/|\\|$)" for ignore_path in config.ignore_paths + profile.ignore_paths: ignore_path = str(ignore_path) if ignore_path.endswith("/") or ignore_path.endswith("\\"): ignore_path = ignore_path[:-1] ignores.append(re.compile(boundary % re.escape(ignore_path))) # some libraries have further automatic ignores if "django" in libraries: ignores += [re.compile("(^|/)(south_)?migrations(/|$)")] return ignores def get_summary_information(self): return { "libraries": self.libraries, "strictness": self.strictness, "profiles": ", ".join(self.profile.list_profiles()), "tools": self.tools_to_run, } def exit_with_zero_on_success(self): return self.config.zero_exit def get_disabled_messages(self, tool_name): return self.profile.get_disabled_messages(tool_name) def use_external_config(self, _): # Currently there is only one single global setting for whether to use # global config, but this could be extended in the future return not self.config.no_external_config def tool_options(self, tool_name): tool = getattr(self.profile, tool_name, None) if tool is None: return {} return tool.get("options", {}) def external_config_location(self, tool_name): return getattr(self.config, "%s_config_file" % tool_name, None) @property def die_on_tool_error(self): return self.config.die_on_tool_error @property def summary_only(self): return self.config.summary_only @property def messages_only(self): return self.config.messages_only @property def quiet(self): return self.config.quiet @property def blending(self): return self.config.blending @property def absolute_paths(self): return self.config.absolute_paths @property def max_line_length(self): return self.config.max_line_length @property def include_tool_stdout(self): return self.config.include_tool_stdout @property def direct_tool_stdout(self): return self.config.direct_tool_stdout @property def show_profile(self): return self.config.show_profile @property def legacy_tool_names(self) -> bool: return self.config.legacy_tool_names