airbyte-cdk 6.48.15__py3-none-any.whl → 6.48.16.post14.dev15122056170__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,6 +39,7 @@ poetry run airbyte-cdk connector --help
39
39
  """
40
40
 
41
41
  import os
42
+ import sys
42
43
  from pathlib import Path
43
44
  from types import ModuleType
44
45
 
@@ -166,10 +167,11 @@ def test(
166
167
  click.echo(f"Collect only: {collect_only}")
167
168
  click.echo(f"Pytest args: {pytest_args}")
168
169
  click.echo("Invoking Pytest...")
169
- pytest.main(
170
+ exit_code = pytest.main(
170
171
  pytest_args,
171
172
  plugins=[],
172
173
  )
174
+ sys.exit(exit_code)
173
175
 
174
176
 
175
177
  __all__ = [
@@ -49,7 +49,12 @@ from airbyte_cdk.utils.connector_paths import (
49
49
  resolve_connector_name_and_directory,
50
50
  )
51
51
 
52
- AIRBYTE_INTERNAL_GCP_PROJECT = "dataline-integration-testing"
52
+ GCP_PROJECT_ID: str = os.environ.get("GCP_PROJECT_ID", "") or "dataline-integration-testing"
53
+ # We put the `or` outside the `get()` because we want the `GCP_PROJECT_ID`
54
+ # env var to be ignored if it contains an empty string, such as in CI where the
55
+ # workflow might set it to a value that is itself actually missing or unset.
56
+ """The GCP project ID to use for fetching integration test secrets."""
57
+
53
58
  CONNECTOR_LABEL = "connector"
54
59
  GLOBAL_MASK_KEYS_URL = "https://connectors.airbyte.com/files/registries/v0/specs_secrets_mask.yaml"
55
60
 
@@ -83,8 +88,11 @@ def secrets_cli_group() -> None:
83
88
  @click.option(
84
89
  "--gcp-project-id",
85
90
  type=str,
86
- default=AIRBYTE_INTERNAL_GCP_PROJECT,
87
- help=f"GCP project ID. Defaults to '{AIRBYTE_INTERNAL_GCP_PROJECT}'.",
91
+ default=GCP_PROJECT_ID,
92
+ help=(
93
+ "GCP project ID for retrieving integration tests credentials. "
94
+ "Defaults to the value of the `GCP_PROJECT_ID` environment variable, if set."
95
+ ),
88
96
  )
89
97
  @click.option(
90
98
  "--print-ci-secrets-masks",
@@ -95,7 +103,7 @@ def secrets_cli_group() -> None:
95
103
  )
96
104
  def fetch(
97
105
  connector: str | Path | None = None,
98
- gcp_project_id: str = AIRBYTE_INTERNAL_GCP_PROJECT,
106
+ gcp_project_id: str = GCP_PROJECT_ID,
99
107
  print_ci_secrets_masks: bool = False,
100
108
  ) -> None:
101
109
  """Fetch secrets for a connector from Google Secret Manager.
