Skip to content

Commit bd452a8

Browse files
committed
chore: cleanup
1 parent bc521a6 commit bd452a8

File tree

3 files changed

+33
-109
lines changed

3 files changed

+33
-109
lines changed

airbyte_cdk/cli/source_declarative_manifest/_run.py

Lines changed: 27 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616

1717
from __future__ import annotations
1818

19+
import argparse
1920
import json
2021
import pkgutil
2122
import sys
2223
import traceback
23-
from collections.abc import Mapping, MutableMapping
24+
from collections.abc import MutableMapping
2425
from pathlib import Path
2526
from typing import Any, cast
2627

@@ -92,7 +93,8 @@ def handle_command(args: list[str]) -> None:
9293

9394
def _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

224229
def _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

272267
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-
"""
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-
329291
def run() -> None:
330292
args: list[str] = sys.argv[1:]
331293
handle_command(args)

unit_tests/source_declarative_manifest/test_source_declarative_remote_manifest.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
33
#
44

5-
import pytest
65
from pathlib import Path
76
from unittest.mock import mock_open, patch
87

8+
import pytest
9+
910
from airbyte_cdk.cli.source_declarative_manifest._run import (
11+
_parse_manifest_from_file,
1012
create_declarative_source,
1113
handle_command,
12-
_parse_manifest_from_args,
1314
)
1415
from airbyte_cdk.sources.declarative.manifest_declarative_source import ManifestDeclarativeSource
1516

@@ -32,20 +33,9 @@ def test_given_injected_declarative_manifest_then_return_declarative_manifest(va
3233
assert isinstance(source, ManifestDeclarativeSource)
3334

3435

35-
def test_parse_manifest_from_args(valid_remote_config: Path) -> None:
36+
def test_parse_manifest_from_file(valid_remote_config: Path) -> None:
3637
mock_manifest_content = '{"test_manifest": "fancy_declarative_components"}'
3738
with patch("builtins.open", mock_open(read_data=mock_manifest_content)):
3839
# Test with manifest path
39-
result = _parse_manifest_from_args(
40-
[
41-
"check",
42-
"--config",
43-
str(valid_remote_config),
44-
"--manifest-path",
45-
"manifest.yaml",
46-
]
47-
)
40+
result = _parse_manifest_from_file("manifest.yaml")
4841
assert result == {"test_manifest": "fancy_declarative_components"}
49-
50-
# Test without manifest path
51-
assert _parse_manifest_from_args(["check", "--config", str(valid_remote_config)]) is None

unit_tests/source_declarative_manifest/test_source_declarative_w_custom_components.py

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,17 @@
66
import json
77
import logging
88
import sys
9-
import tempfile
109
import types
1110
from collections.abc import Callable, Mapping
1211
from pathlib import Path
1312
from tempfile import NamedTemporaryFile
14-
from typing import Any, Generator
13+
from typing import Any
1514

1615
import pytest
1716
import yaml
1817
from airbyte_protocol_dataclasses.models.airbyte_protocol import AirbyteCatalog
1918

2019
from airbyte_cdk.cli.source_declarative_manifest._run import (
21-
_parse_components_from_args,
2220
_register_components_from_file,
2321
create_declarative_source,
2422
)
@@ -295,29 +293,3 @@ def test_register_components_from_file(components_file: str) -> None:
295293

296294
# Verify the components were loaded correctly
297295
verify_components_loaded()
298-
299-
300-
def test_parse_components_from_args(monkeypatch: pytest.MonkeyPatch, components_file: str) -> None:
301-
"""Test that components can be loaded from command line arguments."""
302-
303-
# Mock the arguments
304-
class MockArgs:
305-
components_path = components_file
306-
307-
# Mock the parse_args function to return our mock args
308-
def mock_parse_args(*args: Any, **kwargs: Any) -> Any:
309-
return MockArgs()
310-
311-
# Apply the monkeypatch
312-
from airbyte_cdk.entrypoint import AirbyteEntrypoint
313-
314-
monkeypatch.setattr(AirbyteEntrypoint, "parse_args", mock_parse_args)
315-
316-
# Call the function with any args (they'll be ignored due to the mock)
317-
result = _parse_components_from_args(["some", "args"])
318-
319-
# Verify result
320-
assert result is True # Should return True when successful
321-
322-
# Verify the components were loaded
323-
verify_components_loaded()

0 commit comments

Comments
 (0)