Skip to content

Commit d6d29ef

Browse files
committed
Merge remote-tracking branch 'origin/master' into rm-imp-again
2 parents af5c040 + 7a42481 commit d6d29ef

13 files changed

+172
-136
lines changed

.github/dependabot.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: pip
4+
directory: "/"
5+
schedule:
6+
interval: daily
7+
open-pull-requests-limit: 10
8+
- package-ecosystem: "github-actions"
9+
directory: "/"
10+
schedule:
11+
interval: daily

.github/workflows/ci.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ jobs:
2727
python-version: ["3.8", "3.9", "3.10", "3.11"]
2828

2929
steps:
30-
- uses: actions/checkout@v3
30+
- uses: actions/checkout@v4
3131
- name: "Set up Python"
3232
uses: actions/setup-python@v4
3333
with:
3434
python-version: ${{ matrix.python-version }}
3535

36-
- uses: actions/cache@v2
36+
- uses: actions/cache@v3
3737
with:
3838
path: ~/.cache/pip
3939
# This is like the example but we use ``*requirements.txt`` rather
@@ -55,9 +55,8 @@ jobs:
5555
5656
- name: "Lint"
5757
run: |
58-
flake8 *.py pip_check_reqs tests
5958
mypy .
60-
isort --check-only .
59+
ruff .
6160
black --check .
6261
pylint pip_check_reqs tests
6362
pip-extra-reqs pip_check_reqs

pip_check_reqs/common.py

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Common functions."""
22

3+
from __future__ import annotations
4+
35
import ast
46
import fnmatch
57
# import imp
@@ -12,14 +14,8 @@
1214
from pathlib import Path
1315
from typing import (
1416
Callable,
15-
Dict,
1617
Generator,
1718
Iterable,
18-
List,
19-
Optional,
20-
Set,
21-
Tuple,
22-
Union,
2319
)
2420

2521
from packaging.markers import Marker
@@ -39,7 +35,7 @@ class FoundModule:
3935

4036
modname: str
4137
filename: str
42-
locations: List[Tuple[str, int]] = field(default_factory=list)
38+
locations: list[tuple[str, int]] = field(default_factory=list)
4339

4440
def __post_init__(self) -> None:
4541
self.filename = os.path.realpath(self.filename)
@@ -49,22 +45,22 @@ class _ImportVisitor(ast.NodeVisitor):
4945
def __init__(self, ignore_modules_function: Callable[[str], bool]) -> None:
5046
super().__init__()
5147
self._ignore_modules_function = ignore_modules_function
52-
self._modules: Dict[str, FoundModule] = {}
53-
self._location: Optional[str] = None
48+
self._modules: dict[str, FoundModule] = {}
49+
self._location: str | None = None
5450

5551
def set_location(self, location: str) -> None:
5652
self._location = location
5753

5854
# Ignore the name error as we are overriding the method.
59-
def visit_Import( # pylint: disable=invalid-name
55+
def visit_Import( # noqa: N802, pylint: disable=invalid-name
6056
self,
6157
node: ast.Import,
6258
) -> None:
6359
for alias in node.names:
6460
self._add_module(alias.name, node.lineno)
6561

6662
# Ignore the name error as we are overriding the method.
67-
def visit_ImportFrom( # pylint: disable=invalid-name
63+
def visit_ImportFrom( # noqa: N802, pylint: disable=invalid-name
6864
self,
6965
node: ast.ImportFrom,
7066
) -> None:
@@ -104,7 +100,7 @@ def _add_module(self, modname: str, lineno: int) -> None:
104100
break
105101

106102
# ... though it might not be a file, so not interesting to us
107-
if not os.path.isdir(modpath):
103+
if not Path(modpath).is_dir():
108104
break
109105

110106
path = [modpath]
@@ -120,18 +116,18 @@ def _add_module(self, modname: str, lineno: int) -> None:
120116
assert isinstance(self._location, str)
121117
self._modules[modname].locations.append((self._location, lineno))
122118

123-
def finalise(self) -> Dict[str, FoundModule]:
124-
result = self._modules
125-
return result
119+
def finalise(self) -> dict[str, FoundModule]:
120+
return self._modules
126121

127122

128123
def pyfiles(root: Path) -> Generator[Path, None, None]:
129124
if root.is_file():
130125
if root.suffix == ".py":
131126
yield root.absolute()
132127
else:
133-
raise ValueError(f"{root} is not a python file or directory")
134-
elif root.is_dir():
128+
msg = f"{root} is not a python file or directory"
129+
raise ValueError(msg)
130+
else:
135131
for item in root.rglob("*.py"):
136132
yield item.absolute()
137133

@@ -140,31 +136,33 @@ def find_imported_modules(
140136
paths: Iterable[Path],
141137
ignore_files_function: Callable[[str], bool],
142138
ignore_modules_function: Callable[[str], bool],
143-
) -> Dict[str, FoundModule]:
139+
) -> dict[str, FoundModule]:
144140
vis = _ImportVisitor(ignore_modules_function=ignore_modules_function)
145141
for path in paths:
146142
for filename in pyfiles(path):
147143
if ignore_files_function(str(filename)):
148144
log.info("ignoring: %s", os.path.relpath(filename))
149145
continue
150146
log.debug("scanning: %s", os.path.relpath(filename))
151-
with open(filename, encoding="utf-8") as file_obj:
152-
content = file_obj.read()
147+
content = filename.read_text(encoding="utf-8")
153148
vis.set_location(str(filename))
154149
vis.visit(ast.parse(content, str(filename)))
155150
return vis.finalise()
156151

157152

158153
def find_required_modules(
154+
*,
159155
ignore_requirements_function: Callable[
160-
[Union[str, ParsedRequirement]], bool
156+
[str | ParsedRequirement],
157+
bool,
161158
],
162159
skip_incompatible: bool,
163160
requirements_filename: Path,
164-
) -> Set[NormalizedName]:
161+
) -> set[NormalizedName]:
165162
explicit = set()
166163
for requirement in parse_requirements(
167-
str(requirements_filename), session=PipSession()
164+
str(requirements_filename),
165+
session=PipSession(),
168166
):
169167
requirement_name = install_req_from_line(
170168
requirement.requirement,
@@ -203,22 +201,23 @@ def has_compatible_markers(full_requirement: str) -> bool:
203201

204202

205203
def is_package_file(path: str) -> str:
206-
"""Determines whether the path points to a Python package sentinel
207-
file - the __init__.py or its compiled variants.
204+
"""Determine whether the path points to a Python package sentinel file.
205+
206+
A sentinel file is the __init__.py or its compiled variants.
208207
"""
209208
search_result = re.search(r"(.+)/__init__\.py[co]?$", path)
210209
if search_result is not None:
211210
return search_result.group(1)
212211
return ""
213212

