5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 __revision__
= "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
27 Verify that the SCons source code contains only correct handling of
28 keyboard interrupts (e.g. Ctrl-C).
38 test
= TestSCons
.TestSCons()
40 # We do not want statements of the form:
42 # # do something, e.g.
45 # # do the exception handling
49 # The code above may catch a KeyboardInterrupt exception, which was not
50 # intended by the programmer. We check for these situations in all python
54 cwd
= os
.environ
['SCONS_CWD']
56 scons_lib_dir
= os
.environ
['SCONS_LIB_DIR']
57 MANIFEST
= os
.path
.join(scons_lib_dir
, 'MANIFEST.in')
60 scons_lib_dir
= os
.path
.join(cwd
, 'build', 'scons')
61 MANIFEST
= os
.path
.join(scons_lib_dir
, 'MANIFEST')
63 # We expect precisely this many uncaught KeyboardInterrupt exceptions
64 # from the files in the following dictionary.
67 'engine/SCons/Job.py' : 5,
68 'engine/SCons/Script/Main.py' : 1,
69 'engine/SCons/Taskmaster.py' : 3,
75 test
.skip_test('%s does not exist; skipping test.\n' % MANIFEST
)
77 files
= fp
.read().split()
78 files
= [f
for f
in files
if f
[-3:] == '.py']
80 # some regexps to parse the python files
81 tryexc_pat
= re
.compile(
82 r
'^(?P<try_or_except>(?P<indent> *)(try|except)( [^\n]*)?:.*)',re
.MULTILINE
)
83 keyboardint_pat
= re
.compile(r
' *except +([^,],)*KeyboardInterrupt([ ,][^\n]*)?:[^\n]*')
84 exceptall_pat
= re
.compile(r
' *except(?: *| +Exception *, *[^: ]+):[^\n]*')
86 uncaughtKeyboardInterrupt
= 0
88 with
open(os
.path
.join(scons_lib_dir
, f
)) as ifp
:
93 match
= tryexc_pat
.search( contents
, lastend
)
99 indent_list
= try_except_lines
[match
.group('indent')]
102 line_num
= 1 + contents
[:match
.start()].count('\n')
103 indent_list
.append( (line_num
, match
.group('try_or_except') ) )
104 try_except_lines
[match
.group('indent')] = indent_list
105 uncaught_this_file
= []
106 for indent
in try_except_lines
.keys():
107 exc_keyboardint_seen
= 0
109 for (l
,statement
) in try_except_lines
[indent
] + [(-1,indent
+ 'try')]:
110 #print "%4d %s" % (l,statement),
111 m1
= keyboardint_pat
.match(statement
)
112 m2
= exceptall_pat
.match(statement
)
113 if statement
.find(indent
+ 'try') == 0:
114 if exc_all_seen
and not exc_keyboardint_seen
:
115 uncaught_this_file
.append(line
)
116 exc_keyboardint_seen
= 0
121 exc_keyboardint_seen
= 1
122 #print " -> keyboard -> ", m1.groups()
125 #print " -> all -> ", m2.groups()
128 #print "Warning: unknown statement %s" % statement
129 expected_num
= expected_uncaught
.get(f
, 0)
130 if expected_num
!= len(uncaught_this_file
):
131 uncaughtKeyboardInterrupt
= 1
132 msg
= "%s: expected %d uncaught interrupts, got %d:"
133 print(msg
% (f
, expected_num
, len(uncaught_this_file
)))
134 for line
in uncaught_this_file
:
135 print(" File %s:%d: Uncaught KeyboardInterrupt!" % (f
,line
))
137 test
.fail_test(uncaughtKeyboardInterrupt
)
143 # indent-tabs-mode:nil
145 # vim: set expandtab tabstop=4 shiftwidth=4: