@@ -191,6 +191,9 @@ def create_declarative_source(
191191 f"but got type: { type (config ['__injected_declarative_manifest' ])} "
192192 )
193193
194+ # Load custom components if provided - this will register them in sys.modules
195+ _parse_components_from_args (args )
196+
194197 return ConcurrentDeclarativeSource (
195198 config = config ,
196199 catalog = catalog ,
@@ -266,6 +269,63 @@ def _parse_manifest_from_args(args: list[str]) -> dict[str, Any] | None:
266269 return None
267270
268271
272+ def _register_components_from_file (filepath : str ) -> None :
273+ """Load and register components from a Python file for CLI usage.
274+
275+ This is a special case for CLI usage that bypasses the checksum validation
276+ since the user is explicitly providing the file to execute.
277+ """
278+ import importlib .util
279+ import sys
280+
281+ # Use Python's import mechanism to properly load the module
282+ components_path = Path (filepath )
283+
284+ # Standard module names that the rest of the system expects
285+ module_name = "components"
286+ sdm_module_name = "source_declarative_manifest.components"
287+
288+ # Create module spec
289+ spec = importlib .util .spec_from_file_location (module_name , components_path )
290+ if spec is None or spec .loader is None :
291+ raise ImportError (f"Could not load module from { components_path } " )
292+
293+ # Create module and execute code
294+ module = importlib .util .module_from_spec (spec )
295+
296+ # Register the module BEFORE executing its code
297+ # This is critical for features like dataclasses that look up the module
298+ sys .modules [module_name ] = module
299+ sys .modules [sdm_module_name ] = module
300+
301+ # Now execute the module code
302+ spec .loader .exec_module (module )
303+
304+
305+ def _parse_components_from_args (args : list [str ]) -> bool :
306+ """Loads and registers the custom components.py module if it exists.
307+
308+ This function imports the components module from a provided path
309+ and registers it in sys.modules so it can be found by the source.
310+
311+ Returns True if components were registered, False otherwise.
312+ """
313+ parsed_args = AirbyteEntrypoint .parse_args (args )
314+
315+ # Safely check if components_path is provided in the args
316+ if hasattr (parsed_args , "components_path" ) and parsed_args .components_path :
317+ try :
318+ # Use our CLI-specific function that bypasses checksum validation
319+ _register_components_from_file (parsed_args .components_path )
320+ return True
321+ except Exception as error :
322+ raise ValueError (
323+ f"Failed to load components from { parsed_args .components_path } : { error } "
324+ )
325+
326+ return False
327+
328+
269329def run () -> None :
270330 args : list [str ] = sys .argv [1 :]
271331 handle_command (args )
0 commit comments