feldera 0.172.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 (35) hide show
  1. feldera-0.219.0/PKG-INFO +164 -0
  2. feldera-0.219.0/README.md +141 -0
  3. {feldera-0.172.0 → feldera-0.219.0}/feldera/__init__.py +2 -2
  4. {feldera-0.172.0 → feldera-0.219.0}/feldera/pipeline.py +78 -18
  5. {feldera-0.172.0 → feldera-0.219.0}/feldera/pipeline_builder.py +11 -7
  6. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/_helpers.py +1 -1
  7. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/_httprequests.py +364 -232
  8. feldera-0.219.0/feldera/rest/config.py +44 -0
  9. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/errors.py +2 -0
  10. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/feldera_client.py +123 -33
  11. {feldera-0.172.0 → feldera-0.219.0}/feldera/testutils.py +1 -10
  12. feldera-0.219.0/feldera.egg-info/PKG-INFO +164 -0
  13. {feldera-0.172.0 → feldera-0.219.0}/pyproject.toml +4 -6
  14. feldera-0.172.0/PKG-INFO +0 -97
  15. feldera-0.172.0/README.md +0 -73
  16. feldera-0.172.0/feldera/rest/config.py +0 -51
  17. feldera-0.172.0/feldera.egg-info/PKG-INFO +0 -97
  18. {feldera-0.172.0 → feldera-0.219.0}/feldera/_callback_runner.py +0 -0
  19. {feldera-0.172.0 → feldera-0.219.0}/feldera/_helpers.py +0 -0
  20. {feldera-0.172.0 → feldera-0.219.0}/feldera/enums.py +0 -0
  21. {feldera-0.172.0 → feldera-0.219.0}/feldera/output_handler.py +0 -0
  22. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/__init__.py +0 -0
  23. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/feldera_config.py +0 -0
  24. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/pipeline.py +0 -0
  25. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/sql_table.py +0 -0
  26. {feldera-0.172.0 → feldera-0.219.0}/feldera/rest/sql_view.py +0 -0
  27. {feldera-0.172.0 → feldera-0.219.0}/feldera/runtime_config.py +0 -0
  28. {feldera-0.172.0 → feldera-0.219.0}/feldera/stats.py +0 -0
  29. {feldera-0.172.0 → feldera-0.219.0}/feldera/tests/test_datafusionize.py +0 -0
  30. {feldera-0.172.0 → feldera-0.219.0}/feldera/testutils_oidc.py +0 -0
  31. {feldera-0.172.0 → feldera-0.219.0}/feldera.egg-info/SOURCES.txt +0 -0
  32. {feldera-0.172.0 → feldera-0.219.0}/feldera.egg-info/dependency_links.txt +0 -0
  33. {feldera-0.172.0 → feldera-0.219.0}/feldera.egg-info/requires.txt +0 -0
  34. {feldera-0.172.0 → feldera-0.219.0}/feldera.egg-info/top_level.txt +0 -0
  35. {feldera-0.172.0 → feldera-0.219.0}/setup.cfg +0 -0
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: feldera
3
+ Version: 0.219.0
4
+ Summary: The feldera python client
5
+ Author-email: Feldera Team <dev@feldera.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://www.feldera.com
8
+ Project-URL: Documentation, https://docs.feldera.com/python
9
+ Project-URL: Repository, https://github.com/feldera/feldera
10
+ Project-URL: Issues, https://github.com/feldera/feldera/issues
11
+ Keywords: feldera,python
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: requests
17
+ Requires-Dist: pandas>=2.1.2
18
+ Requires-Dist: typing-extensions
19
+ Requires-Dist: numpy>=2.2.4
20
+ Requires-Dist: pretty-errors
21
+ Requires-Dist: ruff>=0.6.9
22
+ Requires-Dist: PyJWT>=2.8.0
23
+
24
+ # Feldera Python SDK
25
+
26
+ The `feldera` Python package is the Python client for the Feldera HTTP API.
27
+
28
+ The Python SDK documentation is available at: https://docs.feldera.com/python
29
+
30
+ ## Getting started
31
+
32
+ ### Installation
33
+
34
+ ```bash
35
+ uv pip install feldera
36
+ ```
37
+
38
+ ### Example usage
39
+
40
+ The Python client interacts with the API server of the Feldera instance.
41
+
42
+ ```python
43
+ # File: example.py
44
+ from feldera import FelderaClient, PipelineBuilder, Pipeline
45
+
46
+ # Instantiate client
47
+ client = FelderaClient() # Default: http://localhost:8080 without authentication
48
+ # client = FelderaClient(url="https://localhost:8080", api_key="apikey:...", requests_verify="/path/to/tls.crt")
49
+
50
+ # (Re)create pipeline
51
+ name = "example"
52
+ sql = """
53
+ CREATE TABLE t1 (i1 INT) WITH ('materialized' = 'true');
54
+ CREATE MATERIALIZED VIEW v1 AS SELECT * FROM t1;
55
+ """
56
+ print("(Re)creating pipeline...")
57
+ pipeline = PipelineBuilder(client, name, sql).create_or_replace()
58
+ pipeline.start()
59
+ print(f"Pipeline status: {pipeline.status()}")
60
+ pipeline.pause()
61
+ print(f"Pipeline status: {pipeline.status()}")
62
+ pipeline.stop(force=True)
63
+
64
+ # Find existing pipeline
65
+ pipeline = Pipeline.get(name, client)
66
+ pipeline.start()
67
+ print(f"Pipeline status: {pipeline.status()}")
68
+ pipeline.stop(force=True)
69
+ pipeline.clear_storage()
70
+ ```
71
+
72
+ Run using:
73
+ ```bash
74
+ uv run python example.py
75
+ ```
76
+
77
+ ### Environment variables
78
+
79
+ Some default parameter values in the Python SDK can be overridden via environment variables.
80
+
81
+ **Environment variables for `FelderaClient(...)`**
82
+
83
+ ```bash
84
+ export FELDERA_HOST="https://localhost:8080" # Overrides default for `url`
85
+ export FELDERA_API_KEY="apikey:..." # Overrides default for `api_key`
86
+
87
+ # The following together override default for `requests_verify`
88
+ # export FELDERA_TLS_INSECURE="false" # If set to "1", "true" or "yes" (all case-insensitive), disables TLS certificate verification
89
+ # export FELDERA_HTTPS_TLS_CERT="/path/to/tls.crt" # Custom TLS certificate
90
+ ```
91
+
92
+ **Environment variables for `PipelineBuilder(...)`**
93
+
94
+ ```bash
95
+ export FELDERA_RUNTIME_VERSION="..." # Overrides default for `runtime_version`
96
+ ```
97
+
98
+ ## Development
99
+
100
+ Development assumes you have cloned the Feldera code repository.
101
+
102
+ ### Installation
103
+
104
+ ```bash
105
+ cd python
106
+ # Optional: create and activate virtual environment if you don't have one
107
+ uv venv
108
+ source .venv/bin/activate
109
+ # Install in editable mode
110
+ uv pip install -e .
111
+ ```
112
+
113
+ ### Formatting
114
+
115
+ Formatting requires the `ruff` package: `uv pip install ruff`
116
+
117
+ ```bash
118
+ cd python
119
+ ruff check
120
+ ruff format
121
+ ```
122
+
123
+ ### Tests
124
+
125
+ Running the test requires the `pytest` package: `uv pip install pytest`
126
+
127
+ ```bash
128
+ # All tests
129
+ cd python
130
+ uv run python -m pytest tests/
131
+
132
+ # Specific tests directory
133
+ uv run python -m pytest tests/platform/
134
+
135
+ # Specific test file
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
139
+ ```
140
+
141
+ For further information about the tests, please see `tests/README.md`.
142
+
143
+ ### Documentation
144
+
145
+ Building documentation requires the `sphinx` package: `uv pip install sphinx`
146
+
147
+ ```bash
148
+ cd python/docs
149
+ sphinx-apidoc -o . ../feldera
150
+ make html
151
+ make clean # Cleanup afterwards
152
+ ```
153
+
154
+ ### Installation from GitHub
155
+
156
+ Latest `main` branch:
157
+ ```bash
158
+ uv pip install git+https://github.com/feldera/feldera#subdirectory=python
159
+ ```
160
+
161
+ Different branch (replace `BRANCH_NAME`):
162
+ ```bash
163
+ uv pip install git+https://github.com/feldera/feldera@BRANCH_NAME#subdirectory=python
164
+ ```
@@ -0,0 +1,141 @@
1
+ # Feldera Python SDK
2
+
3
+ The `feldera` Python package is the Python client for the Feldera HTTP API.
4
+
5
+ The Python SDK documentation is available at: https://docs.feldera.com/python
6
+
7
+ ## Getting started
8
+
9
+ ### Installation
10
+
11
+ ```bash
12
+ uv pip install feldera
13
+ ```
14
+
15
+ ### Example usage
16
+
17
+ The Python client interacts with the API server of the Feldera instance.
18
+
19
+ ```python
20
+ # File: example.py
21
+ from feldera import FelderaClient, PipelineBuilder, Pipeline
22
+
23
+ # Instantiate client
24
+ client = FelderaClient() # Default: http://localhost:8080 without authentication
25
+ # client = FelderaClient(url="https://localhost:8080", api_key="apikey:...", requests_verify="/path/to/tls.crt")
26
+
27
+ # (Re)create pipeline
28
+ name = "example"
29
+ sql = """
30
+ CREATE TABLE t1 (i1 INT) WITH ('materialized' = 'true');
31
+ CREATE MATERIALIZED VIEW v1 AS SELECT * FROM t1;
32
+ """
33
+ print("(Re)creating pipeline...")
34
+ pipeline = PipelineBuilder(client, name, sql).create_or_replace()
35
+ pipeline.start()
36
+ print(f"Pipeline status: {pipeline.status()}")
37
+ pipeline.pause()
38
+ print(f"Pipeline status: {pipeline.status()}")
39
+ pipeline.stop(force=True)
40
+
41
+ # Find existing pipeline
42
+ pipeline = Pipeline.get(name, client)
43
+ pipeline.start()
44
+ print(f"Pipeline status: {pipeline.status()}")
45
+ pipeline.stop(force=True)
46
+ pipeline.clear_storage()
47
+ ```
48
+
49
+ Run using:
50
+ ```bash
51
+ uv run python example.py
52
+ ```
53
+
54
+ ### Environment variables
55
+
56
+ Some default parameter values in the Python SDK can be overridden via environment variables.
57
+
58
+ **Environment variables for `FelderaClient(...)`**
59
+
60
+ ```bash
61
+ export FELDERA_HOST="https://localhost:8080" # Overrides default for `url`
62
+ export FELDERA_API_KEY="apikey:..." # Overrides default for `api_key`
63
+
64
+ # The following together override default for `requests_verify`
65
+ # export FELDERA_TLS_INSECURE="false" # If set to "1", "true" or "yes" (all case-insensitive), disables TLS certificate verification
66
+ # export FELDERA_HTTPS_TLS_CERT="/path/to/tls.crt" # Custom TLS certificate
67
+ ```
68
+
69
+ **Environment variables for `PipelineBuilder(...)`**
70
+
71
+ ```bash
72
+ export FELDERA_RUNTIME_VERSION="..." # Overrides default for `runtime_version`
73
+ ```
74
+
75
+ ## Development
76
+
77
+ Development assumes you have cloned the Feldera code repository.
78
+
79
+ ### Installation
80
+
81
+ ```bash
82
+ cd python
83
+ # Optional: create and activate virtual environment if you don't have one
84
+ uv venv
85
+ source .venv/bin/activate
86
+ # Install in editable mode
87
+ uv pip install -e .
88
+ ```
89
+
90
+ ### Formatting
91
+
92
+ Formatting requires the `ruff` package: `uv pip install ruff`
93
+
94
+ ```bash
95
+ cd python
96
+ ruff check
97
+ ruff format
98
+ ```
99
+
100
+ ### Tests
101
+
102
+ Running the test requires the `pytest` package: `uv pip install pytest`
103
+
104
+ ```bash
105
+ # All tests
106
+ cd python
107
+ uv run python -m pytest tests/
108
+
109
+ # Specific tests directory
110
+ uv run python -m pytest tests/platform/
111
+
112
+ # Specific test file
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
116
+ ```
117
+
118
+ For further information about the tests, please see `tests/README.md`.
119
+
120
+ ### Documentation
121
+
122
+ Building documentation requires the `sphinx` package: `uv pip install sphinx`
123
+
124
+ ```bash
125
+ cd python/docs
126
+ sphinx-apidoc -o . ../feldera
127
+ make html
128
+ make clean # Cleanup afterwards
129
+ ```
130
+
131
+ ### Installation from GitHub
132
+
133
+ Latest `main` branch:
134
+ ```bash
135
+ uv pip install git+https://github.com/feldera/feldera#subdirectory=python
136
+ ```
137
+
138
+ Different branch (replace `BRANCH_NAME`):
139
+ ```bash
140
+ uv pip install git+https://github.com/feldera/feldera@BRANCH_NAME#subdirectory=python
141
+ ```
@@ -1,9 +1,9 @@
1
1
  from feldera.rest.feldera_client import FelderaClient as FelderaClient
