openlineage-python 1.22.0__tar.gz → 1.23.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.22.0 → openlineage_python-1.23.0}/PKG-INFO +1 -1
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/client.py +81 -3
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/constants.py +1 -1
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/column_lineage_dataset.py +13 -1
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/datasource_dataset.py +2 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/nominal_time_run.py +2 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/composite.py +7 -2
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/factory.py +6 -1
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/http.py +7 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/kafka.py +3 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/transport.py +1 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/pyproject.toml +1 -1
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_client.py +194 -1
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_composite.py +10 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_facet_v2.py +5 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_http.py +119 -1
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_kafka.py +16 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/.gitignore +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/README.md +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/docs/Makefile +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/docs/conf.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/docs/convert.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/docs/index.rst +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/docs/make.bat +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/docs/source/openlineage.client.rst +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/__init__.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/event_v2.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/facet.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/facet_v2.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/filter.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/base.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/data_quality_assertions_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/data_quality_metrics_input_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/dataset_version_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/documentation_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/documentation_job.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/error_message_run.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/external_query_run.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/extraction_error_run.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/job_type_job.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/lifecycle_state_change_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/output_statistics_output_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/ownership_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/ownership_job.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/parent_run.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/processing_engine_run.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/schema_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/source_code_job.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/source_code_location_job.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/sql_job.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/storage_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/symlinks_dataset.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/py.typed +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/run.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/serde.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/__init__.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/console.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/file.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/msk_iam.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/noop.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/utils.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/uuid.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/redact_fields.yml +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/__init__.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/config/config.yml +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/config/exact_filter.yml +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/config/http.yml +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/config/openlineage.yml +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/config/regex_filter.yml +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/conftest.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/example_full_event.json +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/nominal_time_without_end.json +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/serde_example_dataset_event.json +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/serde_example_job_event.json +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/serde_example_run_event.json +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_events.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_facet.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_factory.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_file.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_msk_iam.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_utils.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/test_uuid.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/transport.py +0 -0
- {openlineage_python-1.22.0 → openlineage_python-1.23.0}/tox.ini +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
import logging
|
|
6
7
|
import os
|
|
7
8
|
import warnings
|
|
@@ -16,6 +17,8 @@ if TYPE_CHECKING:
|
|
|
16
17
|
from requests import Session
|
|
17
18
|
from requests.adapters import HTTPAdapter
|
|
18
19
|
|
|
20
|
+
import contextlib
|
|
21
|
+
|
|
19
22
|
from openlineage.client import event_v2
|
|
20
23
|
from openlineage.client.run import DatasetEvent, JobEvent, RunEvent
|
|
21
24
|
from openlineage.client.transport import Transport, TransportFactory, get_default_factory
|
|
@@ -40,6 +43,9 @@ _T = TypeVar("_T", bound="OpenLineageClient")
|
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
class OpenLineageClient:
|
|
46
|
+
DYNAMIC_ENV_VARS_PREFIX = "OPENLINEAGE__"
|
|
47
|
+
DEFAULT_URL_TRANSPORT_NAME = "default_http"
|
|
48
|
+
|
|
43
49
|
def __init__( # noqa: PLR0913
|
|
44
50
|
self,
|
|
45
51
|
url: str | None = None,
|
|
@@ -64,6 +70,8 @@ class OpenLineageClient:
|
|
|
64
70
|
# reload yaml each time config is referred to.
|
|
65
71
|
self._config: dict[str, dict[str, str]] | None = None
|
|
66
72
|
|
|
73
|
+
self._alias_env_vars()
|
|
74
|
+
|
|
67
75
|
self.transport = self._resolve_transport(
|
|
68
76
|
url=url, options=options, session=session, transport=transport, factory=factory
|
|
69
77
|
)
|
|
@@ -131,7 +139,7 @@ class OpenLineageClient:
|
|
|
131
139
|
self._config = {}
|
|
132
140
|
return self._config
|
|
133
141
|
|
|
134
|
-
def _resolve_transport(self, **kwargs: Any) -> Transport:
|
|
142
|
+
def _resolve_transport(self, **kwargs: Any) -> Transport: # noqa: PLR0911
|
|
135
143
|
"""
|
|
136
144
|
Resolves the transport mechanism based on the provided arguments or environment settings.
|
|
137
145
|
|
|
@@ -166,11 +174,16 @@ class OpenLineageClient:
|
|
|
166
174
|
url=kwargs["url"], options=kwargs.get("options"), session=kwargs.get("session")
|
|
167
175
|
)
|
|
168
176
|
|
|
169
|
-
# 5. Check
|
|
177
|
+
# 5. Check transport initialization with env variables
|
|
178
|
+
if config := self._load_config_from_env_variables():
|
|
179
|
+
factory = kwargs.get("factory") or get_default_factory()
|
|
180
|
+
return factory.create(config["transport"])
|
|
181
|
+
|
|
182
|
+
# 6. Check HTTP transport initialization with env variables
|
|
170
183
|
if os.environ.get("OPENLINEAGE_URL"):
|
|
171
184
|
return self._http_transport_from_env_variables()
|
|
172
185
|
|
|
173
|
-
#
|
|
186
|
+
# 7. If all else fails, print events to console
|
|
174
187
|
from openlineage.client.transport.console import ConsoleConfig, ConsoleTransport
|
|
175
188
|
|
|
176
189
|
log.warning("Couldn't find any OpenLineage transport configuration; will print events to console.")
|
|
@@ -245,3 +258,68 @@ class OpenLineageClient:
|
|
|
245
258
|
session=session,
|
|
246
259
|
),
|
|
247
260
|
)
|
|
261
|
+
|
|
262
|
+
def _alias_env_vars(self) -> None:
|
|
263
|
+
default_transport_name = self.DEFAULT_URL_TRANSPORT_NAME.upper()
|
|
264
|
+
if url := os.environ.get("OPENLINEAGE_URL"):
|
|
265
|
+
if any(
|
|
266
|
+
k.startswith(f"OPENLINEAGE__TRANSPORT__TRANSPORTS__{default_transport_name}")
|
|
267
|
+
for k in os.environ
|
|
268
|
+
):
|
|
269
|
+
log.warning(
|
|
270
|
+
"%s already found in environment variables, skipping aliasing OPENLINEAGE_URL",
|
|
271
|
+
default_transport_name,
|
|
272
|
+
)
|
|
273
|
+
return
|
|
274
|
+
os.environ[f"OPENLINEAGE__TRANSPORT__TRANSPORTS__{default_transport_name}__TYPE"] = "http"
|
|
275
|
+
os.environ[f"OPENLINEAGE__TRANSPORT__TRANSPORTS__{default_transport_name}__URL"] = url
|
|
276
|
+
if api_key := os.environ.get("OPENLINEAGE_API_KEY"):
|
|
277
|
+
os.environ[
|
|
278
|
+
f"OPENLINEAGE__TRANSPORT__TRANSPORTS__{default_transport_name}__AUTH"
|
|
279
|
+
] = json.dumps(
|
|
280
|
+
{
|
|
281
|
+
"type": "api_key",
|
|
282
|
+
"apiKey": api_key,
|
|
283
|
+
}
|
|
284
|
+
)
|
|
285
|
+
if endpoint := os.environ.get("OPENLINEAGE_ENDPOINT"):
|
|
286
|
+
os.environ[
|
|
287
|
+
f"OPENLINEAGE__TRANSPORT__TRANSPORTS__{default_transport_name}__ENDPOINT"
|
|
288
|
+
] = endpoint
|
|
289
|
+
|
|
290
|
+
@classmethod
|
|
291
|
+
def _load_config_from_env_variables(cls) -> dict[str, Any] | None:
|
|
292
|
+
config: dict[str, Any] = {}
|
|
293
|
+
|
|
294
|
+
# get os.environ.items only starting with OPENLINEAGE_ prefix and reverse sort
|
|
295
|
+
# to make sure that top-level keys have precedence
|
|
296
|
+
env_vars = sorted(
|
|
297
|
+
filter(lambda k: k[0].startswith(cls.DYNAMIC_ENV_VARS_PREFIX), os.environ.items()), reverse=True
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
for env_key, env_value in env_vars:
|
|
301
|
+
keys = env_key[len(cls.DYNAMIC_ENV_VARS_PREFIX) :].split("__")
|
|
302
|
+
|
|
303
|
+
# Parse value (try to parse as JSON, otherwise lowercase the value)
|
|
304
|
+
with contextlib.suppress(json.JSONDecodeError):
|
|
305
|
+
env_value = json.loads(env_value) # noqa: PLW2901
|
|
306
|
+
|
|
307
|
+
cls._insert_into_config(config, keys, env_value)
|
|
308
|
+
|
|
309
|
+
if (transport_config := config.get("transport")) is None or transport_config.get("type") is None:
|
|
310
|
+
return None
|
|
311
|
+
|
|
312
|
+
return config
|
|
313
|
+
|
|
314
|
+
@staticmethod
|
|
315
|
+
def _insert_into_config(config: dict[str, Any], key_path: list[str], value: str) -> None:
|
|
316
|
+
keys = [key.lower() for key in key_path]
|
|
317
|
+
|
|
318
|
+
current = config
|
|
319
|
+
for key in keys[:-1]:
|
|
320
|
+
if key not in current:
|
|
321
|
+
current[key] = {}
|
|
322
|
+
current = current[key]
|
|
323
|
+
|
|
324
|
+
# Overwrite if key already exists
|
|
325
|
+
current[keys[-1]] = value
|
|
@@ -15,9 +15,15 @@ class ColumnLineageDatasetFacet(DatasetFacet):
|
|
|
15
15
|
fields: dict[str, Fields]
|
|
16
16
|
"""Column level lineage that maps output fields into input fields used to evaluate them."""
|
|
17
17
|
|
|
18
|
+
dataset: list[InputField] | None = attr.field(factory=list)
|
|
19
|
+
"""
|
|
20
|
+
Column level lineage that affects the whole dataset. This includes filtering, sorting, grouping
|
|
21
|
+
(aggregates), joining, window functions, etc.
|
|
22
|
+
"""
|
|
23
|
+
|
|
18
24
|
@staticmethod
|
|
19
25
|
def _get_schema() -> str:
|
|
20
|
-
return "https://openlineage.io/spec/facets/1-
|
|
26
|
+
return "https://openlineage.io/spec/facets/1-2-0/ColumnLineageDatasetFacet.json#/$defs/ColumnLineageDatasetFacet"
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
@attr.define
|
|
@@ -35,6 +41,8 @@ class Fields(RedactMixin):
|
|
|
35
41
|
|
|
36
42
|
@attr.define
|
|
37
43
|
class InputField(RedactMixin):
|
|
44
|
+
"""Represents a single dependency on some field (column)."""
|
|
45
|
+
|
|
38
46
|
namespace: str
|
|
39
47
|
"""The input dataset namespace"""
|
|
40
48
|
|
|
@@ -47,6 +55,10 @@ class InputField(RedactMixin):
|
|
|
47
55
|
transformations: list[Transformation] | None = attr.field(factory=list)
|
|
48
56
|
_skip_redact: ClassVar[list[str]] = ["namespace", "name", "field"]
|
|
49
57
|
|
|
58
|
+
@staticmethod
|
|
59
|
+
def _get_schema() -> str:
|
|
60
|
+
return "https://openlineage.io/spec/facets/1-2-0/ColumnLineageDatasetFacet.json#/$defs/InputField"
|
|
61
|
+
|
|
50
62
|
|
|
51
63
|
@attr.define
|
|
52
64
|
class Transformation(RedactMixin):
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/composite.py
RENAMED
|
@@ -35,7 +35,7 @@ class CompositeConfig(Config):
|
|
|
35
35
|
transport will halt the emission process for subsequent transports.
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
|
-
transports: list[dict[str, Any]] = attr.ib()
|
|
38
|
+
transports: list[dict[str, Any]] | dict[str, dict[str, Any]] = attr.ib()
|
|
39
39
|
continue_on_failure: bool = attr.ib(default=True)
|
|
40
40
|
|
|
41
41
|
@classmethod
|
|
@@ -63,7 +63,12 @@ class CompositeTransport(Transport):
|
|
|
63
63
|
from openlineage.client.transport import get_default_factory
|
|
64
64
|
|
|
65
65
|
transports = []
|
|
66
|
-
|
|
66
|
+
config_transports = self.config.transports
|
|
67
|
+
if isinstance(config_transports, dict):
|
|
68
|
+
config_transports = [
|
|
69
|
+
{**config, "name": name} for name, config in config_transports.items() if config
|
|
70
|
+
]
|
|
71
|
+
for transport_config in config_transports:
|
|
67
72
|
transports.append(get_default_factory().create(transport_config))
|
|
68
73
|
return transports
|
|
69
74
|
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/factory.py
RENAMED
|
@@ -50,6 +50,8 @@ class DefaultTransportFactory(TransportFactory):
|
|
|
50
50
|
msg = "You need to pass transport type in config."
|
|
51
51
|
raise TypeError(msg) from None
|
|
52
52
|
|
|
53
|
+
transport_name = config.pop("name", None)
|
|
54
|
+
|
|
53
55
|
transport_class_type_or_str = self.transports.get(transport_type, transport_type)
|
|
54
56
|
|
|
55
57
|
if isinstance(transport_class_type_or_str, str):
|
|
@@ -68,4 +70,7 @@ class DefaultTransportFactory(TransportFactory):
|
|
|
68
70
|
msg = f"Config {config_class} has to be class, and subclass of Config"
|
|
69
71
|
raise TypeError(msg)
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
transport: Transport = transport_class(config_class.from_dict(config)) # type: ignore[call-arg]
|
|
74
|
+
if transport_name and not transport.name:
|
|
75
|
+
transport.name = transport_name
|
|
76
|
+
return transport
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/http.py
RENAMED
|
@@ -87,6 +87,8 @@ class HttpConfig(Config):
|
|
|
87
87
|
session: Session | None = attr.ib(default=None)
|
|
88
88
|
# not set by TransportFactory
|
|
89
89
|
adapter: HTTPAdapter | None = attr.ib(default=None)
|
|
90
|
+
# custom headers support
|
|
91
|
+
custom_headers: dict[str, str] = attr.ib(factory=dict)
|
|
90
92
|
|
|
91
93
|
@classmethod
|
|
92
94
|
def from_dict(cls, params: dict[str, Any]) -> HttpConfig:
|
|
@@ -150,6 +152,7 @@ class HttpTransport(Transport):
|
|
|
150
152
|
self.session.headers["Content-Type"] = "application/json"
|
|
151
153
|
auth_headers = self._auth_headers(config.auth)
|
|
152
154
|
self.session.headers.update(auth_headers)
|
|
155
|
+
self.session.headers.update(config.custom_headers)
|
|
153
156
|
self.timeout = config.timeout
|
|
154
157
|
self.verify = config.verify
|
|
155
158
|
self.compression = config.compression
|
|
@@ -168,6 +171,9 @@ class HttpTransport(Transport):
|
|
|
168
171
|
http_client.HTTPConnection.debuglevel = 0
|
|
169
172
|
body, headers = self._prepare_request(Serde.to_json(event))
|
|
170
173
|
|
|
174
|
+
# Update headers with custom headers from the config
|
|
175
|
+
headers.update(self.config.custom_headers)
|
|
176
|
+
|
|
171
177
|
if self.session:
|
|
172
178
|
resp = self.session.post(
|
|
173
179
|
url=urljoin(self.url, self.endpoint),
|
|
@@ -179,6 +185,7 @@ class HttpTransport(Transport):
|
|
|
179
185
|
else:
|
|
180
186
|
headers["Content-Type"] = "application/json"
|
|
181
187
|
headers.update(self._auth_headers(self.config.auth))
|
|
188
|
+
headers.update(self.config.custom_headers)
|
|
182
189
|
with Session() as session:
|
|
183
190
|
resp = session.post(
|
|
184
191
|
url=urljoin(self.url, self.endpoint),
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/kafka.py
RENAMED
|
@@ -41,6 +41,9 @@ class KafkaConfig(Config):
|
|
|
41
41
|
|
|
42
42
|
@classmethod
|
|
43
43
|
def from_dict(cls: type[_T], params: dict[str, Any]) -> _T:
|
|
44
|
+
# alias message_key to messageKey
|
|
45
|
+
if message_key := params.pop("message_key", None):
|
|
46
|
+
params["messageKey"] = params.get("messageKey") or message_key
|
|
44
47
|
if "config" not in params:
|
|
45
48
|
msg = "kafka `config` not passed to KafkaConfig"
|
|
46
49
|
raise RuntimeError(msg)
|
|
@@ -20,13 +20,16 @@ from openlineage.client.run import (
|
|
|
20
20
|
RunEvent,
|
|
21
21
|
RunState,
|
|
22
22
|
)
|
|
23
|
-
from openlineage.client.transport.
|
|
23
|
+
from openlineage.client.transport.composite import CompositeTransport
|
|
24
|
+
from openlineage.client.transport.console import ConsoleTransport
|
|
25
|
+
from openlineage.client.transport.http import ApiKeyTokenProvider, HttpTransport, TokenProvider
|
|
24
26
|
from openlineage.client.transport.noop import NoopTransport
|
|
25
27
|
from openlineage.client.uuid import generate_new_uuid
|
|
26
28
|
|
|
27
29
|
if TYPE_CHECKING:
|
|
28
30
|
from pathlib import Path
|
|
29
31
|
|
|
32
|
+
from openlineage.client.transport.kafka import KafkaTransport
|
|
30
33
|
from pytest_mock import MockerFixture
|
|
31
34
|
|
|
32
35
|
|
|
@@ -389,3 +392,193 @@ def test_http_transport_from_url_no_options() -> None:
|
|
|
389
392
|
assert transport.timeout == timeout
|
|
390
393
|
assert transport.verify is True
|
|
391
394
|
assert transport.config.auth.api_key == "xxx"
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
@patch.dict(
|
|
398
|
+
os.environ, {"OPENLINEAGE_URL": "http://example.com", "OPENLINEAGE__TRANSPORT__TYPE": "composite"}
|
|
399
|
+
)
|
|
400
|
+
def test_composite_transport_with_aliased_url() -> None:
|
|
401
|
+
transport: CompositeTransport = OpenLineageClient().transport
|
|
402
|
+
assert transport.kind == CompositeTransport.kind
|
|
403
|
+
assert len(transport.transports) == 1
|
|
404
|
+
assert transport.transports[0].kind == HttpTransport.kind
|
|
405
|
+
assert isinstance(transport.transports[0].config.auth, TokenProvider)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
@patch.dict(
|
|
409
|
+
os.environ,
|
|
410
|
+
{
|
|
411
|
+
"OPENLINEAGE_URL": "http://example.com",
|
|
412
|
+
"OPENLINEAGE_API_KEY": "random_key",
|
|
413
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "composite",
|
|
414
|
+
},
|
|
415
|
+
)
|
|
416
|
+
def test_composite_transport_with_aliased_url_and_api_key() -> None:
|
|
417
|
+
transport: CompositeTransport = OpenLineageClient().transport
|
|
418
|
+
assert transport.kind == CompositeTransport.kind
|
|
419
|
+
assert len(transport.transports) == 1
|
|
420
|
+
assert transport.transports[0].kind == HttpTransport.kind
|
|
421
|
+
assert isinstance(transport.transports[0].config.auth, ApiKeyTokenProvider)
|
|
422
|
+
assert transport.transports[0].config.auth.api_key == "random_key"
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@patch.dict(
|
|
426
|
+
os.environ,
|
|
427
|
+
{
|
|
428
|
+
"OPENLINEAGE_URL": "http://example.com",
|
|
429
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "composite",
|
|
430
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__ANOTHER__TYPE": "console",
|
|
431
|
+
},
|
|
432
|
+
)
|
|
433
|
+
def test_composite_transport_with_aliased_url_and_second_transport() -> None:
|
|
434
|
+
transport: CompositeTransport = OpenLineageClient().transport
|
|
435
|
+
assert transport.kind == CompositeTransport.kind
|
|
436
|
+
assert len(transport.transports) == 2 # noqa: PLR2004
|
|
437
|
+
assert transport.transports[0].kind == HttpTransport.kind
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
@patch.dict(
|
|
441
|
+
os.environ,
|
|
442
|
+
{
|
|
443
|
+
"OPENLINEAGE_URL": "http://example.com",
|
|
444
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "composite",
|
|
445
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__DEFAULT_HTTP": "{}",
|
|
446
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__ANOTHER__TYPE": "console",
|
|
447
|
+
},
|
|
448
|
+
)
|
|
449
|
+
def test_composite_transport_with_aliased_url_and_overriden_alias() -> None:
|
|
450
|
+
transport: CompositeTransport = OpenLineageClient().transport
|
|
451
|
+
assert transport.kind == CompositeTransport.kind
|
|
452
|
+
assert len(transport.transports) == 1
|
|
453
|
+
assert transport.transports[0].kind == ConsoleTransport.kind
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
@patch.dict(
|
|
457
|
+
os.environ,
|
|
458
|
+
{
|
|
459
|
+
"OPENLINEAGE_URL": "http://example.com",
|
|
460
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "composite",
|
|
461
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__DEFAULT_HTTP__TYPE": "console",
|
|
462
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__ANOTHER__TYPE": "console",
|
|
463
|
+
},
|
|
464
|
+
)
|
|
465
|
+
def test_openlineage_url_does_not_alias_when_transport_exists() -> None:
|
|
466
|
+
transport: CompositeTransport = OpenLineageClient().transport
|
|
467
|
+
assert transport.kind == CompositeTransport.kind
|
|
468
|
+
assert len(transport.transports) == 2 # noqa: PLR2004
|
|
469
|
+
assert transport.transports[0].kind == transport.transports[1].kind == ConsoleTransport.kind
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
@patch.dict(
|
|
473
|
+
os.environ,
|
|
474
|
+
{
|
|
475
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "kafka",
|
|
476
|
+
"OPENLINEAGE__TRANSPORT__TOPIC": "my_topic",
|
|
477
|
+
"OPENLINEAGE__TRANSPORT__CONFIG": '{"bootstrap.servers": "localhost:9092,another.host:9092", "acks": "all", "retries": 3}', # noqa: E501
|
|
478
|
+
"OPENLINEAGE__TRANSPORT__FLUSH": "true",
|
|
479
|
+
"OPENLINEAGE__TRANSPORT__MESSAGE_KEY": "some-value",
|
|
480
|
+
},
|
|
481
|
+
)
|
|
482
|
+
def test_kafka_transport_configured_with_aliased_message_key() -> None:
|
|
483
|
+
transport: KafkaTransport = OpenLineageClient().transport
|
|
484
|
+
assert transport.message_key == "some-value"
|
|
485
|
+
assert transport.flush is True
|
|
486
|
+
assert transport.kafka_config.config == {
|
|
487
|
+
"bootstrap.servers": "localhost:9092,another.host:9092",
|
|
488
|
+
"acks": "all",
|
|
489
|
+
"retries": 3,
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
class TestOpenLineageConfigLoader:
|
|
494
|
+
@pytest.mark.parametrize(
|
|
495
|
+
("env_vars", "expected_config"),
|
|
496
|
+
[
|
|
497
|
+
(
|
|
498
|
+
{
|
|
499
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "http",
|
|
500
|
+
"OPENLINEAGE__TRANSPORT__URL": "http://localhost:5050",
|
|
501
|
+
"OPENLINEAGE__TRANSPORT__AUTH__API_KEY": "random_token",
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
"transport": {
|
|
505
|
+
"type": "http",
|
|
506
|
+
"url": "http://localhost:5050",
|
|
507
|
+
"auth": {"api_key": "random_token"},
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
),
|
|
511
|
+
(
|
|
512
|
+
{
|
|
513
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "composite",
|
|
514
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__FIRST__TYPE": "http",
|
|
515
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__FIRST__URL": "http://localhost:5050",
|
|
516
|
+
"OPENLINEAGE__TRANSPORT__TRANSPORTS__SECOND__TYPE": "console",
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"transport": {
|
|
520
|
+
"type": "composite",
|
|
521
|
+
"transports": {
|
|
522
|
+
"first": {"type": "http", "url": "http://localhost:5050"},
|
|
523
|
+
"second": {"type": "console"},
|
|
524
|
+
},
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
),
|
|
528
|
+
(
|
|
529
|
+
{"OPENLINEAGE__TRANSPORT": '{"type": "console"}', "OPENLINEAGE__TRANSPORT__TYPE": "http"},
|
|
530
|
+
{"transport": {"type": "console"}},
|
|
531
|
+
),
|
|
532
|
+
(
|
|
533
|
+
{
|
|
534
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "kafka",
|
|
535
|
+
"OPENLINEAGE__TRANSPORT__PROPERTIES": '{"key.serializer": "org.apache.kafka.common.serialization.StringSerializer"}', # noqa: E501
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
"transport": {
|
|
539
|
+
"type": "kafka",
|
|
540
|
+
"properties": {
|
|
541
|
+
"key.serializer": "org.apache.kafka.common.serialization.StringSerializer"
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
),
|
|
546
|
+
(
|
|
547
|
+
{
|
|
548
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "test",
|
|
549
|
+
"OPENLINEAGE__TRANSPORT__MY_NAME__LIST": '["first", "SeCond"]',
|
|
550
|
+
},
|
|
551
|
+
{"transport": {"my_name": {"list": ["first", "SeCond"]}, "type": "test"}},
|
|
552
|
+
),
|
|
553
|
+
(
|
|
554
|
+
{
|
|
555
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "http",
|
|
556
|
+
"OPENLINEAGE__TRANSPORT__URL": "http://localhost:5050",
|
|
557
|
+
"OPENLINEAGE__TRANSPORT__AUTH__API_KEY": "random_token",
|
|
558
|
+
"OPENLINEAGE__TRANSPORT__AUTH__COMPRESSION": "gzip",
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
"transport": {
|
|
562
|
+
"type": "http",
|
|
563
|
+
"url": "http://localhost:5050",
|
|
564
|
+
"auth": {"api_key": "random_token", "compression": "gzip"},
|
|
565
|
+
}
|
|
566
|
+
},
|
|
567
|
+
),
|
|
568
|
+
(
|
|
569
|
+
{
|
|
570
|
+
"OPENLINEAGE__TRANSPORT": '{"type": "console"}',
|
|
571
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "http",
|
|
572
|
+
"OPENLINEAGE__TRANSPORT__URL": "http://localhost:5050",
|
|
573
|
+
"OPENLINEAGE__TRANSPORT__AUTH__API_KEY": "random_token",
|
|
574
|
+
"OPENLINEAGE__TRANSPORT__AUTH__COMPRESSION": "gzip",
|
|
575
|
+
},
|
|
576
|
+
{"transport": {"type": "console"}},
|
|
577
|
+
),
|
|
578
|
+
],
|
|
579
|
+
)
|
|
580
|
+
@patch.dict(os.environ, {})
|
|
581
|
+
def test_config_loader(self, env_vars, expected_config):
|
|
582
|
+
with patch.dict(os.environ, env_vars):
|
|
583
|
+
config = OpenLineageClient._load_config_from_env_variables() # noqa: SLF001
|
|
584
|
+
assert config == expected_config
|
|
@@ -67,6 +67,16 @@ def test_composite_transport_create_transports():
|
|
|
67
67
|
assert transport.transports[0].kind == "fake"
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
def test_composite_transport_from_dict_config():
|
|
71
|
+
config = CompositeConfig(
|
|
72
|
+
transports={"fake_trans": {"type": "tests.transport.FakeTransport"}}, continue_on_failure=True
|
|
73
|
+
)
|
|
74
|
+
transport = CompositeTransport(config)
|
|
75
|
+
assert len(transport.transports) == 1
|
|
76
|
+
assert transport.transports[0].kind == "fake"
|
|
77
|
+
assert transport.transports[0].name == "fake_trans"
|
|
78
|
+
|
|
79
|
+
|
|
70
80
|
@mock.patch("openlineage.client.transport.get_default_factory")
|
|
71
81
|
def test_transports(mock_factory):
|
|
72
82
|
mock_transport1 = MagicMock(spec=Transport)
|
|
@@ -43,6 +43,11 @@ def test_set_producer_from_argument():
|
|
|
43
43
|
assert facet._producer == "http://another.producer" # noqa: SLF001
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
def test_optional_attributed_not_validated():
|
|
47
|
+
"""Don't pass optional value with validator."""
|
|
48
|
+
nominal_time_run.NominalTimeRunFacet(nominalStartTime="2020-12-17T03:00:00.001Z")
|
|
49
|
+
|
|
50
|
+
|
|
46
51
|
def test_custom_facet() -> None:
|
|
47
52
|
session = mock.MagicMock()
|
|
48
53
|
client = OpenLineageClient(url="http://example.com", session=session)
|
|
@@ -4,12 +4,14 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import datetime
|
|
6
6
|
import gzip
|
|
7
|
+
import os
|
|
7
8
|
from typing import TYPE_CHECKING
|
|
9
|
+
from unittest.mock import MagicMock, patch
|
|
8
10
|
|
|
9
11
|
from openlineage.client import OpenLineageClient
|
|
10
12
|
from openlineage.client.run import Job, Run, RunEvent, RunState
|
|
11
13
|
from openlineage.client.serde import Serde
|
|
12
|
-
from openlineage.client.transport.http import HttpCompression, HttpConfig, HttpTransport
|
|
14
|
+
from openlineage.client.transport.http import ApiKeyTokenProvider, HttpCompression, HttpConfig, HttpTransport
|
|
13
15
|
from openlineage.client.uuid import generate_new_uuid
|
|
14
16
|
from requests import Session
|
|
15
17
|
|
|
@@ -163,3 +165,119 @@ def test_http_config_configs_session() -> None:
|
|
|
163
165
|
assert not hasattr(config.auth, "api_key")
|
|
164
166
|
assert config.session is s
|
|
165
167
|
assert config.adapter is None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@patch("requests.Session.post")
|
|
171
|
+
def test_http_transport_custom_headers_applied(mock_post):
|
|
172
|
+
custom_headers = {"X-Custom-Header": "CustomValue", "X-Another-Header": "AnotherValue"}
|
|
173
|
+
|
|
174
|
+
config = HttpConfig(url="http://example.com", custom_headers=custom_headers)
|
|
175
|
+
|
|
176
|
+
transport = HttpTransport(config)
|
|
177
|
+
mock_event = MagicMock()
|
|
178
|
+
|
|
179
|
+
with patch("openlineage.client.serde.Serde.to_json", return_value='{"mock": "event"}'):
|
|
180
|
+
transport.emit(mock_event)
|
|
181
|
+
|
|
182
|
+
mock_post.assert_called_once()
|
|
183
|
+
_, kwargs = mock_post.call_args
|
|
184
|
+
headers = kwargs["headers"]
|
|
185
|
+
|
|
186
|
+
assert headers["X-Custom-Header"] == "CustomValue"
|
|
187
|
+
assert headers["X-Another-Header"] == "AnotherValue"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@patch("requests.Session.post")
|
|
191
|
+
def test_http_transport_auth_and_custom_headers_applied(mock_post):
|
|
192
|
+
custom_headers = {"X-Custom-Header": "CustomValue"}
|
|
193
|
+
auth_token = "Bearer test_token" # noqa: S105
|
|
194
|
+
|
|
195
|
+
# Set up config with an ApiKeyTokenProvider
|
|
196
|
+
config = HttpConfig(
|
|
197
|
+
url="http://example.com",
|
|
198
|
+
auth=ApiKeyTokenProvider({"api_key": "test_token"}),
|
|
199
|
+
custom_headers=custom_headers,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
transport = HttpTransport(config)
|
|
203
|
+
mock_event = MagicMock()
|
|
204
|
+
|
|
205
|
+
with patch("openlineage.client.serde.Serde.to_json", return_value='{"mock": "event"}'):
|
|
206
|
+
transport.emit(mock_event)
|
|
207
|
+
|
|
208
|
+
mock_post.assert_called_once()
|
|
209
|
+
_, kwargs = mock_post.call_args
|
|
210
|
+
headers = kwargs["headers"]
|
|
211
|
+
|
|
212
|
+
assert headers["Authorization"] == auth_token
|
|
213
|
+
assert headers["X-Custom-Header"] == "CustomValue"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@patch("requests.Session.post")
|
|
217
|
+
def test_http_transport_no_custom_headers(mock_post):
|
|
218
|
+
config = HttpConfig(url="http://example.com")
|
|
219
|
+
transport = HttpTransport(config)
|
|
220
|
+
mock_event = MagicMock()
|
|
221
|
+
|
|
222
|
+
with patch("openlineage.client.serde.Serde.to_json", return_value='{"mock": "event"}'):
|
|
223
|
+
transport.emit(mock_event)
|
|
224
|
+
|
|
225
|
+
mock_post.assert_called_once()
|
|
226
|
+
_, kwargs = mock_post.call_args
|
|
227
|
+
headers = kwargs["headers"]
|
|
228
|
+
|
|
229
|
+
# Only the Content-Type header should be set if no custom headers
|
|
230
|
+
assert headers["Content-Type"] == "application/json"
|
|
231
|
+
assert "X-Custom-Header" not in headers
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@patch("requests.Session.post")
|
|
235
|
+
def test_http_transport_compression_with_custom_headers(mock_post):
|
|
236
|
+
custom_headers = {"X-Custom-Header": "CustomValue"}
|
|
237
|
+
|
|
238
|
+
config = HttpConfig(
|
|
239
|
+
url="http://example.com", compression=HttpCompression.GZIP, custom_headers=custom_headers
|
|
240
|
+
)
|
|
241
|
+
transport = HttpTransport(config)
|
|
242
|
+
mock_event = MagicMock()
|
|
243
|
+
|
|
244
|
+
with patch("openlineage.client.serde.Serde.to_json", return_value='{"mock": "event"}'), patch(
|
|
245
|
+
"gzip.compress", return_value=b"compressed_data"
|
|
246
|
+
):
|
|
247
|
+
transport.emit(mock_event)
|
|
248
|
+
|
|
249
|
+
mock_post.assert_called_once()
|
|
250
|
+
_, kwargs = mock_post.call_args
|
|
251
|
+
headers = kwargs["headers"]
|
|
252
|
+
data = kwargs["data"]
|
|
253
|
+
|
|
254
|
+
assert headers["X-Custom-Header"] == "CustomValue"
|
|
255
|
+
assert headers["Content-Encoding"] == "gzip"
|
|
256
|
+
assert data == b"compressed_data"
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@patch("requests.Session.post")
|
|
260
|
+
@patch.dict(
|
|
261
|
+
os.environ,
|
|
262
|
+
{
|
|
263
|
+
"OPENLINEAGE__TRANSPORT__TYPE": "http",
|
|
264
|
+
"OPENLINEAGE__TRANSPORT__URL": "http://example.com",
|
|
265
|
+
"OPENLINEAGE__TRANSPORT__CUSTOM_HEADERS__CUSTOM_HEADER": "FIRST",
|
|
266
|
+
"OPENLINEAGE__TRANSPORT__CUSTOM_HEADERS__ANOTHER_HEADER": "second",
|
|
267
|
+
},
|
|
268
|
+
)
|
|
269
|
+
def test_http_transport_with_custom_headers_from_env_vars(mock_post):
|
|
270
|
+
transport = OpenLineageClient().transport
|
|
271
|
+
mock_event = MagicMock()
|
|
272
|
+
|
|
273
|
+
with patch("openlineage.client.serde.Serde.to_json", return_value='{"mock": "event"}'), patch(
|
|
274
|
+
"gzip.compress", return_value=b"compressed_data"
|
|
275
|
+
):
|
|
276
|
+
transport.emit(mock_event)
|
|
277
|
+
|
|
278
|
+
mock_post.assert_called_once()
|
|
279
|
+
_, kwargs = mock_post.call_args
|
|
280
|
+
headers = kwargs["headers"]
|
|
281
|
+
|
|
282
|
+
assert headers["custom_header"] == "FIRST"
|
|
283
|
+
assert headers["another_header"] == "second"
|
|
@@ -106,6 +106,22 @@ def test_kafka_loads_partial_config_with_defaults() -> None:
|
|
|
106
106
|
assert config.flush is True
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
def test_kafka_config_accepts_aliased_message_key() -> None:
|
|
110
|
+
config = KafkaConfig.from_dict(
|
|
111
|
+
{
|
|
112
|
+
"type": "kafka",
|
|
113
|
+
"topic": "my_topic",
|
|
114
|
+
"message_key": "some-value",
|
|
115
|
+
"flush": True,
|
|
116
|
+
"config": {"bootstrap.servers": "localhost:9092,another.host:9092", "acks": "all", "retries": 3},
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
assert config.messageKey == "some-value"
|
|
121
|
+
assert config.topic == "my_topic"
|
|
122
|
+
assert config.flush is True
|
|
123
|
+
|
|
124
|
+
|
|
109
125
|
def test_kafka_load_config_fails_on_no_config() -> None:
|
|
110
126
|
with pytest.raises(TypeError):
|
|
111
127
|
KafkaConfig.from_dict(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/job_type_job.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/parent_run.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/generated/sql_job.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/__init__.py
RENAMED
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/console.py
RENAMED
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/file.py
RENAMED
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/msk_iam.py
RENAMED
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/openlineage/client/transport/noop.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openlineage_python-1.22.0 → openlineage_python-1.23.0}/tests/serde_example_dataset_event.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|