or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analysis-runner.mdconfiguration.mdexceptions.mdfile-finding.mdformatters.mdindex.mdmessages.mdprofiles.mdtools.md

tools.mddocs/

0

# Tools

1

2

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.

3

4

## Capabilities

5

6

### ToolBase Abstract Class

7

8

Base class that all analysis tools must implement.

9

10

```python { .api }

11

class ToolBase(ABC):

12

pass

13

```

14

15

Abstract base class for all Prospector tools. Tools must implement the configure and run methods.

16

17

```python { .api }

18

@abstractmethod

19

def configure(self, prospector_config: "ProspectorConfig", found_files: FileFinder) -> Optional[tuple[Optional[Union[str, Path]], Optional[Iterable[Message]]]]

20

```

21

22

Configures the tool based on the provided configuration and discovered files.

23

24

**Parameters:**

25

- `prospector_config`: ProspectorConfig - Prospector configuration object

26

- `found_files`: FileFinder - File discovery object

27

28

**Returns:**

29

- `Optional[tuple]` - Two-element tuple or None:

30

- First element: Optional[Union[str, Path]] - Configuration source (file path or description), None for defaults

31

- Second element: Optional[Iterable[Message]] - Configuration messages/warnings, None if no issues

32

33

This method allows tools to:

34

- Discover tool-specific configuration files (e.g., `.pylintrc`, `pyproject.toml`)

35

- Validate configuration settings

36

- Report configuration issues as messages

37

- Use Prospector defaults if no tool-specific config is found

38

39

```python { .api }

40

@abstractmethod

41

def run(self, found_files: FileFinder) -> list[Message]

42

```

43

44

Executes the analysis tool and returns found issues.

45

46

**Parameters:**

47

- `found_files`: FileFinder - File discovery object containing files to analyze

48

49

**Returns:**

50

- `list[Message]` - List of analysis messages found by the tool

51

52

```python { .api }

53

def get_ignored_codes(self, line: str) -> list[tuple[str, int]]

54

```

55

56

Parses a line of code for inline ignore directives (e.g., `# noqa`).

57

58

**Parameters:**

59

- `line`: str - Line of source code to parse

60

61

**Returns:**

62

- `list[tuple[str, int]]` - List of (error_code, line_offset) tuples for codes to ignore

63

64

### Tool Registry

65

66

```python { .api }

67

TOOLS: dict[str, type[ToolBase]]

68

```

69

70

Registry of all available analysis tools.

71

72

**Available Tools:**

73

- `"dodgy"`: DodgyTool - Detects potentially dodgy code (hardcoded passwords, etc.)

74

- `"mccabe"`: McCabeTool - Cyclomatic complexity analysis

75

- `"pyflakes"`: PyFlakesTool - Fast static analysis for Python

76

- `"pycodestyle"`: PycodestyleTool - PEP 8 style checker

77

- `"pylint"`: PylintTool - Comprehensive Python linter

78

- `"pydocstyle"`: PydocstyleTool - Docstring style checker

79

- `"profile-validator"`: ProfileValidationTool - Validates Prospector profiles

80

- `"vulture"`: VultureTool - Dead code finder (optional dependency)

81

- `"pyroma"`: PyromaTool - Python package quality checker (optional dependency)

82

- `"pyright"`: PyrightTool - Microsoft's Python type checker (optional dependency)

83

- `"mypy"`: MypyTool - Python type checker (optional dependency)

84

- `"bandit"`: BanditTool - Security linter (optional dependency)

85

- `"ruff"`: RuffTool - Fast Python linter and formatter (optional dependency)

86

87

```python { .api }

88

DEFAULT_TOOLS: tuple[str, ...]

89

```

90

91

Tuple of tool names that run by default.

92

93

**Default Tools:**

94

- `"dodgy"` - Always runs

95

- `"mccabe"` - Always runs

96

- `"pyflakes"` - Always runs

97

- `"pycodestyle"` - Always runs

98

- `"pylint"` - Always runs

99

- `"pydocstyle"` - Always runs

100

- `"profile-validator"` - Always runs

101

102

```python { .api }

103

DEPRECATED_TOOL_NAMES: dict[str, str]

104

```

105

106

Mapping of deprecated tool names to their current names.

