Skip to content

Commit 05f9ab9

Browse files
timsaucerclaude
andcommitted
docs: add window function literal detection to make-pythonic skill
Add Technique 1b to detect literal-only arguments in window functions. Window functions enforce literals in partition_evaluator() via get_scalar_value_from_args() / downcast_ref::<Literal>(), not in invoke_with_args() (scalar) or accumulator() (aggregate). Updates the decision flow to branch on scalar vs aggregate vs window. Known window functions with literal-only arguments: ntile (n), lead/lag (offset, default_value), nth_value (n). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent b245515 commit 05f9ab9

1 file changed

Lines changed: 43 additions & 2 deletions

File tree

.ai/skills/make-pythonic/SKILL.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ For **aggregate functions**, the upstream source is in a separate crate:
6464
~/.cargo/registry/src/index.crates.io-*/datafusion-functions-aggregate-<VERSION>/src/
6565
```
6666

67-
There are four concrete techniques to check, in order of signal strength:
67+
There are five concrete techniques to check, in order of signal strength:
6868

6969
#### Technique 1: Check `invoke_with_args()` for literal-only enforcement (strongest signal)
7070

@@ -123,6 +123,41 @@ Known aggregate functions with literal-only arguments:
123123
- `string_agg``delimiter` (str)
124124
- `nth_value``n` (int)
125125

126+
#### Technique 1b: Check `partition_evaluator()` for literal-only enforcement (window functions)
127+
128+
Window functions do not have `invoke_with_args()` or `accumulator()`. Instead, they enforce literal-only arguments in their `partition_evaluator()` method, which constructs the evaluator that processes each partition.
129+
130+
The upstream source is in a separate crate:
131+
132+
```
133+
~/.cargo/registry/src/index.crates.io-*/datafusion-functions-window-<VERSION>/src/
134+
```
135+
136+
Look for `get_scalar_value_from_args()` calls inside `partition_evaluator()`. This helper (defined in the window crate's `utils.rs`) calls `downcast_ref::<Literal>()` and errors with `"There is only support Literal types for field at idx: {index} in Window Function"`.
137+
138+
Example from `ntile.rs`:
139+
```rust
140+
fn partition_evaluator(
141+
&self,
142+
partition_evaluator_args: PartitionEvaluatorArgs,
143+
) -> Result<Box<dyn PartitionEvaluator>> {
144+
let scalar_n =
145+
get_scalar_value_from_args(partition_evaluator_args.input_exprs(), 0)?
146+
.ok_or_else(|| {
147+
exec_datafusion_err!("NTILE requires a positive integer")
148+
})?;
149+
// ...
150+
}
151+
```
152+
153+
**If you find this pattern:** The argument is **Category B** — accept only the corresponding native Python type, not `Expr`. The function will error at planning time with a non-literal expression.
154+
155+
Known window functions with literal-only arguments:
156+
- `ntile``n` (int)
157+
- `lead``offset` (int), `default_value` (scalar)
158+
- `lag``offset` (int), `default_value` (scalar)
159+
- `nth_value``n` (int)
160+
126161
#### Technique 2: Check the `Signature` for data type constraints
127162

128163
Each function defines a `Signature::coercible(...)` that specifies what data types each argument accepts, using `Coercion` entries. This tells you the expected **data type** even if it doesn't enforce literal-only.
@@ -173,7 +208,7 @@ let decimal_places: Option<i32> = match args.scalar_arguments.get(1) {
173208
#### Decision flow
174209

175210
```
176-
Is the function a scalar UDF or an aggregate?
211+
What kind of function is this?
177212
Scalar UDF:
178213
Is argument rejected at runtime if not a literal?
179214
(check invoke_with_args for ColumnarValue::Scalar-only match + exec_err!)
@@ -185,6 +220,12 @@ Is the function a scalar UDF or an aggregate?
185220
downcast_ref::<Literal>() + error)
186221
→ YES: Category B — accept only native type, no Expr
187222
→ NO: continue below
223+
Window:
224+
Is argument rejected at planning time if not a literal?
225+
(check partition_evaluator() for get_scalar_value_from_args /
226+
downcast_ref::<Literal>() + error)
227+
→ YES: Category B — accept only native type, no Expr
228+
→ NO: continue below
188229
189230
Does the Signature constrain it to a specific data type?
190231
→ YES: Category A — accept Expr | <native type matching the constraint>

0 commit comments

Comments
 (0)