amulet-core 1.9.19__py3-none-any.whl → 1.9.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of amulet-core might be problematic. Click here for more details.

Files changed (198) hide show
  1. amulet/__init__.py +27 -27
  2. amulet/__pyinstaller/__init__.py +2 -2
  3. amulet/__pyinstaller/hook-amulet.py +4 -4
  4. amulet/_version.py +21 -21
  5. amulet/api/__init__.py +2 -2
  6. amulet/api/abstract_base_entity.py +128 -128
  7. amulet/api/block.py +630 -630
  8. amulet/api/block_entity.py +71 -71
  9. amulet/api/cache.py +107 -107
  10. amulet/api/chunk/__init__.py +6 -6
  11. amulet/api/chunk/biomes.py +207 -207
  12. amulet/api/chunk/block_entity_dict.py +175 -175
  13. amulet/api/chunk/blocks.py +46 -46
  14. amulet/api/chunk/chunk.py +389 -389
  15. amulet/api/chunk/entity_list.py +75 -75
  16. amulet/api/chunk/status.py +167 -167
  17. amulet/api/data_types/__init__.py +4 -4
  18. amulet/api/data_types/generic_types.py +4 -4
  19. amulet/api/data_types/operation_types.py +16 -16
  20. amulet/api/data_types/world_types.py +49 -49
  21. amulet/api/data_types/wrapper_types.py +71 -71
  22. amulet/api/entity.py +74 -74
  23. amulet/api/errors.py +119 -119
  24. amulet/api/history/__init__.py +36 -36
  25. amulet/api/history/base/__init__.py +3 -3
  26. amulet/api/history/base/base_history.py +26 -26
  27. amulet/api/history/base/history_manager.py +63 -63
  28. amulet/api/history/base/revision_manager.py +73 -73
  29. amulet/api/history/changeable.py +15 -15
  30. amulet/api/history/data_types.py +7 -7
  31. amulet/api/history/history_manager/__init__.py +3 -3
  32. amulet/api/history/history_manager/container.py +102 -102
  33. amulet/api/history/history_manager/database.py +279 -279
  34. amulet/api/history/history_manager/meta.py +93 -93
  35. amulet/api/history/history_manager/object.py +116 -116
  36. amulet/api/history/revision_manager/__init__.py +2 -2
  37. amulet/api/history/revision_manager/disk.py +33 -33
  38. amulet/api/history/revision_manager/ram.py +12 -12
  39. amulet/api/item.py +75 -75
  40. amulet/api/level/__init__.py +4 -4
  41. amulet/api/level/base_level/__init__.py +1 -1
  42. amulet/api/level/base_level/base_level.py +1035 -1026
  43. amulet/api/level/base_level/chunk_manager.py +227 -227
  44. amulet/api/level/base_level/clone.py +389 -389
  45. amulet/api/level/base_level/player_manager.py +101 -101
  46. amulet/api/level/immutable_structure/__init__.py +1 -1
  47. amulet/api/level/immutable_structure/immutable_structure.py +94 -94
  48. amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
  49. amulet/api/level/structure.py +22 -22
  50. amulet/api/level/world.py +19 -19
  51. amulet/api/partial_3d_array/__init__.py +2 -2
  52. amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
  53. amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
  54. amulet/api/partial_3d_array/data_types.py +15 -15
  55. amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
  56. amulet/api/partial_3d_array/util.py +152 -152
  57. amulet/api/player.py +65 -65
  58. amulet/api/registry/__init__.py +2 -2
  59. amulet/api/registry/base_registry.py +34 -34
  60. amulet/api/registry/biome_manager.py +153 -153
  61. amulet/api/registry/block_manager.py +156 -156
  62. amulet/api/selection/__init__.py +2 -2
  63. amulet/api/selection/abstract_selection.py +315 -315
  64. amulet/api/selection/box.py +805 -805
  65. amulet/api/selection/group.py +488 -488
  66. amulet/api/structure.py +37 -37
  67. amulet/api/wrapper/__init__.py +8 -8
  68. amulet/api/wrapper/chunk/interface.py +441 -441
  69. amulet/api/wrapper/chunk/translator.py +567 -567
  70. amulet/api/wrapper/format_wrapper.py +772 -772
  71. amulet/api/wrapper/structure_format_wrapper.py +116 -116
  72. amulet/api/wrapper/world_format_wrapper.py +63 -63
  73. amulet/level/__init__.py +1 -1
  74. amulet/level/formats/anvil_forge_world.py +40 -40
  75. amulet/level/formats/anvil_world/__init__.py +3 -3
  76. amulet/level/formats/anvil_world/_sector_manager.py +291 -384
  77. amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
  78. amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
  79. amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
  80. amulet/level/formats/anvil_world/dimension.py +177 -177
  81. amulet/level/formats/anvil_world/format.py +769 -769
  82. amulet/level/formats/anvil_world/region.py +384 -384
  83. amulet/level/formats/construction/__init__.py +3 -3
  84. amulet/level/formats/construction/format_wrapper.py +515 -515
  85. amulet/level/formats/construction/interface.py +134 -134
  86. amulet/level/formats/construction/section.py +60 -60
  87. amulet/level/formats/construction/util.py +165 -165
  88. amulet/level/formats/leveldb_world/__init__.py +3 -3
  89. amulet/level/formats/leveldb_world/chunk.py +33 -33
  90. amulet/level/formats/leveldb_world/dimension.py +385 -419
  91. amulet/level/formats/leveldb_world/format.py +659 -641
  92. amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
  93. amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
  94. amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
  95. amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
  96. amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
  97. amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
  98. amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
  99. amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
  100. amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
  101. amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
  102. amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
  103. amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
  104. amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
  105. amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
  106. amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
  107. amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
  108. amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
  109. amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
  110. amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
  111. amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
  112. amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
  113. amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
  114. amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
  115. amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
  116. amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
  117. amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
  118. amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
  119. amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
  120. amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
  121. amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
  122. amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
  123. amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
  124. amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
  125. amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
  126. amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
  127. amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
  128. amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
  129. amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
  130. amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
  131. amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
  132. amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
  133. amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
  134. amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
  135. amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
  136. amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
  137. amulet/level/formats/mcstructure/__init__.py +3 -3
  138. amulet/level/formats/mcstructure/chunk.py +50 -50
  139. amulet/level/formats/mcstructure/format_wrapper.py +408 -408
  140. amulet/level/formats/mcstructure/interface.py +175 -175
  141. amulet/level/formats/schematic/__init__.py +3 -3
  142. amulet/level/formats/schematic/chunk.py +55 -55
  143. amulet/level/formats/schematic/data_types.py +4 -4
  144. amulet/level/formats/schematic/format_wrapper.py +373 -373
  145. amulet/level/formats/schematic/interface.py +142 -142
  146. amulet/level/formats/sponge_schem/__init__.py +4 -4
  147. amulet/level/formats/sponge_schem/chunk.py +62 -62
  148. amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
  149. amulet/level/formats/sponge_schem/interface.py +118 -118
  150. amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
  151. amulet/level/formats/sponge_schem/varint/varint.py +87 -87
  152. amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
  153. amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
  154. amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
  155. amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
  156. amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
  157. amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
  158. amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
  159. amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
  160. amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
  161. amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
  162. amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
  163. amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
  164. amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
  165. amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
  166. amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
  167. amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
  168. amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
  169. amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
  170. amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
  171. amulet/level/load.py +59 -59
  172. amulet/level/loader.py +95 -95
  173. amulet/level/translators/chunk/bedrock/__init__.py +267 -267
  174. amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
  175. amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
  176. amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
  177. amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
  178. amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
  179. amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
  180. amulet/libs/leveldb/__init__.py +7 -7
  181. amulet/operations/__init__.py +5 -5
  182. amulet/operations/clone.py +18 -18
  183. amulet/operations/delete_chunk.py +32 -32
  184. amulet/operations/fill.py +30 -30
  185. amulet/operations/paste.py +65 -65
  186. amulet/operations/replace.py +58 -58
  187. amulet/utils/__init__.py +14 -14
  188. amulet/utils/format_utils.py +41 -41
  189. amulet/utils/generator.py +15 -15
  190. amulet/utils/matrix.py +243 -243
  191. amulet/utils/numpy_helpers.py +46 -46
  192. amulet/utils/world_utils.py +349 -349
  193. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
  194. amulet_core-1.9.20.dist-info/RECORD +208 -0
  195. amulet_core-1.9.19.dist-info/RECORD +0 -208
  196. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
  197. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
  198. {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/top_level.txt +0 -0
@@ -1,71 +1,71 @@
1
- from __future__ import annotations
2
- import copy
3
- import numpy
4
-
5
- from amulet_nbt import NamedTag
6
- from amulet.api.data_types import BlockCoordinates
7
- from .abstract_base_entity import AbstractBaseEntity
8
-
9
-
10
- class BlockEntity(AbstractBaseEntity):
11
- """
12
- A class to contain all the data to define a BlockEntity.
13
- """
14
-
15
- obj_name = "BlockEntity"
16
- coord_types = (int, numpy.integer)
17
-
18
- def __init__(
19
- self,
20
- namespace: str,
21
- base_name: str,
22
- x: int,
23
- y: int,
24
- z: int,
25
- nbt: NamedTag,
26
- ):
27
- """
28
- Constructs a :class:`BlockEntity` instance.
29
-
30
- :param namespace: The namespace of the block entity eg "minecraft"
31
- :param base_name: The base name of the block entity eg "chest"
32
- :param x: The x coordinate of the block entity
33
- :param y: The y coordinate of the block entity
34
- :param z: The z coordinate of the block entity
35
- :param nbt: The NBT stored with the block entity
36
- """
37
- super().__init__(namespace, base_name, x, y, z, nbt)
38
-
39
- @property
40
- def x(self) -> int:
41
- """The x location of the BlockEntity. Read Only"""
42
- return self._x
43
-
44
- @property
45
- def y(self) -> int:
46
- """The y location of the BlockEntity. Read Only"""
47
- return self._y
48
-
49
- @property
50
- def z(self) -> int:
51
- """The z location of the BlockEntity. Read Only"""
52
- return self._z
53
-
54
- @property
55
- def location(self) -> BlockCoordinates:
56
- """The location of the BlockEntity. Read Only"""
57
- return self._x, self._y, self._z
58
-
59
- def new_at_location(self, x: int, y: int, z: int) -> BlockEntity:
60
- """
61
- Creates a copy of this BlockEntity at a new location
62
- BlockEntities are stored in the chunk based on their location so location cannot be mutable
63
- """
64
- return BlockEntity(
65
- self._namespace,
66
- self._base_name,
67
- x,
68
- y,
69
- z,
70
- copy.deepcopy(self._nbt),
71
- )
1
+ from __future__ import annotations
2
+ import copy
3
+ import numpy
4
+
5
+ from amulet_nbt import NamedTag
6
+ from amulet.api.data_types import BlockCoordinates
7
+ from .abstract_base_entity import AbstractBaseEntity
8
+
9
+
10
+ class BlockEntity(AbstractBaseEntity):
11
+ """
12
+ A class to contain all the data to define a BlockEntity.
13
+ """
14
+
15
+ obj_name = "BlockEntity"
16
+ coord_types = (int, numpy.integer)
17
+
18
+ def __init__(
19
+ self,
20
+ namespace: str,
21
+ base_name: str,
22
+ x: int,
23
+ y: int,
24
+ z: int,
25
+ nbt: NamedTag,
26
+ ):
27
+ """
28
+ Constructs a :class:`BlockEntity` instance.
29
+
30
+ :param namespace: The namespace of the block entity eg "minecraft"
31
+ :param base_name: The base name of the block entity eg "chest"
32
+ :param x: The x coordinate of the block entity
33
+ :param y: The y coordinate of the block entity
34
+ :param z: The z coordinate of the block entity
35
+ :param nbt: The NBT stored with the block entity
36
+ """
37
+ super().__init__(namespace, base_name, x, y, z, nbt)
38
+
39
+ @property
40
+ def x(self) -> int:
41
+ """The x location of the BlockEntity. Read Only"""
42
+ return self._x
43
+
44
+ @property
45
+ def y(self) -> int:
46
+ """The y location of the BlockEntity. Read Only"""
47
+ return self._y
48
+
49
+ @property
50
+ def z(self) -> int:
51
+ """The z location of the BlockEntity. Read Only"""
52
+ return self._z
53
+
54
+ @property
55
+ def location(self) -> BlockCoordinates:
56
+ """The location of the BlockEntity. Read Only"""
57
+ return self._x, self._y, self._z
58
+
59
+ def new_at_location(self, x: int, y: int, z: int) -> BlockEntity:
60
+ """
61
+ Creates a copy of this BlockEntity at a new location
62
+ BlockEntities are stored in the chunk based on their location so location cannot be mutable
63
+ """
64
+ return BlockEntity(
65
+ self._namespace,
66
+ self._base_name,
67
+ x,
68
+ y,
69
+ z,
70
+ copy.deepcopy(self._nbt),
71
+ )
amulet/api/cache.py CHANGED
@@ -1,107 +1,107 @@
1
- # A cache for objects implemented using leveldb for speed.
2
- import os
3
- import shutil
4
- import time
5
- import logging
6
- import glob
7
- import re
8
- import tempfile
9
-
10
- import portalocker
11
-
12
-
13
- def _get_cache_dir() -> str:
14
- return os.environ.get("AMULET_LEVEL_CACHE_DIR", None) or os.path.join(
15
- os.environ.get("CACHE_DIR"), "level_data"
16
- )
17
-
18
-
19
- log = logging.getLogger(__name__)
20
-
21
-
22
- def _clear_legacy_cache():
23
- legacy_cache_dir = _get_cache_dir()
24
- world_temp_dir = os.path.join(legacy_cache_dir, "world_temp")
25
- if not os.path.isdir(world_temp_dir):
26
- return
27
- paths = [
28
- os.path.join(legacy_cache_dir, t)
29
- for t in os.listdir(world_temp_dir)
30
- if t.isnumeric() and int(t) < (time.time() - 7 * 24 * 3600)
31
- ]
32
- if paths:
33
- log.info("Removing legacy cache.")
34
-
35
- # remove all cache directories that were created before a week ago
36
- # Sometimes if the program is stopped or it crashes the cleanup won't happen
37
- for path in paths:
38
- shutil.rmtree(path, ignore_errors=True)
39
-
40
-
41
- _clear_legacy_cache()
42
-
43
-
44
- TempPattern = re.compile(r"amulettmp.*?-(?P<time>\d+)")
45
-
46
-
47
- def _clear_temp_dirs():
48
- """
49
- Try and delete historic temporary directories.
50
- If things went very wrong in past sessions temporary directories may still exist.
51
- """
52
- temp_dir = tempfile.gettempdir()
53
- for path in glob.glob(os.path.join(glob.escape(temp_dir), "amulettmp*")):
54
- name = os.path.relpath(path, temp_dir)
55
- match = TempPattern.fullmatch(name)
56
- if match and int(match.group("time")) < (time.time() - 7 * 24 * 3600):
57
- lock_path = os.path.join(path, "lock")
58
- if os.path.exists(lock_path):
59
- with open(lock_path) as lock:
60
- # make sure it is not locked by another process
61
- try:
62
- portalocker.lock(lock, portalocker.LockFlags.EXCLUSIVE)
63
- except:
64
- continue
65
- else:
66
- portalocker.unlock(lock)
67
- shutil.rmtree(path, ignore_errors=True)
68
-
69
-
70
- _clear_temp_dirs()
71
-
72
-
73
- class TempDir(str):
74
- """
75
- A temporary directory to do with as you wish.
76
-
77
- >>> t = TempDir()
78
- >>> path = os.path.join(t, "your_file.txt") # TempDir is a subclass of str
79
- >>> # make sure all files in the temporary directory are closed before releasing or closing this object.
80
- >>> # The temporary directory will be deleted when the last reference to `t` is lost or when `t.close()` is called
81
- """
82
-
83
- def __new__(cls):
84
- cache_dir = _get_cache_dir()
85
- os.makedirs(cache_dir, exist_ok=True)
86
- self = super().__new__(
87
- cls,
88
- tempfile.mkdtemp(
89
- prefix="amulettmp",
90
- suffix=f"-{time.time():.0f}",
91
- dir=cache_dir,
92
- ),
93
- )
94
- self.__lock = open(os.path.join(self, "lock"), "w")
95
- portalocker.lock(self.__lock, portalocker.LockFlags.EXCLUSIVE)
96
- return self
97
-
98
- def close(self):
99
- """Close the lock and delete the directory."""
100
- if self.__lock is not None:
101
- portalocker.unlock(self.__lock)
102
- self.__lock.close()
103
- self.__lock = None
104
- shutil.rmtree(self)
105
-
106
- def __del__(self):
107
- self.close()
1
+ # A cache for objects implemented using leveldb for speed.
2
+ import os
3
+ import shutil
4
+ import time
5
+ import logging
6
+ import glob
7
+ import re
8
+ import tempfile
9
+
10
+ import portalocker
11
+
12
+
13
+ def _get_cache_dir() -> str:
14
+ return os.environ.get("AMULET_LEVEL_CACHE_DIR", None) or os.path.join(
15
+ os.environ.get("CACHE_DIR"), "level_data"
16
+ )
17
+
18
+
19
+ log = logging.getLogger(__name__)
20
+
21
+
22
+ def _clear_legacy_cache():
23
+ legacy_cache_dir = _get_cache_dir()
24
+ world_temp_dir = os.path.join(legacy_cache_dir, "world_temp")
25
+ if not os.path.isdir(world_temp_dir):
26
+ return
27
+ paths = [
28
+ os.path.join(legacy_cache_dir, t)
29
+ for t in os.listdir(world_temp_dir)
30
+ if t.isnumeric() and int(t) < (time.time() - 7 * 24 * 3600)
31
+ ]
32
+ if paths:
33
+ log.info("Removing legacy cache.")
34
+
35
+ # remove all cache directories that were created before a week ago
36
+ # Sometimes if the program is stopped or it crashes the cleanup won't happen
37
+ for path in paths:
38
+ shutil.rmtree(path, ignore_errors=True)
39
+
40
+
41
+ _clear_legacy_cache()
42
+
43
+
44
+ TempPattern = re.compile(r"amulettmp.*?-(?P<time>\d+)")
45
+
46
+
47
+ def _clear_temp_dirs():
48
+ """
49
+ Try and delete historic temporary directories.
50
+ If things went very wrong in past sessions temporary directories may still exist.
51
+ """
52
+ temp_dir = tempfile.gettempdir()
53
+ for path in glob.glob(os.path.join(glob.escape(temp_dir), "amulettmp*")):
54
+ name = os.path.relpath(path, temp_dir)
55
+ match = TempPattern.fullmatch(name)
56
+ if match and int(match.group("time")) < (time.time() - 7 * 24 * 3600):
57
+ lock_path = os.path.join(path, "lock")
58
+ if os.path.exists(lock_path):
59
+ with open(lock_path) as lock:
60
+ # make sure it is not locked by another process
61
+ try:
62
+ portalocker.lock(lock, portalocker.LockFlags.EXCLUSIVE)
63
+ except:
64
+ continue
65
+ else:
66
+ portalocker.unlock(lock)
67
+ shutil.rmtree(path, ignore_errors=True)
68
+
69
+
70
+ _clear_temp_dirs()
71
+
72
+
73
+ class TempDir(str):
74
+ """
75
+ A temporary directory to do with as you wish.
76
+
77
+ >>> t = TempDir()
78
+ >>> path = os.path.join(t, "your_file.txt") # TempDir is a subclass of str
79
+ >>> # make sure all files in the temporary directory are closed before releasing or closing this object.
80
+ >>> # The temporary directory will be deleted when the last reference to `t` is lost or when `t.close()` is called
81
+ """
82
+
83
+ def __new__(cls):
84
+ cache_dir = _get_cache_dir()
85
+ os.makedirs(cache_dir, exist_ok=True)
86
+ self = super().__new__(
87
+ cls,
88
+ tempfile.mkdtemp(
89
+ prefix="amulettmp",
90
+ suffix=f"-{time.time():.0f}",
91
+ dir=cache_dir,
92
+ ),
93
+ )
94
+ self.__lock = open(os.path.join(self, "lock"), "w")
95
+ portalocker.lock(self.__lock, portalocker.LockFlags.EXCLUSIVE)
96
+ return self
97
+
98
+ def close(self):
99
+ """Close the lock and delete the directory."""
100
+ if self.__lock is not None:
101
+ portalocker.unlock(self.__lock)
102
+ self.__lock.close()
103
+ self.__lock = None
104
+ shutil.rmtree(self)
105
+
106
+ def __del__(self):
107
+ self.close()
@@ -1,6 +1,6 @@
1
- from .blocks import Blocks
2
- from .status import Status, StatusFormats
3
- from .biomes import Biomes, BiomesShape
4
- from .entity_list import EntityList
5
- from .block_entity_dict import BlockEntityDict
6
- from .chunk import Chunk
1
+ from .blocks import Blocks
2
+ from .status import Status, StatusFormats
3
+ from .biomes import Biomes, BiomesShape
4
+ from .entity_list import EntityList
5
+ from .block_entity_dict import BlockEntityDict
6
+ from .chunk import Chunk