@@ -16,7 +16,7 @@ class InvalidSourceList(Exception):
16
16
"""Exception indicating a problem in the list of sources given to mypy."""
17
17
18
18
19
- def create_source_list (files : Sequence [str ], options : Options ,
19
+ def create_source_list (paths : Sequence [str ], options : Options ,
20
20
fscache : Optional [FileSystemCache ] = None ,
21
21
allow_empty_dir : bool = False ) -> List [BuildSource ]:
22
22
"""From a list of source files/directories, makes a list of BuildSources.
@@ -26,22 +26,24 @@ def create_source_list(files: Sequence[str], options: Options,
26
26
fscache = fscache or FileSystemCache ()
27
27
finder = SourceFinder (fscache )
28
28
29
- targets = []
30
- for f in files :
31
- if f .endswith (PY_EXTENSIONS ):
29
+ sources = []
30
+ for path in paths :
31
+ path = os .path .normpath (path )
32
+ if path .endswith (PY_EXTENSIONS ):
32
33
# Can raise InvalidSourceList if a directory doesn't have a valid module name.
33
- name , base_dir = finder .crawl_up (os .path .normpath (f ))
34
- targets .append (BuildSource (f , name , None , base_dir ))
35
- elif fscache .isdir (f ):
36
- sub_targets = finder .expand_dir (os .path .normpath (f ))
37
- if not sub_targets and not allow_empty_dir :
38
- raise InvalidSourceList ("There are no .py[i] files in directory '{}'"
39
- .format (f ))
40
- targets .extend (sub_targets )
34
+ name , base_dir = finder .crawl_up (path )
35
+ sources .append (BuildSource (path , name , None , base_dir ))
36
+ elif fscache .isdir (path ):
37
+ sub_sources = finder .find_sources_in_dir (path , explicit_package_roots = None )
38
+ if not sub_sources and not allow_empty_dir :
39
+ raise InvalidSourceList (
40
+ "There are no .py[i] files in directory '{}'" .format (path )
41
+ )
42
+ sources .extend (sub_sources )
41
43
else :
42
- mod = os .path .basename (f ) if options .scripts_are_modules else None
43
- targets .append (BuildSource (f , mod , None ))
44
- return targets
44
+ mod = os .path .basename (path ) if options .scripts_are_modules else None
45
+ sources .append (BuildSource (path , mod , None ))
46
+ return sources
45
47
46
48
47
49
def keyfunc (name : str ) -> Tuple [int , str ]:
@@ -62,57 +64,82 @@ def __init__(self, fscache: FileSystemCache) -> None:
62
64
# A cache for package names, mapping from directory path to module id and base dir
63
65
self .package_cache = {} # type: Dict[str, Tuple[str, str]]
64
66
65
- def expand_dir (self , arg : str , mod_prefix : str = '' ) -> List [BuildSource ]:
66
- """Convert a directory name to a list of sources to build."""
67
- f = self .get_init_file (arg )
68
- if mod_prefix and not f :
69
- return []
67
+ def find_sources_in_dir (
68
+ self , path : str , explicit_package_roots : Optional [List [str ]]
69
+ ) -> List [BuildSource ]:
70
+ if explicit_package_roots is None :
71
+ mod_prefix , root_dir = self .crawl_up_dir (path )
72
+ else :
73
+ mod_prefix = os .path .basename (path )
74
+ root_dir = os .path .dirname (path ) or "."
75
+ if mod_prefix :
76
+ mod_prefix += "."
77
+ return self .find_sources_in_dir_helper (path , mod_prefix , root_dir , explicit_package_roots )
78
+
79
+ def find_sources_in_dir_helper (
80
+ self , dir_path : str , mod_prefix : str , root_dir : str ,
81
+ explicit_package_roots : Optional [List [str ]]
82
+ ) -> List [BuildSource ]:
83
+ assert not mod_prefix or mod_prefix .endswith ("." )
84
+
85
+ init_file = self .get_init_file (dir_path )
86
+ # If the current directory is an explicit package root, explore it as such.
87
+ # Alternatively, if we aren't given explicit package roots and we don't have an __init__
88
+ # file, recursively explore this directory as a new package root.
89
+ if (
90
+ (explicit_package_roots is not None and dir_path in explicit_package_roots )
91
+ or (explicit_package_roots is None and init_file is None )
92
+ ):
93
+ mod_prefix = ""
94
+ root_dir = dir_path
95
+
70
96
seen = set () # type: Set[str]
71
97
sources = []
72
- top_mod , base_dir = self .crawl_up_dir (arg )
73
- if f and not mod_prefix :
74
- mod_prefix = top_mod + '.'
75
- if mod_prefix :
76
- sources .append (BuildSource (f , mod_prefix .rstrip ('.' ), None , base_dir ))
77
- names = self .fscache .listdir (arg )
98
+
99
+ if init_file :
100
+ sources .append (BuildSource (init_file , mod_prefix .rstrip ("." ), None , root_dir ))
101
+
102
+ names = self .fscache .listdir (dir_path )
78
103
names .sort (key = keyfunc )
79
104
for name in names :
80
105
# Skip certain names altogether
81
- if (name == '__pycache__' or name == 'py.typed'
82
- or name .startswith ('.' )
83
- or name .endswith (('~' , '.pyc' , '.pyo' ))):
106
+ if name == '__pycache__' or name .startswith ('.' ) or name .endswith ('~' ):
84
107
continue
85
- path = os .path .join (arg , name )
108
+ path = os .path .join (dir_path , name )
109
+
86
110
if self .fscache .isdir (path ):
87
- sub_sources = self .expand_dir (path , mod_prefix + name + '.' )
111
+ sub_sources = self .find_sources_in_dir_helper (
112
+ path , mod_prefix + name + '.' , root_dir , explicit_package_roots
113
+ )
88
114
if sub_sources :
89
115
seen .add (name )
90
116
sources .extend (sub_sources )
91
117
else :
92
- base , suffix = os .path .splitext (name )
93
- if base == '__init__' :
118
+ stem , suffix = os .path .splitext (name )
119
+ if stem == '__init__' :
94
120
continue
95
- if base not in seen and '.' not in base and suffix in PY_EXTENSIONS :
96
- seen .add (base )
97
- src = BuildSource (path , mod_prefix + base , None , base_dir )
121
+ if stem not in seen and '.' not in stem and suffix in PY_EXTENSIONS :
122
+ seen .add (stem )
123
+ src = BuildSource (path , mod_prefix + stem , None , root_dir )
98
124
sources .append (src )
125
+
99
126
return sources
100
127
101
- def crawl_up (self , arg : str ) -> Tuple [str , str ]:
128
+ def crawl_up (self , path : str ) -> Tuple [str , str ]:
102
129
"""Given a .py[i] filename, return module and base directory
103
130
104
131
We crawl up the path until we find a directory without
105
132
__init__.py[i], or until we run out of path components.
106
133
"""
107
- dir , mod = os .path .split (arg )
108
- mod = strip_py (mod ) or mod
109
- base , base_dir = self .crawl_up_dir (dir )
110
- if mod == '__init__' or not mod :
111
- mod = base
134
+ parent , filename = os .path .split (path )
135
+ module_name = strip_py (filename ) or os . path . basename ( filename )
136
+ module_prefix , base_dir = self .crawl_up_dir (parent )
137
+ if module_name == '__init__' or not module_name :
138
+ module = module_prefix
112
139
else :
113
- mod = module_join (base , mod )
140
+ module = module_join (module_prefix , module_name )
114
141
115
- return mod , base_dir
142
+ return module , base_dir
116
143
117
144
def crawl_up_dir (self , dir : str ) -> Tuple [str , str ]:
118
145
"""Given a directory name, return the corresponding module name and base directory
@@ -124,25 +151,24 @@ def crawl_up_dir(self, dir: str) -> Tuple[str, str]:
124
151
125
152
parent_dir , base = os .path .split (dir )
126
153
if not dir or not self .get_init_file (dir ) or not base :
127
- res = ''
154
+ module = ''
128
155
base_dir = dir or '.'
129
156
else :
130
157
# Ensure that base is a valid python module name
131
158
if base .endswith ('-stubs' ):
132
159
base = base [:- 6 ] # PEP-561 stub-only directory
133
160
if not base .isidentifier ():
134
161
raise InvalidSourceList ('{} is not a valid Python package name' .format (base ))
135
- parent , base_dir = self .crawl_up_dir (parent_dir )
136
- res = module_join (parent , base )
162
+ parent_module , base_dir = self .crawl_up_dir (parent_dir )
163
+ module = module_join (parent_module , base )
137
164
138
- self .package_cache [dir ] = res , base_dir
139
- return res , base_dir
165
+ self .package_cache [dir ] = module , base_dir
166
+ return module , base_dir
140
167
141
168
def get_init_file (self , dir : str ) -> Optional [str ]:
142
169
"""Check whether a directory contains a file named __init__.py[i].
143
170
144
- If so, return the file's name (with dir prefixed). If not, return
145
- None.
171
+ If so, return the file's name (with dir prefixed). If not, return None.
146
172
147
173
This prefers .pyi over .py (because of the ordering of PY_EXTENSIONS).
148
174
"""
0 commit comments