88
99from . import _parse_utils , parse , sorting , wrap , wrap_modes
1010from .comments import add_to_line as with_comments
11+ from .comments import parse as parse_comment
1112from .identify import STATEMENT_DECLARATIONS
1213from .place import module_with_reason
1314from .settings import DEFAULT_CONFIG , Config
@@ -320,10 +321,28 @@ def _inject_from_body_comments(
320321 return line_separator .join (lines )
321322
322323 # Single-line / no-paren fallback: main-compatible fold onto the statement.
324+ # Merge any trailing comment already on the statement (nested inline) after body
325+ # comments so we keep main's ``# body; nested`` shape instead of clobbering.
326+ if ignore_comments :
327+ return with_comments (
328+ body_comments ,
329+ import_statement ,
330+ removed = True ,
331+ comment_prefix = comment_prefix ,
332+ )
333+ _base , existing_comment = parse_comment (import_statement )
334+ # Drop spacing that used to precede an inline comment so re-attach is stable.
335+ _base = _base .rstrip ()
336+ merged = list (body_comments )
337+ if existing_comment :
338+ for part in existing_comment .split (";" ):
339+ part = part .strip ()
340+ if part and part not in merged :
341+ merged .append (part )
323342 return with_comments (
324- body_comments ,
325- import_statement ,
326- removed = ignore_comments ,
343+ merged ,
344+ _base ,
345+ removed = False ,
327346 comment_prefix = comment_prefix ,
328347 )
329348
@@ -434,15 +453,19 @@ def _with_from_imports(
434453 only_show_as_imports = True
435454 elif config .force_single_line and module not in config .single_line_exclusions :
436455 import_statement = ""
437- # Preserve comment-only members on the first single-line import
438- # (matches pre-#1852 main behaviour for force_single_line).
456+ # Pending comments (opening + body) must land on the first *emitted*
457+ # single-line statement. As-only modules never emit the bare name, so
458+ # folding into ``comments`` on that non-emitted line drops body text.
459+ pending_comments : list [str ] = list (comments or [])
439460 if body_comments and not config .ignore_comments :
440- comments = list ( comments or []) + body_comments
461+ pending_comments . extend ( body_comments )
441462 body_comments = []
463+ comments = None
442464 while from_imports :
443465 from_import = from_imports .pop (0 )
466+ line_comments = pending_comments or None
444467 single_import_line = with_comments (
445- comments ,
468+ line_comments ,
446469 import_start + from_import ,
447470 removed = config .ignore_comments ,
448471 comment_prefix = config .comment_prefix ,
@@ -453,52 +476,48 @@ def _with_from_imports(
453476 if comment is not None :
454477 comment_text = f" { comment } " if comment else ""
455478 single_import_line += (
456- f"{ (comments and ';' ) or config .comment_prefix } { comment_text } "
479+ f"{ (line_comments and ';' ) or config .comment_prefix } { comment_text } "
457480 )
458481 if from_import in as_imports :
482+ emitted_plain = False
459483 if (
460484 parsed .imports [section ][import_key ][module ][from_import ]
461485 and not only_show_as_imports
462486 ):
463487 output .append (
464488 wrap .line (single_import_line , parsed .line_separator , config )
465489 )
466- from_comments = parsed .categorized_comments ["straight" ].get (
467- f"{ module } .{ from_import } "
490+ pending_comments = []
491+ emitted_plain = True
492+ from_comments = list (
493+ parsed .categorized_comments ["straight" ].get (f"{ module } .{ from_import } " )
494+ or []
468495 )
469-
470- if not config .only_sections :
471- output .extend (
472- wrap .line (
473- with_comments (
474- from_comments ,
475- import_start + as_import ,
476- removed = config .ignore_comments ,
477- comment_prefix = config .comment_prefix ,
478- ),
479- parsed .line_separator ,
480- config ,
481- )
482- for as_import in sorting .sort (config , as_imports [from_import ])
483- )
484-
485- else :
486- output .extend (
496+ as_import_names = (
497+ sorting .sort (config , as_imports [from_import ])
498+ if not config .only_sections
499+ else list (as_imports [from_import ])
500+ )
501+ for index , as_import in enumerate (as_import_names ):
502+ as_line_comments = list (from_comments ) if index == 0 else []
503+ if index == 0 and pending_comments and not emitted_plain :
504+ as_line_comments = pending_comments + as_line_comments
505+ pending_comments = []
506+ output .append (
487507 wrap .line (
488508 with_comments (
489- from_comments ,
509+ as_line_comments or None ,
490510 import_start + as_import ,
491511 removed = config .ignore_comments ,
492512 comment_prefix = config .comment_prefix ,
493513 ),
494514 parsed .line_separator ,
495515 config ,
496516 )
497- for as_import in as_imports [from_import ]
498517 )
499518 else :
500519 output .append (wrap .line (single_import_line , parsed .line_separator , config ))
501- comments = None
520+ pending_comments = []
502521 else :
503522 # Tracks whether any aliased imports were emitted before the grouped
504523 # non-aliased imports in this pass of the outer loop. When True it
0 commit comments