107

108

**Deprecated Names:**

109

- `"pep8"``"pycodestyle"`

110

- `"pep257"``"pydocstyle"`

111

112

### Individual Tool Classes

113

114

#### Built-in Tools

115

116

```python { .api }

117

class DodgyTool(ToolBase):

118

pass

119

```

120

121

Detects potentially dodgy code patterns like hardcoded passwords, TODO comments, and other code smells.

122

123

```python { .api }

124

class McCabeTool(ToolBase):

125

pass

126

```

127

128

Measures cyclomatic complexity of functions and methods. Identifies overly complex code that may be hard to maintain.

129

130

```python { .api }

131

class PyFlakesTool(ToolBase):

132

pass

133

```

134

135

Fast static analysis tool that catches common Python errors like undefined variables, unused imports, and syntax errors.

136

137

```python { .api }

138

class PycodestyleTool(ToolBase):

139

pass

140

```

141

142

Checks Python code against PEP 8 style guidelines, including line length, indentation, whitespace, and naming conventions.

143

144

```python { .api }

145

class PylintTool(ToolBase):

146

pass

147

```

148

149

Comprehensive Python linter that checks for errors, enforces coding standards, looks for code smells, and suggests refactoring.

150

151

```python { .api }

152

class PydocstyleTool(ToolBase):

153

pass

154

```

155

156

Checks docstring style against PEP 257 conventions and other docstring standards.

157

158

```python { .api }

159

class ProfileValidationTool(ToolBase):

160

pass

161

```

162

163

Validates Prospector profile configuration files for syntax and semantic correctness.

164

165

#### Optional Tools

166

167

These tools require additional dependencies and are only available when installed:

168

169

```python { .api }

170

class VultureTool(ToolBase):

171

pass

172

```

173

174

Finds unused code (dead code) in Python programs. Install with `pip install prospector[with_vulture]`.

175

176

```python { .api }

177

class PyromaTool(ToolBase):

178

pass

179

```

180

181

Rates the quality of Python packages based on packaging best practices. Install with `pip install prospector[with_pyroma]`.

182

183

```python { .api }

184

class PyrightTool(ToolBase):

185

pass

186

```

187

188

Microsoft's fast static type checker for Python. Install with `pip install prospector[with_pyright]`.

189

190

```python { .api }

191

class MypyTool(ToolBase):

192

pass

193

```

194

195

Static type checker for Python that uses type hints. Install with `pip install prospector[with_mypy]`.

196

197

```python { .api }

198

class BanditTool(ToolBase):

199

pass

200

```

201

202

Security-focused linter that identifies common security issues in Python code. Install with `pip install prospector[with_bandit]`.

203

204

```python { .api }

205

class RuffTool(ToolBase):

206

pass

207

```

208

209

Fast Python linter and code formatter written in Rust. Install with `pip install prospector[with_ruff]`.

210

211

## Usage Examples

212

213

### Using Tool Registry

214

215

```python

216

from prospector import tools

217

218

# Get all available tools

219

print("Available tools:")

220

for name, tool_class in tools.TOOLS.items():

221

print(f" {name}: {tool_class.__name__}")

222

223

# Check default tools

224

print(f"\nDefault tools: {tools.DEFAULT_TOOLS}")

225

226

# Check for deprecated names

227

if "pep8" in tools.DEPRECATED_TOOL_NAMES:

228

new_name = tools.DEPRECATED_TOOL_NAMES["pep8"]

229

print(f"pep8 is deprecated, use {new_name} instead")

230

```

231

232

### Creating and Configuring Tools

233

234

```python

235

from prospector import tools

236

from prospector.config import ProspectorConfig

237

from prospector.finder import FileFinder

238

from pathlib import Path

239

240

# Create configuration and file finder

241

config = ProspectorConfig()

242

finder = FileFinder(Path("."))

243

244

# Create a specific tool

245

pylint_tool = tools.TOOLS["pylint"]()

246

247

# Configure the tool

248

config_result = pylint_tool.configure(config, finder)

249

if config_result:

250

config_source, config_messages = config_result

251

if config_source:

252

print(f"Pylint configured from: {config_source}")

253

if config_messages:

254

for msg in config_messages:

255

print(f"Config warning: {msg.message}")

256

257

# Run the tool

258

messages = pylint_tool.run(finder)

259

print(f"Pylint found {len(messages)} issues")

260

```

