Skip to content

Commit 8eda69f

Browse files
committed
✨ feat(plugin): add U flag to unset env variables
Setting a variable to empty string isn't the same as removing it from the environment. The new U: flag (INI) and unset key (TOML) allow removing variables from os.environ, following the existing D:/R: flag pattern. Closes #89
1 parent c291176 commit 8eda69f

3 files changed

Lines changed: 54 additions & 8 deletions

File tree

src/pytest_env/plugin.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class Entry:
3333
value: str
3434
transform: bool
3535
skip_if_set: bool
36+
unset: bool = False
3637

3738

3839
@pytest.hookimpl(tryfirst=True)
@@ -43,20 +44,24 @@ def pytest_load_initial_conftests(
4344
) -> None:
4445
"""Load environment variables from configuration files."""
4546
for entry in _load_values(early_config):
46-
if entry.skip_if_set and entry.key in os.environ:
47+
if entry.unset:
48+
os.environ.pop(entry.key, None)
49+
elif entry.skip_if_set and entry.key in os.environ:
4750
continue
48-
# transformation -> replace environment variables, e.g. TEST_DIR={USER}/repo_test_dir.
49-
os.environ[entry.key] = entry.value.format(**os.environ) if entry.transform else entry.value
51+
else:
52+
# transformation -> replace environment variables, e.g. TEST_DIR={USER}/repo_test_dir.
53+
os.environ[entry.key] = entry.value.format(**os.environ) if entry.transform else entry.value
5054

5155

5256
def _parse_toml_config(config: dict[str, Any]) -> Generator[Entry, None, None]:
5357
for key, entry in config.items():
5458
if isinstance(entry, dict):
55-
value = str(entry["value"])
59+
unset = bool(entry.get("unset"))
60+
value = str(entry.get("value", "")) if not unset else ""
5661
transform, skip_if_set = bool(entry.get("transform")), bool(entry.get("skip_if_set"))
5762
else:
58-
value, transform, skip_if_set = str(entry), False, False
59-
yield Entry(key, value, transform, skip_if_set)
63+
value, transform, skip_if_set, unset = str(entry), False, False, False
64+
yield Entry(key, value, transform, skip_if_set, unset=unset)
6065

6166

6267
def _load_values(early_config: pytest.Config) -> Iterator[Entry]:
@@ -91,6 +96,8 @@ def _load_values(early_config: pytest.Config) -> Iterator[Entry]:
9196
transform = "R" not in flags
9297
# D: is a way to mark the value to be set only if it does not exist yet
9398
skip_if_set = "D" in flags
99+
# U: is a way to unset (remove) an environment variable
100+
unset = "U" in flags
94101
key = ini_key_parts[-1].strip()
95102
value = parts[2].strip()
96-
yield Entry(key, value, transform, skip_if_set)
103+
yield Entry(key, value, transform, skip_if_set, unset=unset)

tests/template.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@
66

77
def test_env() -> None:
88
for key, value in ast.literal_eval(os.environ["_TEST_ENV"]).items():
9-
assert os.environ[key] == value, key
9+
if value is None:
10+
assert key not in os.environ, f"{key} should be unset"
11+
else:
12+
assert os.environ[key] == value, key

tests/test_env.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,24 @@
9090
{"MAGIC": "zero"},
9191
id="empty ini works",
9292
),
93+
pytest.param(
94+
{"MAGIC": "alpha"},
95+
"[pytest]\nenv = U:MAGIC",
96+
{"MAGIC": None},
97+
id="U flag - unset existing var",
98+
),
99+
pytest.param(
100+
{},
101+
"[pytest]\nenv = U:MAGIC",
102+
{"MAGIC": None},
103+
id="U flag - unset non-existing var",
104+
),
105+
pytest.param(
106+
{"MAGIC": "alpha"},
107+
"[pytest]\nenv = U:MAGIC\n MAGIC=beta",
108+
{"MAGIC": "beta"},
109+
id="U flag then set - var is set",
110+
),
93111
],
94112
)
95113
def test_env_via_pytest(
@@ -220,6 +238,24 @@ def test_env_via_pytest(
220238
"pytest.toml",
221239
id="pytest toml over pyproject toml",
222240
),
241+
pytest.param(
242+
{"MAGIC": "alpha"},
243+
"[tool.pytest_env]\nMAGIC = {unset = true}",
244+
"",
245+
"",
246+
{"MAGIC": None},
247+
None,
248+
id="pyproject toml unset",
249+
),
250+
pytest.param(
251+
{},
252+
"[tool.pytest_env]\nMAGIC = {unset = true}",
253+
"",
254+
"",
255+
{"MAGIC": None},
256+
None,
257+
id="pyproject toml unset non-existing",
258+
),
223259
],
224260
)
225261
def test_env_via_toml( # noqa: PLR0913, PLR0917

0 commit comments

Comments
 (0)