Prospector is a tool to analyse Python code by aggregating the result of other tools.
—
Plugin system for integrating static analysis tools with base classes and tool registry. The tools framework allows Prospector to run multiple analysis tools with a unified interface.
Base class that all analysis tools must implement.
class ToolBase(ABC):
passAbstract base class for all Prospector tools. Tools must implement the configure and run methods.
@abstractmethod
def configure(self, prospector_config: "ProspectorConfig", found_files: FileFinder) -> Optional[tuple[Optional[Union[str, Path]], Optional[Iterable[Message]]]]Configures the tool based on the provided configuration and discovered files.
Parameters:
prospector_config: ProspectorConfig - Prospector configuration objectfound_files: FileFinder - File discovery objectReturns:
Optional[tuple] - Two-element tuple or None:
This method allows tools to:
.pylintrc, pyproject.toml)@abstractmethod
def run(self, found_files: FileFinder) -> list[Message]Executes the analysis tool and returns found issues.
Parameters:
found_files: FileFinder - File discovery object containing files to analyzeReturns:
list[Message] - List of analysis messages found by the tooldef get_ignored_codes(self, line: str) -> list[tuple[str, int]]Parses a line of code for inline ignore directives (e.g., # noqa).
Parameters:
line: str - Line of source code to parseReturns:
list[tuple[str, int]] - List of (error_code, line_offset) tuples for codes to ignoreTOOLS: dict[str, type[ToolBase]]Registry of all available analysis tools.
Available Tools:
"dodgy": DodgyTool - Detects potentially dodgy code (hardcoded passwords, etc.)"mccabe": McCabeTool - Cyclomatic complexity analysis"pyflakes": PyFlakesTool - Fast static analysis for Python"pycodestyle": PycodestyleTool - PEP 8 style checker"pylint": PylintTool - Comprehensive Python linter"pydocstyle": PydocstyleTool - Docstring style checker"profile-validator": ProfileValidationTool - Validates Prospector profiles"vulture": VultureTool - Dead code finder (optional dependency)"pyroma": PyromaTool - Python package quality checker (optional dependency)"pyright": PyrightTool - Microsoft's Python type checker (optional dependency)"mypy": MypyTool - Python type checker (optional dependency)"bandit": BanditTool - Security linter (optional dependency)"ruff": RuffTool - Fast Python linter and formatter (optional dependency)DEFAULT_TOOLS: tuple[str, ...]Tuple of tool names that run by default.
Default Tools:
"dodgy" - Always runs"mccabe" - Always runs"pyflakes" - Always runs"pycodestyle" - Always runs"pylint" - Always runs"pydocstyle" - Always runs"profile-validator" - Always runsDEPRECATED_TOOL_NAMES: dict[str, str]Mapping of deprecated tool names to their current names.
Deprecated Names:
"pep8" → "pycodestyle""pep257" → "pydocstyle"class DodgyTool(ToolBase):
passDetects potentially dodgy code patterns like hardcoded passwords, TODO comments, and other code smells.
class McCabeTool(ToolBase):
passMeasures cyclomatic complexity of functions and methods. Identifies overly complex code that may be hard to maintain.
class PyFlakesTool(ToolBase):
passFast static analysis tool that catches common Python errors like undefined variables, unused imports, and syntax errors.
class PycodestyleTool(ToolBase):
passChecks Python code against PEP 8 style guidelines, including line length, indentation, whitespace, and naming conventions.
class PylintTool(ToolBase):
passComprehensive Python linter that checks for errors, enforces coding standards, looks for code smells, and suggests refactoring.
class PydocstyleTool(ToolBase):
passChecks docstring style against PEP 257 conventions and other docstring standards.
class ProfileValidationTool(ToolBase):
passValidates Prospector profile configuration files for syntax and semantic correctness.
These tools require additional dependencies and are only available when installed:
class VultureTool(ToolBase):
passFinds unused code (dead code) in Python programs. Install with pip install prospector[with_vulture].
class PyromaTool(ToolBase):
passRates the quality of Python packages based on packaging best practices. Install with pip install prospector[with_pyroma].
class PyrightTool(ToolBase):
passMicrosoft's fast static type checker for Python. Install with pip install prospector[with_pyright].
class MypyTool(ToolBase):
passStatic type checker for Python that uses type hints. Install with pip install prospector[with_mypy].
class BanditTool(ToolBase):
passSecurity-focused linter that identifies common security issues in Python code. Install with pip install prospector[with_bandit].
class RuffTool(ToolBase):
passFast Python linter and code formatter written in Rust. Install with pip install prospector[with_ruff].
from prospector import tools
# Get all available tools
print("Available tools:")
for name, tool_class in tools.TOOLS.items():
print(f" {name}: {tool_class.__name__}")
# Check default tools
print(f"\nDefault tools: {tools.DEFAULT_TOOLS}")
# Check for deprecated names
if "pep8" in tools.DEPRECATED_TOOL_NAMES:
new_name = tools.DEPRECATED_TOOL_NAMES["pep8"]
print(f"pep8 is deprecated, use {new_name} instead")from prospector import tools
from prospector.config import ProspectorConfig
from prospector.finder import FileFinder
from pathlib import Path
# Create configuration and file finder
config = ProspectorConfig()
finder = FileFinder(Path("."))
# Create a specific tool
pylint_tool = tools.TOOLS["pylint"]()
# Configure the tool
config_result = pylint_tool.configure(config, finder)
if config_result:
config_source, config_messages = config_result
if config_source:
print(f"Pylint configured from: {config_source}")
if config_messages:
for msg in config_messages:
print(f"Config warning: {msg.message}")
# Run the tool
messages = pylint_tool.run(finder)
print(f"Pylint found {len(messages)} issues")from prospector.tools.base import ToolBase
from prospector.finder import FileFinder
from prospector.message import Message, Location
from typing import TYPE_CHECKING, Optional, Iterable, Union
from pathlib import Path
if TYPE_CHECKING:
from prospector.config import ProspectorConfig
class CustomTool(ToolBase):
"""Example custom analysis tool"""
def configure(self, prospector_config: "ProspectorConfig", found_files: FileFinder) -> Optional[tuple[Optional[Union[str, Path]], Optional[Iterable[Message]]]]:
# No external configuration needed
return None
def run(self, found_files: FileFinder) -> list[Message]:
messages = []
# Analyze each Python module
for module_path in found_files.iter_all_modules():
try:
with open(module_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
# Check for TODO comments
for line_num, line in enumerate(lines, 1):
if 'TODO' in line:
location = Location(
path=module_path,
module=None,
function=None,
line=line_num,
character=line.find('TODO')
)
message = Message(
source="custom-tool",
code="TODO",
location=location,
message="TODO comment found",
is_fixable=False
)
messages.append(message)
except (OSError, UnicodeDecodeError):
# Skip files that can't be read
continue
return messages
# Register custom tool (in practice, this would be done in __init__.py)
# tools.TOOLS["custom"] = CustomToolfrom prospector import tools
def check_optional_tool(tool_name: str):
"""Check if an optional tool is available"""
try:
tool_class = tools.TOOLS[tool_name]
tool = tool_class()
print(f"{tool_name} is available")
return True
except Exception as e:
print(f"{tool_name} is not available: {e}")
return False
# Check availability of optional tools
optional_tools = ["mypy", "bandit", "vulture", "pyroma", "pyright", "ruff"]
for tool_name in optional_tools:
check_optional_tool(tool_name)from prospector import tools
from prospector.config import ProspectorConfig
from prospector.finder import FileFinder
from pathlib import Path
config = ProspectorConfig()
finder = FileFinder(Path("."))
# Check how each tool gets configured
for tool_name in tools.DEFAULT_TOOLS:
tool_class = tools.TOOLS[tool_name]
tool = tool_class()
try:
result = tool.configure(config, finder)
if result:
config_source, config_messages = result
if config_source:
print(f"{tool_name}: configured from {config_source}")
else:
print(f"{tool_name}: using defaults")
if config_messages:
print(f" {len(list(config_messages))} config messages")
else:
print(f"{tool_name}: no configuration")
except Exception as e:
print(f"{tool_name}: configuration error - {e}")from prospector.tools.base import ToolBase
# Create a tool instance to test ignore parsing
tool = ToolBase() # This would normally be a concrete tool
# Test lines with ignore directives
test_lines = [
"print('hello') # noqa",
"import os # noqa: F401",
"x = 1 # noqa: E501,W292",
"def func(): # pylint: disable=invalid-name",
"regular_line_no_ignore()"
]
for line in test_lines:
ignored_codes = tool.get_ignored_codes(line)
if ignored_codes:
print(f"Line: {line}")
print(f" Ignores: {ignored_codes}")
else:
print(f"Line: {line} (no ignores)")Install with Tessl CLI
npx tessl i tessl/pypi-prospector