261

262

### Implementing a Custom Tool

263

264

```python

265

from prospector.tools.base import ToolBase

266

from prospector.finder import FileFinder

267

from prospector.message import Message, Location

268

from typing import TYPE_CHECKING, Optional, Iterable, Union

269

from pathlib import Path

270

271

if TYPE_CHECKING:

272

from prospector.config import ProspectorConfig

273

274

class CustomTool(ToolBase):

275

"""Example custom analysis tool"""

276

277

def configure(self, prospector_config: "ProspectorConfig", found_files: FileFinder) -> Optional[tuple[Optional[Union[str, Path]], Optional[Iterable[Message]]]]:

278

# No external configuration needed

279

return None

280

281

def run(self, found_files: FileFinder) -> list[Message]:

282

messages = []

283

284

# Analyze each Python module

285

for module_path in found_files.iter_all_modules():

286

try:

287

with open(module_path, 'r', encoding='utf-8') as f:

288

lines = f.readlines()

289

290

# Check for TODO comments

291

for line_num, line in enumerate(lines, 1):

292

if 'TODO' in line:

293

location = Location(

294

path=module_path,

295

module=None,

296

function=None,

297

line=line_num,

298

character=line.find('TODO')

299

)

300

301

message = Message(

302

source="custom-tool",

303

code="TODO",

304

location=location,

305

message="TODO comment found",

306

is_fixable=False

307

)

308

messages.append(message)

309

310

except (OSError, UnicodeDecodeError):

311

# Skip files that can't be read

312

continue

313

314

return messages

315

316

# Register custom tool (in practice, this would be done in __init__.py)

317

# tools.TOOLS["custom"] = CustomTool

318

```

319

320

### Working with Optional Tools

321

322

```python

323

from prospector import tools

324

325

def check_optional_tool(tool_name: str):

326

"""Check if an optional tool is available"""

327

try:

328

tool_class = tools.TOOLS[tool_name]

329

tool = tool_class()

330

print(f"{tool_name} is available")

331

return True

332

except Exception as e:

333

print(f"{tool_name} is not available: {e}")

334

return False

335

336

# Check availability of optional tools

337

optional_tools = ["mypy", "bandit", "vulture", "pyroma", "pyright", "ruff"]

338

for tool_name in optional_tools:

339

check_optional_tool(tool_name)

340

```

341

342

### Tool Configuration Discovery

343

344

```python

345

from prospector import tools

346

from prospector.config import ProspectorConfig

347

from prospector.finder import FileFinder

348

from pathlib import Path

349

350

config = ProspectorConfig()

351

finder = FileFinder(Path("."))

352

353

# Check how each tool gets configured

354

for tool_name in tools.DEFAULT_TOOLS:

355

tool_class = tools.TOOLS[tool_name]

356

tool = tool_class()

357

358

try:

359

result = tool.configure(config, finder)

360

if result:

361

config_source, config_messages = result

362

if config_source:

363

print(f"{tool_name}: configured from {config_source}")

364

else:

365

print(f"{tool_name}: using defaults")

366

367

if config_messages:

368

print(f" {len(list(config_messages))} config messages")

369

else:

370

print(f"{tool_name}: no configuration")

371

372

except Exception as e:

373

print(f"{tool_name}: configuration error - {e}")

374

```

375

376

### Analyzing Inline Ignore Directives

377

378

```python

379

from prospector.tools.base import ToolBase

380

381

# Create a tool instance to test ignore parsing

382

tool = ToolBase() # This would normally be a concrete tool

383

384

# Test lines with ignore directives

385

test_lines = [

386

"print('hello') # noqa",

387

"import os # noqa: F401",

388

"x = 1 # noqa: E501,W292",

389

"def func(): # pylint: disable=invalid-name",

390

"regular_line_no_ignore()"

391

]

392

393

for line in test_lines:

394

ignored_codes = tool.get_ignored_codes(line)

395

if ignored_codes:

396

print(f"Line: {line}")

397

print(f" Ignores: {ignored_codes}")

398

else:

399

print(f"Line: {line} (no ignores)")

400

```