Skip to content

Module isort.parse

Defines parsing functions used by isort for parsing import definitions

None

View Source
"""Defines parsing functions used by isort for parsing import definitions"""

from collections import OrderedDict, defaultdict

from functools import partial

from itertools import chain

from typing import TYPE_CHECKING, Any, Dict, List, NamedTuple, Optional, Set, Tuple

from warnings import warn

from . import place

from .comments import parse as parse_comments

from .exceptions import MissingSection

from .settings import DEFAULT_CONFIG, Config

if TYPE_CHECKING:

    from mypy_extensions import TypedDict

    CommentsAboveDict = TypedDict(

        "CommentsAboveDict", {"straight": Dict[str, Any], "from": Dict[str, Any]}

    )

    CommentsDict = TypedDict(

        "CommentsDict",

        {

            "from": Dict[str, Any],

            "straight": Dict[str, Any],

            "nested": Dict[str, Any],

            "above": CommentsAboveDict,

        },

    )

def _infer_line_separator(contents: str) -> str:

    if "\r\n" in contents:

        return "\r\n"

    if "\r" in contents:

        return "\r"

    return "\n"

def _normalize_line(raw_line: str) -> Tuple[str, str]:

    """Normalizes import related statements in the provided line.

    Returns (normalized_line: str, raw_line: str)

    """

    line = raw_line.replace("from.import ", "from . import ")

    line = line.replace("from.cimport ", "from . cimport ")

    line = line.replace("import*", "import *")

    line = line.replace(" .import ", " . import ")

    line = line.replace(" .cimport ", " . cimport ")

    line = line.replace("\t", " ")

    return (line, raw_line)

def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]:

    """If the current line is an import line it will return its type (from or straight)"""

    if config.honor_noqa and line.lower().rstrip().endswith("noqa"):

        return None

    if "isort:skip" in line or "isort: skip" in line or "isort: split" in line:

        return None

    if line.startswith(("import ", "cimport ")):

        return "straight"

    if line.startswith("from "):

        return "from"

    return None

def _strip_syntax(import_string: str) -> str:

    import_string = import_string.replace("_import", "[[i]]")

    import_string = import_string.replace("_cimport", "[[ci]]")

    for remove_syntax in ["\\", "(", ")", ","]:

        import_string = import_string.replace(remove_syntax, " ")

    import_list = import_string.split()

    for key in ("from", "import", "cimport"):

        if key in import_list:

            import_list.remove(key)

    import_string = " ".join(import_list)

    import_string = import_string.replace("[[i]]", "_import")

    import_string = import_string.replace("[[ci]]", "_cimport")

    return import_string.replace("{ ", "{|").replace(" }", "|}")

def skip_line(

    line: str,

    in_quote: str,

    index: int,

    section_comments: Tuple[str, ...],

    needs_import: bool = True,

) -> Tuple[bool, str]:

    """Determine if a given line should be skipped.

    Returns back a tuple containing:

    (skip_line: bool,

     in_quote: str,)

    """

    should_skip = bool(in_quote)

    if '"' in line or "'" in line:

        char_index = 0

        while char_index < len(line):

            if line[char_index] == "\\":

                char_index += 1

            elif in_quote:

                if line[char_index : char_index + len(in_quote)] == in_quote:

                    in_quote = ""

            elif line[char_index] in ("'", '"'):

                long_quote = line[char_index : char_index + 3]

                if long_quote in ('"""', "'''"):

                    in_quote = long_quote

                    char_index += 2

                else:

                    in_quote = line[char_index]

            elif line[char_index] == "#":

                break

            char_index += 1

    if ";" in line.split("#")[0] and needs_import:

        for part in (part.strip() for part in line.split(";")):

            if (

                part

                and not part.startswith("from ")

                and not part.startswith(("import ", "cimport "))

            ):

                should_skip = True

    return (bool(should_skip or in_quote), in_quote)

class ParsedContent(NamedTuple):

    in_lines: List[str]

    lines_without_imports: List[str]

    import_index: int

    place_imports: Dict[str, List[str]]

    import_placements: Dict[str, str]

    as_map: Dict[str, Dict[str, List[str]]]

    imports: Dict[str, Dict[str, Any]]

    categorized_comments: "CommentsDict"

    change_count: int

    original_line_count: int

    line_separator: str

    sections: Any

    verbose_output: List[str]

    trailing_commas: Set[str]

