@@ -188,10 +188,46 @@ def default_flush_errors(new_messages: List[str], is_serious: bool) -> None:
188
188
raise
189
189
190
190
191
- def compute_lib_path (sources : List [BuildSource ],
191
+ # python_path is usercode, mypy_path is set via config or envionment variable,
192
+ # package_path is calculated by _get_site_packages_dirs, and typeshed_path points
193
+ # to typeshed. Each is a tuple of paths to be searched in find_module()
194
+ SearchPaths = NamedTuple ('SearchPaths' ,
195
+ (('python_path' , Tuple [str , ...]),
196
+ ('mypy_path' , Tuple [str , ...]),
197
+ ('package_path' , Tuple [str , ...]),
198
+ ('typeshed_path' , Tuple [str , ...])))
199
+
200
+
201
+ @functools .lru_cache (maxsize = None )
202
+ def _get_site_packages_dirs (python_executable : Optional [str ]) -> List [str ]:
203
+ """Find package directories for given python.
204
+
205
+ This runs a subprocess call, which generates a list of the site package directories.
206
+ To avoid repeatedly calling a subprocess (which can be slow!) we lru_cache the results."""
207
+ if python_executable is None :
208
+ return []
209
+ if python_executable == sys .executable :
210
+ # Use running Python's package dirs
211
+ return sitepkgs .getsitepackages ()
212
+ else :
213
+ # Use subprocess to get the package directory of given Python
214
+ # executable
215
+ return ast .literal_eval (subprocess .check_output ([python_executable , sitepkgs .__file__ ],
216
+ stderr = subprocess .PIPE ).decode ())
217
+
218
+
219
+ def compute_search_paths (sources : List [BuildSource ],
192
220
options : Options ,
193
221
data_dir : str ,
194
- alt_lib_path : Optional [str ] = None ) -> List [str ]:
222
+ alt_lib_path : Optional [str ] = None ) -> SearchPaths :
223
+ """Compute the search paths as specified in PEP 561.
224
+
225
+ There are the following 4 members created:
226
+ - User code (from `sources`)
227
+ - MYPYPATH (set either via config or environment variable)
228
+ - installed package directories (which will later be split into stub-only and inline)
229
+ - typeshed
230
+ """
195
231
# Determine the default module search path.
196
232
lib_path = collections .deque (
197
233
default_lib_path (data_dir ,
@@ -206,15 +242,14 @@ def compute_lib_path(sources: List[BuildSource],
206
242
lib_path .appendleft (os .path .join (root_dir , 'test-data' , 'unit' , 'lib-stub' ))
207
243
# alt_lib_path is used by some tests to bypass the normal lib_path mechanics.
208
244
# If we don't have one, grab directories of source files.
209
- lib_path_set = set ( lib_path )
245
+ python_path = [] # type: List[str]
210
246
if not alt_lib_path :
211
247
for source in sources :
212
248
# Include directory of the program file in the module search path.
213
249
if source .base_dir :
214
250
dir = source .base_dir
215
- if dir not in lib_path_set :
216
- lib_path .appendleft (dir )
217
- lib_path_set .add (dir )
251
+ if dir not in python_path :
252
+ python_path .append (dir )
218
253
219
254
# Do this even if running as a file, for sanity (mainly because with
220
255
# multiple builds, there could be a mix of files/modules, so its easier
@@ -227,20 +262,23 @@ def compute_lib_path(sources: List[BuildSource],
227
262
else :
228
263
dir = os .getcwd ()
229
264
if dir not in lib_path :
230
- lib_path . appendleft ( dir )
265
+ python_path . insert ( 0 , dir )
231
266
232
- # Prepend a config-defined mypy path .
233
- lib_path . extendleft ( options . mypy_path )
267
+ # Start with a MYPYPATH environment variable at the front of the mypy_path, if defined .
268
+ mypypath = mypy_path ( )
234
269
235
- # Add MYPYPATH environment variable to front of library path, if defined .
236
- lib_path . extendleft ( mypy_path () )
270
+ # Add a config-defined mypy path.
271
+ mypypath . extend ( options . mypy_path )
237
272
238
273
# If provided, insert the caller-supplied extra module path to the
239
274
# beginning (highest priority) of the search path.
240
275
if alt_lib_path :
241
- lib_path . appendleft ( alt_lib_path )
276
+ mypypath . insert ( 0 , alt_lib_path )
242
277
243
- return list (lib_path )
278
+ return SearchPaths (tuple (reversed (python_path )),
279
+ tuple (mypypath ),
280
+ tuple (_get_site_packages_dirs (options .python_executable )),
281
+ tuple (lib_path ))
244
282
245
283
246
284
def _build (sources : List [BuildSource ],
@@ -256,7 +294,7 @@ def _build(sources: List[BuildSource],
256
294
data_dir = default_data_dir (bin_dir )
257
295
fscache = fscache or FileSystemCache ()
258
296
259
- lib_path = compute_lib_path (sources , options , data_dir , alt_lib_path )
297
+ search_paths = compute_search_paths (sources , options , data_dir , alt_lib_path )
260
298
261
299
reports = Reports (data_dir , options .report_dirs )
262
300
source_set = BuildSourceSet (sources )
@@ -266,7 +304,7 @@ def _build(sources: List[BuildSource],
266
304
# Construct a build manager object to hold state during the build.
267
305
#
268
306
# Ignore current directory prefix in error messages.
269
- manager = BuildManager (data_dir , lib_path ,
307
+ manager = BuildManager (data_dir , search_paths ,
270
308
ignore_prefix = os .getcwd (),
271
309
source_set = source_set ,
272
310
reports = reports ,
@@ -613,7 +651,7 @@ class BuildManager:
613
651
"""
614
652
615
653
def __init__ (self , data_dir : str ,
616
- lib_path : List [ str ] ,
654
+ search_paths : SearchPaths ,
617
655
ignore_prefix : str ,
618
656
source_set : BuildSourceSet ,
619
657
reports : Reports ,
@@ -628,7 +666,7 @@ def __init__(self, data_dir: str,
628
666
self .data_dir = data_dir
629
667
self .errors = errors
630
668
self .errors .set_ignore_prefix (ignore_prefix )
631
- self .lib_path = tuple ( lib_path )
669
+ self .search_paths = search_paths
632
670
self .source_set = source_set
633
671
self .reports = reports
634
672
self .options = options
@@ -637,7 +675,7 @@ def __init__(self, data_dir: str,
637
675
self .missing_modules = set () # type: Set[str]
638
676
self .plugin = plugin
639
677
self .semantic_analyzer = SemanticAnalyzerPass2 (self .modules , self .missing_modules ,
640
- lib_path , self .errors , self .plugin )
678
+ self .errors , self .plugin )
641
679
self .semantic_analyzer_pass3 = SemanticAnalyzerPass3 (self .modules , self .errors ,
642
680
self .semantic_analyzer )
643
681
self .all_types = {} # type: Dict[Expression, Type] # Used by tests only
@@ -780,7 +818,7 @@ def correct_rel_imp(imp: Union[ImportFrom, ImportAll]) -> str:
780
818
781
819
def is_module (self , id : str ) -> bool :
782
820
"""Is there a file in the file system corresponding to module id?"""
783
- return self .find_module_cache .find_module (id , self .lib_path ,
821
+ return self .find_module_cache .find_module (id , self .search_paths ,
784
822
self .options .python_executable ) is not None
785
823
786
824
def parse_file (self , id : str , path : str , source : str , ignore_errors : bool ) -> MypyFile :
@@ -844,26 +882,8 @@ def stats_summary(self) -> Mapping[str, object]:
844
882
return self .stats
845
883
846
884
847
- @functools .lru_cache (maxsize = None )
848
- def _get_site_packages_dirs (python_executable : Optional [str ]) -> List [str ]:
849
- """Find package directories for given python.
850
-
851
- This runs a subprocess call, which generates a list of the site package directories.
852
- To avoid repeatedly calling a subprocess (which can be slow!) we lru_cache the results."""
853
- if python_executable is None :
854
- return []
855
- if python_executable == sys .executable :
856
- # Use running Python's package dirs
857
- return sitepkgs .getsitepackages ()
858
- else :
859
- # Use subprocess to get the package directory of given Python
860
- # executable
861
- return ast .literal_eval (subprocess .check_output ([python_executable , sitepkgs .__file__ ],
862
- stderr = subprocess .PIPE ).decode ())
863
-
864
-
865
- # Search paths are a two-tuple of path and whether to verify the module
866
- SearchPaths = List [Tuple [str , bool ]]
885
+ # Package dirs are a two-tuple of path to search and whether to verify the module
886
+ PackageDirs = List [Tuple [str , bool ]]
867
887
868
888
869
889
class FindModuleCache :
@@ -879,26 +899,27 @@ class FindModuleCache:
879
899
880
900
def __init__ (self , fscache : Optional [FileSystemCache ] = None ) -> None :
881
901
self .fscache = fscache or FileSystemCache ()
882
- # Cache find_lib_path_dirs: (dir_chain, lib_path ) -> list of (package_path , should_verify)
883
- self .dirs = {} # type: Dict[Tuple[str, Tuple[str, ...]], SearchPaths ]
884
- # Cache find_module: (id, lib_path , python_version) -> result.
885
- self .results = {} # type: Dict[Tuple[str, Tuple[str, ...] , Optional[str]], Optional[str]]
902
+ # Cache find_lib_path_dirs: (dir_chain, search_paths ) -> list(package_dirs , should_verify)
903
+ self .dirs = {} # type: Dict[Tuple[str, Tuple[str, ...]], PackageDirs ]
904
+ # Cache find_module: (id, search_paths , python_version) -> result.
905
+ self .results = {} # type: Dict[Tuple[str, SearchPaths , Optional[str]], Optional[str]]
886
906
887
907
def clear (self ) -> None :
888
908
self .results .clear ()
889
909
self .dirs .clear ()
890
910
891
- def find_lib_path_dirs (self , dir_chain : str , lib_path : Tuple [str , ...]) -> SearchPaths :
911
+ def find_lib_path_dirs (self , dir_chain : str , lib_path : Tuple [str , ...]) -> PackageDirs :
892
912
# Cache some repeated work within distinct find_module calls: finding which
893
913
# elements of lib_path have even the subdirectory they'd need for the module
894
- # to exist. This is shared among different module ids when they differ only
914
+ # to exist. This is shared among different module ids when they differ only
895
915
# in the last component.
916
+ # This is run for the python_path, mypy_path, and typeshed_path search paths
896
917
key = (dir_chain , lib_path )
897
918
if key not in self .dirs :
898
919
self .dirs [key ] = self ._find_lib_path_dirs (dir_chain , lib_path )
899
920
return self .dirs [key ]
900
921
901
- def _find_lib_path_dirs (self , dir_chain : str , lib_path : Tuple [str , ...]) -> SearchPaths :
922
+ def _find_lib_path_dirs (self , dir_chain : str , lib_path : Tuple [str , ...]) -> PackageDirs :
902
923
dirs = []
903
924
for pathitem in lib_path :
904
925
# e.g., '/usr/lib/python3.4/foo/bar'
@@ -907,15 +928,15 @@ def _find_lib_path_dirs(self, dir_chain: str, lib_path: Tuple[str, ...]) -> Sear
907
928
dirs .append ((dir , True ))
908
929
return dirs
909
930
910
- def find_module (self , id : str , lib_path : Tuple [ str , ...] ,
931
+ def find_module (self , id : str , search_paths : SearchPaths ,
911
932
python_executable : Optional [str ]) -> Optional [str ]:
912
933
"""Return the path of the module source file, or None if not found."""
913
- key = (id , lib_path , python_executable )
934
+ key = (id , search_paths , python_executable )
914
935
if key not in self .results :
915
- self .results [key ] = self ._find_module (id , lib_path , python_executable )
936
+ self .results [key ] = self ._find_module (id , search_paths , python_executable )
916
937
return self .results [key ]
917
938
918
- def _find_module (self , id : str , lib_path : Tuple [ str , ...] ,
939
+ def _find_module (self , id : str , search_paths : SearchPaths ,
919
940
python_executable : Optional [str ]) -> Optional [str ]:
920
941
fscache = self .fscache
921
942
@@ -932,7 +953,7 @@ def _find_module(self, id: str, lib_path: Tuple[str, ...],
932
953
third_party_inline_dirs = []
933
954
third_party_stubs_dirs = []
934
955
# Third-party stub/typed packages
935
- for pkg_dir in _get_site_packages_dirs ( python_executable ) :
956
+ for pkg_dir in search_paths . package_path :
936
957
stub_name = components [0 ] + '-stubs'
937
958
typed_file = os .path .join (pkg_dir , components [0 ], 'py.typed' )
938
959
stub_dir = os .path .join (pkg_dir , stub_name )
@@ -957,8 +978,10 @@ def _find_module(self, id: str, lib_path: Tuple[str, ...],
957
978
elif fscache .isfile (typed_file ):
958
979
path = os .path .join (pkg_dir , dir_chain )
959
980
third_party_inline_dirs .append ((path , True ))
960
- candidate_base_dirs = self .find_lib_path_dirs (dir_chain , lib_path ) + \
961
- third_party_stubs_dirs + third_party_inline_dirs
981
+ python_mypy_path = search_paths .python_path + search_paths .mypy_path
982
+ candidate_base_dirs = self .find_lib_path_dirs (dir_chain , python_mypy_path ) + \
983
+ third_party_stubs_dirs + third_party_inline_dirs + \
984
+ self .find_lib_path_dirs (dir_chain , search_paths .typeshed_path )
962
985
963
986
# If we're looking for a module like 'foo.bar.baz', then candidate_base_dirs now
964
987
# contains just the subdirectories 'foo/bar' that actually exist under the
@@ -989,9 +1012,9 @@ def _find_module(self, id: str, lib_path: Tuple[str, ...],
989
1012
return path
990
1013
return None
991
1014
992
- def find_modules_recursive (self , module : str , lib_path : Tuple [ str , ...] ,
1015
+ def find_modules_recursive (self , module : str , search_paths : SearchPaths ,
993
1016
python_executable : Optional [str ]) -> List [BuildSource ]:
994
- module_path = self .find_module (module , lib_path , python_executable )
1017
+ module_path = self .find_module (module , search_paths , python_executable )
995
1018
if not module_path :
996
1019
return []
997
1020
result = [BuildSource (module_path , module , None )]
@@ -1011,14 +1034,14 @@ def find_modules_recursive(self, module: str, lib_path: Tuple[str, ...],
1011
1034
(os .path .isfile (os .path .join (abs_path , '__init__.py' )) or
1012
1035
os .path .isfile (os .path .join (abs_path , '__init__.pyi' ))):
1013
1036
hits .add (item )
1014
- result += self .find_modules_recursive (module + '.' + item , lib_path ,
1037
+ result += self .find_modules_recursive (module + '.' + item , search_paths ,
1015
1038
python_executable )
1016
1039
elif item != '__init__.py' and item != '__init__.pyi' and \
1017
1040
item .endswith (('.py' , '.pyi' )):
1018
1041
mod = item .split ('.' )[0 ]
1019
1042
if mod not in hits :
1020
1043
hits .add (mod )
1021
- result += self .find_modules_recursive (module + '.' + mod , lib_path ,
1044
+ result += self .find_modules_recursive (module + '.' + mod , search_paths ,
1022
1045
python_executable )
1023
1046
return result
1024
1047
@@ -2304,7 +2327,7 @@ def find_module_and_diagnose(manager: BuildManager,
2304
2327
# difference and just assume 'builtins' everywhere,
2305
2328
# which simplifies code.
2306
2329
file_id = '__builtin__'
2307
- path = manager .find_module_cache .find_module (file_id , manager .lib_path ,
2330
+ path = manager .find_module_cache .find_module (file_id , manager .search_paths ,
2308
2331
manager .options .python_executable )
2309
2332
if path :
2310
2333
# For non-stubs, look at options.follow_imports:
0 commit comments