Skip to content

Commit 60db756

Browse files
authored
Merge pull request #518 from nexB/447-rooted-paths
#447 restore default behavior of v1.x to return a root path by default
2 parents 6469c42 + 4db869f commit 60db756

6 files changed

Lines changed: 532 additions & 33 deletions

File tree

src/scancode/cache.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright (c) 2016 nexB Inc. and others. All rights reserved.
2+
# Copyright (c) 2017 nexB Inc. and others. All rights reserved.
33
# http://nexb.com and https://github.com/nexB/scancode-toolkit/
44
# The ScanCode software is licensed under the Apache License version 2.0.
55
# Data generated with ScanCode require an acknowledgment.
@@ -246,7 +246,7 @@ def get_scan(self, path, file_info):
246246
with open(scan_path, 'rb') as cs:
247247
return json.load(cs, object_pairs_hook=OrderedDict)
248248

249-
def iterate(self, scan_names):
249+
def iterate(self, scan_names, root_dir=None):
250250
"""
251251
Yield scan data for all cached scans e.g. the whole cache given a list of
252252
scan names.
@@ -259,19 +259,25 @@ def iterate(self, scan_names):
259259
path = file_log.rstrip('\n')
260260
file_info = self.get_info(path)
261261

262+
if root_dir:
263+
rooted_path = posixpath.join(root_dir, path)
264+
else:
265+
rooted_path = path
266+
logger_debug('iterate:', 'rooted_path:', rooted_path)
267+
262268
# rare but possible corner case
263269
if file_info is None:
264270
no_info = ('ERROR: file info unavailable in cache: '
265271
'This is either a bug or processing was aborted with CTRL-C.')
266-
scan_result = OrderedDict(path=path)
272+
scan_result = OrderedDict(path=rooted_path)
267273
scan_result['scan_errors'] = [no_info]
268274
if TRACE:
269-
logger_debug('iterate:', 'scan_result:', scan_result, 'for path:', path, '\n')
275+
logger_debug('iterate:', 'scan_result:', scan_result, 'for path:', rooted_path, '\n')
270276
yield scan_result
271277
continue
272278

273279
path = file_info.pop('path')
274-
scan_result = OrderedDict(path=path)
280+
scan_result = OrderedDict(path=rooted_path)
275281

276282
if 'infos' in scan_names:
277283
# infos is always collected but only returned if requested
@@ -296,7 +302,7 @@ def iterate(self, scan_names):
296302
scan_result.update(scan_details)
297303

298304
if TRACE:
299-
logger_debug('iterate:', 'scan_result:', scan_result, 'for path:', path, '\n')
305+
logger_debug('iterate:', 'scan_result:', scan_result, 'for path:', rooted_path, '\n')
300306
yield scan_result
301307

302308
def clear(self, *args):

src/scancode/cli.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,15 @@
8181

8282
notice_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'NOTICE')
8383
notice_text = open(notice_path).read()
84-
84+
8585
delimiter = '\n\n\n'
8686
[notice_text, extra_notice_text] = notice_text.split(delimiter, 1)
8787
extra_notice_text = delimiter + extra_notice_text
88-
88+
8989
delimiter = '\n\n '
9090
[notice_text, acknowledgment_text] = notice_text.split(delimiter, 1)
9191
acknowledgment_text = delimiter + acknowledgment_text
92-
92+
9393
notice = acknowledgment_text.strip().replace(' ', '')
9494

9595

@@ -208,6 +208,9 @@ def validate_formats(ctx, param, value):
208208
help='Include the detected licenses matched text. Has no effect unless --license is requested.')
209209
@click.option('--only-findings', is_flag=True, default=False,
210210
help='Only return files or directories with findings for the requested scans. Files without findings are omitted.')
211+
@click.option('--strip-root', is_flag=True, default=False,
212+
help='Strip the root directory segment of all paths. The default is to always '
213+
'include the last directory segment of the scanned path such that all paths have a common root directory.')
211214

212215
@click.option('-f', '--format', is_flag=False, default='json', show_default=True, metavar='<style>',
213216
help=('Set <output_file> format <style> to one of the standard formats: %s '
@@ -230,7 +233,7 @@ def scancode(ctx,
230233
input, output_file,
231234
copyright, license, package,
232235
email, url, info,
233-
license_score, license_text, only_findings,
236+
license_score, license_text, only_findings, strip_root,
234237
format, verbose, quiet, processes,
235238
diag, timeout, max_memory,
236239
*args, **kwargs):
@@ -275,6 +278,7 @@ def scancode(ctx,
275278
scanners = OrderedDict(zip(possible_scans.keys(), zip(possible_scans.values(), scan_functions)))
276279

277280
scans_cache_class = get_scans_cache_class()
281+
278282
try:
279283
files_count, results = scan(input_path=input,
280284
scanners=scanners,
@@ -286,10 +290,12 @@ def scancode(ctx,
286290
timeout=timeout, max_memory=max_memory,
287291
diag=diag,
288292
scans_cache_class=scans_cache_class,
289-
)
293+
strip_root=strip_root)
290294
if not quiet:
291295
echo_stderr('Saving results.', fg='green')
296+
292297
save_results(scanners, only_findings, files_count, results, format, input, output_file)
298+
293299
finally:
294300
# cleanup
295301
cache = scans_cache_class()
@@ -306,9 +312,10 @@ def scan(input_path,
306312
verbose=False, quiet=False,
307313
processes=1, timeout=DEFAULT_TIMEOUT, max_memory=DEFAULT_MAX_MEMORY,
308314
diag=False,
309-
scans_cache_class=None):
315+
scans_cache_class=None,
316+
strip_root=False):
310317
"""
311-
Return a tuple of (files_count, indexing_time, scan_results) where
318+
Return a tuple of (files_count, scan_results) where
312319
scan_results is an iterable. Run each requested scan proper: each individual file
313320
scan is cached on disk to free memory. Then the whole set of scans is loaded from
314321
the cache and streamed at the end.
@@ -432,7 +439,25 @@ def scan_event(item):
432439

