22from __future__ import annotations
33
44import ast
5+ from itertools import chain
56from typing import TYPE_CHECKING , Optional
67
78import libcst as cst
@@ -119,6 +120,26 @@ def leave_Assign(self, original_node: cst.Assign, updated_node: cst.Assign) -> c
119120
120121 return updated_node
121122
123+ def _find_insertion_index (self , updated_node : cst .Module ) -> int :
124+ insert_index = 0
125+ for i , stmt in enumerate (updated_node .body ):
126+ is_top_level_import = isinstance (stmt , cst .SimpleStatementLine ) and any (
127+ isinstance (child , (cst .Import , cst .ImportFrom )) for child in stmt .body
128+ )
129+
130+ is_conditional_import = isinstance (stmt , cst .If ) and all (
131+ isinstance (inner , cst .SimpleStatementLine )
132+ and all (isinstance (child , (cst .Import , cst .ImportFrom )) for child in inner .body )
133+ for inner in stmt .body .body
134+ )
135+
136+ if is_top_level_import or is_conditional_import :
137+ insert_index = i + 1
138+ else :
139+ # stop when we find the first non-import statement
140+ break
141+ return insert_index
142+
122143 def leave_Module (self , original_node : cst .Module , updated_node : cst .Module ) -> cst .Module :
123144 # Add any new assignments that weren't in the original file
124145 new_statements = list (updated_node .body )
@@ -131,18 +152,24 @@ def leave_Module(self, original_node: cst.Module, updated_node: cst.Module) -> c
131152 ]
132153
133154 if assignments_to_append :
134- # Add a blank line before appending new assignments if needed
135- if new_statements and not isinstance (new_statements [- 1 ], cst .EmptyLine ):
136- new_statements .append (cst .SimpleStatementLine ([cst .Pass ()], leading_lines = [cst .EmptyLine ()]))
137- new_statements .pop () # Remove the Pass statement but keep the empty line
138-
139- # Add the new assignments
140- new_statements .extend (
141- [
142- cst .SimpleStatementLine ([assignment ], leading_lines = [cst .EmptyLine ()])
143- for assignment in assignments_to_append
144- ]
145- )
155+ # after last top-level imports
156+ insert_index = self ._find_insertion_index (updated_node )
157+
158+ assignment_lines = [
159+ cst .SimpleStatementLine ([assignment ], leading_lines = [cst .EmptyLine ()])
160+ for assignment in assignments_to_append
161+ ]
162+
163+ new_statements = list (chain (new_statements [:insert_index ], assignment_lines , new_statements [insert_index :]))
164+
165+ # Add a blank line after the last assignment if needed
166+ after_index = insert_index + len (assignment_lines )
167+ if after_index < len (new_statements ):
168+ next_statement = new_statements [after_index ]
169+ if not next_statement .leading_lines or not isinstance (next_statement .leading_lines [- 1 ], cst .EmptyLine ):
170+ new_statements [after_index ] = next_statement .with_changes (
171+ leading_lines = [cst .EmptyLine (), * next_statement .leading_lines ]
172+ )
146173
147174 return updated_node .with_changes (body = new_statements )
148175
0 commit comments