sentry-options 0.0.4__tar.gz → 0.0.5__tar.gz

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.
@@ -240,7 +240,7 @@ dependencies = [
240
240
 
241
241
  [[package]]
242
242
  name = "example"
243
- version = "0.0.4"
243
+ version = "0.0.5"
244
244
  dependencies = [
245
245
  "anyhow",
246
246
  "sentry-options",
@@ -1111,7 +1111,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1111
1111
 
1112
1112
  [[package]]
1113
1113
  name = "sentry-options"
1114
- version = "0.0.4"
1114
+ version = "0.0.5"
1115
1115
  dependencies = [
1116
1116
  "sentry-options-validation",
1117
1117
  "serde_json",
@@ -1121,7 +1121,7 @@ dependencies = [
1121
1121
 
1122
1122
  [[package]]
1123
1123
  name = "sentry-options-cli"
1124
- version = "0.0.4"
1124
+ version = "0.0.5"
1125
1125
  dependencies = [
1126
1126
  "clap",
1127
1127
  "sentry-options-validation",
@@ -1135,7 +1135,7 @@ dependencies = [
1135
1135
 
1136
1136
  [[package]]
1137
1137
  name = "sentry-options-python"
1138
- version = "0.0.4"
1138
+ version = "0.0.5"
1139
1139
  dependencies = [
1140
1140
  "pyo3",
1141
1141
  "sentry-options",
@@ -1144,7 +1144,7 @@ dependencies = [
1144
1144
 
1145
1145
  [[package]]
1146
1146
  name = "sentry-options-validation"
1147
- version = "0.0.4"
1147
+ version = "0.0.5"
1148
1148
  dependencies = [
1149
1149
  "anyhow",
1150
1150
  "jsonschema",
@@ -11,13 +11,13 @@ default-members = [
11
11
  ]
12
12
 
13
13
  [workspace.package]
14
- version = "0.0.4"
14
+ version = "0.0.5"
15
15
  edition = "2024"
16
16
  repository = "https://github.com/getsentry/sentry-options"
17
17
  license = "Apache-2.0"
18
18
 
19
19
  [workspace.dependencies]
20
- sentry-options-validation = { path = "sentry-options-validation", version = "0.0.4" }
20
+ sentry-options-validation = { path = "sentry-options-validation", version = "0.0.5" }
21
21
  serde = { version = "1.0", features = ["derive"] }
22
22
  serde_json = "1.0.145"
23
23
  anyhow = "1.0.100"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sentry_options
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3 :: Only
6
6
  Classifier: Programming Language :: Python :: Implementation :: CPython
@@ -79,7 +79,8 @@ fn options_err(err: RustOptionsError) -> PyErr {
79
79
  }
80
80
  }
81
81
 
82
- /// Initialize global options from default path or SENTRY_OPTIONS_DIR env var.
82
+ /// Initialize global options using fallback chain: SENTRY_OPTIONS_DIR env var,
83
+ /// then /etc/sentry-options if it exists, otherwise sentry-options/.
83
84
  #[pyfunction]
84
85
  fn init() -> PyResult<()> {
85
86
  let opts = RustOptions::new().map_err(options_err)?;
@@ -1,16 +1,15 @@
1
1
  //! Options client for reading validated configuration values.
2
2
 
3
3
  use std::collections::HashMap;
4
- use std::path::{Path, PathBuf};
4
+ use std::path::Path;
5
5
  use std::sync::{Arc, OnceLock, RwLock};
6
6
 
7
- use sentry_options_validation::{SchemaRegistry, ValidationError, ValuesWatcher};
7
+ use sentry_options_validation::{
8
+ SchemaRegistry, ValidationError, ValuesWatcher, resolve_options_dir,
9
+ };
8
10
  use serde_json::Value;
9
11
  use thiserror::Error;
10
12
 
11
- const DEFAULT_OPTIONS_DIR: &str = "/etc/sentry-options";
12
- const OPTIONS_DIR_ENV: &str = "SENTRY_OPTIONS_DIR";
13
-
14
13
  static GLOBAL_OPTIONS: OnceLock<Options> = OnceLock::new();
15
14
 
16
15
  #[derive(Debug, Error)]
@@ -38,14 +37,11 @@ pub struct Options {
38
37
  }
39
38
 
40
39
  impl Options {
41
- /// Load options from default path (`/etc/sentry-options`) or `SENTRY_OPTIONS_DIR` env var.
40
+ /// Load options using fallback chain: `SENTRY_OPTIONS_DIR` env var, then `/etc/sentry-options`
41
+ /// if it exists, otherwise `sentry-options/`.
42
42
  /// Expects `{dir}/schemas/` and `{dir}/values/` subdirectories.
43
43
  pub fn new() -> Result<Self> {
44
- let base_dir = std::env::var(OPTIONS_DIR_ENV)
45
- .map(PathBuf::from)
46
- .unwrap_or_else(|_| PathBuf::from(DEFAULT_OPTIONS_DIR));
47
-
48
- Self::from_directory(&base_dir)
44
+ Self::from_directory(&resolve_options_dir())
49
45
  }
50
46
 
51
47
  /// Load options from a specific directory (useful for testing).
@@ -98,7 +94,8 @@ impl Options {
98
94
  }
99
95
  }
100
96
 
101
- /// Initialize global options from default path or `SENTRY_OPTIONS_DIR` env var.
97
+ /// Initialize global options using fallback chain: `SENTRY_OPTIONS_DIR` env var,
98
+ /// then `/etc/sentry-options` if it exists, otherwise `sentry-options/`.
102
99
  pub fn init() -> Result<()> {
103
100
  let opts = Options::new()?;
104
101
  GLOBAL_OPTIONS
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "sentry_options"
7
- version = "0.0.4"
7
+ version = "0.0.5"
8
8
  description = "Python client for sentry-options using Rust validation"
9
9
  license = "Apache-2.0"
10
10
  requires-python = ">=3.11"
@@ -26,6 +26,32 @@ const VALUES_FILE_NAME: &str = "values.json";
26
26
  /// Time between file polls in seconds
27
27
  const POLLING_DELAY: u64 = 5;
28
28
 
29
+ /// Production path where options are deployed via config map
30
+ pub const PRODUCTION_OPTIONS_DIR: &str = "/etc/sentry-options";
31
+
32
+ /// Local fallback path for development
33
+ pub const LOCAL_OPTIONS_DIR: &str = "sentry-options";
34
+
35
+ /// Environment variable to override options directory
36
+ pub const OPTIONS_DIR_ENV: &str = "SENTRY_OPTIONS_DIR";
37
+
38
+ /// Resolve options directory using fallback chain:
39
+ /// 1. `SENTRY_OPTIONS_DIR` env var (if set)
40
+ /// 2. `/etc/sentry-options` (if exists)
41
+ /// 3. `sentry-options/` (local fallback)
42
+ pub fn resolve_options_dir() -> PathBuf {
43
+ if let Ok(dir) = std::env::var(OPTIONS_DIR_ENV) {
44
+ return PathBuf::from(dir);
45
+ }
46
+
47
+ let prod_path = PathBuf::from(PRODUCTION_OPTIONS_DIR);
48
+ if prod_path.exists() {
49
+ return prod_path;
50
+ }
51
+
52
+ PathBuf::from(LOCAL_OPTIONS_DIR)
53
+ }
54
+
29
55
  /// Result type for validation operations
30
56
  pub type ValidationResult<T> = Result<T, ValidationError>;
31
57
 
@@ -52,12 +78,22 @@ pub enum ValidationError {
52
78
 
53
79
  #[error("Failed to parse JSON: {0}")]
54
80
  JSONParse(#[from] serde_json::Error),
81
+
82
+ #[error("{} validation error(s)", .0.len())]
83
+ ValidationErrors(Vec<ValidationError>),
84
+ }
85
+
86
+ /// Metadata for a single option in a namespace schema
87
+ #[derive(Debug, Clone)]
88
+ pub struct OptionMetadata {
89
+ pub option_type: String,
90
+ pub default: Value,
55
91
  }
56
92
 
57
- /// Schema for a namespace, containing validator and defaults
93
+ /// Schema for a namespace, containing validator and option metadata
58
94
  pub struct NamespaceSchema {
59
95
  pub namespace: String,
60
- defaults: HashMap<String, Value>,
96
+ pub options: HashMap<String, OptionMetadata>,
61
97
  validator: jsonschema::Validator,
62
98
  }
63
99
 
@@ -85,7 +121,7 @@ impl NamespaceSchema {
85
121
  /// Get the default value for an option key.
86
122
  /// Returns None if the key doesn't exist in the schema.
87
123
  pub fn get_default(&self, key: &str) -> Option<&Value> {
88
- self.defaults.get(key)
124
+ self.options.get(key).map(|meta| &meta.default)
89
125
  }
90
126
  }
91
127
 
@@ -254,8 +290,8 @@ impl SchemaRegistry {
254
290
  message: format!("Failed to compile validator: {}", e),
255
291
  })?;
256
292
 
257
- // Extract defaults and validate types
258
- let mut defaults = HashMap::new();
293
+ // Extract option metadata and validate types
294
+ let mut options = HashMap::new();
259
295
  if let Some(properties) = schema.get("properties").and_then(|p| p.as_object()) {
260
296
  for (prop_name, prop_value) in properties {
261
297
  if let (Some(prop_type), Some(default_value)) = (
@@ -263,14 +299,20 @@ impl SchemaRegistry {
263
299
  prop_value.get("default"),
264
300
  ) {
265
301
  Self::validate_default_type(prop_name, prop_type, default_value, path)?;
266
- defaults.insert(prop_name.clone(), default_value.clone());
302
+ options.insert(
303
+ prop_name.clone(),
304
+ OptionMetadata {
305
+ option_type: prop_type.to_string(),
306
+ default: default_value.clone(),
307
+ },
308
+ );
267
309
  }
268
310
  }
269
311
  }
270
312
 
271
313
  Ok(Arc::new(NamespaceSchema {
272
314
  namespace: namespace.to_string(),
273
- defaults,
315
+ options,
274
316
  validator,
275
317
  }))
276
318
  }
@@ -280,6 +322,11 @@ impl SchemaRegistry {
280
322
  self.schemas.get(namespace)
281
323
  }
282
324
 
325
+ /// Get all loaded schemas (for schema evolution validation)
326
+ pub fn schemas(&self) -> &HashMap<String, Arc<NamespaceSchema>> {
327
+ &self.schemas
328
+ }
329
+
283
330
  /// Load and validate JSON values from a directory.
284
331
  /// Expects structure: `{values_dir}/{namespace}/values.json`
285
332
  /// Skips namespaces without a values.json file.