1616
1717from __future__ import annotations
1818
19+ import argparse
1920import json
2021import pkgutil
2122import sys
2223import traceback
23- from collections .abc import Mapping , MutableMapping
24+ from collections .abc import MutableMapping
2425from pathlib import Path
2526from typing import Any , cast
2627
@@ -92,7 +93,8 @@ def handle_command(args: list[str]) -> None:
9293
9394def _get_local_yaml_source (args : list [str ]) -> SourceLocalYaml :
9495 try :
95- config , catalog , state = _parse_inputs_into_config_catalog_state (args )
96+ parsed_args = AirbyteEntrypoint .parse_args (args )
97+ config , catalog , state = _parse_inputs_into_config_catalog_state (parsed_args )
9698 return SourceLocalYaml (config = config , catalog = catalog , state = state )
9799 except Exception as error :
98100 print (
@@ -166,7 +168,9 @@ def create_declarative_source(
166168 config : MutableMapping [str , Any ] | None
167169 catalog : ConfiguredAirbyteCatalog | None
168170 state : list [AirbyteStateMessage ]
169- config , catalog , state = _parse_inputs_into_config_catalog_state (args )
171+
172+ parsed_args = AirbyteEntrypoint .parse_args (args )
173+ config , catalog , state = _parse_inputs_into_config_catalog_state (parsed_args )
170174
171175 if config is None :
172176 raise ValueError (
@@ -175,9 +179,10 @@ def create_declarative_source(
175179 )
176180
177181 # If a manifest_path is provided in the args, inject it into the config
178- injected_manifest = _parse_manifest_from_args (args )
179- if injected_manifest :
180- config ["__injected_declarative_manifest" ] = injected_manifest
182+ if hasattr (parsed_args , "manifest_path" ) and parsed_args .manifest_path :
183+ injected_manifest = _parse_manifest_from_file (parsed_args .manifest_path )
184+ if injected_manifest :
185+ config ["__injected_declarative_manifest" ] = injected_manifest
181186
182187 if "__injected_declarative_manifest" not in config :
183188 raise ValueError (
@@ -191,8 +196,8 @@ def create_declarative_source(
191196 f"but got type: { type (config ['__injected_declarative_manifest' ])} "
192197 )
193198
194- # Load custom components if provided - this will register them in sys.modules
195- _parse_components_from_args ( args )
199+ if hasattr ( parsed_args , "components_path" ) and parsed_args . components_path :
200+ _register_components_from_file ( parsed_args . components_path )
196201
197202 return ConcurrentDeclarativeSource (
198203 config = config ,
@@ -222,13 +227,12 @@ def create_declarative_source(
222227
223228
224229def _parse_inputs_into_config_catalog_state (
225- args : list [ str ] ,
230+ parsed_args : argparse . Namespace ,
226231) -> tuple [
227232 MutableMapping [str , Any ] | None ,
228233 ConfiguredAirbyteCatalog | None ,
229234 list [AirbyteStateMessage ],
230235]:
231- parsed_args = AirbyteEntrypoint .parse_args (args )
232236 config = (
233237 ConcurrentDeclarativeSource .read_config (parsed_args .config )
234238 if hasattr (parsed_args , "config" )
@@ -248,40 +252,25 @@ def _parse_inputs_into_config_catalog_state(
248252 return config , catalog , state
249253
250254
251- def _parse_manifest_from_args (args : list [str ]) -> dict [str , Any ] | None :
252- """Extracts and parse the manifest file if specified in the args."""
253- parsed_args = AirbyteEntrypoint .parse_args (args )
254-
255- # Safely check if manifest_path is provided in the args
256- if hasattr (parsed_args , "manifest_path" ) and parsed_args .manifest_path :
257- try :
258- # Read the manifest file
259- with open (parsed_args .manifest_path , "r" ) as manifest_file :
260- manifest_content = yaml .safe_load (manifest_file )
261- if not isinstance (manifest_content , dict ):
262- raise ValueError (f"Manifest must be a dictionary, got { type (manifest_content )} " )
263- return manifest_content
264- except Exception as error :
265- raise ValueError (
266- f"Failed to load manifest file from { parsed_args .manifest_path } : { error } "
267- )
268-
269- return None
255+ def _parse_manifest_from_file (filepath : str ) -> dict [str , Any ] | None :
256+ """Extract and parse a manifest file specified in the args."""
257+ try :
258+ with open (filepath , "r" ) as manifest_file :
259+ manifest_content = yaml .safe_load (manifest_file )
260+ if not isinstance (manifest_content , dict ):
261+ raise ValueError (f"Manifest must be a dictionary, got { type (manifest_content )} " )
262+ return manifest_content
263+ except Exception as error :
264+ raise ValueError (f"Failed to load manifest file from { filepath } : { error } " )
270265
271266
272267def _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- """
268+ """Load and register components from a Python file specified in the args."""
278269 import importlib .util
279270 import sys
280271
281- # Use Python's import mechanism to properly load the module
282272 components_path = Path (filepath )
283273
284- # Standard module names that the rest of the system expects
285274 module_name = "components"
286275 sdm_module_name = "source_declarative_manifest.components"
287276
@@ -290,42 +279,15 @@ def _register_components_from_file(filepath: str) -> None:
290279 if spec is None or spec .loader is None :
291280 raise ImportError (f"Could not load module from { components_path } " )
292281
293- # Create module and execute code
282+ # Create module and execute code, registering the module before executing its code
283+ # To avoid issues with dataclasses that look up the module
294284 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
298285 sys .modules [module_name ] = module
299286 sys .modules [sdm_module_name ] = module
300287
301- # Now execute the module code
302288 spec .loader .exec_module (module )
303289
304290
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-
329291def run () -> None :
330292 args : list [str ] = sys .argv [1 :]
331293 handle_command (args )
0 commit comments