2020# ScanCode.io is a free software code scanning tool from nexB Inc. and others.
2121# Visit https://github.com/nexB/scancode.io for support and download.
2222
23+ import inspect
24+ from importlib .machinery import SourceFileLoader
2325from pathlib import Path
2426
2527from django .apps import AppConfig
@@ -55,17 +57,32 @@ def ready(self):
5557
5658 def load_pipelines (self ):
5759 """
58- Load Pipelines from the "scancodeio_pipelines" entry point group.
60+ Load Pipelines from the "scancodeio_pipelines" entry point group and from the
61+ pipelines Python files found at `SCANCODEIO_PIPELINES_DIRS` locations.
5962 """
6063 entry_points = importlib_metadata .entry_points ()
6164
6265 # Ignore duplicated entries caused by duplicated paths in `sys.path`.
6366 pipeline_entry_points = set (entry_points .get ("scancodeio_pipelines" ))
6467
6568 for entry_point in sorted (pipeline_entry_points ):
66- pipeline_class = entry_point .load ()
67- pipeline_name = entry_point .name
68- self .register_pipeline (pipeline_name , pipeline_class )
69+ self .register_pipeline (name = entry_point .name , cls = entry_point .load ())
70+
71+ # Register user provided pipelines
72+ pipelines_dirs = getattr (settings , "SCANCODEIO_PIPELINES_DIRS" , [])
73+
74+ for pipelines_dir in pipelines_dirs :
75+ pipelines_path = Path (pipelines_dir )
76+
77+ if not pipelines_path .is_dir ():
78+ raise ImproperlyConfigured (
79+ f'The provided pipelines directory "{ pipelines_dir } " in '
80+ f"the SCANCODEIO_PIPELINES_DIRS setting is not available."
81+ )
82+
83+ python_files = pipelines_path .rglob ("*.py" )
84+ for path in python_files :
85+ self .register_pipeline_from_file (path )
6986
7087 def register_pipeline (self , name , cls ):
7188 """
@@ -83,6 +100,24 @@ def register_pipeline(self, name, cls):
83100
84101 self ._pipelines [name ] = cls
85102
103+ def register_pipeline_from_file (self , path ):
104+ """
105+ Search for a Pipeline subclass in the provided file `path` and register it
106+ when found.
107+ """
108+ module_name = path .stem
109+ module = SourceFileLoader (module_name , str (path )).load_module ()
110+ pipeline_classes = inspect .getmembers (module , is_pipeline )
111+
112+ if len (pipeline_classes ) > 1 :
113+ raise ImproperlyConfigured (
114+ f"Only one Pipeline class allowed per pipeline file: { path } ."
115+ )
116+
117+ elif pipeline_classes :
118+ pipeline_class = pipeline_classes [0 ][1 ]
119+ self .register_pipeline (name = module_name , cls = pipeline_class )
120+
86121 @property
87122 def pipelines (self ):
88123 return dict (self ._pipelines )
0 commit comments