Coverage for enderchest/filesystem.py: 94%
36 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-04 01:41 +0000
« prev ^ index » next coverage.py v7.5.0, created at 2024-05-04 01:41 +0000
1"""Functionality for managing the EnderChest and shulker box config files and folders"""
3import os
4from pathlib import Path
5from typing import Iterable
7from .loggers import INVENTORY_LOGGER
9ENDER_CHEST_FOLDER_NAME = "EnderChest"
11ENDER_CHEST_CONFIG_NAME = "enderchest.cfg"
13SHULKER_BOX_CONFIG_NAME = "shulkerbox.cfg"
15PLACE_CACHE_NAME = ".place_cache.json"
18def ender_chest_folder(minecraft_root: Path, check_exists: bool = True) -> Path:
19 """Given a minecraft root directory, return the path to the EnderChest
20 folder
22 Parameters
23 ----------
24 minecraft_root : Path
25 The root directory that your minecraft stuff (or, at least, the one
26 that's the parent of your EnderChest folder)
27 check_exists : bool, optional
28 By default, this method will raise an error if no EnderChest exists
29 at that location (meaning no folder or no enderchest config file in
30 that folder). To disable that check, call this method with
31 `check_exists=False`.
33 Returns
34 -------
35 Path
36 The path to the EnderChest folder
38 Raises
39 ------
40 FileNotFoundError
41 If no valid EnderChest installation exists within the given
42 minecraft root (and checking hasn't been disabled)
43 """
44 return ender_chest_config(minecraft_root, check_exists=check_exists).parent
47def ender_chest_config(minecraft_root, check_exists: bool = True) -> Path:
48 """Given a minecraft root directory, return the path to the EnderChest
49 config file
51 Parameters
52 ----------
53 minecraft_root : Path
54 The root directory that your minecraft stuff (or, at least, the one
55 that's the parent of your EnderChest folder)
56 check_exists : bool, optional
57 By default, this method will raise an error if the enderchest config
58 file does not already exist. To disable that check, call this method
59 with `check_exists=False`.
61 Returns
62 -------
63 Path
64 The path to the EnderChest config file
66 Raises
67 ------
68 FileNotFoundError
69 If the EnderChest config file isn't where it's supposed to be (and
70 checking hasn't been disabled)
72 Notes
73 -----
74 This method does not check if the config file is valid
75 """
76 config_path = minecraft_root / ENDER_CHEST_FOLDER_NAME / ENDER_CHEST_CONFIG_NAME
78 if check_exists and not config_path.exists():
79 raise FileNotFoundError(
80 f"No valid EnderChest installation exists within {minecraft_root}"
81 )
82 return config_path
85def shulker_box_root(minecraft_root: Path, shulker_box_name: str) -> Path:
86 """Generate the path to the root of a shulker box, given its name and the
87 minecraft root directory
89 Parameters
90 ----------
91 minecraft_root : Path
92 The root directory that your minecraft stuff (or, at least, the one
93 that's the parent of your EnderChest folder)
94 shulker_box_name : str
95 The name of the shulker box to resolve
97 Returns
98 -------
99 Path
100 The path to the shulker box folder
102 Notes
103 -----
104 This method does not check if a shulker box exists at that location
105 """
106 return ender_chest_folder(minecraft_root) / shulker_box_name
109def shulker_box_config(minecraft_root: Path, shulker_box_name: str) -> Path:
110 """Generate the path to a shulker box config file, given its name and
111 the minecraft root directory
113 Parameters
114 ----------
115 minecraft_root : Path
116 The root directory that your minecraft stuff (or, at least, the one
117 that's the parent of your EnderChest folder)
118 shulker_box_name : str
119 The name of the shulker box to resolve
121 Returns
122 -------
123 Path
124 The path to the shulker box folder
126 Notes
127 -----
128 This method does not check if a shulker box config exists at that location
129 """
130 return shulker_box_root(minecraft_root, shulker_box_name) / SHULKER_BOX_CONFIG_NAME
133def place_cache(minecraft_root: Path) -> Path:
134 """Given a minecraft root directory, return the path to the EnderChest
135 place cache file
137 Parameters
138 ----------
139 minecraft_root : Path
140 The root directory that your minecraft stuff (or, at least, the one
141 that's the parent of your EnderChest folder)
143 Returns
144 -------
145 Path
146 The path to the place cache file
148 Notes
149 -----
150 This method does not check if the cache file is valid or if it even exists
151 """
152 return ender_chest_folder(minecraft_root) / PLACE_CACHE_NAME
155def shulker_box_configs(minecraft_root: Path) -> Iterable[Path]:
156 """Find all shulker box configs on the system
158 Parameters
159 ----------
160 minecraft_root : Path
161 The root directory that your minecraft stuff (or, at least, the one
162 that's the parent of your EnderChest folder)
164 Returns
165 -------
166 list-like of paths
167 The paths to all the shulker box configs on the system
169 Notes
170 -----
171 This method does not check to make sure those config files are valid,
172 just that they exist
173 """
174 INVENTORY_LOGGER.debug(f"Searching for shulker configs within {minecraft_root}")
175 return ender_chest_folder(minecraft_root).glob(f"*/{SHULKER_BOX_CONFIG_NAME}")
178def minecraft_folders(search_path: Path) -> Iterable[Path]:
179 """Find all .minecraft folders within a given search path
181 Parameters
182 ----------
183 search_path : Path
184 The directory to search
186 Returns
187 -------
188 list-like of paths
189 The paths to all the .minecraft folders this method could find
191 Notes
192 -----
193 This method does not check to make sure that those .minecraft folders
194 contain valid minecraft instances, just that they exist
195 """
196 return search_path.rglob(".minecraft")
199def links_into_enderchest(
200 minecraft_root: Path, link: Path, check_exists: bool = True
201) -> bool:
202 """Determine whether a symlink's target is inside the EnderChest specified
203 by the Minecraft root.
205 Parameters
206 ----------
207 minecraft_root : Path
208 The root directory that your minecraft stuff (or, at least, the one
209 that's the parent of your EnderChest folder)
210 link : Path
211 The link to check
212 check_exists : bool, optional
213 By default, this method will raise an error if no EnderChest exists
214 at that location (meaning no folder or no enderchest config file in
215 that folder). To disable that check, call this method with
216 `check_exists=False`.
218 Returns
219 -------
220 bool
221 True if the path is inside of the EnderChest folder. False otherwise.
223 Notes
224 -----
225 This method only checks the *direct target* of the link as opposed to the
226 fully resolved path.
228 Raises
229 ------
230 FileNotFoundError
231 If no valid EnderChest installation exists within the given
232 minecraft root (and checking hasn't been disabled)
233 OSError
234 If the link provided isn't actually a symbolic link
235 """
236 chest_folder = os.path.normpath(
237 ender_chest_folder(minecraft_root, check_exists=check_exists)
238 .expanduser()
239 .absolute()
240 )
242 target = os.readlink(link)
243 if not os.path.isabs(target):
244 target = os.path.normpath(link.parent / target)
246 # Windows shenanigans: https://bugs.python.org/issue42957
247 if target.startswith(("\\\\?\\", "\\??\\")): # pragma: no cover
248 try:
249 os.stat(target[4:])
250 target = target[4:]
251 except (OSError, FileNotFoundError):
252 # then maybe this is somehow legit
253 pass
255 # there's probably a better way to check if a file is inside a sub-path
256 try:
257 common_root = os.path.commonpath([target, chest_folder])
258 except ValueError: # if they have no common root
259 common_root = ""
260 return common_root == chest_folder