2
2
  from feldera.pipeline import Pipeline as Pipeline
3
3
  from feldera.pipeline_builder import PipelineBuilder as PipelineBuilder
4
- from feldera.rest._helpers import client_version
4
+ from feldera.rest._helpers import determine_client_version
5
5
 
6
- __version__ = client_version()
6
+ __version__ = determine_client_version()
7
7
 
8
8
  import pretty_errors
9
9
 
@@ -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
@@ -1,12 +1,12 @@
1
1
  import os
2
2
  from typing import Optional
3
3
 
4
+ from feldera.enums import CompilationProfile, PipelineFieldSelector
5
+ from feldera.pipeline import Pipeline
6
+ from feldera.rest.errors import FelderaAPIError
4
7
  from feldera.rest.feldera_client import FelderaClient
5
8
  from feldera.rest.pipeline import Pipeline as InnerPipeline
6
- from feldera.pipeline import Pipeline
7
- from feldera.enums import CompilationProfile, PipelineFieldSelector
8
9
  from feldera.runtime_config import RuntimeConfig
9
- from feldera.rest.errors import FelderaAPIError
10
10
 
11
11
 
12
12
  class PipelineBuilder:
@@ -49,10 +49,11 @@ class PipelineBuilder:
49
49
  "FELDERA_RUNTIME_VERSION", runtime_version
