@@ -72,6 +72,33 @@ class File extends Container {
7272 * are specified to be extracted.
7373 */
7474 string getContents ( ) { file_contents ( this , result ) }
75+
76+ /** Holds if this file is likely to get executed directly, and thus act as an entry point for execution. */
77+ predicate maybeExecutedDirectly ( ) {
78+ // Only consider files in the source code, and not things like the standard library
79+ exists ( this .getRelativePath ( ) ) and
80+ (
81+ // The file doesn't have the extension `.py` but still contains Python statements
82+ not this .getExtension ( ) .matches ( "py%" ) and
83+ exists ( Stmt s | s .getLocation ( ) .getFile ( ) = this )
84+ or
85+ // The file contains the usual `if __name__ == '__main__':` construction
86+ exists ( If i , Name name , StrConst main , Cmpop op |
87+ i .getScope ( ) .( Module ) .getFile ( ) = this and
88+ op instanceof Eq and
89+ i .getTest ( ) .( Compare ) .compares ( name , op , main ) and
90+ name .getId ( ) = "__name__" and
91+ main .getText ( ) = "__main__"
92+ )
93+ or
94+ // The file contains a `#!` line referencing the python interpreter
95+ exists ( Comment c |
96+ c .getLocation ( ) .getFile ( ) = this and
97+ c .getLocation ( ) .getStartLine ( ) = 1 and
98+ c .getText ( ) .regexpMatch ( "^#! */.*python(2|3)?[ \\\\t]*$" )
99+ )
100+ )
101+ }
75102}
76103
77104private predicate occupied_line ( File f , int n ) {
@@ -121,6 +148,9 @@ class Folder extends Container {
121148 this .getBaseName ( ) .regexpMatch ( "[^\\d\\W]\\w*" ) and
122149 result = this .getParent ( ) .getImportRoot ( n )
123150 }
151+
152+ /** Holds if execution may start in a file in this directory. */
153+ predicate mayContainEntryPoint ( ) { any ( File f | f .getParent ( ) = this ) .maybeExecutedDirectly ( ) }
124154}
125155
126156/**
0 commit comments