Ejemplo n.º 1
0
def sorted_imports(
    parsed: parse.ParsedContent,
    config: Config = DEFAULT_CONFIG,
    extension: str = "py",
    import_type: str = "import",
) -> str:
    """Adds the imports back to the file.

    (at the index of the first import) sorted alphabetically and split between groups

    """
    if parsed.import_index == -1:
        return _output_as_string(parsed.lines_without_imports,
                                 parsed.line_separator)

    formatted_output: List[str] = parsed.lines_without_imports.copy()
    remove_imports = [
        format_simplified(removal) for removal in config.remove_imports
    ]

    sections: Iterable[str] = itertools.chain(parsed.sections,
                                              config.forced_separate)

    if config.no_sections:
        parsed.imports["no_sections"] = {"straight": {}, "from": {}}
        base_sections: Tuple[str, ...] = ()
        for section in sections:
            if section == "FUTURE":
                base_sections = ("FUTURE", )
                continue
            parsed.imports["no_sections"]["straight"].update(
                parsed.imports[section].get("straight", {}))
            parsed.imports["no_sections"]["from"].update(
                parsed.imports[section].get("from", {}))
        sections = base_sections + ("no_sections", )

    output: List[str] = []
    seen_headings: Set[str] = set()
    pending_lines_before = False
    for section in sections:
        straight_modules = parsed.imports[section]["straight"]
        if not config.only_sections:
            straight_modules = sorting.naturally(
                straight_modules,
                key=lambda key: sorting.module_key(
                    key, config, section_name=section, straight_import=True),
            )

        from_modules = parsed.imports[section]["from"]
        if not config.only_sections:
            from_modules = sorting.naturally(
                from_modules,
                key=lambda key: sorting.module_key(
                    key, config, section_name=section))

        straight_imports = _with_straight_imports(parsed, config,
                                                  straight_modules, section,
                                                  remove_imports, import_type)
        from_imports = _with_from_imports(parsed, config, from_modules,
                                          section, remove_imports, import_type)

        lines_between = [""] * (config.lines_between_types
                                if from_modules and straight_modules else 0)
        if config.from_first:
            section_output = from_imports + lines_between + straight_imports
        else:
            section_output = straight_imports + lines_between + from_imports

        if config.force_sort_within_sections:
            # collapse comments
            comments_above = []
            new_section_output: List[str] = []
            for line in section_output:
                if not line:
                    continue
                if line.startswith("#"):
                    comments_above.append(line)
                elif comments_above:
                    new_section_output.append(
                        _LineWithComments(line, comments_above))
                    comments_above = []
                else:
                    new_section_output.append(line)
            # only_sections options is not imposed if force_sort_within_sections is True
            new_section_output = sorting.naturally(
                new_section_output,
                key=partial(
                    sorting.section_key,
                    order_by_type=config.order_by_type,
                    force_to_top=config.force_to_top,
                    lexicographical=config.lexicographical,
                    length_sort=config.length_sort,
                    reverse_relative=config.reverse_relative,
                    group_by_package=config.group_by_package,
                ),
            )

            # uncollapse comments
            section_output = []
            for line in new_section_output:
                comments = getattr(line, "comments", ())
                if comments:
                    section_output.extend(comments)
                section_output.append(str(line))

        section_name = section
        no_lines_before = section_name in config.no_lines_before

        if section_output:
            if section_name in parsed.place_imports:
                parsed.place_imports[section_name] = section_output
                continue

            section_title = config.import_headings.get(section_name.lower(),
                                                       "")
            if section_title and section_title not in seen_headings:
                if config.dedup_headings:
                    seen_headings.add(section_title)
                section_comment = f"# {section_title}"
                if section_comment not in parsed.lines_without_imports[0:1]:
                    section_output.insert(0, section_comment)

            if pending_lines_before or not no_lines_before:
                output += [""] * config.lines_between_sections

            output += section_output

            pending_lines_before = False
        else:
            pending_lines_before = pending_lines_before or not no_lines_before

    if config.ensure_newline_before_comments:
        output = _ensure_newline_before_comment(output)

    while output and output[-1].strip() == "":
        output.pop()  # pragma: no cover
    while output and output[0].strip() == "":
        output.pop(0)

    if config.formatting_function:
        output = config.formatting_function(parsed.line_separator.join(output),
                                            extension, config).splitlines()

    output_at = 0
    if parsed.import_index < parsed.original_line_count:
        output_at = parsed.import_index
    formatted_output[output_at:0] = output

    imports_tail = output_at + len(output)
    while [
            character.strip()
            for character in formatted_output[imports_tail:imports_tail + 1]
    ] == [""]:
        formatted_output.pop(imports_tail)

    if len(formatted_output) > imports_tail:
        next_construct = ""
        tail = formatted_output[imports_tail:]

        for index, line in enumerate(tail):
            should_skip, in_quote, *_ = parse.skip_line(
                line,
                in_quote="",
                index=len(formatted_output),
                section_comments=config.section_comments,
                needs_import=False,
            )
            if not should_skip and line.strip():
                if (line.strip().startswith("#") and len(tail) > (index + 1)
                        and tail[index + 1].strip()):
                    continue
                next_construct = line
                break
            elif in_quote:
                next_construct = line
                break

        if config.lines_after_imports != -1:
            formatted_output[imports_tail:0] = [
                "" for line in range(config.lines_after_imports)
            ]
        elif extension != "pyi" and next_construct.startswith(
                STATEMENT_DECLARATIONS):
            formatted_output[imports_tail:0] = ["", ""]
        else:
            formatted_output[imports_tail:0] = [""]

    if parsed.place_imports:
        new_out_lines = []
        for index, line in enumerate(formatted_output):
            new_out_lines.append(line)
            if line in parsed.import_placements:
                new_out_lines.extend(
                    parsed.place_imports[parsed.import_placements[line]])
                if (len(formatted_output) <= (index + 1)
                        or formatted_output[index + 1].strip() != ""):
                    new_out_lines.append("")
        formatted_output = new_out_lines

    return _output_as_string(formatted_output, parsed.line_separator)
