Skip to content

Commit a1c1a0e

Browse files
Move --ignore functionality to codebase creation
Deprecate --ingore and --include pre-scan plugins and move the ignore/include functionality to codebase import stage to get rid of multiple codebase walks. Signed-off-by: Ayan Sinha Mahapatra <asmahapatra@aboutcode.org>
1 parent 913bc2f commit a1c1a0e

9 files changed

Lines changed: 170 additions & 128 deletions

File tree

pyproject-scancode-toolkit-mini.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ scancode-train-gibberish-model = "textcode.train_gibberish_model:train_gibberish
256256
# scancode_pre_scan is the entry point for pre_scan plugins executed before the
257257
# scans. See also plugincode.pre_scan module for details and doc.
258258
[project.entry-points.scancode_pre_scan]
259-
ignore = "scancode.plugin_ignore:ProcessIgnore"
260259
facet = "summarycode.facet:AddFacet"
261260

262261

pyproject-scancode-toolkit.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,6 @@ scancode-train-gibberish-model = "textcode.train_gibberish_model:train_gibberish
257257
# scancode_pre_scan is the entry point for pre_scan plugins executed before the
258258
# scans. See also plugincode.pre_scan module for details and doc.
259259
[project.entry-points.scancode_pre_scan]
260-
ignore = "scancode.plugin_ignore:ProcessIgnore"
261260
facet = "summarycode.facet:AddFacet"
262261

263262

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ scancode-train-gibberish-model = "textcode.train_gibberish_model:train_gibberish
262262
# scancode_pre_scan is the entry point for pre_scan plugins executed before the
263263
# scans. See also plugincode.pre_scan module for details and doc.
264264
[project.entry-points.scancode_pre_scan]
265-
ignore = "scancode.plugin_ignore:ProcessIgnore"
266265
facet = "summarycode.facet:AddFacet"
267266

268267

src/commoncode/resource.py

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from commoncode.datautils import List
4040
from commoncode.datautils import Mapping
4141
from commoncode.datautils import String
42+
from commoncode.fileset import is_included
4243
from commoncode.filetype import is_file as filetype_is_file
4344
from commoncode.filetype import is_special
4445
from commoncode.fileutils import as_posixpath
@@ -62,7 +63,7 @@
6263

6364
# Tracing flags
6465
TRACE = False
65-
TRACE_DEEP = False
66+
TRACE_DEEP = True
6667

6768

