Prospector is a tool to analyse Python code by aggregating the result of other tools.
—
Standardized data structures for representing analysis results and source code locations. The Message and Location classes provide a unified format for all analysis tool outputs.
Represents a single analysis issue or finding from a static analysis tool.
class Message:
def __init__(self, source: str, code: str, location: Location, message: str,
doc_url: Optional[str] = None, is_fixable: bool = False) -> NoneCreates a new message representing an analysis finding.
Parameters:
source: str - Name of the tool that generated this message (e.g., "pylint", "pyflakes")code: str - Error/warning code from the tool (e.g., "E501", "unused-import")location: Location - Source code location where the issue was foundmessage: str - Human-readable description of the issuedoc_url: Optional[str] - URL to documentation about this issue typeis_fixable: bool - Whether this issue can be automatically fixedProperties:
source: str - Tool that generated the messagecode: str - Error/warning codelocation: Location - Location of the issuemessage: str - Human-readable message textdoc_url: Optional[str] - Documentation URLis_fixable: bool - Auto-fixable flagComparison Methods: Messages can be compared and sorted. Two messages are equal if they have the same location and code. Messages are sorted first by location, then by code.
def __eq__(self, other: object) -> booldef __lt__(self, other: "Message") -> booldef __repr__(self) -> strRepresents a specific location in source code where an issue was found.
class Location:
def __init__(self, path: Optional[Union[Path, str]], module: Optional[str],
function: Optional[str], line: Optional[int], character: Optional[int],
line_end: Optional[int] = None, character_end: Optional[int] = None) -> NoneCreates a new location object.
Parameters:
path: Optional[Union[Path, str]] - File path (converted to absolute Path)module: Optional[str] - Python module namefunction: Optional[str] - Function or method name where issue occursline: Optional[int] - Line number (1-based, None for file-level issues)character: Optional[int] - Character position on the line (0-based)line_end: Optional[int] - End line number for multi-line issuescharacter_end: Optional[int] - End character position for multi-line issuesProperties:
path: Optional[Path] - Absolute file pathmodule: Optional[str] - Module namefunction: Optional[str] - Function nameline: Optional[int] - Line numbercharacter: Optional[int] - Character positionline_end: Optional[int] - End line numbercharacter_end: Optional[int] - End character positiondef absolute_path(self) -> Optional[Path]Returns the absolute path to the file.
Returns:
Optional[Path] - Absolute path or None if no path was provideddef relative_path(self, root: Optional[Path]) -> Optional[Path]Returns the path relative to the specified root directory.
Parameters:
root: Optional[Path] - Root directory for relative path calculationReturns:
Optional[Path] - Relative path if possible, otherwise absolute pathComparison Methods: Locations can be compared and sorted. Comparison is done by path first, then line, then character.
def __eq__(self, other: object) -> booldef __lt__(self, other: "Location") -> booldef __hash__(self) -> intdef __repr__(self) -> strdef make_tool_error_message(filepath: Union[Path, str], source: str, code: str, message: str,
line: Optional[int] = None, character: Optional[int] = None,
module: Optional[str] = None, function: Optional[str] = None) -> MessageConvenience function for creating Message objects for tool errors.
Parameters:
filepath: Union[Path, str] - Path to the filesource: str - Tool namecode: str - Error codemessage: str - Error messageline: Optional[int] - Line numbercharacter: Optional[int] - Character positionmodule: Optional[str] - Module namefunction: Optional[str] - Function nameReturns:
Message - New Message object with the specified parametersfrom pathlib import Path
from prospector.message import Message, Location, make_tool_error_message
# Create a location
location = Location(
path=Path("src/main.py"),
module="main",
function="calculate",
line=42,
character=10
)
# Create a message
message = Message(
source="pylint",
code="C0103",
location=location,
message="Invalid name 'x' for variable",
doc_url="https://pylint.pycqa.org/en/latest/messages/convention/invalid-name.html",
is_fixable=False
)
print(f"Issue: {message.message}")
print(f"Location: {message.location}")
print(f"From: {message.source}")from prospector.message import make_tool_error_message
# Create a message using the convenience function
message = make_tool_error_message(
filepath="src/utils.py",
source="pyflakes",
code="unused-import",
message="'os' imported but unused",
line=1,
character=0,
module="utils"
)
print(f"{message.source}: {message.message}")from pathlib import Path
from prospector.message import Location
# Create location with minimal information
location = Location(
path="src/main.py",
module=None,
function=None,
line=100,
character=15
)
# Get absolute path
abs_path = location.absolute_path()
print(f"Absolute path: {abs_path}")
# Get relative path from project root
project_root = Path("/home/user/myproject")
rel_path = location.relative_path(project_root)
print(f"Relative path: {rel_path}")
# Location for file-level issues
file_location = Location(
path="setup.py",
module=None,
function=None,
line=None, # No specific line
character=None
)from prospector.message import Message, Location
# Create multiple messages
messages = [
Message("pylint", "E501", Location("file2.py", None, None, 10, 0), "Line too long"),
Message("pylint", "C0103", Location("file1.py", None, None, 5, 0), "Invalid name"),
Message("pyflakes", "unused", Location("file1.py", None, None, 5, 0), "Unused import"),
Message("pylint", "E501", Location("file1.py", None, None, 20, 0), "Line too long"),
]
# Sort messages (by location, then by code)
sorted_messages = sorted(messages)
for msg in sorted_messages:
print(f"{msg.location.path}:{msg.location.line} {msg.source}:{msg.code} {msg.message}")
# Check for duplicates
msg1 = Message("pylint", "E501", Location("test.py", None, None, 10, 0), "Error 1")
msg2 = Message("pylint", "E501", Location("test.py", None, None, 10, 0), "Error 2")
print(f"Are messages equal? {msg1 == msg2}") # True - same location and codefrom prospector.message import Message, Location
# Create location for multi-line issue
location = Location(
path="src/complex.py",
module="complex",
function="long_function",
line=50, # Start line
character=4, # Start character
line_end=55, # End line
character_end=8 # End character
)
message = Message(
source="pylint",
code="R0915",
location=location,
message="Too many statements (60/50)"
)
print(f"Issue spans lines {location.line}-{location.line_end}")Install with Tessl CLI
npx tessl i tessl/pypi-prospector