1use crate::engine::OverlayExport;
4use crate::error::Result;
5use crate::invocation::InvocationLimits;
6use crate::pyodide::PYODIDE_VERSION;
7use crate::runtime::PyRuntime;
8use crate::runtime_language::RuntimeLanguage;
9use std::fmt;
10use std::path::PathBuf;
11use std::sync::{Arc, Mutex};
12
13#[derive(Clone)]
15pub struct SnapshotConfig {
16 pub load_from: Option<std::path::PathBuf>,
18 pub save_to: Option<std::path::PathBuf>,
20 cache: Arc<SnapshotCache>,
21}
22
23impl SnapshotConfig {
24 pub fn clear_cache(&self) {
26 let mut guard = self.cache.bytes.lock().unwrap();
27 *guard = None;
28 }
29
30 pub(crate) fn cached_bytes(&self) -> Option<Arc<[u8]>> {
31 self.cache.bytes.lock().unwrap().clone()
32 }
33
34 pub(crate) fn store_cached_bytes(&self, bytes: Arc<[u8]>) {
35 let mut guard = self.cache.bytes.lock().unwrap();
36 *guard = Some(bytes);
37 }
38}
39
40impl Default for SnapshotConfig {
41 fn default() -> Self {
42 Self {
43 load_from: None,
44 save_to: None,
45 cache: Arc::new(SnapshotCache::default()),
46 }
47 }
48}
49
50impl fmt::Debug for SnapshotConfig {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 f.debug_struct("SnapshotConfig")
53 .field("load_from", &self.load_from)
54 .field("save_to", &self.save_to)
55 .field("cached", &self.cache)
56 .finish()
57 }
58}
59
60#[derive(Default)]
61struct SnapshotCache {
62 bytes: Mutex<Option<Arc<[u8]>>>,
63}
64
65impl fmt::Debug for SnapshotCache {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 let cached = self.bytes.lock().unwrap().as_ref().map(|arc| arc.len());
68 f.debug_struct("SnapshotCache")
69 .field("bytes", &cached)
70 .finish()
71 }
72}
73
74pub type WarmHook = dyn Fn(&mut PyRuntime) -> Result<()> + Send + Sync;
76
77#[derive(Clone, Default)]
79pub struct HostHooks {
80 pub before_warm_snapshot: Option<Arc<WarmHook>>,
82 pub after_warm_restore: Option<Arc<WarmHook>>,
84}
85
86impl fmt::Debug for HostHooks {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 f.debug_struct("HostHooks")
89 .field(
90 "before_warm_snapshot",
91 &self.before_warm_snapshot.as_ref().map(|_| "Some"),
92 )
93 .field(
94 "after_warm_restore",
95 &self.after_warm_restore.as_ref().map(|_| "Some"),
96 )
97 .finish()
98 }
99}
100
101#[derive(Clone)]
103pub struct WarmState {
104 snapshot: Arc<[u8]>,
105 overlay: Arc<OverlayExport>,
106 overlay_preloaded: bool,
107}
108
109impl WarmState {
110 pub fn new(snapshot: Arc<[u8]>, overlay: OverlayExport) -> Self {
112 Self {
113 snapshot,
114 overlay: Arc::new(overlay),
115 overlay_preloaded: false,
116 }
117 }
118
119 pub fn with_overlay_preloaded(snapshot: Arc<[u8]>, overlay: OverlayExport) -> Self {
121 Self {
122 snapshot,
123 overlay: Arc::new(overlay),
124 overlay_preloaded: true,
125 }
126 }
127
128 pub fn into_overlay_preloaded(mut self) -> Self {
133 self.overlay_preloaded = true;
134 self
135 }
136
137 pub fn snapshot(&self) -> Arc<[u8]> {
139 self.snapshot.clone()
140 }
141
142 pub fn overlay(&self) -> Arc<OverlayExport> {
144 self.overlay.clone()
145 }
146
147 pub fn overlay_preloaded(&self) -> bool {
150 self.overlay_preloaded
151 }
152}
153
154impl fmt::Debug for WarmState {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 f.debug_struct("WarmState")
157 .field("snapshot_len", &self.snapshot.len())
158 .field("overlay_blobs", &self.overlay.blobs.len())
159 .field("overlay_preloaded", &self.overlay_preloaded)
160 .finish()
161 }
162}
163
164#[derive(Debug, Clone)]
166pub struct PyRuntimeConfig {
167 pub pyodide_version: String,
169 pub pyodide_package_dir: Option<PathBuf>,
175 pub default_language: RuntimeLanguage,
177 pub snapshot: SnapshotConfig,
179 pub hooks: HostHooks,
181 pub budget_override: Option<InvocationLimits>,
183 pub reset_policy: ResetPolicy,
185 pub host_capabilities: Vec<String>,
187 pub warm_state: Option<WarmState>,
189}
190
191impl Default for PyRuntimeConfig {
192 fn default() -> Self {
193 let default_package_dir =
194 option_env!("AARDVARK_PYODIDE_DEFAULT_PACKAGES").and_then(|value| {
195 let trimmed = value.trim();
196 if trimmed.is_empty() {
197 None
198 } else {
199 Some(PathBuf::from(trimmed))
200 }
201 });
202 Self {
203 pyodide_version: PYODIDE_VERSION.to_owned(),
204 pyodide_package_dir: default_package_dir,
205 default_language: RuntimeLanguage::Python,
206 snapshot: SnapshotConfig::default(),
207 hooks: HostHooks::default(),
208 budget_override: None,
209 reset_policy: ResetPolicy::Manual,
210 host_capabilities: vec!["rawctx_buffers".to_string()],
211 warm_state: None,
212 }
213 }
214}
215
216impl PyRuntimeConfig {
217 pub fn pyodide_package_dir(&self) -> Option<&PathBuf> {
219 self.pyodide_package_dir.as_ref()
220 }
221
222 pub fn set_pyodide_package_dir<P: Into<PathBuf>>(&mut self, path: P) {
224 self.pyodide_package_dir = Some(path.into());
225 }
226
227 pub fn clear_pyodide_package_dir(&mut self) {
229 self.pyodide_package_dir = None;
230 }
231
232 pub fn with_pyodide_package_dir<P: Into<PathBuf>>(mut self, path: P) -> Self {
234 self.set_pyodide_package_dir(path);
235 self
236 }
237
238 pub fn without_pyodide_package_dir(mut self) -> Self {
240 self.clear_pyodide_package_dir();
241 self
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
247pub enum ResetPolicy {
248 #[default]
250 Manual,
251 AfterInvocation,
253}