openlineage-python 1.8.0__tar.gz → 1.9.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.
Files changed (43) hide show
  1. openlineage_python-1.9.0/PKG-INFO +34 -0
  2. openlineage_python-1.9.0/README.md +5 -0
  3. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/constants.py +1 -1
  4. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/file.py +12 -13
  5. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/pyproject.toml +1 -1
  6. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/test_file.py +103 -1
  7. openlineage_python-1.8.0/PKG-INFO +0 -109
  8. openlineage_python-1.8.0/README.md +0 -80
  9. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/.gitignore +0 -0
  10. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/__init__.py +0 -0
  11. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/client.py +0 -0
  12. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/facet.py +0 -0
  13. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/filter.py +0 -0
  14. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/py.typed +0 -0
  15. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/run.py +0 -0
  16. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/serde.py +0 -0
  17. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/__init__.py +0 -0
  18. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/console.py +0 -0
  19. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/factory.py +0 -0
  20. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/http.py +0 -0
  21. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/kafka.py +0 -0
  22. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/noop.py +0 -0
  23. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/transport/transport.py +0 -0
  24. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/openlineage/client/utils.py +0 -0
  25. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/__init__.py +0 -0
  26. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/config/config.yml +0 -0
  27. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/config/exact_filter.yml +0 -0
  28. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/config/http.yml +0 -0
  29. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/config/openlineage.yml +0 -0
  30. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/config/regex_filter.yml +0 -0
  31. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/conftest.py +0 -0
  32. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/nominal_time_without_end.json +0 -0
  33. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/serde_example_dataset_event.json +0 -0
  34. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/serde_example_job_event.json +0 -0
  35. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/serde_example_run_event.json +0 -0
  36. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/test_client.py +0 -0
  37. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/test_events.py +0 -0
  38. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/test_facet.py +0 -0
  39. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/test_factory.py +0 -0
  40. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/test_http.py +0 -0
  41. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/test_kafka.py +0 -0
  42. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tests/transport.py +0 -0
  43. {openlineage_python-1.8.0 → openlineage_python-1.9.0}/tox.ini +0 -0
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.1
2
+ Name: openlineage-python
3
+ Version: 1.9.0
4
+ Summary: OpenLineage Python Client
5
+ Author-email: OpenLineage <info@openlineage.io>
6
+ Keywords: openlineage
7
+ Classifier: Operating System :: OS Independent
8
+ Classifier: Programming Language :: Python
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.8
16
+ Requires-Dist: attrs>=20.0
17
+ Requires-Dist: python-dateutil>=2.8.2
18
+ Requires-Dist: pyyaml>=5.4
19
+ Requires-Dist: requests>=2.20.0
20
+ Provides-Extra: kafka
21
+ Requires-Dist: confluent-kafka>=2.1.1; extra == 'kafka'
22
+ Provides-Extra: test
23
+ Requires-Dist: covdefaults>=2.3; extra == 'test'
24
+ Requires-Dist: pytest-cov>=4.1; extra == 'test'
25
+ Requires-Dist: pytest-mock>=3.10; extra == 'test'
26
+ Requires-Dist: pytest>=7.3.1; extra == 'test'
27
+ Requires-Dist: pyyaml>=5.4; extra == 'test'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # OpenLineage Python Client
31
+
32
+ Python client for [OpenLineage](https://openlineage.io).
33
+
34
+ Refer to the [documentation](https://openlineage.io/docs/client/python) for details.
@@ -0,0 +1,5 @@
1
+ # OpenLineage Python Client
2
+
3
+ Python client for [OpenLineage](https://openlineage.io).
4
+
5
+ Refer to the [documentation](https://openlineage.io/docs/client/python) for details.
@@ -2,7 +2,7 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "1.8.0"
5
+ __version__ = "1.9.0"
6
6
 
7
7
  DEFAULT_TIMEOUT_MS = 5000
8
8
  DEFAULT_NAMESPACE_NAME = "default"
@@ -3,6 +3,7 @@
3
3
 
4
4
  from __future__ import annotations
5
5
 
6
+ import io
6
7
  from dataclasses import dataclass
7
8
  from datetime import datetime
8
9
  from typing import TYPE_CHECKING, Any
@@ -25,14 +26,7 @@ class FileConfig(Config):
25
26
  msg = "`log_file_path` key not passed to FileConfig"
26
27
  raise RuntimeError(msg)
27
28
 
28
- log_file_path = params["log_file_path"]
29
-
30
- with open(log_file_path, "a" if cls.append else "w") as log_file_handle:
31
- if not log_file_handle.writable():
32
- msg = f"Log file {log_file_path} is not writeable"
33
- raise RuntimeError(msg)
34
-
35
- return cls(log_file_path)
29
+ return cls(log_file_path=params["log_file_path"], append=params.get("append", False))
36
30
 
37
31
 
38
32
  class FileTransport(Transport):
@@ -46,8 +40,13 @@ class FileTransport(Transport):
46
40
  if self.config.append:
47
41
  log_file_path = self.config.log_file_path
48
42
  else:
49
- timestr = datetime.now().strftime("%Y%m%d-%H%M%S.%f")
50
- log_file_path = f"{self.config.log_file_path}-{timestr}"
51
-
52
- with open(log_file_path, "a" if self.config.append else "w") as log_file_handle:
53
- log_file_handle.write(Serde.to_json(event) + "\n")
43
+ time_str = datetime.now().strftime("%Y%m%d-%H%M%S.%f")
44
+ log_file_path = f"{self.config.log_file_path}-{time_str}"
45
+
46
+ try:
47
+ with open(log_file_path, "a" if self.config.append else "w") as log_file_handle:
48
+ log_file_handle.write(Serde.to_json(event) + "\n")
49
+ except (PermissionError, io.UnsupportedOperation) as error:
50
+ # If we lack write permissions or file is opened in wrong mode
51
+ msg = f"Log file `{log_file_path}` is not writeable"
52
+ raise RuntimeError(msg) from error
@@ -6,7 +6,7 @@ requires = [
6
6
 
7
7
  [project]
8
8
  name = "openlineage-python"
9
- version = "1.8.0"
9
+ version = "1.9.0"
10
10
  description = "OpenLineage Python Client"
11
11
  readme = "README.md"
12
12
  keywords = [
@@ -2,13 +2,16 @@
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
  import datetime
4
4
  import json
5
+ import os
5
6
  import tempfile
6
7
  import time
7
8
  import uuid
8
9
  from os import listdir
9
- from os.path import isfile, join
10
+ from os.path import exists, isfile, join
10
11
  from typing import Any, Dict, List
12
+ from unittest import mock
11
13
 
14
+ import pytest
12
15
  from openlineage.client import OpenLineageClient
13
16
  from openlineage.client.run import Job, Run, RunEvent, RunState
14
17
  from openlineage.client.transport.file import FileConfig, FileTransport
@@ -90,3 +93,102 @@ def test_client_with_file_transport_write_emits() -> None:
90
93
  test_event = test_event_set[i]
91
94
 
92
95
  assert_test_events(log_line, test_event)
96
+
97
+
98
+ @pytest.mark.parametrize(
99
+ "append",
100
+ [
101
+ True,
102
+ False,
103
+ ],
104
+ )
105
+ @mock.patch("openlineage.client.transport.file.datetime")
106
+ def test_file_transport_raises_error_with_non_writeable_file(mock_dt, append) -> None:
107
+ event = RunEvent(
108
+ eventType=RunState.START,
109
+ eventTime=datetime.datetime.now().isoformat(),
110
+ run=Run(runId=str(uuid.uuid4())),
111
+ job=Job(namespace="file", name="test"),
112
+ producer="prod",
113
+ )
114
+ mock_dt.now().strftime.return_value = "timestr"
115
+
116
+ with tempfile.TemporaryDirectory() as log_dir:
117
+ log_base_file_path = join(log_dir, "logtest")
118
+
119
+ config = FileConfig(log_file_path=log_base_file_path, append=append)
120
+ transport = FileTransport(config)
121
+
122
+ log_file_path = log_base_file_path if append else f"{log_base_file_path}-timestr"
123
+
124
+ # Create the file and make it read-only
125
+ with open(log_file_path, "w") as f:
126
+ f.write("This is a test file.")
127
+ os.chmod(log_file_path, 0o444)
128
+
129
+ with pytest.raises(RuntimeError, match=f"Log file `{log_file_path}` is not writeable"):
130
+ transport.emit(event)
131
+
132
+ os.remove(log_file_path)
133
+
134
+
135
+ @pytest.mark.parametrize(
136
+ "append",
137
+ [
138
+ True,
139
+ False,
140
+ ],
141
+ )
142
+ def test_file_config_from_dict(append) -> None:
143
+ log_dir = tempfile.TemporaryDirectory()
144
+ file_path = join(log_dir.name, "logtest")
145
+
146
+ config = FileConfig.from_dict(params={"log_file_path": file_path, "append": append})
147
+ assert config.append is append
148
+ assert config.log_file_path == file_path
149
+
150
+
151
+ def test_file_config_from_dict_no_file() -> None:
152
+ with pytest.raises(RuntimeError):
153
+ FileConfig.from_dict({})
154
+
155
+
156
+ @pytest.mark.parametrize(
157
+ "append",
158
+ [
159
+ True,
160
+ False,
161
+ ],
162
+ )
163
+ def test_file_config_does_not_create_file_if_missing(append) -> None:
164
+ log_dir = tempfile.TemporaryDirectory()
165
+ file_path = join(log_dir.name, "test")
166
+
167
+ assert not exists(file_path)
168
+ FileConfig.from_dict(params={"log_file_path": file_path, "append": append})
169
+ assert not exists(file_path)
170
+
171
+
172
+ @pytest.mark.parametrize(
173
+ "append",
174
+ [
175
+ True,
176
+ False,
177
+ ],
178
+ )
179
+ def test_file_config_append_mode_does_not_modify_file_if_exists(append) -> None:
180
+ log_dir = tempfile.TemporaryDirectory()
181
+ file_path = join(log_dir.name, "test-file")
182
+
183
+ with open(file_path, "w") as file:
184
+ file.write("test content")
185
+
186
+ assert exists(file_path)
187
+ FileConfig.from_dict(params={"log_file_path": file_path, "append": append})
188
+ assert exists(file_path)
189
+
190
+ with open(file_path) as file:
191
+ file_content = file.read()
192
+ assert file_content == "test content"
193
+
194
+ os.remove(file_path)
@@ -1,109 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: openlineage-python
3
- Version: 1.8.0
4
- Summary: OpenLineage Python Client
5
- Author-email: OpenLineage <info@openlineage.io>
6
- Keywords: openlineage
7
- Classifier: Operating System :: OS Independent
8
- Classifier: Programming Language :: Python
9
- Classifier: Programming Language :: Python :: 3 :: Only
10
- Classifier: Programming Language :: Python :: 3.8
11
- Classifier: Programming Language :: Python :: 3.9
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Requires-Python: >=3.8
16
- Requires-Dist: attrs>=20.0
17
- Requires-Dist: python-dateutil>=2.8.2
18
- Requires-Dist: pyyaml>=5.4
19
- Requires-Dist: requests>=2.20.0
20
- Provides-Extra: kafka
21
- Requires-Dist: confluent-kafka>=2.1.1; extra == 'kafka'
22
- Provides-Extra: test
23
- Requires-Dist: covdefaults>=2.3; extra == 'test'
24
- Requires-Dist: pytest-cov>=4.1; extra == 'test'
25
- Requires-Dist: pytest-mock>=3.10; extra == 'test'
26
- Requires-Dist: pytest>=7.3.1; extra == 'test'
27
- Requires-Dist: pyyaml>=5.4; extra == 'test'
28
- Description-Content-Type: text/markdown
29
-
30
- # OpenLineage-python
31
-
32
- To install from source, run:
33
-
34
- ```bash
35
- $ python -m pip install .
36
- ```
37
-
38
- ## Configuration
39
- ### Config file
40
-
41
- The most common way to configure the OpenLineage Client is by `.yaml` file, which contains all the
42
- details of how to connect to your OpenLineage backend.
43
-
44
- The config file is located by:
45
- 1) looking at the `OPENLINEAGE_CONFIG` environment variable
46
- 2) looking for the `openlineage.yml` file in the current working directory
47
- 3) looking for the `openlineage.yml` file in the `$HOME/.openlineage` directory.
48
-
49
- Different ways of connecting to OpenLineage backend are supported
50
- by the standardized `Transport` interface.
51
- This is an example config for specifying `http` transport:
52
-
53
- ```yaml
54
- transport:
55
- type: "http"
56
- url: "https://backend:5000"
57
- auth:
58
- type: "api_key"
59
- api_key: "f048521b-dfe8-47cd-9c65-0cb07d57591e"
60
- ```
61
-
62
- The `type` property is required. It can be one of the built-in transports or a custom one.
63
- There are four built-in transports, `http`, `kafka`, `console` and `file`.
64
- Custom transports `type` is a fully qualified class name that can be imported.
65
-
66
- The rest of the properties are defined by the particular transport.
67
- Specifications for the built-in options are below.
68
-
69
- #### HTTP
70
-
71
- * `url` - required string parameter.
72
- * `endpoint` - optional string parameter specifying the endpoint to which events are sent. By default `api/v1/lineage`.
73
- * `timeout` - optional float parameter specifying the timeout when sending events. By default 5 seconds.
74
- * `verify` optional boolean attribute specifying if the client should verify TLS certificates of the backend. By default true.
75
- * `auth` - optional dictionary specifying authentication options. The type property is required.
76
- * `type`: required property if an auth dictionary is set. Set to `api_key` or to the fully qualified class name of your TokenProvider.
77
- * `api_key`: if `api_key` type is set, it sets value at `Authentication` HTTP header as `Bearer`.
78
-
79
- #### Kafka
80
-
81
- For KafkaTransport, `confluent-kafka` needs to be installed.
82
- You can also install `pip install openlineage-python[kafka]`
83
-
84
- * `config` - required string parameter. A dictionary that contains a Kafka producer config as in [Kafka producer config](https://docs.confluent.io/platform/current/clients/confluent-kafka-python/html/index.html#kafka-client-configuration).
85
- * `topic` - required string parameter. The topic on what events will be sent.
86
- * `flush` - optional boolean parameter. If set to True, Kafka will flush after each event. By default true.
87
-
88
- #### File
89
-
90
- * `log_file_path` - required string parameter specifying the path of the file (if `append` is true a file path is expected, otherwise a file prefix is expected).
91
- * `append` - optional boolean parameter. If set to True, each event will be appended to a single file (log_file_path), otherwise, all event would be written separately in distinct files suffixed by a timestring. By default false.
92
-
93
- ### Custom transport
94
-
95
- To implement a custom transport, follow the instructions in the `openlineage/client/transport/transport.py` file.
96
-
97
- ### Config as env vars
98
-
99
- If there is no config file found, the OpenLineage client looks at environment variables.
100
- This way of configuring the client supports only `http` transport, and only a subset of its config.
101
-
102
- * `OPENLINEAGE_URL` - point to the service that will consume OpenLineage events.
103
- * `OPENLINEAGE_ENDPOINT` - specify the endpoint to send the OpenLineage events.
104
- * `OPENLINEAGE_API_KEY` - set if the consumer of OpenLineage events requires a `Bearer` authentication key.
105
-
106
- `OPENLINEAGE_URL`, `OPENLINEAGE_ENDPOINT`, and `OPENLINEAGE_API_KEY` can also be set up manually when creating a client instance.
107
-
108
- #### Logging level
109
- In addition to conventional logging approaches, the OpenLineage client library provides an alternative way of configuring its logging behavior. By setting the `OPENLINEAGE_CLIENT_LOGGING` environment variable, you can establish the logging level for the `openlineage.client` and its child modules.
@@ -1,80 +0,0 @@
1
- # OpenLineage-python
2
-
3
- To install from source, run:
4
-
5
- ```bash
6
- $ python -m pip install .
7
- ```
8
-
9
- ## Configuration
10
- ### Config file
11
-
12
- The most common way to configure the OpenLineage Client is by `.yaml` file, which contains all the
13
- details of how to connect to your OpenLineage backend.
14
-
15
- The config file is located by:
16
- 1) looking at the `OPENLINEAGE_CONFIG` environment variable
17
- 2) looking for the `openlineage.yml` file in the current working directory
18
- 3) looking for the `openlineage.yml` file in the `$HOME/.openlineage` directory.
19
-
20
- Different ways of connecting to OpenLineage backend are supported
21
- by the standardized `Transport` interface.
22
- This is an example config for specifying `http` transport:
23
-
24
- ```yaml
25
- transport:
26
- type: "http"
27
- url: "https://backend:5000"
28
- auth:
29
- type: "api_key"
30
- api_key: "f048521b-dfe8-47cd-9c65-0cb07d57591e"
31
- ```
32
-
33
- The `type` property is required. It can be one of the built-in transports or a custom one.
34
- There are four built-in transports, `http`, `kafka`, `console` and `file`.
35
- Custom transports `type` is a fully qualified class name that can be imported.
36
-
37
- The rest of the properties are defined by the particular transport.
38
- Specifications for the built-in options are below.
39
-
40
- #### HTTP
41
-
42
- * `url` - required string parameter.
43
- * `endpoint` - optional string parameter specifying the endpoint to which events are sent. By default `api/v1/lineage`.
44
- * `timeout` - optional float parameter specifying the timeout when sending events. By default 5 seconds.
45
- * `verify` optional boolean attribute specifying if the client should verify TLS certificates of the backend. By default true.
46
- * `auth` - optional dictionary specifying authentication options. The type property is required.
47
- * `type`: required property if an auth dictionary is set. Set to `api_key` or to the fully qualified class name of your TokenProvider.
48
- * `api_key`: if `api_key` type is set, it sets value at `Authentication` HTTP header as `Bearer`.
49
-
50
- #### Kafka
51
-
52
- For KafkaTransport, `confluent-kafka` needs to be installed.
53
- You can also install `pip install openlineage-python[kafka]`
54
-
55
- * `config` - required string parameter. A dictionary that contains a Kafka producer config as in [Kafka producer config](https://docs.confluent.io/platform/current/clients/confluent-kafka-python/html/index.html#kafka-client-configuration).
56
- * `topic` - required string parameter. The topic on what events will be sent.
57
- * `flush` - optional boolean parameter. If set to True, Kafka will flush after each event. By default true.
58
-
59
- #### File
60
-
61
- * `log_file_path` - required string parameter specifying the path of the file (if `append` is true a file path is expected, otherwise a file prefix is expected).
62
- * `append` - optional boolean parameter. If set to True, each event will be appended to a single file (log_file_path), otherwise, all event would be written separately in distinct files suffixed by a timestring. By default false.
63
-
64
- ### Custom transport
65
-
66
- To implement a custom transport, follow the instructions in the `openlineage/client/transport/transport.py` file.
67
-
68
- ### Config as env vars
69
-
70
- If there is no config file found, the OpenLineage client looks at environment variables.
71
- This way of configuring the client supports only `http` transport, and only a subset of its config.
72
-
73
- * `OPENLINEAGE_URL` - point to the service that will consume OpenLineage events.
74
- * `OPENLINEAGE_ENDPOINT` - specify the endpoint to send the OpenLineage events.
75
- * `OPENLINEAGE_API_KEY` - set if the consumer of OpenLineage events requires a `Bearer` authentication key.
76
-
77
- `OPENLINEAGE_URL`, `OPENLINEAGE_ENDPOINT`, and `OPENLINEAGE_API_KEY` can also be set up manually when creating a client instance.
78
-
79
- #### Logging level
80
- In addition to conventional logging approaches, the OpenLineage client library provides an alternative way of configuring its logging behavior. By setting the `OPENLINEAGE_CLIENT_LOGGING` environment variable, you can establish the logging level for the `openlineage.client` and its child modules.