Skip to content

Commit

Permalink
Implemented #1751: Strict typing enforcement (turned on mypy strict m…
Browse files Browse the repository at this point in the history
…ode).
  • Loading branch information
timothycrosley committed Jun 18, 2021
1 parent 517cc88 commit be0fbd0
Show file tree
Hide file tree
Showing 17 changed files with 140 additions and 86 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -18,6 +18,7 @@ Find out more about isort's release policy [here](https://pycqa.github.io/isort/

#### Goal Zero (Tickets related to aspirational goal of achieving 0 regressions for remaining 5.0.0 lifespan):
- Implemented #1394: 100% branch coverage (in addition to line coverage) enforced.
- Implemented #1751: Strict typing enforcement (turned on mypy strict mode).

### 5.8.0 March 20th 2021
- Fixed #1631: as import comments can in some cases be duplicated.
Expand Down
19 changes: 19 additions & 0 deletions isort/__init__.py
@@ -1,4 +1,23 @@
"""Defines the public isort interface"""
__all__ = (
"Config",
"ImportKey",
"__version__",
"check_code",
"check_file",
"check_stream",
"code",
"file",
"find_imports_in_code",
"find_imports_in_file",
"find_imports_in_paths",
"find_imports_in_stream",
"place_module",
"place_module_with_reason",
"settings",
"stream",
)

from . import settings
from ._version import __version__
from .api import ImportKey
Expand Down
2 changes: 1 addition & 1 deletion isort/_future/__init__.py
@@ -1,7 +1,7 @@
import sys

if sys.version_info.major <= 3 and sys.version_info.minor <= 6:
from . import _dataclasses as dataclasses # type: ignore
from . import _dataclasses as dataclasses

else:
import dataclasses # type: ignore
Expand Down
42 changes: 29 additions & 13 deletions isort/api.py
@@ -1,11 +1,27 @@
__all__ = (
"ImportKey",
"check_code_string",
"check_file",
"check_stream",
"find_imports_in_code",
"find_imports_in_file",
"find_imports_in_paths",
"find_imports_in_stream",
"place_module",
"place_module_with_reason",
"sort_code_string",
"sort_file",
"sort_stream",
)

import contextlib
import shutil
import sys
from enum import Enum
from io import StringIO
from itertools import chain
from pathlib import Path
from typing import Iterator, Optional, Set, TextIO, Union, cast
from typing import Any, Iterator, Optional, Set, TextIO, Union, cast
from warnings import warn

