/
place.py
145 lines (115 loc) · 5.05 KB
/
place.py
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
"""Contains all logic related to placing an import within a certain section."""
import importlib
from fnmatch import fnmatch
from functools import lru_cache
from pathlib import Path
from typing import FrozenSet, Iterable, Optional, Tuple
from isort import sections
from isort.settings import DEFAULT_CONFIG, Config
from isort.utils import exists_case_sensitive
LOCAL = "LOCALFOLDER"
def module(name: str, config: Config = DEFAULT_CONFIG) -> str:
"""Returns the section placement for the given module name."""
return module_with_reason(name, config)[0]
@lru_cache(maxsize=1000)
def module_with_reason(name: str, config: Config = DEFAULT_CONFIG) -> Tuple[str, str]:
"""Returns the section placement for the given module name alongside the reasoning."""
return (
_forced_separate(name, config)
or _local(name, config)
or _known_pattern(name, config)
or _src_path(name, config)
or (config.default_section, "Default option in Config or universal default.")
)
def _forced_separate(name: str, config: Config) -> Optional[Tuple[str, str]]:
for forced_separate in config.forced_separate:
# Ensure all forced_separate patterns will match to end of string
path_glob = forced_separate
if not forced_separate.endswith("*"):
path_glob = f"{forced_separate}*"
if fnmatch(name, path_glob) or fnmatch(name, "." + path_glob):
return (forced_separate, f"Matched forced_separate ({forced_separate}) config value.")
return None
def _local(name: str, config: Config) -> Optional[Tuple[str, str]]:
if name.startswith("."):
return (LOCAL, "Module name started with a dot.")
return None
def _known_pattern(name: str, config: Config) -> Optional[Tuple[str, str]]:
parts = name.split(".")
module_names_to_check = (".".join(parts[:first_k]) for first_k in range(len(parts), 0, -1))
for module_name_to_check in module_names_to_check:
for pattern, placement in config.known_patterns:
if placement in config.sections and pattern.match(module_name_to_check):
return (placement, f"Matched configured known pattern {pattern}")
return None
def _src_path(
name: str,
config: Config,
src_paths: Optional[Iterable[Path]] = None,
prefix: Tuple[str, ...] = (),
) -> Optional[Tuple[str, str]]:
if src_paths is None:
src_paths = config.src_paths
root_module_name, *nested_module = name.split(".", 1)
new_prefix = prefix + (root_module_name,)
namespace = ".".join(new_prefix)
for src_path in src_paths:
module_path = (src_path / root_module_name).resolve()
if not prefix and not module_path.is_dir() and src_path.name == root_module_name:
module_path = src_path.resolve()
if nested_module and (
namespace in config.namespace_packages
or (
config.auto_identify_namespace_packages
and _is_namespace_package(module_path, config.supported_extensions)
)
):
return _src_path(nested_module[0], config, (module_path,), new_prefix)
if (
_is_module(module_path)
or _is_package(module_path)
or _src_path_is_module(src_path, root_module_name)
):
return (sections.FIRSTPARTY, f"Found in one of the configured src_paths: {src_path}.")
return None
def _is_module(path: Path) -> bool:
return (
exists_case_sensitive(str(path.with_suffix(".py")))
or any(
exists_case_sensitive(str(path.with_suffix(ext_suffix)))
for ext_suffix in importlib.machinery.EXTENSION_SUFFIXES
)
or exists_case_sensitive(str(path / "__init__.py"))
)
def _is_package(path: Path) -> bool:
return exists_case_sensitive(str(path)) and path.is_dir()
def _is_namespace_package(path: Path, src_extensions: FrozenSet[str]) -> bool:
if not _is_package(path):
return False
init_file = path / "__init__.py"
if not init_file.exists():
filenames = [
filepath
for filepath in path.iterdir()
if filepath.suffix.lstrip(".") in src_extensions
or filepath.name.lower() in ("setup.cfg", "pyproject.toml")
]
if filenames:
return False
else:
with init_file.open("rb") as open_init_file:
file_start = open_init_file.read(4096)
if (
b"__import__('pkg_resources').declare_namespace(__name__)" not in file_start
and b'__import__("pkg_resources").declare_namespace(__name__)' not in file_start
and b"__path__ = __import__('pkgutil').extend_path(__path__, __name__)"
not in file_start
and b'__path__ = __import__("pkgutil").extend_path(__path__, __name__)'
not in file_start
):
return False
return True
def _src_path_is_module(src_path: Path, module_name: str) -> bool:
return (
module_name == src_path.name and src_path.is_dir() and exists_case_sensitive(str(src_path))
)