feldera 0.186.0__tar.gz → 0.219.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.
- {feldera-0.186.0 → feldera-0.219.0}/PKG-INFO +4 -3
- {feldera-0.186.0 → feldera-0.219.0}/README.md +2 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/pipeline.py +78 -18
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/_httprequests.py +2 -3
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/errors.py +2 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/feldera_client.py +64 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/PKG-INFO +4 -3
- {feldera-0.186.0 → feldera-0.219.0}/pyproject.toml +4 -5
- {feldera-0.186.0 → feldera-0.219.0}/feldera/__init__.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/_callback_runner.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/_helpers.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/enums.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/output_handler.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/pipeline_builder.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/__init__.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/_helpers.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/config.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/feldera_config.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/pipeline.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/sql_table.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/sql_view.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/runtime_config.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/stats.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/tests/test_datafusionize.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/testutils.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera/testutils_oidc.py +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/SOURCES.txt +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/dependency_links.txt +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/requires.txt +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/top_level.txt +0 -0
- {feldera-0.186.0 → feldera-0.219.0}/setup.cfg +0 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feldera
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.219.0
|
|
4
4
|
Summary: The feldera python client
|
|
5
5
|
Author-email: Feldera Team <dev@feldera.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://www.feldera.com
|
|
8
8
|
Project-URL: Documentation, https://docs.feldera.com/python
|
|
9
9
|
Project-URL: Repository, https://github.com/feldera/feldera
|
|
10
10
|
Project-URL: Issues, https://github.com/feldera/feldera/issues
|
|
11
11
|
Keywords: feldera,python
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Operating System :: OS Independent
|
|
15
14
|
Requires-Python: >=3.10
|
|
@@ -135,6 +134,8 @@ uv run python -m pytest tests/platform/
|
|
|
135
134
|
|
|
136
135
|
# Specific test file
|
|
137
136
|
uv run python -m pytest tests/platform/test_pipeline_crud.py
|
|
137
|
+
|
|
138
|
+
# Tip: add argument -x at the end for it to fail fast
|
|
138
139
|
```
|
|
139
140
|
|
|
140
141
|
For further information about the tests, please see `tests/README.md`.
|
|
@@ -111,6 +111,8 @@ uv run python -m pytest tests/platform/
|
|
|
111
111
|
|
|
112
112
|
# Specific test file
|
|
113
113
|
uv run python -m pytest tests/platform/test_pipeline_crud.py
|
|
114
|
+
|
|
115
|
+
# Tip: add argument -x at the end for it to fail fast
|
|
114
116
|
```
|
|
115
117
|
|
|
116
118
|
For further information about the tests, please see `tests/README.md`.
|
|
@@ -1,36 +1,35 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import pathlib
|
|
2
3
|
import time
|
|
4
|
+
from collections import deque
|
|
3
5
|
from datetime import datetime
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import pandas
|
|
6
|
+
from threading import Event
|
|
7
|
+
from typing import Any, Callable, Dict, Generator, List, Mapping, Optional
|
|
7
8
|
from uuid import UUID
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
from threading import Event
|
|
11
|
-
from collections import deque
|
|
10
|
+
import pandas
|
|
12
11
|
|
|
13
|
-
from feldera.
|
|
12
|
+
from feldera._callback_runner import CallbackRunner
|
|
13
|
+
from feldera._helpers import chunk_dataframe, ensure_dataframe_has_columns
|
|
14
14
|
from feldera.enums import (
|
|
15
15
|
BootstrapPolicy,
|
|
16
|
-
CompletionTokenStatus,
|
|
17
|
-
PipelineFieldSelector,
|
|
18
|
-
PipelineStatus,
|
|
19
|
-
ProgramStatus,
|
|
20
16
|
CheckpointStatus,
|
|
21
|
-
|
|
22
|
-
StorageStatus,
|
|
17
|
+
CompletionTokenStatus,
|
|
23
18
|
DeploymentDesiredStatus,
|
|
24
19
|
DeploymentResourcesDesiredStatus,
|
|
25
20
|
DeploymentResourcesStatus,
|
|
26
21
|
DeploymentRuntimeDesiredStatus,
|
|
27
22
|
DeploymentRuntimeStatus,
|
|
23
|
+
PipelineFieldSelector,
|
|
24
|
+
PipelineStatus,
|
|
25
|
+
ProgramStatus,
|
|
26
|
+
StorageStatus,
|
|
27
|
+
TransactionStatus,
|
|
28
28
|
)
|
|
29
|
-
from feldera.rest.pipeline import Pipeline as InnerPipeline
|
|
30
|
-
from feldera.rest.feldera_client import FelderaClient
|
|
31
|
-
from feldera._callback_runner import CallbackRunner
|
|
32
29
|
from feldera.output_handler import OutputHandler
|
|
33
|
-
from feldera.
|
|
30
|
+
from feldera.rest.errors import FelderaAPIError
|
|
31
|
+
from feldera.rest.feldera_client import FelderaClient
|
|
32
|
+
from feldera.rest.pipeline import Pipeline as InnerPipeline
|
|
34
33
|
from feldera.rest.sql_table import SQLTable
|
|
35
34
|
from feldera.rest.sql_view import SQLView
|
|
36
35
|
from feldera.runtime_config import RuntimeConfig
|
|
@@ -826,10 +825,11 @@ pipeline '{self.name}' to sync checkpoint '{uuid}'"""
|
|
|
826
825
|
|
|
827
826
|
resp = self.client.sync_checkpoint_status(self.name)
|
|
828
827
|
success = resp.get("success")
|
|
828
|
+
periodic = resp.get("periodic")
|
|
829
829
|
|
|
830
830
|
fail = resp.get("failure") or {}
|
|
831
831
|
|
|
832
|
-
if uuid == success:
|
|
832
|
+
if uuid == success or uuid == periodic:
|
|
833
833
|
return CheckpointStatus.Success
|
|
834
834
|
|
|
835
835
|
fail = resp.get("failure") or {}
|
|
@@ -844,6 +844,26 @@ pipeline '{self.name}' to sync checkpoint '{uuid}'"""
|
|
|
844
844
|
|
|
845
845
|
return CheckpointStatus.Unknown
|
|
846
846
|
|
|
847
|
+
def last_successful_checkpoint_sync(self) -> UUID:
|
|
848
|
+
"""
|
|
849
|
+
Returns the UUID of the last successfully synced checkpoint.
|
|
850
|
+
|
|
851
|
+
:return: The UUID of the last successfully synced checkpoint.
|
|
852
|
+
"""
|
|
853
|
+
|
|
854
|
+
resp = self.client.sync_checkpoint_status(self.name)
|
|
855
|
+
success = resp.get("success")
|
|
856
|
+
periodic = resp.get("periodic")
|
|
857
|
+
|
|
858
|
+
if success is None and periodic is None:
|
|
859
|
+
raise RuntimeError("no checkpoints have been synced yet")
|
|
860
|
+
elif success is None:
|
|
861
|
+
return UUID(periodic)
|
|
862
|
+
elif periodic is None:
|
|
863
|
+
return UUID(success)
|
|
864
|
+
else:
|
|
865
|
+
return max(UUID(success), UUID(periodic))
|
|
866
|
+
|
|
847
867
|
def query(self, query: str) -> Generator[Mapping[str, Any], None, None]:
|
|
848
868
|
"""
|
|
849
869
|
Executes an ad-hoc SQL query on this pipeline and returns a generator
|
|
@@ -1337,6 +1357,7 @@ pipeline '{self.name}' to sync checkpoint '{uuid}'"""
|
|
|
1337
1357
|
stats: bool = True,
|
|
1338
1358
|
pipeline_config: bool = True,
|
|
1339
1359
|
system_config: bool = True,
|
|
1360
|
+
dataflow_graph: bool = True,
|
|
1340
1361
|
) -> bytes:
|
|
1341
1362
|
"""
|
|
1342
1363
|
Generate a support bundle containing diagnostic information from this pipeline.
|
|
@@ -1354,6 +1375,7 @@ pipeline '{self.name}' to sync checkpoint '{uuid}'"""
|
|
|
1354
1375
|
:param stats: Whether to collect stats data (default: True)
|
|
1355
1376
|
:param pipeline_config: Whether to collect pipeline configuration data (default: True)
|
|
1356
1377
|
:param system_config: Whether to collect system configuration data (default: True)
|
|
1378
|
+
:param dataflow_graph: Whether to collect dataflow graph (default: True)
|
|
1357
1379
|
:return: The support bundle as bytes (ZIP archive)
|
|
1358
1380
|
:raises FelderaAPIError: If the pipeline does not exist or if there's an error
|
|
1359
1381
|
"""
|
|
@@ -1374,6 +1396,8 @@ pipeline '{self.name}' to sync checkpoint '{uuid}'"""
|
|
|
1374
1396
|
params["pipeline_config"] = "false"
|
|
1375
1397
|
if not system_config:
|
|
1376
1398
|
params["system_config"] = "false"
|
|
1399
|
+
if not dataflow_graph:
|
|
1400
|
+
params["dataflow_graph"] = "false"
|
|
1377
1401
|
|
|
1378
1402
|
support_bundle_bytes = self.client.get_pipeline_support_bundle(
|
|
1379
1403
|
self.name, params=params
|
|
@@ -1393,6 +1417,42 @@ pipeline '{self.name}' to sync checkpoint '{uuid}'"""
|
|
|
1393
1417
|
|
|
1394
1418
|
return support_bundle_bytes
|
|
1395
1419
|
|
|
1420
|
+
def start_samply_profile(self, duration: int = 30):
|
|
1421
|
+
"""
|
|
1422
|
+
Starts profiling this pipeline using samply.
|
|
1423
|
+
|
|
1424
|
+
:param duration: The duration of the profile in seconds (default: 30)
|
|
1425
|
+
:raises FelderaAPIError: If the pipeline does not exist or if there's an error
|
|
1426
|
+
"""
|
|
1427
|
+
|
|
1428
|
+
self.client.start_samply_profile(self.name, duration)
|
|
1429
|
+
|
|
1430
|
+
def get_samply_profile(self, output_path: Optional[str] = None) -> bytes:
|
|
1431
|
+
"""
|
|
1432
|
+
Returns the gzip file of the samply profile as bytes.
|
|
1433
|
+
|
|
1434
|
+
The gzip file contains the samply profile that can be inspected by the samply tool.
|
|
1435
|
+
|
|
1436
|
+
:param output_path: Optional path to save the samply profile file. If None,
|
|
1437
|
+
the samply profile is only returned as bytes.
|
|
1438
|
+
:return: The samply profile as bytes (GZIP file)
|
|
1439
|
+
:raises FelderaAPIError: If the pipeline does not exist or if there's an error
|
|
1440
|
+
"""
|
|
1441
|
+
|
|
1442
|
+
samply_profile_bytes = self.client.get_samply_profile(self.name)
|
|
1443
|
+
|
|
1444
|
+
if output_path is not None:
|
|
1445
|
+
path = pathlib.Path(output_path)
|
|
1446
|
+
|
|
1447
|
+
if path.suffix != ".gz":
|
|
1448
|
+
path = path.with_suffix(".gz")
|
|
1449
|
+
with open(path, "wb") as f:
|
|
1450
|
+
f.write(samply_profile_bytes)
|
|
1451
|
+
|
|
1452
|
+
print(f"Samply profile written to {path}")
|
|
1453
|
+
|
|
1454
|
+
return samply_profile_bytes
|
|
1455
|
+
|
|
1396
1456
|
def generate_completion_token(self, table_name: str, connector_name: str) -> str:
|
|
1397
1457
|
"""
|
|
1398
1458
|
Returns a completion token that can be passed to :meth:`.Pipeline.completion_token_status` to
|
|
@@ -50,9 +50,8 @@ class HttpRequests:
|
|
|
50
50
|
|
|
51
51
|
if response.status_code == 200:
|
|
52
52
|
health_data = response.json()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return runner_healthy and compiler_healthy
|
|
53
|
+
all_healthy = health_data.get("all_healthy", False)
|
|
54
|
+
return all_healthy
|
|
56
55
|
else:
|
|
57
56
|
logging.warning(
|
|
58
57
|
f"Health check returned status {response.status_code}. The instance might be in the process of being upgraded. Waiting to see if it recovers."
|
|
@@ -39,6 +39,8 @@ class FelderaAPIError(FelderaError):
|
|
|
39
39
|
if self.message:
|
|
40
40
|
err_msg += f"\nMessage: {self.message}"
|
|
41
41
|
self.details = json_data.get("details")
|
|
42
|
+
if self.details:
|
|
43
|
+
err_msg += f"\nDetails: {self.details}"
|
|
42
44
|
except Exception:
|
|
43
45
|
self.message = request.text
|
|
44
46
|
err_msg += request.text
|
|
@@ -1234,6 +1234,46 @@ Reason: The pipeline is in a STOPPED state due to the following error:
|
|
|
1234
1234
|
|
|
1235
1235
|
return buffer
|
|
1236
1236
|
|
|
1237
|
+
def start_samply_profile(self, pipeline_name: str, duration: int):
|
|
1238
|
+
"""
|
|
1239
|
+
Start the profiling this pipeline with samply.
|
|
1240
|
+
|
|
1241
|
+
:param pipeline_name: The name of the pipeline
|
|
1242
|
+
:param duration: The duration of the profile in seconds (default: 30)
|
|
1243
|
+
:raises FelderaAPIError: If the pipeline does not exist or if there's an error
|
|
1244
|
+
"""
|
|
1245
|
+
|
|
1246
|
+
if duration <= 0:
|
|
1247
|
+
raise ValueError("Duration must be a positive integer")
|
|
1248
|
+
|
|
1249
|
+
params = {"duration_secs": duration}
|
|
1250
|
+
|
|
1251
|
+
self.http.post(
|
|
1252
|
+
path=f"/pipelines/{pipeline_name}/samply_profile",
|
|
1253
|
+
params=params,
|
|
1254
|
+
)
|
|
1255
|
+
|
|
1256
|
+
def get_samply_profile(self, pipeline_name: str) -> bytes:
|
|
1257
|
+
"""
|
|
1258
|
+
Get the most recent available samply profile for the given pipeline.
|
|
1259
|
+
|
|
1260
|
+
:param pipeline_name: The name of the pipeline
|
|
1261
|
+
:return: The samply profile as bytes (GZIP file)
|
|
1262
|
+
:raises FelderaAPIError: If the pipeline does not exist or if there's an error
|
|
1263
|
+
"""
|
|
1264
|
+
|
|
1265
|
+
resp = self.http.get(
|
|
1266
|
+
path=f"/pipelines/{pipeline_name}/samply_profile",
|
|
1267
|
+
stream=True,
|
|
1268
|
+
)
|
|
1269
|
+
|
|
1270
|
+
buffer = b""
|
|
1271
|
+
for chunk in resp.iter_content(chunk_size=1024):
|
|
1272
|
+
if chunk:
|
|
1273
|
+
buffer += chunk
|
|
1274
|
+
|
|
1275
|
+
return buffer
|
|
1276
|
+
|
|
1237
1277
|
def generate_completion_token(
|
|
1238
1278
|
self, pipeline_name: str, table_name: str, connector_name: str
|
|
1239
1279
|
) -> str:
|
|
@@ -1261,3 +1301,27 @@ Reason: The pipeline is in a STOPPED state due to the following error:
|
|
|
1261
1301
|
)
|
|
1262
1302
|
|
|
1263
1303
|
return token
|
|
1304
|
+
|
|
1305
|
+
def get_cluster_events(self) -> list[dict]:
|
|
1306
|
+
"""
|
|
1307
|
+
Retrieves all cluster events (status fields only).
|
|
1308
|
+
|
|
1309
|
+
:returns: List of cluster events.
|
|
1310
|
+
"""
|
|
1311
|
+
return self.http.get(path="/cluster/events")
|
|
1312
|
+
|
|
1313
|
+
def get_cluster_event(self, event_id: str, selector: str = "status") -> dict:
|
|
1314
|
+
"""
|
|
1315
|
+
Retrieves a specific cluster event.
|
|
1316
|
+
|
|
1317
|
+
:param event_id: Identifier (UUID) of the event to retrieve, or `latest` for the latest event.
|
|
1318
|
+
:param selector: (Optional) Limit the returned fields. Valid values: "all", "status" (default).
|
|
1319
|
+
|
|
1320
|
+
:returns: Event (fields limited based on selector).
|
|
1321
|
+
"""
|
|
1322
|
+
# Selector of fields
|
|
1323
|
+
if selector not in ["all", "status"]:
|
|
1324
|
+
raise ValueError(f"invalid selector: {selector}")
|
|
1325
|
+
|
|
1326
|
+
# Issue request and return response
|
|
1327
|
+
return self.http.get(path=f"/cluster/events/{event_id}?selector={selector}")
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: feldera
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.219.0
|
|
4
4
|
Summary: The feldera python client
|
|
5
5
|
Author-email: Feldera Team <dev@feldera.com>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://www.feldera.com
|
|
8
8
|
Project-URL: Documentation, https://docs.feldera.com/python
|
|
9
9
|
Project-URL: Repository, https://github.com/feldera/feldera
|
|
10
10
|
Project-URL: Issues, https://github.com/feldera/feldera/issues
|
|
11
11
|
Keywords: feldera,python
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Operating System :: OS Independent
|
|
15
14
|
Requires-Python: >=3.10
|
|
@@ -135,6 +134,8 @@ uv run python -m pytest tests/platform/
|
|
|
135
134
|
|
|
136
135
|
# Specific test file
|
|
137
136
|
uv run python -m pytest tests/platform/test_pipeline_crud.py
|
|
137
|
+
|
|
138
|
+
# Tip: add argument -x at the end for it to fail fast
|
|
138
139
|
```
|
|
139
140
|
|
|
140
141
|
For further information about the tests, please see `tests/README.md`.
|
|
@@ -6,8 +6,8 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
name = "feldera"
|
|
7
7
|
readme = "README.md"
|
|
8
8
|
description = "The feldera python client"
|
|
9
|
-
version = "0.
|
|
10
|
-
license =
|
|
9
|
+
version = "0.219.0"
|
|
10
|
+
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
12
12
|
authors = [
|
|
13
13
|
{ "name" = "Feldera Team", "email" = "dev@feldera.com" },
|
|
@@ -17,7 +17,6 @@ keywords = [
|
|
|
17
17
|
"python",
|
|
18
18
|
]
|
|
19
19
|
classifiers = [
|
|
20
|
-
"License :: OSI Approved :: MIT License",
|
|
21
20
|
"Programming Language :: Python :: 3.10",
|
|
22
21
|
"Operating System :: OS Independent",
|
|
23
22
|
]
|
|
@@ -36,8 +35,8 @@ Documentation = "https://docs.feldera.com/python"
|
|
|
36
35
|
Repository = "https://github.com/feldera/feldera"
|
|
37
36
|
Issues = "https://github.com/feldera/feldera/issues"
|
|
38
37
|
|
|
39
|
-
[
|
|
40
|
-
dev
|
|
38
|
+
[dependency-groups]
|
|
39
|
+
dev = [
|
|
41
40
|
"pytest-timeout>=2.3.1",
|
|
42
41
|
"pytest-xdist>=3.8.0",
|
|
43
42
|
"pytest>=8.3.5",
|
|
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
|
|
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
|