214213

215-
def ignorer(ignore_cfg: List[str]) -> Callable[..., bool]:
214+
def ignorer(ignore_cfg: list[str]) -> Callable[..., bool]:
216215
if not ignore_cfg:
217-
return lambda candidate: False
216+
return lambda _: False
218217

219218
def ignorer_function(
220-
candidate: Union[str, ParsedRequirement],
221-
ignore_cfg: List[str] = ignore_cfg,
219+
candidate: str | ParsedRequirement,
220+
ignore_cfg: list[str] = ignore_cfg,
222221
) -> bool:
223222
for ignore in ignore_cfg:
224223
if isinstance(candidate, str):

pip_check_reqs/find_extra_reqs.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,41 @@
11
"""Find extra requirements."""
22

3+
from __future__ import annotations
4+
35
import argparse
46
import collections
57
import importlib.metadata
68
import logging
79
import os
810
import sys
911
from pathlib import Path
10-
from typing import Callable, Iterable, List, Optional, Union
12+
from typing import TYPE_CHECKING, Callable, Iterable
1113
from unittest import mock
1214

1315
from packaging.utils import canonicalize_name
1416
from pip._internal.commands.show import search_packages_info
15-
from pip._internal.req.req_file import ParsedRequirement
1617

1718
from pip_check_reqs import common
1819
from pip_check_reqs.common import version_info
1920

21+
if TYPE_CHECKING:
22+
from pip._internal.req.req_file import ParsedRequirement
23+
2024
log = logging.getLogger(__name__)
2125

2226

