Skip to content

Commit 2e528db

Browse files
Extract normalize_arg_names() to simplify parametrize parsing (#532)
## Summary - **Extracted `normalize_arg_names()`** from `parse_parametrize_args()` to separate name normalization from value parsing. The new function tries `Vec<String>` extraction first, then falls back to splitting a comma-separated string. - **Collapsed 3 parsing paths into 1.** Previously, `parse_parametrize_args()` had three separate branches: (1) single string with commas, (2) single string without commas, and (3) `Vec<String>`. Each branch independently extracted values and hardcoded `expect_multiple` as `true`, `false`, or `true` respectively. Now `expect_multiple` is derived uniformly from `names.len() > 1`. - **No behavior change.** The old paths already had `expect_multiple` matching whether the name count was greater than 1 -- this refactor just makes that relationship explicit and removes the redundant extraction calls. ## Test plan - [x] All 685 existing tests pass (`just test`) - [x] All pre-commit checks pass (`uvx prek run -a`) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bb35456 commit 2e528db

File tree

1 file changed

+28
-44
lines changed

1 file changed

+28
-44
lines changed

crates/karva_test_semantic/src/extensions/tags/parametrize.rs

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -59,58 +59,42 @@ impl ParametrizationArgs {
5959
}
6060
}
6161

62+
/// Normalize argument names from Python into a `Vec<String>`.
63+
///
64+
/// Handles both input formats for parameter names:
65+
/// - A list of strings: `["arg1", "arg2"]`
66+
/// - A single comma-separated string: `"arg1, arg2"` or just `"arg1"`
67+
fn normalize_arg_names(arg_names: &Bound<'_, PyAny>) -> Option<Vec<String>> {
68+
if let Ok(names) = arg_names.extract::<Vec<String>>() {
69+
return Some(names);
70+
}
71+
if let Ok(name) = arg_names.extract::<String>() {
72+
return Some(name.split(',').map(|s| s.trim().to_string()).collect());
73+
}
74+
None
75+
}
76+
6277
/// Parse parametrize arguments from Python objects.
6378
///
6479
/// This helper function handles multiple input formats:
65-
/// - `("arg1, arg2", [(1, 2), (3, 4)])` - single arg name with values (wrapped into Vec<Vec>)
66-
/// - `("arg1", [3, 4])` - comma-separated arg names (re-extracted as Vec<Vec>)
67-
/// - `(["arg1", "arg2"], [(1, 2), (3, 4)])` - direct arg names and nested values
68-
/// - `(["arg1", "arg2"], [pytest.param(1, 2), pytest.param(3, 4)])` - direct arg names and single values
69-
/// - `(["arg1"], [pytest.param(1), pytest.param(3)])` - direct arg names and single values
80+
/// - `("arg1, arg2", [(1, 2), (3, 4)])` - comma-separated arg names with tuple values
81+
/// - `("arg1", [3, 4])` - single arg name with scalar values
82+
/// - `(["arg1", "arg2"], [(1, 2), (3, 4)])` - list of arg names with tuple values
83+
/// - `(["arg1", "arg2"], [pytest.param(1, 2), ...])` - list of arg names with param values
84+
/// - `(["arg1"], [pytest.param(1), ...])` - single-element list with param values
7085
pub(super) fn parse_parametrize_args(
7186
arg_names: &Bound<'_, PyAny>,
7287
arg_values: &Bound<'_, PyAny>,
7388
) -> Option<(Vec<String>, Vec<Parametrization>)> {
7489
let py = arg_values.py();
75-
76-
// Try extracting as (String, Vec<Py<PyAny>>)
77-
if let (Ok(name), Ok(values)) = (
78-
arg_names.extract::<String>(),
79-
arg_values.extract::<Vec<Py<PyAny>>>(),
80-
) {
81-
// Check if the string contains comma-separated argument names
82-
if name.contains(',') {
83-
let names: Vec<String> = name.split(',').map(|s| s.trim().to_string()).collect();
84-
let parametrizations = arg_values
85-
.extract::<Vec<Py<PyAny>>>()
86-
.ok()?
87-
.into_iter()
88-
.map(|param| handle_custom_parametrize_param(py, param, true))
89-
.collect();
90-
91-
Some((names, parametrizations))
92-
} else {
93-
// Single argument name - wrap each value in a Vec
94-
let parametrizations = values
95-
.into_iter()
96-
.map(|param| handle_custom_parametrize_param(py, param, false))
97-
.collect();
98-
99-
Some((vec![name], parametrizations))
100-
}
101-
} else if let (Ok(names), Ok(values)) = (
102-
arg_names.extract::<Vec<String>>(),
103-
arg_values.extract::<Vec<Py<PyAny>>>(),
104-
) {
105-
let parametrizations = values
106-
.into_iter()
107-
.map(|param| handle_custom_parametrize_param(py, param, true))
108-
.collect();
109-
// Direct extraction of Vec<String> and Vec<Vec<Py<PyAny>>>
110-
Some((names, parametrizations))
111-
} else {
112-
None
113-
}
90+
let names = normalize_arg_names(arg_names)?;
91+
let values = arg_values.extract::<Vec<Py<PyAny>>>().ok()?;
92+
let expect_multiple = names.len() > 1;
93+
let parametrizations = values
94+
.into_iter()
95+
.map(|param| handle_custom_parametrize_param(py, param, expect_multiple))
96+
.collect();
97+
Some((names, parametrizations))
11498
}
11599

116100
/// Represents different argument names and values that can be given to a test.

0 commit comments

Comments
 (0)