433440
# finally return an iterator on cached results
434441
cached_scan = scans_cache_class()
435-
return files_count, cached_scan.iterate(scans)
442+
root_dir = _get_root_dir(input_path, strip_root)
443+
return files_count, cached_scan.iterate(scans, root_dir)
444+
445+
446+
def _get_root_dir(input_path, strip_root=False):
447+
"""
448+
Return a root dir name or None.
449+
"""
450+
if strip_root:
451+
root_dir = None
452+
else:
453+
_scanned_path = os.path.abspath(os.path.normpath(os.path.expanduser(input_path)))
454+
if filetype.is_dir(_scanned_path):
455+
root_dir = _scanned_path
456+
else:
457+
root_dir = fileutils.parent_directory(_scanned_path)
458+
root_dir = fileutils.file_name(root_dir)
459+
460+
return root_dir
436461

437462

438463
def _resource_logger(logfile_fd, resources):
@@ -575,7 +600,7 @@ def has_findings(active_scans, file_data):
575600
return any(file_data.get(scan_name) for scan_name in active_scans)
576601

577602

578-
def save_results(scanners, only_findings, files_count, scanned_files, format, input, output_file):
603+
def save_results(scanners, only_findings, files_count, results, format, input, output_file):
579604
"""
580605
Save scan results to file or screen.
581606
"""
@@ -589,11 +614,10 @@ def save_results(scanners, only_findings, files_count, scanned_files, format, in
589614

590615
# FIXME: this is forcing all the scan results to be loaded in memory
591616
# and defeats lazy loading from cache
592-
scanned_files = [file_data for file_data in scanned_files
593-
if has_findings(active_scans, file_data)]
617+
results = [file_data for file_data in results if has_findings(active_scans, file_data)]
594618
# FIXME: computing len before hand will need a list and therefore need loding
595619
# it all aheaed of time
596-
files_count = len(scanned_files)
620+
files_count = len(results)
597621

598622
# note: in tests, sys.stdout is not used, but some io wrapper with no name
599623
# attributes
@@ -609,7 +633,7 @@ def save_results(scanners, only_findings, files_count, scanned_files, format, in
609633
if not os.path.isfile(format):
610634
echo_stderr('\nInvalid template passed.', fg='red')
611635
else:
612-
for template_chunk in as_template(scanned_files, template=format):
636+
for template_chunk in as_template(results, template=format):
613637
try:
614638
output_file.write(template_chunk)
615639
except Exception as e:
@@ -619,4 +643,4 @@ def save_results(scanners, only_findings, files_count, scanned_files, format, in
619643
raise e
620644
return
621645

622-
write_formatted_output(scanners, files_count, version, notice, scanned_files, format, input, output_file, echo_stderr)
646+
write_formatted_output(scanners, files_count, version, notice, results, format, input, output_file, echo_stderr)

tests/formattedcode/test_writers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,15 @@ def test_scan_html_output_does_not_truncate_copyright(monkeypatch):
150150
test_dir = test_env.get_test_loc('do_not_truncate_copyright/scan/')
151151
runner = CliRunner()
152152
json_result_file = test_env.get_temp_file('test.json')
153-
json_result = runner.invoke(cli.scancode, [ '-clip', '--format', 'json', test_dir, json_result_file], catch_exceptions=True)
153+
json_result = runner.invoke(cli.scancode, [ '-clip', '--strip-root', '--format', 'json', test_dir, json_result_file], catch_exceptions=True)
154154
assert json_result.exit_code == 0
155155
assert 'Scanning done' in json_result.output
156156
expected_json = test_env.get_test_loc('do_not_truncate_copyright/expected.json')
157157

158158
check_scan(test_env.get_test_loc(expected_json), json_result_file, strip_dates=True, regen=False)
159159

160160
html_result_file = test_env.get_temp_file('test.html')
161-
html_result = runner.invoke(cli.scancode, [ '-clip', '--format', 'html', '-n', '3', test_dir, html_result_file], catch_exceptions=True)
161+
html_result = runner.invoke(cli.scancode, [ '-clip', '--strip-root', '--format', 'html', '-n', '3', test_dir, html_result_file], catch_exceptions=True)
162162
assert html_result.exit_code == 0
163163
assert 'Scanning done' in html_result.output
164164

0 commit comments

Comments
 (0)