Skip to content

Commit 0b1e63d

Browse files
committed
Remove the use of sh in tests
1 parent 09d7cee commit 0b1e63d

File tree

6 files changed

+116
-87
lines changed

6 files changed

+116
-87
lines changed

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ click
33
ipython
44
pytest-cov
55
pytest>=3.9
6-
sh>=2
76
tox
87
wheel
98
ruff

tests/test_cli.py

Lines changed: 52 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
import subprocess
3-
import sys
43
from pathlib import Path
54
from typing import Optional, Sequence
65

@@ -9,9 +8,7 @@
98
import dotenv
109
from dotenv.cli import cli as dotenv_cli
1110
from dotenv.version import __version__
12-
13-
if sys.platform != "win32":
14-
import sh
11+
from tests.test_lib import check_process, run_dotenv
1512

1613

1714
def invoke_sub(args: Sequence[str]) -> subprocess.CompletedProcess:
@@ -192,111 +189,109 @@ def test_set_no_file(cli):
192189
assert "Missing argument" in result.output
193190

194191

195-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
196192
def test_get_default_path(tmp_path):
197-
with sh.pushd(tmp_path):
198-
(tmp_path / ".env").write_text("a=b")
193+
(tmp_path / ".env").write_text("A=x")
199194

200-
result = sh.dotenv("get", "a")
195+
result = run_dotenv(["get", "A"], cwd=tmp_path)
201196

202-
assert result == "b\n"
197+
check_process(result, exit_code=0, stdout="x\n")
203198

204199

205-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
206200
def test_run(tmp_path):
207-
with sh.pushd(tmp_path):
208-
(tmp_path / ".env").write_text("a=b")
201+
(tmp_path / ".env").write_text("A=x")
209202

210-
result = sh.dotenv("run", "printenv", "a")
203+
result = run_dotenv(["run", "printenv", "A"], cwd=tmp_path)
211204

212-
assert result == "b\n"
205+
check_process(result, exit_code=0, stdout="x\n")
213206

214207

215-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
216208
def test_run_with_existing_variable(tmp_path):
217-
with sh.pushd(tmp_path):
218-
(tmp_path / ".env").write_text("a=b")
219-
env = dict(os.environ)
220-
env.update({"LANG": "en_US.UTF-8", "a": "c"})
209+
(tmp_path / ".env").write_text("A=x")
210+
env = dict(os.environ)
211+
env.update({"LANG": "en_US.UTF-8", "A": "y"})
221212

222-
result = sh.dotenv("run", "printenv", "a", _env=env)
213+
result = run_dotenv(["run", "printenv", "A"], cwd=tmp_path, env=env)
223214

224-
assert result == "b\n"
215+
check_process(result, exit_code=0, stdout="x\n")
225216

226217

227-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
228218
def test_run_with_existing_variable_not_overridden(tmp_path):
229-
with sh.pushd(tmp_path):
230-
(tmp_path / ".env").write_text("a=b")
231-
env = dict(os.environ)
232-
env.update({"LANG": "en_US.UTF-8", "a": "c"})
219+
(tmp_path / ".env").write_text("A=x")
220+
env = dict(os.environ)
221+
env.update({"LANG": "en_US.UTF-8", "A": "C"})
233222

234-
result = sh.dotenv("run", "--no-override", "printenv", "a", _env=env)
223+
result = run_dotenv(
224+
["run", "--no-override", "printenv", "A"], cwd=tmp_path, env=env
225+
)
235226

236-
assert result == "c\n"
227+
check_process(result, exit_code=0, stdout="C\n")
237228

238229

239-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
240230
def test_run_with_none_value(tmp_path):
241-
with sh.pushd(tmp_path):
242-
(tmp_path / ".env").write_text("a=b\nc")
231+
(tmp_path / ".env").write_text("A=x\nc")
243232

244-
result = sh.dotenv("run", "printenv", "a")
233+
result = run_dotenv(["run", "printenv", "A"], cwd=tmp_path)
245234

246-
assert result == "b\n"
235+
check_process(result, exit_code=0, stdout="x\n")
247236

248237

249-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
250-
def test_run_with_other_env(dotenv_path):
251-
dotenv_path.write_text("a=b")
238+
def test_run_with_other_env(dotenv_path, tmp_path):
239+
dotenv_path.write_text("A=x")
252240

253-
result = sh.dotenv("--file", dotenv_path, "run", "printenv", "a")
241+
result = run_dotenv(
242+
["--file", str(dotenv_path), "run", "printenv", "A"],
243+
cwd=tmp_path,
244+
)
254245