Ejemplo n.º 2
0
def sorted_imports(
    parsed: parse.ParsedContent,
    config: Config = DEFAULT_CONFIG,
    extension: str = "py",
    import_type: str = "import",
) -> str:
    """Adds the imports back to the file.

    (at the index of the first import) sorted alphabetically and split between groups

    """
    if parsed.import_index == -1:
        return _output_as_string(parsed.lines_without_imports,
                                 parsed.line_separator)

    formatted_output: List[str] = parsed.lines_without_imports.copy()
    remove_imports = [
        format_simplified(removal) for removal in config.remove_imports
    ]

    sort_ignore_case = config.force_alphabetical_sort_within_sections
    sections: Iterable[str] = itertools.chain(parsed.sections,
                                              config.forced_separate)

    if config.no_sections:
        parsed.imports["no_sections"] = {"straight": {}, "from": {}}
        base_sections: Tuple[str, ...] = ()
        for section in sections:
            if section == "FUTURE":
                base_sections = ("FUTURE", )
                continue
            parsed.imports["no_sections"]["straight"].update(
                parsed.imports[section].get("straight", {}))
            parsed.imports["no_sections"]["from"].update(
                parsed.imports[section].get("from", {}))
        sections = base_sections + ("no_sections", )

    output: List[str] = []
    pending_lines_before = False
    for section in sections:
        straight_modules = parsed.imports[section]["straight"]
        straight_modules = sorting.naturally(
            straight_modules,
            key=lambda key: sorting.module_key(
                key, config, section_name=section))
        from_modules = parsed.imports[section]["from"]
        from_modules = sorting.naturally(
            from_modules,
            key=lambda key: sorting.module_key(
                key, config, section_name=section))

        if config.force_sort_within_sections:
            copied_comments = copy.deepcopy(parsed.categorized_comments)

        section_output: List[str] = []
        if config.from_first:
            section_output = _with_from_imports(
                parsed,
                config,
                from_modules,
                section,
                section_output,
                sort_ignore_case,
                remove_imports,
                import_type,
            )
            if config.lines_between_types and from_modules and straight_modules:
                section_output.extend([""] * config.lines_between_types)
            section_output = _with_straight_imports(
                parsed,
                config,
                straight_modules,
                section,
                section_output,
                remove_imports,
                import_type,
            )
        else:
            section_output = _with_straight_imports(
                parsed,
                config,
                straight_modules,
                section,
                section_output,
                remove_imports,
                import_type,
            )
            if config.lines_between_types and from_modules and straight_modules:
                section_output.extend([""] * config.lines_between_types)
            section_output = _with_from_imports(
                parsed,
                config,
                from_modules,
                section,
                section_output,
                sort_ignore_case,
                remove_imports,
                import_type,
            )

        if config.force_sort_within_sections:
            # Remove comments
            section_output = [
                line for line in section_output if not line.startswith("#")
            ]

            section_output = sorting.naturally(
                section_output,
                key=partial(
                    sorting.section_key,
                    order_by_type=config.order_by_type,
                    force_to_top=config.force_to_top,
                    lexicographical=config.lexicographical,
                    length_sort=config.length_sort,
                ),
            )

            # Add comments back
            all_comments = copied_comments["above"]["from"]
            all_comments.update(copied_comments["above"]["straight"])
            comment_indexes = {}
            for module, comment_list in all_comments.items():
                for idx, line in enumerate(section_output):
                    if module in line:
                        comment_indexes[idx] = comment_list
            added = 0
            for idx, comment_list in comment_indexes.items():
                for comment in comment_list:
                    section_output.insert(idx + added, comment)
                    added += 1

        section_name = section
        no_lines_before = section_name in config.no_lines_before

        if section_output:
            if section_name in parsed.place_imports:
                parsed.place_imports[section_name] = section_output
                continue

            section_title = config.import_headings.get(section_name.lower(),
                                                       "")
            if section_title:
                section_comment = f"# {section_title}"
                if section_comment not in parsed.lines_without_imports[0:1]:
                    section_output.insert(0, section_comment)

            if pending_lines_before or not no_lines_before:
                output += [""] * config.lines_between_sections

            output += section_output

            pending_lines_before = False
        else:
            pending_lines_before = pending_lines_before or not no_lines_before

    while output and output[-1].strip() == "":
        output.pop()  # pragma: no cover
    while output and output[0].strip() == "":
        output.pop(0)

    output_at = 0
    if parsed.import_index < parsed.original_line_count:
        output_at = parsed.import_index
    formatted_output[output_at:0] = output

    imports_tail = output_at + len(output)
    while [
            character.strip()
            for character in formatted_output[imports_tail:imports_tail + 1]
    ] == [""]:
        formatted_output.pop(imports_tail)

    if len(formatted_output) > imports_tail:
        next_construct = ""
        _in_quote: str = ""
        tail = formatted_output[imports_tail:]

        for index, line in enumerate(tail):
            in_quote = _in_quote
            should_skip, _in_quote, *_ = parse.skip_line(
                line,
                in_quote=_in_quote,
                index=len(formatted_output),
                section_comments=parsed.section_comments,
            )
            if not should_skip and line.strip():
                if (line.strip().startswith("#") and len(tail) > (index + 1)
                        and tail[index + 1].strip()):
                    continue
                next_construct = line
                break
            elif not in_quote:
                parts = line.split()
                if (len(parts) >= 3 and parts[1] == "=" and "'" not in parts[0]
                        and '"' not in parts[0]):
                    next_construct = line
                    break

        if config.lines_after_imports != -1:
            formatted_output[imports_tail:0] = [
                "" for line in range(config.lines_after_imports)
            ]
        elif extension != "pyi" and next_construct.startswith(
                STATEMENT_DECLERATIONS):
            formatted_output[imports_tail:0] = ["", ""]
        else:
            formatted_output[imports_tail:0] = [""]

    if parsed.place_imports:
        new_out_lines = []
        for index, line in enumerate(formatted_output):
            new_out_lines.append(line)
            if line in parsed.import_placements:
                new_out_lines.extend(
                    parsed.place_imports[parsed.import_placements[line]])
                if len(formatted_output) <= index or formatted_output[
                        index + 1].strip() != "":
                    new_out_lines.append("")
        formatted_output = new_out_lines

    return _output_as_string(formatted_output, parsed.line_separator)