◐ Shell
clean mode source ↗

Py 3.14 changes fix by youknowone · Pull Request #6755 · RustPython/RustPython

5936-5959: Conditional-annotation indices can desync from __annotate__ enumeration.
next_conditional_annotation_index is incremented for every simple annotated assignment compiled (including those nested in if/for/while/try/match), but compile_module_annotate enumerates only top-level annotations via collect_simple_annotations. This can shift indices and cause executed annotations to be omitted from __annotate__ (e.g., a conditional annotation before a top-level one).

Consider aligning enumeration with compilation order by recursively collecting simple annotations from nested bodies (or, alternatively, only increment the index for annotations that compile_module_annotate will enumerate).

🔧 Possible fix: recursively collect annotations in AST order
-    fn collect_simple_annotations(body: &[Stmt]) -> Vec<(&str, &Expr)> {
-        let mut annotations = Vec::new();
-        for stmt in body {
-            if let Stmt::AnnAssign(StmtAnnAssign {
-                target,
-                annotation,
-                simple,
-                ..
-            }) = stmt
-                && *simple
-                && let Expr::Name(ExprName { id, .. }) = target.as_ref()
-            {
-                annotations.push((id.as_str(), annotation.as_ref()));
-            }
-        }
-        annotations
-    }
+    fn collect_simple_annotations(body: &[Stmt]) -> Vec<(&str, &Expr)> {
+        fn walk<'a>(stmts: &'a [Stmt], out: &mut Vec<(&'a str, &'a Expr)>) {
+            for stmt in stmts {
+                match stmt {
+                    Stmt::AnnAssign(StmtAnnAssign { target, annotation, simple, .. })
+                        if *simple && matches!(target.as_ref(), Expr::Name(_)) =>
+                    {
+                        if let Expr::Name(ExprName { id, .. }) = target.as_ref() {
+                            out.push((id.as_str(), annotation.as_ref()));
+                        }
+                    }
+                    Stmt::If(StmtIf { body, elif_else_clauses, .. }) => {
+                        walk(body, out);
+                        for clause in elif_else_clauses {
+                            walk(&clause.body, out);
+                        }
+                    }
+                    Stmt::For(StmtFor { body, orelse, .. })
+                    | Stmt::While(StmtWhile { body, orelse, .. }) => {
+                        walk(body, out);
+                        walk(orelse, out);
+                    }
+                    Stmt::With(StmtWith { body, .. }) => walk(body, out),
+                    Stmt::Try(StmtTry { body, handlers, orelse, finalbody, .. }) => {
+                        walk(body, out);
+                        for handler in handlers {
+                            if let ExceptHandler::ExceptHandler(ExceptHandlerExceptHandler { body, .. }) = handler {
+                                walk(body, out);
+                            }
+                        }
+                        walk(orelse, out);
+                        walk(finalbody, out);
+                    }
+                    Stmt::Match(StmtMatch { cases, .. }) => {
+                        for case in cases {
+                            walk(&case.body, out);
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+        let mut annotations = Vec::new();
+        walk(body, &mut annotations);
+        annotations
+    }