255-
assert result == "b\n"
246+
check_process(result, exit_code=0, stdout="x\n")
256247

257248

258-
def test_run_without_cmd(cli):
259-
result = cli.invoke(dotenv_cli, ["run"])
249+
def test_run_without_cmd(tmp_path):
250+
result = run_dotenv(["run"], cwd=tmp_path)
260251

261-
assert result.exit_code == 2
262-
assert "Invalid value for '-f'" in result.output
252+
check_process(result, exit_code=2)
253+
assert "Invalid value for '-f'" in result.stderr
263254

264255

265-
def test_run_with_invalid_cmd(cli):
266-
result = cli.invoke(dotenv_cli, ["run", "i_do_not_exist"])
256+
def test_run_with_invalid_cmd(tmp_path):
257+
result = run_dotenv(["run", "i_do_not_exist"], cwd=tmp_path)
267258

268-
assert result.exit_code == 2
269-
assert "Invalid value for '-f'" in result.output
259+
check_process(result, exit_code=2)
260+
assert "Invalid value for '-f'" in result.stderr
270261

271262

272-
def test_run_with_version(cli):
273-
result = cli.invoke(dotenv_cli, ["--version"])
263+
def test_run_with_version(tmp_path):
264+
result = run_dotenv(["--version"], cwd=tmp_path)
274265

275-
assert result.exit_code == 0
276-
assert result.output.strip().endswith(__version__)
266+
check_process(result, exit_code=0)
267+
assert result.stdout.strip().endswith(__version__)
277268

278269

279-
def test_run_with_command_flags(dotenv_path):
270+
def test_run_with_command_flags(dotenv_path, tmp_path):
280271
"""
281272
Check that command flags passed after `dotenv run` are not interpreted.
282273
283274
Here, we want to run `printenv --version`, not `dotenv --version`.
284275
"""
285276

286-
result = invoke_sub(["--file", dotenv_path, "run", "printenv", "--version"])
277+
result = run_dotenv(
278+
["--file", str(dotenv_path), "run", "printenv", "--version"],
279+
cwd=tmp_path,
280+
)
287281

288-
assert result.returncode == 0
282+
check_process(result, exit_code=0)
289283
assert result.stdout.strip().startswith("printenv ")
290284

291285

292-
def test_run_with_dotenv_and_command_flags(cli, dotenv_path):
286+
def test_run_with_dotenv_and_command_flags(dotenv_path, tmp_path):
293287
"""
294288
Check that dotenv flags supersede command flags.
295289
"""
296290

297-
result = invoke_sub(
298-
["--version", "--file", dotenv_path, "run", "printenv", "--version"]
291+
result = run_dotenv(
292+
["--version", "--file", str(dotenv_path), "run", "printenv", "--version"],
293+
cwd=tmp_path,
299294
)
300295

301-
assert result.returncode == 0
296+
check_process(result, exit_code=0)
302297
assert result.stdout.strip().startswith("dotenv, version")

tests/test_lib.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import subprocess
2+
from typing import Optional
3+
4+
5+
def run_dotenv(args, cwd=None, env=None) -> subprocess.CompletedProcess:
6+
process = subprocess.run(
7+
["dotenv"] + args,
8+
capture_output=True,
9+
text=True,
10+
cwd=cwd,
11+
env=env,
12+
)
13+
14+
return process
15+
16+
17+
def check_process(
18+
process: subprocess.CompletedProcess,
19+
exit_code: int,
20+
stdout: Optional[str] = None,
21+
):
22+
assert process.returncode == exit_code, (
23+
f"Unexpected exit code {process.returncode} (expected {exit_code})\n"
24+
f"stdout:\n{process.stdout}\n"
25+
f"stderr:\n{process.stderr}"
26+
)
27+
28+
if stdout is not None:
29+
assert process.stdout == stdout, (
30+
f"Unexpected output: {process.stdout.strip()!r} (expected {stdout!r})"
31+
)

tests/test_main.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import os
44
import stat
5+
import subprocess
56
import sys
67
import textwrap
78
from unittest import mock
@@ -10,9 +11,6 @@
1011

1112
import dotenv
1213

13-
if sys.platform != "win32":
14-
import sh
15-
1614

1715
def test_set_key_no_file(tmp_path):
1816
nx_path = tmp_path / "nx"
@@ -483,7 +481,6 @@ def test_load_dotenv_file_stream(dotenv_path):
483481
assert os.environ == {"a": "b"}
484482

485483

486-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
487484
def test_load_dotenv_in_current_dir(tmp_path):
488485
dotenv_path = tmp_path / ".env"
489486
dotenv_path.write_bytes(b"a=b")
@@ -499,9 +496,14 @@ def test_load_dotenv_in_current_dir(tmp_path):
499496
)
500497
os.chdir(tmp_path)
501498

