Coverage for enderchest/filesystem.py: 94%
33 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-03 20:14 +0000
« 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
6from .loggers import GATHER_LOGGER
8ENDER_CHEST_FOLDER_NAME = "EnderChest"
10ENDER_CHEST_CONFIG_NAME = "enderchest.cfg"
12SHULKER_BOX_CONFIG_NAME = "shulkerbox.cfg"
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
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`.
30 Returns
31 -------
32 Path
33 The path to the EnderChest folder
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
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
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`.
58 Returns
59 -------
60 Path
61 The path to the EnderChest config file
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)
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
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
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
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)
92 Returns
93 -------
94 Path
95 The path to the shulker box folder
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
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
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)
114 Returns
115 -------
116 Path
117 The path to the shulker box folder
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
126def shulker_box_configs(minecraft_root: Path) -> Iterable[Path]:
127 """Find all shulker box configs on the system
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)
135 Returns
136 -------
137 list-like of paths
138 The paths to all the shulker box configs on the system
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}")
149def minecraft_folders(search_path: Path) -> Iterable[Path]:
150 """Find all .minecraft folders within a given search path
152 Parameters
153 ----------
154 search_path : Path
155 The directory to search
157 Returns
158 -------
159 list-like of paths
160 The paths to all the .minecraft folders this method could find
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")
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.
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`.
189 Returns
190 -------
191 bool
192 True if the path is inside of the EnderChest folder. False otherwise.
194 Notes
195 -----
196 This method only checks the *direct target* of the link as opposed to the
197 fully resolved path.
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 )
213 target = os.readlink(link)
214 if not os.path.isabs(target):
215 target = os.path.normpath(link.parent / target)
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
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