88A ` pytest ` plugin that sets environment variables from ` pyproject.toml ` , ` pytest.toml ` , ` .pytest.toml ` , or ` pytest.ini `
99configuration files. It can also load variables from ` .env ` files.
1010
11+ <!-- mdformat-toc start --slug=github --no-anchors --maxlevel=6 --minlevel=2 -->
12+
13+ - [ Installation] ( #installation )
14+ - [ Quick start] ( #quick-start )
15+ - [ How-to guides] ( #how-to-guides )
16+ - [ Load variables from ` .env ` files] ( #load-variables-from-env-files )
17+ - [ Control variable behavior] ( #control-variable-behavior )
18+ - [ Set different environments for test suites] ( #set-different-environments-for-test-suites )
19+ - [ Reference] ( #reference )
20+ - [ TOML configuration format] ( #toml-configuration-format )
21+ - [ INI configuration format] ( #ini-configuration-format )
22+ - [ ` .env ` file format] ( #env-file-format )
23+ - [ CLI options] ( #cli-options )
24+ - [ ` --envfile PATH ` ] ( #--envfile-path )
25+ - [ ` --pytest-env-verbose ` ] ( #--pytest-env-verbose )
26+ - [ Explanation] ( #explanation )
27+ - [ Precedence] ( #precedence )
28+ - [ File discovery] ( #file-discovery )
29+ - [ Choosing a configuration format] ( #choosing-a-configuration-format )
30+
31+ <!-- mdformat-toc end -->
32+
1133## Installation
1234
1335``` shell
@@ -35,36 +57,16 @@ def test_database_connection():
3557 assert os.environ[" DEBUG" ] == " true"
3658```
3759
38- ## How-to guides
39-
40- ### Set different environments for test suites
60+ To see exactly what pytest-env sets, pass ` --pytest-env-verbose ` :
4161
42- Create a subdirectory config to override parent settings:
43-
44- ```
45- project/
46- ├── pyproject.toml # [tool.pytest_env] DB_HOST = "prod-db"
47- └── tests_integration/
48- ├── pytest.toml # [pytest_env] DB_HOST = "test-db"
49- └── test_api.py
5062```
51-
52- Running ` pytest tests_integration/ ` uses the subdirectory configuration.
53-
54- ### Switch environments at runtime
55-
56- Use the ` --envfile ` CLI option to override or extend your configuration:
57-
58- ``` shell
59- # Override all configured env files with a different one.
60- pytest --envfile .env.local
61-
62- # Add an additional env file to those already configured.
63- pytest --envfile +.env.override
63+ $ pytest --pytest-env-verbose
64+ pytest-env:
65+ SET DATABASE_URL=postgresql://localhost/test_db (from /project/pyproject.toml)
66+ SET DEBUG=true (from /project/pyproject.toml)
6467```
6568
66- Override mode loads only the specified file. Extend mode (prefix with ` + ` ) loads configuration files first, then the CLI
67- file. Variables in the CLI file take precedence.
69+ ## How-to guides
6870
6971### Load variables from ` .env ` files
7072
@@ -83,48 +85,53 @@ SECRET_KEY='my-secret-key'
8385DEBUG=" true"
8486```
8587
86- Files are loaded before inline variables, so inline configuration takes precedence.
87-
88- ### Expand variables using other environment variables
89-
90- Reference existing environment variables in values:
88+ Files are loaded before inline variables, so inline configuration takes precedence. To switch ` .env ` files at runtime
89+ without changing configuration, use the ` --envfile ` CLI option:
9190
92- ``` toml
93- [ tool . pytest_env ]
94- RUN_PATH = { value = " /run/path/{USER} " , transform = true }
91+ ``` shell
92+ pytest --envfile .env.local # ignore configured env_files, load only this file
93+ pytest --envfile +.env.override # load configured env_files first, then this file on top
9594```
9695
97- The ` {USER} ` placeholder expands to the current user's name.
96+ ### Control variable behavior
9897
99- ### Set conditional defaults
100-
101- Only set a variable if it does not already exist:
98+ Variables set as plain values are assigned directly. For more control, use inline tables with the ` transform ` ,
99+ ` skip_if_set ` , and ` unset ` keys:
102100
103101``` toml
104102[tool .pytest_env ]
103+ SIMPLE = " value"
104+ RUN_PATH = { value = " /run/path/{USER}" , transform = true }
105105HOME = { value = " ~/tmp" , skip_if_set = true }
106+ TEMP_VAR = { unset = true }
106107```
107108
108- This leaves ` HOME ` unchanged if already set, otherwise sets it to ` ~/tmp ` .
109+ ` transform ` expands ` {VAR} ` placeholders using existing environment variables. ` skip_if_set ` leaves the variable
110+ unchanged when it already exists. ` unset ` removes it entirely (different from setting to empty string).
109111
110- ### Remove variables from the environment
112+ ### Set different environments for test suites
111113
112- Unset a variable completely (different from setting to empty string) :
114+ Create a subdirectory config to override parent settings :
113115
114- ``` toml
115- [tool .pytest_env ]
116- DATABASE_URL = { unset = true }
116+ ```
117+ project/
118+ ├── pyproject.toml # [tool.pytest_env] DB_HOST = "prod-db"
119+ └── tests_integration/
120+ ├── pytest.toml # [pytest_env] DB_HOST = "test-db"
121+ └── test_api.py
117122```
118123
124+ Running ` pytest tests_integration/ ` uses the subdirectory configuration. The plugin walks up the directory tree and
125+ stops at the first file containing a ` pytest_env ` section, so subdirectory configs naturally override parent configs.
126+
119127## Reference
120128
121129### TOML configuration format
122130
123- Define environment variables under ` [tool.pytest_env] ` in ` pyproject.toml ` , or ` [pytest_env] ` in ` pytest.toml ` or
131+ Define environment variables under ` [tool.pytest_env] ` in ` pyproject.toml ` , or ` [pytest_env] ` in ` pytest.toml ` /
124132` .pytest.toml ` :
125133
126134``` toml
127- # pyproject.toml
128135[tool .pytest_env ]
129136SIMPLE_VAR = " value"
130137NUMBER_VAR = 42
@@ -133,10 +140,8 @@ CONDITIONAL = { value = "default", skip_if_set = true }
133140REMOVED = { unset = true }
134141```
135142
136- Each key is the environment variable name. Values can be:
137-
138- - ** Plain values** : Cast to string and set directly.
139- - ** Inline tables** : Objects with the following keys:
143+ Each key is the environment variable name. Values can be plain values (cast to string) or inline tables with the
144+ following keys:
140145
141146| Key | Type | Description |
142147| ------------- | ------ | ---------------------------------------------------------------------------- |
@@ -171,13 +176,13 @@ env = [
171176
172177Prefix flags modify behavior. Flags are case-insensitive and can be combined in any order (e.g., ` R:D:KEY=VALUE ` ):
173178
174- | Flag | Description |
175- | ---- | ------------------------------------------------------------------- |
176- | ` D: ` | Default — only set if the variable is not already defined. |
177- | ` R: ` | Raw — skip ` {VAR} ` expansion (INI expands by default, unlike TOML). |
178- | ` U: ` | Unset — remove the variable from the environment entirely. |
179+ | Flag | Description |
180+ | ---- | -------------------------------------------------------------------- |
181+ | ` D: ` | Default -- only set if the variable is not already defined. |
182+ | ` R: ` | Raw -- skip ` {VAR} ` expansion (INI expands by default, unlike TOML). |
183+ | ` U: ` | Unset -- remove the variable from the environment entirely. |
179184
180- ** Note ** : In INI format, variable expansion is enabled by default. In TOML format, it requires ` transform = true ` .
185+ In INI format variable expansion is enabled by default. In TOML format it requires ` transform = true ` .
181186
182187### ` .env ` file format
183188
@@ -195,13 +200,8 @@ env_files =
195200 .env.test
196201```
197202
198- Files are parsed by [ python-dotenv] ( https://github.com/theskumar/python-dotenv ) and support:
199-
200- - ` KEY=VALUE ` lines
201- - ` # ` comments
202- - ` export ` prefix
203- - Quoted values with escape sequences in double quotes
204- - ` ${VAR:-default} ` expansion
203+ Files are parsed by [ python-dotenv] ( https://github.com/theskumar/python-dotenv ) and support ` KEY=VALUE ` lines, ` # `
204+ comments, ` export ` prefix, quoted values with escape sequences in double quotes, and ` ${VAR:-default} ` expansion.
205205
206206Example ` .env ` file:
207207
@@ -213,83 +213,71 @@ MESSAGE="hello\nworld"
213213API_KEY=${FALLBACK_KEY:- default_key}
214214```
215215
216- Missing ` .env ` files are silently skipped. Paths are resolved relative to the project root.
216+ Missing ` .env ` files from configuration are silently skipped. Paths are resolved relative to the project root.
217217
218- ### CLI option: ` --envfile `
218+ ### CLI options
219219
220- Override or extend configuration-based ` env_files ` at runtime:
220+ #### ` --envfile PATH `
221221
222- ``` shell
223- pytest --envfile PATH # Override mode
224- pytest --envfile +PATH # Extend mode
225- ```
222+ Override or extend configuration-based ` env_files ` at runtime.
226223
227- ** Override mode** (` --envfile PATH ` ): Loads only the specified file, ignoring all ` env_files ` from configuration.
224+ ** Override mode** (` --envfile PATH ` ): loads only the specified file, ignoring all ` env_files ` from configuration.
228225
229- ** Extend mode** (` --envfile +PATH ` ): Loads configuration files first in their normal order, then loads the CLI file.
226+ ** Extend mode** (` --envfile +PATH ` ): loads configuration files first in their normal order, then loads the CLI file.
230227Variables from the CLI file override those from configuration files.
231228
232229Unlike configuration-based ` env_files ` , CLI-specified files must exist. Missing files raise ` FileNotFoundError ` . Paths
233230are resolved relative to the project root.
234231
235- ## Explanation
232+ #### ` --pytest-env-verbose `
236233
237- ### Configuration precedence
234+ Print all environment variable assignments in the test session header. Each line shows the action (` SET ` , ` SKIP ` , or
235+ ` UNSET ` ), the variable name with its final value, and the source file:
238236
239- When multiple configuration sources define the same variable, the following precedence rules apply (highest to lowest):
237+ ```
238+ pytest-env:
239+ SET DATABASE_URL=postgres://localhost/test (from /path/to/.env)
240+ SET DEBUG=true (from /path/to/pyproject.toml)
241+ SKIP HOME=/Users/me (from /path/to/pyproject.toml)
242+ UNSET TEMP_VAR (from /path/to/pyproject.toml)
243+ ```
240244
241- 1 . Inline variables in configuration files (TOML or INI format)
242- 1 . Variables from ` .env ` files loaded via ` env_files `
243- 1 . Variables already present in the environment (unless ` skip_if_set = false ` or no ` D: ` flag)
245+ Useful for debugging when multiple env files, inline configuration, and CLI options interact.
244246
245- When using ` --envfile ` , CLI files take precedence over configuration-based ` env_files ` , but inline variables still win.
247+ ## Explanation
246248
247- ### Configuration format precedence
249+ ### Precedence
248250
249- When multiple configuration formats are present :
251+ When multiple sources define the same variable, precedence applies in this order (highest to lowest) :
250252
251- 1 . TOML native format (` [pytest_env] ` or ` [tool.pytest_env] ` ) takes precedence over INI format.
252- 1 . Among TOML files, the first file with a ` pytest_env ` section is used, checked in order: ` pytest.toml ` ,
253- ` .pytest.toml ` , ` pyproject.toml ` .
254- 1 . If no TOML file contains ` pytest_env ` , the plugin falls back to INI-style ` env ` configuration.
253+ 1 . Inline variables in configuration files (TOML or INI format).
254+ 1 . Variables from ` .env ` files loaded via ` env_files ` . When using ` --envfile ` , CLI files take precedence over
255+ configuration-based ` env_files ` .
256+ 1 . Variables already present in the environment (preserved when ` skip_if_set = true ` or ` D: ` flag is used).
257+
258+ When multiple configuration formats are present, TOML native format (` [pytest_env] ` / ` [tool.pytest_env] ` ) takes
259+ precedence over INI format. Among TOML files, the first file with a ` pytest_env ` section wins, checked in order:
260+ ` pytest.toml ` , ` .pytest.toml ` , ` pyproject.toml ` . If no TOML file contains ` pytest_env ` , the plugin falls back to
261+ INI-style ` env ` configuration.
255262
256263### File discovery
257264
258265The plugin walks up the directory tree starting from pytest's resolved configuration directory. For each directory, it
259266checks ` pytest.toml ` , ` .pytest.toml ` , and ` pyproject.toml ` in order, stopping at the first file containing a
260- ` pytest_env ` section.
261-
262- This means subdirectory configurations take precedence over parent configurations, allowing you to have different
263- settings for integration tests versus unit tests.
264-
265- ### When to use TOML vs INI format
266-
267- Use the ** TOML native format** (` [pytest_env] ` ) when:
268-
269- - You need fine-grained control over expansion and conditional setting.
270- - Your configuration is complex with multiple inline tables.
271- - You prefer explicit ` transform = true ` for variable expansion.
272-
273- Use the ** INI format** (` env ` key) when:
274-
275- - You want simple ` KEY=VALUE ` pairs with minimal syntax.
276- - You prefer expansion by default (add ` R: ` to disable).
277- - You are migrating from an existing INI-based setup.
278-
279- Both formats are fully supported and can coexist (TOML takes precedence if both are present).
280-
281- ### When to use ` .env ` files vs inline configuration
282-
283- Use ** ` .env ` files** when:
267+ ` pytest_env ` section. This means subdirectory configurations take precedence over parent configurations, allowing
268+ different settings for integration tests versus unit tests.
284269
285- - You have many environment variables that would clutter your config file.
286- - You want to share environment configuration with other tools (e.g., Docker, shell scripts).
287- - You need different ` .env ` files for different environments (dev, staging, prod).
270+ ### Choosing a configuration format
288271
289- Use ** inline configuration** when:
272+ ** TOML native format** (` [pytest_env] ` ) is best when you need fine-grained control over expansion and conditional
273+ setting, or when your configuration uses multiple inline tables. Variable expansion requires explicit
274+ ` transform = true ` .
290275
291- - You have a small number of test-specific variables.
292- - You want variables to be version-controlled alongside test configuration.
293- - You need features like ` transform ` , ` skip_if_set ` , or ` unset ` that ` .env ` files do not support.
276+ ** INI format** (` env ` key) is best for simple ` KEY=VALUE ` pairs with minimal syntax. Variable expansion is on by default
277+ (use ` R: ` to disable). Both formats are fully supported and can coexist -- TOML takes precedence if both are present.
294278
295- You can combine both approaches. Inline variables always take precedence over ` .env ` files.
279+ ** ` .env ` files** work well when you have many variables that would clutter your config file, want to share environment
280+ configuration with other tools (Docker, shell scripts), or need different files for different environments. ** Inline
281+ configuration** is better for a small number of test-specific variables that should be version-controlled, or when you
282+ need ` transform ` , ` skip_if_set ` , or ` unset ` . You can combine both -- inline variables always take precedence over ` .env `
283+ files.
0 commit comments