openlineage-python 1.35.0__tar.gz → 1.36.0__tar.gz
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.
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/PKG-INFO +1 -1
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/client.py +10 -9
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/constants.py +1 -1
- openlineage_python-1.36.0/openlineage/client/naming/dataset.py +512 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/async_http.py +64 -3
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/pyproject.toml +1 -1
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_client.py +274 -1
- openlineage_python-1.36.0/tests/test_dataset.py +276 -0
- openlineage_python-1.36.0/tests/transform/transformers/__init__.py +2 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/.gitignore +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/README.md +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/docs/Makefile +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/docs/conf.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/docs/convert.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/docs/index.rst +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/docs/make.bat +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/docs/source/openlineage.client.rst +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/event_v2.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/facet.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/facet_v2.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/facets.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/filter.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/base.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/catalog_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/column_lineage_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/data_quality_assertions_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/data_quality_metrics_input_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/dataset_type_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/dataset_version_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/datasource_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/documentation_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/documentation_job.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/environment_variables_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/error_message_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/external_query_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/extraction_error_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/input_statistics_input_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/job_type_job.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/lifecycle_state_change_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/nominal_time_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/output_statistics_output_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/ownership_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/ownership_job.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/parent_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/processing_engine_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/schema_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/source_code_job.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/source_code_location_job.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/sql_job.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/storage_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/symlinks_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/tags_dataset.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/tags_job.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generated/tags_run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/base.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/cli.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/generate.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/header.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/templates/Enum.jinja2 +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/templates/dataclass.jinja2 +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/templates/facet_v2.jinja2 +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/templates/root.jinja2 +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/generator/templates/validators.jinja2 +0 -0
- {openlineage_python-1.35.0/openlineage/client/transport/transform/transformers → openlineage_python-1.36.0/openlineage/client/naming}/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/py.typed +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/run.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/serde.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/tags.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/amazon_datazone.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/composite.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/console.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/factory.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/file.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/http.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/kafka.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/msk_iam.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/noop.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/transform/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/transform/transform.py +0 -0
- {openlineage_python-1.35.0/tests → openlineage_python-1.36.0/openlineage/client/transport/transform/transformers}/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/transform/transformers/job_namespace_replace_transformer.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/transport/transport.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/utils.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/openlineage/client/uuid.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/redact_fields.yml +0 -0
- {openlineage_python-1.35.0/tests/generator → openlineage_python-1.36.0/tests}/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/config/config.yml +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/config/exact_filter.yml +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/config/http.yml +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/config/openlineage.yml +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/config/regex_filter.yml +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/config/test-transport.yaml +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/conftest.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/example_full_event.json +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/fixtures/events.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/fixtures/mock_responses.py +0 -0
- {openlineage_python-1.35.0/tests/transform → openlineage_python-1.36.0/tests/generator}/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/generator/test_base.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/generator/test_cli.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/integration/docker-compose.yml +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/integration/test_server/Dockerfile +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/integration/test_server/requirements.txt +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/integration/test_server/test_server.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/nominal_time_without_end.json +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/serde_example_dataset_event.json +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/serde_example_job_event.json +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/serde_example_run_event.json +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_amazon_datazone.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_async_http.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_composite.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_events.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_facet.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_facet_v2.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_factory.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_file.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_http.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_http_integration.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_kafka.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_msk_iam.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_utils.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/test_uuid.py +0 -0
- {openlineage_python-1.35.0/tests/transform/transformers → openlineage_python-1.36.0/tests/transform}/__init__.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/transform/test_transform.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/transform/transformers/test_job_namespace_replace_transformer.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tests/transport.py +0 -0
- {openlineage_python-1.35.0 → openlineage_python-1.36.0}/tox.ini +0 -0
|
@@ -232,9 +232,9 @@ class OpenLineageClient:
|
|
|
232
232
|
This method determines the appropriate transport by executing a sequence of checks:
|
|
233
233
|
1. Verifies if OpenLineage is disabled through environment variable.
|
|
234
234
|
2. Looks for a transport object provided in the arguments.
|
|
235
|
-
3. Attempts to configure the transport from
|
|
235
|
+
3. Attempts to configure the transport from user config, config file or env vars.
|
|
236
236
|
4. Tries to initialize HTTP transport with an url argument (deprecated).
|
|
237
|
-
5. Tries to set up HTTP transport using environment variables.
|
|
237
|
+
5. Tries to set up HTTP transport using simple environment variables.
|
|
238
238
|
6. If no configuration is found, defaults to a console transport and logs a warning message.
|
|
239
239
|
|
|
240
240
|
Returns:
|
|
@@ -245,11 +245,11 @@ class OpenLineageClient:
|
|
|
245
245
|
log.info("OpenLineage is disabled. No events will be emitted.")
|
|
246
246
|
return NoopTransport(NoopConfig())
|
|
247
247
|
|
|
248
|
-
# 2. Check if transport is provided explicitly
|
|
248
|
+
# 2. Check if transport is provided explicitly as argument
|
|
249
249
|
if kwargs.get("transport"):
|
|
250
250
|
return cast("Transport", kwargs["transport"])
|
|
251
251
|
|
|
252
|
-
# 3. Check if transport configuration is provided
|
|
252
|
+
# 3. Check if transport configuration is provided as config (explicit, file or env vars)
|
|
253
253
|
if self.config.transport and self.config.transport.get("type"):
|
|
254
254
|
factory = kwargs.get("factory") or get_default_factory()
|
|
255
255
|
return factory.create(self.config.transport)
|
|
@@ -260,7 +260,7 @@ class OpenLineageClient:
|
|
|
260
260
|
url=kwargs["url"], options=kwargs.get("options"), session=kwargs.get("session")
|
|
261
261
|
)
|
|
262
262
|
|
|
263
|
-
# 5. Check HTTP transport initialization with env variables
|
|
263
|
+
# 5. Check HTTP transport initialization with simple env variables
|
|
264
264
|
if os.environ.get("OPENLINEAGE_URL"):
|
|
265
265
|
return self._http_transport_from_env_variables()
|
|
266
266
|
|
|
@@ -307,7 +307,8 @@ class OpenLineageClient:
|
|
|
307
307
|
log.exception("Couldn't check if OpenLineage config file is readable: `%s`", path)
|
|
308
308
|
return None
|
|
309
309
|
|
|
310
|
-
|
|
310
|
+
@staticmethod
|
|
311
|
+
def _http_transport_from_env_variables() -> HttpTransport:
|
|
311
312
|
"""
|
|
312
313
|
Create HTTP transport from legacy environment variables
|
|
313
314
|
"""
|
|
@@ -345,7 +346,7 @@ class OpenLineageClient:
|
|
|
345
346
|
k.startswith(f"OPENLINEAGE__TRANSPORT__TRANSPORTS__{default_transport_name}")
|
|
346
347
|
for k in os.environ
|
|
347
348
|
):
|
|
348
|
-
log.
|
|
349
|
+
log.info(
|
|
349
350
|
"%s already found in environment variables, skipping aliasing OPENLINEAGE_URL",
|
|
350
351
|
default_transport_name,
|
|
351
352
|
)
|
|
@@ -388,7 +389,7 @@ class OpenLineageClient:
|
|
|
388
389
|
def _load_config_from_env_variables(cls) -> dict[str, Any] | None:
|
|
389
390
|
config: dict[str, Any] = {}
|
|
390
391
|
|
|
391
|
-
# get os.environ.items only starting with
|
|
392
|
+
# get os.environ.items only starting with OPENLINEAGE__ prefix and reverse sort
|
|
392
393
|
# to make sure that top-level keys have precedence
|
|
393
394
|
env_vars = sorted(
|
|
394
395
|
filter(lambda k: k[0].startswith(cls.DYNAMIC_ENV_VARS_PREFIX), os.environ.items()), reverse=True
|
|
@@ -467,8 +468,8 @@ class OpenLineageClient:
|
|
|
467
468
|
|
|
468
469
|
return event
|
|
469
470
|
|
|
471
|
+
@staticmethod
|
|
470
472
|
def _update_tag_facet(
|
|
471
|
-
self,
|
|
472
473
|
tags_facet: TagsJobFacet | TagsRunFacet,
|
|
473
474
|
user_tags: list[TagsJobFacetFields | TagsRunFacetFields],
|
|
474
475
|
) -> TagsJobFacet | TagsRunFacet:
|
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
# Copyright 2018-2025 contributors to the OpenLineage project
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Utility module containing dataset naming helpers based on OpenLineage's dataset naming
|
|
6
|
+
specification.
|
|
7
|
+
|
|
8
|
+
Each class represents naming logic for a specific platform (e.g., BigQuery, AWS Glue),
|
|
9
|
+
and provides methods to construct valid dataset names and namespaces.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from typing import Protocol
|
|
16
|
+
|
|
17
|
+
import attr
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _check_not_empty(instance: object, attribute: attr.Attribute[str], value: str) -> str: # noqa: ARG001
|
|
21
|
+
"""Validator to check if a string is not None and not empty."""
|
|
22
|
+
if value is None:
|
|
23
|
+
msg = f"{attribute.name} cannot be None"
|
|
24
|
+
raise ValueError(msg)
|
|
25
|
+
if not value.strip():
|
|
26
|
+
msg = f"{attribute.name} cannot be empty"
|
|
27
|
+
raise ValueError(msg)
|
|
28
|
+
return value
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _check_enum_not_none(
|
|
32
|
+
_: object, attribute: attr.Attribute[PubSubResourceType], value: PubSubResourceType
|
|
33
|
+
) -> PubSubResourceType:
|
|
34
|
+
"""Validator to check if an enum value is not None."""
|
|
35
|
+
if value is None:
|
|
36
|
+
msg = f"{attribute.name} cannot be None"
|
|
37
|
+
raise ValueError(msg)
|
|
38
|
+
return PubSubResourceType(value)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DatasetNaming(Protocol):
|
|
42
|
+
"""Interface representing dataset naming logic for a specific platform."""
|
|
43
|
+
|
|
44
|
+
def get_namespace(self) -> str:
|
|
45
|
+
"""
|
|
46
|
+
Returns the dataset namespace (e.g., URI or ARN identifying the data source).
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
The namespace string
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def get_name(self) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Returns the dataset name specific to the platform format.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The dataset name string
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@attr.define
|
|
62
|
+
class Athena(DatasetNaming):
|
|
63
|
+
"""Naming implementation for Athena."""
|
|
64
|
+
|
|
65
|
+
region_name: str = attr.field(validator=_check_not_empty)
|
|
66
|
+
catalog: str = attr.field(validator=_check_not_empty)
|
|
67
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
68
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
69
|
+
|
|
70
|
+
def get_namespace(self) -> str:
|
|
71
|
+
return f"awsathena://athena.{self.region_name}.amazonaws.com"
|
|
72
|
+
|
|
73
|
+
def get_name(self) -> str:
|
|
74
|
+
return f"{self.catalog}.{self.database}.{self.table}"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@attr.define
|
|
78
|
+
class AWSGlue(DatasetNaming):
|
|
79
|
+
"""Naming implementation for AWS Glue."""
|
|
80
|
+
|
|
81
|
+
region_name: str = attr.field(validator=_check_not_empty)
|
|
82
|
+
account_id: str = attr.field(validator=_check_not_empty)
|
|
83
|
+
database_name: str = attr.field(validator=_check_not_empty)
|
|
84
|
+
table_name: str = attr.field(validator=_check_not_empty)
|
|
85
|
+
|
|
86
|
+
def get_namespace(self) -> str:
|
|
87
|
+
return f"arn:aws:glue:{self.region_name}:{self.account_id}"
|
|
88
|
+
|
|
89
|
+
def get_name(self) -> str:
|
|
90
|
+
return f"table/{self.database_name}/{self.table_name}"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@attr.define
|
|
94
|
+
class AzureCosmosDB(DatasetNaming):
|
|
95
|
+
"""Naming implementation for Azure Cosmos DB."""
|
|
96
|
+
|
|
97
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
98
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
99
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
100
|
+
|
|
101
|
+
def get_namespace(self) -> str:
|
|
102
|
+
return f"azurecosmos://{self.host}/dbs/{self.database}"
|
|
103
|
+
|
|
104
|
+
def get_name(self) -> str:
|
|
105
|
+
return f"colls/{self.table}"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@attr.define
|
|
109
|
+
class AzureDataExplorer(DatasetNaming):
|
|
110
|
+
"""Naming implementation for Azure Data Explorer."""
|
|
111
|
+
|
|
112
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
113
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
114
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
115
|
+
|
|
116
|
+
def get_namespace(self) -> str:
|
|
117
|
+
return f"azurekusto://{self.host}.kusto.windows.net"
|
|
118
|
+
|
|
119
|
+
def get_name(self) -> str:
|
|
120
|
+
return f"{self.database}/{self.table}"
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@attr.define
|
|
124
|
+
class AzureSynapse(DatasetNaming):
|
|
125
|
+
"""Naming implementation for AzureSynapse."""
|
|
126
|
+
|
|
127
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
128
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
129
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
130
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
131
|
+
|
|
132
|
+
def get_namespace(self) -> str:
|
|
133
|
+
return f"sqlserver://{self.host}:{self.port}"
|
|
134
|
+
|
|
135
|
+
def get_name(self) -> str:
|
|
136
|
+
return f"{self.schema}.{self.table}"
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@attr.define
|
|
140
|
+
class BigQuery(DatasetNaming):
|
|
141
|
+
"""
|
|
142
|
+
Naming implementation for BigQuery.
|
|
143
|
+
|
|
144
|
+
Namespace is always "bigquery".
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
project_id: str = attr.field(validator=_check_not_empty)
|
|
148
|
+
dataset_name: str = attr.field(validator=_check_not_empty)
|
|
149
|
+
table_name: str = attr.field(validator=_check_not_empty)
|
|
150
|
+
|
|
151
|
+
def get_namespace(self) -> str:
|
|
152
|
+
return "bigquery"
|
|
153
|
+
|
|
154
|
+
def get_name(self) -> str:
|
|
155
|
+
return f"{self.project_id}.{self.dataset_name}.{self.table_name}"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@attr.define
|
|
159
|
+
class Cassandra(DatasetNaming):
|
|
160
|
+
"""Naming implementation for Cassandra."""
|
|
161
|
+
|
|
162
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
163
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
164
|
+
keyspace: str = attr.field(validator=_check_not_empty)
|
|
165
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
166
|
+
|
|
167
|
+
def get_namespace(self) -> str:
|
|
168
|
+
return f"cassandra://{self.host}:{self.port}"
|
|
169
|
+
|
|
170
|
+
def get_name(self) -> str:
|
|
171
|
+
return f"{self.keyspace}.{self.table}"
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@attr.define
|
|
175
|
+
class MySQL(DatasetNaming):
|
|
176
|
+
"""Naming implementation for MySQL."""
|
|
177
|
+
|
|
178
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
179
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
180
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
181
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
182
|
+
|
|
183
|
+
def get_namespace(self) -> str:
|
|
184
|
+
return f"mysql://{self.host}:{self.port}"
|
|
185
|
+
|
|
186
|
+
def get_name(self) -> str:
|
|
187
|
+
return f"{self.database}.{self.table}"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@attr.define
|
|
191
|
+
class CrateDB(DatasetNaming):
|
|
192
|
+
"""Naming implementation for CrateDB."""
|
|
193
|
+
|
|
194
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
195
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
196
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
197
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
198
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
199
|
+
|
|
200
|
+
def get_namespace(self) -> str:
|
|
201
|
+
return f"crate://{self.host}:{self.port}"
|
|
202
|
+
|
|
203
|
+
def get_name(self) -> str:
|
|
204
|
+
return f"{self.database}.{self.schema}.{self.table}"
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@attr.define
|
|
208
|
+
class DB2(DatasetNaming):
|
|
209
|
+
"""Naming implementation for DB2."""
|
|
210
|
+
|
|
211
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
212
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
213
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
214
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
215
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
216
|
+
|
|
217
|
+
def get_namespace(self) -> str:
|
|
218
|
+
return f"db2://{self.host}:{self.port}"
|
|
219
|
+
|
|
220
|
+
def get_name(self) -> str:
|
|
221
|
+
return f"{self.database}.{self.schema}.{self.table}"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@attr.define
|
|
225
|
+
class OceanBase(DatasetNaming):
|
|
226
|
+
"""Naming implementation for OceanBase."""
|
|
227
|
+
|
|
228
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
229
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
230
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
231
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
232
|
+
|
|
233
|
+
def get_namespace(self) -> str:
|
|
234
|
+
return f"oceanbase://{self.host}:{self.port}"
|
|
235
|
+
|
|
236
|
+
def get_name(self) -> str:
|
|
237
|
+
return f"{self.database}.{self.table}"
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
@attr.define
|
|
241
|
+
class Oracle(DatasetNaming):
|
|
242
|
+
"""Naming implementation for Oracle."""
|
|
243
|
+
|
|
244
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
245
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
246
|
+
service_name: str = attr.field(validator=_check_not_empty)
|
|
247
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
248
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
249
|
+
|
|
250
|
+
def get_namespace(self) -> str:
|
|
251
|
+
return f"oracle://{self.host}:{self.port}"
|
|
252
|
+
|
|
253
|
+
def get_name(self) -> str:
|
|
254
|
+
return f"{self.service_name}.{self.schema}.{self.table}"
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@attr.define
|
|
258
|
+
class Postgres(DatasetNaming):
|
|
259
|
+
"""Naming implementation for Postgres."""
|
|
260
|
+
|
|
261
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
262
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
263
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
264
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
265
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
266
|
+
|
|
267
|
+
def get_namespace(self) -> str:
|
|
268
|
+
return f"postgres://{self.host}:{self.port}"
|
|
269
|
+
|
|
270
|
+
def get_name(self) -> str:
|
|
271
|
+
return f"{self.database}.{self.schema}.{self.table}"
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@attr.define
|
|
275
|
+
class Teradata(DatasetNaming):
|
|
276
|
+
"""Naming implementation for Teradata."""
|
|
277
|
+
|
|
278
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
279
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
280
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
281
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
282
|
+
|
|
283
|
+
def get_namespace(self) -> str:
|
|
284
|
+
return f"teradata://{self.host}:{self.port}"
|
|
285
|
+
|
|
286
|
+
def get_name(self) -> str:
|
|
287
|
+
return f"{self.database}.{self.table}"
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@attr.define
|
|
291
|
+
class Redshift(DatasetNaming):
|
|
292
|
+
"""Naming implementation for Redshift."""
|
|
293
|
+
|
|
294
|
+
cluster_identifier: str = attr.field(validator=_check_not_empty)
|
|
295
|
+
region: str = attr.field(validator=_check_not_empty)
|
|
296
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
297
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
298
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
299
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
300
|
+
|
|
301
|
+
def get_namespace(self) -> str:
|
|
302
|
+
return f"redshift://{self.cluster_identifier}.{self.region}:{self.port}"
|
|
303
|
+
|
|
304
|
+
def get_name(self) -> str:
|
|
305
|
+
return f"{self.database}.{self.schema}.{self.table}"
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@attr.define
|
|
309
|
+
class Snowflake(DatasetNaming):
|
|
310
|
+
"""Naming implementation for Snowflake."""
|
|
311
|
+
|
|
312
|
+
organization_name: str = attr.field(validator=_check_not_empty)
|
|
313
|
+
account_name: str = attr.field(validator=_check_not_empty)
|
|
314
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
315
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
316
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
317
|
+
|
|
318
|
+
def get_namespace(self) -> str:
|
|
319
|
+
return f"snowflake://{self.organization_name}-{self.account_name}"
|
|
320
|
+
|
|
321
|
+
def get_name(self) -> str:
|
|
322
|
+
return f"{self.database}.{self.schema}.{self.table}"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@attr.define
|
|
326
|
+
class Trino(DatasetNaming):
|
|
327
|
+
"""Naming implementation for Trino."""
|
|
328
|
+
|
|
329
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
330
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
331
|
+
catalog: str = attr.field(validator=_check_not_empty)
|
|
332
|
+
schema: str = attr.field(validator=_check_not_empty)
|
|
333
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
334
|
+
|
|
335
|
+
def get_namespace(self) -> str:
|
|
336
|
+
return f"trino://{self.host}:{self.port}"
|
|
337
|
+
|
|
338
|
+
def get_name(self) -> str:
|
|
339
|
+
return f"{self.catalog}.{self.schema}.{self.table}"
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
@attr.define
|
|
343
|
+
class ABFSS(DatasetNaming):
|
|
344
|
+
"""Naming implementation for ABFSS."""
|
|
345
|
+
|
|
346
|
+
container: str = attr.field(validator=_check_not_empty)
|
|
347
|
+
service: str = attr.field(validator=_check_not_empty)
|
|
348
|
+
path: str = attr.field(validator=_check_not_empty)
|
|
349
|
+
|
|
350
|
+
def get_namespace(self) -> str:
|
|
351
|
+
return f"abfss://{self.container}@{self.service}.dfs.core.windows.net"
|
|
352
|
+
|
|
353
|
+
def get_name(self) -> str:
|
|
354
|
+
return self.path
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@attr.define
|
|
358
|
+
class DBFS(DatasetNaming):
|
|
359
|
+
"""Naming implementation for DBFS."""
|
|
360
|
+
|
|
361
|
+
workspace: str = attr.field(validator=_check_not_empty)
|
|
362
|
+
path: str = attr.field(validator=_check_not_empty)
|
|
363
|
+
|
|
364
|
+
def get_namespace(self) -> str:
|
|
365
|
+
return f"dbfs://{self.workspace}"
|
|
366
|
+
|
|
367
|
+
def get_name(self) -> str:
|
|
368
|
+
return self.path
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@attr.define
|
|
372
|
+
class GCS(DatasetNaming):
|
|
373
|
+
"""Naming implementation for GCS."""
|
|
374
|
+
|
|
375
|
+
bucket_name: str = attr.field(validator=_check_not_empty)
|
|
376
|
+
object_key: str = attr.field(validator=_check_not_empty)
|
|
377
|
+
|
|
378
|
+
def get_namespace(self) -> str:
|
|
379
|
+
return f"gs://{self.bucket_name}"
|
|
380
|
+
|
|
381
|
+
def get_name(self) -> str:
|
|
382
|
+
return self.object_key
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@attr.define
|
|
386
|
+
class HDFS(DatasetNaming):
|
|
387
|
+
"""Naming implementation for HDFS."""
|
|
388
|
+
|
|
389
|
+
namenode_host: str = attr.field(validator=_check_not_empty)
|
|
390
|
+
namenode_port: str = attr.field(validator=_check_not_empty)
|
|
391
|
+
path: str = attr.field(validator=_check_not_empty)
|
|
392
|
+
|
|
393
|
+
def get_namespace(self) -> str:
|
|
394
|
+
return f"hdfs://{self.namenode_host}:{self.namenode_port}"
|
|
395
|
+
|
|
396
|
+
def get_name(self) -> str:
|
|
397
|
+
return self.path
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
@attr.define
|
|
401
|
+
class Hive(DatasetNaming):
|
|
402
|
+
"""Naming implementation for Hive."""
|
|
403
|
+
|
|
404
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
405
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
406
|
+
database: str = attr.field(validator=_check_not_empty)
|
|
407
|
+
table: str = attr.field(validator=_check_not_empty)
|
|
408
|
+
|
|
409
|
+
def get_namespace(self) -> str:
|
|
410
|
+
return f"hive://{self.host}:{self.port}"
|
|
411
|
+
|
|
412
|
+
def get_name(self) -> str:
|
|
413
|
+
return f"{self.database}.{self.table}"
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
@attr.define
|
|
417
|
+
class Kafka(DatasetNaming):
|
|
418
|
+
"""Naming implementation for Kafka."""
|
|
419
|
+
|
|
420
|
+
bootstrap_server_host: str = attr.field(validator=_check_not_empty)
|
|
421
|
+
port: str = attr.field(validator=_check_not_empty)
|
|
422
|
+
topic: str = attr.field(validator=_check_not_empty)
|
|
423
|
+
|
|
424
|
+
def get_namespace(self) -> str:
|
|
425
|
+
return f"kafka://{self.bootstrap_server_host}:{self.port}"
|
|
426
|
+
|
|
427
|
+
def get_name(self) -> str:
|
|
428
|
+
return self.topic
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
@attr.define
|
|
432
|
+
class LocalFileSystem(DatasetNaming):
|
|
433
|
+
"""Naming implementation for Local File System."""
|
|
434
|
+
|
|
435
|
+
path: str = attr.field(validator=_check_not_empty)
|
|
436
|
+
|
|
437
|
+
def get_namespace(self) -> str:
|
|
438
|
+
return "file"
|
|
439
|
+
|
|
440
|
+
def get_name(self) -> str:
|
|
441
|
+
return self.path
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@attr.define
|
|
445
|
+
class RemoteFileSystem(DatasetNaming):
|
|
446
|
+
"""Naming implementation for Remote File System."""
|
|
447
|
+
|
|
448
|
+
host: str = attr.field(validator=_check_not_empty)
|
|
449
|
+
path: str = attr.field(validator=_check_not_empty)
|
|
450
|
+
|
|
451
|
+
def get_namespace(self) -> str:
|
|
452
|
+
return f"file://{self.host}"
|
|
453
|
+
|
|
454
|
+
def get_name(self) -> str:
|
|
455
|
+
return self.path
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
@attr.define
|
|
459
|
+
class S3(DatasetNaming):
|
|
460
|
+
"""Naming implementation for S3."""
|
|
461
|
+
|
|
462
|
+
bucket_name: str = attr.field(validator=_check_not_empty)
|
|
463
|
+
object_key: str = attr.field(validator=_check_not_empty)
|
|
464
|
+
|
|
465
|
+
def get_namespace(self) -> str:
|
|
466
|
+
return f"s3://{self.bucket_name}"
|
|
467
|
+
|
|
468
|
+
def get_name(self) -> str:
|
|
469
|
+
return self.object_key
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
@attr.define
|
|
473
|
+
class WASBS(DatasetNaming):
|
|
474
|
+
"""Naming implementation for WASBS."""
|
|
475
|
+
|
|
476
|
+
container: str = attr.field(validator=_check_not_empty)
|
|
477
|
+
service_name: str = attr.field(validator=_check_not_empty)
|
|
478
|
+
object_key: str = attr.field(validator=_check_not_empty)
|
|
479
|
+
|
|
480
|
+
def get_namespace(self) -> str:
|
|
481
|
+
return f"wasbs://{self.container}@{self.service_name}.dfs.core.windows.net"
|
|
482
|
+
|
|
483
|
+
def get_name(self) -> str:
|
|
484
|
+
return self.object_key
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class PubSubResourceType(Enum):
|
|
488
|
+
"""Enum representing supported Pub/Sub resource types."""
|
|
489
|
+
|
|
490
|
+
TOPIC = "topic"
|
|
491
|
+
SUBSCRIPTION = "subscription"
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@attr.define
|
|
495
|
+
class PubSubNaming(DatasetNaming):
|
|
496
|
+
"""
|
|
497
|
+
Naming implementation for Google Pub/Sub.
|
|
498
|
+
|
|
499
|
+
Namespace is fixed as "pubsub". Name format depends on the resource type:
|
|
500
|
+
- topic:{project_id}:{topic_id}
|
|
501
|
+
- subscription:{project_id}:{subscription_id}
|
|
502
|
+
"""
|
|
503
|
+
|
|
504
|
+
resource_type: PubSubResourceType = attr.field(validator=_check_enum_not_none)
|
|
505
|
+
project_id: str = attr.field(validator=_check_not_empty)
|
|
506
|
+
resource_id: str = attr.field(validator=_check_not_empty)
|
|
507
|
+
|
|
508
|
+
def get_namespace(self) -> str:
|
|
509
|
+
return "pubsub"
|
|
510
|
+
|
|
511
|
+
def get_name(self) -> str:
|
|
512
|
+
return f"{self.resource_type.value}:{self.project_id}:{self.resource_id}"
|