Coverage for enderchest/filesystem.py: 94%
36 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-06 16:00 +0000
« 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
6from .loggers import GATHER_LOGGER
8ENDER_CHEST_FOLDER_NAME = "EnderChest"
10ENDER_CHEST_CONFIG_NAME = "enderchest.cfg"
12SHULKER_BOX_CONFIG_NAME = "shulkerbox.cfg"
14PLACE_CACHE_NAME = ".place_cache.json"
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
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`.
32 Returns
33 -------
34 Path
35 The path to the EnderChest folder
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
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
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`.
60 Returns
61 -------
62 Path
63 The path to the EnderChest config file
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)
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
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
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
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
96 Returns
97 -------
98 Path
99 The path to the shulker box folder
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
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
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
120 Returns
121 -------
122 Path
123 The path to the shulker box folder
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
132def place_cache(minecraft_root: Path) -> Path:
133 """Given a minecraft root directory, return the path to the EnderChest
134 place cache file
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)
142 Returns
143 -------
144 Path
145 The path to the place cache file
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
154def shulker_box_configs(minecraft_root: Path) -> Iterable[Path]:
155 """Find all shulker box configs on the system
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)
163 Returns
164 -------
165 list-like of paths
166 The paths to all the shulker box configs on the system
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}")
177def minecraft_folders(search_path: Path) -> Iterable[Path]:
178 """Find all .minecraft folders within a given search path
180 Parameters
181 ----------
182 search_path : Path
183 The directory to search
185 Returns
186 -------
187 list-like of paths
188 The paths to all the .minecraft folders this method could find
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")
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.
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`.
217 Returns
218 -------
219 bool
220 True if the path is inside of the EnderChest folder. False otherwise.
222 Notes
223 -----
224 This method only checks the *direct target* of the link as opposed to the
225 fully resolved path.
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 )
241 target = os.readlink(link)
242 if not os.path.isabs(target):
243 target = os.path.normpath(link.parent / target)
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
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