6869
def logger_debug(*args):
@@ -98,7 +99,7 @@ def skip_ignored(location):
9899
if TRACE_DEEP:
99100
logger_debug()
100101
logger_debug(
101-
"Codebase.populate: walk: ignored loc:",
102+
"Codebase.populate: walk: skip_ignored:",
102103
location,
103104
"ignored:",
104105
ignored(location),
@@ -109,6 +110,42 @@ def skip_ignored(location):
109110
return is_special(location) or ignored(location)
110111

111112

113+
def is_ignored(location, includes=None, excludes=None):
114+
115+
excludes = {
116+
pattern: 'User ignore: Supplied by --ignore' for pattern in excludes
117+
}
118+
119+
includes = {
120+
pattern: 'User include: Supplied by --include' for pattern in includes
121+
}
122+
123+
included_from_options = is_included(
124+
path=location,
125+
includes=includes,
126+
excludes=excludes,
127+
)
128+
129+
if TRACE_DEEP:
130+
logger_debug(
131+
"Codebase.populate: walk: is_ignored:",
132+
"is_ignored: location:",
133+
location,
134+
"included_from_options:",
135+
included_from_options,
136+
"skip_ignored",
137+
skip_ignored(location)
138+
)
139+
140+
if skip_ignored(location) or not included_from_options:
141+
if TRACE_DEEP:
142+
logger_debug("is_ignored: location:", location, "is_skipped",)
143+
144+
return True
145+
146+
return False
147+
148+
112149
def depth_walk(
113150
root_location,
114151
max_depth,
@@ -202,6 +239,8 @@ class Codebase:
202239
__slots__ = (
203240
"max_depth",
204241
"location",
242+
"includes",
243+
"ignores",
205244
"has_single_resource",
206245
"resource_attributes",
207246
"resource_class",
@@ -236,6 +275,8 @@ def __init__(
236275
max_in_memory=10000,
237276
max_depth=0,
238277
paths=tuple(),
278+
ignores=tuple(),
279+
includes=tuple(),
239280
*args,
240281
**kwargs,
241282
):
@@ -298,6 +339,8 @@ def __init__(
298339

299340
# finally populate
300341
self.paths = self._prepare_clean_paths(paths)
342+
self.ignores = ignores
343+
self.includes = includes
301344
self._populate()
302345

303346
def _prepare_clean_paths(self, paths=tuple()):
@@ -461,11 +504,17 @@ def _populate(self):
461504
return
462505

463506
if self.paths:
464-
return self._create_resources_from_paths(root=root, paths=self.paths)
507+
# In case of a list of full paths, we create resources without walking
508+
return self._create_resources_from_full_paths(root=root, paths=self.paths)
509+
# In case we have multiple
465510
else:
466-
return self._create_resources_from_root(root=root)
511+
return self._create_resources_from_root(
512+
root=root,
513+
includes=self.includes,
514+
ignores=self.ignores,
515+
)
467516

468-
def _create_resources_from_paths(self, root, paths):
517+
def _create_resources_from_full_paths(self, root, paths):
469518
# without paths we iterate the provided paths. We report an error
470519
# if a path is missing on disk.
471520

@@ -483,22 +532,21 @@ def _create_resources_from_paths(self, root, paths):
483532
msg = f"ERROR: cannot populate codebase: path: {path!r} not found in {res_loc!r}"
484533
self.errors.append(msg)
485534
raise Exception(path, join(base_location, path))
486-
continue
487535

488536
# create all parents. The last parent is the one we want to use
489537
parent = root
490538
if TRACE:
491-
logger_debug("Codebase._create_resources_from_paths: parent", parent)
539+
logger_debug("Codebase._create_resources_from_full_paths: parent", parent)
492540
for parent_path in get_ancestor_paths(path, include_self=False):
493541
if TRACE:
494542
logger_debug(
495-
f" Codebase._create_resources_from_paths: parent_path: {parent_path!r}"
543+
f" Codebase._create_resources_from_full_paths: parent_path: {parent_path!r}"
496544
)
497545
if not parent_path:
498546
continue
499547
newpar = parents_by_path.get(parent_path)
500548
if TRACE:
501-
logger_debug(" Codebase._create_resources_from_paths: newpar", repr(newpar))
549+
logger_debug(" Codebase._create_resources_from_full_paths: newpar", repr(newpar))
502550

503551
if not newpar:
504552
newpar = self._get_or_create_resource(
@@ -509,7 +557,7 @@ def _create_resources_from_paths(self, root, paths):
509557
)
510558
if not newpar:
511559
raise Exception(
512-
"ERROR: Codebase._create_resources_from_paths:"
560+
"ERROR: Codebase._create_resources_from_full_paths:"
513561
f" cannot create parent for: {parent_path!r}"
514562
)
515563
parent = newpar
@@ -518,7 +566,7 @@ def _create_resources_from_paths(self, root, paths):
518566

519567
if TRACE:
520568
logger_debug(
521-
f" Codebase._create_resources_from_paths:",
569+
f" Codebase._create_resources_from_full_paths:",
522570
f"created newpar: {newpar!r}",
523571
)
524572

@@ -529,10 +577,10 @@ def _create_resources_from_paths(self, root, paths):
529577
is_file=isfile(res_loc),
530578
)
531579
if TRACE:
532-
logger_debug("Codebase._create_resources_from_paths: resource", res)
580+
logger_debug("Codebase._create_resources_from_full_paths: resource", res)
533581

534-
def _create_resources_from_root(self, root):
535-
# without paths we walks the root location top-down
582+
def _create_resources_from_root(self, root, includes, ignores):
583+
# without paths we walk the root location top-down
536584

537585
# track resources parents by location during construction.
538586
# NOTE: this cannot exhaust memory on a large codebase, because we do
@@ -545,9 +593,15 @@ def err(_error):
545593
f"ERROR: cannot populate codebase: {_error}\n{traceback.format_exc()}"
546594
)
547595

596+
skip_ignored = partial(is_ignored, includes=includes, excludes=ignores)
597+
598+
if TRACE_DEEP:
599+
logger_debug(f"parents_by_loc: {parents_by_loc}, ignores: {ignores}, includes: {includes}")
600+
548601
# Walk over the directory and build the resource tree
549602
for top, dirs, files in depth_walk(
550603
root_location=root.location,
604+
skip_ignored=skip_ignored,
551605
max_depth=self.max_depth,
552606
error_handler=err,
553607
):
@@ -557,6 +611,7 @@ def err(_error):
557611
top=top,
558612
dirs=dirs,
559613
files=files,
614+
skip_ignored=skip_ignored,
560615
):
561616
# on the plain, bare FS, files cannot be parents
562617
if not created.is_file:
@@ -574,6 +629,8 @@ def _create_resources(self, parent, top, dirs, files, skip_ignored=skip_ignored)
574629
for name in names:
575630
location = join(top, name)
576631
if skip_ignored(location):
632+
if TRACE_DEEP:
633+
logger_debug(f"_create_resources, depth_walk loop: ignored location: {location}")
577634
continue
578635
res = self._get_or_create_resource(
579636
name=name,

src/scancode/cli.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,26 @@ def default_processes():
221221
callback=validate_input_path,
222222
type=click.Path(exists=True, readable=True, path_type=str))
223223

224+
@click.option('--include',
225+
multiple=True,
226+
default=None,
227+
metavar='<pattern>',
228+
help='Include files matching <pattern>.',
229+
sort_order=11,
230+
help_group=cliutils.CORE_GROUP,
231+
cls=PluggableCommandLineOption,
232+
)
233+
234+
@click.option('--ignore',
235+
multiple=True,
236+
default=None,
237+
metavar='<pattern>',
238+
help='Ignore files matching <pattern>.',
239+
sort_order=10,
240+
help_group=cliutils.CORE_GROUP,
241+
cls=PluggableCommandLineOption,
242+
)
243+
224244
@click.option('--strip-root',
225245
is_flag=True,
226246
default=False,
@@ -395,6 +415,8 @@ def default_processes():
395415
def scancode(
396416
ctx,
397417
input, # NOQA
418+
include,
419+
ignore,
398420
strip_root,
399421
full_root,
400422
processes,
@@ -505,6 +527,8 @@ def scancode(
505527
# run proper
506528
success, _results = run_scan(
507529
input=input,
530+
include=include,
531+
ignore=ignore,
508532
from_json=from_json,
509533
strip_root=strip_root,
510534
full_root=full_root,
@@ -545,7 +569,9 @@ def scancode(
545569

546570

547571
def run_scan(
548-
input, # NOQA
572+
input, #
573+
include=[],
574+
ignore=[],
549575
from_json=False,
550576
strip_root=False,
551577
full_root=False,
@@ -644,12 +670,10 @@ def echo_func(*_args, **_kwargs):
644670
# and we craft a list of synthetic --include path pattern options from
645671
# the input list of paths
646672
included_paths = [as_posixpath(path).rstrip('/') for path in input]
647-
# FIXME: this is a hack as this "include" is from an external plugin!!!
648-
include = list(requested_options.get('include', []) or [])
649673
include.extend(included_paths)
650-
requested_options['include'] = include
651674

652675
# ... and use the common prefix as our new input
676+
# FIXME: we should not walk outside inputs
653677
input = common_prefix # NOQA
654678

655679
# build mappings of all options to pass down to plugins
@@ -894,6 +918,8 @@ def echo_func(*_args, **_kwargs):
894918
try:
895919
codebase = codebase_class(
896920
location=input,
921+
includes=include,
922+
ignores=ignore,
897923
resource_attributes=resource_attributes,
898924
codebase_attributes=codebase_attributes,
899925
full_root=full_root,

0 commit comments

Comments
 (0)