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=1 -->
12+
13+ - [ pytest-env] ( #pytest-env )
14+ - [ Installation] ( #installation )
15+ - [ Quick start] ( #quick-start )
16+ - [ How-to guides] ( #how-to-guides )
17+ - [ Load variables from ` .env ` files] ( #load-variables-from-env-files )
18+ - [ Control variable behavior] ( #control-variable-behavior )
19+ - [ Set different environments for test suites] ( #set-different-environments-for-test-suites )
20+ - [ Reference] ( #reference )
21+ - [ TOML configuration format] ( #toml-configuration-format )
22+ - [ INI configuration format] ( #ini-configuration-format )
23+ - [ ` .env ` file format] ( #env-file-format )
24+ - [ CLI options] ( #cli-options )
25+ - [ ` --envfile PATH ` ] ( #--envfile-path )
26+ - [ ` --pytest-env-verbose ` ] ( #--pytest-env-verbose )
27+ - [ Explanation] ( #explanation )
28+ - [ Precedence] ( #precedence )
29+ - [ File discovery] ( #file-discovery )
30+ - [ Choosing a configuration format] ( #choosing-a-configuration-format )
31+
32+ <!-- mdformat-toc end -->
33+
1134## Installation
1235
1336``` shell
@@ -37,35 +60,6 @@ def test_database_connection():
3760
3861## How-to guides
3962
40- ### Set different environments for test suites
41-
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
50- ```
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
64- ```
65-
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.
68-
6963### Load variables from ` .env ` files
7064
7165Specify ` .env ` files in your configuration:
@@ -83,48 +77,53 @@ SECRET_KEY='my-secret-key'
8377DEBUG=" true"
8478```
8579
86- Files are loaded before inline variables, so inline configuration takes precedence.
87-
88- ### Expand variables using other environment variables
80+ Files are loaded before inline variables, so inline configuration takes precedence. To switch ` .env ` files at runtime
81+ without changing configuration, use the ` --envfile ` CLI option:
8982
90- Reference existing environment variables in values:
91-
92- ``` toml
93- [tool .pytest_env ]
94- RUN_PATH = { value = " /run/path/{USER}" , transform = true }
83+ ``` shell
84+ pytest --envfile .env.local # ignore configured env_files, load only this file
85+ pytest --envfile +.env.override # load configured env_files first, then this file on top
9586```
9687
97- The ` {USER} ` placeholder expands to the current user's name.
98-
99- ### Set conditional defaults
88+ ### Control variable behavior
10089
101- Only set a variable if it does not already exist:
90+ Variables set as plain values are assigned directly. For more control, use inline tables with the ` transform ` ,
91+ ` skip_if_set ` , and ` unset ` keys:
10292
10393``` toml
10494[tool .pytest_env ]
95+ SIMPLE = " value"
96+ RUN_PATH = { value = " /run/path/{USER}" , transform = true }
10597HOME = { value = " ~/tmp" , skip_if_set = true }
98+ TEMP_VAR = { unset = true }
10699```
107100
108- This leaves ` HOME ` unchanged if already set, otherwise sets it to ` ~/tmp ` .
101+ ` transform ` expands ` {VAR} ` placeholders using existing environment variables. ` skip_if_set ` leaves the variable
102+ unchanged when it already exists. ` unset ` removes it entirely (different from setting to empty string).
109103
110- ### Remove variables from the environment
104+ ### Set different environments for test suites
111105
112- Unset a variable completely (different from setting to empty string) :
106+ Create a subdirectory config to override parent settings :
113107
114- ``` toml
115- [tool .pytest_env ]
116- DATABASE_URL = { unset = true }
108+ ```
109+ project/
110+ ├── pyproject.toml # [tool.pytest_env] DB_HOST = "prod-db"
111+ └── tests_integration/
112+ ├── pytest.toml # [pytest_env] DB_HOST = "test-db"
113+ └── test_api.py
117114```
118115
116+ Running ` pytest tests_integration/ ` uses the subdirectory configuration. The plugin walks up the directory tree and
117+ stops at the first file containing a ` pytest_env ` section, so subdirectory configs naturally override parent configs.
118+
119119## Reference
120120
121121### TOML configuration format
122122
123- Define environment variables under ` [tool.pytest_env] ` in ` pyproject.toml ` , or ` [pytest_env] ` in ` pytest.toml ` or
123+ Define environment variables under ` [tool.pytest_env] ` in ` pyproject.toml ` , or ` [pytest_env] ` in ` pytest.toml ` /
124124` .pytest.toml ` :
125125
126126``` toml
127- # pyproject.toml
128127[tool .pytest_env ]
129128SIMPLE_VAR = " value"
130129NUMBER_VAR = 42
@@ -133,10 +132,8 @@ CONDITIONAL = { value = "default", skip_if_set = true }
133132REMOVED = { unset = true }
134133```
135134
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:
135+ Each key is the environment variable name. Values can be plain values (cast to string) or inline tables with the
136+ following keys:
140137
141138| Key | Type | Description |
142139| ------------- | ------ | ---------------------------------------------------------------------------- |
@@ -171,13 +168,13 @@ env = [
171168
172169Prefix flags modify behavior. Flags are case-insensitive and can be combined in any order (e.g., ` R:D:KEY=VALUE ` ):
173170
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. |
171+ | Flag | Description |
172+ | ---- | -------------------------------------------------------------------- |
173+ | ` D: ` | Default -- only set if the variable is not already defined. |
174+ | ` R: ` | Raw -- skip ` {VAR} ` expansion (INI expands by default, unlike TOML). |
175+ | ` U: ` | Unset -- remove the variable from the environment entirely. |
179176
180- ** Note ** : In INI format, variable expansion is enabled by default. In TOML format, it requires ` transform = true ` .
177+ In INI format variable expansion is enabled by default. In TOML format it requires ` transform = true ` .
181178
182179### ` .env ` file format
183180
@@ -195,13 +192,8 @@ env_files =
195192 .env.test
196193```
197194
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
195+ Files are parsed by [ python-dotenv] ( https://github.com/theskumar/python-dotenv ) and support ` KEY=VALUE ` lines, ` # `
196+ comments, ` export ` prefix, quoted values with escape sequences in double quotes, and ` ${VAR:-default} ` expansion.
205197
206198Example ` .env ` file:
207199
@@ -213,83 +205,71 @@ MESSAGE="hello\nworld"
213205API_KEY=${FALLBACK_KEY:- default_key}
214206```
215207
216- Missing ` .env ` files are silently skipped. Paths are resolved relative to the project root.
208+ Missing ` .env ` files from configuration are silently skipped. Paths are resolved relative to the project root.
217209
218- ### CLI option: ` --envfile `
210+ ### CLI options
219211
220- Override or extend configuration-based ` env_files ` at runtime:
212+ #### ` --envfile PATH `
221213
222- ``` shell
223- pytest --envfile PATH # Override mode
224- pytest --envfile +PATH # Extend mode
225- ```
214+ Override or extend configuration-based ` env_files ` at runtime.
226215
227- ** Override mode** (` --envfile PATH ` ): Loads only the specified file, ignoring all ` env_files ` from configuration.
216+ ** Override mode** (` --envfile PATH ` ): loads only the specified file, ignoring all ` env_files ` from configuration.
228217
229- ** Extend mode** (` --envfile +PATH ` ): Loads configuration files first in their normal order, then loads the CLI file.
218+ ** Extend mode** (` --envfile +PATH ` ): loads configuration files first in their normal order, then loads the CLI file.
230219Variables from the CLI file override those from configuration files.
231220
232221Unlike configuration-based ` env_files ` , CLI-specified files must exist. Missing files raise ` FileNotFoundError ` . Paths
233222are resolved relative to the project root.
234223
235- ## Explanation
224+ #### ` --pytest-env-verbose `
236225
237- ### Configuration precedence
226+ Print all environment variable assignments in the test session header. Each line shows the action (` SET ` , ` SKIP ` , or
227+ ` UNSET ` ), the variable name with its final value, and the source file:
238228
239- When multiple configuration sources define the same variable, the following precedence rules apply (highest to lowest):
229+ ```
230+ pytest-env:
231+ SET DATABASE_URL=postgres://localhost/test (from /path/to/.env)
232+ SET DEBUG=true (from /path/to/pyproject.toml)
233+ SKIP HOME=/Users/me (from /path/to/pyproject.toml)
234+ UNSET TEMP_VAR (from /path/to/pyproject.toml)
235+ ```
240236
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)
237+ Useful for debugging when multiple env files, inline configuration, and CLI options interact.
244238
245- When using ` --envfile ` , CLI files take precedence over configuration-based ` env_files ` , but inline variables still win.
239+ ## Explanation
246240
247- ### Configuration format precedence
241+ ### Precedence
248242
249- When multiple configuration formats are present :
243+ When multiple sources define the same variable, precedence applies in this order (highest to lowest) :
250244
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.
245+ 1 . Inline variables in configuration files (TOML or INI format).
246+ 1 . Variables from ` .env ` files loaded via ` env_files ` . When using ` --envfile ` , CLI files take precedence over
247+ configuration-based ` env_files ` .
248+ 1 . Variables already present in the environment (preserved when ` skip_if_set = true ` or ` D: ` flag is used).
249+
250+ When multiple configuration formats are present, TOML native format (` [pytest_env] ` / ` [tool.pytest_env] ` ) takes
251+ precedence over INI format. Among TOML files, the first file with a ` pytest_env ` section wins, checked in order:
252+ ` pytest.toml ` , ` .pytest.toml ` , ` pyproject.toml ` . If no TOML file contains ` pytest_env ` , the plugin falls back to
253+ INI-style ` env ` configuration.
255254
256255### File discovery
257256
258257The plugin walks up the directory tree starting from pytest's resolved configuration directory. For each directory, it
259258checks ` 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:
259+ ` pytest_env ` section. This means subdirectory configurations take precedence over parent configurations, allowing
260+ different settings for integration tests versus unit tests.
284261
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).
262+ ### Choosing a configuration format
288263
289- Use ** inline configuration** when:
264+ ** TOML native format** (` [pytest_env] ` ) is best when you need fine-grained control over expansion and conditional
265+ setting, or when your configuration uses multiple inline tables. Variable expansion requires explicit
266+ ` transform = true ` .
290267
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.
268+ ** INI format** (` env ` key) is best for simple ` KEY=VALUE ` pairs with minimal syntax. Variable expansion is on by default
269+ (use ` R: ` to disable). Both formats are fully supported and can coexist -- TOML takes precedence if both are present.
294270
295- You can combine both approaches. Inline variables always take precedence over ` .env ` files.
271+ ** ` .env ` files** work well when you have many variables that would clutter your config file, want to share environment
272+ configuration with other tools (Docker, shell scripts), or need different files for different environments. ** Inline
273+ configuration** is better for a small number of test-specific variables that should be version-controlled, or when you
274+ need ` transform ` , ` skip_if_set ` , or ` unset ` . You can combine both -- inline variables always take precedence over ` .env `
275+ files.
0 commit comments