or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-interface.mdconfiguration.mdenvironment-system.mdexecution-system.mdindex.mdplugin-system.mdsession-management.md

plugin-system.mddocs/

0

# Plugin System

1

2

Hook-based extensibility system using pluggy that allows plugins to extend CLI options, configuration, environment types, and execution hooks. Tox's plugin system supports both entry-point plugins and toxfile.py plugins for maximum flexibility.

3

4

## Capabilities

5

6

### Plugin Hook Decorator

7

8

The core decorator for marking plugin hook implementations.

9

10

```python { .api }

11

impl: Callable[[_F], _F]

12

"""

13

Decorator to mark tox plugin hooks.

14

15

Usage:

16

@impl

17

def hook_function(...): ...

18

"""

19

20

NAME: str = "tox"

21

"""The name of the tox hook system."""

22

```

23

24

Usage example:

25

```python

26

from tox.plugin import impl

27

28

@impl

29

def tox_add_option(parser):

30

parser.add_argument("--my-option", help="Custom option")

31

```

32

33

### Hook Specifications

34

35

The complete set of hooks available for plugin development.

36

37

```python { .api }

38

class ToxHookSpecs:

39

"""Hook specification definitions for tox plugins."""

40

41

def tox_add_option(self, parser: ToxParser) -> None:

42

"""

43

Add command line options.

44

45

Args:

46

parser: Command line parser to extend

47

"""

48

49

def tox_add_core_config(self, core_conf: CoreConfigSet) -> None:

50

"""

51

Add core configuration options.

52

53

Args:

54

core_conf: Core configuration set to extend

55

"""

56

57

def tox_add_env_config(self, env_conf: EnvConfigSet) -> None:

58

"""

59

Add environment-specific configuration options.

60

61

Args:

62

env_conf: Environment configuration set to extend

63

"""

64

65

def tox_register_tox_env(self, register) -> None:

66

"""

67

Register new environment types.

68

69

Args:

70

register: Environment registry to extend

71

"""

72

73

def tox_extend_envs(self) -> list[str]:

74

"""

75

Extend the list of available environments.

76

77

Returns:

78

list[str]: Additional environment names

79

"""

80

81

def tox_before_run_commands(self, tox_env: ToxEnv, run_conf, opts) -> None:

82

"""

83

Hook called before running commands in environment.

84

85

Args:

86

tox_env: Environment where commands will run

87

run_conf: Run configuration

88

opts: Additional options

89

"""

90

91

def tox_after_run_commands(self, tox_env: ToxEnv, run_conf, opts, outcome: Outcome) -> None:

92

"""

93

Hook called after running commands in environment.

94

95

Args:

96

tox_env: Environment where commands ran

97

run_conf: Run configuration

98

opts: Additional options

99

outcome: Execution outcome

100

"""

101

```

102

103

### Plugin Manager

104

105

The plugin manager coordinates plugin discovery and hook execution.

106

107

```python { .api }

108

MANAGER: PluginManager

109

"""Global plugin manager instance."""

110

```

111

112

## Plugin Development

113

114

### Basic Plugin Structure

115

116

Create a plugin by implementing hook functions:

117

118

```python

119

# my_tox_plugin.py

120

from tox.plugin import impl

121

from tox.config.cli.parser import ToxParser

122

123

@impl

124

def tox_add_option(parser: ToxParser) -> None:

125

"""Add custom CLI option."""

126

parser.add_argument(

127

"--coverage-report",

128

action="store_true",

129

help="Generate coverage report after tests"

130

)

131

132

@impl

133

def tox_add_core_config(core_conf) -> None:

134

"""Add core configuration option."""

135

core_conf.add_config(

136

keys="coverage_threshold",

137

desc="Minimum coverage threshold",

138

of_type=float,

139

default=80.0

140

)

141

142

@impl

143

def tox_after_run_commands(tox_env, run_conf, opts, outcome) -> None:

144

"""Generate coverage report if requested."""

145

if getattr(opts, 'coverage_report', False):

146

print(f"Generating coverage report for {tox_env.name}")

147

# Coverage report generation logic

148

```

149

150

### Environment Type Plugins

151

152

Register custom environment types:

153

154

```python

155

from tox.plugin import impl

156

from tox.tox_env.api import ToxEnv

157

158

class DockerToxEnv(ToxEnv):

159

"""Docker-based tox environment."""

160

161

def create(self) -> None:

162

"""Create Docker container."""

163

print(f"Creating Docker container for {self.name}")

164

165

def execute(self, request):

166

"""Execute command in Docker container."""

167

# Docker execution logic

168

return ExecuteStatus(0, "success", "")

169

170

@impl

171

def tox_register_tox_env(register) -> None:

172

"""Register Docker environment type."""

173

register.add_env_type(

174

name="docker",

175

factory=DockerToxEnv,

176

description="Docker container environment"

177

)

178

```

