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.
Files changed (31) hide show
  1. {feldera-0.186.0 → feldera-0.219.0}/PKG-INFO +4 -3
  2. {feldera-0.186.0 → feldera-0.219.0}/README.md +2 -0
  3. {feldera-0.186.0 → feldera-0.219.0}/feldera/pipeline.py +78 -18
  4. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/_httprequests.py +2 -3
  5. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/errors.py +2 -0
  6. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/feldera_client.py +64 -0
  7. {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/PKG-INFO +4 -3
  8. {feldera-0.186.0 → feldera-0.219.0}/pyproject.toml +4 -5
  9. {feldera-0.186.0 → feldera-0.219.0}/feldera/__init__.py +0 -0
  10. {feldera-0.186.0 → feldera-0.219.0}/feldera/_callback_runner.py +0 -0
  11. {feldera-0.186.0 → feldera-0.219.0}/feldera/_helpers.py +0 -0
  12. {feldera-0.186.0 → feldera-0.219.0}/feldera/enums.py +0 -0
  13. {feldera-0.186.0 → feldera-0.219.0}/feldera/output_handler.py +0 -0
  14. {feldera-0.186.0 → feldera-0.219.0}/feldera/pipeline_builder.py +0 -0
  15. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/__init__.py +0 -0
  16. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/_helpers.py +0 -0
  17. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/config.py +0 -0
  18. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/feldera_config.py +0 -0
  19. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/pipeline.py +0 -0
  20. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/sql_table.py +0 -0
  21. {feldera-0.186.0 → feldera-0.219.0}/feldera/rest/sql_view.py +0 -0
  22. {feldera-0.186.0 → feldera-0.219.0}/feldera/runtime_config.py +0 -0
  23. {feldera-0.186.0 → feldera-0.219.0}/feldera/stats.py +0 -0
  24. {feldera-0.186.0 → feldera-0.219.0}/feldera/tests/test_datafusionize.py +0 -0
  25. {feldera-0.186.0 → feldera-0.219.0}/feldera/testutils.py +0 -0
  26. {feldera-0.186.0 → feldera-0.219.0}/feldera/testutils_oidc.py +0 -0
  27. {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/SOURCES.txt +0 -0
  28. {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/dependency_links.txt +0 -0
  29. {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/requires.txt +0 -0
  30. {feldera-0.186.0 → feldera-0.219.0}/feldera.egg-info/top_level.txt +0 -0
  31. {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.186.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 pathlib
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
- from typing import List, Dict, Callable, Optional, Generator, Mapping, Any
10
- from threading import Event
11
- from collections import deque
10
+ import pandas
12
11
 
13
- from feldera.rest.errors import FelderaAPIError
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
- TransactionStatus,
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._helpers import ensure_dataframe_has_columns, chunk_dataframe
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
- runner_healthy = health_data.get("runner", {}).get("healthy", False)
54
- compiler_healthy = health_data.get("compiler", {}).get("healthy", False)
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.186.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.186.0"
10
- license = { text = "MIT" }
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
- [tool.uv]
40
- dev-dependencies = [
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