soma_config/
python_environment.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
10#[serde(default)]
11pub struct PythonEnvironmentConfig {
12 pub enabled: bool,
13 pub cache_root: String,
14 pub uv_program: String,
15 pub uv_version: String,
16 pub python_executable: String,
17 pub runtime_implementation: String,
18 pub runtime_version: String,
19 pub runtime_platform: String,
20 pub wheel_platform_tag: String,
21 pub sdk_wheel: String,
22 pub sdk_wheel_sha256: String,
23 pub offline: bool,
24 pub update: bool,
25 pub policy_version: u32,
26}
27
28impl Default for PythonEnvironmentConfig {
29 fn default() -> Self {
30 Self {
31 enabled: false,
32 cache_root: String::new(),
33 uv_program: String::new(),
34 uv_version: String::new(),
35 python_executable: String::new(),
36 runtime_implementation: String::new(),
37 runtime_version: String::new(),
38 runtime_platform: String::new(),
39 wheel_platform_tag: String::new(),
40 sdk_wheel: String::new(),
41 sdk_wheel_sha256: String::new(),
42 offline: false,
43 update: false,
44 policy_version: 2,
45 }
46 }
47}
48
49impl PythonEnvironmentConfig {
50 pub(crate) fn validate(&self) -> anyhow::Result<()> {
51 if !self.enabled {
52 return Ok(());
53 }
54 for (name, value) in [
55 ("SOMA_PYTHON_ENVIRONMENT_CACHE_ROOT", &self.cache_root),
56 ("SOMA_PYTHON_ENVIRONMENT_UV_PROGRAM", &self.uv_program),
57 ("SOMA_PYTHON_ENVIRONMENT_UV_VERSION", &self.uv_version),
58 (
59 "SOMA_PYTHON_ENVIRONMENT_PYTHON_EXECUTABLE",
60 &self.python_executable,
61 ),
62 (
63 "SOMA_PYTHON_ENVIRONMENT_RUNTIME_IMPLEMENTATION",
64 &self.runtime_implementation,
65 ),
66 (
67 "SOMA_PYTHON_ENVIRONMENT_RUNTIME_VERSION",
68 &self.runtime_version,
69 ),
70 (
71 "SOMA_PYTHON_ENVIRONMENT_RUNTIME_PLATFORM",
72 &self.runtime_platform,
73 ),
74 (
75 "SOMA_PYTHON_ENVIRONMENT_WHEEL_PLATFORM_TAG",
76 &self.wheel_platform_tag,
77 ),
78 ("SOMA_PYTHON_ENVIRONMENT_SDK_WHEEL", &self.sdk_wheel),
79 (
80 "SOMA_PYTHON_ENVIRONMENT_SDK_WHEEL_SHA256",
81 &self.sdk_wheel_sha256,
82 ),
83 ] {
84 if value.trim().is_empty() {
85 anyhow::bail!("{name} is required when Python environments are enabled");
86 }
87 }
88 let digest = self.sdk_wheel_sha256.trim();
89 if digest.len() != 64 || !digest.bytes().all(|byte| byte.is_ascii_hexdigit()) {
90 anyhow::bail!(
91 "SOMA_PYTHON_ENVIRONMENT_SDK_WHEEL_SHA256 must be exactly 64 hexadecimal characters"
92 );
93 }
94 if self.policy_version == 0 {
95 anyhow::bail!("SOMA_PYTHON_ENVIRONMENT_POLICY_VERSION must be greater than zero");
96 }
97 if self.offline && self.update {
98 anyhow::bail!(
99 "SOMA_PYTHON_ENVIRONMENT_UPDATE cannot be enabled with SOMA_PYTHON_ENVIRONMENT_OFFLINE"
100 );
101 }
102 for (name, value) in [
103 ("SOMA_PYTHON_ENVIRONMENT_CACHE_ROOT", &self.cache_root),
104 ("SOMA_PYTHON_ENVIRONMENT_UV_PROGRAM", &self.uv_program),
105 (
106 "SOMA_PYTHON_ENVIRONMENT_PYTHON_EXECUTABLE",
107 &self.python_executable,
108 ),
109 ("SOMA_PYTHON_ENVIRONMENT_SDK_WHEEL", &self.sdk_wheel),
110 ] {
111 if !std::path::Path::new(value).is_absolute() {
112 anyhow::bail!("{name} must be an absolute path");
113 }
114 }
115 Ok(())
116 }
117}