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.
- amulet/__init__.py +27 -27
- amulet/__pyinstaller/__init__.py +2 -2
- amulet/__pyinstaller/hook-amulet.py +4 -4
- amulet/_version.py +21 -21
- amulet/api/__init__.py +2 -2
- amulet/api/abstract_base_entity.py +128 -128
- amulet/api/block.py +630 -630
- amulet/api/block_entity.py +71 -71
- amulet/api/cache.py +107 -107
- amulet/api/chunk/__init__.py +6 -6
- amulet/api/chunk/biomes.py +207 -207
- amulet/api/chunk/block_entity_dict.py +175 -175
- amulet/api/chunk/blocks.py +46 -46
- amulet/api/chunk/chunk.py +389 -389
- amulet/api/chunk/entity_list.py +75 -75
- amulet/api/chunk/status.py +167 -167
- amulet/api/data_types/__init__.py +4 -4
- amulet/api/data_types/generic_types.py +4 -4
- amulet/api/data_types/operation_types.py +16 -16
- amulet/api/data_types/world_types.py +49 -49
- amulet/api/data_types/wrapper_types.py +71 -71
- amulet/api/entity.py +74 -74
- amulet/api/errors.py +119 -119
- amulet/api/history/__init__.py +36 -36
- amulet/api/history/base/__init__.py +3 -3
- amulet/api/history/base/base_history.py +26 -26
- amulet/api/history/base/history_manager.py +63 -63
- amulet/api/history/base/revision_manager.py +73 -73
- amulet/api/history/changeable.py +15 -15
- amulet/api/history/data_types.py +7 -7
- amulet/api/history/history_manager/__init__.py +3 -3
- amulet/api/history/history_manager/container.py +102 -102
- amulet/api/history/history_manager/database.py +279 -279
- amulet/api/history/history_manager/meta.py +93 -93
- amulet/api/history/history_manager/object.py +116 -116
- amulet/api/history/revision_manager/__init__.py +2 -2
- amulet/api/history/revision_manager/disk.py +33 -33
- amulet/api/history/revision_manager/ram.py +12 -12
- amulet/api/item.py +75 -75
- amulet/api/level/__init__.py +4 -4
- amulet/api/level/base_level/__init__.py +1 -1
- amulet/api/level/base_level/base_level.py +1035 -1026
- amulet/api/level/base_level/chunk_manager.py +227 -227
- amulet/api/level/base_level/clone.py +389 -389
- amulet/api/level/base_level/player_manager.py +101 -101
- amulet/api/level/immutable_structure/__init__.py +1 -1
- amulet/api/level/immutable_structure/immutable_structure.py +94 -94
- amulet/api/level/immutable_structure/void_format_wrapper.py +117 -117
- amulet/api/level/structure.py +22 -22
- amulet/api/level/world.py +19 -19
- amulet/api/partial_3d_array/__init__.py +2 -2
- amulet/api/partial_3d_array/base_partial_3d_array.py +263 -263
- amulet/api/partial_3d_array/bounded_partial_3d_array.py +528 -528
- amulet/api/partial_3d_array/data_types.py +15 -15
- amulet/api/partial_3d_array/unbounded_partial_3d_array.py +229 -229
- amulet/api/partial_3d_array/util.py +152 -152
- amulet/api/player.py +65 -65
- amulet/api/registry/__init__.py +2 -2
- amulet/api/registry/base_registry.py +34 -34
- amulet/api/registry/biome_manager.py +153 -153
- amulet/api/registry/block_manager.py +156 -156
- amulet/api/selection/__init__.py +2 -2
- amulet/api/selection/abstract_selection.py +315 -315
- amulet/api/selection/box.py +805 -805
- amulet/api/selection/group.py +488 -488
- amulet/api/structure.py +37 -37
- amulet/api/wrapper/__init__.py +8 -8
- amulet/api/wrapper/chunk/interface.py +441 -441
- amulet/api/wrapper/chunk/translator.py +567 -567
- amulet/api/wrapper/format_wrapper.py +772 -772
- amulet/api/wrapper/structure_format_wrapper.py +116 -116
- amulet/api/wrapper/world_format_wrapper.py +63 -63
- amulet/level/__init__.py +1 -1
- amulet/level/formats/anvil_forge_world.py +40 -40
- amulet/level/formats/anvil_world/__init__.py +3 -3
- amulet/level/formats/anvil_world/_sector_manager.py +291 -384
- amulet/level/formats/anvil_world/data_pack/__init__.py +2 -2
- amulet/level/formats/anvil_world/data_pack/data_pack.py +224 -224
- amulet/level/formats/anvil_world/data_pack/data_pack_manager.py +77 -77
- amulet/level/formats/anvil_world/dimension.py +177 -177
- amulet/level/formats/anvil_world/format.py +769 -769
- amulet/level/formats/anvil_world/region.py +384 -384
- amulet/level/formats/construction/__init__.py +3 -3
- amulet/level/formats/construction/format_wrapper.py +515 -515
- amulet/level/formats/construction/interface.py +134 -134
- amulet/level/formats/construction/section.py +60 -60
- amulet/level/formats/construction/util.py +165 -165
- amulet/level/formats/leveldb_world/__init__.py +3 -3
- amulet/level/formats/leveldb_world/chunk.py +33 -33
- amulet/level/formats/leveldb_world/dimension.py +385 -419
- amulet/level/formats/leveldb_world/format.py +659 -641
- amulet/level/formats/leveldb_world/interface/chunk/__init__.py +36 -36
- amulet/level/formats/leveldb_world/interface/chunk/base_leveldb_interface.py +836 -836
- amulet/level/formats/leveldb_world/interface/chunk/generate_interface.py +31 -31
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_0.py +30 -30
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_1.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_10.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_11.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_12.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_13.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_14.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_15.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_16.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_17.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_18.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_19.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_2.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_20.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_21.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_22.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_23.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_24.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_25.py +24 -24
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_26.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_27.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_28.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_29.py +33 -33
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_3.py +57 -57
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_30.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_31.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_32.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_33.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_34.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_35.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_36.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_37.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_38.py +10 -10
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_39.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_4.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_40.py +16 -16
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_5.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_6.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_7.py +12 -12
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_8.py +180 -180
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_9.py +18 -18
- amulet/level/formats/leveldb_world/interface/chunk/leveldb_chunk_versions.py +79 -79
- amulet/level/formats/mcstructure/__init__.py +3 -3
- amulet/level/formats/mcstructure/chunk.py +50 -50
- amulet/level/formats/mcstructure/format_wrapper.py +408 -408
- amulet/level/formats/mcstructure/interface.py +175 -175
- amulet/level/formats/schematic/__init__.py +3 -3
- amulet/level/formats/schematic/chunk.py +55 -55
- amulet/level/formats/schematic/data_types.py +4 -4
- amulet/level/formats/schematic/format_wrapper.py +373 -373
- amulet/level/formats/schematic/interface.py +142 -142
- amulet/level/formats/sponge_schem/__init__.py +4 -4
- amulet/level/formats/sponge_schem/chunk.py +62 -62
- amulet/level/formats/sponge_schem/format_wrapper.py +463 -463
- amulet/level/formats/sponge_schem/interface.py +118 -118
- amulet/level/formats/sponge_schem/varint/__init__.py +1 -1
- amulet/level/formats/sponge_schem/varint/varint.py +87 -87
- amulet/level/interfaces/chunk/anvil/anvil_0.py +72 -72
- amulet/level/interfaces/chunk/anvil/anvil_1444.py +336 -336
- amulet/level/interfaces/chunk/anvil/anvil_1466.py +94 -94
- amulet/level/interfaces/chunk/anvil/anvil_1467.py +37 -37
- amulet/level/interfaces/chunk/anvil/anvil_1484.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1503.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1519.py +34 -34
- amulet/level/interfaces/chunk/anvil/anvil_1901.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1908.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_1912.py +21 -21
- amulet/level/interfaces/chunk/anvil/anvil_1934.py +20 -20
- amulet/level/interfaces/chunk/anvil/anvil_2203.py +69 -69
- amulet/level/interfaces/chunk/anvil/anvil_2529.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2681.py +76 -76
- amulet/level/interfaces/chunk/anvil/anvil_2709.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_2844.py +267 -267
- amulet/level/interfaces/chunk/anvil/anvil_3463.py +19 -19
- amulet/level/interfaces/chunk/anvil/anvil_na.py +607 -607
- amulet/level/interfaces/chunk/anvil/base_anvil_interface.py +326 -326
- amulet/level/load.py +59 -59
- amulet/level/loader.py +95 -95
- amulet/level/translators/chunk/bedrock/__init__.py +267 -267
- amulet/level/translators/chunk/bedrock/bedrock_nbt_blockstate_translator.py +46 -46
- amulet/level/translators/chunk/bedrock/bedrock_numerical_translator.py +39 -39
- amulet/level/translators/chunk/bedrock/bedrock_psudo_numerical_translator.py +37 -37
- amulet/level/translators/chunk/java/java_1_18_translator.py +40 -40
- amulet/level/translators/chunk/java/java_blockstate_translator.py +94 -94
- amulet/level/translators/chunk/java/java_numerical_translator.py +62 -62
- amulet/libs/leveldb/__init__.py +7 -7
- amulet/operations/__init__.py +5 -5
- amulet/operations/clone.py +18 -18
- amulet/operations/delete_chunk.py +32 -32
- amulet/operations/fill.py +30 -30
- amulet/operations/paste.py +65 -65
- amulet/operations/replace.py +58 -58
- amulet/utils/__init__.py +14 -14
- amulet/utils/format_utils.py +41 -41
- amulet/utils/generator.py +15 -15
- amulet/utils/matrix.py +243 -243
- amulet/utils/numpy_helpers.py +46 -46
- amulet/utils/world_utils.py +349 -349
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/METADATA +97 -97
- amulet_core-1.9.20.dist-info/RECORD +208 -0
- amulet_core-1.9.19.dist-info/RECORD +0 -208
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/WHEEL +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/entry_points.txt +0 -0
- {amulet_core-1.9.19.dist-info → amulet_core-1.9.20.dist-info}/top_level.txt +0 -0
amulet/level/load.py
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Union
|
|
4
|
-
import logging
|
|
5
|
-
|
|
6
|
-
from amulet.api.level import World, Structure
|
|
7
|
-
|
|
8
|
-
from amulet.api.wrapper import WorldFormatWrapper, StructureFormatWrapper
|
|
9
|
-
from . import loader
|
|
10
|
-
|
|
11
|
-
log = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def load_level(path: str) -> Union[World, Structure]:
|
|
15
|
-
"""
|
|
16
|
-
Load and return a :class:`World` or :class:`Structure` class exposing the data at ``path``
|
|
17
|
-
|
|
18
|
-
Calls :func:`load_format` to try and find a :class:`FormatWrapper` that can open the data.
|
|
19
|
-
|
|
20
|
-
If one is found it will wrap it with either a :class:`World` or :class:`Structure` class and return it.
|
|
21
|
-
|
|
22
|
-
:param path: The file path to a file or directory for the object to be loaded.
|
|
23
|
-
:return: A World or Structure class instance containing the data.
|
|
24
|
-
:raises:
|
|
25
|
-
LoaderNoneMatched: If no loader could be found that can open the data at path.
|
|
26
|
-
|
|
27
|
-
Exception: Other errors.
|
|
28
|
-
"""
|
|
29
|
-
log.info(f"Loading level {path}")
|
|
30
|
-
format_wrapper = load_format(path)
|
|
31
|
-
if isinstance(format_wrapper, WorldFormatWrapper):
|
|
32
|
-
return World(path, format_wrapper)
|
|
33
|
-
elif isinstance(format_wrapper, StructureFormatWrapper):
|
|
34
|
-
return Structure(path, format_wrapper)
|
|
35
|
-
else:
|
|
36
|
-
raise Exception(
|
|
37
|
-
f"FormatWrapper of type {format_wrapper.__class__.__name__} is not supported. Report this to a developer."
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def load_format(path: str) -> Union[WorldFormatWrapper, StructureFormatWrapper]:
|
|
42
|
-
"""
|
|
43
|
-
Find a valid subclass of :class:`FormatWrapper` and return the data wrapped in the class.
|
|
44
|
-
This exposes a low level API to read and write the world data.
|
|
45
|
-
|
|
46
|
-
Inspects the data at the given path to find a valid subclass of :class:`FormatWrapper`.
|
|
47
|
-
|
|
48
|
-
If a valid wrapper is found it is set up and returned.
|
|
49
|
-
|
|
50
|
-
This is not recommended for new users and does not include a history system.
|
|
51
|
-
|
|
52
|
-
:param path: The file path to a file or directory for the object to be loaded.
|
|
53
|
-
:return: An instance of WorldFormatWrapper or StructureFormatWrapper containing the data at path.
|
|
54
|
-
:raises:
|
|
55
|
-
LoaderNoneMatched: If no loader could be found that can open the data at path.
|
|
56
|
-
|
|
57
|
-
Exception: Other errors.
|
|
58
|
-
"""
|
|
59
|
-
return loader.Formats.get(path)(path)
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Union
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from amulet.api.level import World, Structure
|
|
7
|
+
|
|
8
|
+
from amulet.api.wrapper import WorldFormatWrapper, StructureFormatWrapper
|
|
9
|
+
from . import loader
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_level(path: str) -> Union[World, Structure]:
|
|
15
|
+
"""
|
|
16
|
+
Load and return a :class:`World` or :class:`Structure` class exposing the data at ``path``
|
|
17
|
+
|
|
18
|
+
Calls :func:`load_format` to try and find a :class:`FormatWrapper` that can open the data.
|
|
19
|
+
|
|
20
|
+
If one is found it will wrap it with either a :class:`World` or :class:`Structure` class and return it.
|
|
21
|
+
|
|
22
|
+
:param path: The file path to a file or directory for the object to be loaded.
|
|
23
|
+
:return: A World or Structure class instance containing the data.
|
|
24
|
+
:raises:
|
|
25
|
+
LoaderNoneMatched: If no loader could be found that can open the data at path.
|
|
26
|
+
|
|
27
|
+
Exception: Other errors.
|
|
28
|
+
"""
|
|
29
|
+
log.info(f"Loading level {path}")
|
|
30
|
+
format_wrapper = load_format(path)
|
|
31
|
+
if isinstance(format_wrapper, WorldFormatWrapper):
|
|
32
|
+
return World(path, format_wrapper)
|
|
33
|
+
elif isinstance(format_wrapper, StructureFormatWrapper):
|
|
34
|
+
return Structure(path, format_wrapper)
|
|
35
|
+
else:
|
|
36
|
+
raise Exception(
|
|
37
|
+
f"FormatWrapper of type {format_wrapper.__class__.__name__} is not supported. Report this to a developer."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_format(path: str) -> Union[WorldFormatWrapper, StructureFormatWrapper]:
|
|
42
|
+
"""
|
|
43
|
+
Find a valid subclass of :class:`FormatWrapper` and return the data wrapped in the class.
|
|
44
|
+
This exposes a low level API to read and write the world data.
|
|
45
|
+
|
|
46
|
+
Inspects the data at the given path to find a valid subclass of :class:`FormatWrapper`.
|
|
47
|
+
|
|
48
|
+
If a valid wrapper is found it is set up and returned.
|
|
49
|
+
|
|
50
|
+
This is not recommended for new users and does not include a history system.
|
|
51
|
+
|
|
52
|
+
:param path: The file path to a file or directory for the object to be loaded.
|
|
53
|
+
:return: An instance of WorldFormatWrapper or StructureFormatWrapper containing the data at path.
|
|
54
|
+
:raises:
|
|
55
|
+
LoaderNoneMatched: If no loader could be found that can open the data at path.
|
|
56
|
+
|
|
57
|
+
Exception: Other errors.
|
|
58
|
+
"""
|
|
59
|
+
return loader.Formats.get(path)(path)
|
amulet/level/loader.py
CHANGED
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import importlib
|
|
4
|
-
from typing import AbstractSet, Any, Dict
|
|
5
|
-
import pkgutil
|
|
6
|
-
import logging
|
|
7
|
-
import inspect
|
|
8
|
-
|
|
9
|
-
from amulet.api.errors import LoaderNoneMatched
|
|
10
|
-
from amulet.api.wrapper import FormatWrapper, Interface, Translator
|
|
11
|
-
|
|
12
|
-
log = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
ParentPackage = ".".join(__name__.split(".")[:-1])
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Loader:
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
base_class,
|
|
21
|
-
object_type: str,
|
|
22
|
-
package_name: str,
|
|
23
|
-
create_instance=True,
|
|
24
|
-
):
|
|
25
|
-
self._base_class = base_class
|
|
26
|
-
self._object_type = object_type
|
|
27
|
-
self._objects: Dict[str, Any] = {}
|
|
28
|
-
self._create_instance = create_instance
|
|
29
|
-
self._recursive_find(package_name)
|
|
30
|
-
|
|
31
|
-
def _recursive_find(self, module_name: str):
|
|
32
|
-
module = importlib.import_module(module_name)
|
|
33
|
-
|
|
34
|
-
c = getattr(module, "export", None)
|
|
35
|
-
if inspect.isclass(c) and issubclass(c, self._base_class):
|
|
36
|
-
if self._create_instance:
|
|
37
|
-
self._objects[module_name] = c()
|
|
38
|
-
else:
|
|
39
|
-
self._objects[module_name] = c
|
|
40
|
-
|
|
41
|
-
log.debug(f'Enabled {self._object_type} "{module_name}"')
|
|
42
|
-
|
|
43
|
-
elif hasattr(module, "__path__"):
|
|
44
|
-
for _, sub_module_name, ispkg in pkgutil.iter_modules(
|
|
45
|
-
module.__path__, module.__name__ + "."
|
|
46
|
-
):
|
|
47
|
-
self._recursive_find(sub_module_name)
|
|
48
|
-
|
|
49
|
-
def keys(self) -> AbstractSet[str]:
|
|
50
|
-
"""
|
|
51
|
-
:return: The identifiers of all loaded objects
|
|
52
|
-
"""
|
|
53
|
-
return self._objects.keys()
|
|
54
|
-
|
|
55
|
-
def get(self, identifier: Any) -> Any:
|
|
56
|
-
"""
|
|
57
|
-
Given an ``identifier`` will find a valid class and return it
|
|
58
|
-
|
|
59
|
-
:param identifier: The identifier for the desired loaded object
|
|
60
|
-
:return: The class for the object
|
|
61
|
-
"""
|
|
62
|
-
object_id = self.identify(identifier)
|
|
63
|
-
return self._objects[object_id]
|
|
64
|
-
|
|
65
|
-
def identify(self, identifier: Any) -> str:
|
|
66
|
-
if not self._objects:
|
|
67
|
-
raise Exception(f"No {self._object_type} loaders found.")
|
|
68
|
-
for object_name, obj in self._objects.items():
|
|
69
|
-
if obj.is_valid(identifier):
|
|
70
|
-
return object_name
|
|
71
|
-
|
|
72
|
-
raise LoaderNoneMatched(
|
|
73
|
-
f"Could not find a matching {self._object_type} for {identifier}"
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
def __contains__(self, item: str):
|
|
77
|
-
return item in self._objects
|
|
78
|
-
|
|
79
|
-
def report(self):
|
|
80
|
-
print(f"{self._object_type} objects")
|
|
81
|
-
for obj_name, obj in self._objects.items():
|
|
82
|
-
print(obj_name, obj)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Translators = Loader(Translator, "translator", f"{ParentPackage}.translators")
|
|
86
|
-
Interfaces = Loader(Interface, "interface", f"{ParentPackage}.interfaces")
|
|
87
|
-
Formats = Loader(
|
|
88
|
-
FormatWrapper, "format", f"{ParentPackage}.formats", create_instance=False
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if __name__ == "__main__":
|
|
93
|
-
Formats.report()
|
|
94
|
-
Interfaces.report()
|
|
95
|
-
Translators.report()
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
from typing import AbstractSet, Any, Dict
|
|
5
|
+
import pkgutil
|
|
6
|
+
import logging
|
|
7
|
+
import inspect
|
|
8
|
+
|
|
9
|
+
from amulet.api.errors import LoaderNoneMatched
|
|
10
|
+
from amulet.api.wrapper import FormatWrapper, Interface, Translator
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
ParentPackage = ".".join(__name__.split(".")[:-1])
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Loader:
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
base_class,
|
|
21
|
+
object_type: str,
|
|
22
|
+
package_name: str,
|
|
23
|
+
create_instance=True,
|
|
24
|
+
):
|
|
25
|
+
self._base_class = base_class
|
|
26
|
+
self._object_type = object_type
|
|
27
|
+
self._objects: Dict[str, Any] = {}
|
|
28
|
+
self._create_instance = create_instance
|
|
29
|
+
self._recursive_find(package_name)
|
|
30
|
+
|
|
31
|
+
def _recursive_find(self, module_name: str):
|
|
32
|
+
module = importlib.import_module(module_name)
|
|
33
|
+
|
|
34
|
+
c = getattr(module, "export", None)
|
|
35
|
+
if inspect.isclass(c) and issubclass(c, self._base_class):
|
|
36
|
+
if self._create_instance:
|
|
37
|
+
self._objects[module_name] = c()
|
|
38
|
+
else:
|
|
39
|
+
self._objects[module_name] = c
|
|
40
|
+
|
|
41
|
+
log.debug(f'Enabled {self._object_type} "{module_name}"')
|
|
42
|
+
|
|
43
|
+
elif hasattr(module, "__path__"):
|
|
44
|
+
for _, sub_module_name, ispkg in pkgutil.iter_modules(
|
|
45
|
+
module.__path__, module.__name__ + "."
|
|
46
|
+
):
|
|
47
|
+
self._recursive_find(sub_module_name)
|
|
48
|
+
|
|
49
|
+
def keys(self) -> AbstractSet[str]:
|
|
50
|
+
"""
|
|
51
|
+
:return: The identifiers of all loaded objects
|
|
52
|
+
"""
|
|
53
|
+
return self._objects.keys()
|
|
54
|
+
|
|
55
|
+
def get(self, identifier: Any) -> Any:
|
|
56
|
+
"""
|
|
57
|
+
Given an ``identifier`` will find a valid class and return it
|
|
58
|
+
|
|
59
|
+
:param identifier: The identifier for the desired loaded object
|
|
60
|
+
:return: The class for the object
|
|
61
|
+
"""
|
|
62
|
+
object_id = self.identify(identifier)
|
|
63
|
+
return self._objects[object_id]
|
|
64
|
+
|
|
65
|
+
def identify(self, identifier: Any) -> str:
|
|
66
|
+
if not self._objects:
|
|
67
|
+
raise Exception(f"No {self._object_type} loaders found.")
|
|
68
|
+
for object_name, obj in self._objects.items():
|
|
69
|
+
if obj.is_valid(identifier):
|
|
70
|
+
return object_name
|
|
71
|
+
|
|
72
|
+
raise LoaderNoneMatched(
|
|
73
|
+
f"Could not find a matching {self._object_type} for {identifier}"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def __contains__(self, item: str):
|
|
77
|
+
return item in self._objects
|
|
78
|
+
|
|
79
|
+
def report(self):
|
|
80
|
+
print(f"{self._object_type} objects")
|
|
81
|
+
for obj_name, obj in self._objects.items():
|
|
82
|
+
print(obj_name, obj)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
Translators = Loader(Translator, "translator", f"{ParentPackage}.translators")
|
|
86
|
+
Interfaces = Loader(Interface, "interface", f"{ParentPackage}.interfaces")
|
|
87
|
+
Formats = Loader(
|
|
88
|
+
FormatWrapper, "format", f"{ParentPackage}.formats", create_instance=False
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
Formats.report()
|
|
94
|
+
Interfaces.report()
|
|
95
|
+
Translators.report()
|