openlineage-python 1.36.0__tar.gz → 1.37.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.36.0 → openlineage_python-1.37.0}/PKG-INFO +1 -1
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/client.py +6 -6
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/constants.py +1 -1
- openlineage_python-1.37.0/openlineage/client/tags.py +11 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/__init__.py +4 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/async_http.py +3 -1
- openlineage_python-1.37.0/openlineage/client/transport/datadog.py +164 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/http.py +3 -1
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/pyproject.toml +1 -1
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_client.py +11 -0
- openlineage_python-1.37.0/tests/test_datadog.py +1014 -0
- openlineage_python-1.36.0/openlineage/client/tags.py +0 -17
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/.gitignore +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/README.md +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/docs/Makefile +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/docs/conf.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/docs/convert.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/docs/index.rst +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/docs/make.bat +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/docs/source/openlineage.client.rst +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/event_v2.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/facet.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/facet_v2.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/facets.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/filter.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/base.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/catalog_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/column_lineage_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/data_quality_assertions_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/data_quality_metrics_input_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/dataset_type_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/dataset_version_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/datasource_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/documentation_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/documentation_job.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/environment_variables_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/error_message_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/external_query_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/extraction_error_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/input_statistics_input_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/job_type_job.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/lifecycle_state_change_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/nominal_time_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/output_statistics_output_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/ownership_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/ownership_job.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/parent_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/processing_engine_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/schema_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/source_code_job.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/source_code_location_job.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/sql_job.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/storage_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/symlinks_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/tags_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/tags_job.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generated/tags_run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/base.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/cli.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/generate.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/header.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/templates/Enum.jinja2 +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/templates/dataclass.jinja2 +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/templates/facet_v2.jinja2 +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/templates/root.jinja2 +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/generator/templates/validators.jinja2 +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/naming/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/naming/dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/py.typed +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/run.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/serde.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/amazon_datazone.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/composite.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/console.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/factory.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/file.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/kafka.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/msk_iam.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/noop.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/transform/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/transform/transform.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/transform/transformers/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/transform/transformers/job_namespace_replace_transformer.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/transport.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/utils.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/uuid.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/redact_fields.yml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/config/config.yml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/config/exact_filter.yml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/config/http.yml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/config/openlineage.yml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/config/regex_filter.yml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/config/test-transport.yaml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/conftest.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/example_full_event.json +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/fixtures/events.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/fixtures/mock_responses.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/generator/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/generator/test_base.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/generator/test_cli.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/integration/docker-compose.yml +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/integration/test_server/Dockerfile +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/integration/test_server/requirements.txt +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/integration/test_server/test_server.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/nominal_time_without_end.json +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/serde_example_dataset_event.json +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/serde_example_job_event.json +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/serde_example_run_event.json +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_amazon_datazone.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_async_http.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_composite.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_dataset.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_events.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_facet.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_facet_v2.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_factory.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_file.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_http.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_http_integration.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_kafka.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_msk_iam.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_utils.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/test_uuid.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/transform/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/transform/test_transform.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/transform/transformers/__init__.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/transform/transformers/test_job_namespace_replace_transformer.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tests/transport.py +0 -0
- {openlineage_python-1.36.0 → openlineage_python-1.37.0}/tox.ini +0 -0
|
@@ -67,11 +67,9 @@ class OpenLineageConfig:
|
|
|
67
67
|
if "filters" in params:
|
|
68
68
|
config.filters = [FilterConfig(**filter_config) for filter_config in params["filters"]]
|
|
69
69
|
if "tags" in params:
|
|
70
|
-
job_tags = params["tags"].get("job", {})
|
|
71
|
-
run_tags = params["tags"].get("run", {})
|
|
72
70
|
config.tags = TagsConfig(
|
|
73
|
-
job=[
|
|
74
|
-
run=[
|
|
71
|
+
job=params["tags"].get("job", {}),
|
|
72
|
+
run=params["tags"].get("run", {}),
|
|
75
73
|
)
|
|
76
74
|
return config
|
|
77
75
|
|
|
@@ -452,14 +450,16 @@ class OpenLineageClient:
|
|
|
452
450
|
"""
|
|
453
451
|
run_event_types = (RunEvent, event_v2.RunEvent)
|
|
454
452
|
run_and_job_event_types = (RunEvent, event_v2.RunEvent, JobEvent, event_v2.JobEvent)
|
|
455
|
-
tags_job = self.config.tags.job
|
|
453
|
+
# tags_job = self.config.tags.job
|
|
454
|
+
tags_job = [TagsJobFacetFields(key, value, "USER") for (key, value) in self.config.tags.job.items()]
|
|
456
455
|
if isinstance(event, run_and_job_event_types) and tags_job:
|
|
457
456
|
# Ensure facets exists
|
|
458
457
|
event.job.facets = {} if not event.job.facets else event.job.facets
|
|
459
458
|
tags_facet = event.job.facets.get("tags", TagsJobFacet())
|
|
460
459
|
event.job.facets["tags"] = self._update_tag_facet(tags_facet, tags_job) # type: ignore [arg-type, assignment]
|
|
461
460
|
|
|
462
|
-
tags_run = self.config.tags.run
|
|
461
|
+
# tags_run = self.config.tags.run
|
|
462
|
+
tags_run = [TagsRunFacetFields(key, value, "USER") for (key, value) in self.config.tags.run.items()]
|
|
463
463
|
if isinstance(event, run_event_types) and tags_run:
|
|
464
464
|
# Ensure facets exists
|
|
465
465
|
event.run.facets = {} if not event.run.facets else event.run.facets
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Copyright 2018-2025 contributors to the OpenLineage project
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import attr
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@attr.define
|
|
9
|
+
class TagsConfig:
|
|
10
|
+
job: dict[str, str] = attr.field(factory=dict)
|
|
11
|
+
run: dict[str, str] = attr.field(factory=dict)
|
{openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/__init__.py
RENAMED
|
@@ -6,6 +6,7 @@ from openlineage.client.transport.amazon_datazone import AmazonDataZoneConfig, A
|
|
|
6
6
|
from openlineage.client.transport.async_http import AsyncHttpConfig, AsyncHttpTransport
|
|
7
7
|
from openlineage.client.transport.composite import CompositeTransport
|
|
8
8
|
from openlineage.client.transport.console import ConsoleTransport
|
|
9
|
+
from openlineage.client.transport.datadog import DatadogConfig, DatadogTransport
|
|
9
10
|
from openlineage.client.transport.factory import DefaultTransportFactory
|
|
10
11
|
from openlineage.client.transport.file import FileTransport
|
|
11
12
|
from openlineage.client.transport.http import HttpConfig, HttpTransport
|
|
@@ -29,6 +30,7 @@ _factory.register_transport(NoopTransport.kind, NoopTransport)
|
|
|
29
30
|
_factory.register_transport(FileTransport.kind, FileTransport)
|
|
30
31
|
_factory.register_transport(TransformTransport.kind, TransformTransport)
|
|
31
32
|
_factory.register_transport(AmazonDataZoneTransport.kind, AmazonDataZoneTransport)
|
|
33
|
+
_factory.register_transport(DatadogTransport.kind, DatadogTransport)
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
def get_default_factory() -> DefaultTransportFactory:
|
|
@@ -50,6 +52,8 @@ __all__ = [
|
|
|
50
52
|
"CompositeTransport",
|
|
51
53
|
"Config",
|
|
52
54
|
"ConsoleTransport",
|
|
55
|
+
"DatadogConfig",
|
|
56
|
+
"DatadogTransport",
|
|
53
57
|
"FileTransport",
|
|
54
58
|
"HttpConfig",
|
|
55
59
|
"HttpTransport",
|
{openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/async_http.py
RENAMED
|
@@ -606,7 +606,9 @@ class AsyncHttpTransport(Transport):
|
|
|
606
606
|
}
|
|
607
607
|
if self.config.compression == HttpCompression.GZIP:
|
|
608
608
|
headers["Content-Encoding"] = "gzip"
|
|
609
|
-
|
|
609
|
+
# levels higher than 3 are twice as slow:
|
|
610
|
+
# https://github.com/python/cpython/issues/91349#issuecomment-2737161048
|
|
611
|
+
return gzip.compress(event.encode("utf-8"), compresslevel=3), headers
|
|
610
612
|
|
|
611
613
|
return event, headers
|
|
612
614
|
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Copyright 2018-2025 contributors to the OpenLineage project
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
import attr
|
|
10
|
+
from openlineage.client.transport.async_http import AsyncHttpConfig, AsyncHttpTransport
|
|
11
|
+
from openlineage.client.transport.http import HttpConfig, HttpTransport
|
|
12
|
+
from openlineage.client.transport.transport import Config, Transport
|
|
13
|
+
from openlineage.client.utils import get_only_specified_fields
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from openlineage.client.client import Event
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# A mapping from https://docs.datadoghq.com/getting_started/site/ to
|
|
20
|
+
# Datadog endpoint that accepts OL events.
|
|
21
|
+
SITE_MAPPING = {
|
|
22
|
+
"datadoghq.com": "https://data-obs-intake.datadoghq.com",
|
|
23
|
+
"us3.datadoghq.com": "https://data-obs-intake.us3.datadoghq.com",
|
|
24
|
+
"us5.datadoghq.com": "https://data-obs-intake.us5.datadoghq.com",
|
|
25
|
+
"datadoghq.eu": "https://data-obs-intake.datadoghq.eu",
|
|
26
|
+
"ddog-gov.com": "https://data-obs-intake.ddog-gov.com",
|
|
27
|
+
"ap1.datadoghq.com": "https://data-obs-intake.ap1.datadoghq.com",
|
|
28
|
+
"ap2.datadoghq.com": "https://data-obs-intake.ap2.datadoghq.com",
|
|
29
|
+
"datad0g.com": "https://data-obs-intake.datad0g.com",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _is_valid_url(url: str) -> bool:
|
|
34
|
+
try:
|
|
35
|
+
parsed = urlparse(url)
|
|
36
|
+
return parsed.scheme in ("http", "https") and bool(parsed.netloc)
|
|
37
|
+
except Exception:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@attr.define
|
|
42
|
+
class DatadogConfig(Config):
|
|
43
|
+
apiKey: str = attr.field() # noqa: N815
|
|
44
|
+
site: str = attr.field(default="datadoghq.com")
|
|
45
|
+
timeout: float = attr.field(default=5.0)
|
|
46
|
+
retry: dict[str, Any] = attr.field(
|
|
47
|
+
default={
|
|
48
|
+
"total": 5,
|
|
49
|
+
"read": 5,
|
|
50
|
+
"connect": 5,
|
|
51
|
+
"backoff_factor": 0.3,
|
|
52
|
+
"status_forcelist": [500, 502, 503, 504],
|
|
53
|
+
"allowed_methods": ["HEAD", "POST"],
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
max_queue_size: int = attr.field(default=10000)
|
|
57
|
+
max_concurrent_requests: int = attr.field(default=100)
|
|
58
|
+
async_transport_rules: dict[str, dict[str, bool]] = attr.field(default={"dbt": {"*": True}})
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_dict(cls, params: dict[str, Any]) -> DatadogConfig:
|
|
62
|
+
api_key = params.get("apiKey") or params.get("apikey") or os.getenv("DD_API_KEY")
|
|
63
|
+
if not api_key:
|
|
64
|
+
msg = (
|
|
65
|
+
"apiKey is required for DatadogTransport. "
|
|
66
|
+
"Provide it in config or set DD_API_KEY environment variable."
|
|
67
|
+
)
|
|
68
|
+
raise ValueError(msg)
|
|
69
|
+
|
|
70
|
+
site = params.get("site") or os.getenv("DD_SITE", "datadoghq.com")
|
|
71
|
+
|
|
72
|
+
if site not in SITE_MAPPING and not _is_valid_url(site): # type: ignore
|
|
73
|
+
msg = f"Invalid site '{site}'. Must be one of: {list(SITE_MAPPING.keys())} or a valid URL"
|
|
74
|
+
raise ValueError(msg)
|
|
75
|
+
|
|
76
|
+
config_params = params.copy()
|
|
77
|
+
config_params["apiKey"] = api_key
|
|
78
|
+
config_params["site"] = site
|
|
79
|
+
|
|
80
|
+
specified_dict = get_only_specified_fields(cls, config_params)
|
|
81
|
+
return cls(**specified_dict)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class DatadogTransport(Transport):
|
|
85
|
+
kind = "datadog"
|
|
86
|
+
config_class = DatadogConfig
|
|
87
|
+
|
|
88
|
+
def __init__(self, config: DatadogConfig) -> None:
|
|
89
|
+
self.config = config
|
|
90
|
+
|
|
91
|
+
if _is_valid_url(config.site):
|
|
92
|
+
intake_url = config.site
|
|
93
|
+
else:
|
|
94
|
+
intake_url = SITE_MAPPING[config.site]
|
|
95
|
+
|
|
96
|
+
shared_config = {
|
|
97
|
+
"url": intake_url,
|
|
98
|
+
"timeout": config.timeout,
|
|
99
|
+
"retry": config.retry,
|
|
100
|
+
"compression": "gzip",
|
|
101
|
+
"auth": {"type": "api_key", "apiKey": config.apiKey},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
http_config = HttpConfig.from_dict(shared_config)
|
|
105
|
+
self.http = HttpTransport(http_config)
|
|
106
|
+
|
|
107
|
+
async_config = shared_config.copy()
|
|
108
|
+
async_config.update(
|
|
109
|
+
{
|
|
110
|
+
"max_queue_size": config.max_queue_size,
|
|
111
|
+
"max_concurrent_requests": config.max_concurrent_requests,
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
async_http_config = AsyncHttpConfig.from_dict(async_config)
|
|
115
|
+
self.async_http = AsyncHttpTransport(async_http_config)
|
|
116
|
+
|
|
117
|
+
def emit(self, event: Event) -> Any:
|
|
118
|
+
# Check if event has JobTypeJobFacet with integration = "dbt"
|
|
119
|
+
should_use_async = self._should_use_async_transport(event)
|
|
120
|
+
|
|
121
|
+
if should_use_async:
|
|
122
|
+
return self.async_http.emit(event)
|
|
123
|
+
else:
|
|
124
|
+
return self.http.emit(event)
|
|
125
|
+
|
|
126
|
+
def close(self, timeout: float = -1) -> bool:
|
|
127
|
+
http_result = self.http.close(timeout)
|
|
128
|
+
async_result = self.async_http.close(timeout)
|
|
129
|
+
return http_result and async_result
|
|
130
|
+
|
|
131
|
+
def _should_use_async_transport(self, event: Event) -> bool:
|
|
132
|
+
"""
|
|
133
|
+
Determine if event should use async transport based on async_transport_rules.
|
|
134
|
+
Returns True if the event matches the configured rules for async transport.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
# Check for double wildcard first - if present, use async for all events
|
|
138
|
+
wildcard_rules = self.config.async_transport_rules.get("*", {})
|
|
139
|
+
if wildcard_rules.get("*"):
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
if not hasattr(event, "job") or not hasattr(event.job, "facets"):
|
|
143
|
+
return False
|
|
144
|
+
|
|
145
|
+
job_facets = event.job.facets
|
|
146
|
+
if not job_facets:
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
job_type_facet = job_facets.get("jobType")
|
|
150
|
+
if not job_type_facet:
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
integration = getattr(job_type_facet, "integration", "").lower()
|
|
154
|
+
job_type = getattr(job_type_facet, "jobType", "").lower()
|
|
155
|
+
|
|
156
|
+
if not integration:
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
for rule_integration, job_type_rules in self.config.async_transport_rules.items():
|
|
160
|
+
if rule_integration == "*" or rule_integration.lower() == integration:
|
|
161
|
+
for rule_job_type, use_async in job_type_rules.items():
|
|
162
|
+
if rule_job_type == "*" or rule_job_type.lower() == job_type:
|
|
163
|
+
return use_async
|
|
164
|
+
return False
|
{openlineage_python-1.36.0 → openlineage_python-1.37.0}/openlineage/client/transport/http.py
RENAMED
|
@@ -226,6 +226,8 @@ class HttpTransport(Transport):
|
|
|
226
226
|
}
|
|
227
227
|
if self.compression == HttpCompression.GZIP:
|
|
228
228
|
headers["Content-Encoding"] = "gzip"
|
|
229
|
-
|
|
229
|
+
# levels higher than 3 are twice as slow:
|
|
230
|
+
# https://github.com/python/cpython/issues/91349#issuecomment-2737161048
|
|
231
|
+
return gzip.compress(event_str.encode("utf-8"), compresslevel=3), headers
|
|
230
232
|
|
|
231
233
|
return event_str, headers
|
|
@@ -8,6 +8,7 @@ import re
|
|
|
8
8
|
from typing import TYPE_CHECKING
|
|
9
9
|
from unittest.mock import MagicMock, patch
|
|
10
10
|
|
|
11
|
+
import attr
|
|
11
12
|
import pytest
|
|
12
13
|
from openlineage.client import event_v2
|
|
13
14
|
from openlineage.client.client import OpenLineageClient, OpenLineageClientOptions, OpenLineageConfig
|
|
@@ -417,6 +418,16 @@ def test_ol_config_from_dict():
|
|
|
417
418
|
OpenLineageConfig.from_dict({"facets": "invalid_data"})
|
|
418
419
|
|
|
419
420
|
|
|
421
|
+
def test_ol_config_from_the_same_dict():
|
|
422
|
+
config_dict = {
|
|
423
|
+
"transport": {"url": "http://localhost:5050"},
|
|
424
|
+
"facets": {"environment_variables": ["VAR1", "VAR2"]},
|
|
425
|
+
"filters": [{"type": "exact", "match": "job_name"}],
|
|
426
|
+
}
|
|
427
|
+
config = OpenLineageConfig.from_dict(config_dict)
|
|
428
|
+
assert OpenLineageConfig.from_dict(attr.asdict(config)) == config
|
|
429
|
+
|
|
430
|
+
|
|
420
431
|
@patch("yaml.safe_load", return_value=None)
|
|
421
432
|
def test_config_file_content_empty_file(mock_yaml) -> None: # noqa: ARG001
|
|
422
433
|
assert OpenLineageClient().config == OpenLineageConfig()
|