from isort import core
Expand Down Expand Up @@ -57,8 +73,8 @@ def sort_code_string(
file_path: Optional[Path] = None,
disregard_skip: bool = False,
show_diff: Union[bool, TextIO] = False,
**config_kwargs,
):
**config_kwargs: Any,
) -> str:
"""Sorts any imports within the provided code string, returning a new string with them sorted.
- **code**: The string of code with imports that need to be sorted.
Expand Down Expand Up @@ -93,7 +109,7 @@ def check_code_string(
config: Config = DEFAULT_CONFIG,
file_path: Optional[Path] = None,
disregard_skip: bool = False,
**config_kwargs,
**config_kwargs: Any,
) -> bool:
"""Checks the order, format, and categorization of imports within the provided code string.
Returns `True` if everything is correct, otherwise `False`.
Expand Down Expand Up @@ -127,7 +143,7 @@ def sort_stream(
disregard_skip: bool = False,
show_diff: Union[bool, TextIO] = False,
raise_on_skip: bool = True,
**config_kwargs,
**config_kwargs: Any,
) -> bool:
"""Sorts any imports within the provided code stream, outputs to the provided output stream.
Returns `True` if anything is modified from the original input stream, otherwise `False`.
Expand Down Expand Up @@ -215,7 +231,7 @@ def check_stream(
config: Config = DEFAULT_CONFIG,
file_path: Optional[Path] = None,
disregard_skip: bool = False,
**config_kwargs,
**config_kwargs: Any,
) -> bool:
"""Checks any imports within the provided code stream, returning `False` if any unsorted or
incorrectly imports are found or `True` if no problems are identified.
Expand Down Expand Up @@ -282,7 +298,7 @@ def check_file(
file_path: Optional[Path] = None,
disregard_skip: bool = True,
extension: Optional[str] = None,
**config_kwargs,
**config_kwargs: Any,
) -> bool:
"""Checks any imports within the provided file, returning `False` if any unsorted or
incorrectly imports are found or `True` if no problems are identified.
Expand Down Expand Up @@ -335,7 +351,7 @@ def sort_file(
show_diff: Union[bool, TextIO] = False,
write_to_stdout: bool = False,
output: Optional[TextIO] = None,
**config_kwargs,
**config_kwargs: Any,
) -> bool:
"""Sorts and formats any groups of imports imports within the provided file or Path.
Returns `True` if the file has been changed, otherwise `False`.
Expand Down Expand Up @@ -458,7 +474,7 @@ def find_imports_in_code(
file_path: Optional[Path] = None,
unique: Union[bool, ImportKey] = False,
top_only: bool = False,
**config_kwargs,
**config_kwargs: Any,
) -> Iterator[identify.Import]:
"""Finds and returns all imports within the provided code string.
Expand Down Expand Up @@ -486,7 +502,7 @@ def find_imports_in_stream(
unique: Union[bool, ImportKey] = False,
top_only: bool = False,
_seen: Optional[Set[str]] = None,
**config_kwargs,
**config_kwargs: Any,
) -> Iterator[identify.Import]:
"""Finds and returns all imports within the provided code stream.
Expand Down Expand Up @@ -527,7 +543,7 @@ def find_imports_in_file(
file_path: Optional[Path] = None,
unique: Union[bool, ImportKey] = False,
top_only: bool = False,
**config_kwargs,
**config_kwargs: Any,
) -> Iterator[identify.Import]:
"""Finds and returns all imports within the provided source file.
Expand Down Expand Up @@ -556,7 +572,7 @@ def find_imports_in_paths(
file_path: Optional[Path] = None,
unique: Union[bool, ImportKey] = False,
top_only: bool = False,
**config_kwargs,
**config_kwargs: Any,
) -> Iterator[identify.Import]:
"""Finds and returns all imports within the provided source paths.
Expand All @@ -581,7 +597,7 @@ def find_imports_in_paths(


def _config(
path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs
path: Optional[Path] = None, config: Config = DEFAULT_CONFIG, **config_kwargs: Any
) -> Config:
if path and (
config is DEFAULT_CONFIG
Expand Down
2 changes: 1 addition & 1 deletion isort/core.py
Expand Up @@ -445,7 +445,7 @@ def process(
return made_changes


def _indented_config(config: Config, indent: str):
def _indented_config(config: Config, indent: str) -> Config:
if not indent:
return config

Expand Down
6 changes: 3 additions & 3 deletions isort/deprecated/finders.py
Expand Up @@ -19,19 +19,19 @@
from isort.utils import exists_case_sensitive

try:
from pipreqs import pipreqs
from pipreqs import pipreqs # type: ignore

except ImportError:
pipreqs = None

try:
from pip_api import parse_requirements
from pip_api import parse_requirements # type: ignore

except ImportError:
parse_requirements = None

try:
from requirementslib import Pipfile
from requirementslib import Pipfile # type: ignore

except ImportError:
Pipfile = None
Expand Down
8 changes: 4 additions & 4 deletions isort/format.py
Expand Up @@ -6,7 +6,7 @@
from typing import Optional, TextIO

try:
import colorama
import colorama # type: ignore
except ImportError:
colorama_unavailable = True
else:
Expand Down Expand Up @@ -48,7 +48,7 @@ def show_unified_diff(
file_path: Optional[Path],
output: Optional[TextIO] = None,
color_output: bool = False,
):
) -> None:
"""Shows a unified_diff for the provided input and output against the provided file path.
- **file_input**: A string that represents the contents of a file before changes.
Expand Down Expand Up @@ -125,7 +125,7 @@ def __init__(self, error: str, success: str, output: Optional[TextIO]):
def style_text(text: str, style: Optional[str] = None) -> str:
if style is None:
return text
return style + text + colorama.Style.RESET_ALL
return style + text + str(colorama.Style.RESET_ALL)

def diff_line(self, line: str) -> None:
style = None
Expand All @@ -138,7 +138,7 @@ def diff_line(self, line: str) -> None:

def create_terminal_printer(
color: bool, output: Optional[TextIO] = None, error: str = "", success: str = ""
):
) -> BasicPrinter:
if color and colorama_unavailable:
no_colorama_message = (
"\n"
Expand Down
2 changes: 1 addition & 1 deletion isort/identify.py
Expand Up @@ -32,7 +32,7 @@ def statement(self) -> str:
import_string += f" as {self.alias}"
return import_string

def __str__(self):
def __str__(self) -> str:
return (
f"{self.file_path or ''}:{self.line_number} "
f"{'indented ' if self.indented else ''}{self.statement()}"
Expand Down
10 changes: 5 additions & 5 deletions isort/io.py
Expand Up @@ -4,7 +4,7 @@
from contextlib import contextmanager
from io import BytesIO, StringIO, TextIOWrapper
from pathlib import Path
from typing import Callable, Iterator, TextIO, Union
from typing import Any, Callable, Iterator, TextIO, Union

from isort._future import dataclass
from isort.exceptions import UnsupportedEncoding
Expand All @@ -19,7 +19,7 @@ class File:
encoding: str

@staticmethod
def detect_encoding(filename: str, readline: Callable[[], bytes]):
def detect_encoding(filename: Union[str, Path], readline: Callable[[], bytes]) -> str:
try:
return tokenize.detect_encoding(readline)[0]
except Exception:
Expand All @@ -33,11 +33,11 @@ def from_contents(contents: str, filename: str) -> "File":
)

@property
def extension(self):
def extension(self) -> str:
return self.path.suffix.lstrip(".")

@staticmethod
def _open(filename):
def _open(filename: Union[str, Path]) -> TextIOWrapper:
"""Open a file in read only mode using the encoding detected by
detect_encoding().
"""
Expand Down Expand Up @@ -66,7 +66,7 @@ def read(filename: Union[str, Path]) -> Iterator["File"]:


class _EmptyIO(StringIO):
def write(self, *args, **kwargs): # skipcq: PTC-W0049
def write(self, *args: Any, **kwargs: Any) -> None: # type: ignore # skipcq: PTC-W0049
pass


Expand Down
10 changes: 7 additions & 3 deletions isort/literal.py
Expand Up @@ -69,10 +69,14 @@ def assignment(code: str, sort_type: str, extension: str, config: Config = DEFAU
return sorted_value_code


def register_type(name: str, kind: type):
def register_type(
name: str, kind: type
) -> Callable[[Callable[[Any, ISortPrettyPrinter], str]], Callable[[Any, ISortPrettyPrinter], str]]:
"""Registers a new literal sort type."""

def wrap(function):
def wrap(
function: Callable[[Any, ISortPrettyPrinter], str]
) -> Callable[[Any, ISortPrettyPrinter], str]:
type_mapping[name] = (kind, function)
return function

Expand All @@ -81,7 +85,7 @@ def wrap(function):

@register_type("dict", dict)
def _dict(value: Dict[Any, Any], printer: ISortPrettyPrinter) -> str:
return printer.pformat(dict(sorted(value.items(), key=lambda item: item[1])))
return printer.pformat(dict(sorted(value.items(), key=lambda item: item[1]))) # type: ignore


@register_type("list", list)
Expand Down
11 changes: 6 additions & 5 deletions isort/main.py
Expand Up @@ -7,15 +7,16 @@
from gettext import gettext as _
from io import TextIOWrapper
from pathlib import Path
from typing import Any, Dict, List, Optional, Sequence
from typing import Any, Dict, List, Optional, Sequence, Union
from warnings import warn

from . import __version__, api, files, sections
from .exceptions import FileSkipped, ISortError, UnsupportedEncoding
from .format import create_terminal_printer
from .logo import ASCII_ART
from .profiles import profiles
from .settings import VALID_PY_TARGETS, Config, WrapModes
from .settings import VALID_PY_TARGETS, Config
from .wrap_modes import WrapModes

try:
from .setuptools_commands import ISortCommand # noqa: F401
Expand Down Expand Up @@ -920,16 +921,16 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> Dict[str, Any]:
return arguments


def _preconvert(item):
def _preconvert(item: Any) -> Union[str, List[Any]]:
"""Preconverts objects from native types into JSONifyiable types"""
if isinstance(item, (set, frozenset)):
return list(item)
if isinstance(item, WrapModes):
return item.name
return str(item.name)
if isinstance(item, Path):
return str(item)
if callable(item) and hasattr(item, "__name__"):
return item.__name__
return str(item.__name__)
raise TypeError("Unserializable object {} of type {}".format(item, type(item)))


Expand Down
16 changes: 9 additions & 7 deletions isort/output.py
@@ -1,7 +1,7 @@
import copy
import itertools
from functools import partial
from typing import Iterable, List, Set, Tuple
from typing import Any, Iterable, List, Optional, Set, Tuple, Type

from isort.format import format_simplified

Expand Down Expand Up @@ -609,19 +609,21 @@ def _normalize_empty_lines(lines: List[str]) -> List[str]:
class _LineWithComments(str):
comments: List[str]

def __new__(cls, value: str, comments: List[str]):
instance = super().__new__(cls, value) # type: ignore
def __new__(
cls: Type["_LineWithComments"], value: Any, comments: List[str]
) -> "_LineWithComments":
instance = super().__new__(cls, value)
instance.comments = comments
return instance


def _ensure_newline_before_comment(output):
def _ensure_newline_before_comment(output: List[str]) -> List[str]:
new_output: List[str] = []

def is_comment(line):
return line and line.startswith("#")
def is_comment(line: Optional[str]) -> bool:
return line.startswith("#") if line else False

for line, prev_line in zip(output, [None] + output):
for line, prev_line in zip(output, [None] + output): # type: ignore
if is_comment(line) and prev_line != "" and not is_comment(prev_line):
new_output.append("")
new_output.append(line)
Expand Down

0 comments on commit be0fbd0

Please sign in to comment.