diff options
Diffstat (limited to 'isort/parse.py')
-rw-r--r-- | isort/parse.py | 116 |
1 files changed, 38 insertions, 78 deletions
diff --git a/isort/parse.py b/isort/parse.py index 51dbc96f..a87461ff 100644 --- a/isort/parse.py +++ b/isort/parse.py @@ -1,5 +1,6 @@ """Defines parsing functions used by isort for parsing import definitions""" from collections import OrderedDict, defaultdict, namedtuple +from io import StringIO from itertools import chain from typing import TYPE_CHECKING, Any, Dict, Generator, Iterator, List, NamedTuple, Optional, Tuple from warnings import warn @@ -10,6 +11,8 @@ from isort.settings import DEFAULT_CONFIG, Config from .comments import parse as parse_comments from .finders import FindersManager +IMPORT_START_IDENTIFIERS = ("from ", "from.import", "import ", "import*") + if TYPE_CHECKING: from mypy_extensions import TypedDict @@ -37,6 +40,18 @@ def _infer_line_separator(file_contents: str) -> str: 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("import*", "import *") + line = line.replace(" .import ", " . import ") + line = line.replace("\t", " ") + return (line, raw_line) + + def import_type(line: str) -> Optional[str]: """If the current line is an import line it will return its type (from or straight)""" if "isort:skip" in line or "NOQA" in line: @@ -62,45 +77,24 @@ def _strip_syntax(import_string: str) -> str: def skip_line( - line: str, - in_quote: str, - in_top_comment: bool, - index: int, - section_comments: List[str], - first_comment_index_start: int, - first_comment_index_end: int, -) -> Tuple[bool, str, bool, int, int]: + line: str, in_quote: str, index: int, section_comments: List[str] +) -> Tuple[bool, str]: """Determine if a given line should be skipped. Returns back a tuple containing: (skip_line: bool, - in_quote: str, - in_top_comment: bool, - first_comment_index_start: int, - first_comment_index_end: int) + in_quote: str,) """ skip_line = bool(in_quote) - if index == 1 and line.startswith("#"): - in_top_comment = True - return (True, in_quote, in_top_comment, first_comment_index_start, first_comment_index_end) - elif in_top_comment: - if not line.startswith("#") or line in section_comments: - in_top_comment = False - first_comment_index_end = index - 1 - if '"' in line or "'" in line: char_index = 0 - if first_comment_index_start == -1 and (line.startswith('"') or line.startswith("'")): - first_comment_index_start = index 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 = "" - if first_comment_index_end < first_comment_index_start: - first_comment_index_end = index elif line[char_index] in ("'", '"'): long_quote = line[char_index : char_index + 3] if long_quote in ('"""', "'''"): @@ -112,13 +106,12 @@ def skip_line( break char_index += 1 - return ( - bool(skip_line or in_quote or in_top_comment), - in_quote, - in_top_comment, - first_comment_index_start, - first_comment_index_end, - ) + if ";" in line: + for part in (part.strip() for part in line.split(";")): + if part and not part.startswith("from ") and not part.startswith("import "): + skip_line = True + + return (bool(skip_line or in_quote), in_quote) class ParsedContent(NamedTuple): @@ -130,8 +123,6 @@ class ParsedContent(NamedTuple): as_map: Dict[str, List[str]] imports: Dict[str, Dict[str, Any]] categorized_comments: "CommentsDict" - first_comment_index_start: int - first_comment_index_end: int change_count: int original_line_count: int line_separator: str @@ -142,16 +133,12 @@ class ParsedContent(NamedTuple): 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) - add_imports = (format_natural(addition) for addition in config.add_imports) in_lines = contents.split(line_separator) out_lines = [] original_line_count = len(in_lines) section_comments = [f"# {heading}" for heading in config.import_headings.values()] finder = FindersManager(config=config) - if original_line_count > 1 or in_lines[:1] not in ([], [""]) or config.force_adds: - in_lines.extend(add_imports) - line_count = len(in_lines) place_imports: Dict[str, List[str]] = {} @@ -170,31 +157,12 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte index = 0 import_index = -1 in_quote = "" - in_top_comment = False - first_comment_index_start = -1 - first_comment_index_end = -1 while index < line_count: - raw_line = line = in_lines[index] - line = line.replace("from.import ", "from . import ") - line = line.replace("\t", " ").replace("import*", "import *") - line = line.replace(" .import ", " . import ") + line = in_lines[index] index += 1 statement_index = index - - ( - skipping_line, - in_quote, - in_top_comment, - first_comment_index_start, - first_comment_index_end, - ) = skip_line( - line, - in_quote=in_quote, - in_top_comment=in_top_comment, - index=index, - section_comments=section_comments, - first_comment_index_start=first_comment_index_start, - first_comment_index_end=first_comment_index_end, + (skipping_line, in_quote) = skip_line( + line, in_quote=in_quote, index=index, section_comments=section_comments ) if line in section_comments and not skipping_line: @@ -207,20 +175,17 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte place_imports[section] = [] import_placements[line] = section - if ";" in line: - for part in (part.strip() for part in line.split(";")): - if part and not part.startswith("from ") and not part.startswith("import "): - skipping_line = True - - type_of_import: str = import_type(line) or "" - if not type_of_import or skipping_line: - out_lines.append(raw_line) + if skipping_line: + out_lines.append(line) continue - for line in (line.strip() for line in line.split(";")): + for line in ( + (line.strip() for line in line.split(";")) if ";" in line else (line,) # type: ignore + ): + line, raw_line = _normalize_line(line) type_of_import = import_type(line) or "" if not type_of_import: - out_lines.append(line) + out_lines.append(raw_line) continue if import_index == -1: @@ -353,7 +318,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte if comments: categorized_comments["from"].setdefault(import_from, []).extend(comments) - if len(out_lines) > max(import_index, first_comment_index_end + 1, 1) - 1: + if len(out_lines) > max(import_index, 1) - 1: last = out_lines and out_lines[-1].rstrip() or "" while ( last.startswith("#") @@ -364,10 +329,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte categorized_comments["above"]["from"].setdefault(import_from, []).insert( 0, out_lines.pop(-1) ) - if ( - len(out_lines) - > max(import_index - 1, first_comment_index_end + 1, 1) - 1 - ): + if len(out_lines) > max(import_index - 1, 1) - 1: last = out_lines[-1].rstrip() else: last = "" @@ -391,7 +353,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte categorized_comments["straight"][module] = comments comments = [] - if len(out_lines) > max(import_index, first_comment_index_end + 1, 1) - 1: + if len(out_lines) > max(import_index, +1, 1) - 1: last = out_lines and out_lines[-1].rstrip() or "" while ( @@ -403,7 +365,7 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte categorized_comments["above"]["straight"].setdefault(module, []).insert( 0, out_lines.pop(-1) ) - if len(out_lines) > 0 and len(out_lines) != first_comment_index_end: + if len(out_lines) > 0 and len(out_lines): last = out_lines[-1].rstrip() else: last = "" @@ -435,8 +397,6 @@ def file_contents(contents: str, config: Config = DEFAULT_CONFIG) -> ParsedConte as_map=as_map, imports=imports, categorized_comments=categorized_comments, - first_comment_index_start=first_comment_index_start, - first_comment_index_end=first_comment_index_end, change_count=change_count, original_line_count=original_line_count, line_separator=line_separator, |