179

180

### Configuration Extension

181

182

Extend tox configuration with plugin-specific options:

183

184

```python

185

@impl

186

def tox_add_env_config(env_conf) -> None:

187

"""Add environment configuration options."""

188

env_conf.add_config(

189

keys="docker_image",

190

desc="Docker image to use",

191

of_type=str,

192

default="python:3.11"

193

)

194

195

env_conf.add_config(

196

keys="docker_volumes",

197

desc="Docker volumes to mount",

198

of_type=list,

199

default=[]

200

)

201

```

202

203

## Plugin Discovery

204

205

### Entry Point Plugins

206

207

Register plugins via setuptools entry points:

208

209

```python

210

# setup.py or pyproject.toml

211

[project.entry-points.tox]

212

my_plugin = "my_tox_plugin"

213

docker_plugin = "tox_docker.plugin"

214

```

215

216

### Toxfile Plugins

217

218

Create `toxfile.py` in your project root:

219

220

```python

221

# toxfile.py

222

from tox.plugin import impl

223

224

@impl

225

def tox_add_option(parser):

226

"""Project-specific plugin hook."""

227

parser.add_argument("--project-flag", help="Project specific option")

228

```

229

230

## Advanced Plugin Patterns

231

232

### Multi-Hook Plugins

233

234

Plugins can implement multiple hooks:

235

236

```python

237

from tox.plugin import impl

238

239

class MyToxPlugin:

240

"""Comprehensive tox plugin."""

241

242

@impl

243

def tox_add_option(self, parser):

244

"""Add CLI options."""

245

parser.add_argument("--verbose-timing", action="store_true")

246

247

@impl

248

def tox_before_run_commands(self, tox_env, run_conf, opts):

249

"""Pre-execution hook."""

250

if getattr(opts, 'verbose_timing', False):

251

print(f"Starting {tox_env.name} at {time.time()}")

252

253

@impl

254

def tox_after_run_commands(self, tox_env, run_conf, opts, outcome):

255

"""Post-execution hook."""

256

if getattr(opts, 'verbose_timing', False):

257

print(f"Finished {tox_env.name} at {time.time()}")

258

259

# Register plugin instance

260

plugin = MyToxPlugin()

261

```

262

263

### Conditional Plugin Behavior

264

265

Implement conditional logic in plugins:

266

267

```python

268

@impl

269

def tox_before_run_commands(tox_env, run_conf, opts):

270

"""Conditional pre-execution logic."""

271

272

# Only run for Python environments

273

if hasattr(tox_env, 'python_executable'):

274

print(f"Python environment: {tox_env.name}")

275

276

# Only run in CI environment

277

if os.getenv('CI'):

278

print("Running in CI environment")

279

280

# Environment-specific logic

281

if tox_env.name.startswith('py3'):

282

print("Python 3 environment detected")

283

```

284

285

### Plugin Configuration

286

287

Plugins can define their own configuration:

288

289

```python

290

@impl

291

def tox_add_core_config(core_conf):

292

"""Add plugin configuration."""

293

core_conf.add_config(

294

keys="my_plugin_enabled",

295

desc="Enable my plugin functionality",

296

of_type=bool,

297

default=True

298

)

299

300

@impl

301

def tox_before_run_commands(tox_env, run_conf, opts):

302

"""Check plugin configuration."""

303

if tox_env.core["my_plugin_enabled"]:

304

print("Plugin is enabled")

305

else:

306

print("Plugin is disabled")

307

```

308

309

## Plugin Testing

310

311

Test plugins using tox's testing utilities:

312

313

```python

314

# test_my_plugin.py

315

import pytest

316

from tox.plugin import impl

317

from tox.run import main

318

319

def test_plugin_option():

320

"""Test custom CLI option."""

321

result = main(['--my-option', '--help'])

322

# Assert option is present in help output

323

324

def test_plugin_environment():

325

"""Test custom environment type."""

326

result = main(['-e', 'docker'])

327

# Assert Docker environment runs correctly

328

```

329

330

## Plugin Error Handling

331

332

Handle errors gracefully in plugins:

333

334

```python

335

@impl

336

def tox_register_tox_env(register):

337

"""Register with error handling."""

338

try:

339

# Check if Docker is available

340

import docker

341

register.add_env_type(

342

name="docker",

343

factory=DockerToxEnv,

344

description="Docker environment"

345

)

346

except ImportError:

347

print("Docker not available, skipping Docker environment")

348

```

349

350

## Built-in Plugin Examples

351

352

Tox includes several built-in plugins that demonstrate best practices:

353

354

- **Virtual environment plugin**: Creates Python virtual environments

355

- **Pip plugin**: Handles package installation via pip

356

- **Configuration plugins**: Extend configuration options

357

- **Reporting plugins**: Handle output formatting and logging

358

359

These serve as reference implementations for developing custom plugins with similar functionality.