502-
result = sh.Command(sys.executable)(code_path)
499+
result = subprocess.run(
500+
[sys.executable, str(code_path)],
501+
capture_output=True,
502+
text=True,
503+
check=True,
504+
)
503505

504-
assert result == "b\n"
506+
assert result.stdout == "b\n"
505507

506508

507509
def test_dotenv_values_file(dotenv_path):

tests/test_zip_imports.py

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
import os
2+
import posixpath
3+
import subprocess
24
import sys
35
import textwrap
46
from typing import List
57
from unittest import mock
68
from zipfile import ZipFile
79

8-
import pytest
9-
10-
if sys.platform != "win32":
11-
import sh
12-
1310

1411
def walk_to_root(path: str):
1512
last_dir = None
1613
current_dir = path
1714
while last_dir != current_dir:
1815
yield current_dir
19-
(parent_dir, _) = os.path.split(current_dir)
16+
parent_dir = posixpath.dirname(current_dir)
2017
last_dir, current_dir = current_dir, parent_dir
2118

2219

@@ -32,12 +29,11 @@ def setup_zipfile(path, files: List[FileToAdd]):
3229
with ZipFile(zip_file_path, "w") as zipfile:
3330
for f in files:
3431
zipfile.writestr(data=f.content, zinfo_or_arcname=f.path)
35-
for dirname in walk_to_root(os.path.dirname(f.path)):
32+
for dirname in walk_to_root(posixpath.dirname(f.path)):
3633
if dirname not in dirs_init_py_added_to:
37-
print(os.path.join(dirname, "__init__.py"))
38-
zipfile.writestr(
39-
data="", zinfo_or_arcname=os.path.join(dirname, "__init__.py")
40-
)
34+
init_path = posixpath.join(dirname, "__init__.py")
35+
print(f"setup_zipfile: {init_path}")
36+
zipfile.writestr(data="", zinfo_or_arcname=init_path)
4137
dirs_init_py_added_to.add(dirname)
4238
return zip_file_path
4339

@@ -65,7 +61,6 @@ def test_load_dotenv_gracefully_handles_zip_imports_when_no_env_file(tmp_path):
6561
import child1.child2.test # noqa
6662

6763

68-
@pytest.mark.skipif(sys.platform == "win32", reason="sh module doesn't support Windows")
6964
def test_load_dotenv_outside_zip_file_when_called_in_zipfile(tmp_path):
7065
zip_file_path = setup_zipfile(
7166
tmp_path,
@@ -83,24 +78,32 @@ def test_load_dotenv_outside_zip_file_when_called_in_zipfile(tmp_path):
8378
],
8479
)
8580
dotenv_path = tmp_path / ".env"
86-
dotenv_path.write_bytes(b"a=b")
81+
dotenv_path.write_bytes(b"A=x")
8782
code_path = tmp_path / "code.py"
8883
code_path.write_text(
8984
textwrap.dedent(
9085
f"""
91-
import os
92-
import sys
86+
import os
87+
import sys
9388
94-
sys.path.append("{zip_file_path}")
89+
sys.path.append({str(zip_file_path)!r})
9590
96-
import child1.child2.test
91+
import child1.child2.test
9792
98-
print(os.environ['a'])
99-
"""
93+
print(os.environ['A'])
94+
"""
10095
)
10196
)
102-
os.chdir(str(tmp_path))
10397

104-
result = sh.Command(sys.executable)(code_path)
98+
result = subprocess.run(
99+
[sys.executable, str(code_path)],
100+
capture_output=True,
101+
check=True,
102+
cwd=tmp_path,
103+
text=True,
104+
env={
105+
k: v for k, v in os.environ.items() if k.upper() != "A"
106+
}, # env without 'A'
107+
)
105108

106-
assert result == "b\n"
109+
assert result.stdout == "x\n"

tox.ini

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ python =
1515
deps =
1616
pytest
1717
pytest-cov
18-
sh >= 2.0.2, <3
1918
click
2019
py{310,311,312,313,314,314t,pypy3}: ipython
2120
commands = pytest --cov --cov-report=term-missing {posargs}

0 commit comments

Comments
 (0)