0
# Environment System
1
2
Virtual environment creation, management, and lifecycle control for tox. The environment system supports different environment types including Python virtual environments, external environments, and custom environment implementations through a flexible hierarchy and plugin system.
3
4
## Capabilities
5
6
### Base Environment Class
7
8
The foundational environment abstraction that defines the interface for all tox environments.
9
10
```python { .api }
11
class ToxEnv:
12
"""Base tox environment class."""
13
14
def __init__(self, create_args: ToxEnvCreateArgs) -> None:
15
"""
16
Initialize the tox environment.
17
18
Args:
19
create_args: Environment creation arguments
20
"""
21
22
def setup(self) -> None:
23
"""
24
Setup the environment for use.
25
26
This installs dependencies, configures the environment,
27
and prepares it for command execution.
28
"""
29
30
def teardown(self) -> None:
31
"""
32
Clean up the environment.
33
34
This removes temporary files and releases resources
35
used by the environment.
36
"""
37
38
def execute(self, cmd: Sequence[Path | str], stdin: StdinSource,
39
show: bool | None = None, cwd: Path | None = None,
40
run_id: str = "", executor: Execute | None = None) -> Outcome:
41
"""
42
Execute commands in the environment.
43
44
Args:
45
cmd: Command and arguments to execute
46
stdin: Standard input source
47
show: Whether to show output
48
cwd: Working directory for execution
49
run_id: Unique identifier for this execution
50
executor: Custom executor to use
51
52
Returns:
53
Outcome: Execution result with exit code and output
54
"""
55
56
@property
57
def name(self) -> str:
58
"""Environment name (e.g., 'py311', 'docs')."""
59
60
@property
61
def conf(self) -> EnvConfigSet:
62
"""Environment configuration."""
63
64
@property
65
def core(self) -> CoreConfigSet:
66
"""Core tox configuration."""
67
68
@property
69
def env_dir(self) -> Path:
70
"""The tox environment directory."""
71
72
@property
73
def cache(self) -> Info:
74
"""Environment cache/info management."""
75
76
def register_config(self) -> None:
77
"""Register configuration options for this environment."""
78
79
@staticmethod
80
@abstractmethod
81
def id() -> str:
82
"""Return the type ID of this environment."""
83
84
@property
85
@abstractmethod
86
def executor(self) -> Execute:
87
"""Return the executor for this environment."""
88
89
@property
90
@abstractmethod
91
def installer(self) -> Installer[Any]:
92
"""Return the installer for this environment."""
93
94
@property
95
@abstractmethod
96
def runs_on_platform(self) -> str:
97
"""Return platform regex this environment runs on."""
98
```
99
100
### Python Environments
101
102
Specialized environment classes for Python-based testing and execution.
103
104
```python { .api }
105
class PythonToxEnv(ToxEnv):
106
"""Python-specific tox environment."""
107
108
def python_executable(self) -> Path:
109
"""
110
Get path to Python executable for this environment.
111
112
Returns:
113
Path: Path to Python interpreter
114
"""
115
116
def install_deps(self) -> None:
117
"""Install environment dependencies."""
118
119
def install_package(self, package_path: Path) -> None:
120
"""
121
Install package in environment.
122
123
Args:
124
package_path: Path to package to install
125
"""
126
127
@property
128
def base_python(self) -> str:
129
"""Base Python version specification."""
130
131
class VirtualEnvToxEnv(PythonToxEnv):
132
"""Virtual environment implementation for Python."""
133
134
def create_virtual_env(self) -> None:
135
"""Create the virtual environment."""
136
137
@property
138
def virtual_env_path(self) -> Path:
139
"""Path to virtual environment directory."""
140
141
class VirtualEnvRunner:
142
"""Runner for virtual environment operations."""
143
144
def create(self, base_python: str, path: Path) -> None:
145
"""
146
Create virtual environment.
147
148
Args:
149
base_python: Base Python interpreter
150
path: Target path for virtual environment
151
"""
152
```
153
154
### Environment Types
155
156
Different categories of environments supported by tox.
157
158
```python { .api }
159
class RunToxEnv(ToxEnv):
160
"""Environment for running commands."""
161
162
def run_commands(self, commands: list[str]) -> ExecuteStatus:
163
"""
164
Run sequence of commands.
165
166
Args:
167
commands: Commands to execute
168
169
Returns:
170
ExecuteStatus: Combined execution status
171
"""
172
173
class PackageToxEnv(ToxEnv):
174
"""Environment for packaging operations."""
175
176
def build_package(self) -> Path:
177
"""
178
Build package in this environment.
179
180
Returns:
181
Path: Path to built package
182
"""
183
```
184
185
### Environment Registry
186
187
System for registering and discovering environment types.
188
189
```python { .api }
190
class ToxEnvInfo:
191
"""Information about an environment type."""
192
193
def __init__(self, name: str, description: str, factory: type) -> None:
194
"""
195
Initialize environment info.
196
197
Args:
198
name: Environment type name
199
description: Human-readable description
200
factory: Environment class factory
201
"""
202
203
@property
204
def name(self) -> str:
205
"""Environment type name."""
206
207
@property
208
def description(self) -> str:
209
"""Environment description."""
210
211
# Global environment registry
212
REGISTER: dict[str, ToxEnvInfo]
213
```
214
215
### Package Management
216
217
System for managing package installation and dependencies within environments.
218
219
```python { .api }
220
class PipInstall:
221
"""Pip installation handler for Python environments."""
222
223
def __init__(self, env: PythonToxEnv) -> None:
224
"""
225
Initialize pip installer.
226
227
Args:
228
env: Python environment
229
"""
230
231
def install(self, packages: list[str]) -> ExecuteStatus:
232
"""
233
Install packages via pip.
234
235
Args:
236
packages: Package specifications to install
237
238
Returns:
239
ExecuteStatus: Installation result
240
"""
241
242
def install_requirements(self, req_file: Path) -> ExecuteStatus:
243
"""
244
Install from requirements file.
245
246
Args:
247
req_file: Path to requirements file
248
249
Returns:
250
ExecuteStatus: Installation result
251
"""
252
253
class RequirementsFile:
254
"""Handler for requirements files."""
255
256
def __init__(self, path: Path) -> None:
257
"""
258
Initialize requirements file handler.
259
260
Args:
261
path: Path to requirements file
262
"""
263
264
def read(self) -> list[str]:
265
"""
266
Read requirements from file.
267
268
Returns:
269
list[str]: List of requirement specifications
270
"""
271
```
272
273
## Environment Configuration
274
275
### Core Environment Settings
276
277
Key configuration options that control environment behavior:
278
279
```python
280
# Example environment configuration
281
[testenv:py311]
282
basepython = python3.11
283
deps =
284
pytest>=6.0
285
pytest-cov>=2.0
286
requests
287
commands =
288
pytest {posargs}
289
coverage report
290
changedir = {toxinidir}/tests
291
passenv =
292
HOME
293
USER
294
CI
295
setenv =
296
PYTHONPATH = {toxinidir}/src
297
COVERAGE_FILE = {envtmpdir}/.coverage
298
```
299
300
### Environment Variables
301
302
Control environment behavior through variables:
303
304
- `basepython`: Python interpreter to use
305
- `deps`: Dependencies to install
306
- `commands`: Commands to execute
307
- `commands_pre`: Pre-execution commands
308
- `commands_post`: Post-execution commands
309
- `changedir`: Working directory
310
- `passenv`: Environment variables to inherit
311
- `setenv`: Environment variables to set
312
- `install_command`: Custom install command
313
- `allowlist_externals`: Allowed external commands
314
315
### Environment Lifecycle Hooks
316
317
Control points in the environment lifecycle:
318
319
```python
320
# Pre/post command hooks
321
[testenv]
322
commands_pre =
323
python --version
324
pip list
325
commands = pytest {posargs}
326
commands_post =
327
coverage html
328
echo "Tests completed"
329
```
330
331
## Custom Environment Types
332
333
Create custom environment types by extending the base classes:
334
335
```python
336
from tox.tox_env.api import ToxEnv
337
from tox.plugin import impl
338
339
class CustomToxEnv(ToxEnv):
340
"""Custom environment implementation."""
341
342
def create(self) -> None:
343
"""Custom environment creation."""
344
print(f"Creating custom environment: {self.name}")
345
346
def setup(self) -> None:
347
"""Custom environment setup."""
348
print(f"Setting up custom environment: {self.name}")
349
350
def execute(self, request):
351
"""Custom command execution."""
352
print(f"Executing in {self.name}: {request.cmd}")
353
# Custom execution logic
354
return ExecuteStatus(0, "", "")
355
356
@impl
357
def tox_register_tox_env(register):
358
"""Register custom environment type."""
359
register.add_env_type(
360
name="custom",
361
factory=CustomToxEnv,
362
description="Custom environment type"
363
)
364
```
365
366
## Environment Selection
367
368
Environments can be selected through various mechanisms:
369
370
### Command Line Selection
371
372
```bash
373
# Run specific environments
374
tox -e py311,py312
375
376
# Run all environments
377
tox
378
379
# Run environments matching pattern
380
tox -e py3{11,12}
381
```
382
383
### Configuration-Based Selection
384
385
```ini
386
[tox]
387
envlist = py311,py312,docs,lint
388
389
# Conditional environments
390
[testenv:docs]
391
deps = sphinx
392
commands = sphinx-build docs docs/_build
393
394
[testenv:lint]
395
deps = flake8
396
commands = flake8 src/
397
```
398
399
### Programmatic Selection
400
401
```python
402
from tox.session.state import State
403
404
# Select specific environments
405
state = setup_state(['-e', 'py311,docs'])
406
407
# Get selected environments
408
selected = state.envs.all()
409
for name, env in selected.items():
410
print(f"Selected: {name}")
411
```
412
413
## Error Handling
414
415
Environment system includes specific error handling:
416
417
```python { .api }
418
class ToxEnvError(Exception):
419
"""Base environment error."""
420
421
class SkipEnvironment(Exception):
422
"""Skip this environment."""
423
424
class FailEnvironment(Exception):
425
"""Mark environment as failed."""
426
```
427
428
Usage in custom environments:
429
430
```python
431
def setup(self) -> None:
432
if not self.python_executable().exists():
433
raise SkipEnvironment(f"Python not found: {self.base_python}")
434
435
try:
436
self.install_deps()
437
except Exception as e:
438
raise FailEnvironment(f"Dependency installation failed: {e}")
439
```
440
441
## Environment Isolation
442
443
Tox provides strong isolation between environments:
444
445
- **File System**: Each environment has separate directories
446
- **Python Path**: Isolated Python installations and packages
447
- **Environment Variables**: Controlled variable inheritance
448
- **Process Isolation**: Commands run in separate processes
449
- **Resource Management**: Separate resource allocation per environment
450
451
This isolation ensures that environments don't interfere with each other and provides reproducible testing conditions.