Skip to content

Commit 276b6df

Browse files
jacob-bush-shopifypi-monofranciscojavierarceo
authored
feat: Allow default openlineage configuration (#6467)
fix: Update transport_type default and align docs/config - config.py: get_transport_config() returns None when transport_type is None, allowing OpenLineage SDK to use its own defaults - config.py: update docstrings to reflect None default - repo_config.py: change transport_type from StrictStr='console' to Optional[StrictStr]=None - docs/reference/openlineage.md: update config table default - 07-openlineage-and-materialization.md: update field reference table - skills/references/configuration.md: update comment Signed-off-by: Jacob Bush <jacob.bush@shopify.com> Co-authored-by: AI (Pi/Claude Opus 4.6 [250k]) <noreply@pi.dev> Co-authored-by: Francisco Javier Arceo <arceofrancisco@gmail.com>
1 parent 32928f7 commit 276b6df

7 files changed

Lines changed: 173 additions & 14 deletions

File tree

docs/how-to-guides/feast-operator/07-openlineage-and-materialization.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ openlineage:
103103
| Field | Type | Description |
104104
|-------|------|-------------|
105105
| `enabled` | bool | Activates OpenLineage. Must be `true` |
106-
| `transportType` | string | `http` / `console` / `file` / `kafka` |
106+
| `transportType` | string | `http` / `console` / `file` / `kafka` (omit to use OpenLineage SDK defaults) |
107107
| `transportUrl` | string | Base URL for HTTP transport |
108108
| `transportEndpoint` | string | API path appended to `transportUrl` |
109109
| `apiKeySecretRef.name` | string | Name of a Secret containing key `api_key` |

docs/reference/openlineage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ fs.materialize(
8888
| Option | Default | Description |
8989
|--------|---------|-------------|
9090
| `enabled` | `false` | Enable/disable OpenLineage integration |
91-
| `transport_type` | `http` | Transport type: `http`, `file`, `kafka` |
91+
| `transport_type` | `None` | Transport type: `http`, `console`, `file`, `kafka`. When unset, defers to OpenLineage SDK defaults. |
9292
| `transport_url` | - | URL for HTTP transport (required) |
9393
| `transport_endpoint` | `api/v1/lineage` | API endpoint for HTTP transport |
9494
| `api_key` | - | Optional API key for authentication |

sdk/python/feast/openlineage/client.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ def __init__(
106106
# Initialize the OpenLineage client
107107
try:
108108
transport_config = self._config.get_transport_config()
109-
self._client = OpenLineageClient(config={"transport": transport_config})
109+
if transport_config is None:
110+
self._client = OpenLineageClient()
111+
else:
112+
self._client = OpenLineageClient(config={"transport": transport_config})
110113
logger.info(
111-
f"OpenLineage client initialized with {self._config.transport_type} transport"
114+
f"OpenLineage client initialized with {self._config.transport_type or 'default'} transport"
112115
)
113116
except Exception as e:
114117
logger.error(f"Failed to initialize OpenLineage client: {e}")

sdk/python/feast/openlineage/config.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class OpenLineageConfig:
2828
2929
Attributes:
3030
enabled: Whether OpenLineage integration is enabled
31-
transport_type: Type of transport (http, console, file, kafka)
31+
transport_type: Type of transport (http, console, file, kafka), or None to use
32+
OpenLineage SDK defaults
3233
transport_url: URL for HTTP transport
3334
transport_endpoint: API endpoint for HTTP transport
3435
api_key: Optional API key for authentication
@@ -40,7 +41,7 @@ class OpenLineageConfig:
4041
"""
4142

4243
enabled: bool = True
43-
transport_type: str = "console"
44+
transport_type: Optional[str] = None
4445
transport_url: Optional[str] = None
4546
transport_endpoint: str = "api/v1/lineage"
4647
api_key: Optional[str] = None
@@ -63,7 +64,7 @@ def from_dict(cls, config_dict: Dict[str, Any]) -> "OpenLineageConfig":
6364
"""
6465
return cls(
6566
enabled=config_dict.get("enabled", True),
66-
transport_type=config_dict.get("transport_type", "console"),
67+
transport_type=config_dict.get("transport_type"),
6768
transport_url=config_dict.get("transport_url"),
6869
transport_endpoint=config_dict.get("transport_endpoint", "api/v1/lineage"),
6970
api_key=config_dict.get("api_key"),
@@ -81,7 +82,7 @@ def from_env(cls) -> "OpenLineageConfig":
8182
8283
Environment variables:
8384
FEAST_OPENLINEAGE_ENABLED: Enable/disable OpenLineage (default: true)
84-
FEAST_OPENLINEAGE_TRANSPORT_TYPE: Transport type (default: console)
85+
FEAST_OPENLINEAGE_TRANSPORT_TYPE: Transport type (default: None, uses OL SDK defaults)
8586
FEAST_OPENLINEAGE_URL: HTTP transport URL
8687
FEAST_OPENLINEAGE_ENDPOINT: API endpoint (default: api/v1/lineage)
8788
FEAST_OPENLINEAGE_API_KEY: API key for authentication
@@ -93,7 +94,7 @@ def from_env(cls) -> "OpenLineageConfig":
9394
"""
9495
return cls(
9596
enabled=os.getenv("FEAST_OPENLINEAGE_ENABLED", "true").lower() == "true",
96-
transport_type=os.getenv("FEAST_OPENLINEAGE_TRANSPORT_TYPE", "console"),
97+
transport_type=os.getenv("FEAST_OPENLINEAGE_TRANSPORT_TYPE"),
9798
transport_url=os.getenv("FEAST_OPENLINEAGE_URL"),
9899
transport_endpoint=os.getenv(
99100
"FEAST_OPENLINEAGE_ENDPOINT", "api/v1/lineage"
@@ -129,13 +130,17 @@ def to_dict(self) -> Dict[str, Any]:
129130
"additional_config": self.additional_config,
130131
}
131132

132-
def get_transport_config(self) -> Dict[str, Any]:
133+
def get_transport_config(self) -> Optional[Dict[str, Any]]:
133134
"""
134135
Get transport-specific configuration for OpenLineage client.
135136
136137
Returns:
137-
Dictionary with transport configuration
138+
Dictionary with transport configuration, or None if transport_type
139+
is not set (allowing the OpenLineage SDK to use its own defaults).
138140
"""
141+
if not self.transport_type:
142+
return None
143+
139144
config: Dict[str, Any] = {"type": self.transport_type}
140145

141146
if self.transport_type == "http":

sdk/python/feast/repo_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,8 @@ class OpenLineageConfig(FeastBaseModel):
249249
enabled: StrictBool = False
250250
""" bool: Whether OpenLineage integration is enabled. Defaults to False. """
251251

252-
transport_type: StrictStr = "console"
253-
""" str: Type of transport (http, console, file, kafka). Defaults to console. """
252+
transport_type: Optional[StrictStr] = None
253+
""" str: Type of transport (http, console, file, kafka). Defaults to None (uses OpenLineage SDK defaults). """
254254

255255
transport_url: Optional[StrictStr] = None
256256
""" str: URL for HTTP transport. Required when transport_type is 'http'. """
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Copyright 2026 The Feast Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from pathlib import Path
16+
17+
import pytest
18+
19+
# ---------------------------------------------------------------------------
20+
# Guard: skip entire module if openlineage-python is not installed
21+
# ---------------------------------------------------------------------------
22+
ol = pytest.importorskip(
23+
"openlineage.client", reason="openlineage-python not installed"
24+
)
25+
26+
from openlineage.client.transport.console import ConsoleTransport # noqa: E402
27+
from openlineage.client.transport.transform import TransformTransport # noqa: E402
28+
29+
from feast.openlineage.client import FeastOpenLineageClient # noqa: E402
30+
from feast.openlineage.config import OpenLineageConfig # noqa: E402
31+
32+
_TRANSFORM_YML = """\
33+
transport:
34+
type: transform
35+
transformer_class: openlineage.client.transport.transform.JobNamespaceReplaceTransformer
36+
transformer_properties:
37+
new_job_namespace: new_value
38+
transport:
39+
type: console
40+
"""
41+
42+
43+
def _write_openlineage_yml(tmp_path: Path, content: str = _TRANSFORM_YML) -> str:
44+
"""Write an openlineage.yml file and return its path."""
45+
yml = tmp_path / "openlineage.yml"
46+
yml.write_text(content)
47+
return str(yml)
48+
49+
50+
class TestDefaultConsoleTransport:
51+
"""When transport_type is None and there is no openlineage.yml,
52+
the OpenLineage SDK should fall back to ConsoleTransport."""
53+
54+
def test_default_config_uses_console_transport(
55+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
56+
) -> None:
57+
# Ensure no openlineage.yml is found by changing cwd to an empty dir
58+
monkeypatch.chdir(tmp_path)
59+
monkeypatch.delenv("OPENLINEAGE_CONFIG", raising=False)
60+
monkeypatch.delenv("OPENLINEAGE_URL", raising=False)
61+
monkeypatch.delenv("OPENLINEAGE_DISABLED", raising=False)
62+
63+
config = OpenLineageConfig(enabled=True) # transport_type defaults to None
64+
client = FeastOpenLineageClient(config=config)
65+
66+
assert client.is_enabled
67+
assert isinstance(client._client.transport, ConsoleTransport)
68+
69+
def test_default_from_dict_uses_console_transport(
70+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
71+
) -> None:
72+
monkeypatch.chdir(tmp_path)
73+
monkeypatch.delenv("OPENLINEAGE_CONFIG", raising=False)
74+
monkeypatch.delenv("OPENLINEAGE_URL", raising=False)
75+
monkeypatch.delenv("OPENLINEAGE_DISABLED", raising=False)
76+
77+
config = OpenLineageConfig.from_dict({"enabled": True})
78+
client = FeastOpenLineageClient(config=config)
79+
80+
assert client.is_enabled
81+
assert isinstance(client._client.transport, ConsoleTransport)
82+
83+
84+
class TestTransformTransportFromYml:
85+
def test_transform_yml_is_respected(
86+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
87+
) -> None:
88+
yml_path = _write_openlineage_yml(tmp_path)
89+
monkeypatch.setenv("OPENLINEAGE_CONFIG", yml_path)
90+
monkeypatch.delenv("OPENLINEAGE_URL", raising=False)
91+
monkeypatch.delenv("OPENLINEAGE_DISABLED", raising=False)
92+
93+
config = OpenLineageConfig(enabled=True) # transport_type=None
94+
client = FeastOpenLineageClient(config=config)
95+
96+
assert client.is_enabled
97+
assert isinstance(client._client.transport, TransformTransport)
98+
# The inner transport should be console
99+
assert isinstance(client._client.transport.transport, ConsoleTransport)
100+
101+
def test_transform_yml_in_cwd_is_respected(
102+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
103+
) -> None:
104+
monkeypatch.delenv("OPENLINEAGE_CONFIG", raising=False)
105+
monkeypatch.delenv("OPENLINEAGE_URL", raising=False)
106+
monkeypatch.delenv("OPENLINEAGE_DISABLED", raising=False)
107+
108+
# Write openlineage.yml in the dir we'll chdir to
109+
_write_openlineage_yml(tmp_path)
110+
monkeypatch.chdir(tmp_path)
111+
112+
config = OpenLineageConfig(enabled=True) # transport_type=None
113+
client = FeastOpenLineageClient(config=config)
114+
115+
assert client.is_enabled
116+
assert isinstance(client._client.transport, TransformTransport)
117+
118+
119+
class TestExplicitConfigOverridesYml:
120+
def test_explicit_console_ignores_yml_env(
121+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
122+
) -> None:
123+
yml_path = _write_openlineage_yml(tmp_path)
124+
monkeypatch.setenv("OPENLINEAGE_CONFIG", yml_path)
125+
monkeypatch.delenv("OPENLINEAGE_URL", raising=False)
126+
monkeypatch.delenv("OPENLINEAGE_DISABLED", raising=False)
127+
128+
config = OpenLineageConfig(enabled=True, transport_type="console")
129+
client = FeastOpenLineageClient(config=config)
130+
131+
assert client.is_enabled
132+
# Must be plain ConsoleTransport, NOT TransformTransport
133+
assert isinstance(client._client.transport, ConsoleTransport)
134+
assert not isinstance(client._client.transport, TransformTransport)
135+
136+
def test_explicit_console_ignores_yml_in_cwd(
137+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
138+
) -> None:
139+
monkeypatch.delenv("OPENLINEAGE_CONFIG", raising=False)
140+
monkeypatch.delenv("OPENLINEAGE_URL", raising=False)
141+
monkeypatch.delenv("OPENLINEAGE_DISABLED", raising=False)
142+
143+
_write_openlineage_yml(tmp_path)
144+
monkeypatch.chdir(tmp_path)
145+
146+
config = OpenLineageConfig(enabled=True, transport_type="console")
147+
client = FeastOpenLineageClient(config=config)
148+
149+
assert client.is_enabled
150+
assert isinstance(client._client.transport, ConsoleTransport)
151+
assert not isinstance(client._client.transport, TransformTransport)

skills/references/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ materialization:
258258
```yaml
259259
openlineage:
260260
enabled: true
261-
transport_type: http # http, console, file, kafka
261+
transport_type: http # http, console, file, kafka (omit to use OpenLineage SDK defaults)
262262
transport_url: http://marquez:5000
263263
transport_endpoint: api/v1/lineage
264264
namespace: feast

0 commit comments

Comments
 (0)