Coverage for enderchest/filesystem.py: 94%

33 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-10-03 20:14 +0000

1"""Functionality for managing the EnderChest and shulker box config files and folders""" 

2import os 

3from pathlib import Path 

4from typing import Iterable 

5 

6from .loggers import GATHER_LOGGER 

7 

8ENDER_CHEST_FOLDER_NAME = "EnderChest" 

9 

10ENDER_CHEST_CONFIG_NAME = "enderchest.cfg" 

11 

12SHULKER_BOX_CONFIG_NAME = "shulkerbox.cfg" 

13 

14 

15def ender_chest_folder(minecraft_root: Path, check_exists: bool = True) -> Path: 

16 """Given a minecraft root directory, return the path to the EnderChest 

17 folder 

18 

19 Parameters 

20 ---------- 

21 minecraft_root : Path 

22 The root directory that your minecraft stuff (or, at least, the one 

23 that's the parent of your EnderChest folder) 

24 check_exists : bool, optional 

25 By default, this method will raise an error if no EnderChest exists 

26 at that location (meaning no folder or no enderchest config file in 

27 that folder). To disable that check, call this method with 

28 `check_exists=False`. 

29 

30 Returns 

31 ------- 

32 Path 

33 The path to the EnderChest folder 

34 

35 Raises 

36 ------ 

37 FileNotFoundError 

38 If no valid EnderChest installation exists within the given 

39 minecraft root (and checking hasn't been disabled) 

40 """ 

41 return ender_chest_config(minecraft_root, check_exists=check_exists).parent 

42 

43 

44def ender_chest_config(minecraft_root, check_exists: bool = True) -> Path: 

45 """Given a minecraft root directory, return the path to the EnderChest 

46 config file 

47 

48 Parameters 

49 ---------- 

50 minecraft_root : Path 

51 The root directory that your minecraft stuff (or, at least, the one 

52 that's the parent of your EnderChest folder) 

53 check_exists : bool, optional 

54 By default, this method will raise an error if the enderchest config 

55 file does not already exist. To disable that check, call this method 

56 with `check_exists=False`. 

57 

58 Returns 

59 ------- 

60 Path 

61 The path to the EnderChest config file 

62 

63 Raises 

64 ------ 

65 FileNotFoundError 

66 If the EnderChest config file isn't where it's supposed to be (and 

67 checking hasn't been disabled) 

68 

69 Notes 

70 ----- 

71 This method does not check if the config file is valid 

72 """ 

73 config_path = minecraft_root / ENDER_CHEST_FOLDER_NAME / ENDER_CHEST_CONFIG_NAME 

74 

75 if check_exists and not config_path.exists(): 

76 raise FileNotFoundError( 

77 f"No valid EnderChest installation exists within {minecraft_root}" 

78 ) 

79 return config_path 

80 

81 

82def shulker_box_root(minecraft_root: Path, shulker_box_name: str) -> Path: 

83 """Generate the path to the root of a shulker box, given its name and the 

84 minecraft root directory 

85 

86 Parameters 

87 ---------- 

88 minecraft_root : Path 

89 The root directory that your minecraft stuff (or, at least, the one 

90 that's the parent of your EnderChest folder) 

91 

92 Returns 

93 ------- 

94 Path 

95 The path to the shulker box folder 

96 

97 Notes 

98 ----- 

99 This method does not check if a shulker box exists at that location 

100 """ 

101 return ender_chest_folder(minecraft_root) / shulker_box_name 

102 

103 

104def shulker_box_config(minecraft_root: Path, shulker_box_name: str) -> Path: 

105 """Generate the path to a shulker box config file, given its name and 

106 the minecraft root directory 

107 

108 Parameters 

109 ---------- 

110 minecraft_root : Path 

111 The root directory that your minecraft stuff (or, at least, the one 

112 that's the parent of your EnderChest folder) 

113 

114 Returns 

115 ------- 

116 Path 

117 The path to the shulker box folder 

118 

119 Notes 

120 ----- 

121 This method does not check if a shulker box config exists at that location 

122 """ 

123 return shulker_box_root(minecraft_root, shulker_box_name) / SHULKER_BOX_CONFIG_NAME 

124 

125 

126def shulker_box_configs(minecraft_root: Path) -> Iterable[Path]: 

127 """Find all shulker box configs on the system 

128 

129 Parameters 

130 ---------- 

131 minecraft_root : Path 

132 The root directory that your minecraft stuff (or, at least, the one 

133 that's the parent of your EnderChest folder) 

134 

135 Returns 

136 ------- 

137 list-like of paths 

138 The paths to all the shulker box configs on the system 

139 

140 Notes 

141 ----- 

142 This method does not check to make sure those config files are valid, 

143 just that they exist 

144 """ 

145 GATHER_LOGGER.debug(f"Searching for shulker configs within {minecraft_root}") 

146 return ender_chest_folder(minecraft_root).glob(f"*/{SHULKER_BOX_CONFIG_NAME}") 

147 

148 

149def minecraft_folders(search_path: Path) -> Iterable[Path]: 

150 """Find all .minecraft folders within a given search path 

151 

152 Parameters 

153 ---------- 

154 search_path : Path 

155 The directory to search 

156 

157 Returns 

158 ------- 

159 list-like of paths 

160 The paths to all the .minecraft folders this method could find 

161 

162 Notes 

163 ----- 

164 This method does not check to make sure that those .minecraft folders 

165 contain valid minecraft instances, just that they exist 

166 """ 

167 return search_path.rglob(".minecraft") 

168 

169 

170def links_into_enderchest( 

171 minecraft_root: Path, link: Path, check_exists: bool = True 

172) -> bool: 

173 """Determine whether a symlink's target is inside the EnderChest specified 

174 by the Minecraft root. 

175 

176 Parameters 

177 ---------- 

178 minecraft_root : Path 

179 The root directory that your minecraft stuff (or, at least, the one 

180 that's the parent of your EnderChest folder) 

181 link : Path 

182 The link to check 

183 check_exists : bool, optional 

184 By default, this method will raise an error if no EnderChest exists 

185 at that location (meaning no folder or no enderchest config file in 

186 that folder). To disable that check, call this method with 

187 `check_exists=False`. 

188 

189 Returns 

190 ------- 

191 bool 

192 True if the path is inside of the EnderChest folder. False otherwise. 

193 

194 Notes 

195 ----- 

196 This method only checks the *direct target* of the link as opposed to the 

197 fully resolved path. 

198 

199 Raises 

200 ------ 

201 FileNotFoundError 

202 If no valid EnderChest installation exists within the given 

203 minecraft root (and checking hasn't been disabled) 

204 OSError 

205 If the link provided isn't actually a symbolic link 

206 """ 

207 chest_folder = os.path.normpath( 

208 ender_chest_folder(minecraft_root, check_exists=check_exists) 

209 .expanduser() 

210 .absolute() 

211 ) 

212 

213 target = os.readlink(link) 

214 if not os.path.isabs(target): 

215 target = os.path.normpath(link.parent / target) 

216 

217 # Windows shenanigans: https://bugs.python.org/issue42957 

218 if target.startswith(("\\\\?\\", "\\??\\")): # pragma: no cover 

219 try: 

220 os.stat(target[4:]) 

221 target = target[4:] 

222 except (OSError, FileNotFoundError): 

223 # then maybe this is somehow legit 

224 pass 

225 

226 # there's probably a better way to check if a file is inside a sub-path 

227 try: 

228 common_root = os.path.commonpath([target, chest_folder]) 

229 except ValueError: # if they have no common root 

230 common_root = "" 

231 return common_root == chest_folder