0
# Configuration and Plugin System
1
2
Central configuration management and plugin architecture enabling extensive customization through hooks, configuration files, command-line options, and the pluggy-based plugin system.
3
4
## Capabilities
5
6
### Configuration Management
7
8
Central configuration object providing access to all pytest settings and options.
9
10
```python { .api }
11
class Config:
12
"""Central configuration object with access to pluginmanager and hooks."""
13
14
# Core attributes
15
pluginmanager: PytestPluginManager # Plugin manager instance
16
invocation_params: InvocationParams # Command-line invocation parameters
17
option: argparse.Namespace # Parsed command-line options
18
rootpath: Path # Root directory path
19
inipath: Path | None # Path to configuration file
20
21
def getini(self, name: str) -> Any:
22
"""
23
Get configuration value from ini file.
24
25
Parameters:
26
- name: Configuration option name
27
28
Returns:
29
Configuration value
30
"""
31
32
def getoption(self, name: str, default=None, skip: bool = False) -> Any:
33
"""
34
Get command-line option value.
35
36
Parameters:
37
- name: Option name
38
- default: Default value if option not set
39
- skip: Whether to skip if option not found
40
41
Returns:
42
Option value
43
"""
44
45
def addinivalue_line(self, name: str, line: str) -> None:
46
"""
47
Add line to multi-line ini configuration value.
48
49
Parameters:
50
- name: Configuration option name
51
- line: Line to add
52
"""
53
54
def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None:
55
"""Issue warning during configuration time."""
56
```
57
58
### Plugin Management
59
60
Plugin manager extending pluggy with pytest-specific functionality for loading and managing plugins.
61
62
```python { .api }
63
class PytestPluginManager:
64
"""Extends pluggy.PluginManager with pytest-specific functionality."""
65
66
def register(self, plugin, name: str | None = None) -> None:
67
"""
68
Register a plugin instance.
69
70
Parameters:
71
- plugin: Plugin instance
72
- name: Optional plugin name
73
"""
74
75
def unregister(self, plugin=None, name: str | None = None) -> Any:
76
"""
77
Unregister a plugin instance.
78
79
Parameters:
80
- plugin: Plugin instance to unregister
81
- name: Plugin name to unregister
82
83
Returns:
84
Unregistered plugin
85
"""
86
87
def get_plugin(self, name: str):
88
"""Get plugin by name."""
89
90
def is_registered(self, plugin) -> bool:
91
"""Check if plugin is registered."""
92
93
def list_plugin_distinfo(self) -> list[tuple[Any, DistInfo]]:
94
"""List all plugin distribution info."""
95
96
def list_name_plugin(self) -> list[tuple[str, Any]]:
97
"""List all plugins with their names."""
98
99
def get_canonical_name(self, plugin) -> str:
100
"""Get canonical name for plugin."""
101
102
def load_setuptools_entrypoints(self, group: str, names: str | None = None) -> int:
103
"""Load plugins from setuptools entry points."""
104
105
def consider_env(self, name: str, value: str | None = None) -> None:
106
"""Consider environment variable for plugin loading."""
107
108
def consider_preparse(self, args: list[str], exclude_only: bool = False) -> None:
109
"""Consider command-line arguments for plugin loading."""
110
111
def consider_conftest(self, conftestmodule) -> None:
112
"""Register hooks from conftest module."""
113
```
114
115
### Hook System
116
117
Decorators for marking functions as hook implementations and specifications.
118
119
```python { .api }
120
def hookimpl(**kwargs):
121
"""
122
Mark function as hook implementation.
123
124
Parameters:
125
- tryfirst: Execute this hook first
126
- trylast: Execute this hook last
127
- hookwrapper: Hook wrapper (can yield)
128
- optionalhook: Hook is optional
129
- specname: Name of hook specification
130
131
Returns:
132
HookimplMarker decorator
133
"""
134
135
def hookspec(**kwargs):
136
"""
137
Mark function as hook specification.
138
139
Parameters:
140
- firstresult: Return first non-None result
141
- historic: Historic hook (replay for late registrations)
142
- warn_on_impl: Issue warnings for implementations
143
144
Returns:
145
HookspecMarker decorator
146
"""
147
```
148
149
**Usage Example:**
150
151
```python
152
import pytest
153
154
# Plugin implementation
155
class MyPlugin:
156
@pytest.hookimpl(tryfirst=True)
157
def pytest_configure(self, config):
158
print("Configuring my plugin")
159
160
@pytest.hookimpl
161
def pytest_collection_modifyitems(self, config, items):
162
# Modify collected test items
163
pass
164
165
@pytest.hookimpl(hookwrapper=True)
166
def pytest_runtest_setup(self, item):
167
print(f"Setting up {item.name}")
168
outcome = yield
169
print(f"Setup complete for {item.name}")
170
171
# Register plugin
172
def pytest_configure(config):
173
config.pluginmanager.register(MyPlugin(), "myplugin")
174
```
175
176
### Argument Parsing
177
178
Command-line argument parser for pytest with support for options and ini values.
179
180
```python { .api }
181
class Parser:
182
"""Command-line argument parser for pytest."""
183
184
def addoption(
185
self,
186
*names,
187
action=None,
188
nargs=None,
189
const=None,
190
default=None,
191
type=None,
192
choices=None,
193
required=None,
194
help=None,
195
metavar=None,
196
dest=None,
197
**kwargs
198
) -> None:
199
"""Add command-line option."""
200
201
def getgroup(self, name: str, description: str = "") -> OptionGroup:
202
"""Get or create option group."""
203
204
def addini(
205
self,
206
name: str,
207
help: str,
208
type: str | None = None,
209
default=None,
210
**kwargs
211
) -> None:
212
"""Add ini file configuration option."""
213
214
class OptionGroup:
215
"""Groups related command-line options."""
216
217
def addoption(self, *names, **kwargs) -> None:
218
"""Add option to this group."""
219
220
def _addoption(self, *names, **kwargs) -> None:
221
"""Internal method to add option."""
222
```
223
224
**Usage Example:**
225
226
```python
227
# In conftest.py or plugin
228
def pytest_addoption(parser):
229
parser.addoption(
230
"--myopt",
231
action="store",
232
default="default",
233
help="My custom option"
234
)
235
236
group = parser.getgroup("mygroup", "My custom options")
237
group.addoption(
238
"--verbose-mode",
239
action="store_true",
240
help="Enable verbose mode"
241
)
242
243
parser.addini(
244
"my_setting",
245
help="My configuration setting",
246
type="string",
247
default="default_value"
248
)
249
250
def pytest_configure(config):
251
my_opt = config.getoption("myopt")
252
my_setting = config.getini("my_setting")
253
```
254
255
### Exit Codes
256
257
Enumeration of pytest exit codes for different execution outcomes.
258
259
```python { .api }
260
from enum import IntEnum
261
262
class ExitCode(IntEnum):
263
"""Integer enum for pytest exit codes."""
264
265
OK = 0 # All tests passed
266
TESTS_FAILED = 1 # One or more tests failed
267
INTERRUPTED = 2 # Test run interrupted
268
INTERNAL_ERROR = 3 # Internal pytest error
269
USAGE_ERROR = 4 # Command-line usage error
270
NO_TESTS_RAN = 5 # No tests found/collected
271
```
272
273
### Configuration Exceptions
274
275
```python { .api }
276
class UsageError(Exception):
277
"""
278
Raised for command-line usage errors.
279
280
This exception is raised when:
281
- Invalid command-line arguments are provided
282
- Required options are missing
283
- Option values are invalid
284
"""
285
```
286
287
## Entry Points
288
289
Main entry points for running pytest programmatically.
290
291
```python { .api }
292
def main(args=None, plugins=None) -> int:
293
"""
294
Main entry point for running pytest programmatically.
295
296
Parameters:
297
- args: Command-line arguments (defaults to sys.argv)
298
- plugins: List of plugin objects to register
299
300
Returns:
301
Exit code (ExitCode enum value)
302
"""
303
304
def console_main() -> int:
305
"""
306
Console script entry point.
307
Called by pytest command-line script.
308
309
Returns:
310
Exit code
311
"""
312
313
# Module-level entry point
314
import pytest.config.cmdline
315
316
def cmdline.main() -> int:
317
"""Command line interface entry point."""
318
```
319
320
## Configuration Files
321
322
pytest supports multiple configuration file formats:
323
324
```python
325
# pytest.ini
326
[tool:pytest]
327
addopts = -v --tb=short
328
testpaths = tests
329
python_files = test_*.py *_test.py
330
python_classes = Test*
331
python_functions = test_*
332
markers =
333
slow: marks tests as slow
334
integration: marks tests as integration tests
335
336
# pyproject.toml
337
[tool.pytest.ini_options]
338
addopts = "-v --tb=short"
339
testpaths = ["tests"]
340
python_files = ["test_*.py", "*_test.py"]
341
342
# setup.cfg
343
[tool:pytest]
344
addopts = -v --tb=short
345
testpaths = tests
346
```
347
348
## Hook Specifications
349
350
Comprehensive hook system for extending pytest functionality at every stage:
351
352
```python { .api }
353
# Configuration and startup hooks
354
def pytest_configure(config: Config) -> None:
355
"""Called after command line options are parsed and plugins loaded."""
356
357
def pytest_unconfigure(config: Config) -> None:
358
"""Called before test process is exited."""
359
360
def pytest_sessionstart(session: Session) -> None:
361
"""Called after Session object has been created."""
362
363
def pytest_sessionfinish(session: Session, exitstatus: int) -> None:
364
"""Called after whole test run finished."""
365
366
# Collection hooks
367
def pytest_collect_file(parent: Collector, path: Path) -> Collector | None:
368
"""Create a collector for the given path."""
369
370
def pytest_collection_modifyitems(config: Config, items: list[Item]) -> None:
371
"""Called after collection is completed to modify or re-order items."""
372
373
def pytest_generate_tests(metafunc: Metafunc) -> None:
374
"""Generate (parametrize) tests for the given function."""
375
376
# Test execution hooks
377
def pytest_runtest_setup(item: Item) -> None:
378
"""Called to perform the setup phase for a test item."""
379
380
def pytest_runtest_call(item: Item) -> None:
381
"""Called to run the test for test item."""
382
383
def pytest_runtest_teardown(item: Item, nextitem: Item | None) -> None:
384
"""Called to perform the teardown phase for a test item."""
385
386
def pytest_runtest_makereport(item: Item, call: CallInfo) -> TestReport | None:
387
"""Called to create a TestReport for each phase."""
388
389
# Fixture hooks
390
def pytest_fixture_setup(fixturedef: FixtureDef, request: FixtureRequest) -> object:
391
"""Perform fixture setup execution."""
392
393
def pytest_fixture_post_finalizer(fixturedef: FixtureDef, request: FixtureRequest) -> None:
394
"""Called after fixture teardown."""
395
396
# Reporting hooks
397
def pytest_report_teststatus(report: TestReport, config: Config) -> tuple[str, str, str] | None:
398
"""Return result-category, shortletter and verbose word for reporting."""
399
400
def pytest_terminal_summary(terminalreporter: TerminalReporter, exitstatus: int, config: Config) -> None:
401
"""Add sections to the terminal summary reporting."""
402
403
# Warning and error hooks
404
def pytest_warning_recorded(warning_message, when: str, nodeid: str, location: tuple[str, int, str]) -> None:
405
"""Process a warning captured by the internal pytest warnings plugin."""
406
407
def pytest_internalerror(excrepr: ExceptionRepr, excinfo: ExceptionInfo) -> bool:
408
"""Called for internal errors."""
409
```
410
411
## Plugin Development
412
413
```python
414
# conftest.py - Automatic plugin loading
415
def pytest_configure(config):
416
"""Called after command line options are parsed."""
417
418
def pytest_collection_modifyitems(config, items):
419
"""Called after collection is completed."""
420
421
def pytest_runtest_setup(item):
422
"""Called before each test runs."""
423
424
# External plugin package
425
from setuptools import setup
426
427
setup(
428
name="pytest-myplugin",
429
entry_points={
430
"pytest11": [
431
"myplugin = myplugin.plugin",
432
],
433
},
434
)
435
```
436
437
## Types
438
439
```python { .api }
440
from typing import Any
441
from pathlib import Path
442
443
class ExceptionRepr:
444
"""Representation of an exception for reporting purposes."""
445
446
def toterminal(self, tw: TerminalWriter) -> None:
447
"""Write exception representation to terminal."""
448
449
def __str__(self) -> str:
450
"""String representation of exception."""
451
452
class InvocationParams:
453
"""Parameters used during pytest invocation."""
454
455
# Attributes
456
args: tuple[str, ...] # Command line arguments
457
plugins: tuple[str, ...] | None # Plugin names
458
dir: Path # Working directory
459
```