@@ -179,21 +179,6 @@ private predicate legalDottedName(string name) {
179179bindingset [ name]
180180private predicate legalShortName ( string name ) { name .regexpMatch ( "(\\p{L}|_)(\\p{L}|\\d|_)*" ) }
181181
182- /**
183- * Holds if `f` is potentially a source package.
184- * Does it have an __init__.py file (or --respect-init=False for Python 2) and is it within the source archive?
185- */
186- private predicate isPotentialSourcePackage ( Folder f ) {
187- f .getRelativePath ( ) != "" and
188- isPotentialPackage ( f )
189- }
190-
191- private predicate isPotentialPackage ( Folder f ) {
192- exists ( f .getFile ( "__init__.py" ) )
193- or
194- py_flags_versioned ( "options.respect_init" , "False" , _) and major_version ( ) = 2 and exists ( f )
195- }
196-
197182private string moduleNameFromBase ( Container file ) {
198183 // We used to also require `isPotentialPackage(f)` to hold in this case,
199184 // but we saw modules not getting resolved because their folder did not
@@ -236,31 +221,87 @@ private predicate transitively_imported_from_entry_point(File file) {
236221 )
237222}
238223
224+ private predicate isRegularPackage ( Folder f , string name ) {
225+ legalShortName ( name ) and
226+ name = f .getStem ( ) and
227+ exists ( f .getFile ( "__init__.py" ) )
228+ }
229+
230+ private predicate isPotentialModuleFile ( File file , string name ) {
231+ legalShortName ( name ) and
232+ name = file .getStem ( ) and
233+ file .getExtension ( ) = [ "py" , "pyc" , "so" , "pyd" ] and
234+ // it has to be imported in this folder
235+ name =
236+ any ( ImportExpr i | i .getLocation ( ) .getFile ( ) .getParent ( ) = file .getParent ( ) )
237+ .getName ( )
238+ .regexpReplaceAll ( "\\..*" , "" ) and
239+ name != ""
240+ }
241+
242+ // See https://peps.python.org/pep-0420/#specification
243+ private predicate isNameSpacePackage ( Folder f , string name ) {
244+ legalShortName ( name ) and
245+ name = f .getStem ( ) and
246+ not isRegularPackage ( f , name ) and
247+ // it has to be imported in this folder
248+ name =
249+ any ( ImportExpr i | i .getLocation ( ) .getFile ( ) .getParent ( ) = f )
250+ .getName ( )
251+ .regexpReplaceAll ( "\\..*" , "" ) and
252+ name != "" and
253+ // no siblibling regular package
254+ // no sibling module
255+ not exists ( Folder sibling | sibling .getParent ( ) = f .getParent ( ) |
256+ isRegularPackage ( sibling .getFolder ( name ) , name )
257+ or
258+ isPotentialModuleFile ( sibling .getAFile ( ) , name )
259+ )
260+ }
261+
262+ private predicate isPackage ( Folder f , string name ) {
263+ isRegularPackage ( f , name )
264+ or
265+ isNameSpacePackage ( f , name )
266+ }
267+
268+ private predicate isModuleFile ( File file , string name ) {
269+ isPotentialModuleFile ( file , name ) and
270+ not isPackage ( file .getParent ( ) , _)
271+ }
272+
273+ private predicate isOutermostPackage ( Folder f , string name ) {
274+ isPackage ( f , name ) and
275+ not isPackage ( f .getParent ( ) , _)
276+ }
277+
239278cached
240- string moduleNameFromFile ( Container file ) {
279+ string moduleNameFromFile ( Container c ) {
280+ // package
281+ isOutermostPackage ( c , result )
282+ or
283+ // module
284+ isModuleFile ( c , result )
285+ or
241286 Stages:: AST:: ref ( ) and
242287 exists ( string basename |
243- basename = moduleNameFromBase ( file ) and
288+ basename = moduleNameFromBase ( c ) and
244289 legalShortName ( basename )
245290 |
246- result = moduleNameFromFile ( file .getParent ( ) ) + "." + basename
291+ // recursive case
292+ result = moduleNameFromFile ( c .getParent ( ) ) + "." + basename
247293 or
248294 // If `file` is a transitive import of a file that's executed directly, we allow references
249295 // to it by its `basename`.
250- transitively_imported_from_entry_point ( file ) and
296+ transitively_imported_from_entry_point ( c ) and
251297 result = basename
252298 )
253299 or
254- isPotentialSourcePackage ( file ) and
255- result = file .getStem ( ) and
256- (
257- not isPotentialSourcePackage ( file .getParent ( ) ) or
258- not legalShortName ( file .getParent ( ) .getBaseName ( ) )
259- )
260- or
261- result = file .getStem ( ) and file .getParent ( ) = file .getImportRoot ( )
300+ //
301+ // standard library
302+ result = c .getStem ( ) and c .getParent ( ) = c .getImportRoot ( )
262303 or
263- result = file .getStem ( ) and isStubRoot ( file .getParent ( ) )
304+ result = c .getStem ( ) and isStubRoot ( c .getParent ( ) )
264305}
265306
266307private predicate isStubRoot ( Folder f ) {
0 commit comments