Coverage for src / dotbot / util / common.py: 89%

18 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-29 10:55 -0800

1import os 

2import platform 

3import subprocess 

4import sys 

5from typing import Optional 

6 

7 

8def shell_command( 

9 command: str, 

10 cwd: Optional[str] = None, 

11 *, 

12 enable_stdin: bool = False, 

13 enable_stdout: bool = False, 

14 enable_stderr: bool = False, 

15) -> int: 

16 with open(os.devnull, "w") as devnull_w, open(os.devnull) as devnull_r: 

17 stdin = None if enable_stdin else devnull_r 

18 stdout = None if enable_stdout else devnull_w 

19 stderr = None if enable_stderr else devnull_w 

20 executable = os.environ.get("SHELL") 

21 if platform.system() == "Windows": 

22 # We avoid setting the executable kwarg on Windows because it does 

23 # not have the desired effect when combined with shell=True. It 

24 # will result in the correct program being run (e.g. bash), but it 

25 # will be invoked with a '/c' argument instead of a '-c' argument, 

26 # which it won't understand. 

27 # 

28 # See https://github.com/anishathalye/dotbot/issues/219 and 

29 # https://bugs.python.org/issue40467. 

30 # 

31 # This means that complex commands that require Bash's parsing 

32 # won't work; a workaround for this is to write the command as 

33 # `bash -c "..."`. 

34 executable = None 

35 return subprocess.call( # noqa: S602 

36 command, 

37 shell=True, 

38 executable=executable, 

39 stdin=stdin, 

40 stdout=stdout, 

41 stderr=stderr, 

42 cwd=cwd, 

43 ) 

44 

45 

46def normslash(path: str) -> str: 

47 if sys.platform == "win32": 

48 # this is how normcase in cpython/Lib/ntpath.py does it; we don't use normcase 

49 # because we don't want to make all characters lowercase 

50 return path.replace("/", "\\") 

51 return path