1
1
"""Common functions."""
2
2
3
+ from __future__ import annotations
4
+
3
5
import ast
4
6
import fnmatch
5
7
import imp
11
13
from pathlib import Path
12
14
from typing import (
13
15
Callable ,
14
- Dict ,
15
16
Generator ,
16
17
Iterable ,
17
- List ,
18
- Optional ,
19
- Set ,
20
- Tuple ,
21
- Union ,
22
18
)
23
19
24
20
from packaging .markers import Marker
@@ -38,7 +34,7 @@ class FoundModule:
38
34
39
35
modname : str
40
36
filename : str
41
- locations : List [ Tuple [str , int ]] = field (default_factory = list )
37
+ locations : list [ tuple [str , int ]] = field (default_factory = list )
42
38
43
39
def __post_init__ (self ) -> None :
44
40
self .filename = os .path .realpath (self .filename )
@@ -48,22 +44,22 @@ class _ImportVisitor(ast.NodeVisitor):
48
44
def __init__ (self , ignore_modules_function : Callable [[str ], bool ]) -> None :
49
45
super ().__init__ ()
50
46
self ._ignore_modules_function = ignore_modules_function
51
- self ._modules : Dict [str , FoundModule ] = {}
52
- self ._location : Optional [ str ] = None
47
+ self ._modules : dict [str , FoundModule ] = {}
48
+ self ._location : str | None = None
53
49
54
50
def set_location (self , location : str ) -> None :
55
51
self ._location = location
56
52
57
53
# Ignore the name error as we are overriding the method.
58
- def visit_Import ( # pylint: disable=invalid-name
54
+ def visit_Import ( # noqa: N802, pylint: disable=invalid-name
59
55
self ,
60
56
node : ast .Import ,
61
57
) -> None :
62
58
for alias in node .names :
63
59
self ._add_module (alias .name , node .lineno )
64
60
65
61
# Ignore the name error as we are overriding the method.
66
- def visit_ImportFrom ( # pylint: disable=invalid-name
62
+ def visit_ImportFrom ( # noqa: N802, pylint: disable=invalid-name
67
63
self ,
68
64
node : ast .ImportFrom ,
69
65
) -> None :
@@ -103,7 +99,7 @@ def _add_module(self, modname: str, lineno: int) -> None:
103
99
break
104
100
105
101
# ... though it might not be a file, so not interesting to us
106
- if not os . path . isdir (modpath ):
102
+ if not Path (modpath ). is_dir ( ):
107
103
break
108
104
109
105
path = [modpath ]
@@ -119,17 +115,17 @@ def _add_module(self, modname: str, lineno: int) -> None:
119
115
assert isinstance (self ._location , str )
120
116
self ._modules [modname ].locations .append ((self ._location , lineno ))
121
117
122
- def finalise (self ) -> Dict [str , FoundModule ]:
123
- result = self ._modules
124
- return result
118
+ def finalise (self ) -> dict [str , FoundModule ]:
119
+ return self ._modules
125
120
126
121
127
122
def pyfiles (root : Path ) -> Generator [Path , None , None ]:
128
123
if root .is_file ():
129
124
if root .suffix == ".py" :
130
125
yield root .absolute ()
131
126
else :
132
- raise ValueError (f"{ root } is not a python file or directory" )
127
+ msg = f"{ root } is not a python file or directory"
128
+ raise ValueError (msg )
133
129
elif root .is_dir ():
134
130
for item in root .rglob ("*.py" ):
135
131
yield item .absolute ()
@@ -139,31 +135,33 @@ def find_imported_modules(
139
135
paths : Iterable [Path ],
140
136
ignore_files_function : Callable [[str ], bool ],
141
137
ignore_modules_function : Callable [[str ], bool ],
142
- ) -> Dict [str , FoundModule ]:
138
+ ) -> dict [str , FoundModule ]:
143
139
vis = _ImportVisitor (ignore_modules_function = ignore_modules_function )
144
140
for path in paths :
145
141
for filename in pyfiles (path ):
146
142
if ignore_files_function (str (filename )):
147
143
log .info ("ignoring: %s" , os .path .relpath (filename ))
148
144
continue
149
145
log .debug ("scanning: %s" , os .path .relpath (filename ))
150
- with open (filename , encoding = "utf-8" ) as file_obj :
151
- content = file_obj .read ()
146
+ content = filename .read_text (encoding = "utf-8" )
152
147
vis .set_location (str (filename ))
153
148
vis .visit (ast .parse (content , str (filename )))
154
149
return vis .finalise ()
155
150
156
151
157
152
def find_required_modules (
153
+ * ,
158
154
ignore_requirements_function : Callable [
159
- [Union [str , ParsedRequirement ]], bool
155
+ [str | ParsedRequirement ],
156
+ bool ,
160
157
],
161
158
skip_incompatible : bool ,
162
159
requirements_filename : Path ,
163
- ) -> Set [NormalizedName ]:
160
+ ) -> set [NormalizedName ]:
164
161
explicit = set ()
165
162
for requirement in parse_requirements (
166
- str (requirements_filename ), session = PipSession ()
163
+ str (requirements_filename ),
164
+ session = PipSession (),
167
165
):
168
166
requirement_name = install_req_from_line (
169
167
requirement .requirement ,
@@ -202,22 +200,23 @@ def has_compatible_markers(full_requirement: str) -> bool:
202
200
203
201
204
202
def is_package_file (path : str ) -> str :
205
- """Determines whether the path points to a Python package sentinel
206
- file - the __init__.py or its compiled variants.
203
+ """Determine whether the path points to a Python package sentinel file.
204
+
205
+ A sentinel file is the __init__.py or its compiled variants.
207
206
"""
208
207
search_result = re .search (r"(.+)/__init__\.py[co]?$" , path )
209
208
if search_result is not None :
210
209
return search_result .group (1 )
211
210
return ""
212
211
213
212
214
- def ignorer (ignore_cfg : List [str ]) -> Callable [..., bool ]:
213
+ def ignorer (ignore_cfg : list [str ]) -> Callable [..., bool ]:
215
214
if not ignore_cfg :
216
- return lambda candidate : False
215
+ return lambda _ : False
217
216
218
217
def ignorer_function (
219
- candidate : Union [ str , ParsedRequirement ] ,
220
- ignore_cfg : List [str ] = ignore_cfg ,
218
+ candidate : str | ParsedRequirement ,
219
+ ignore_cfg : list [str ] = ignore_cfg ,
221
220
) -> bool :
222
221
for ignore in ignore_cfg :
223
222
if isinstance (candidate , str ):
0 commit comments