def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent:

    """Parses a python file taking out and categorizing imports."""

    line_separator: str = config.line_ending or _infer_line_separator(contents)

    in_lines = contents.splitlines()

    if contents and contents[-1] in ("\n", "\r"):

        in_lines.append("")

    out_lines = []

    original_line_count = len(in_lines)

    if config.old_finders:

        from .deprecated.finders import FindersManager

        finder = FindersManager(config=config).find

    else:

        finder = partial(place.module, config=config)

    line_count = len(in_lines)

    place_imports: Dict[str, List[str]] = {}

    import_placements: Dict[str, str] = {}

    as_map: Dict[str, Dict[str, List[str]]] = {

        "straight": defaultdict(list),

        "from": defaultdict(list),

    }

    imports: OrderedDict[str, Dict[str, Any]] = OrderedDict()

    verbose_output: List[str] = []

    for section in chain(config.sections, config.forced_separate):

        imports[section] = {"straight": OrderedDict(), "from": OrderedDict()}

    categorized_comments: CommentsDict = {

        "from": {},

        "straight": {},

        "nested": {},

        "above": {"straight": {}, "from": {}},

    }

    trailing_commas: Set[str] = set()

    index = 0

    import_index = -1

    in_quote = ""

    while index < line_count:

        line = in_lines[index]

        index += 1

        statement_index = index

        (skipping_line, in_quote) = skip_line(

            line, in_quote=in_quote, index=index, section_comments=config.section_comments

        )

        if (

            line in config.section_comments or line in config.section_comments_end

        ) and not skipping_line:

            if import_index == -1:  # pragma: no branch

                import_index = index - 1

            continue

        if "isort:imports-" in line and line.startswith("#"):

            section = line.split("isort:imports-")[-1].split()[0].upper()

            place_imports[section] = []

            import_placements[line] = section

        elif "isort: imports-" in line and line.startswith("#"):

            section = line.split("isort: imports-")[-1].split()[0].upper()

            place_imports[section] = []

            import_placements[line] = section

        if skipping_line:

            out_lines.append(line)

            continue

        lstripped_line = line.lstrip()

        if (

            config.float_to_top

            and import_index == -1

            and line

            and not in_quote

            and not lstripped_line.startswith("#")

            and not lstripped_line.startswith("'''")

            and not lstripped_line.startswith('"""')

        ):

            if not lstripped_line.startswith("import") and not lstripped_line.startswith("from"):

                import_index = index - 1

                while import_index and not in_lines[import_index - 1]:

                    import_index -= 1

            else:

                commentless = line.split("#", 1)[0].strip()

                if (

                    ("isort:skip" in line or "isort: skip" in line)

                    and "(" in commentless

                    and ")" not in commentless

                ):

                    import_index = index

                    starting_line = line

                    while "isort:skip" in starting_line or "isort: skip" in starting_line:

                        commentless = starting_line.split("#", 1)[0]

                        if (

                            "(" in commentless

                            and not commentless.rstrip().endswith(")")

                            and import_index < line_count

                        ):

                            while import_index < line_count and not commentless.rstrip().endswith(

                                ")"

                            ):

                                commentless = in_lines[import_index].split("#", 1)[0]

                                import_index += 1

                        else:

                            import_index += 1

                        if import_index >= line_count:

                            break

                        starting_line = in_lines[import_index]

        line, *end_of_line_comment = line.split("#", 1)

        if ";" in line:

            statements = [line.strip() for line in line.split(";")]

        else:

            statements = [line]

        if end_of_line_comment:

            statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}"

        for statement in statements:

            line, raw_line = _normalize_line(statement)

            type_of_import = import_type(line, config) or ""

            raw_lines = [raw_line]

            if not type_of_import:

                out_lines.append(raw_line)

                continue

            if import_index == -1:

                import_index = index - 1

            nested_comments = {}

            import_string, comment = parse_comments(line)

            comments = [comment] if comment else []

            line_parts = [part for part in _strip_syntax(import_string).strip().split(" ") if part]

            if type_of_import == "from" and len(line_parts) == 2 and comments:

                nested_comments[line_parts[-1]] = comments[0]

            if "(" in line.split("#", 1)[0] and index < line_count:

                while not line.split("#")[0].strip().endswith(")") and index < line_count:

                    line, new_comment = parse_comments(in_lines[index])

                    index += 1

                    if new_comment:

                        comments.append(new_comment)

                    stripped_line = _strip_syntax(line).strip()

                    if (

                        type_of_import == "from"

                        and stripped_line

                        and " " not in stripped_line.replace(" as ", "")

                        and new_comment

                    ):

                        nested_comments[stripped_line] = comments[-1]

                    import_string += line_separator + line

                    raw_lines.append(line)

            else:

                while line.strip().endswith("\\"):

                    line, new_comment = parse_comments(in_lines[index])

                    line = line.lstrip()

                    index += 1

                    if new_comment:

                        comments.append(new_comment)

                    # Still need to check for parentheses after an escaped line

                    if (

                        "(" in line.split("#")[0]

                        and ")" not in line.split("#")[0]

                        and index < line_count

                    ):

                        stripped_line = _strip_syntax(line).strip()

                        if (

                            type_of_import == "from"

                            and stripped_line

                            and " " not in stripped_line.replace(" as ", "")

                            and new_comment

                        ):

                            nested_comments[stripped_line] = comments[-1]

                        import_string += line_separator + line

                        raw_lines.append(line)

                        while not line.split("#")[0].strip().endswith(")") and index < line_count:

                            line, new_comment = parse_comments(in_lines[index])

                            index += 1

                            if new_comment:

                                comments.append(new_comment)

                            stripped_line = _strip_syntax(line).strip()

                            if (

                                type_of_import == "from"

                                and stripped_line

                                and " " not in stripped_line.replace(" as ", "")

                                and new_comment

                            ):

                                nested_comments[stripped_line] = comments[-1]

                            import_string += line_separator + line

                            raw_lines.append(line)

                    stripped_line = _strip_syntax(line).strip()

                    if (

                        type_of_import == "from"

                        and stripped_line

                        and " " not in stripped_line.replace(" as ", "")

                        and new_comment

                    ):

                        nested_comments[stripped_line] = comments[-1]

                    if import_string.strip().endswith(

                        (" import", " cimport")

                    ) or line.strip().startswith(("import ", "cimport ")):

                        import_string += line_separator + line

                    else:

                        import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip()

            if type_of_import == "from":

                cimports: bool

                import_string = (

                    import_string.replace("import(", "import (")

                    .replace("\\", " ")

                    .replace("\n", " ")

                )

                if "import " not in import_string:

                    out_lines.extend(raw_lines)

                    continue

                if " cimport " in import_string:

                    parts = import_string.split(" cimport ")

                    cimports = True

                else:

                    parts = import_string.split(" import ")

                    cimports = False

                from_import = parts[0].split(" ")

                import_string = (" cimport " if cimports else " import ").join(

                    [from_import[0] + " " + "".join(from_import[1:])] + parts[1:]

                )

            just_imports = [

                item.replace("{|", "{ ").replace("|}", " }")

                for item in _strip_syntax(import_string).split()

            ]

            attach_comments_to: Optional[List[Any]] = None

            direct_imports = just_imports[1:]

            straight_import = True

            top_level_module = ""

            if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports):

                straight_import = False

                while "as" in just_imports:

                    nested_module = None

                    as_index = just_imports.index("as")

                    if type_of_import == "from":

                        nested_module = just_imports[as_index - 1]

                        top_level_module = just_imports[0]

                        module = top_level_module + "." + nested_module

                        as_name = just_imports[as_index + 1]

                        direct_imports.remove(nested_module)

                        direct_imports.remove(as_name)

                        direct_imports.remove("as")

                        if nested_module == as_name and config.remove_redundant_aliases:

                            pass

                        elif as_name not in as_map["from"][module]:  # pragma: no branch

                            as_map["from"][module].append(as_name)

                        full_name = f"{nested_module} as {as_name}"

                        associated_comment = nested_comments.get(full_name)

                        if associated_comment:

                            categorized_comments["nested"].setdefault(top_level_module, {})[

                                full_name

                            ] = associated_comment

                            if associated_comment in comments:  # pragma: no branch

                                comments.pop(comments.index(associated_comment))

                    else:

                        module = just_imports[as_index - 1]

                        as_name = just_imports[as_index + 1]

                        if module == as_name and config.remove_redundant_aliases:

                            pass

                        elif as_name not in as_map["straight"][module]:

                            as_map["straight"][module].append(as_name)

                    if comments and attach_comments_to is None:

                        if nested_module and config.combine_as_imports:

                            attach_comments_to = categorized_comments["from"].setdefault(

                                f"{top_level_module}.__combined_as__", []

                            )

                        else:

                            if type_of_import == "from" or (

                                config.remove_redundant_aliases and as_name == module.split(".")[-1]

                            ):

                                attach_comments_to = categorized_comments["straight"].setdefault(

                                    module, []

                                )

                            else:

                                attach_comments_to = categorized_comments["straight"].setdefault(

                                    f"{module} as {as_name}", []

                                )

                    del just_imports[as_index : as_index + 2]

            if type_of_import == "from":

                import_from = just_imports.pop(0)

                placed_module = finder(import_from)

                if config.verbose and not config.only_modified:

                    print(f"from-type place_module for {import_from} returned {placed_module}")

                elif config.verbose:

                    verbose_output.append(

                        f"from-type place_module for {import_from} returned {placed_module}"

                    )

                if placed_module == "":

                    warn(

                        f"could not place module {import_from} of line {line} --"

                        " Do you need to define a default section?"

                    )

                if placed_module and placed_module not in imports:

                    raise MissingSection(import_module=import_from, section=placed_module)

                root = imports[placed_module][type_of_import]  # type: ignore

                for import_name in just_imports:

                    associated_comment = nested_comments.get(import_name)

                    if associated_comment:

                        categorized_comments["nested"].setdefault(import_from, {})[

                            import_name

                        ] = associated_comment

                        if associated_comment in comments:  # pragma: no branch

                            comments.pop(comments.index(associated_comment))

                if (

                    config.force_single_line

                    and comments

                    and attach_comments_to is None

                    and len(just_imports) == 1

                ):

                    nested_from_comments = categorized_comments["nested"].setdefault(

                        import_from, {}

                    )

                    existing_comment = nested_from_comments.get(just_imports[0], "")

                    nested_from_comments[

                        just_imports[0]

                    ] = f"{existing_comment}{'; ' if existing_comment else ''}{'; '.join(comments)}"

                    comments = []

                if comments and attach_comments_to is None:

                    attach_comments_to = categorized_comments["from"].setdefault(import_from, [])

                if len(out_lines) > max(import_index, 1) - 1:

                    last = out_lines[-1].rstrip() if out_lines else ""

                    while (

                        last.startswith("#")

                        and not last.endswith('"""')

                        and not last.endswith("'''")

                        and "isort:imports-" not in last

                        and "isort: imports-" not in last

                        and not config.treat_all_comments_as_code

                        and not last.strip() in config.treat_comments_as_code

                    ):

                        categorized_comments["above"]["from"].setdefault(import_from, []).insert(

                            0, out_lines.pop(-1)

                        )

                        if out_lines:

                            last = out_lines[-1].rstrip()

                        else:

                            last = ""

                    if statement_index - 1 == import_index:  # pragma: no cover

                        import_index -= len(

                            categorized_comments["above"]["from"].get(import_from, [])

                        )

                if import_from not in root:

                    root[import_from] = OrderedDict(

                        (module, module in direct_imports) for module in just_imports

                    )

                else:

                    root[import_from].update(

                        (module, root[import_from].get(module, False) or module in direct_imports)

                        for module in just_imports

                    )

                if comments and attach_comments_to is not None:

                    attach_comments_to.extend(comments)

                if "," in import_string.split(just_imports[-1])[-1]:

                    trailing_commas.add(import_from)

            else:

                if comments and attach_comments_to is not None:

                    attach_comments_to.extend(comments)

                    comments = []

                for module in just_imports:

                    if comments:

                        categorized_comments["straight"][module] = comments

                        comments = []

                    if len(out_lines) > max(import_index, +1, 1) - 1:

                        last = out_lines[-1].rstrip() if out_lines else ""

                        while (

                            last.startswith("#")

                            and not last.endswith('"""')

                            and not last.endswith("'''")

                            and "isort:imports-" not in last

                            and "isort: imports-" not in last

                            and not config.treat_all_comments_as_code

                            and not last.strip() in config.treat_comments_as_code

                        ):

                            categorized_comments["above"]["straight"].setdefault(module, []).insert(

                                0, out_lines.pop(-1)

                            )

                            if out_lines:

                                last = out_lines[-1].rstrip()

                            else:

                                last = ""

                        if index - 1 == import_index:

                            import_index -= len(

                                categorized_comments["above"]["straight"].get(module, [])

                            )

                    placed_module = finder(module)

                    if config.verbose and not config.only_modified:

                        print(f"else-type place_module for {module} returned {placed_module}")

                    elif config.verbose:

                        verbose_output.append(

                            f"else-type place_module for {module} returned {placed_module}"

                        )

                    if placed_module == "":

                        warn(

                            f"could not place module {module} of line {line} --"

                            " Do you need to define a default section?"

                        )

                        imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()})

                    if placed_module and placed_module not in imports:

                        raise MissingSection(import_module=module, section=placed_module)

                    straight_import |= imports[placed_module][type_of_import].get(  # type: ignore

                        module, False

                    )

                    imports[placed_module][type_of_import][module] = straight_import  # type: ignore

    change_count = len(out_lines) - original_line_count

    return ParsedContent(

        in_lines=in_lines,

        lines_without_imports=out_lines,

        import_index=import_index,

        place_imports=place_imports,

        import_placements=import_placements,

        as_map=as_map,

        imports=imports,

        categorized_comments=categorized_comments,

        change_count=change_count,

        original_line_count=original_line_count,

        line_separator=line_separator,

        sections=config.sections,

        verbose_output=verbose_output,

        trailing_commas=trailing_commas,

    )

