-
Notifications
You must be signed in to change notification settings - Fork 74
build: prepare PyAirbyte slim package boundaries #1035
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
8c12be3
957093a
4081609
ba6dca9
3352d81
52bd2a8
9cbc54d
bb99bc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -123,21 +123,40 @@ | |
|
|
||
| from __future__ import annotations | ||
|
|
||
| from typing import TYPE_CHECKING | ||
| # ruff: noqa: F822 # Lazy exports are resolved by __getattr__ at runtime. | ||
| from importlib import import_module | ||
| from typing import TYPE_CHECKING, Any, cast | ||
|
|
||
| from airbyte import registry | ||
| from airbyte.caches.bigquery import BigQueryCache | ||
| from airbyte.caches.duckdb import DuckDBCache | ||
| from airbyte.caches.util import get_colab_cache, get_default_cache, new_local_cache | ||
| from airbyte.datasets import CachedDataset | ||
| from airbyte.destinations.base import Destination | ||
| from airbyte.destinations.util import get_destination | ||
| from airbyte.records import StreamRecord | ||
| from airbyte.registry import get_available_connectors | ||
| from airbyte.results import ReadResult, WriteResult | ||
| from airbyte.secrets import SecretSourceEnum, get_secret | ||
| from airbyte.sources.base import Source | ||
| from airbyte.sources.util import get_source | ||
|
|
||
|
|
||
| _LAZY_EXPORTS = { | ||
| "BigQueryCache": "airbyte.caches.bigquery", | ||
| "CachedDataset": "airbyte.datasets", | ||
| "Destination": "airbyte.destinations.base", | ||
| "DuckDBCache": "airbyte.caches.duckdb", | ||
| "ReadResult": "airbyte.results", | ||
| "Source": "airbyte.sources.base", | ||
| "WriteResult": "airbyte.results", | ||
| "get_colab_cache": "airbyte.caches.util", | ||
| "get_default_cache": "airbyte.caches.util", | ||
| "get_destination": "airbyte.destinations.util", | ||
| "get_source": "airbyte.sources.util", | ||
| "new_local_cache": "airbyte.caches.util", | ||
| } | ||
|
Comment on lines
+136
to
+149
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't follow what 'lazy exports' is supposed to mean. If obvious/easy, answer here. If shameful/controversial, answer in slack.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not shameful. Here "lazy exports" means the top-level Example: |
||
|
|
||
|
|
||
| def __getattr__(name: str) -> Any: # noqa: ANN401 | ||
| if name not in _LAZY_EXPORTS: | ||
| raise AttributeError(f"module 'airbyte' has no attribute {name!r}") | ||
|
|
||
| module = import_module(_LAZY_EXPORTS[name]) | ||
| value = cast("Any", getattr(module, name)) | ||
| globals()[name] = value | ||
| return value | ||
|
|
||
|
|
||
| # Submodules imported here for documentation reasons: https://github.com/mitmproxy/pdoc/issues/757 | ||
|
|
@@ -161,6 +180,15 @@ | |
| secrets, | ||
| sources, | ||
| ) | ||
| from airbyte.caches.bigquery import BigQueryCache | ||
| from airbyte.caches.duckdb import DuckDBCache | ||
| from airbyte.caches.util import get_colab_cache, get_default_cache, new_local_cache | ||
| from airbyte.datasets import CachedDataset | ||
| from airbyte.destinations.base import Destination | ||
| from airbyte.destinations.util import get_destination | ||
| from airbyte.results import ReadResult, WriteResult | ||
| from airbyte.sources.base import Source | ||
| from airbyte.sources.util import get_source | ||
|
|
||
|
|
||
| __all__ = [ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,31 +103,41 @@ | |
| import time | ||
| from collections.abc import Iterator, Mapping | ||
| from dataclasses import asdict, dataclass | ||
| from datetime import datetime, timezone | ||
| from typing import TYPE_CHECKING, Any | ||
|
|
||
| from typing_extensions import final | ||
|
|
||
| from airbyte_cdk.utils.datetime_helpers import ab_datetime_parse | ||
|
|
||
| from airbyte._util import api_util | ||
| from airbyte.caches._utils._dest_to_cache import destination_to_cache | ||
| from airbyte.cloud.constants import FAILED_STATUSES, FINAL_STATUSES | ||
| from airbyte.datasets import CachedDataset | ||
| from airbyte.exceptions import AirbyteConnectionSyncError, AirbyteConnectionSyncTimeoutError | ||
|
|
||
|
|
||
| def _parse_datetime(value: str | int) -> datetime: | ||
| if isinstance(value, int) or ( | ||
| isinstance(value, str) | ||
| and (value.isdigit() or (value.startswith("-") and value[1:].isdigit())) | ||
| ): | ||
| return datetime.fromtimestamp(int(value), tz=timezone.utc) | ||
|
|
||
| if not isinstance(value, str): | ||
| raise TypeError(f"Could not parse datetime string: {value}") | ||
|
|
||
| normalized = value.replace("Z", "+00:00") | ||
| return datetime.fromisoformat(normalized) | ||
|
|
||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get why we want to reuse cdk dateparse logic but higher priority is a deterministic code path. Vendor in what's needed and drop cdk for datetime parse logic here.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh. You already did? Sorry, my bad.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No worries. I double-checked the current file and there is no remaining
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move this to a proper _util helper module and import a non-underscored helper from there.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applied in Validation run:
|
||
|
|
||
| DEFAULT_SYNC_TIMEOUT_SECONDS = 30 * 60 # 30 minutes | ||
| """The default timeout for waiting for a sync job to complete, in seconds.""" | ||
|
|
||
| if TYPE_CHECKING: | ||
| from datetime import datetime | ||
|
|
||
| import sqlalchemy | ||
|
|
||
| from airbyte._util.api_imports import ConnectionResponse, JobResponse, JobStatusEnum | ||
| from airbyte.caches.base import CacheBase | ||
| from airbyte.cloud.connections import CloudConnection | ||
| from airbyte.cloud.workspaces import CloudWorkspace | ||
| from airbyte.datasets import CachedDataset | ||
|
|
||
|
|
||
| @dataclass | ||
|
|
@@ -168,7 +178,7 @@ def records_synced(self) -> int: | |
| def created_at(self) -> datetime: | ||
| """Return the creation time of the attempt.""" | ||
| timestamp = self._get_attempt_data()["createdAt"] | ||
| return ab_datetime_parse(timestamp) | ||
| return _parse_datetime(timestamp) | ||
|
|
||
| def _get_attempt_data(self) -> dict[str, Any]: | ||
| """Get attempt data from the provided attempt data.""" | ||
|
|
@@ -307,7 +317,7 @@ def records_synced(self) -> int: | |
| def start_time(self) -> datetime: | ||
| """Return the start time of the sync job in UTC.""" | ||
| try: | ||
| return ab_datetime_parse(self._fetch_latest_job_info().start_time) | ||
| return _parse_datetime(self._fetch_latest_job_info().start_time) | ||
| except (ValueError, TypeError) as e: | ||
| if "Invalid isoformat string" in str(e): | ||
| job_info_raw = api_util._make_config_api_request( # noqa: SLF001 | ||
|
|
@@ -321,7 +331,7 @@ def start_time(self) -> datetime: | |
| ) | ||
| raw_start_time = job_info_raw.get("startTime") | ||
| if raw_start_time: | ||
| return ab_datetime_parse(raw_start_time) | ||
| return _parse_datetime(raw_start_time) | ||
| raise | ||
|
|
||
| def _fetch_job_with_attempts(self) -> dict[str, Any]: | ||
|
|
@@ -421,6 +431,8 @@ def get_sql_cache(self) -> CacheBase: | |
| if self._cache: | ||
| return self._cache | ||
|
|
||
| from airbyte.caches._utils._dest_to_cache import destination_to_cache # noqa: PLC0415 | ||
|
|
||
| destination_configuration = self._get_destination_configuration() | ||
| self._cache = destination_to_cache(destination_configuration=destination_configuration) | ||
| return self._cache | ||
|
|
@@ -449,6 +461,8 @@ def get_dataset(self, stream_name: str) -> CachedDataset: | |
| (catalog information) to the `CachedDataset` object via the "Get stream properties" | ||
| API: https://reference.airbyte.com/reference/getstreamproperties | ||
| """ | ||
| from airbyte.datasets import CachedDataset # noqa: PLC0415 | ||
|
|
||
| return CachedDataset( | ||
| self.get_sql_cache(), | ||
| stream_name=stream_name, | ||
|
|
@@ -482,7 +496,7 @@ def streams( | |
| """ | ||
| return self._SyncResultStreams(self) | ||
|
|
||
| class _SyncResultStreams(Mapping[str, CachedDataset]): | ||
| class _SyncResultStreams(Mapping[str, "CachedDataset"]): | ||
| """A mapping of stream names to cached datasets.""" | ||
|
|
||
| def __init__( | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.