@@ -92,6 +92,8 @@ def wrap(self, timeout=None):
9292
9393from scancode import utils
9494
95+ echo_stderr = partial (click .secho , err = True )
96+
9597
9698info_text = '''
9799ScanCode scans code and other files for origin and license.
@@ -189,12 +191,12 @@ def print_about(ctx, param, value):
189191
190192 scancode --format html samples/zlib scancode_result.html
191193
192- Scan a single file for copyrights. Print scan results on terminal as JSON:
194+ Scan a single file for copyrights. Print scan results to stdout as JSON:
193195
194196 scancode --copyright samples/zlib/zlib.h
195197
196- Scan a single file for licenses, print verbose progress on terminal as each file
197- is scanned. Save scan to a JSON file:
198+ Scan a single file for licenses, print verbose progress to stderr as each
199+ file is scanned. Save scan to a JSON file:
198200
199201 scancode --license --verbose samples/zlib/zlib.h licenses.json
200202
@@ -257,6 +259,7 @@ def validate_formats(ctx, param, value):
257259 raise click .BadParameter ('Invalid template file: "%(value)s" does not exists or is not readable.' % locals ())
258260 return value
259261
262+
260263@click .command (name = 'scancode' , epilog = epilog_text , cls = ScanCommand )
261264@click .pass_context
262265
@@ -277,16 +280,16 @@ def validate_formats(ctx, param, value):
277280 'or the path to a custom template' % ' or ' .join (formats )),
278281 callback = validate_formats )
279282@click .option ('--verbose' , is_flag = True , default = False , help = 'Print verbose file-by-file progress messages.' )
280- @click .option ('--quiet' , is_flag = True , default = False , help = 'Do not print progress messages.' )
283+ @click .option ('--quiet' , is_flag = True , default = False , help = 'Do not print summary or progress messages.' )
281284@click .option ('-n' , '--processes' , is_flag = False , default = 1 , type = int , show_default = True , help = 'Scan <input> using n parallel processes.' )
282285
283286@click .help_option ('-h' , '--help' )
284287@click .option ('--examples' , is_flag = True , is_eager = True , callback = print_examples , help = ('Show command examples and exit.' ))
285288@click .option ('--about' , is_flag = True , is_eager = True , callback = print_about , help = 'Show information about ScanCode and licensing and exit.' )
286289@click .option ('--version' , is_flag = True , is_eager = True , callback = print_version , help = 'Show the version and exit.' )
287- @click .option ('--diag' , is_flag = True , default = False , help = 'Include detailed diagnostic messages in results if there are scanning errors .' )
288- @click .option ('--timeout' , is_flag = False , default = DEFAULT_TIMEOUT , type = int , show_default = True , help = 'Stop scanning a file if it takes longer than a timeout in seconds.' )
289- @click .option ('--max-memory' , is_flag = False , default = DEFAULT_MAX_MEMORY , type = int , show_default = True , help = 'Stop scanning a file if it its scan requires more than a maximum amount of memory in megabytes.' )
290+ @click .option ('--diag' , is_flag = True , default = False , help = 'Include additional diagnostic information such as error messages or result details .' )
291+ @click .option ('--timeout' , is_flag = False , default = DEFAULT_TIMEOUT , type = int , show_default = True , help = 'Stop scanning a file if scanning takes longer than a timeout in seconds.' )
292+ @click .option ('--max-memory' , is_flag = False , default = DEFAULT_MAX_MEMORY , type = int , show_default = True , help = 'Stop scanning a file if scanning requires more than a maximum amount of memory in megabytes.' )
290293
291294def scancode (ctx , input , output_file , copyright , license , package ,
292295 email , url , info , license_score , format ,
@@ -295,7 +298,8 @@ def scancode(ctx, input, output_file, copyright, license, package,
295298 * args , ** kwargs ):
296299 """scan the <input> file or directory for origin clues and license and save results to the <output_file>.
297300
298- The scan results are printed on terminal if <output_file> is not provided.
301+ The scan results are printed to stdout if <output_file> is not provided.
302+ Error and progress is printed to stderr.
299303 """
300304 possible_scans = [copyright , license , package , email , url , info ]
301305 # Default scan when no options is provided
@@ -306,26 +310,25 @@ def scancode(ctx, input, output_file, copyright, license, package,
306310
307311 scans_cache_class = get_scans_cache_class ()
308312 try :
309- to_stdout = output_file == sys .stdout
310313 files_count , results = scan (input , copyright , license , package , email , url , info , license_score ,
311- verbose , quiet , processes , scans_cache_class , to_stdout ,
314+ verbose , quiet , processes , scans_cache_class ,
312315 diag , timeout , max_memory )
313- click .secho ('Saving results.' , err = to_stdout , fg = 'green' )
316+ if not quiet :
317+ echo_stderr ('Saving results.' , fg = 'green' )
314318 save_results (files_count , results , format , input , output_file )
315319 finally :
316320 # cleanup
317321 cache = scans_cache_class ()
318322 cache .clear ()
319323
320- # TODO: addproper return code
324+ # TODO: add proper return code
321325 # rc = 1 if has__errors else 0
322326 # ctx.exit(rc)
323327
324328
325329def scan (input_path , copyright = True , license = True , package = True ,
326330 email = False , url = False , info = True , license_score = 0 ,
327- verbose = False , quiet = False , processes = 1 ,
328- scans_cache_class = None , to_stdout = False ,
331+ verbose = False , quiet = False , processes = 1 , scans_cache_class = None ,
329332 diag = False , timeout = DEFAULT_TIMEOUT , max_memory = DEFAULT_MAX_MEMORY ):
330333 """
331334 Return a tuple of (file_count, indexing_time, scan_results) where
@@ -354,19 +357,22 @@ def scan(input_path, copyright=True, license=True, package=True,
354357 scans = info and ['infos' ] or []
355358 scans .extend ([k for k , v in scanners .items () if v ])
356359 _scans = ', ' .join (scans )
357- click .secho ('Scanning files for: %(_scans)s with %(processes)d process(es)...' % locals (), err = to_stdout )
360+ if not quiet :
361+ echo_stderr ('Scanning files for: %(_scans)s with %(processes)d process(es)...' % locals ())
358362
359363 scan_summary ['scans' ] = scans [:]
360364 scan_start = time ()
361365 indexing_time = 0
362366 if license :
363367 # build index outside of the main loop
364368 # this also ensures that forked processes will get the index on POSIX naturally
365- click .secho ('Building license detection index...' , err = to_stdout , fg = 'green' , nl = False )
369+ if not quiet :
370+ echo_stderr ('Building license detection index...' , fg = 'green' , nl = False )
366371 from licensedcode .index import get_index
367372 _idx = get_index ()
368373 indexing_time = time () - scan_start
369- click .secho ('Done.' , err = to_stdout , fg = 'green' , nl = True )
374+ if not quiet :
375+ echo_stderr ('Done.' , fg = 'green' , nl = True )
370376
371377 scan_summary ['indexing_time' ] = indexing_time
372378
@@ -391,10 +397,13 @@ def scan(input_path, copyright=True, license=True, package=True,
391397 scanned_files = pool .imap_unordered (scanit , logged_resources , chunksize = 1 )
392398 pool .close ()
393399
394- click .secho ('Scanning files...' , err = to_stdout , fg = 'green' )
400+ if not quiet :
401+ echo_stderr ('Scanning files...' , fg = 'green' )
395402
396403 def scan_event (item ):
397404 """Progress event displayed each time a file is scanned"""
405+ if quiet :
406+ return ''
398407 if item :
399408 _scan_success , _scanned_path = item
400409 _progress_line = verbose and _scanned_path or fileutils .file_name (_scanned_path )
@@ -403,7 +412,8 @@ def scan_event(item):
403412 scanning_errors = []
404413 files_count = 0
405414 with utils .progressmanager (scanned_files , item_show_func = scan_event ,
406- show_pos = True , verbose = verbose , quiet = quiet ) as scanned :
415+ show_pos = True , verbose = verbose , quiet = quiet ,
416+ file = sys .stderr ) as scanned :
407417 while True :
408418 try :
409419 result = scanned .next ()
@@ -422,6 +432,8 @@ def scan_event(item):
422432 # http://bugs.python.org/issue15101
423433 pool .terminate ()
424434
435+ # TODO: add stats to results somehow
436+
425437 # Compute stats
426438 ##########################
427439 scan_summary ['files_count' ] = files_count
@@ -434,19 +446,20 @@ def scan_event(item):
434446 files_scanned_per_second = round (float (files_count ) / scanning_time , 2 )
435447 scan_summary ['files_scanned_per_second' ] = files_scanned_per_second
436448
437- # Display stats
438- ##########################
439- click .secho ('Scanning done.' , fg = scanning_errors and 'red' or 'green' , err = to_stdout )
440- if scanning_errors :
441- click .secho ('Some files failed to scan properly. See scan for details:' , fg = 'red' , err = to_stdout )
442- for errored_path in scanning_errors :
443- click .secho (' ' + errored_path , fg = 'red' , err = to_stdout )
444-
445- click .secho ('Scan statistics: %(files_count)d files scanned in %(total_time)ds.' % locals (), err = to_stdout )
446- click .secho ('Scan options: %(_scans)s with %(processes)d process(es).' % locals (), err = to_stdout )
447- click .secho ('Scanning speed: %(files_scanned_per_second)s files per sec.' % locals (), err = to_stdout )
448- click .secho ('Scanning time: %(scanning_time)ds.' % locals (), err = to_stdout )
449- click .secho ('Indexing time: %(indexing_time)ds.' % locals (), err = to_stdout , reset = True )
449+ if not quiet :
450+ # Display stats
451+ ##########################
452+ echo_stderr ('Scanning done.' , fg = scanning_errors and 'red' or 'green' )
453+ if scanning_errors :
454+ echo_stderr ('Some files failed to scan properly. See scan for details:' , fg = 'red' )
455+ for errored_path in scanning_errors :
456+ echo_stderr (' ' + errored_path , fg = 'red' )
457+
458+ echo_stderr ('Scan statistics: %(files_count)d files scanned in %(total_time)ds.' % locals ())
459+ echo_stderr ('Scan options: %(_scans)s with %(processes)d process(es).' % locals ())
460+ echo_stderr ('Scanning speed: %(files_scanned_per_second)s files per sec.' % locals ())
461+ echo_stderr ('Scanning time: %(scanning_time)ds.' % locals ())
462+ echo_stderr ('Indexing time: %(indexing_time)ds.' % locals (), reset = True )
450463
451464 # finally return an iterator on cached results
452465 scan_names = []
@@ -585,22 +598,25 @@ def save_results(files_count, scanned_files, format, input, output_file):
585598 """
586599 Save scan results to file or screen.
587600 """
588- if output_file != sys .stdout :
601+ # note: in tests, sys.sdtout is not used, but some io wrapper with no name attributes
602+ is_real_file = hasattr (output_file , 'name' )
603+
604+ if output_file != sys .stdout and is_real_file :
589605 parent_dir = os .path .dirname (output_file .name )
590606 if parent_dir :
591607 fileutils .create_dir (abspath (expanduser (parent_dir )))
592608
593609 if format and format not in formats :
594610 # render using a user-provided custom format template
595611 if not os .path .isfile (format ):
596- click . secho ('\n Invalid template passed.' , err = True , fg = 'red' )
612+ echo_stderr ('\n Invalid template passed.' , fg = 'red' )
597613 else :
598614 for template_chunk in as_template (scanned_files , template = format ):
599615 try :
600616 output_file .write (template_chunk )
601617 except Exception as e :
602618 extra_context = 'ERROR: Failed to write output to HTML for: ' + repr (template_chunk )
603- click . secho (extra_context , err = True , fg = 'red' )
619+ echo_stderr (extra_context , fg = 'red' )
604620 e .args += (extra_context ,)
605621 raise e
606622
@@ -610,7 +626,7 @@ def save_results(files_count, scanned_files, format, input, output_file):
610626 output_file .write (template_chunk )
611627 except Exception as e :
612628 extra_context = 'ERROR: Failed to write output to HTML for: ' + repr (template_chunk )
613- click . secho (extra_context , err = True , fg = 'red' )
629+ echo_stderr (extra_context , fg = 'red' )
614630 e .args += (extra_context ,)
615631 raise e
616632
@@ -619,9 +635,9 @@ def save_results(files_count, scanned_files, format, input, output_file):
619635 try :
620636 create_html_app_assets (scanned_files , output_file )
621637 except HtmlAppAssetCopyWarning :
622- click . secho ('\n HTML app creation skipped when printing to terminal.' , err = True , fg = 'yellow' )
638+ echo_stderr ('\n HTML app creation skipped when printing to stdout.' , fg = 'yellow' )
623639 except HtmlAppAssetCopyError :
624- click . secho ('\n Failed to create HTML app.' , err = True , fg = 'red' )
640+ echo_stderr ('\n Failed to create HTML app.' , fg = 'red' )
625641
626642 elif format == 'json' :
627643 meta = OrderedDict ()
0 commit comments