summaryrefslogtreecommitdiff
path: root/isort/sorting.py
blob: abd6bdbd7073fab1d60d80f9b6da89c5b5bb460d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import re
from typing import Any, Callable, Iterable, List,  Optional

from .settings import Config

_import_line_intro_re = re.compile("^(?:from|import) ")
_import_line_midline_import_re = re.compile(" import ")


def module_key(
    module_name: str,
    config: Config,
    sub_imports: bool = False,
    ignore_case: bool = False,
    section_name: Optional[Any] = None,
) -> str:
    match = re.match(r"^(\.+)\s*(.*)", module_name)
    if match:
        sep = " " if config.reverse_relative else "_"
        module_name = sep.join(match.groups())

    prefix = ""
    if ignore_case:
        module_name = str(module_name).lower()
    else:
        module_name = str(module_name)

    if sub_imports and config.order_by_type:
        if module_name.isupper() and len(module_name) > 1:  # see issue #376
            prefix = "A"
        elif module_name[0:1].isupper():
            prefix = "B"
        else:
            prefix = "C"
    if not config.case_sensitive:
        module_name = module_name.lower()

    length_sort = config.length_sort or str(section_name).lower() in config.length_sort_sections
    _length_sort_maybe = length_sort and (str(len(module_name)) + ":" + module_name) or module_name
    return f"{module_name in config.force_to_top and 'A' or 'B'}{prefix}{_length_sort_maybe}"


def section_key(
    line: str, order_by_type: bool, force_to_top: List[str], lexicographical: bool = False
) -> str:
    section = "B"

    if lexicographical:
        line = _import_line_intro_re.sub("", _import_line_midline_import_re.sub(".", line))
    else:
        line = re.sub("^from ", "", line)
        line = re.sub("^import ", "", line)
    if line.split(" ")[0] in force_to_top:
        section = "A"
    if not order_by_type:
        line = line.lower()
    return f"{section}{line}"


def naturally(to_sort: Iterable[str], key: Optional[Callable[[str], Any]] = None) -> List[str]:
    """Returns a naturally sorted list"""
    if key is None:
        key_callback = _natural_keys
    else:

        def key_callback(text: str) -> List[Any]:
            return _natural_keys(key(text))  # type: ignore

    return sorted(to_sort, key=key_callback)


def _atoi(text: str) -> Any:
    return int(text) if text.isdigit() else text


def _natural_keys(text: str) -> List[Any]:
    return [_atoi(c) for c in re.split(r"(\d+)", text)]