Skip to content

Commit 004db1e

Browse files
authored
fix: improve python (#51)
## Description <!-- Provide a brief description of your changes --> ## Related Issue <!-- Link to the related issue(s) --> Closes # ## Type of Change <!-- Mark the relevant option with an "x" --> - [ ] `feat`: New feature (minor version bump) - [ ] `fix`: Bug fix (patch version bump) - [ ] `docs`: Documentation only changes - [ ] `chore`: Maintenance tasks, dependency updates - [ ] `refactor`: Code refactoring without functional changes - [ ] `test`: Adding or updating tests - [ ] `ci`: CI/CD changes - [ ] `perf`: Performance improvements - [ ] `build`: Build system changes - [ ] `style`: Code style/formatting changes ## PR Title Format **IMPORTANT**: Since we use squash and merge, your PR title will become the commit message. Please ensure your PR title follows the [Conventional Commits](https://www.conventionalcommits.org/) format: ``` <type>(<optional-scope>): <description> ``` ### Examples: - `feat(operators): add new string comparison operator` - `fix(wasm): correct memory allocation bug` - `docs: update API examples in README` - `chore(deps): update rust dependencies` For breaking changes, use `!` after the type/scope or include `BREAKING CHANGE:` in the PR description: - `feat(api)!: redesign evaluation API` ## Testing <!-- Describe the testing you've performed --> - [ ] Unit tests added/updated - [ ] Integration tests added/updated - [ ] Manual testing performed - [ ] All tests pass (`cargo test`) - [ ] Code is formatted (`cargo fmt`) - [ ] Clippy checks pass (`cargo clippy -- -D warnings`) - [ ] WASM builds successfully (if applicable) ## Breaking Changes <!-- If this introduces breaking changes, describe them here --> - [ ] This PR includes breaking changes - [ ] Documentation has been updated to reflect breaking changes - [ ] Migration guide included (if needed) ## Additional Notes <!-- Any additional information, context, or screenshots --> Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
1 parent 8d2baa3 commit 004db1e

2 files changed

Lines changed: 88 additions & 2 deletions

File tree

.github/workflows/python-wheels.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Python Wheels
22

33
on:
44
push:
5-
branches: [main, feat/python]
5+
branches: [main]
66
pull_request:
77
branches: [main]
88
release:

python/src/lib.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,24 @@ struct FlagEvaluator {
3333
#[pymethods]
3434
impl FlagEvaluator {
3535
/// Create a new FlagEvaluator instance
36+
///
37+
/// Args:
38+
/// permissive (bool, optional): If True, use permissive validation mode (accept invalid configs).
39+
/// If False, use strict mode (reject invalid configs).
40+
/// Defaults to False (strict mode).
3641
#[new]
37-
fn new() -> Self {
42+
#[pyo3(signature = (permissive=false))]
43+
fn new(permissive: bool) -> Self {
44+
use ::flagd_evaluator::storage::{set_validation_mode, ValidationMode};
45+
46+
let mode = if permissive {
47+
ValidationMode::Permissive
48+
} else {
49+
ValidationMode::Strict
50+
};
51+
52+
set_validation_mode(mode);
53+
3854
FlagEvaluator { state: None }
3955
}
4056

@@ -253,12 +269,82 @@ impl FlagEvaluator {
253269
}
254270
}
255271

272+
/// Evaluate targeting rules (JSON Logic) against context data.
273+
///
274+
/// This is a helper function for the flagd provider to evaluate targeting rules.
275+
/// For general flag evaluation, use the FlagEvaluator class instead.
276+
///
277+
/// Args:
278+
/// targeting (dict): JSON Logic targeting rules
279+
/// context (dict): Evaluation context data
280+
///
281+
/// Returns:
282+
/// dict: Evaluation result with 'success', 'result', and optional 'error' fields
283+
#[pyfunction]
284+
fn evaluate_targeting(
285+
py: Python,
286+
targeting: &Bound<'_, PyDict>,
287+
context: &Bound<'_, PyDict>,
288+
) -> PyResult<PyObject> {
289+
use ::flagd_evaluator::operators;
290+
291+
// Convert Python dicts to JSON values
292+
let targeting_value: Value = pythonize::depythonize(targeting.as_any()).map_err(|e| {
293+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("Failed to parse targeting: {}", e))
294+
})?;
295+
296+
let context_value: Value = pythonize::depythonize(context.as_any()).map_err(|e| {
297+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!("Failed to parse context: {}", e))
298+
})?;
299+
300+
// Convert to JSON strings for evaluation
301+
let targeting_str = serde_json::to_string(&targeting_value).map_err(|e| {
302+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
303+
"Failed to serialize targeting: {}",
304+
e
305+
))
306+
})?;
307+
308+
let context_str = serde_json::to_string(&context_value).map_err(|e| {
309+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
310+
"Failed to serialize context: {}",
311+
e
312+
))
313+
})?;
314+
315+
// Evaluate using JSON Logic with custom operators
316+
let logic = operators::create_evaluator();
317+
let result_dict = PyDict::new_bound(py);
318+
319+
match logic.evaluate_json(&targeting_str, &context_str) {
320+
Ok(result) => {
321+
result_dict.set_item("success", true)?;
322+
// Convert result back to Python
323+
let py_result = pythonize::pythonize(py, &result).map_err(|e| {
324+
PyErr::new::<pyo3::exceptions::PyValueError, _>(format!(
325+
"Failed to convert result: {}",
326+
e
327+
))
328+
})?;
329+
result_dict.set_item("result", py_result)?;
330+
}
331+
Err(e) => {
332+
result_dict.set_item("success", false)?;
333+
result_dict.set_item("result", py.None())?;
334+
result_dict.set_item("error", format!("{}", e))?;
335+
}
336+
}
337+
338+
Ok(result_dict.into())
339+
}
340+
256341
/// flagd_evaluator - Feature flag evaluation
257342
///
258343
/// This module provides native Python bindings for the flagd-evaluator library,
259344
/// offering high-performance feature flag evaluation.
260345
#[pymodule]
261346
fn flagd_evaluator(m: &Bound<'_, PyModule>) -> PyResult<()> {
262347
m.add_class::<FlagEvaluator>()?;
348+
m.add_function(wrap_pyfunction!(evaluate_targeting, m)?)?;
263349
Ok(())
264350
}

0 commit comments

Comments
 (0)