Coverage for pyguymer3/save_file_if_needed.py: 2%

41 statements  

« prev     ^ index     » next       coverage.py v7.9.2, created at 2025-07-08 18:47 +0000

1#!/usr/bin/env python3 

2 

3# Define function ... 

4def save_file_if_needed( 

5 fname, 

6 fcontent, 

7 /, 

8 *, 

9 debug = __debug__, 

10 gitFiles = None, 

11 gitMessage = "regenerated", 

12 gitPath = None, 

13 timeout = 60.0, 

14): 

15 """Save a file. If the file already exists, then only overwrite it if the 

16 content is different. 

17 

18 Parameters 

19 ---------- 

20 fname : str 

21 the file name 

22 fcontent : bytes or str 

23 the file content 

24 debug : bool, optional 

25 print debug messages 

26 gitFiles : list of str, optional 

27 a list of file names known to Git, if the file ends up being saved and 

28 the file is not already known to Git then it will be added to Git 

29 gitMessage : str, optional 

30 the Git commit message, if the file ends up being saved and then commit 

31 it to Git 

32 gitPath : str, optional 

33 the path to the "git" binary (if not provided then Python will attempt 

34 to find the binary itself) 

35 timeout : float, optional 

36 the timeout for any requests/subprocess calls 

37 

38 Returns 

39 ------- 

40 needsSaving : bool 

41 did the file need saving 

42 

43 Notes 

44 ----- 

45 Copyright 2017 Thomas Guymer [1]_ 

46 

47 References 

48 ---------- 

49 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3 

50 """ 

51 

52 # Import standard modules ... 

53 import os 

54 import shutil 

55 import subprocess 

56 

57 # ************************************************************************** 

58 

59 # Try to find the paths if the user did not provide them ... 

60 if gitPath is None: 

61 gitPath = shutil.which("git") 

62 assert gitPath is not None, "\"git\" is not installed" 

63 

64 # Check that the content is one of the two types allowed in Python 3 and set 

65 # the file access mode and the encoding ... 

66 match fcontent: 

67 case bytes(): 

68 mode = "b" 

69 encoding = None 

70 case str(): 

71 mode = "t" 

72 encoding = "utf-8" 

73 case _: 

74 # Crash ... 

75 raise TypeError(f"\"fcontent\" is an unexpected type ({repr(type(fcontent))})") from None 

76 

77 # Initialize trigger ... 

78 needsSaving = False 

79 

80 # Check if the file exists ... 

81 if os.path.exists(fname): 

82 # Open the file ... 

83 with open(fname, f"r{mode}", encoding = encoding) as fObj: 

84 # Check if the content is the same ... 

85 if fObj.read() != fcontent: 

86 # Set trigger ... 

87 needsSaving = True 

88 else: 

89 # Set trigger ... 

90 needsSaving = True 

91 

92 # Create short-hand for the parent directory ... 

93 dname = os.path.dirname(fname) 

94 

95 # Check that there is a parent directory in the provided file name path ... 

96 if dname != "": 

97 # Check if the parent directory does not exist ... 

98 if not os.path.exists(dname): 

99 # Make the parent directory ... 

100 os.makedirs(dname) 

101 

102 # Check if the file needs saving ... 

103 if needsSaving: 

104 if debug: 

105 print(f"INFO: Saving \"{fname}\" ...") 

106 

107 # Open the file ... 

108 with open(fname, f"w{mode}", encoding = encoding) as fObj: 

109 # Save the file ... 

110 fObj.write(fcontent) 

111 

112 # Check if the user provided a list of files known to Git ... 

113 if gitFiles is not None: 

114 # Check if the file is not known to Git ... 

115 if fname not in gitFiles: 

116 if debug: 

117 print(f"INFO: Adding \"{fname}\" to Git ...") 

118 

119 # Add the file to Git ... 

120 subprocess.run( 

121 [ 

122 gitPath, 

123 "add", 

124 "--intent-to-add", 

125 fname, 

126 ], 

127 check = True, 

128 encoding = "utf-8", 

129 stderr = subprocess.DEVNULL, 

130 stdout = subprocess.DEVNULL, 

131 timeout = timeout, 

132 ) 

133 

134 # Check if the user provided a commit message ... 

135 if gitMessage is not None: 

136 if debug: 

137 print(f"INFO: Committing \"{fname}\" to Git ...") 

138 

139 # Commit the file to Git ... 

140 subprocess.run( 

141 [ 

142 gitPath, 

143 "commit", 

144 fname, 

145 "-m", gitMessage, 

146 ], 

147 check = True, 

148 encoding = "utf-8", 

149 stderr = subprocess.DEVNULL, 

150 stdout = subprocess.DEVNULL, 

151 timeout = timeout, 

152 ) 

153 

154 # Return answer ... 

155 return needsSaving