@@ -68,6 +68,7 @@ class WindowsError(Exception):
6868from scancode .help import epilog_text
6969from scancode .help import examples_text
7070from scancode .interrupt import DEFAULT_TIMEOUT
71+ from scancode .interrupt import DEFAULT_PLUGIN_TIMEOUT
7172from scancode .interrupt import fake_interruptible
7273from scancode .interrupt import interruptible
7374from scancode .pool import ScanCodeTimeoutError
@@ -253,6 +254,14 @@ def default_processes():
253254 f'[default: { DEFAULT_TIMEOUT } seconds]' ,
254255 help_group = cliutils .CORE_GROUP , sort_order = 10 , cls = PluggableCommandLineOption )
255256
257+ @click .option ('--timeout-plugins' ,
258+ type = float ,
259+ default = DEFAULT_PLUGIN_TIMEOUT ,
260+ metavar = '<seconds>' ,
261+ help = 'Stop an unfinished codebase processing or post-scan plugin after'
262+ f' a timeout in seconds. [default: { DEFAULT_TIMEOUT } seconds]' ,
263+ help_group = cliutils .CORE_GROUP , sort_order = 10 , cls = PluggableCommandLineOption )
264+
256265@click .option ('-q' , '--quiet' ,
257266 is_flag = True ,
258267 default = False ,
@@ -399,6 +408,7 @@ def scancode(
399408 full_root ,
400409 processes ,
401410 timeout ,
411+ timeout_plugins ,
402412 quiet ,
403413 verbose ,
404414 max_depth ,
@@ -510,6 +520,7 @@ def scancode(
510520 full_root = full_root ,
511521 processes = processes ,
512522 timeout = timeout ,
523+ timeout_plugins = timeout_plugins ,
513524 quiet = quiet ,
514525 verbose = verbose ,
515526 max_depth = max_depth ,
@@ -551,7 +562,8 @@ def run_scan(
551562 full_root = False ,
552563 max_in_memory = 10000 ,
553564 processes = 1 ,
554- timeout = 120 ,
565+ timeout = DEFAULT_TIMEOUT ,
566+ timeout_plugins = DEFAULT_PLUGIN_TIMEOUT ,
555567 quiet = True ,
556568 verbose = False ,
557569 max_depth = 0 ,
@@ -659,6 +671,7 @@ def echo_func(*_args, **_kwargs):
659671 full_root = full_root ,
660672 processes = processes ,
661673 timeout = timeout ,
674+ timeout_plugins = timeout_plugins ,
662675 quiet = quiet ,
663676 verbose = verbose ,
664677 from_json = from_json ,
@@ -947,6 +960,7 @@ def echo_func(*_args, **_kwargs):
947960 stage = 'pre-scan' ,
948961 plugins = pre_scan_plugins ,
949962 codebase = codebase ,
963+ timeout = timeout_plugins ,
950964 stage_msg = 'Run %(stage)ss...' ,
951965 plugin_msg = ' Run %(stage)s: %(name)s...' ,
952966 quiet = quiet ,
@@ -966,6 +980,7 @@ def echo_func(*_args, **_kwargs):
966980 codebase = codebase ,
967981 processes = processes ,
968982 timeout = timeout ,
983+ timeout_plugins = timeout_plugins ,
969984 timing = timeout ,
970985 quiet = quiet ,
971986 verbose = verbose ,
@@ -983,6 +998,7 @@ def echo_func(*_args, **_kwargs):
983998 stage = 'post-scan' ,
984999 plugins = post_scan_plugins ,
9851000 codebase = codebase ,
1001+ timeout = timeout_plugins ,
9861002 stage_msg = 'Run %(stage)ss...' ,
9871003 plugin_msg = ' Run %(stage)s: %(name)s...' ,
9881004 quiet = quiet ,
@@ -1001,6 +1017,7 @@ def echo_func(*_args, **_kwargs):
10011017 stage = 'output-filter' ,
10021018 plugins = output_filter_plugins ,
10031019 codebase = codebase ,
1020+ timeout = timeout_plugins ,
10041021 stage_msg = 'Apply %(stage)ss...' ,
10051022 plugin_msg = ' Apply %(stage)s: %(name)s...' ,
10061023 quiet = quiet ,
@@ -1035,6 +1052,7 @@ def echo_func(*_args, **_kwargs):
10351052 stage = 'output' ,
10361053 plugins = output_plugins ,
10371054 codebase = codebase ,
1055+ timeout = timeout_plugins ,
10381056 stage_msg = 'Save scan results...' ,
10391057 plugin_msg = ' Save scan results as: %(name)s...' ,
10401058 quiet = quiet ,
@@ -1095,6 +1113,7 @@ def run_codebase_plugins(
10951113 stage ,
10961114 plugins ,
10971115 codebase ,
1116+ timeout ,
10981117 stage_msg = '' ,
10991118 plugin_msg = '' ,
11001119 quiet = False ,
@@ -1119,6 +1138,7 @@ def run_codebase_plugins(
11191138 # Sort plugins by run_order, from low to high
11201139 sorted_plugins = sorted (plugins , key = lambda x : x .run_order )
11211140
1141+ scan_errors = []
11221142 success = True
11231143 # TODO: add progress indicator
11241144 for plugin in sorted_plugins :
@@ -1135,7 +1155,11 @@ def run_codebase_plugins(
11351155 logger_debug (pformat (sorted (kwargs .items ())))
11361156 logger_debug ()
11371157
1138- plugin .process_codebase (codebase , ** kwargs )
1158+ process_codebase_func = partial (plugin .process_codebase , codebase , ** kwargs )
1159+ error , _value = interruptible (process_codebase_func , timeout = timeout )
1160+ if error :
1161+ msg = 'ERROR: for scanner: ' + plugin .name + ':\n ' + error
1162+ codebase .errors .append (msg )
11391163
11401164 except Exception as _e :
11411165 msg = 'ERROR: failed to run %(stage)s plugin: %(name)s:' % locals ()
@@ -1158,6 +1182,7 @@ def run_scanners(
11581182 codebase ,
11591183 processes ,
11601184 timeout ,
1185+ timeout_plugins ,
11611186 timing ,
11621187 quiet = False ,
11631188 verbose = False ,
@@ -1208,8 +1233,10 @@ def run_scanners(
12081233
12091234 # TODO: add progress indicator
12101235 # run the process codebase of each scan plugin (most often a no-op)
1236+ use_threading = processes >= 0
12111237 scan_process_codebase_success = run_codebase_plugins (
12121238 stage , plugins , codebase ,
1239+ timeout = timeout_plugins ,
12131240 stage_msg = 'Filter %(stage)ss...' ,
12141241 plugin_msg = ' Filter %(stage)s: %(name)s...' ,
12151242 quiet = quiet , verbose = verbose , kwargs = kwargs ,
0 commit comments