amulet-core 2.0a3__cp312-cp312-win_amd64.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__.cp312-win_amd64.pyd +0 -0
- amulet/__init__.pyi +30 -0
- amulet/__pyinstaller/__init__.py +2 -0
- amulet/__pyinstaller/hook-amulet.py +4 -0
- amulet/_init.py +28 -0
- amulet/_version.py +21 -0
- amulet/biome.cpp +36 -0
- amulet/biome.hpp +43 -0
- amulet/biome.pyi +77 -0
- amulet/block.cpp +435 -0
- amulet/block.hpp +119 -0
- amulet/block.pyi +273 -0
- amulet/block_entity.cpp +12 -0
- amulet/block_entity.hpp +56 -0
- amulet/block_entity.pyi +80 -0
- amulet/chunk.cpp +16 -0
- amulet/chunk.hpp +99 -0
- amulet/chunk.pyi +30 -0
- amulet/chunk_/components/biome.py +155 -0
- amulet/chunk_/components/block_entity.py +117 -0
- amulet/chunk_/components/entity.py +64 -0
- amulet/chunk_/components/height_2d.py +16 -0
- amulet/chunk_components.pyi +95 -0
- amulet/collections.pyi +37 -0
- amulet/data_types.py +29 -0
- amulet/entity.py +180 -0
- amulet/errors.py +63 -0
- amulet/game/__init__.py +7 -0
- amulet/game/_game.py +152 -0
- amulet/game/_universal/__init__.py +1 -0
- amulet/game/_universal/_biome.py +17 -0
- amulet/game/_universal/_block.py +47 -0
- amulet/game/_universal/_version.py +68 -0
- amulet/game/abc/__init__.py +22 -0
- amulet/game/abc/_block_specification.py +150 -0
- amulet/game/abc/biome.py +213 -0
- amulet/game/abc/block.py +331 -0
- amulet/game/abc/game_version_container.py +25 -0
- amulet/game/abc/json_interface.py +27 -0
- amulet/game/abc/version.py +44 -0
- amulet/game/bedrock/__init__.py +1 -0
- amulet/game/bedrock/_biome.py +35 -0
- amulet/game/bedrock/_block.py +42 -0
- amulet/game/bedrock/_version.py +165 -0
- amulet/game/java/__init__.py +2 -0
- amulet/game/java/_biome.py +35 -0
- amulet/game/java/_block.py +60 -0
- amulet/game/java/_version.py +176 -0
- amulet/game/translate/__init__.py +12 -0
- amulet/game/translate/_functions/__init__.py +15 -0
- amulet/game/translate/_functions/_code_functions/__init__.py +0 -0
- amulet/game/translate/_functions/_code_functions/_text.py +553 -0
- amulet/game/translate/_functions/_code_functions/banner_pattern.py +67 -0
- amulet/game/translate/_functions/_code_functions/bedrock_chest_connection.py +152 -0
- amulet/game/translate/_functions/_code_functions/bedrock_moving_block_pos.py +88 -0
- amulet/game/translate/_functions/_code_functions/bedrock_sign.py +152 -0
- amulet/game/translate/_functions/_code_functions/bedrock_skull_rotation.py +16 -0
- amulet/game/translate/_functions/_code_functions/custom_name.py +146 -0
- amulet/game/translate/_functions/_frozen.py +66 -0
- amulet/game/translate/_functions/_state.py +54 -0
- amulet/game/translate/_functions/_typing.py +98 -0
- amulet/game/translate/_functions/abc.py +116 -0
- amulet/game/translate/_functions/carry_nbt.py +160 -0
- amulet/game/translate/_functions/carry_properties.py +80 -0
- amulet/game/translate/_functions/code.py +143 -0
- amulet/game/translate/_functions/map_block_name.py +66 -0
- amulet/game/translate/_functions/map_nbt.py +111 -0
- amulet/game/translate/_functions/map_properties.py +93 -0
- amulet/game/translate/_functions/multiblock.py +112 -0
- amulet/game/translate/_functions/new_block.py +42 -0
- amulet/game/translate/_functions/new_entity.py +43 -0
- amulet/game/translate/_functions/new_nbt.py +206 -0
- amulet/game/translate/_functions/new_properties.py +64 -0
- amulet/game/translate/_functions/sequence.py +51 -0
- amulet/game/translate/_functions/walk_input_nbt.py +331 -0
- amulet/game/translate/_translator.py +433 -0
- amulet/item.py +75 -0
- amulet/level/__init__.pyi +27 -0
- amulet/level/_load.py +100 -0
- amulet/level/abc/__init__.py +12 -0
- amulet/level/abc/_chunk_handle.py +335 -0
- amulet/level/abc/_dimension.py +86 -0
- amulet/level/abc/_history/__init__.py +1 -0
- amulet/level/abc/_history/_cache.py +224 -0
- amulet/level/abc/_history/_history_manager.py +291 -0
- amulet/level/abc/_level/__init__.py +5 -0
- amulet/level/abc/_level/_compactable_level.py +10 -0
- amulet/level/abc/_level/_creatable_level.py +29 -0
- amulet/level/abc/_level/_disk_level.py +17 -0
- amulet/level/abc/_level/_level.py +453 -0
- amulet/level/abc/_level/_loadable_level.py +42 -0
- amulet/level/abc/_player_storage.py +7 -0
- amulet/level/abc/_raw_level.py +187 -0
- amulet/level/abc/_registry.py +40 -0
- amulet/level/bedrock/__init__.py +2 -0
- amulet/level/bedrock/_chunk_handle.py +19 -0
- amulet/level/bedrock/_dimension.py +22 -0
- amulet/level/bedrock/_level.py +187 -0
- amulet/level/bedrock/_raw/__init__.py +5 -0
- amulet/level/bedrock/_raw/_actor_counter.py +53 -0
- amulet/level/bedrock/_raw/_chunk.py +54 -0
- amulet/level/bedrock/_raw/_chunk_decode.py +668 -0
- amulet/level/bedrock/_raw/_chunk_encode.py +602 -0
- amulet/level/bedrock/_raw/_constant.py +9 -0
- amulet/level/bedrock/_raw/_dimension.py +343 -0
- amulet/level/bedrock/_raw/_level.py +463 -0
- amulet/level/bedrock/_raw/_level_dat.py +90 -0
- amulet/level/bedrock/_raw/_typing.py +6 -0
- amulet/level/bedrock/_raw/leveldb_chunk_versions.py +83 -0
- amulet/level/bedrock/chunk/__init__.py +1 -0
- amulet/level/bedrock/chunk/_chunk.py +126 -0
- amulet/level/bedrock/chunk/components/__init__.py +0 -0
- amulet/level/bedrock/chunk/components/chunk_version.py +12 -0
- amulet/level/bedrock/chunk/components/finalised_state.py +13 -0
- amulet/level/bedrock/chunk/components/raw_chunk.py +15 -0
- amulet/level/construction/__init__.py +0 -0
- amulet/level/java/__init__.pyi +21 -0
- amulet/level/java/_chunk_handle.py +17 -0
- amulet/level/java/_chunk_handle.pyi +15 -0
- amulet/level/java/_dimension.py +20 -0
- amulet/level/java/_dimension.pyi +13 -0
- amulet/level/java/_level.py +184 -0
- amulet/level/java/_level.pyi +120 -0
- amulet/level/java/_raw/__init__.pyi +19 -0
- amulet/level/java/_raw/_chunk.pyi +23 -0
- amulet/level/java/_raw/_chunk_decode.py +561 -0
- amulet/level/java/_raw/_chunk_encode.py +463 -0
- amulet/level/java/_raw/_constant.py +9 -0
- amulet/level/java/_raw/_constant.pyi +20 -0
- amulet/level/java/_raw/_data_pack/__init__.py +2 -0
- amulet/level/java/_raw/_data_pack/__init__.pyi +8 -0
- amulet/level/java/_raw/_data_pack/data_pack.py +241 -0
- amulet/level/java/_raw/_data_pack/data_pack.pyi +197 -0
- amulet/level/java/_raw/_data_pack/data_pack_manager.py +77 -0
- amulet/level/java/_raw/_data_pack/data_pack_manager.pyi +75 -0
- amulet/level/java/_raw/_dimension.py +86 -0
- amulet/level/java/_raw/_dimension.pyi +72 -0
- amulet/level/java/_raw/_level.py +507 -0
- amulet/level/java/_raw/_level.pyi +238 -0
- amulet/level/java/_raw/_typing.py +3 -0
- amulet/level/java/_raw/_typing.pyi +5 -0
- amulet/level/java/anvil/__init__.py +2 -0
- amulet/level/java/anvil/__init__.pyi +11 -0
- amulet/level/java/anvil/_dimension.py +170 -0
- amulet/level/java/anvil/_dimension.pyi +109 -0
- amulet/level/java/anvil/_region.py +421 -0
- amulet/level/java/anvil/_region.pyi +197 -0
- amulet/level/java/anvil/_sector_manager.py +223 -0
- amulet/level/java/anvil/_sector_manager.pyi +142 -0
- amulet/level/java/chunk.pyi +81 -0
- amulet/level/java/chunk_/_chunk.py +260 -0
- amulet/level/java/chunk_/components/inhabited_time.py +12 -0
- amulet/level/java/chunk_/components/last_update.py +12 -0
- amulet/level/java/chunk_/components/legacy_version.py +12 -0
- amulet/level/java/chunk_/components/light_populated.py +12 -0
- amulet/level/java/chunk_/components/named_height_2d.py +37 -0
- amulet/level/java/chunk_/components/status.py +11 -0
- amulet/level/java/chunk_/components/terrain_populated.py +12 -0
- amulet/level/java/chunk_components.pyi +22 -0
- amulet/level/java/long_array.pyi +38 -0
- amulet/level/java_forge/__init__.py +0 -0
- amulet/level/mcstructure/__init__.py +0 -0
- amulet/level/nbt/__init__.py +0 -0
- amulet/level/schematic/__init__.py +0 -0
- amulet/level/sponge_schematic/__init__.py +0 -0
- amulet/level/temporary_level/__init__.py +1 -0
- amulet/level/temporary_level/_level.py +16 -0
- amulet/palette/__init__.pyi +8 -0
- amulet/palette/biome_palette.pyi +45 -0
- amulet/palette/block_palette.pyi +45 -0
- amulet/player.py +64 -0
- amulet/py.typed +0 -0
- amulet/selection/__init__.py +2 -0
- amulet/selection/abstract_selection.py +342 -0
- amulet/selection/box.py +852 -0
- amulet/selection/group.py +481 -0
- amulet/utils/__init__.pyi +28 -0
- amulet/utils/call_spec/__init__.py +24 -0
- amulet/utils/call_spec/__init__.pyi +53 -0
- amulet/utils/call_spec/_call_spec.py +262 -0
- amulet/utils/call_spec/_call_spec.pyi +272 -0
- amulet/utils/format_utils.py +41 -0
- amulet/utils/generator.py +18 -0
- amulet/utils/matrix.py +243 -0
- amulet/utils/matrix.pyi +177 -0
- amulet/utils/numpy.pyi +11 -0
- amulet/utils/numpy_helpers.py +19 -0
- amulet/utils/shareable_lock.py +335 -0
- amulet/utils/shareable_lock.pyi +190 -0
- amulet/utils/signal/__init__.py +10 -0
- amulet/utils/signal/__init__.pyi +25 -0
- amulet/utils/signal/_signal.py +228 -0
- amulet/utils/signal/_signal.pyi +84 -0
- amulet/utils/task_manager.py +235 -0
- amulet/utils/task_manager.pyi +168 -0
- amulet/utils/typed_property.py +111 -0
- amulet/utils/typing.py +4 -0
- amulet/utils/typing.pyi +6 -0
- amulet/utils/weakref.py +70 -0
- amulet/utils/weakref.pyi +50 -0
- amulet/utils/world_utils.py +102 -0
- amulet/utils/world_utils.pyi +109 -0
- amulet/version.cpp +136 -0
- amulet/version.hpp +142 -0
- amulet/version.pyi +94 -0
- amulet_core-2.0a3.dist-info/METADATA +103 -0
- amulet_core-2.0a3.dist-info/RECORD +210 -0
- amulet_core-2.0a3.dist-info/WHEEL +5 -0
- amulet_core-2.0a3.dist-info/entry_points.txt +2 -0
- amulet_core-2.0a3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
from typing import Callable, Iterator
|
|
2
|
+
from threading import Lock, Condition, get_ident
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
import time
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from .task_manager import AbstractCancelManager, VoidCancelManager, TaskCancelled
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LockNotAcquired(TaskCancelled):
|
|
14
|
+
"""An exception raised if the lock was not acquired."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ShareableRLock:
|
|
18
|
+
"""
|
|
19
|
+
This is a custom lock implementation that can be acquired in
|
|
20
|
+
1) unique mode.
|
|
21
|
+
- This is the normal mode where only this thread can use the resource.
|
|
22
|
+
- All other acquires block until this releases it.
|
|
23
|
+
2) shared mode.
|
|
24
|
+
- This allows multiple threads to acquire the resource at the same time.
|
|
25
|
+
- This is useful if multiple threads want to read a resource but not write to it.
|
|
26
|
+
- If the resource is locked in unique mode this will block
|
|
27
|
+
- Once locked in shared mode it
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
_state_lock: Lock
|
|
32
|
+
_state_condition: Condition
|
|
33
|
+
_blocking_threads: dict[
|
|
34
|
+
int, tuple[bool, bool]
|
|
35
|
+
] # dict[thread_id, tuple[is_serial, is_blocking]]
|
|
36
|
+
_unique_thread: int | None
|
|
37
|
+
_unique_r_count: int
|
|
38
|
+
_shared_threads: dict[
|
|
39
|
+
int, int
|
|
40
|
+
] # Map from thread id to the number of times it has been acquired in shared mode.
|
|
41
|
+
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
self._state_lock = Lock()
|
|
44
|
+
self._state_condition = Condition(self._state_lock)
|
|
45
|
+
self._blocking_threads = {}
|
|
46
|
+
self._unique_thread = None
|
|
47
|
+
self._unique_r_count = 0
|
|
48
|
+
self._shared_threads = {}
|
|
49
|
+
|
|
50
|
+
def _wait(
|
|
51
|
+
self,
|
|
52
|
+
exit_condition: Callable[[], bool],
|
|
53
|
+
blocking: bool = True,
|
|
54
|
+
timeout: float = -1,
|
|
55
|
+
task_manager: AbstractCancelManager = VoidCancelManager(),
|
|
56
|
+
) -> bool:
|
|
57
|
+
"""Wait until a condition is False.
|
|
58
|
+
|
|
59
|
+
:param exit_condition: The condition to check.
|
|
60
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
61
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
62
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
63
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
64
|
+
This effectively manually triggers timeout.
|
|
65
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
66
|
+
:return: True if the lock was acquired, otherwise False.
|
|
67
|
+
"""
|
|
68
|
+
if task_manager.is_cancel_requested():
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
if exit_condition():
|
|
72
|
+
# We don't need to wait
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
if not blocking:
|
|
76
|
+
# Need to wait but told not to
|
|
77
|
+
return False
|
|
78
|
+
|
|
79
|
+
if timeout == -1:
|
|
80
|
+
# wait with no timeout
|
|
81
|
+
while not exit_condition():
|
|
82
|
+
if task_manager.is_cancel_requested():
|
|
83
|
+
return False
|
|
84
|
+
self._state_condition.wait()
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
# Wait with a timeout
|
|
89
|
+
end_time = time.time() + timeout
|
|
90
|
+
while not exit_condition():
|
|
91
|
+
if task_manager.is_cancel_requested():
|
|
92
|
+
return False
|
|
93
|
+
wait_time = end_time - time.time()
|
|
94
|
+
if wait_time > 0:
|
|
95
|
+
self._state_condition.wait(wait_time)
|
|
96
|
+
else:
|
|
97
|
+
return False
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
def acquire_unique(
|
|
101
|
+
self,
|
|
102
|
+
blocking: bool = True,
|
|
103
|
+
timeout: float = -1,
|
|
104
|
+
task_manager: AbstractCancelManager = VoidCancelManager(),
|
|
105
|
+
) -> bool:
|
|
106
|
+
"""
|
|
107
|
+
Only use this if you know what you are doing. Consider using :meth:`unique` instead
|
|
108
|
+
Acquire the lock in unique mode. This is equivalent to threading.RLock.acquire
|
|
109
|
+
With improper use this can lead to a deadlock.
|
|
110
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
111
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
112
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
113
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
114
|
+
This effectively manually triggers timeout.
|
|
115
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
116
|
+
:return: True if the lock was acquired otherwise False.
|
|
117
|
+
"""
|
|
118
|
+
with self._state_lock:
|
|
119
|
+
ident = get_ident()
|
|
120
|
+
if (
|
|
121
|
+
ident in self._shared_threads
|
|
122
|
+
and blocking
|
|
123
|
+
and timeout == -1
|
|
124
|
+
and any(
|
|
125
|
+
self._blocking_threads[thread_id][1]
|
|
126
|
+
for thread_id in self._blocking_threads
|
|
127
|
+
if thread_id in self._shared_threads
|
|
128
|
+
)
|
|
129
|
+
):
|
|
130
|
+
logging.warning("Possible deadlock")
|
|
131
|
+
|
|
132
|
+
def on_cancel() -> None:
|
|
133
|
+
with self._state_lock:
|
|
134
|
+
self._state_condition.notify_all()
|
|
135
|
+
|
|
136
|
+
task_manager.register_on_cancel(on_cancel)
|
|
137
|
+
|
|
138
|
+
self._blocking_threads[ident] = (
|
|
139
|
+
True,
|
|
140
|
+
blocking
|
|
141
|
+
and timeout == -1
|
|
142
|
+
and isinstance(task_manager, VoidCancelManager),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def exit_condition() -> bool:
|
|
146
|
+
# The thread was the first to join
|
|
147
|
+
# The lock is not locked or is locked by this thread
|
|
148
|
+
can_exit = (
|
|
149
|
+
next(iter(self._blocking_threads)) == ident
|
|
150
|
+
and self._unique_thread in (None, ident)
|
|
151
|
+
and self._shared_threads.keys() in (set(), {ident})
|
|
152
|
+
)
|
|
153
|
+
if can_exit:
|
|
154
|
+
self._blocking_threads.pop(ident)
|
|
155
|
+
return can_exit
|
|
156
|
+
|
|
157
|
+
locked = self._wait(
|
|
158
|
+
exit_condition,
|
|
159
|
+
blocking,
|
|
160
|
+
timeout,
|
|
161
|
+
task_manager,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
task_manager.unregister_on_cancel(on_cancel)
|
|
165
|
+
|
|
166
|
+
if locked:
|
|
167
|
+
# Lock it
|
|
168
|
+
self._unique_thread = ident
|
|
169
|
+
self._unique_r_count += 1
|
|
170
|
+
return True
|
|
171
|
+
else:
|
|
172
|
+
self._blocking_threads.pop(ident)
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
def release_unique(self) -> None:
|
|
176
|
+
"""
|
|
177
|
+
Only use this if you know what you are doing. Consider using :meth:`unique` instead
|
|
178
|
+
Release the unique hold on the lock. This must be called by the same thread that acquired it.
|
|
179
|
+
This must be called exactly the same number of times as :meth:`acquire_unique` was called.
|
|
180
|
+
"""
|
|
181
|
+
with self._state_lock:
|
|
182
|
+
if self._unique_thread != get_ident():
|
|
183
|
+
raise RuntimeError("Lock released by a thread that does not own it.")
|
|
184
|
+
|
|
185
|
+
self._unique_r_count -= 1
|
|
186
|
+
if self._unique_r_count == 0:
|
|
187
|
+
self._unique_thread = None
|
|
188
|
+
# Let another thread continue
|
|
189
|
+
self._state_condition.notify_all()
|
|
190
|
+
|
|
191
|
+
def acquire_shared(
|
|
192
|
+
self,
|
|
193
|
+
blocking: bool = True,
|
|
194
|
+
timeout: float = -1,
|
|
195
|
+
task_manager: AbstractCancelManager = VoidCancelManager(),
|
|
196
|
+
) -> bool:
|
|
197
|
+
"""
|
|
198
|
+
Only use this if you know what you are doing. Consider using :meth:`shared` instead
|
|
199
|
+
Acquire the lock in shared mode.
|
|
200
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
201
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
202
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
203
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
204
|
+
This effectively manually triggers timeout.
|
|
205
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
206
|
+
:return: True if the lock was acquired otherwise False.
|
|
207
|
+
"""
|
|
208
|
+
with self._state_lock:
|
|
209
|
+
ident = get_ident()
|
|
210
|
+
|
|
211
|
+
def exit_condition() -> bool:
|
|
212
|
+
if self._unique_thread == ident or ident in self._shared_threads:
|
|
213
|
+
# Lock is already acquired by this thread
|
|
214
|
+
self._blocking_threads.pop(ident)
|
|
215
|
+
return True
|
|
216
|
+
elif self._unique_thread is None:
|
|
217
|
+
# Is it not locked in unique mode
|
|
218
|
+
for thread_id, (is_serial, _) in self._blocking_threads.items():
|
|
219
|
+
if is_serial:
|
|
220
|
+
return False
|
|
221
|
+
elif thread_id == ident:
|
|
222
|
+
self._blocking_threads.pop(ident)
|
|
223
|
+
return True
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
def on_cancel() -> None:
|
|
227
|
+
with self._state_lock:
|
|
228
|
+
self._state_condition.notify_all()
|
|
229
|
+
|
|
230
|
+
task_manager.register_on_cancel(on_cancel)
|
|
231
|
+
|
|
232
|
+
self._blocking_threads[ident] = (False, False)
|
|
233
|
+
|
|
234
|
+
locked = self._wait(exit_condition, blocking, timeout, task_manager)
|
|
235
|
+
|
|
236
|
+
task_manager.unregister_on_cancel(on_cancel)
|
|
237
|
+
|
|
238
|
+
if locked:
|
|
239
|
+
# Not unique locked or unique locked by the current thread
|
|
240
|
+
self._shared_threads.setdefault(ident, 0)
|
|
241
|
+
self._shared_threads[ident] += 1
|
|
242
|
+
return True
|
|
243
|
+
else:
|
|
244
|
+
self._blocking_threads.pop(ident)
|
|
245
|
+
return False
|
|
246
|
+
|
|
247
|
+
def release_shared(self) -> None:
|
|
248
|
+
"""
|
|
249
|
+
Only use this if you know what you are doing. Consider using :meth:`shared` instead
|
|
250
|
+
Release the shared hold on the lock. This must be called by the same thread that acquired it.
|
|
251
|
+
This must be called exactly the same number of times as :meth:`acquire_shared` was called.
|
|
252
|
+
"""
|
|
253
|
+
with self._state_lock:
|
|
254
|
+
ident = get_ident()
|
|
255
|
+
if ident not in self._shared_threads:
|
|
256
|
+
raise RuntimeError("Lock released by a thread that does not own it.")
|
|
257
|
+
|
|
258
|
+
self._shared_threads[ident] -= 1
|
|
259
|
+
if self._shared_threads[ident] == 0:
|
|
260
|
+
self._shared_threads.pop(ident)
|
|
261
|
+
self._state_condition.notify_all()
|
|
262
|
+
|
|
263
|
+
@contextmanager
|
|
264
|
+
def unique(
|
|
265
|
+
self,
|
|
266
|
+
blocking: bool = True,
|
|
267
|
+
timeout: float = -1,
|
|
268
|
+
task_manager: AbstractCancelManager = VoidCancelManager(),
|
|
269
|
+
) -> Iterator[None]:
|
|
270
|
+
"""
|
|
271
|
+
Acquire the lock in unique mode.
|
|
272
|
+
This is used as follows
|
|
273
|
+
|
|
274
|
+
>>> lock: ShareableRLock
|
|
275
|
+
>>> with lock.unique():
|
|
276
|
+
>>> # code with lock acquired
|
|
277
|
+
>>> # the lock will automatically be released here
|
|
278
|
+
|
|
279
|
+
This will block while all other threads using the resource finish
|
|
280
|
+
and once acquired block all other threads until the lock is released.
|
|
281
|
+
|
|
282
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
283
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
284
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
285
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
286
|
+
This effectively manually triggers timeout.
|
|
287
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
288
|
+
:return: None
|
|
289
|
+
:raises: LockNotAcquired if the lock could not be acquired.
|
|
290
|
+
"""
|
|
291
|
+
if not self.acquire_unique(blocking, timeout, task_manager):
|
|
292
|
+
raise LockNotAcquired
|
|
293
|
+
try:
|
|
294
|
+
yield
|
|
295
|
+
finally:
|
|
296
|
+
self.release_unique()
|
|
297
|
+
|
|
298
|
+
@contextmanager
|
|
299
|
+
def shared(
|
|
300
|
+
self,
|
|
301
|
+
blocking: bool = True,
|
|
302
|
+
timeout: float = -1,
|
|
303
|
+
task_manager: AbstractCancelManager = VoidCancelManager(),
|
|
304
|
+
) -> Iterator[None]:
|
|
305
|
+
"""
|
|
306
|
+
Acquire the lock in shared mode.
|
|
307
|
+
This is used as follows
|
|
308
|
+
|
|
309
|
+
>>> lock: ShareableRLock
|
|
310
|
+
>>> with lock.shared():
|
|
311
|
+
>>> # code with lock acquired
|
|
312
|
+
>>> # the lock will automatically be released here
|
|
313
|
+
|
|
314
|
+
If the lock is acquired by a different thread in unique mode then this will block until it is finished.
|
|
315
|
+
If the lock is acquired in unique mode by this thread or by otherthreads in shared mode then this will acquire
|
|
316
|
+
the lock.
|
|
317
|
+
|
|
318
|
+
If another thread wants to acquire the lock in unique mode it will block until all threads have finished in
|
|
319
|
+
shared mode.
|
|
320
|
+
|
|
321
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
322
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
323
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
324
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
325
|
+
This effectively manually triggers timeout.
|
|
326
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
327
|
+
:return: None
|
|
328
|
+
:raises: LockNotAcquired if the lock could not be acquired.
|
|
329
|
+
"""
|
|
330
|
+
if not self.acquire_shared(blocking, timeout, task_manager):
|
|
331
|
+
raise LockNotAcquired
|
|
332
|
+
try:
|
|
333
|
+
yield
|
|
334
|
+
finally:
|
|
335
|
+
self.release_shared()
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging as logging
|
|
4
|
+
import time as time
|
|
5
|
+
from _thread import allocate_lock as Lock
|
|
6
|
+
from _thread import get_ident
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from threading import Condition
|
|
9
|
+
|
|
10
|
+
import amulet.utils.task_manager
|
|
11
|
+
from amulet.utils.task_manager import (
|
|
12
|
+
AbstractCancelManager,
|
|
13
|
+
TaskCancelled,
|
|
14
|
+
VoidCancelManager,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"AbstractCancelManager",
|
|
19
|
+
"Condition",
|
|
20
|
+
"Lock",
|
|
21
|
+
"LockNotAcquired",
|
|
22
|
+
"ShareableRLock",
|
|
23
|
+
"TaskCancelled",
|
|
24
|
+
"VoidCancelManager",
|
|
25
|
+
"contextmanager",
|
|
26
|
+
"get_ident",
|
|
27
|
+
"log",
|
|
28
|
+
"logging",
|
|
29
|
+
"time",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
class LockNotAcquired(amulet.utils.task_manager.TaskCancelled):
|
|
33
|
+
"""
|
|
34
|
+
An exception raised if the lock was not acquired.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
class ShareableRLock:
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
This is a custom lock implementation that can be acquired in
|
|
41
|
+
1) unique mode.
|
|
42
|
+
- This is the normal mode where only this thread can use the resource.
|
|
43
|
+
- All other acquires block until this releases it.
|
|
44
|
+
2) shared mode.
|
|
45
|
+
- This allows multiple threads to acquire the resource at the same time.
|
|
46
|
+
- This is useful if multiple threads want to read a resource but not write to it.
|
|
47
|
+
- If the resource is locked in unique mode this will block
|
|
48
|
+
- Once locked in shared mode it
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def shared(*args, **kwds) -> typing.Iterator[NoneType]:
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
Acquire the lock in shared mode.
|
|
58
|
+
This is used as follows
|
|
59
|
+
|
|
60
|
+
>>> lock: ShareableRLock
|
|
61
|
+
>>> with lock.shared():
|
|
62
|
+
>>> # code with lock acquired
|
|
63
|
+
>>> # the lock will automatically be released here
|
|
64
|
+
|
|
65
|
+
If the lock is acquired by a different thread in unique mode then this will block until it is finished.
|
|
66
|
+
If the lock is acquired in unique mode by this thread or by otherthreads in shared mode then this will acquire
|
|
67
|
+
the lock.
|
|
68
|
+
|
|
69
|
+
If another thread wants to acquire the lock in unique mode it will block until all threads have finished in
|
|
70
|
+
shared mode.
|
|
71
|
+
|
|
72
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
73
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
74
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
75
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
76
|
+
This effectively manually triggers timeout.
|
|
77
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
78
|
+
:return: None
|
|
79
|
+
:raises: LockNotAcquired if the lock could not be acquired.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def unique(*args, **kwds) -> typing.Iterator[NoneType]:
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
Acquire the lock in unique mode.
|
|
88
|
+
This is used as follows
|
|
89
|
+
|
|
90
|
+
>>> lock: ShareableRLock
|
|
91
|
+
>>> with lock.unique():
|
|
92
|
+
>>> # code with lock acquired
|
|
93
|
+
>>> # the lock will automatically be released here
|
|
94
|
+
|
|
95
|
+
This will block while all other threads using the resource finish
|
|
96
|
+
and once acquired block all other threads until the lock is released.
|
|
97
|
+
|
|
98
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
99
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
100
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
101
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
102
|
+
This effectively manually triggers timeout.
|
|
103
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
104
|
+
:return: None
|
|
105
|
+
:raises: LockNotAcquired if the lock could not be acquired.
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def __init__(self) -> None: ...
|
|
110
|
+
def _wait(
|
|
111
|
+
self,
|
|
112
|
+
exit_condition: typing.Callable[[], bool],
|
|
113
|
+
blocking: bool = True,
|
|
114
|
+
timeout: float = -1,
|
|
115
|
+
task_manager: amulet.utils.task_manager.AbstractCancelManager = ...,
|
|
116
|
+
) -> bool:
|
|
117
|
+
"""
|
|
118
|
+
Wait until a condition is False.
|
|
119
|
+
|
|
120
|
+
:param exit_condition: The condition to check.
|
|
121
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
122
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
123
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
124
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
125
|
+
This effectively manually triggers timeout.
|
|
126
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
127
|
+
:return: True if the lock was acquired, otherwise False.
|
|
128
|
+
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
def acquire_shared(
|
|
132
|
+
self,
|
|
133
|
+
blocking: bool = True,
|
|
134
|
+
timeout: float = -1,
|
|
135
|
+
task_manager: amulet.utils.task_manager.AbstractCancelManager = ...,
|
|
136
|
+
) -> bool:
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
Only use this if you know what you are doing. Consider using :meth:`shared` instead
|
|
140
|
+
Acquire the lock in shared mode.
|
|
141
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
142
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
143
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
144
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
145
|
+
This effectively manually triggers timeout.
|
|
146
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
147
|
+
:return: True if the lock was acquired otherwise False.
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
def acquire_unique(
|
|
152
|
+
self,
|
|
153
|
+
blocking: bool = True,
|
|
154
|
+
timeout: float = -1,
|
|
155
|
+
task_manager: amulet.utils.task_manager.AbstractCancelManager = ...,
|
|
156
|
+
) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
Only use this if you know what you are doing. Consider using :meth:`unique` instead
|
|
160
|
+
Acquire the lock in unique mode. This is equivalent to threading.RLock.acquire
|
|
161
|
+
With improper use this can lead to a deadlock.
|
|
162
|
+
:param blocking: Should this block until the lock can be acquired. Default is True.
|
|
163
|
+
If false and the lock cannot be acquired on the first try, this returns False.
|
|
164
|
+
:param timeout: Maximum amount of time to block for. Has no effect is blocking is False. Default is forever.
|
|
165
|
+
:param task_manager: A custom object through which acquiring can be cancelled.
|
|
166
|
+
This effectively manually triggers timeout.
|
|
167
|
+
This is useful for GUIs so that the user can cancel an operation that may otherwise block for a while.
|
|
168
|
+
:return: True if the lock was acquired otherwise False.
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
def release_shared(self) -> None:
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
Only use this if you know what you are doing. Consider using :meth:`shared` instead
|
|
176
|
+
Release the shared hold on the lock. This must be called by the same thread that acquired it.
|
|
177
|
+
This must be called exactly the same number of times as :meth:`acquire_shared` was called.
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
def release_unique(self) -> None:
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
Only use this if you know what you are doing. Consider using :meth:`unique` instead
|
|
185
|
+
Release the unique hold on the lock. This must be called by the same thread that acquired it.
|
|
186
|
+
This must be called exactly the same number of times as :meth:`acquire_unique` was called.
|
|
187
|
+
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
log: logging.Logger # value = <Logger amulet.utils.shareable_lock (INFO)>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from amulet.utils.signal._signal import (
|
|
4
|
+
Signal,
|
|
5
|
+
SignalInstance,
|
|
6
|
+
SignalInstanceConstructor,
|
|
7
|
+
create_signal_instance,
|
|
8
|
+
get_fallback_signal_instance_constructor,
|
|
9
|
+
get_pyside6_signal_instance_constructor,
|
|
10
|
+
set_signal_instance_constructor,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from . import _signal
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"Signal",
|
|
17
|
+
"SignalInstance",
|
|
18
|
+
"SignalInstanceCacheName",
|
|
19
|
+
"SignalInstanceConstructor",
|
|
20
|
+
"create_signal_instance",
|
|
21
|
+
"get_fallback_signal_instance_constructor",
|
|
22
|
+
"get_pyside6_signal_instance_constructor",
|
|
23
|
+
"set_signal_instance_constructor",
|
|
24
|
+
]
|
|
25
|
+
SignalInstanceCacheName: str
|