apache-airflow-providers-openlineage 1.3.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of apache-airflow-providers-openlineage might be problematic. Click here for more details.

@@ -0,0 +1,432 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The ASF licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ from __future__ import annotations
19
+
20
+ import datetime
21
+ import json
22
+ import logging
23
+ import os
24
+ from contextlib import suppress
25
+ from functools import wraps
26
+ from typing import TYPE_CHECKING, Any, Iterable
27
+ from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
28
+
29
+ import attrs
30
+ from attrs import asdict
31
+
32
+ # TODO: move this maybe to Airflow's logic?
33
+ from openlineage.client.utils import RedactMixin
34
+
35
+ from airflow.compat.functools import cache
36
+ from airflow.configuration import conf
37
+ from airflow.providers.openlineage.plugins.facets import (
38
+ AirflowMappedTaskRunFacet,
39
+ AirflowRunFacet,
40
+ )
41
+ from airflow.utils.context import AirflowContextDeprecationWarning
42
+ from airflow.utils.log.secrets_masker import Redactable, Redacted, SecretsMasker, should_hide_value_for_key
43
+
44
+ if TYPE_CHECKING:
45
+ from airflow.models import DAG, BaseOperator, Connection, DagRun, TaskInstance
46
+
47
+
48
+ log = logging.getLogger(__name__)
49
+ _NOMINAL_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
50
+
51
+
52
+ def openlineage_job_name(dag_id: str, task_id: str) -> str:
53
+ return f"{dag_id}.{task_id}"
54
+
55
+
56
+ def get_operator_class(task: BaseOperator) -> type:
57
+ if task.__class__.__name__ in ("DecoratedMappedOperator", "MappedOperator"):
58
+ return task.operator_class
59
+ return task.__class__
60
+
61
+
62
+ def to_json_encodable(task: BaseOperator) -> dict[str, object]:
63
+ def _task_encoder(obj):
64
+ from airflow.models import DAG
65
+
66
+ if isinstance(obj, datetime.datetime):
67
+ return obj.isoformat()
68
+ elif isinstance(obj, DAG):
69
+ return {
70
+ "dag_id": obj.dag_id,
71
+ "tags": obj.tags,
72
+ "schedule_interval": obj.schedule_interval,
73
+ "timetable": obj.timetable.serialize(),
74
+ }
75
+ else:
76
+ return str(obj)
77
+
78
+ return json.loads(json.dumps(task.__dict__, default=_task_encoder))
79
+
80
+
81
+ def url_to_https(url) -> str | None:
82
+ # Ensure URL exists
83
+ if not url:
84
+ return None
85
+
86
+ base_url = None
87
+ if url.startswith("git@"):
88
+ part = url.split("git@")[1:2]
89
+ if part:
90
+ base_url = f'https://{part[0].replace(":", "/", 1)}'
91
+ elif url.startswith("https://"):
92
+ base_url = url
93
+
94
+ if not base_url:
95
+ raise ValueError(f"Unable to extract location from: {url}")
96
+
97
+ if base_url.endswith(".git"):
98
+ base_url = base_url[:-4]
99
+ return base_url
100
+
101
+
102
+ def redacted_connection_uri(conn: Connection, filtered_params=None, filtered_prefixes=None):
103
+ """
104
+ Return the connection URI for the given Connection.
105
+
106
+ This method additionally filters URI by removing query parameters that are known to carry sensitive data
107
+ like username, password, access key.
108
+ """
109
+ if filtered_prefixes is None:
110
+ filtered_prefixes = []
111
+ if filtered_params is None:
112
+ filtered_params = []
113
+
114
+ def filter_key_params(k: str):
115
+ return k not in filtered_params and any(substr in k for substr in filtered_prefixes)
116
+
117
+ conn_uri = conn.get_uri()
118
+ parsed = urlparse(conn_uri)
119
+
120
+ # Remove username and password
121
+ netloc = f"{parsed.hostname}" + (f":{parsed.port}" if parsed.port else "")
122
+ parsed = parsed._replace(netloc=netloc)
123
+ if parsed.query:
124
+ query_dict = dict(parse_qsl(parsed.query))
125
+ if conn.EXTRA_KEY in query_dict:
126
+ query_dict = json.loads(query_dict[conn.EXTRA_KEY])
127
+ filtered_qs = {k: v for k, v in query_dict.items() if not filter_key_params(k)}
128
+ parsed = parsed._replace(query=urlencode(filtered_qs))
129
+ return urlunparse(parsed)
130
+
131
+
132
+ def get_connection(conn_id) -> Connection | None:
133
+ from airflow.hooks.base import BaseHook
134
+
135
+ with suppress(Exception):
136
+ return BaseHook.get_connection(conn_id=conn_id)
137
+ return None
138
+
139
+
140
+ def get_job_name(task):
141
+ return f"{task.dag_id}.{task.task_id}"
142
+
143
+
144
+ def get_custom_facets(task_instance: TaskInstance | None = None) -> dict[str, Any]:
145
+ custom_facets = {}
146
+ # check for -1 comes from SmartSensor compatibility with dynamic task mapping
147
+ # this comes from Airflow code
148
+ if hasattr(task_instance, "map_index") and getattr(task_instance, "map_index") != -1:
149
+ custom_facets["airflow_mappedTask"] = AirflowMappedTaskRunFacet.from_task_instance(task_instance)
150
+ return custom_facets
151
+
152
+
153
+ class InfoJsonEncodable(dict):
154
+ """
155
+ Airflow objects might not be json-encodable overall.
156
+
157
+ The class provides additional attributes to control
158
+ what and how is encoded:
159
+
160
+ * renames: a dictionary of attribute name changes
161
+ * | casts: a dictionary consisting of attribute names
162
+ | and corresponding methods that should change
163
+ | object value
164
+ * includes: list of attributes to be included in encoding
165
+ * excludes: list of attributes to be excluded from encoding
166
+
167
+ Don't use both includes and excludes.
168
+ """
169
+
170
+ renames: dict[str, str] = {}
171
+ casts: dict[str, Any] = {}
172
+ includes: list[str] = []
173
+ excludes: list[str] = []
174
+
175
+ def __init__(self, obj):
176
+ self.obj = obj
177
+ self._fields = []
178
+
179
+ self._cast_fields()
180
+ self._rename_fields()
181
+ self._include_fields()
182
+ dict.__init__(
183
+ self,
184
+ **{field: InfoJsonEncodable._cast_basic_types(getattr(self, field)) for field in self._fields},
185
+ )
186
+
187
+ @staticmethod
188
+ def _cast_basic_types(value):
189
+ if isinstance(value, datetime.datetime):
190
+ return value.isoformat()
191
+ if isinstance(value, (set, list, tuple)):
192
+ return str(list(value))
193
+ return value
194
+
195
+ def _rename_fields(self):
196
+ for field, renamed in self.renames.items():
197
+ if hasattr(self.obj, field):
198
+ setattr(self, renamed, getattr(self.obj, field))
199
+ self._fields.append(renamed)
200
+
201
+ def _cast_fields(self):
202
+ for field, func in self.casts.items():
203
+ setattr(self, field, func(self.obj))
204
+ self._fields.append(field)
205
+
206
+ def _include_fields(self):
207
+ if self.includes and self.excludes:
208
+ raise Exception("Don't use both includes and excludes.")
209
+ if self.includes:
210
+ for field in self.includes:
211
+ if field not in self._fields and hasattr(self.obj, field):
212
+ setattr(self, field, getattr(self.obj, field))
213
+ self._fields.append(field)
214
+ else:
215
+ for field, val in self.obj.__dict__.items():
216
+ if field not in self._fields and field not in self.excludes and field not in self.renames:
217
+ setattr(self, field, val)
218
+ self._fields.append(field)
219
+
220
+
221
+ class DagInfo(InfoJsonEncodable):
222
+ """Defines encoding DAG object to JSON."""
223
+
224
+ includes = ["dag_id", "schedule_interval", "tags", "start_date"]
225
+ casts = {"timetable": lambda dag: dag.timetable.serialize() if getattr(dag, "timetable", None) else None}
226
+ renames = {"_dag_id": "dag_id"}
227
+
228
+
229
+ class DagRunInfo(InfoJsonEncodable):
230
+ """Defines encoding DagRun object to JSON."""
231
+
232
+ includes = [
233
+ "conf",
234
+ "dag_id",
235
+ "data_interval_start",
236
+ "data_interval_end",
237
+ "external_trigger",
238
+ "run_id",
239
+ "run_type",
240
+ "start_date",
241
+ ]
242
+
243
+
244
+ class TaskInstanceInfo(InfoJsonEncodable):
245
+ """Defines encoding TaskInstance object to JSON."""
246
+
247
+ includes = ["duration", "try_number", "pool"]
248
+ casts = {
249
+ "map_index": lambda ti: ti.map_index
250
+ if hasattr(ti, "map_index") and getattr(ti, "map_index") != -1
251
+ else None
252
+ }
253
+
254
+
255
+ class TaskInfo(InfoJsonEncodable):
256
+ """Defines encoding BaseOperator/AbstractOperator object to JSON."""
257
+
258
+ renames = {
259
+ "_BaseOperator__init_kwargs": "args",
260
+ "_BaseOperator__from_mapped": "mapped",
261
+ "_downstream_task_ids": "downstream_task_ids",
262
+ "_upstream_task_ids": "upstream_task_ids",
263
+ }
264
+ excludes = [
265
+ "_BaseOperator__instantiated",
266
+ "_dag",
267
+ "_hook",
268
+ "_log",
269
+ "_outlets",
270
+ "_inlets",
271
+ "_lock_for_execution",
272
+ "handler",
273
+ "params",
274
+ "python_callable",
275
+ "retry_delay",
276
+ ]
277
+ casts = {
278
+ "operator_class": lambda task: task.task_type,
279
+ "task_group": lambda task: TaskGroupInfo(task.task_group)
280
+ if hasattr(task, "task_group") and getattr(task.task_group, "_group_id", None)
281
+ else None,
282
+ }
283
+
284
+
285
+ class TaskGroupInfo(InfoJsonEncodable):
286
+ """Defines encoding TaskGroup object to JSON."""
287
+
288
+ renames = {
289
+ "_group_id": "group_id",
290
+ }
291
+ includes = [
292
+ "downstream_group_ids",
293
+ "downstream_task_ids",
294
+ "prefix_group_id",
295
+ "tooltip",
296
+ "upstream_group_ids",
297
+ "upstream_task_ids",
298
+ ]
299
+
300
+
301
+ def get_airflow_run_facet(
302
+ dag_run: DagRun,
303
+ dag: DAG,
304
+ task_instance: TaskInstance,
305
+ task: BaseOperator,
306
+ task_uuid: str,
307
+ ):
308
+ return {
309
+ "airflow": json.loads(
310
+ json.dumps(
311
+ asdict(
312
+ AirflowRunFacet(
313
+ dag=DagInfo(dag),
314
+ dagRun=DagRunInfo(dag_run),
315
+ taskInstance=TaskInstanceInfo(task_instance),
316
+ task=TaskInfo(task),
317
+ taskUuid=task_uuid,
318
+ )
319
+ ),
320
+ default=str,
321
+ )
322
+ )
323
+ }
324
+
325
+
326
+ class OpenLineageRedactor(SecretsMasker):
327
+ """
328
+ This class redacts sensitive data similar to SecretsMasker in Airflow logs.
329
+
330
+ The difference is that our default max recursion depth is way higher - due to
331
+ the structure of OL events we need more depth.
332
+ Additionally, we allow data structures to specify data that needs not to be
333
+ redacted by specifying _skip_redact list by deriving RedactMixin.
334
+ """
335
+
336
+ @classmethod
337
+ def from_masker(cls, other: SecretsMasker) -> OpenLineageRedactor:
338
+ instance = cls()
339
+ instance.patterns = other.patterns
340
+ instance.replacer = other.replacer
341
+ return instance
342
+
343
+ def _redact(self, item: Redactable, name: str | None, depth: int, max_depth: int) -> Redacted:
344
+ if depth > max_depth:
345
+ return item
346
+ try:
347
+ # It's impossible to check the type of variable in a dict without accessing it, and
348
+ # this already causes warning - so suppress it
349
+ with suppress(AirflowContextDeprecationWarning):
350
+ if type(item).__name__ == "Proxy":
351
+ # Those are deprecated values in _DEPRECATION_REPLACEMENTS
352
+ # in airflow.utils.context.Context
353
+ return "<<non-redactable: Proxy>>"
354
+ if name and should_hide_value_for_key(name):
355
+ return self._redact_all(item, depth, max_depth)
356
+ if attrs.has(type(item)):
357
+ # TODO: FIXME when mypy gets compatible with new attrs
358
+ for dict_key, subval in attrs.asdict(
359
+ item, # type: ignore[arg-type]
360
+ recurse=False,
361
+ ).items():
362
+ if _is_name_redactable(dict_key, item):
363
+ setattr(
364
+ item,
365
+ dict_key,
366
+ self._redact(subval, name=dict_key, depth=(depth + 1), max_depth=max_depth),
367
+ )
368
+ return item
369
+ elif is_json_serializable(item) and hasattr(item, "__dict__"):
370
+ for dict_key, subval in item.__dict__.items():
371
+ if type(subval).__name__ == "Proxy":
372
+ return "<<non-redactable: Proxy>>"
373
+ if _is_name_redactable(dict_key, item):
374
+ setattr(
375
+ item,
376
+ dict_key,
377
+ self._redact(subval, name=dict_key, depth=(depth + 1), max_depth=max_depth),
378
+ )
379
+ return item
380
+ else:
381
+ return super()._redact(item, name, depth, max_depth)
382
+ except Exception as exc:
383
+ log.warning("Unable to redact %r. Error was: %s: %s", item, type(exc).__name__, exc)
384
+ return item
385
+
386
+
387
+ def is_json_serializable(item):
388
+ try:
389
+ json.dumps(item)
390
+ return True
391
+ except (TypeError, ValueError):
392
+ return False
393
+
394
+
395
+ def _is_name_redactable(name, redacted):
396
+ if not issubclass(redacted.__class__, RedactMixin):
397
+ return not name.startswith("_")
398
+ return name not in redacted.skip_redact
399
+
400
+
401
+ def print_warning(log):
402
+ def decorator(f):
403
+ @wraps(f)
404
+ def wrapper(*args, **kwargs):
405
+ try:
406
+ return f(*args, **kwargs)
407
+ except Exception as e:
408
+ log.warning(e)
409
+
410
+ return wrapper
411
+
412
+ return decorator
413
+
414
+
415
+ @cache
416
+ def is_source_enabled() -> bool:
417
+ source_var = conf.get(
418
+ "openlineage", "disable_source_code", fallback=os.getenv("OPENLINEAGE_AIRFLOW_DISABLE_SOURCE_CODE")
419
+ )
420
+ return isinstance(source_var, str) and source_var.lower() not in ("true", "1", "t")
421
+
422
+
423
+ def get_filtered_unknown_operator_keys(operator: BaseOperator) -> dict:
424
+ not_required_keys = {"dag", "task_group"}
425
+ return {attr: value for attr, value in operator.__dict__.items() if attr not in not_required_keys}
426
+
427
+
428
+ def normalize_sql(sql: str | Iterable[str]):
429
+ if isinstance(sql, str):
430
+ sql = [stmt for stmt in sql.split(";") if stmt != ""]
431
+ sql = [obj for stmt in sql for obj in stmt.split(";") if obj != ""]
432
+ return ";\n".join(sql)
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.1
2
+ Name: apache-airflow-providers-openlineage
3
+ Version: 1.3.1rc1
4
+ Summary: Provider package apache-airflow-providers-openlineage for Apache Airflow
5
+ Keywords: airflow-provider,openlineage,airflow,integration
6
+ Author-email: Apache Software Foundation <dev@airflow.apache.org>
7
+ Maintainer-email: Apache Software Foundation <dev@airflow.apache.org>
8
+ Requires-Python: ~=3.8
9
+ Description-Content-Type: text/x-rst
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: Console
12
+ Classifier: Environment :: Web Environment
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: Framework :: Apache Airflow
16
+ Classifier: Framework :: Apache Airflow :: Provider
17
+ Classifier: License :: OSI Approved :: Apache Software License
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Topic :: System :: Monitoring
23
+ Requires-Dist: apache-airflow-providers-common-sql>=1.6.0.dev0
24
+ Requires-Dist: apache-airflow>=2.7.0.dev0
25
+ Requires-Dist: attrs>=22.2
26
+ Requires-Dist: openlineage-integration-common>=0.28.0
27
+ Requires-Dist: openlineage-python>=0.28.0
28
+ Requires-Dist: apache-airflow-providers-common-sql ; extra == "common.sql"
29
+ Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
30
+ Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-openlineage/1.3.1/changelog.html
31
+ Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-openlineage/1.3.1
32
+ Project-URL: Slack Chat, https://s.apache.org/airflow-slack
33
+ Project-URL: Source Code, https://github.com/apache/airflow
34
+ Project-URL: Twitter, https://twitter.com/ApacheAirflow
35
+ Project-URL: YouTube, https://www.youtube.com/channel/UCSXwxpWZQ7XZ1WL3wqevChA/
36
+ Provides-Extra: common.sql
37
+
38
+
39
+ .. Licensed to the Apache Software Foundation (ASF) under one
40
+ or more contributor license agreements. See the NOTICE file
41
+ distributed with this work for additional information
42
+ regarding copyright ownership. The ASF licenses this file
43
+ to you under the Apache License, Version 2.0 (the
44
+ "License"); you may not use this file except in compliance
45
+ with the License. You may obtain a copy of the License at
46
+
47
+ .. http://www.apache.org/licenses/LICENSE-2.0
48
+
49
+ .. Unless required by applicable law or agreed to in writing,
50
+ software distributed under the License is distributed on an
51
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52
+ KIND, either express or implied. See the License for the
53
+ specific language governing permissions and limitations
54
+ under the License.
55
+
56
+ .. Licensed to the Apache Software Foundation (ASF) under one
57
+ or more contributor license agreements. See the NOTICE file
58
+ distributed with this work for additional information
59
+ regarding copyright ownership. The ASF licenses this file
60
+ to you under the Apache License, Version 2.0 (the
61
+ "License"); you may not use this file except in compliance
62
+ with the License. You may obtain a copy of the License at
63
+
64
+ .. http://www.apache.org/licenses/LICENSE-2.0
65
+
66
+ .. Unless required by applicable law or agreed to in writing,
67
+ software distributed under the License is distributed on an
68
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
69
+ KIND, either express or implied. See the License for the
70
+ specific language governing permissions and limitations
71
+ under the License.
72
+
73
+ .. NOTE! THIS FILE IS AUTOMATICALLY GENERATED AND WILL BE
74
+ OVERWRITTEN WHEN PREPARING PACKAGES.
75
+
76
+ .. IF YOU WANT TO MODIFY TEMPLATE FOR THIS FILE, YOU SHOULD MODIFY THE TEMPLATE
77
+ `PROVIDER_README_TEMPLATE.rst.jinja2` IN the `dev/breeze/src/airflow_breeze/templates` DIRECTORY
78
+
79
+
80
+ Package ``apache-airflow-providers-openlineage``
81
+
82
+ Release: ``1.3.1.rc1``
83
+
84
+
85
+ `OpenLineage <https://openlineage.io/>`__
86
+
87
+
88
+ Provider package
89
+ ----------------
90
+
91
+ This is a provider package for ``openlineage`` provider. All classes for this provider package
92
+ are in ``airflow.providers.openlineage`` python package.
93
+
94
+ You can find package information and changelog for the provider
95
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-openlineage/1.3.1/>`_.
96
+
97
+ Installation
98
+ ------------
99
+
100
+ You can install this package on top of an existing Airflow 2 installation (see ``Requirements`` below
101
+ for the minimum Airflow version supported) via
102
+ ``pip install apache-airflow-providers-openlineage``
103
+
104
+ The package supports the following python versions: 3.8,3.9,3.10,3.11
105
+
106
+ Requirements
107
+ ------------
108
+
109
+ ======================================= ==================
110
+ PIP package Version required
111
+ ======================================= ==================
112
+ ``apache-airflow`` ``>=2.7.0``
113
+ ``apache-airflow-providers-common-sql`` ``>=1.6.0``
114
+ ``attrs`` ``>=22.2``
115
+ ``openlineage-integration-common`` ``>=0.28.0``
116
+ ``openlineage-python`` ``>=0.28.0``
117
+ ======================================= ==================
118
+
119
+ Cross provider package dependencies
120
+ -----------------------------------
121
+
122
+ Those are dependencies that might be needed in order to use all the features of the package.
123
+ You need to install the specified provider packages in order to use them.
124
+
125
+ You can install such cross-provider dependencies when installing from PyPI. For example:
126
+
127
+ .. code-block:: bash
128
+
129
+ pip install apache-airflow-providers-openlineage[common.sql]
130
+
131
+
132
+ ============================================================================================================ ==============
133
+ Dependent package Extra
134
+ ============================================================================================================ ==============
135
+ `apache-airflow-providers-common-sql <https://airflow.apache.org/docs/apache-airflow-providers-common-sql>`_ ``common.sql``
136
+ ============================================================================================================ ==============
137
+
138
+ The changelog for the provider package can be found in the
139
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-openlineage/1.3.1/changelog.html>`_.
@@ -0,0 +1,22 @@
1
+ airflow/providers/openlineage/LICENSE,sha256=ywUBpKZc7Jb96rVt5I3IDbg7dIJAbUSHkuoDcF3jbH4,13569
2
+ airflow/providers/openlineage/__init__.py,sha256=gie-OVx8Ck47h2pFcd-7v_fe2F9fNjheEYbAuF5DfzU,1586
3
+ airflow/providers/openlineage/get_provider_info.py,sha256=SXz-PbczkwBJmSNi9LyD2_F90EDlto9_U5amXWRzCeE,5413
4
+ airflow/providers/openlineage/sqlparser.py,sha256=cB2NFH9rPUnkHqZ4NGh7AsAnoR7Y0YeEtQN9kgMTtRg,13384
5
+ airflow/providers/openlineage/extractors/__init__.py,sha256=I0X4f6zUniclyD9zT0DFHRImpCpJVP4MkPJT3cd7X5I,1081
6
+ airflow/providers/openlineage/extractors/base.py,sha256=KUYdZa8B238BISeaKLPNghaAt4AGGGkC-ufzsI4FB5w,5897
7
+ airflow/providers/openlineage/extractors/bash.py,sha256=fz1nVywzk1kUsZWeEbQ8zV6osTGhmd_pLgAKoJla54g,2843
8
+ airflow/providers/openlineage/extractors/manager.py,sha256=K1hbmxmer7KtO_0730GLGTdBOle6MoArw7qcfyzy9m8,7917
9
+ airflow/providers/openlineage/extractors/python.py,sha256=HdSJi6r6EWNinLUroUdcVi3b_4vmuoc_-E51Xc8ocmo,3423
10
+ airflow/providers/openlineage/plugins/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
11
+ airflow/providers/openlineage/plugins/adapter.py,sha256=Pq_h4fGZ9m7kWVTfvDFakIkmg3wseZvBscsimIGx-hQ,13787
12
+ airflow/providers/openlineage/plugins/facets.py,sha256=pt8UvUHk-rXJdJreNq5B2NSWe-CtlfnmImdY5vesgjY,2202
13
+ airflow/providers/openlineage/plugins/listener.py,sha256=t0UuV0R__lBGSCT4W9RdegSE6aHlvKQGGdRJK0PR6u4,8511
14
+ airflow/providers/openlineage/plugins/macros.py,sha256=AtBwQZPqTOWO38OucjgYS2ooiKkMTuRKLnBXdQHnAuw,2356
15
+ airflow/providers/openlineage/plugins/openlineage.py,sha256=XiEznOts-q9Uq08rkorclK49FAmtIIsnkW5hsdoxeB0,1987
16
+ airflow/providers/openlineage/utils/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
17
+ airflow/providers/openlineage/utils/sql.py,sha256=9Hvzs_aKBRAmuxAO22Myaz-PwwY1XvcLEwRq0sAD33Q,7634
18
+ airflow/providers/openlineage/utils/utils.py,sha256=-i8W7LtBMlRZdIqlXuqEr8isDIUl6REyB0wOLdyv_KI,14281
19
+ apache_airflow_providers_openlineage-1.3.1rc1.dist-info/entry_points.txt,sha256=GAx0_i2OeZzqaiiiYuA-xchICDXiCT5kVqpKSxsOjt4,214
20
+ apache_airflow_providers_openlineage-1.3.1rc1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
21
+ apache_airflow_providers_openlineage-1.3.1rc1.dist-info/METADATA,sha256=FvvTSzEur02Lm53ZSU6zhGmAmKc2AZrbREdF1aAF5Mc,6329
22
+ apache_airflow_providers_openlineage-1.3.1rc1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: flit 3.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [airflow.plugins]
2
+ openlineage=airflow.providers.openlineage.plugins.openlineage:OpenLineageProviderPlugin
3
+
4
+ [apache_airflow_provider]
5
+ provider_info=airflow.providers.openlineage.get_provider_info:get_provider_info
6
+