@@ -72,6 +72,33 @@ class File extends Container {
72
72
* are specified to be extracted.
73
73
*/
74
74
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
+ }
75
102
}
76
103
77
104
private predicate occupied_line ( File f , int n ) {
@@ -121,6 +148,9 @@ class Folder extends Container {
121
148
this .getBaseName ( ) .regexpMatch ( "[^\\d\\W]\\w*" ) and
122
149
result = this .getParent ( ) .getImportRoot ( n )
123
150
}
151
+
152
+ /** Holds if execution may start in a file in this directory. */
153
+ predicate mayContainEntryPoint ( ) { any ( File f | f .getParent ( ) = this ) .maybeExecutedDirectly ( ) }
124
154
}
125
155
126
156
/**
0 commit comments