Coverage for wasmtime/_store.py: 88%

64 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2026-05-27 18:55 +0000

1from . import _ffi as ffi 

2from ctypes import byref, c_uint64, cast, c_void_p, CFUNCTYPE 

3import ctypes 

4from wasmtime import Engine, WasmtimeError, Managed 

5from . import _value as value 

6import typing 

7 

8if typing.TYPE_CHECKING: 

9 from ._wasi import WasiConfig 

10 

11 

12class Store(Managed["ctypes._Pointer[ffi.wasmtime_store_t]"]): 

13 __context: "typing.Optional[ctypes._Pointer[ffi.wasmtime_context_t]]" 

14 

15 def __init__(self, engine: typing.Optional[Engine] = None, data: typing.Optional[typing.Any] = None): 

16 

17 if engine is None: 

18 engine = Engine() 

19 elif not isinstance(engine, Engine): 

20 raise TypeError("expected an Engine") 

21 data_id = None 

22 finalize = cast(0, CFUNCTYPE(None, c_void_p)) 

23 if data: 

24 data_id = value._intern(data) 

25 finalize = value._externref_finalizer 

26 self._set_ptr(ffi.wasmtime_store_new(engine.ptr(), data_id, finalize)) 

27 self.__context = ffi.wasmtime_store_context(self.ptr()) 

28 self.engine = engine 

29 

30 def _delete(self, ptr: "ctypes._Pointer[ffi.wasmtime_store_t]") -> None: 

31 ffi.wasmtime_store_delete(ptr) 

32 self.__context = None 

33 

34 def _context(self) -> "ctypes._Pointer[ffi.wasmtime_context_t]": 

35 if self.__context is None: 

36 raise ValueError('already closed') 

37 return self.__context 

38 

39 def data(self) -> typing.Optional[typing.Any]: 

40 """ 

41 TODO 

42 """ 

43 data = ffi.wasmtime_context_get_data(self._context()) 

44 if data: 

45 # FIXME https://github.com/bytecodealliance/wasmtime-py/issues/303 

46 return value._unintern(data) # type: ignore 

47 else: 

48 return None 

49 

50 def gc(self) -> None: 

51 """ 

52 Runs a GC over `externref` values that have been passed into this Store, 

53 cleaning out anything that is no longer referenced. 

54 

55 This is not required to be run manually, but can be done so if you'd 

56 like more precise control over when unreferenced `externref` values are 

57 deallocated. 

58 """ 

59 ffi.wasmtime_context_gc(self._context()) 

60 

61 def set_fuel(self, fuel: int) -> None: 

62 """ 

63 Sets the amount of fuel in this store to `fuel`. 

64 

65 This is only relevant when `Config.consume_fuel` is configured. 

66 

67 This is a required call to ensure that the store has fuel to 

68 execute WebAssembly since otherwise stores start with zero fuel. 

69 

70 Raises a `WasmtimeError` if this store's configuration is not configured 

71 to consume fuel. 

72 """ 

73 err = ffi.wasmtime_context_set_fuel(self._context(), fuel) 

74 if err: 

75 raise WasmtimeError._from_ptr(err) 

76 

77 def get_fuel(self) -> int: 

78 """ 

79 Returns the amount of fuel left in the store. 

80 

81 This is only relevant when `Config.consume_fuel` is configured. 

82 

83 Raises a `WasmtimeError` if this store's configuration is not configured 

84 to consume fuel or if the store doesn't have enough fuel remaining. 

85 """ 

86 remaining = c_uint64(0) 

87 err = ffi.wasmtime_context_get_fuel(self._context(), byref(remaining)) 

88 if err: 

89 raise WasmtimeError._from_ptr(err) 

90 return remaining.value 

91 

92 def set_wasi(self, wasi: "WasiConfig") -> None: 

93 """ 

94 TODO 

95 """ 

96 error = ffi.wasmtime_context_set_wasi(self._context(), wasi._consume()) 

97 if error: 

98 raise WasmtimeError._from_ptr(error) 

99 

100 def set_wasi_http(self) -> None: 

101 """ 

102 Initializes the WASI HTTP context for this store. 

103 

104 Must be called before instantiating a component that uses `wasi:http`. 

105 Requires WASI to be configured first via `set_wasi()`. 

106 """ 

107 ffi.wasmtime_context_set_wasi_http(self._context()) 

108 

109 def set_epoch_deadline(self, ticks_after_current: int) -> None: 

110 """ 

111 Configures the relative epoch deadline, after the current engine's 

112 epoch, after which WebAssembly code will trap. 

113 """ 

114 ffi.wasmtime_context_set_epoch_deadline(self._context(), ticks_after_current) 

115 

116 def set_limits(self, 

117 memory_size: int = -1, 

118 table_elements: int = -1, 

119 instances: int = -1, 

120 tables: int = -1, 

121 memories: int = -1) -> None: 

122 """ 

123 Configures the limits of various items within this store. 

124 

125 * `memory_size` - the maximum size, in bytes, that linear memory is 

126 allowed to consume within this store. Setting this to a lower value 

127 will cause instantiation to fail if a module needs more memory. 

128 Additionally the `memory.grow` instruction will return -1 once this 

129 threshold is reached. 

130 

131 * `table_elements` - the maximum number of elements that can be stored 

132 within tables in this store. Currently each table element takes 8 

133 bytes. 

134 

135 * `instances` - the maximum number of WebAssembly instances that can 

136 be created. 

137 

138 * `tables` - the maximum number of WebAssembly tables that can 

139 be created. 

140 

141 * `memories` - the maximum number of WebAssembly linear memories that 

142 can be created. 

143 

144 If any limit is negative then the limit will not be set as a part of 

145 this invocation and it will be ignored. 

146 """ 

147 ffi.wasmtime_store_limiter(self.ptr(), memory_size, table_elements, instances, tables, memories) 

148 

149 

150class StoreContext: 

151 __ptr: typing.Optional["ctypes._Pointer[ffi.wasmtime_context_t]"] 

152 

153 def __init__(self, ptr: "ctypes._Pointer[ffi.wasmtime_context_t]"): 

154 self.__ptr = ptr 

155 

156 def _context(self) -> "ctypes._Pointer[ffi.wasmtime_context_t]": 

157 if self.__ptr is None: 

158 raise ValueError("caller is no longer valid") 

159 return self.__ptr 

160 

161 def _invalidate(self) -> None: 

162 self.__ptr = None 

163 

164 

165if typing.TYPE_CHECKING: 

166 from ._func import Caller 

167 

168 

169Storelike = typing.Union[Store, "Caller", StoreContext]