50
50
  )
51
51
 
52
- def create(self) -> Pipeline:
52
+ def create(self, wait: bool = True) -> Pipeline:
53
53
  """
54
54
  Create the pipeline if it does not exist.
55
55
 
56
+ :param wait: Whether to wait for the pipeline to be created. True by default
56
57
  :return: The created pipeline
57
58
  """
58
59
 
@@ -82,17 +83,20 @@ class PipelineBuilder:
82
83
  runtime_config=self.runtime_config.to_dict(),
83
84
  )
84
85
 
85
- inner = self.client.create_pipeline(inner)
86
+ inner = self.client.create_pipeline(inner, wait=wait)
86
87
  pipeline = Pipeline(self.client)
87
88
  pipeline._inner = inner
88
89
 
89
90
  return pipeline
90
91
 
91
- def create_or_replace(self) -> Pipeline:
92
+ def create_or_replace(self, wait: bool = True) -> Pipeline:
92
93
  """
93
94
  Creates a pipeline if it does not exist and replaces it if it exists.
94
95
 
95
96
  If the pipeline exists and is running, it will be stopped and replaced.
97
+
98
+ :param wait: Whether to wait for the pipeline to be created. True by default
99
+ :return: The created pipeline
96
100
  """
97
101
 
98
102
  if self.name is None or self.sql is None:
@@ -121,7 +125,7 @@ class PipelineBuilder:
121
125
  runtime_config=self.runtime_config.to_dict(),
122
126
  )
123
127
 
124
- inner = self.client.create_or_update_pipeline(inner)
128
+ inner = self.client.create_or_update_pipeline(inner, wait=wait)
125
129
  pipeline = Pipeline(self.client)
126
130
  pipeline._inner = inner
127
131
 
@@ -2,7 +2,7 @@ import logging
2
2
  import os
3
3
 
4
4
 
5
- def client_version() -> str:
5
+ def determine_client_version() -> str:
6
6
  from importlib.metadata import version, PackageNotFoundError
7
7
 
8
8
  try: