aardvark_core/persistent/
isolate.rs1use std::sync::Arc;
2
3use crate::bundle::BundleFingerprint;
4use crate::config::PyRuntimeConfig;
5use crate::error::Result;
6use crate::invocation::InvocationDescriptor;
7use crate::persistent::{BundleArtifact, InlinePythonOptions};
8use crate::runtime::PyRuntime;
9use crate::runtime_language::RuntimeLanguage;
10use crate::strategy::{
11 DefaultInvocationStrategy, JsonInvocationStrategy, PyInvocationStrategy, RawCtxInput,
12 RawCtxInvocationStrategy,
13};
14use serde_json::Value as JsonValue;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum CleanupMode {
19 Full,
20 SharedBuffersOnly,
21 None,
22}
23
24impl Default for CleanupMode {
25 fn default() -> Self {
26 Self::Full
27 }
28}
29
30#[derive(Clone)]
32pub struct IsolateConfig {
33 pub runtime: PyRuntimeConfig,
34 pub cleanup: CleanupMode,
35}
36
37impl Default for IsolateConfig {
38 fn default() -> Self {
39 Self {
40 runtime: PyRuntimeConfig::default(),
41 cleanup: CleanupMode::Full,
42 }
43 }
44}
45
46#[derive(Clone)]
48pub struct BundleHandle {
49 artifact: Arc<BundleArtifact>,
50}
51
52impl BundleHandle {
53 pub fn from_artifact(artifact: Arc<BundleArtifact>) -> Self {
55 Self { artifact }
56 }
57
58 pub fn prepare_default_handler(&self) -> HandlerSession {
60 self.prepare_handler(None)
61 }
62
63 pub fn prepare_handler(&self, descriptor: Option<InvocationDescriptor>) -> HandlerSession {
65 let mut descriptor = descriptor.unwrap_or_else(|| self.artifact.default_descriptor());
66 descriptor.language = descriptor.language.or(Some(self.artifact.language()));
67 HandlerSession {
68 artifact: self.artifact.clone(),
69 descriptor,
70 }
71 }
72
73 pub(crate) fn artifact(&self) -> &Arc<BundleArtifact> {
74 &self.artifact
75 }
76}
77
78pub struct HandlerSession {
80 artifact: Arc<BundleArtifact>,
81 descriptor: InvocationDescriptor,
82}
83
84impl HandlerSession {
85 pub fn descriptor(&self) -> &InvocationDescriptor {
87 &self.descriptor
88 }
89
90 pub fn descriptor_mut(&mut self) -> &mut InvocationDescriptor {
92 &mut self.descriptor
93 }
94
95 pub fn invoke(&self, isolate: &mut PythonIsolate) -> Result<crate::ExecutionOutcome> {
97 let mut strategy = DefaultInvocationStrategy;
98 isolate.invoke_with_strategy(self, &mut strategy)
99 }
100
101 pub fn invoke_json(
103 &self,
104 isolate: &mut PythonIsolate,
105 input: Option<JsonValue>,
106 ) -> Result<crate::ExecutionOutcome> {
107 let mut strategy = JsonInvocationStrategy::new(input);
108 isolate.invoke_with_strategy(self, &mut strategy)
109 }
110
111 pub fn invoke_rawctx(
113 &self,
114 isolate: &mut PythonIsolate,
115 inputs: Vec<RawCtxInput>,
116 ) -> Result<crate::ExecutionOutcome> {
117 let mut strategy = RawCtxInvocationStrategy::new(inputs);
118 isolate.invoke_with_strategy(self, &mut strategy)
119 }
120
121 pub async fn invoke_async(
123 &self,
124 isolate: &mut PythonIsolate,
125 ) -> Result<crate::ExecutionOutcome> {
126 self.invoke(isolate)
127 }
128
129 pub(crate) fn artifact(&self) -> &Arc<BundleArtifact> {
130 &self.artifact
131 }
132
133 pub(crate) fn descriptor_cloned(&self) -> InvocationDescriptor {
134 self.descriptor.clone()
135 }
136}
137
138pub struct PythonIsolate {
140 runtime: PyRuntime,
141 cleanup: CleanupMode,
142 loaded_fingerprint: Option<BundleFingerprint>,
143 current_artifact: Option<Arc<BundleArtifact>>,
144}
145
146impl PythonIsolate {
147 pub fn new(config: IsolateConfig) -> Result<Self> {
148 let runtime = PyRuntime::new(config.runtime.clone())?;
149 Ok(Self {
150 runtime,
151 cleanup: config.cleanup,
152 loaded_fingerprint: None,
153 current_artifact: None,
154 })
155 }
156
157 pub fn load_bundle(&mut self, handle: &BundleHandle) -> Result<()> {
159 let fingerprint = handle.artifact().fingerprint();
160 if self.loaded_fingerprint == Some(fingerprint) {
161 self.current_artifact = Some(handle.artifact().clone());
162 return Ok(());
163 }
164 self.loaded_fingerprint = None;
165 self.current_artifact = Some(handle.artifact().clone());
166 Ok(())
167 }
168
169 pub fn invoke_with_strategy<S: PyInvocationStrategy>(
171 &mut self,
172 handler: &HandlerSession,
173 strategy: &mut S,
174 ) -> Result<crate::ExecutionOutcome> {
175 self.ensure_artifact(handler.artifact())?;
176 let bundle = handler.artifact().bundle();
177 let descriptor = handler.descriptor_cloned();
178 if handler.artifact().manifest().is_some() {
179 let (session, _) = self
180 .runtime
181 .prepare_session_with_manifest_and_descriptor(bundle, descriptor)?;
182 self.runtime.run_session_with_strategy(&session, strategy)
183 } else {
184 let session = self
185 .runtime
186 .prepare_session_with_descriptor(bundle, descriptor)?;
187 self.runtime.run_session_with_strategy(&session, strategy)
188 }
189 }
190
191 fn ensure_artifact(&mut self, artifact: &Arc<BundleArtifact>) -> Result<()> {
192 let fingerprint = artifact.fingerprint();
193 if self.loaded_fingerprint == Some(fingerprint) {
194 return Ok(());
195 }
196 self.materialise_bundle(artifact)?;
197 self.loaded_fingerprint = Some(fingerprint);
198 self.current_artifact = Some(artifact.clone());
199 Ok(())
200 }
201
202 fn materialise_bundle(&mut self, artifact: &Arc<BundleArtifact>) -> Result<()> {
203 let descriptor = artifact.default_descriptor();
204 if artifact.manifest().is_some() {
205 let bundle = artifact.bundle();
206 let (_session, _) = self
207 .runtime
208 .prepare_session_with_manifest_and_descriptor(bundle, descriptor)?;
209 } else {
210 let bundle = artifact.bundle();
211 let _ = self
212 .runtime
213 .prepare_session_with_descriptor(bundle, descriptor)?;
214 }
215 Ok(())
216 }
217
218 pub fn cleanup_mode(&self) -> CleanupMode {
219 self.cleanup
220 }
221
222 pub fn runtime_language(&self) -> Option<RuntimeLanguage> {
223 self.current_artifact
224 .as_ref()
225 .map(|artifact| artifact.language())
226 }
227
228 pub fn runtime(&mut self) -> &mut PyRuntime {
229 &mut self.runtime
230 }
231
232 pub fn run_inline_python(
234 &mut self,
235 code: &str,
236 entrypoint: &str,
237 ) -> Result<crate::ExecutionOutcome> {
238 let options = InlinePythonOptions {
239 entrypoint: Some(entrypoint.to_owned()),
240 ..InlinePythonOptions::default()
241 };
242 self.run_inline_python_with_options(code, options)
243 }
244
245 pub fn run_inline_python_with_options(
247 &mut self,
248 code: &str,
249 options: InlinePythonOptions,
250 ) -> Result<crate::ExecutionOutcome> {
251 let (bundle, entrypoint) = options.build_bundle(code)?;
252 let descriptor = InvocationDescriptor::new(entrypoint);
253 let (session, _) = self
254 .runtime
255 .prepare_session_with_manifest_and_descriptor(bundle, descriptor)?;
256 self.runtime.run_session(&session)
257 }
258}