Coverage for enderchest/filesystem.py: 94%

36 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-06 16:00 +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 

14PLACE_CACHE_NAME = ".place_cache.json" 

15 

16 

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

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

19 folder 

20 

21 Parameters 

22 ---------- 

23 minecraft_root : Path 

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

25 that's the parent of your EnderChest folder) 

26 check_exists : bool, optional 

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

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

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

30 `check_exists=False`. 

31 

32 Returns 

33 ------- 

34 Path 

35 The path to the EnderChest folder 

36 

37 Raises 

38 ------ 

39 FileNotFoundError 

40 If no valid EnderChest installation exists within the given 

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

42 """ 

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

44 

45 

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

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

48 config file 

49 

50 Parameters 

51 ---------- 

52 minecraft_root : Path 

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

54 that's the parent of your EnderChest folder) 

55 check_exists : bool, optional 

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

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

58 with `check_exists=False`. 

59 

60 Returns 

61 ------- 

62 Path 

63 The path to the EnderChest config file 

64 

65 Raises 

66 ------ 

67 FileNotFoundError 

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

69 checking hasn't been disabled) 

70 

71 Notes 

72 ----- 

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

74 """ 

75 config_path = minecraft_root / ENDER_CHEST_FOLDER_NAME / ENDER_CHEST_CONFIG_NAME 

76 

77 if check_exists and not config_path.exists(): 

78 raise FileNotFoundError( 

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

80 ) 

81 return config_path 

82 

83 

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

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

86 minecraft root directory 

87 

88 Parameters 

89 ---------- 

90 minecraft_root : Path 

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

92 that's the parent of your EnderChest folder) 

93 shulker_box_name : str 

94 The name of the shulker box to resolve 

95 

96 Returns 

97 ------- 

98 Path 

99 The path to the shulker box folder 

100 

101 Notes 

102 ----- 

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

104 """ 

105 return ender_chest_folder(minecraft_root) / shulker_box_name 

106 

107 

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

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

110 the minecraft root directory 

111 

112 Parameters 

113 ---------- 

114 minecraft_root : Path 

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

116 that's the parent of your EnderChest folder) 

117 shulker_box_name : str 

118 The name of the shulker box to resolve 

119 

120 Returns 

121 ------- 

122 Path 

123 The path to the shulker box folder 

124 

125 Notes 

126 ----- 

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

128 """ 

129 return shulker_box_root(minecraft_root, shulker_box_name) / SHULKER_BOX_CONFIG_NAME 

130 

131 

132def place_cache(minecraft_root: Path) -> Path: 

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

134 place cache file 

135 

136 Parameters 

137 ---------- 

138 minecraft_root : Path 

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

140 that's the parent of your EnderChest folder) 

141 

142 Returns 

143 ------- 

144 Path 

145 The path to the place cache file 

146 

147 Notes 

148 ----- 

149 This method does not check if the cache file is valid or if it even exists 

150 """ 

151 return ender_chest_folder(minecraft_root) / PLACE_CACHE_NAME 

152 

153 

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

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

156 

157 Parameters 

158 ---------- 

159 minecraft_root : Path 

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

161 that's the parent of your EnderChest folder) 

162 

163 Returns 

164 ------- 

165 list-like of paths 

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

167 

168 Notes 

169 ----- 

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

171 just that they exist 

172 """ 

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

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

175 

176 

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

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

179 

180 Parameters 

181 ---------- 

182 search_path : Path 

183 The directory to search 

184 

185 Returns 

186 ------- 

187 list-like of paths 

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

189 

190 Notes 

191 ----- 

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

193 contain valid minecraft instances, just that they exist 

194 """ 

195 return search_path.rglob(".minecraft") 

196 

197 

198def links_into_enderchest( 

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

200) -> bool: 

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

202 by the Minecraft root. 

203 

204 Parameters 

205 ---------- 

206 minecraft_root : Path 

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

208 that's the parent of your EnderChest folder) 

209 link : Path 

210 The link to check 

211 check_exists : bool, optional 

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

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

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

215 `check_exists=False`. 

216 

217 Returns 

218 ------- 

219 bool 

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

221 

222 Notes 

223 ----- 

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

225 fully resolved path. 

226 

227 Raises 

228 ------ 

229 FileNotFoundError 

230 If no valid EnderChest installation exists within the given 

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

232 OSError 

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

234 """ 

235 chest_folder = os.path.normpath( 

236 ender_chest_folder(minecraft_root, check_exists=check_exists) 

237 .expanduser() 

238 .absolute() 

239 ) 

240 

241 target = os.readlink(link) 

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

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

244 

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

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

247 try: 

248 os.stat(target[4:]) 

249 target = target[4:] 

250 except (OSError, FileNotFoundError): 

251 # then maybe this is somehow legit 

252 pass 

253 

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

255 try: 

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

257 except ValueError: # if they have no common root 

258 common_root = "" 

259 return common_root == chest_folder