@@ -192,41 +200,41 @@ def fetch(
192
200
 
193
201
 
194
202
  @secrets_cli_group.command("list")
195
- @click.option(
196
- "--connector-name",
203
+ @click.argument(
204
+ "connector",
205
+ required=False,
197
206
  type=str,
198
- help="Name of the connector to fetch secrets for. Ignored if --connector-directory is provided.",
199
- )
200
- @click.option(
201
- "--connector-directory",
202
- type=click.Path(exists=True, file_okay=False, path_type=Path),
203
- help="Path to the connector directory.",
207
+ metavar="[CONNECTOR]",
204
208
  )
205
209
  @click.option(
206
210
  "--gcp-project-id",
207
211
  type=str,
208
- default=AIRBYTE_INTERNAL_GCP_PROJECT,
209
- help=f"GCP project ID. Defaults to '{AIRBYTE_INTERNAL_GCP_PROJECT}'.",
212
+ default=GCP_PROJECT_ID,
213
+ help=(
214
+ "GCP project ID for retrieving integration tests credentials. "
215
+ "Defaults to the value of the `GCP_PROJECT_ID` environment variable, if set."
216
+ ),
210
217
  )
211
218
  def list_(
212
- connector_name: str | None = None,
213
- connector_directory: Path | None = None,
214
- gcp_project_id: str = AIRBYTE_INTERNAL_GCP_PROJECT,
219
+ connector: str | Path | None = None,
220
+ *,
221
+ gcp_project_id: str = GCP_PROJECT_ID,
215
222
  ) -> None:
216
223
  """List secrets for a connector from Google Secret Manager.
217
224
 
218
225
  This command fetches secrets for a connector from Google Secret Manager and prints
219
226
  them as a table.
220
227
 
228
+ [CONNECTOR] can be a connector name (e.g. 'source-pokeapi'), a path to a connector directory, or omitted to use the current working directory.
229
+ If a string containing '/' is provided, it is treated as a path. Otherwise, it is treated as a connector name.
230
+
221
231
  If no connector name or directory is provided, we will look within the current working
222
232
  directory. If the current working directory is not a connector directory (e.g. starting
223
233
  with 'source-') and no connector name or path is provided, the process will fail.
224
234
  """
225
235
  click.echo("Scanning secrets...", err=True)
226
236
 
227
- connector_name = connector_name or resolve_connector_name(
228
- connector_directory=connector_directory or Path().resolve().absolute(),
229
- )
237
+ connector_name, _ = resolve_connector_name_and_directory(connector)
230
238
  secrets: list[Secret] = _fetch_secret_handles( # type: ignore
231
239
  connector_name=connector_name,
232
240
  gcp_project_id=gcp_project_id,
@@ -303,7 +311,7 @@ def _get_secret_url(secret_name: str, gcp_project_id: str) -> str:
303
311
 
304
312
  def _fetch_secret_handles(
305
313
  connector_name: str,
306
- gcp_project_id: str = AIRBYTE_INTERNAL_GCP_PROJECT,
314
+ gcp_project_id: str = GCP_PROJECT_ID,
307
315
  ) -> list["Secret"]: # type: ignore
308
316
  """Fetch secrets from Google Secret Manager."""
309
317
  if not secretmanager:
@@ -16,15 +16,17 @@ source-declarative-manifest spec
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
+ import argparse
19
20
  import json
20
21
  import pkgutil
21
22
  import sys
22
23
  import traceback
23
- from collections.abc import Mapping
24
+ from collections.abc import MutableMapping
24
25
  from pathlib import Path
25
26
  from typing import Any, cast
26
27
 
27
28
  import orjson
29
+ import yaml
28
30
 
29
31
  from airbyte_cdk.entrypoint import AirbyteEntrypoint, launch
30
32
  from airbyte_cdk.models import (
@@ -54,7 +56,7 @@ class SourceLocalYaml(YamlDeclarativeSource):
54
56
  def __init__(
55
57
  self,
56
58
  catalog: ConfiguredAirbyteCatalog | None,
57
- config: Mapping[str, Any] | None,
59
+ config: MutableMapping[str, Any] | None,
58
60
  state: TState,
59
61
  **kwargs: Any,
60
62
  ) -> None:
@@ -91,7 +93,8 @@ def handle_command(args: list[str]) -> None:
91
93
 
92
94
  def _get_local_yaml_source(args: list[str]) -> SourceLocalYaml:
93
95
  try:
94
- 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)
95
98
  return SourceLocalYaml(config=config, catalog=catalog, state=state)
96
99
  except Exception as error:
97
100
  print(
@@ -162,14 +165,30 @@ def create_declarative_source(
162
165
  connector builder.
163
166
  """
164
167
  try:
165
- config: Mapping[str, Any] | None
168
+ config: MutableMapping[str, Any] | None
166
169
  catalog: ConfiguredAirbyteCatalog | None
167
170
  state: list[AirbyteStateMessage]
168
- config, catalog, state = _parse_inputs_into_config_catalog_state(args)
169
- if config is None or "__injected_declarative_manifest" not in config:
171
+
172
+ parsed_args = AirbyteEntrypoint.parse_args(args)
173
+ config, catalog, state = _parse_inputs_into_config_catalog_state(parsed_args)
174
+
175
+ if config is None:
176
+ raise ValueError(
177
+ "Invalid config: `__injected_declarative_manifest` should be provided at the root "
178
+ "of the config or using the --manifest-path argument."
179
+ )
180
+
181
+ # If a manifest_path is provided in the args, inject it into the config
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
186
+
187
+ if "__injected_declarative_manifest" not in config:
170
188
  raise ValueError(
171
189
  "Invalid config: `__injected_declarative_manifest` should be provided at the root "
172
- f"of the config but config only has keys: {list(config.keys() if config else [])}"
190
+ "of the config or using the --manifest-path argument. "
191
+ f"Config only has keys: {list(config.keys() if config else [])}"
173
192
  )
174
193
  if not isinstance(config["__injected_declarative_manifest"], dict):
175
194
  raise ValueError(
@@ -177,6 +196,9 @@ def create_declarative_source(
177
196
  f"but got type: {type(config['__injected_declarative_manifest'])}"
178
197
  )
179
198
 
199
+ if hasattr(parsed_args, "components_path") and parsed_args.components_path:
200
+ _register_components_from_file(parsed_args.components_path)
201
+
180
202
  return ConcurrentDeclarativeSource(
181
203
  config=config,
182
204
  catalog=catalog,
@@ -205,13 +227,12 @@ def create_declarative_source(
205
227
 
206
228
 
207
229
  def _parse_inputs_into_config_catalog_state(
208
- args: list[str],
230
+ parsed_args: argparse.Namespace,
209
231
  ) -> tuple[
210
- Mapping[str, Any] | None,
232
+ MutableMapping[str, Any] | None,
211
233
  ConfiguredAirbyteCatalog | None,
212
234
  list[AirbyteStateMessage],
213
235
  ]:
214
- parsed_args = AirbyteEntrypoint.parse_args(args)
215
236
  config = (
216
237
  ConcurrentDeclarativeSource.read_config(parsed_args.config)
217
238
  if hasattr(parsed_args, "config")
@@ -231,6 +252,44 @@ def _parse_inputs_into_config_catalog_state(
231
252
  return config, catalog, state
232
253
 
233
254
 
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", encoding="utf-8") as manifest_file:
259
+ manifest_content = yaml.safe_load(manifest_file)
260
+ if manifest_content is None:
261
+ raise ValueError(f"Manifest file at {filepath} is empty")
262
+ if not isinstance(manifest_content, dict):
263
+ raise ValueError(f"Manifest must be a dictionary, got {type(manifest_content)}")
264
+ return manifest_content
265
+ except Exception as error:
266
+ raise ValueError(f"Failed to load manifest file from {filepath}: {error}")
267
+
268
+
269
+ def _register_components_from_file(filepath: str) -> None:
270
+ """Load and register components from a Python file specified in the args."""
271
+ import importlib.util
272
+ import sys
273
+
274
+ components_path = Path(filepath)
275
+
276
+ module_name = "components"
277
+ sdm_module_name = "source_declarative_manifest.components"
278
+
279
+ # Create module spec
280
+ spec = importlib.util.spec_from_file_location(module_name, components_path)
281
+ if spec is None or spec.loader is None:
282
+ raise ImportError(f"Could not load module from {components_path}")
283
+
284
+ # Create module and execute code, registering the module before executing its code
285
+ # To avoid issues with dataclasses that look up the module
286
+ module = importlib.util.module_from_spec(spec)
287
+ sys.modules[module_name] = module
288
+ sys.modules[sdm_module_name] = module
289
+
290
+ spec.loader.exec_module(module)
291
+
292
+
234
293
  def run() -> None:
235
294
  args: list[str] = sys.argv[1:]
236
295
  handle_command(args)
airbyte_cdk/connector.py CHANGED
@@ -8,7 +8,7 @@ import logging
8
8
  import os
9
9
  import pkgutil
10
10
  from abc import ABC, abstractmethod
11
- from typing import Any, Generic, Mapping, Optional, Protocol, TypeVar
11
+ from typing import Any, Generic, Mapping, MutableMapping, Optional, Protocol, TypeVar
12
12
 
13
13
  import yaml
14
14
 
@@ -41,9 +41,9 @@ class BaseConnector(ABC, Generic[TConfig]):
41
41
  """
42
42
 
43
43
  @staticmethod
44
- def read_config(config_path: str) -> Mapping[str, Any]:
44
+ def read_config(config_path: str) -> MutableMapping[str, Any]:
45
45
  config = BaseConnector._read_json_file(config_path)
46
- if isinstance(config, Mapping):
46
+ if isinstance(config, MutableMapping):
47
47
  return config
48
48
  else:
49
49
  raise ValueError(
airbyte_cdk/entrypoint.py CHANGED
@@ -84,6 +84,18 @@ class AirbyteEntrypoint(object):
84
84
  required_check_parser.add_argument(
85
85
  "--config", type=str, required=True, help="path to the json configuration file"
86
86
  )
87
+ check_parser.add_argument(
88
+ "--manifest-path",
89
+ type=str,
90
+ required=False,
91
+ help="path to the YAML manifest file to inject into the config",
92
+ )
93
+ check_parser.add_argument(
94
+ "--components-path",
95
+ type=str,
96
+ required=False,
97
+ help="path to the custom components file, if it exists",
98
+ )
87
99
 
88
100
  # discover
89
101
  discover_parser = subparsers.add_parser(
@@ -95,6 +107,18 @@ class AirbyteEntrypoint(object):
95
107
  required_discover_parser.add_argument(
96
108
  "--config", type=str, required=True, help="path to the json configuration file"
97
109
  )
110
+ discover_parser.add_argument(
111
+ "--manifest-path",
112
+ type=str,
113
+ required=False,
114
+ help="path to the YAML manifest file to inject into the config",
115
+ )
116
+ discover_parser.add_argument(
117
+ "--components-path",
118
+ type=str,
119
+ required=False,
120
+ help="path to the custom components file, if it exists",
121
+ )
98
122
 
99
123
  # read
100
124
  read_parser = subparsers.add_parser(
@@ -114,6 +138,18 @@ class AirbyteEntrypoint(object):
114
138
  required=True,
115
139
  help="path to the catalog used to determine which data to read",
116
140
  )
141
+ read_parser.add_argument(
142
+ "--manifest-path",
143
+ type=str,
144
+ required=False,
145
+ help="path to the YAML manifest file to inject into the config",
146
+ )
147
+ read_parser.add_argument(
148
+ "--components-path",
149
+ type=str,
150
+ required=False,
151
+ help="path to the custom components file, if it exists",
152
+ )
117
153
 
118
154
  return main_parser.parse_args(args)
119
155
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: airbyte-cdk
3
- Version: 6.48.15
3
+ Version: 6.48.16.post14.dev15122056170
4
4
  Summary: A framework for writing Airbyte Connectors.
5
5
  Home-page: https://airbyte.com
6
6
  License: MIT
@@ -1,17 +1,17 @@
1
1
  airbyte_cdk/__init__.py,sha256=52uncJvDQNHvwKxaqzXgnMYTptIl65LDJr2fvlk8-DU,11707
2
2
  airbyte_cdk/cli/__init__.py,sha256=CXsai3MYMLZ_sqi2vPAIVcKDun8VRqlv0cKffBI0iSY,346
3
3
  airbyte_cdk/cli/airbyte_cdk/__init__.py,sha256=8IoEcbdYr7CMAh97Xut5__uHH9vV4LKUtSBNTk3qEWY,2031
4
- airbyte_cdk/cli/airbyte_cdk/_connector.py,sha256=drKb_EXJOFX-cSeLwJB8WXE-lesOL0dx2ziWSmW3Jkg,5187
4
+ airbyte_cdk/cli/airbyte_cdk/_connector.py,sha256=PA1beSvhcvm-6watFCrjNat9GC4RpYolC53z2r1V3dc,5234
5
5
  airbyte_cdk/cli/airbyte_cdk/_image.py,sha256=AkBEZrRYXEwvhW7hPOPRWeYEZutzi2PIzjpl7_yaE8I,2890
6
6
  airbyte_cdk/cli/airbyte_cdk/_manifest.py,sha256=aFdeeWgek7oXR3YfZPxk7kBZ64Blmsr0dAXN6BVGiIA,482
7
- airbyte_cdk/cli/airbyte_cdk/_secrets.py,sha256=1IqVz-csoMJoKajSX4DzoWrngswuNHBPkzzchQggAeE,17010
7
+ airbyte_cdk/cli/airbyte_cdk/_secrets.py,sha256=jLtpkhFJHavABN3UuqddeDRGS8v1iEj0e_f6WTeYoQA,17409
8
8
  airbyte_cdk/cli/airbyte_cdk/_version.py,sha256=ohZNIktLFk91sdzqFW5idaNrZAPX2dIRnz---_fcKOE,352
9
9
  airbyte_cdk/cli/airbyte_cdk/exceptions.py,sha256=bsGmlWN6cXL2jCD1WYAZMqFmK1OLg2xLrcC_60KHSeA,803
10
10
  airbyte_cdk/cli/source_declarative_manifest/__init__.py,sha256=-0ST722Nj65bgRokzpzPkD1NBBW5CytEHFUe38cB86Q,91
11
- airbyte_cdk/cli/source_declarative_manifest/_run.py,sha256=9qtbjt-I_stGWzWX6yVUKO_eE-Ga7g-uTuibML9qLBs,8330
11
+ airbyte_cdk/cli/source_declarative_manifest/_run.py,sha256=3ZXAic1MJkDyjfOGvcmmkIDY5vAqsNPp8A7Z8xjzm-A,10829
12
12
  airbyte_cdk/cli/source_declarative_manifest/spec.json,sha256=Earc1L6ngcdIr514oFQlUoOxdF4RHqtUyStSIAquXdY,554
13
13
  airbyte_cdk/config_observation.py,sha256=7SSPxtN0nXPkm4euGNcTTr1iLbwUL01jy-24V1Hzde0,3986
14
- airbyte_cdk/connector.py,sha256=bO23kdGRkl8XKFytOgrrWFc_VagteTHVEF6IsbizVkM,4224
14
+ airbyte_cdk/connector.py,sha256=N6TUlrZOMjLAI85JrNAKkfyTqnO5xfBCw4oEfgjJd9o,4254
15
15
  airbyte_cdk/connector_builder/README.md,sha256=Hw3wvVewuHG9-QgsAq1jDiKuLlStDxKBz52ftyNRnBw,1665
16
16
  airbyte_cdk/connector_builder/__init__.py,sha256=4Hw-PX1-VgESLF16cDdvuYCzGJtHntThLF4qIiULWeo,61
17
17
  airbyte_cdk/connector_builder/connector_builder_handler.py,sha256=OFTzxyfAevI3Um8fXTOLTgoCc4Sx9NzF0boqYkAATfM,6590
@@ -33,7 +33,7 @@ airbyte_cdk/destinations/vector_db_based/indexer.py,sha256=beiSi2Uu67EoTr7yQSaCJ
33
33
  airbyte_cdk/destinations/vector_db_based/test_utils.py,sha256=MkqLiOJ5QyKbV4rNiJhe-BHM7FD-ADHQ4bQGf4c5lRY,1932
34
34
  airbyte_cdk/destinations/vector_db_based/utils.py,sha256=FOyEo8Lc-fY8UyhpCivhZtIqBRyxf3cUt6anmK03fUY,1127
35
35
  airbyte_cdk/destinations/vector_db_based/writer.py,sha256=nZ00xPiohElJmYktEZZIhr0m5EDETCHGhg0Lb2S7A20,5095
36
- airbyte_cdk/entrypoint.py,sha256=NRJv5BNZRSUEVTmNBa9N7ih6fW5sg4DwL0nkB9kI99Y,18570
36
+ airbyte_cdk/entrypoint.py,sha256=R2kAsAnCAI7eZCctQpMCImLhFFwo7PniJVA0e7RhJVI,19774
37
37
  airbyte_cdk/exception_handler.py,sha256=D_doVl3Dt60ASXlJsfviOCswxGyKF2q0RL6rif3fNks,2013
38
38
  airbyte_cdk/logger.py,sha256=1cURbvawbunCAV178q-XhTHcbAQZTSf07WhU7U9AXWU,3744
39
39
  airbyte_cdk/manifest_migrations/README.md,sha256=PvnbrW1gyzhlkeucd0YAOXcXVxi0xBUUynzs4DMqjDo,2942
@@ -419,9 +419,9 @@ airbyte_cdk/utils/slice_hasher.py,sha256=EDxgROHDbfG-QKQb59m7h_7crN1tRiawdf5uU7G
419
419
  airbyte_cdk/utils/spec_schema_transformations.py,sha256=-5HTuNsnDBAhj-oLeQXwpTGA0HdcjFOf2zTEMUTTg_Y,816
420
420
  airbyte_cdk/utils/stream_status_utils.py,sha256=ZmBoiy5HVbUEHAMrUONxZvxnvfV9CesmQJLDTAIWnWw,1171
421
421
  airbyte_cdk/utils/traced_exception.py,sha256=C8uIBuCL_E4WnBAOPSxBicD06JAldoN9fGsQDp463OY,6292
422
- airbyte_cdk-6.48.15.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
423
- airbyte_cdk-6.48.15.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
424
- airbyte_cdk-6.48.15.dist-info/METADATA,sha256=DP9YSnAFjf7aaIFb4V1xGPe6kc3TY8HAQNZdy7Bgaq4,6344
425
- airbyte_cdk-6.48.15.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
426
- airbyte_cdk-6.48.15.dist-info/entry_points.txt,sha256=AKWbEkHfpzzk9nF9tqBUaw1MbvTM4mGtEzmZQm0ZWvM,139
427
- airbyte_cdk-6.48.15.dist-info/RECORD,,
422
+ airbyte_cdk-6.48.16.post14.dev15122056170.dist-info/LICENSE.txt,sha256=Wfe61S4BaGPj404v8lrAbvhjYR68SHlkzeYrg3_bbuM,1051
423
+ airbyte_cdk-6.48.16.post14.dev15122056170.dist-info/LICENSE_SHORT,sha256=aqF6D1NcESmpn-cqsxBtszTEnHKnlsp8L4x9wAh3Nxg,55
424
+ airbyte_cdk-6.48.16.post14.dev15122056170.dist-info/METADATA,sha256=4-aZbDzT7hC6E84o6NC0UDVyW0zanMyD7b_T-i_GH6o,6366
425
+ airbyte_cdk-6.48.16.post14.dev15122056170.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
426
+ airbyte_cdk-6.48.16.post14.dev15122056170.dist-info/entry_points.txt,sha256=AKWbEkHfpzzk9nF9tqBUaw1MbvTM4mGtEzmZQm0ZWvM,139
427
+ airbyte_cdk-6.48.16.post14.dev15122056170.dist-info/RECORD,,