Variables

TYPE_CHECKING

Functions

file_contents

def file_contents(
    contents: str,
    config: isort.settings.Config = Config(py_version='py3', force_to_top=frozenset(), skip=frozenset({'build', '.pants.d', '.mypy_cache', 'buck-out', '.nox', '.eggs', '.git', 'venv', '.tox', '.direnv', 'dist', '.venv', '.hg', '__pypackages__', '.bzr', 'node_modules', '_build', '.svn'}), extend_skip=frozenset(), skip_glob=frozenset(), extend_skip_glob=frozenset(), skip_gitignore=False, line_length=79, wrap_length=0, line_ending='', sections=('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), no_sections=False, known_future_library=frozenset({'__future__'}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset({'getopt', 'mailcap', 'imaplib', 'nntplib', 'filecmp', 'threading', 'fnmatch', 'timeit', 'sqlite3', 'configparser', 'bdb', 'runpy', 'chunk', 'curses', 'ntpath', 'posixpath', 'platform', 'html', 'fractions', 'tomllib', 'token', 'zlib', 'code', 'poplib', 'fcntl', 'numbers', 'select', 'codecs', 'binascii', 'weakref', 'zipfile', 'distutils', 'secrets', 'sunau', '_ast', 'smtpd', 'dataclasses', 'http', 'gzip', 'posix', 'functools', 'zoneinfo', 'inspect', 'cgitb', 'symtable', 'pathlib', 'unittest', 'itertools', 'operator', 'fpectl', 'linecache', 'encodings', 'sys', 'resource', 'errno', 'telnetlib', 'traceback', 'idlelib', 'warnings', 'sre', 'io', 'cgi', 'email', 'sre_parse', 'dbm', 'pickletools', 'wsgiref', 'turtledemo', 'optparse', 'concurrent', 'string', 'statistics', 'subprocess', 'modulefinder', 'mailbox', 'lib2to3', 'colorsys', 'signal', 'time', 'shelve', 'calendar', 'pipes', 'importlib', 'unicodedata', 'gc', 'crypt', 'msvcrt', 'csv', 'ossaudiodev', 'msilib', 'pickle', 'hashlib', 'nis', 'aifc', 'hmac', 'winsound', 'graphlib', 'zipapp', 'typing', 'contextvars', 'struct', 'netrc', 'turtle', 'dummy_threading', 'cmath', 'array', 'copy', 'pydoc', 'pstats', 'grp', 'tarfile', 'copyreg', 'uu', 'zipimport', 'tabnanny', 'pyclbr', 'getpass', 'keyword', 'mmap', 'spwd', 'codeop', 'sre_compile', 'dis', 'sysconfig', 'atexit', 'venv', 'argparse', 'syslog', 'rlcompleter', 'xdrlib', 'faulthandler', 'lzma', 'macpath', 'enum', 'abc', 'ctypes', 'pprint', '_dummy_thread', 'test', 'socketserver', 'marshal', 'xml', 'binhex', 'socket', 'sched', 'textwrap', 'asyncio', 'webbrowser', 'urllib', 'pwd', 'bz2', 'cmd', 'ipaddress', 'os', 'xmlrpc', 'plistlib', 'fileinput', 'bisect', 'trace', 'gettext', 'contextlib', 'collections', 'ensurepip', 'mimetypes', 'ast', 'sndhdr', 'asyncore', 'difflib', 'tty', 'multiprocessing', 'site', '_thread', 'pkgutil', 'readline', 'stat', 'tokenize', 'smtplib', 'heapq', 'queue', 'shutil', 'termios', 'quopri', 'types', 'profile', 'tkinter', 'symbol', 'cProfile', 'sre_constants', 'formatter', 'locale', 'ssl', 'tempfile', 'stringprep', 'random', 'datetime', 'compileall', 'asynchat', 'tracemalloc', 'imp', 'decimal', 'imghdr', 'py_compile', 'pdb', 'shlex', 'selectors', 'wave', 'reprlib', 'winreg', 'logging', 'math', 'glob', 're', 'audioop', 'ftplib', 'base64', 'parser', 'pty', 'doctest', 'uuid', 'builtins', 'json'}), extra_standard_library=frozenset(), known_other={}, multi_line_output=<WrapModes.GRID: 0>, forced_separate=(), indent='    ', comment_prefix='  #', length_sort=False, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset(), append_only=False, reverse_relative=False, force_single_line=False, single_line_exclusions=(), default_section='THIRDPARTY', import_headings={}, import_footers={}, balanced_wrapping=False, use_parentheses=False, order_by_type=True, atomic=False, lines_before_imports=-1, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=False, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, group_by_package=False, ignore_whitespace=False, no_lines_before=frozenset(), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=({'py_version': 'py3', 'force_to_top': frozenset(), 'skip': frozenset({'build', '.pants.d', '.mypy_cache', 'buck-out', '.nox', '.eggs', '.git', 'venv', '.tox', '.direnv', 'dist', '.venv', '.hg', '__pypackages__', '.bzr', 'node_modules', '_build', '.svn'}), 'extend_skip': frozenset(), 'skip_glob': frozenset(), 'extend_skip_glob': frozenset(), 'skip_gitignore': False, 'line_length': 79, 'wrap_length': 0, 'line_ending': '', 'sections': ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), 'no_sections': False, 'known_future_library': frozenset({'__future__'}), 'known_third_party': frozenset(), 'known_first_party': frozenset(), 'known_local_folder': frozenset(), 'known_standard_library': frozenset({'getopt', 'mailcap', 'imaplib', 'nntplib', 'filecmp', 'threading', 'fnmatch', 'timeit', 'sqlite3', 'configparser', 'bdb', 'runpy', 'chunk', 'curses', 'ntpath', 'posixpath', 'platform', 'html', 'fractions', 'tomllib', 'token', 'zlib', 'code', 'poplib', 'fcntl', 'numbers', 'select', 'codecs', 'binascii', 'weakref', 'zipfile', 'distutils', 'secrets', 'sunau', '_ast', 'smtpd', 'dataclasses', 'http', 'gzip', 'posix', 'functools', 'zoneinfo', 'inspect', 'cgitb', 'symtable', 'pathlib', 'unittest', 'itertools', 'operator', 'fpectl', 'linecache', 'encodings', 'sys', 'resource', 'errno', 'telnetlib', 'traceback', 'idlelib', 'warnings', 'sre', 'io', 'cgi', 'email', 'sre_parse', 'dbm', 'pickletools', 'wsgiref', 'turtledemo', 'optparse', 'concurrent', 'string', 'statistics', 'subprocess', 'modulefinder', 'mailbox', 'lib2to3', 'colorsys', 'signal', 'time', 'shelve', 'calendar', 'pipes', 'importlib', 'unicodedata', 'gc', 'crypt', 'msvcrt', 'csv', 'ossaudiodev', 'msilib', 'pickle', 'hashlib', 'nis', 'aifc', 'hmac', 'winsound', 'graphlib', 'zipapp', 'typing', 'contextvars', 'struct', 'netrc', 'turtle', 'dummy_threading', 'cmath', 'array', 'copy', 'pydoc', 'pstats', 'grp', 'tarfile', 'copyreg', 'uu', 'zipimport', 'tabnanny', 'pyclbr', 'getpass', 'keyword', 'mmap', 'spwd', 'codeop', 'sre_compile', 'dis', 'sysconfig', 'atexit', 'venv', 'argparse', 'syslog', 'rlcompleter', 'xdrlib', 'faulthandler', 'lzma', 'macpath', 'enum', 'abc', 'ctypes', 'pprint', '_dummy_thread', 'test', 'socketserver', 'marshal', 'xml', 'binhex', 'socket', 'sched', 'textwrap', 'asyncio', 'webbrowser', 'urllib', 'pwd', 'bz2', 'cmd', 'ipaddress', 'os', 'xmlrpc', 'plistlib', 'fileinput', 'bisect', 'trace', 'gettext', 'contextlib', 'collections', 'ensurepip', 'mimetypes', 'ast', 'sndhdr', 'asyncore', 'difflib', 'tty', 'multiprocessing', 'site', '_thread', 'pkgutil', 'readline', 'stat', 'tokenize', 'smtplib', 'heapq', 'queue', 'shutil', 'termios', 'quopri', 'types', 'profile', 'tkinter', 'symbol', 'cProfile', 'sre_constants', 'formatter', 'locale', 'ssl', 'tempfile', 'stringprep', 'random', 'datetime', 'compileall', 'asynchat', 'tracemalloc', 'imp', 'decimal', 'imghdr', 'py_compile', 'pdb', 'shlex', 'selectors', 'wave', 'reprlib', 'winreg', 'logging', 'math', 'glob', 're', 'audioop', 'ftplib', 'base64', 'parser', 'pty', 'doctest', 'uuid', 'builtins', 'json'}), 'extra_standard_library': frozenset(), 'known_other': {}, 'multi_line_output': <WrapModes.GRID: 0>, 'forced_separate': (), 'indent': '    ', 'comment_prefix': '  #', 'length_sort': False, 'length_sort_straight': False, 'length_sort_sections': frozenset(), 'add_imports': frozenset(), 'remove_imports': frozenset(), 'append_only': False, 'reverse_relative': False, 'force_single_line': False, 'single_line_exclusions': (), 'default_section': 'THIRDPARTY', 'import_headings': {}, 'import_footers': {}, 'balanced_wrapping': False, 'use_parentheses': False, 'order_by_type': True, 'atomic': False, 'lines_before_imports': -1, 'lines_after_imports': -1, 'lines_between_sections': 1, 'lines_between_types': 0, 'combine_as_imports': False, 'combine_star': False, 'include_trailing_comma': False, 'from_first': False, 'verbose': False, 'quiet': False, 'force_adds': False, 'force_alphabetical_sort_within_sections': False, 'force_alphabetical_sort': False, 'force_grid_wrap': 0, 'force_sort_within_sections': False, 'lexicographical': False, 'group_by_package': False, 'ignore_whitespace': False, 'no_lines_before': frozenset(), 'no_inline_sort': False, 'ignore_comments': False, 'case_sensitive': False, 'sources': (), 'virtual_env': '', 'conda_env': '', 'ensure_newline_before_comments': False, 'directory': '', 'profile': '', 'honor_noqa': False, 'src_paths': (), 'old_finders': False, 'remove_redundant_aliases': False, 'float_to_top': False, 'filter_files': False, 'formatter': '', 'formatting_function': None, 'color_output': False, 'treat_comments_as_code': frozenset(), 'treat_all_comments_as_code': False, 'supported_extensions': frozenset({'py', 'pyx', 'pyi', 'pxd'}), 'blocked_extensions': frozenset({'pex'}), 'constants': frozenset(), 'classes': frozenset(), 'variables': frozenset(), 'dedup_headings': False, 'only_sections': False, 'only_modified': False, 'combine_straight_imports': False, 'auto_identify_namespace_packages': True, 'namespace_packages': frozenset(), 'follow_links': True, 'indented_import_headings': True, 'honor_case_in_force_sorted_sections': False, 'sort_relative_in_force_sorted_sections': False, 'overwrite_in_place': False, 'reverse_sort': False, 'star_first': False, 'git_ls_files': {}, 'format_error': '{error}: {message}', 'format_success': '{success}: {message}', 'sort_order': 'natural', 'sort_reexports': False, 'split_on_trailing_comma': False, 'source': 'defaults'},), virtual_env='', conda_env='', ensure_newline_before_comments=False, directory='/home/timothycrosley/Projects/isort', profile='', honor_noqa=False, src_paths=(PosixPath('/home/timothycrosley/Projects/isort/src'), PosixPath('/home/timothycrosley/Projects/isort')), old_finders=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatter='', formatting_function=None, color_output=False, treat_comments_as_code=frozenset(), treat_all_comments_as_code=False, supported_extensions=frozenset({'py', 'pyx', 'pyi', 'pxd'}), blocked_extensions=frozenset({'pex'}), constants=frozenset(), classes=frozenset(), variables=frozenset(), dedup_headings=False, only_sections=False, only_modified=False, combine_straight_imports=False, auto_identify_namespace_packages=True, namespace_packages=frozenset(), follow_links=True, indented_import_headings=True, honor_case_in_force_sorted_sections=False, sort_relative_in_force_sorted_sections=False, overwrite_in_place=False, reverse_sort=False, star_first=False, git_ls_files={}, format_error='{error}: {message}', format_success='{success}: {message}', sort_order='natural', sort_reexports=False, split_on_trailing_comma=False)
) -> isort.parse.ParsedContent

Parses a python file taking out and categorizing imports.

View Source
def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedContent:

    """Parses a python file taking out and categorizing imports."""

    line_separator: str = config.line_ending or _infer_line_separator(contents)

    in_lines = contents.splitlines()

    if contents and contents[-1] in ("\n", "\r"):

        in_lines.append("")

    out_lines = []

    original_line_count = len(in_lines)

    if config.old_finders:

        from .deprecated.finders import FindersManager

        finder = FindersManager(config=config).find

    else:

        finder = partial(place.module, config=config)

    line_count = len(in_lines)

    place_imports: Dict[str, List[str]] = {}

    import_placements: Dict[str, str] = {}

    as_map: Dict[str, Dict[str, List[str]]] = {

        "straight": defaultdict(list),

        "from": defaultdict(list),

    }

    imports: OrderedDict[str, Dict[str, Any]] = OrderedDict()

    verbose_output: List[str] = []

    for section in chain(config.sections, config.forced_separate):

        imports[section] = {"straight": OrderedDict(), "from": OrderedDict()}

    categorized_comments: CommentsDict = {

        "from": {},

        "straight": {},

        "nested": {},

        "above": {"straight": {}, "from": {}},

    }

    trailing_commas: Set[str] = set()

    index = 0

    import_index = -1

    in_quote = ""

    while index < line_count:

        line = in_lines[index]

        index += 1

        statement_index = index

        (skipping_line, in_quote) = skip_line(

            line, in_quote=in_quote, index=index, section_comments=config.section_comments

        )

        if (

            line in config.section_comments or line in config.section_comments_end

        ) and not skipping_line:

            if import_index == -1:  # pragma: no branch

                import_index = index - 1

            continue

        if "isort:imports-" in line and line.startswith("#"):

            section = line.split("isort:imports-")[-1].split()[0].upper()

            place_imports[section] = []

            import_placements[line] = section

        elif "isort: imports-" in line and line.startswith("#"):

            section = line.split("isort: imports-")[-1].split()[0].upper()

            place_imports[section] = []

            import_placements[line] = section

        if skipping_line:

            out_lines.append(line)

            continue

        lstripped_line = line.lstrip()

        if (

            config.float_to_top

            and import_index == -1

            and line

            and not in_quote

            and not lstripped_line.startswith("#")

            and not lstripped_line.startswith("'''")

            and not lstripped_line.startswith('"""')

        ):

            if not lstripped_line.startswith("import") and not lstripped_line.startswith("from"):

                import_index = index - 1

                while import_index and not in_lines[import_index - 1]:

                    import_index -= 1

            else:

                commentless = line.split("#", 1)[0].strip()

                if (

                    ("isort:skip" in line or "isort: skip" in line)

                    and "(" in commentless

                    and ")" not in commentless

                ):

                    import_index = index

                    starting_line = line

                    while "isort:skip" in starting_line or "isort: skip" in starting_line:

                        commentless = starting_line.split("#", 1)[0]

                        if (

                            "(" in commentless

                            and not commentless.rstrip().endswith(")")

                            and import_index < line_count

                        ):

                            while import_index < line_count and not commentless.rstrip().endswith(

                                ")"

                            ):

                                commentless = in_lines[import_index].split("#", 1)[0]

                                import_index += 1

                        else:

                            import_index += 1

                        if import_index >= line_count:

                            break

                        starting_line = in_lines[import_index]

        line, *end_of_line_comment = line.split("#", 1)

        if ";" in line:

            statements = [line.strip() for line in line.split(";")]

        else:

            statements = [line]

        if end_of_line_comment:

            statements[-1] = f"{statements[-1]}#{end_of_line_comment[0]}"

        for statement in statements:

            line, raw_line = _normalize_line(statement)

            type_of_import = import_type(line, config) or ""

            raw_lines = [raw_line]

            if not type_of_import:

                out_lines.append(raw_line)

                continue

            if import_index == -1:

                import_index = index - 1

            nested_comments = {}

            import_string, comment = parse_comments(line)

            comments = [comment] if comment else []

            line_parts = [part for part in _strip_syntax(import_string).strip().split(" ") if part]

            if type_of_import == "from" and len(line_parts) == 2 and comments:

                nested_comments[line_parts[-1]] = comments[0]

            if "(" in line.split("#", 1)[0] and index < line_count:

                while not line.split("#")[0].strip().endswith(")") and index < line_count:

                    line, new_comment = parse_comments(in_lines[index])

                    index += 1

                    if new_comment:

                        comments.append(new_comment)

                    stripped_line = _strip_syntax(line).strip()

                    if (

                        type_of_import == "from"

                        and stripped_line

                        and " " not in stripped_line.replace(" as ", "")

                        and new_comment

                    ):

                        nested_comments[stripped_line] = comments[-1]

                    import_string += line_separator + line

                    raw_lines.append(line)

            else:

                while line.strip().endswith("\\"):

                    line, new_comment = parse_comments(in_lines[index])

                    line = line.lstrip()

                    index += 1

                    if new_comment:

                        comments.append(new_comment)

                    # Still need to check for parentheses after an escaped line

                    if (

                        "(" in line.split("#")[0]

                        and ")" not in line.split("#")[0]

                        and index < line_count

                    ):

                        stripped_line = _strip_syntax(line).strip()

                        if (

                            type_of_import == "from"

                            and stripped_line

                            and " " not in stripped_line.replace(" as ", "")

                            and new_comment

                        ):

                            nested_comments[stripped_line] = comments[-1]

                        import_string += line_separator + line

                        raw_lines.append(line)

                        while not line.split("#")[0].strip().endswith(")") and index < line_count:

                            line, new_comment = parse_comments(in_lines[index])

                            index += 1

                            if new_comment:

                                comments.append(new_comment)

                            stripped_line = _strip_syntax(line).strip()

                            if (

                                type_of_import == "from"

                                and stripped_line

                                and " " not in stripped_line.replace(" as ", "")

                                and new_comment

                            ):

                                nested_comments[stripped_line] = comments[-1]

                            import_string += line_separator + line

                            raw_lines.append(line)

                    stripped_line = _strip_syntax(line).strip()

                    if (

                        type_of_import == "from"

                        and stripped_line

                        and " " not in stripped_line.replace(" as ", "")

                        and new_comment

                    ):

                        nested_comments[stripped_line] = comments[-1]

                    if import_string.strip().endswith(

                        (" import", " cimport")

                    ) or line.strip().startswith(("import ", "cimport ")):

                        import_string += line_separator + line

                    else:

                        import_string = import_string.rstrip().rstrip("\\") + " " + line.lstrip()

            if type_of_import == "from":

                cimports: bool

                import_string = (

                    import_string.replace("import(", "import (")

                    .replace("\\", " ")

                    .replace("\n", " ")

                )

                if "import " not in import_string:

                    out_lines.extend(raw_lines)

                    continue

                if " cimport " in import_string:

                    parts = import_string.split(" cimport ")

                    cimports = True

                else:

                    parts = import_string.split(" import ")

                    cimports = False

                from_import = parts[0].split(" ")

                import_string = (" cimport " if cimports else " import ").join(

                    [from_import[0] + " " + "".join(from_import[1:])] + parts[1:]

                )

            just_imports = [

                item.replace("{|", "{ ").replace("|}", " }")

                for item in _strip_syntax(import_string).split()

            ]

            attach_comments_to: Optional[List[Any]] = None

            direct_imports = just_imports[1:]

            straight_import = True

            top_level_module = ""

            if "as" in just_imports and (just_imports.index("as") + 1) < len(just_imports):

                straight_import = False

                while "as" in just_imports:

                    nested_module = None

                    as_index = just_imports.index("as")

                    if type_of_import == "from":

                        nested_module = just_imports[as_index - 1]

                        top_level_module = just_imports[0]

                        module = top_level_module + "." + nested_module

                        as_name = just_imports[as_index + 1]

                        direct_imports.remove(nested_module)

                        direct_imports.remove(as_name)

                        direct_imports.remove("as")

                        if nested_module == as_name and config.remove_redundant_aliases:

                            pass

                        elif as_name not in as_map["from"][module]:  # pragma: no branch

                            as_map["from"][module].append(as_name)

                        full_name = f"{nested_module} as {as_name}"

                        associated_comment = nested_comments.get(full_name)

                        if associated_comment:

                            categorized_comments["nested"].setdefault(top_level_module, {})[

                                full_name

                            ] = associated_comment

                            if associated_comment in comments:  # pragma: no branch

                                comments.pop(comments.index(associated_comment))

                    else:

                        module = just_imports[as_index - 1]

                        as_name = just_imports[as_index + 1]

                        if module == as_name and config.remove_redundant_aliases:

                            pass

                        elif as_name not in as_map["straight"][module]:

                            as_map["straight"][module].append(as_name)

                    if comments and attach_comments_to is None:

                        if nested_module and config.combine_as_imports:

                            attach_comments_to = categorized_comments["from"].setdefault(

                                f"{top_level_module}.__combined_as__", []

                            )

                        else:

                            if type_of_import == "from" or (

                                config.remove_redundant_aliases and as_name == module.split(".")[-1]

                            ):

                                attach_comments_to = categorized_comments["straight"].setdefault(

                                    module, []

                                )

                            else:

                                attach_comments_to = categorized_comments["straight"].setdefault(

                                    f"{module} as {as_name}", []

                                )

                    del just_imports[as_index : as_index + 2]

            if type_of_import == "from":

                import_from = just_imports.pop(0)

                placed_module = finder(import_from)

                if config.verbose and not config.only_modified:

                    print(f"from-type place_module for {import_from} returned {placed_module}")

                elif config.verbose:

                    verbose_output.append(

                        f"from-type place_module for {import_from} returned {placed_module}"

                    )

                if placed_module == "":

                    warn(

                        f"could not place module {import_from} of line {line} --"

                        " Do you need to define a default section?"

                    )

                if placed_module and placed_module not in imports:

                    raise MissingSection(import_module=import_from, section=placed_module)

                root = imports[placed_module][type_of_import]  # type: ignore

                for import_name in just_imports:

                    associated_comment = nested_comments.get(import_name)

                    if associated_comment:

                        categorized_comments["nested"].setdefault(import_from, {})[

                            import_name

                        ] = associated_comment

                        if associated_comment in comments:  # pragma: no branch

                            comments.pop(comments.index(associated_comment))

                if (

                    config.force_single_line

                    and comments

                    and attach_comments_to is None

                    and len(just_imports) == 1

                ):

                    nested_from_comments = categorized_comments["nested"].setdefault(

                        import_from, {}

                    )

                    existing_comment = nested_from_comments.get(just_imports[0], "")

                    nested_from_comments[

                        just_imports[0]

                    ] = f"{existing_comment}{'; ' if existing_comment else ''}{'; '.join(comments)}"

                    comments = []

                if comments and attach_comments_to is None:

                    attach_comments_to = categorized_comments["from"].setdefault(import_from, [])

                if len(out_lines) > max(import_index, 1) - 1:

                    last = out_lines[-1].rstrip() if out_lines else ""

                    while (

                        last.startswith("#")

                        and not last.endswith('"""')

                        and not last.endswith("'''")

                        and "isort:imports-" not in last

                        and "isort: imports-" not in last

                        and not config.treat_all_comments_as_code

                        and not last.strip() in config.treat_comments_as_code

                    ):

                        categorized_comments["above"]["from"].setdefault(import_from, []).insert(

                            0, out_lines.pop(-1)

                        )

                        if out_lines:

                            last = out_lines[-1].rstrip()

                        else:

                            last = ""

                    if statement_index - 1 == import_index:  # pragma: no cover

                        import_index -= len(

                            categorized_comments["above"]["from"].get(import_from, [])

                        )

                if import_from not in root:

                    root[import_from] = OrderedDict(

                        (module, module in direct_imports) for module in just_imports

                    )

                else:

                    root[import_from].update(

                        (module, root[import_from].get(module, False) or module in direct_imports)

                        for module in just_imports

                    )

                if comments and attach_comments_to is not None:

                    attach_comments_to.extend(comments)

                if "," in import_string.split(just_imports[-1])[-1]:

                    trailing_commas.add(import_from)

            else:

                if comments and attach_comments_to is not None:

                    attach_comments_to.extend(comments)

                    comments = []

                for module in just_imports:

                    if comments:

                        categorized_comments["straight"][module] = comments

                        comments = []

                    if len(out_lines) > max(import_index, +1, 1) - 1:

                        last = out_lines[-1].rstrip() if out_lines else ""

                        while (

                            last.startswith("#")

                            and not last.endswith('"""')

                            and not last.endswith("'''")

                            and "isort:imports-" not in last

                            and "isort: imports-" not in last

                            and not config.treat_all_comments_as_code

                            and not last.strip() in config.treat_comments_as_code

                        ):

                            categorized_comments["above"]["straight"].setdefault(module, []).insert(

                                0, out_lines.pop(-1)

                            )

                            if out_lines:

                                last = out_lines[-1].rstrip()

                            else:

                                last = ""

                        if index - 1 == import_index:

                            import_index -= len(

                                categorized_comments["above"]["straight"].get(module, [])

                            )

                    placed_module = finder(module)

                    if config.verbose and not config.only_modified:

                        print(f"else-type place_module for {module} returned {placed_module}")

                    elif config.verbose:

                        verbose_output.append(

                            f"else-type place_module for {module} returned {placed_module}"

                        )

                    if placed_module == "":

                        warn(

                            f"could not place module {module} of line {line} --"

                            " Do you need to define a default section?"

                        )

                        imports.setdefault("", {"straight": OrderedDict(), "from": OrderedDict()})

                    if placed_module and placed_module not in imports:

                        raise MissingSection(import_module=module, section=placed_module)

                    straight_import |= imports[placed_module][type_of_import].get(  # type: ignore

                        module, False

                    )

                    imports[placed_module][type_of_import][module] = straight_import  # type: ignore

    change_count = len(out_lines) - original_line_count

    return ParsedContent(

        in_lines=in_lines,

        lines_without_imports=out_lines,

        import_index=import_index,

        place_imports=place_imports,

        import_placements=import_placements,

        as_map=as_map,

        imports=imports,

        categorized_comments=categorized_comments,

        change_count=change_count,

        original_line_count=original_line_count,

        line_separator=line_separator,

        sections=config.sections,

        verbose_output=verbose_output,

        trailing_commas=trailing_commas,

    )

import_type

def import_type(
    line: str,
    config: isort.settings.Config = Config(py_version='py3', force_to_top=frozenset(), skip=frozenset({'build', '.pants.d', '.mypy_cache', 'buck-out', '.nox', '.eggs', '.git', 'venv', '.tox', '.direnv', 'dist', '.venv', '.hg', '__pypackages__', '.bzr', 'node_modules', '_build', '.svn'}), extend_skip=frozenset(), skip_glob=frozenset(), extend_skip_glob=frozenset(), skip_gitignore=False, line_length=79, wrap_length=0, line_ending='', sections=('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), no_sections=False, known_future_library=frozenset({'__future__'}), known_third_party=frozenset(), known_first_party=frozenset(), known_local_folder=frozenset(), known_standard_library=frozenset({'getopt', 'mailcap', 'imaplib', 'nntplib', 'filecmp', 'threading', 'fnmatch', 'timeit', 'sqlite3', 'configparser', 'bdb', 'runpy', 'chunk', 'curses', 'ntpath', 'posixpath', 'platform', 'html', 'fractions', 'tomllib', 'token', 'zlib', 'code', 'poplib', 'fcntl', 'numbers', 'select', 'codecs', 'binascii', 'weakref', 'zipfile', 'distutils', 'secrets', 'sunau', '_ast', 'smtpd', 'dataclasses', 'http', 'gzip', 'posix', 'functools', 'zoneinfo', 'inspect', 'cgitb', 'symtable', 'pathlib', 'unittest', 'itertools', 'operator', 'fpectl', 'linecache', 'encodings', 'sys', 'resource', 'errno', 'telnetlib', 'traceback', 'idlelib', 'warnings', 'sre', 'io', 'cgi', 'email', 'sre_parse', 'dbm', 'pickletools', 'wsgiref', 'turtledemo', 'optparse', 'concurrent', 'string', 'statistics', 'subprocess', 'modulefinder', 'mailbox', 'lib2to3', 'colorsys', 'signal', 'time', 'shelve', 'calendar', 'pipes', 'importlib', 'unicodedata', 'gc', 'crypt', 'msvcrt', 'csv', 'ossaudiodev', 'msilib', 'pickle', 'hashlib', 'nis', 'aifc', 'hmac', 'winsound', 'graphlib', 'zipapp', 'typing', 'contextvars', 'struct', 'netrc', 'turtle', 'dummy_threading', 'cmath', 'array', 'copy', 'pydoc', 'pstats', 'grp', 'tarfile', 'copyreg', 'uu', 'zipimport', 'tabnanny', 'pyclbr', 'getpass', 'keyword', 'mmap', 'spwd', 'codeop', 'sre_compile', 'dis', 'sysconfig', 'atexit', 'venv', 'argparse', 'syslog', 'rlcompleter', 'xdrlib', 'faulthandler', 'lzma', 'macpath', 'enum', 'abc', 'ctypes', 'pprint', '_dummy_thread', 'test', 'socketserver', 'marshal', 'xml', 'binhex', 'socket', 'sched', 'textwrap', 'asyncio', 'webbrowser', 'urllib', 'pwd', 'bz2', 'cmd', 'ipaddress', 'os', 'xmlrpc', 'plistlib', 'fileinput', 'bisect', 'trace', 'gettext', 'contextlib', 'collections', 'ensurepip', 'mimetypes', 'ast', 'sndhdr', 'asyncore', 'difflib', 'tty', 'multiprocessing', 'site', '_thread', 'pkgutil', 'readline', 'stat', 'tokenize', 'smtplib', 'heapq', 'queue', 'shutil', 'termios', 'quopri', 'types', 'profile', 'tkinter', 'symbol', 'cProfile', 'sre_constants', 'formatter', 'locale', 'ssl', 'tempfile', 'stringprep', 'random', 'datetime', 'compileall', 'asynchat', 'tracemalloc', 'imp', 'decimal', 'imghdr', 'py_compile', 'pdb', 'shlex', 'selectors', 'wave', 'reprlib', 'winreg', 'logging', 'math', 'glob', 're', 'audioop', 'ftplib', 'base64', 'parser', 'pty', 'doctest', 'uuid', 'builtins', 'json'}), extra_standard_library=frozenset(), known_other={}, multi_line_output=<WrapModes.GRID: 0>, forced_separate=(), indent='    ', comment_prefix='  #', length_sort=False, length_sort_straight=False, length_sort_sections=frozenset(), add_imports=frozenset(), remove_imports=frozenset(), append_only=False, reverse_relative=False, force_single_line=False, single_line_exclusions=(), default_section='THIRDPARTY', import_headings={}, import_footers={}, balanced_wrapping=False, use_parentheses=False, order_by_type=True, atomic=False, lines_before_imports=-1, lines_after_imports=-1, lines_between_sections=1, lines_between_types=0, combine_as_imports=False, combine_star=False, include_trailing_comma=False, from_first=False, verbose=False, quiet=False, force_adds=False, force_alphabetical_sort_within_sections=False, force_alphabetical_sort=False, force_grid_wrap=0, force_sort_within_sections=False, lexicographical=False, group_by_package=False, ignore_whitespace=False, no_lines_before=frozenset(), no_inline_sort=False, ignore_comments=False, case_sensitive=False, sources=({'py_version': 'py3', 'force_to_top': frozenset(), 'skip': frozenset({'build', '.pants.d', '.mypy_cache', 'buck-out', '.nox', '.eggs', '.git', 'venv', '.tox', '.direnv', 'dist', '.venv', '.hg', '__pypackages__', '.bzr', 'node_modules', '_build', '.svn'}), 'extend_skip': frozenset(), 'skip_glob': frozenset(), 'extend_skip_glob': frozenset(), 'skip_gitignore': False, 'line_length': 79, 'wrap_length': 0, 'line_ending': '', 'sections': ('FUTURE', 'STDLIB', 'THIRDPARTY', 'FIRSTPARTY', 'LOCALFOLDER'), 'no_sections': False, 'known_future_library': frozenset({'__future__'}), 'known_third_party': frozenset(), 'known_first_party': frozenset(), 'known_local_folder': frozenset(), 'known_standard_library': frozenset({'getopt', 'mailcap', 'imaplib', 'nntplib', 'filecmp', 'threading', 'fnmatch', 'timeit', 'sqlite3', 'configparser', 'bdb', 'runpy', 'chunk', 'curses', 'ntpath', 'posixpath', 'platform', 'html', 'fractions', 'tomllib', 'token', 'zlib', 'code', 'poplib', 'fcntl', 'numbers', 'select', 'codecs', 'binascii', 'weakref', 'zipfile', 'distutils', 'secrets', 'sunau', '_ast', 'smtpd', 'dataclasses', 'http', 'gzip', 'posix', 'functools', 'zoneinfo', 'inspect', 'cgitb', 'symtable', 'pathlib', 'unittest', 'itertools', 'operator', 'fpectl', 'linecache', 'encodings', 'sys', 'resource', 'errno', 'telnetlib', 'traceback', 'idlelib', 'warnings', 'sre', 'io', 'cgi', 'email', 'sre_parse', 'dbm', 'pickletools', 'wsgiref', 'turtledemo', 'optparse', 'concurrent', 'string', 'statistics', 'subprocess', 'modulefinder', 'mailbox', 'lib2to3', 'colorsys', 'signal', 'time', 'shelve', 'calendar', 'pipes', 'importlib', 'unicodedata', 'gc', 'crypt', 'msvcrt', 'csv', 'ossaudiodev', 'msilib', 'pickle', 'hashlib', 'nis', 'aifc', 'hmac', 'winsound', 'graphlib', 'zipapp', 'typing', 'contextvars', 'struct', 'netrc', 'turtle', 'dummy_threading', 'cmath', 'array', 'copy', 'pydoc', 'pstats', 'grp', 'tarfile', 'copyreg', 'uu', 'zipimport', 'tabnanny', 'pyclbr', 'getpass', 'keyword', 'mmap', 'spwd', 'codeop', 'sre_compile', 'dis', 'sysconfig', 'atexit', 'venv', 'argparse', 'syslog', 'rlcompleter', 'xdrlib', 'faulthandler', 'lzma', 'macpath', 'enum', 'abc', 'ctypes', 'pprint', '_dummy_thread', 'test', 'socketserver', 'marshal', 'xml', 'binhex', 'socket', 'sched', 'textwrap', 'asyncio', 'webbrowser', 'urllib', 'pwd', 'bz2', 'cmd', 'ipaddress', 'os', 'xmlrpc', 'plistlib', 'fileinput', 'bisect', 'trace', 'gettext', 'contextlib', 'collections', 'ensurepip', 'mimetypes', 'ast', 'sndhdr', 'asyncore', 'difflib', 'tty', 'multiprocessing', 'site', '_thread', 'pkgutil', 'readline', 'stat', 'tokenize', 'smtplib', 'heapq', 'queue', 'shutil', 'termios', 'quopri', 'types', 'profile', 'tkinter', 'symbol', 'cProfile', 'sre_constants', 'formatter', 'locale', 'ssl', 'tempfile', 'stringprep', 'random', 'datetime', 'compileall', 'asynchat', 'tracemalloc', 'imp', 'decimal', 'imghdr', 'py_compile', 'pdb', 'shlex', 'selectors', 'wave', 'reprlib', 'winreg', 'logging', 'math', 'glob', 're', 'audioop', 'ftplib', 'base64', 'parser', 'pty', 'doctest', 'uuid', 'builtins', 'json'}), 'extra_standard_library': frozenset(), 'known_other': {}, 'multi_line_output': <WrapModes.GRID: 0>, 'forced_separate': (), 'indent': '    ', 'comment_prefix': '  #', 'length_sort': False, 'length_sort_straight': False, 'length_sort_sections': frozenset(), 'add_imports': frozenset(), 'remove_imports': frozenset(), 'append_only': False, 'reverse_relative': False, 'force_single_line': False, 'single_line_exclusions': (), 'default_section': 'THIRDPARTY', 'import_headings': {}, 'import_footers': {}, 'balanced_wrapping': False, 'use_parentheses': False, 'order_by_type': True, 'atomic': False, 'lines_before_imports': -1, 'lines_after_imports': -1, 'lines_between_sections': 1, 'lines_between_types': 0, 'combine_as_imports': False, 'combine_star': False, 'include_trailing_comma': False, 'from_first': False, 'verbose': False, 'quiet': False, 'force_adds': False, 'force_alphabetical_sort_within_sections': False, 'force_alphabetical_sort': False, 'force_grid_wrap': 0, 'force_sort_within_sections': False, 'lexicographical': False, 'group_by_package': False, 'ignore_whitespace': False, 'no_lines_before': frozenset(), 'no_inline_sort': False, 'ignore_comments': False, 'case_sensitive': False, 'sources': (), 'virtual_env': '', 'conda_env': '', 'ensure_newline_before_comments': False, 'directory': '', 'profile': '', 'honor_noqa': False, 'src_paths': (), 'old_finders': False, 'remove_redundant_aliases': False, 'float_to_top': False, 'filter_files': False, 'formatter': '', 'formatting_function': None, 'color_output': False, 'treat_comments_as_code': frozenset(), 'treat_all_comments_as_code': False, 'supported_extensions': frozenset({'py', 'pyx', 'pyi', 'pxd'}), 'blocked_extensions': frozenset({'pex'}), 'constants': frozenset(), 'classes': frozenset(), 'variables': frozenset(), 'dedup_headings': False, 'only_sections': False, 'only_modified': False, 'combine_straight_imports': False, 'auto_identify_namespace_packages': True, 'namespace_packages': frozenset(), 'follow_links': True, 'indented_import_headings': True, 'honor_case_in_force_sorted_sections': False, 'sort_relative_in_force_sorted_sections': False, 'overwrite_in_place': False, 'reverse_sort': False, 'star_first': False, 'git_ls_files': {}, 'format_error': '{error}: {message}', 'format_success': '{success}: {message}', 'sort_order': 'natural', 'sort_reexports': False, 'split_on_trailing_comma': False, 'source': 'defaults'},), virtual_env='', conda_env='', ensure_newline_before_comments=False, directory='/home/timothycrosley/Projects/isort', profile='', honor_noqa=False, src_paths=(PosixPath('/home/timothycrosley/Projects/isort/src'), PosixPath('/home/timothycrosley/Projects/isort')), old_finders=False, remove_redundant_aliases=False, float_to_top=False, filter_files=False, formatter='', formatting_function=None, color_output=False, treat_comments_as_code=frozenset(), treat_all_comments_as_code=False, supported_extensions=frozenset({'py', 'pyx', 'pyi', 'pxd'}), blocked_extensions=frozenset({'pex'}), constants=frozenset(), classes=frozenset(), variables=frozenset(), dedup_headings=False, only_sections=False, only_modified=False, combine_straight_imports=False, auto_identify_namespace_packages=True, namespace_packages=frozenset(), follow_links=True, indented_import_headings=True, honor_case_in_force_sorted_sections=False, sort_relative_in_force_sorted_sections=False, overwrite_in_place=False, reverse_sort=False, star_first=False, git_ls_files={}, format_error='{error}: {message}', format_success='{success}: {message}', sort_order='natural', sort_reexports=False, split_on_trailing_comma=False)
) -> Optional[str]

If the current line is an import line it will return its type (from or straight)

View Source
def import_type(line: str, config: Config = DEFAULT_CONFIG) -> Optional[str]:

    """If the current line is an import line it will return its type (from or straight)"""

    if config.honor_noqa and line.lower().rstrip().endswith("noqa"):

        return None

    if "isort:skip" in line or "isort: skip" in line or "isort: split" in line:

        return None

    if line.startswith(("import ", "cimport ")):

        return "straight"

    if line.startswith("from "):

        return "from"

    return None

skip_line

def skip_line(
    line: str,
    in_quote: str,
    index: int,
    section_comments: Tuple[str, ...],
    needs_import: bool = True
) -> Tuple[bool, str]

Determine if a given line should be skipped.

Returns back a tuple containing:

(skip_line: bool, in_quote: str,)

View Source
def skip_line(

    line: str,

    in_quote: str,

    index: int,

    section_comments: Tuple[str, ...],

    needs_import: bool = True,

) -> Tuple[bool, str]:

    """Determine if a given line should be skipped.

    Returns back a tuple containing:

    (skip_line: bool,

     in_quote: str,)

    """

    should_skip = bool(in_quote)

    if '"' in line or "'" in line:

        char_index = 0

        while char_index < len(line):

            if line[char_index] == "\\":

                char_index += 1

            elif in_quote:

                if line[char_index : char_index + len(in_quote)] == in_quote:

                    in_quote = ""

            elif line[char_index] in ("'", '"'):

                long_quote = line[char_index : char_index + 3]

                if long_quote in ('"""', "'''"):

                    in_quote = long_quote

                    char_index += 2

                else:

                    in_quote = line[char_index]

            elif line[char_index] == "#":

                break

            char_index += 1

    if ";" in line.split("#")[0] and needs_import:

        for part in (part.strip() for part in line.split(";")):

            if (

                part

                and not part.startswith("from ")

                and not part.startswith(("import ", "cimport "))

            ):

                should_skip = True

    return (bool(should_skip or in_quote), in_quote)

Classes

ParsedContent

class ParsedContent(
    /,
    *args,
    **kwargs
)
View Source
class ParsedContent(NamedTuple):

    in_lines: List[str]

    lines_without_imports: List[str]

    import_index: int

    place_imports: Dict[str, List[str]]

    import_placements: Dict[str, str]

    as_map: Dict[str, Dict[str, List[str]]]

    imports: Dict[str, Dict[str, Any]]

    categorized_comments: "CommentsDict"

    change_count: int

    original_line_count: int

    line_separator: str

    sections: Any

    verbose_output: List[str]

    trailing_commas: Set[str]

Ancestors (in MRO)

  • builtins.tuple

Class variables

as_map
categorized_comments
change_count
import_index
import_placements
imports
in_lines
line_separator
lines_without_imports
original_line_count
place_imports
sections
trailing_commas
verbose_output

Methods

count

def count(
    self,
    value,
    /
)

Return number of occurrences of value.

index

def index(
    self,
    value,
    start=0,
    stop=9223372036854775807,
    /
)

Return first index of value.

Raises ValueError if the value is not present.