def contains_uncollapsable_type_comments(self) -> bool: ignored_ids = set() try: last_leaf = self.leaves[-1] ignored_ids.add(id(last_leaf)) if last_leaf.type == token.COMMA or ( last_leaf.type == token.RPAR and not last_leaf.value ): # When trailing commas or optional parens are inserted by Black for # consistency, comments after the previous last element are not moved # (they don't have to, rendering will still be correct). So we ignore # trailing commas and invisible. last_leaf = self.leaves[-2] ignored_ids.add(id(last_leaf)) except IndexError: return False # A type comment is uncollapsable if it is attached to a leaf # that isn't at the end of the line (since that could cause it # to get associated to a different argument) or if there are # comments before it (since that could cause it to get hidden # behind a comment. comment_seen = False for leaf_id, comments in self.comments.items(): for comment in comments: if is_type_comment(comment): if comment_seen or ( not is_type_comment(comment, " ignore") and leaf_id not in ignored_ids ): return True comment_seen = True return False
def contains_unsplittable_type_ignore(self) -> bool: if not self.leaves: return False # If a 'type: ignore' is attached to the end of a line, we # can't split the line, because we can't know which of the # subexpressions the ignore was meant to apply to. # # We only want this to apply to actual physical lines from the # original source, though: we don't want the presence of a # 'type: ignore' at the end of a multiline expression to # justify pushing it all onto one line. Thus we # (unfortunately) need to check the actual source lines and # only report an unsplittable 'type: ignore' if this line was # one line in the original code. # Grab the first and last line numbers, skipping generated leaves first_line = next((leaf.lineno for leaf in self.leaves if leaf.lineno != 0), 0) last_line = next( (leaf.lineno for leaf in reversed(self.leaves) if leaf.lineno != 0), 0 ) if first_line == last_line: # We look at the last two leaves since a comma or an # invisible paren could have been added at the end of the # line. for node in self.leaves[-2:]: for comment in self.comments.get(id(node), []): if is_type_comment(comment, " ignore"): return True return False
def append_comment(self, comment: Leaf) -> bool: """Add an inline or standalone comment to the line.""" if (comment.type == STANDALONE_COMMENT and self.bracket_tracker.any_open_brackets()): comment.prefix = "" return False if comment.type != token.COMMENT: return False if not self.leaves: comment.type = STANDALONE_COMMENT comment.prefix = "" return False last_leaf = self.leaves[-1] if (last_leaf.type == token.RPAR and not last_leaf.value and last_leaf.parent and len(list(last_leaf.parent.leaves())) <= 3 and not is_type_comment(comment)): # Comments on an optional parens wrapping a single leaf should belong to # the wrapped node except if it's a type comment. Pinning the comment like # this avoids unstable formatting caused by comment migration. if len(self.leaves) < 2: comment.type = STANDALONE_COMMENT comment.prefix = "" return False last_leaf = self.leaves[-2] self.comments.setdefault(id(last_leaf), []).append(comment) return True