Coverage for src/system/commands.py: 100.00%

5 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-08 13:49 +0000

1#!/usr/bin/env python3 

2 

3# Standard libraries 

4from pathlib import Path 

5from shutil import which 

6import subprocess 

7from typing import List, Optional 

8 

9# Commands 

10class Commands: 

11 

12 # Exec 

13 @staticmethod 

14 def exec(binary: str, arguments: List[str]) -> int: # pragma: no cover 

15 

16 # Run system command 

17 try: 

18 process = subprocess.run( 

19 args=[binary] + arguments, 

20 cwd=None, 

21 check=False, 

22 shell=False, 

23 ) 

24 return process.returncode 

25 except subprocess.CalledProcessError as e: 

26 print(f"Error executing command: {e}") 

27 return 1 

28 

29 # Exists 

30 @staticmethod 

31 def exists(binary: str) -> bool: # pragma: no cover 

32 

33 # Check system binary exists 

34 path: Optional[str] = which(binary) 

35 if path is None: 

36 return False 

37 

38 # Check mise shims resolve to an active tool version 

39 if Path(path).resolve().name == 'mise' and Path(path).name != 'mise': 

40 try: 

41 return subprocess.run( 

42 args=['mise', 'which', binary], 

43 check=False, 

44 shell=False, 

45 stdout=subprocess.DEVNULL, 

46 stderr=subprocess.DEVNULL, 

47 ).returncode == 0 

48 except OSError: 

49 return False 

50 

51 # Result 

52 return True 

53 

54 # Grep 

55 @staticmethod 

56 def grep(file: Path, string: str) -> bool: # pragma: no cover 

57 

58 # Ignore missing file 

59 if not file.exists(): 

60 return False 

61 

62 # Check file contains string 

63 with open( 

64 file, 

65 encoding='utf8', 

66 mode='r', 

67 ) as f: 

68 for line in f.readlines(): 

69 

70 # Find string in line 

71 if string in line: 

72 return True 

73 

74 # Fallback 

75 return False # pragma: no cover 

76 

77 # Mise 

78 @staticmethod 

79 def mise(arguments: List[str], check: bool = True) -> bool: # pragma: no cover 

80 

81 # Run mise 

82 return Commands.run( 

83 'mise', 

84 arguments, 

85 check, 

86 ) 

87 

88 # Output 

89 @staticmethod 

90 def output( 

91 binary: str, 

92 arguments: List[str], 

93 quiet: bool = False, 

94 errors: bool = False, 

95 ) -> str: # pragma: no cover 

96 

97 # Variables 

98 stderr: Optional[int] = None 

99 

100 # Redirect errors 

101 if errors: 

102 stderr = subprocess.STDOUT 

103 elif quiet: 

104 stderr = subprocess.DEVNULL 

105 

106 # Get system output 

107 try: 

108 return subprocess.check_output( 

109 args=[binary] + arguments, 

110 cwd=None, 

111 shell=False, 

112 stderr=stderr, 

113 ).strip().decode() 

114 except subprocess.CalledProcessError as err: 

115 return str(err.output.decode()) 

116 except FileNotFoundError: 

117 return '' 

118 

119 # Pip 

120 @staticmethod 

121 def pip(arguments: List[str]) -> bool: # pragma: no cover 

122 

123 # Run with pipx 

124 if Commands.exists('pipx'): 

125 return Commands.run( 

126 'pipx', 

127 arguments, 

128 ) 

129 

130 # Run with pip # pragma: no cover 

131 return Commands.run( 

132 'sudo', 

133 ['pip'] + arguments, 

134 ) 

135 

136 # Run 

137 @staticmethod 

138 def run(binary: str, arguments: List[str], 

139 check: bool = True) -> bool: # pragma: no cover 

140 

141 # Variables 

142 stderr: Optional[int] = None if check else subprocess.DEVNULL 

143 stdout: Optional[int] = None if check else subprocess.DEVNULL 

144 

145 # Run system command 

146 try: 

147 process = subprocess.run( 

148 args=[binary] + arguments, 

149 cwd=None, 

150 check=check, 

151 shell=False, 

152 stderr=stderr, 

153 stdout=stdout, 

154 ) 

155 return process.returncode == 0 

156 except subprocess.CalledProcessError as e: 

157 if check: 

158 print(f"Error executing command: {e}") 

159 return False