2327
def find_extra_reqs(
28+
*,
2429
requirements_filename: Path,
2530
paths: Iterable[Path],
2631
ignore_files_function: Callable[[str], bool],
2732
ignore_modules_function: Callable[[str], bool],
2833
ignore_requirements_function: Callable[
29-
[Union[str, ParsedRequirement]], bool
34+
[str | ParsedRequirement],
35+
bool,
3036
],
3137
skip_incompatible: bool,
32-
) -> List[str]:
38+
) -> list[str]:
3339
# 1. find files used by imports in the code (as best we can without
3440
# executing)
3541
used_modules = common.find_imported_modules(
@@ -55,7 +61,7 @@ def find_extra_reqs(
5561
package_location = package.location
5662
package_files = []
5763
for item in package.files or []:
58-
here = Path(".").resolve()
64+
here = Path().resolve()
5965
item_location_rel = Path(package_location) / item
6066
item_location = item_location_rel.resolve()
6167
try:
@@ -68,11 +74,13 @@ def find_extra_reqs(
6874
package_files.append(str(relative_item_location))
6975

7076
log.debug(
71-
"installed package: %s (at %s)", package_name, package_location
77+
"installed package: %s (at %s)",
78+
package_name,
79+
package_location,
7280
)
7381
for package_file in package_files:
7482
path = os.path.realpath(
75-
os.path.join(package_location, package_file),
83+
Path(package_location) / package_file,
7684
)
7785
installed_files[path] = package_name
7886
package_path = common.is_package_file(path)
@@ -112,8 +120,8 @@ def find_extra_reqs(
112120
return [name for name in explicit if name not in used]
113121

114122

115-
def main(arguments: Optional[List[str]] = None) -> None:
116-
"""Main entry point."""
123+
def main(arguments: list[str] | None = None) -> None:
124+
"""pip-extra-reqs entry point."""
117125
usage = "usage: %prog [options] files or directories"
118126
parser = argparse.ArgumentParser(usage)
119127
parser.add_argument("paths", type=Path, nargs="*")
@@ -156,7 +164,7 @@ def main(arguments: Optional[List[str]] = None) -> None:
156164
dest="skip_incompatible",
157165
action="store_true",
158166
default=False,
159-
help="skip requirements that have incompatible " "environment markers",
167+
help="skip requirements that have incompatible environment markers",
160168
)
161169
parser.add_argument(
162170
"-v",
@@ -186,7 +194,7 @@ def main(arguments: Optional[List[str]] = None) -> None:
186194
parse_result = parser.parse_args(arguments)
187195

188196
if parse_result.version:
189-
print(version_info())
197+
sys.stdout.write(version_info() + "\n")
190198
sys.exit(0)
191199

192200
if not parse_result.paths:

pip_check_reqs/find_missing_reqs.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
"""Find missing requirements."""
22

3+
from __future__ import annotations
4+
35
import argparse
46
import collections
57
import importlib.metadata
68
import logging
79
import os
810
import sys
911
from pathlib import Path
10-
from typing import Callable, Iterable, List, Optional, Tuple
12+
from typing import Callable, Iterable
1113
from unittest import mock
1214

1315
from packaging.utils import NormalizedName, canonicalize_name
@@ -27,7 +29,7 @@ def find_missing_reqs(
2729
paths: Iterable[Path],
2830
ignore_files_function: Callable[[str], bool],
2931
ignore_modules_function: Callable[[str], bool],
30-
) -> List[Tuple[NormalizedName, List[FoundModule]]]:
32+
) -> list[tuple[NormalizedName, list[FoundModule]]]:
3133
# 1. find files used by imports in the code (as best we can without
3234
# executing)
3335
used_modules = common.find_imported_modules(
@@ -53,7 +55,7 @@ def find_missing_reqs(
5355
package_location = package.location
5456
package_files = []
5557
for item in package.files or []:
56-
here = Path(".").resolve()
58+
here = Path().resolve()
5759
item_location_rel = Path(package_location) / item
5860
item_location = item_location_rel.resolve()
5961
try:
@@ -66,11 +68,13 @@ def find_missing_reqs(
6668
package_files.append(str(relative_item_location))
6769

6870
log.debug(
69-
"installed package: %s (at %s)", package_name, package_location
71+
"installed package: %s (at %s)",
72+
package_name,
73+
package_location,
7074
)
7175
for package_file in package_files:
7276
path = os.path.realpath(
73-
os.path.join(package_location, package_file),
77+
str(Path(package_location) / package_file),
7478
)
7579
installed_files[path] = package_name
7680
package_path = common.is_package_file(path)
@@ -113,11 +117,10 @@ def find_missing_reqs(
113117
log.debug("found requirement: %s", requirement_name)
114118
explicit.add(canonicalize_name(requirement_name))
115119

116-
result = [(name, used[name]) for name in used if name not in explicit]
117-
return result
120+
return [(name, used[name]) for name in used if name not in explicit]
118121

119122

120-
def main(arguments: Optional[List[str]] = None) -> None:
123+
def main(arguments: list[str] | None = None) -> None:
121124
usage = "usage: %prog [options] files or directories"
122125
parser = argparse.ArgumentParser(usage)
123126
parser.add_argument("paths", type=Path, nargs="*")
@@ -174,7 +177,7 @@ def main(arguments: Optional[List[str]] = None) -> None:
174177
parse_result = parser.parse_args(arguments)
175178

176179
if parse_result.version:
177-
print(version_info())
180+
sys.stdout.write(version_info() + "\n")
178181
sys.exit(0)
179182

180183
if not parse_result.paths:

0 commit comments

Comments
 (0)