feldera 0.118.0__tar.gz → 0.179.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.
Potentially problematic release.
This version of feldera might be problematic. Click here for more details.
- feldera-0.179.0/PKG-INFO +97 -0
- feldera-0.179.0/README.md +73 -0
- feldera-0.179.0/feldera/_callback_runner.py +95 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/_helpers.py +8 -2
- {feldera-0.118.0 → feldera-0.179.0}/feldera/enums.py +145 -92
- {feldera-0.118.0 → feldera-0.179.0}/feldera/output_handler.py +16 -4
- {feldera-0.118.0 → feldera-0.179.0}/feldera/pipeline.py +514 -148
- {feldera-0.118.0 → feldera-0.179.0}/feldera/pipeline_builder.py +15 -8
- feldera-0.179.0/feldera/rest/_helpers.py +40 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/_httprequests.py +43 -20
- feldera-0.179.0/feldera/rest/config.py +51 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/errors.py +16 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/feldera_client.py +507 -136
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/pipeline.py +15 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/runtime_config.py +4 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/stats.py +8 -5
- feldera-0.179.0/feldera/tests/test_datafusionize.py +38 -0
- feldera-0.179.0/feldera/testutils.py +390 -0
- feldera-0.179.0/feldera/testutils_oidc.py +368 -0
- feldera-0.179.0/feldera.egg-info/PKG-INFO +97 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera.egg-info/SOURCES.txt +3 -5
- {feldera-0.118.0 → feldera-0.179.0}/feldera.egg-info/requires.txt +1 -0
- {feldera-0.118.0 → feldera-0.179.0}/pyproject.toml +7 -2
- feldera-0.118.0/PKG-INFO +0 -152
- feldera-0.118.0/README.md +0 -129
- feldera-0.118.0/feldera/_callback_runner.py +0 -119
- feldera-0.118.0/feldera/rest/_helpers.py +0 -9
- feldera-0.118.0/feldera/rest/config.py +0 -30
- feldera-0.118.0/feldera.egg-info/PKG-INFO +0 -152
- feldera-0.118.0/tests/test_pipeline_builder.py +0 -53
- feldera-0.118.0/tests/test_shared_pipeline0.py +0 -565
- feldera-0.118.0/tests/test_shared_pipeline1.py +0 -124
- feldera-0.118.0/tests/test_shared_pipeline_stress.py +0 -26
- feldera-0.118.0/tests/test_udf.py +0 -311
- {feldera-0.118.0 → feldera-0.179.0}/feldera/__init__.py +0 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/__init__.py +0 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/feldera_config.py +0 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/sql_table.py +0 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera/rest/sql_view.py +0 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera.egg-info/dependency_links.txt +0 -0
- {feldera-0.118.0 → feldera-0.179.0}/feldera.egg-info/top_level.txt +0 -0
- {feldera-0.118.0 → feldera-0.179.0}/setup.cfg +0 -0
feldera-0.179.0/PKG-INFO
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: feldera
|
|
3
|
+
Version: 0.179.0
|
|
4
|
+
Summary: The feldera python client
|
|
5
|
+
Author-email: Feldera Team <dev@feldera.com>
|
|
6
|
+
License: 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: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: requests
|
|
18
|
+
Requires-Dist: pandas>=2.1.2
|
|
19
|
+
Requires-Dist: typing-extensions
|
|
20
|
+
Requires-Dist: numpy>=2.2.4
|
|
21
|
+
Requires-Dist: pretty-errors
|
|
22
|
+
Requires-Dist: ruff>=0.6.9
|
|
23
|
+
Requires-Dist: PyJWT>=2.8.0
|
|
24
|
+
|
|
25
|
+
# Feldera Python SDK
|
|
26
|
+
|
|
27
|
+
Feldera Python is the Feldera SDK for Python developers.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv pip install feldera
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Installing from Github
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv pip install git+https://github.com/feldera/feldera#subdirectory=python
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Similarly, to install from a specific branch:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uv pip install git+https://github.com/feldera/feldera@{BRANCH_NAME}#subdirectory=python
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Replace `{BRANCH_NAME}` with the name of the branch you want to install from.
|
|
48
|
+
|
|
49
|
+
### Installing from Local Directory
|
|
50
|
+
|
|
51
|
+
If you have cloned the Feldera repo, you can install the python SDK as follows:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# the Feldera Python SDK is present inside the python/ directory
|
|
55
|
+
cd python
|
|
56
|
+
# If you don't have a virtual environment, create one
|
|
57
|
+
uv venv
|
|
58
|
+
source .venv/activate
|
|
59
|
+
# Install the SDK in editable mode
|
|
60
|
+
uv pip install .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Documentation
|
|
64
|
+
|
|
65
|
+
The Python SDK documentation is available at
|
|
66
|
+
[Feldera Python SDK Docs](https://docs.feldera.com/python).
|
|
67
|
+
|
|
68
|
+
To build the html documentation run:
|
|
69
|
+
|
|
70
|
+
Ensure that you have sphinx installed. If not, install it using `uv pip install sphinx`.
|
|
71
|
+
|
|
72
|
+
Then run the following commands:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
cd docs
|
|
76
|
+
sphinx-apidoc -o . ../feldera
|
|
77
|
+
make html
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
To clean the build, run `make clean`.
|
|
81
|
+
|
|
82
|
+
## Linting and formatting
|
|
83
|
+
|
|
84
|
+
Use [Ruff] to run the lint checks that will be executed by the
|
|
85
|
+
precommit hook when a PR is submitted:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
ruff check python/
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
To reformat the code in the same way as the precommit hook:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
ruff format
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
[Ruff]: https://github.com/astral-sh/ruff
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Feldera Python SDK
|
|
2
|
+
|
|
3
|
+
Feldera Python is the Feldera SDK for Python developers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv pip install feldera
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Installing from Github
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv pip install git+https://github.com/feldera/feldera#subdirectory=python
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Similarly, to install from a specific branch:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv pip install git+https://github.com/feldera/feldera@{BRANCH_NAME}#subdirectory=python
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Replace `{BRANCH_NAME}` with the name of the branch you want to install from.
|
|
24
|
+
|
|
25
|
+
### Installing from Local Directory
|
|
26
|
+
|
|
27
|
+
If you have cloned the Feldera repo, you can install the python SDK as follows:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# the Feldera Python SDK is present inside the python/ directory
|
|
31
|
+
cd python
|
|
32
|
+
# If you don't have a virtual environment, create one
|
|
33
|
+
uv venv
|
|
34
|
+
source .venv/activate
|
|
35
|
+
# Install the SDK in editable mode
|
|
36
|
+
uv pip install .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Documentation
|
|
40
|
+
|
|
41
|
+
The Python SDK documentation is available at
|
|
42
|
+
[Feldera Python SDK Docs](https://docs.feldera.com/python).
|
|
43
|
+
|
|
44
|
+
To build the html documentation run:
|
|
45
|
+
|
|
46
|
+
Ensure that you have sphinx installed. If not, install it using `uv pip install sphinx`.
|
|
47
|
+
|
|
48
|
+
Then run the following commands:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd docs
|
|
52
|
+
sphinx-apidoc -o . ../feldera
|
|
53
|
+
make html
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
To clean the build, run `make clean`.
|
|
57
|
+
|
|
58
|
+
## Linting and formatting
|
|
59
|
+
|
|
60
|
+
Use [Ruff] to run the lint checks that will be executed by the
|
|
61
|
+
precommit hook when a PR is submitted:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
ruff check python/
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
To reformat the code in the same way as the precommit hook:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
ruff format
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
[Ruff]: https://github.com/astral-sh/ruff
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from threading import Thread, Event
|
|
2
|
+
from typing import Callable, List, Optional, Mapping, Any
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from feldera import FelderaClient
|
|
6
|
+
from feldera._helpers import dataframe_from_response
|
|
7
|
+
from feldera.enums import PipelineFieldSelector
|
|
8
|
+
from feldera.rest.sql_table import SQLTable
|
|
9
|
+
from feldera.rest.sql_view import SQLView
|
|
10
|
+
from feldera.rest.pipeline import Pipeline
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CallbackRunner(Thread):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
client: FelderaClient,
|
|
17
|
+
pipeline_name: str,
|
|
18
|
+
view_name: str,
|
|
19
|
+
callback: Callable[[pd.DataFrame, int], None],
|
|
20
|
+
exception_callback: Callable[[BaseException], None],
|
|
21
|
+
event: Event,
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
:param client: The :class:`.FelderaClient` to use.
|
|
25
|
+
:param pipeline_name: The name of the current pipeline.
|
|
26
|
+
:param view_name: The name of the view we are listening to.
|
|
27
|
+
:param callback: The callback function to call on the data we receive.
|
|
28
|
+
:param exception_callback: The callback function to call when an exception occurs.
|
|
29
|
+
:param event: The event to wait for before starting the callback runner.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
super().__init__()
|
|
33
|
+
self.daemon = True
|
|
34
|
+
self.client: FelderaClient = client
|
|
35
|
+
self.pipeline_name: str = pipeline_name
|
|
36
|
+
self.view_name: str = view_name
|
|
37
|
+
self.callback: Callable[[pd.DataFrame, int], None] = callback
|
|
38
|
+
self.exception_callback: Callable[[BaseException], None] = exception_callback
|
|
39
|
+
self.event: Event = event
|
|
40
|
+
|
|
41
|
+
self.pipeline: Pipeline = self.client.get_pipeline(
|
|
42
|
+
self.pipeline_name, PipelineFieldSelector.ALL
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
view_schema = None
|
|
46
|
+
|
|
47
|
+
schemas: List[SQLTable | SQLView] = self.pipeline.tables + self.pipeline.views
|
|
48
|
+
for schema in schemas:
|
|
49
|
+
if schema.name == self.view_name:
|
|
50
|
+
view_schema = schema
|
|
51
|
+
break
|
|
52
|
+
|
|
53
|
+
if view_schema is None:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
f"Table or View {self.view_name} not found in the pipeline schema."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
self.schema: SQLTable | SQLView = view_schema
|
|
59
|
+
|
|
60
|
+
def to_callback(self, chunk: Mapping[str, Any]):
|
|
61
|
+
data: Optional[list[Mapping[str, Any]]] = chunk.get("json_data")
|
|
62
|
+
seq_no: Optional[int] = chunk.get("sequence_number")
|
|
63
|
+
if data is not None and seq_no is not None:
|
|
64
|
+
self.callback(dataframe_from_response([data], self.schema.fields), seq_no)
|
|
65
|
+
|
|
66
|
+
def run(self):
|
|
67
|
+
"""
|
|
68
|
+
The main loop of the thread. Listens for data and calls the callback function on each chunk of data received.
|
|
69
|
+
|
|
70
|
+
:meta private:
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
gen_obj = self.client.listen_to_pipeline(
|
|
75
|
+
self.pipeline_name,
|
|
76
|
+
self.view_name,
|
|
77
|
+
format="json",
|
|
78
|
+
case_sensitive=self.schema.case_sensitive,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
iterator = gen_obj()
|
|
82
|
+
|
|
83
|
+
# Trigger the HTTP call
|
|
84
|
+
chunk = next(iterator)
|
|
85
|
+
|
|
86
|
+
# Unblock the main thread
|
|
87
|
+
self.event.set()
|
|
88
|
+
|
|
89
|
+
self.to_callback(chunk)
|
|
90
|
+
|
|
91
|
+
for chunk in iterator:
|
|
92
|
+
self.to_callback(chunk)
|
|
93
|
+
|
|
94
|
+
except BaseException as e:
|
|
95
|
+
self.exception_callback(e)
|
|
@@ -2,6 +2,7 @@ import uuid
|
|
|
2
2
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
from decimal import Decimal
|
|
5
|
+
from typing import Mapping, Any
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
def sql_type_to_pandas_type(sql_type: str):
|
|
@@ -60,9 +61,14 @@ def ensure_dataframe_has_columns(df: pd.DataFrame):
|
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
def dataframe_from_response(
|
|
64
|
+
def dataframe_from_response(
|
|
65
|
+
buffer: list[list[Mapping[str, Any]]], fields: list[Mapping[str, Any]]
|
|
66
|
+
):
|
|
64
67
|
"""
|
|
65
68
|
Converts the response from Feldera to a pandas DataFrame.
|
|
69
|
+
|
|
70
|
+
:param buffer: A buffer of a list of JSON formatted output of the view you are listening to.
|
|
71
|
+
:param fields: The schema (list of fields) of the view you are listening to.
|
|
66
72
|
"""
|
|
67
73
|
|
|
68
74
|
pd_schema = {}
|
|
@@ -70,7 +76,7 @@ def dataframe_from_response(buffer: list[list[dict]], schema: dict):
|
|
|
70
76
|
decimal_col = []
|
|
71
77
|
uuid_col = []
|
|
72
78
|
|
|
73
|
-
for column in
|
|
79
|
+
for column in fields:
|
|
74
80
|
column_name = column["name"]
|
|
75
81
|
if not column["case_sensitive"]:
|
|
76
82
|
column_name = column_name.lower()
|
|
@@ -34,137 +34,164 @@ class BuildMode(Enum):
|
|
|
34
34
|
GET_OR_CREATE = 3
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
class
|
|
37
|
+
class DeploymentDesiredStatus(Enum):
|
|
38
|
+
"""
|
|
39
|
+
Deployment desired status of the pipeline.
|
|
38
40
|
"""
|
|
39
|
-
Represents the state that this pipeline is currently in.
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
STOPPED = 0
|
|
43
|
+
UNAVAILABLE = 1
|
|
44
|
+
STANDBY = 2
|
|
45
|
+
PAUSED = 3
|
|
46
|
+
RUNNING = 4
|
|
47
|
+
SUSPENDED = 5
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
│ │
|
|
52
|
-
┌─────────┼────────────────────┴─────┐
|
|
53
|
-
│ ▼ │
|
|
54
|
-
│ Paused ◄──────► Unavailable │
|
|
55
|
-
│ │ ▲ ▲ │
|
|
56
|
-
│ /start │ │ /pause │ │
|
|
57
|
-
│ ▼ │ │ │
|
|
58
|
-
│ Running ◄─────────────┘ │
|
|
59
|
-
└────────────────────────────────────┘
|
|
49
|
+
@staticmethod
|
|
50
|
+
def from_str(value):
|
|
51
|
+
for member in DeploymentDesiredStatus:
|
|
52
|
+
if member.name.lower() == value.lower():
|
|
53
|
+
return member
|
|
54
|
+
raise ValueError(
|
|
55
|
+
f"Unknown value '{value}' for enum {DeploymentDesiredStatus.__name__}"
|
|
56
|
+
)
|
|
60
57
|
|
|
61
|
-
"""
|
|
62
58
|
|
|
63
|
-
|
|
59
|
+
class DeploymentResourcesDesiredStatus(Enum):
|
|
64
60
|
"""
|
|
65
|
-
The
|
|
61
|
+
The desired status of deployment resources of the pipeline.
|
|
66
62
|
"""
|
|
67
63
|
|
|
68
|
-
STOPPED =
|
|
69
|
-
|
|
70
|
-
The pipeline has not (yet) been started or has been stopped either
|
|
71
|
-
manually by the user or automatically by the system due to a
|
|
72
|
-
resource or runtime error.
|
|
64
|
+
STOPPED = 0
|
|
65
|
+
PROVISIONED = 1
|
|
73
66
|
|
|
74
|
-
|
|
67
|
+
@staticmethod
|
|
68
|
+
def from_str(value):
|
|
69
|
+
for member in DeploymentResourcesDesiredStatus:
|
|
70
|
+
if member.name.lower() == value.lower():
|
|
71
|
+
return member
|
|
72
|
+
raise ValueError(
|
|
73
|
+
f"Unknown value '{value}' for enum {DeploymentResourcesDesiredStatus.__name__}"
|
|
74
|
+
)
|
|
75
75
|
|
|
76
|
-
1. The user starts it via `/start` or `/pause`, transitioning to `PROVISIONING`.
|
|
77
|
-
2. Early start fails (e.g., compilation failure), transitioning to `STOPPING`.
|
|
78
|
-
"""
|
|
79
76
|
|
|
80
|
-
|
|
77
|
+
class DeploymentResourcesStatus(Enum):
|
|
81
78
|
"""
|
|
82
|
-
|
|
83
|
-
are being provisioned.
|
|
84
|
-
|
|
85
|
-
The pipeline remains in this state until:
|
|
86
|
-
|
|
87
|
-
1. Resources are provisioned successfully, transitioning to `INITIALIZING`.
|
|
88
|
-
2. Provisioning fails or times out, transitioning to `STOPPING`.
|
|
89
|
-
3. The user cancels the pipeline via `/stop`, transitioning to `STOPPING`.
|
|
79
|
+
The desired status of deployment resources of the pipeline.
|
|
90
80
|
"""
|
|
91
81
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
82
|
+
STOPPED = 0
|
|
83
|
+
PROVISIONING = 1
|
|
84
|
+
PROVISIONED = 2
|
|
85
|
+
STOPPING = 3
|
|
95
86
|
|
|
96
|
-
|
|
87
|
+
@staticmethod
|
|
88
|
+
def from_str(value):
|
|
89
|
+
for member in DeploymentResourcesStatus:
|
|
90
|
+
if member.name.lower() == value.lower():
|
|
91
|
+
return member
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"Unknown value '{value}' for enum {DeploymentResourcesStatus.__name__}"
|
|
94
|
+
)
|
|
97
95
|
|
|
98
|
-
1. Initialization succeeds, transitioning to `PAUSED`.
|
|
99
|
-
2. Initialization fails or times out, transitioning to `STOPPING`.
|
|
100
|
-
3. The user suspends the pipeline via `/suspend`, transitioning to `SUSPENDING`.
|
|
101
|
-
4. The user stops the pipeline via `/stop`, transitioning to `STOPPING`.
|
|
102
|
-
"""
|
|
103
96
|
|
|
104
|
-
|
|
97
|
+
class DeploymentRuntimeDesiredStatus(Enum):
|
|
105
98
|
"""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
The pipeline remains in this state until:
|
|
109
|
-
|
|
110
|
-
1. The user starts it via `/start`, transitioning to `RUNNING`.
|
|
111
|
-
2. A runtime error occurs, transitioning to `STOPPING`.
|
|
112
|
-
3. The user suspends it via `/suspend`, transitioning to `SUSPENDING`.
|
|
113
|
-
4. The user stops it via `/stop`, transitioning to `STOPPING`.
|
|
99
|
+
Deployment runtime desired status of the pipeline.
|
|
114
100
|
"""
|
|
115
101
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
102
|
+
UNAVAILABLE = 0
|
|
103
|
+
STANDBY = 1
|
|
104
|
+
PAUSED = 2
|
|
105
|
+
RUNNING = 3
|
|
106
|
+
SUSPENDED = 4
|
|
119
107
|
|
|
120
|
-
|
|
108
|
+
@staticmethod
|
|
109
|
+
def from_str(value):
|
|
110
|
+
for member in DeploymentRuntimeDesiredStatus:
|
|
111
|
+
if member.name.lower() == value.lower():
|
|
112
|
+
return member
|
|
113
|
+
raise ValueError(
|
|
114
|
+
f"Unknown value '{value}' for enum {DeploymentRuntimeDesiredStatus.__name__}"
|
|
115
|
+
)
|
|
121
116
|
|
|
122
|
-
1. The user pauses it via `/pause`, transitioning to `PAUSED`.
|
|
123
|
-
2. A runtime error occurs, transitioning to `STOPPING`.
|
|
124
|
-
3. The user suspends it via `/suspend`, transitioning to `SUSPENDING`.
|
|
125
|
-
4. The user stops it via `/stop`, transitioning to `STOPPING`.
|
|
126
|
-
"""
|
|
127
117
|
|
|
128
|
-
|
|
118
|
+
class DeploymentRuntimeStatus(Enum):
|
|
129
119
|
"""
|
|
130
|
-
|
|
131
|
-
|
|
120
|
+
Deployment runtime status of the pipeline.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
UNAVAILABLE = 0
|
|
124
|
+
STANDBY = 1
|
|
125
|
+
AWAITINGAPPROVAL = 2
|
|
126
|
+
INITIALIZING = 3
|
|
127
|
+
BOOTSTRAPPING = 4
|
|
128
|
+
REPLAYING = 5
|
|
129
|
+
PAUSED = 6
|
|
130
|
+
RUNNING = 7
|
|
131
|
+
SUSPENDED = 8
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
@staticmethod
|
|
134
|
+
def from_str(value):
|
|
135
|
+
for member in DeploymentRuntimeStatus:
|
|
136
|
+
if member.name.lower() == value.lower():
|
|
137
|
+
return member
|
|
138
|
+
raise ValueError(
|
|
139
|
+
f"Unknown value '{value}' for enum {DeploymentRuntimeStatus.__name__}"
|
|
140
|
+
)
|
|
134
141
|
|
|
135
|
-
1. A successful status check transitions it back to `PAUSED` or `RUNNING`.
|
|
136
|
-
2. A runtime error occurs, transitioning to `STOPPING`.
|
|
137
|
-
3. The user suspends it via `/suspend`, transitioning to `SUSPENDING`.
|
|
138
|
-
4. The user stops it via `/stop`, transitioning to `STOPPING`.
|
|
139
142
|
|
|
140
|
-
|
|
141
|
-
are only applied once the pipeline becomes reachable.
|
|
143
|
+
class PipelineStatus(Enum):
|
|
142
144
|
"""
|
|
143
|
-
|
|
144
|
-
SUSPENDING = 7
|
|
145
|
+
Represents the state that this pipeline is currently in.
|
|
145
146
|
"""
|
|
146
|
-
The pipeline is being suspended to storage.
|
|
147
147
|
|
|
148
|
-
|
|
148
|
+
NOT_FOUND = 0
|
|
149
|
+
STOPPED = 1
|
|
150
|
+
PROVISIONING = 2
|
|
151
|
+
UNAVAILABLE = 3
|
|
152
|
+
STANDBY = 4
|
|
153
|
+
AWAITINGAPPROVAL = 5
|
|
154
|
+
INITIALIZING = 6
|
|
155
|
+
BOOTSTRAPPING = 7
|
|
156
|
+
REPLAYING = 8
|
|
157
|
+
PAUSED = 9
|
|
158
|
+
RUNNING = 10
|
|
159
|
+
SUSPENDED = 11
|
|
160
|
+
STOPPING = 12
|
|
149
161
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
162
|
+
@staticmethod
|
|
163
|
+
def from_str(value):
|
|
164
|
+
for member in PipelineStatus:
|
|
165
|
+
if member.name.lower() == value.lower():
|
|
166
|
+
return member
|
|
167
|
+
raise ValueError(f"Unknown value '{value}' for enum {PipelineStatus.__name__}")
|
|
168
|
+
|
|
169
|
+
def __eq__(self, other):
|
|
170
|
+
return self.value == other.value
|
|
153
171
|
|
|
154
|
-
STOPPING = 8
|
|
155
|
-
"""
|
|
156
|
-
The pipeline's compute resources are being scaled down to zero.
|
|
157
172
|
|
|
158
|
-
|
|
159
|
-
|
|
173
|
+
class TransactionStatus(Enum):
|
|
174
|
+
"""
|
|
175
|
+
Represents the transaction handling status of a pipeline.
|
|
160
176
|
"""
|
|
161
177
|
|
|
178
|
+
NoTransaction = 1
|
|
179
|
+
"""There is currently no active transaction."""
|
|
180
|
+
|
|
181
|
+
TransactionInProgress = 2
|
|
182
|
+
"""There is an active transaction in progress."""
|
|
183
|
+
|
|
184
|
+
CommitInProgress = 3
|
|
185
|
+
"""A commit is currently in progress."""
|
|
186
|
+
|
|
162
187
|
@staticmethod
|
|
163
188
|
def from_str(value):
|
|
164
|
-
for member in
|
|
189
|
+
for member in TransactionStatus:
|
|
165
190
|
if member.name.lower() == value.lower():
|
|
166
191
|
return member
|
|
167
|
-
raise ValueError(
|
|
192
|
+
raise ValueError(
|
|
193
|
+
f"Unknown value '{value}' for enum {TransactionStatus.__name__}"
|
|
194
|
+
)
|
|
168
195
|
|
|
169
196
|
def __eq__(self, other):
|
|
170
197
|
return self.value == other.value
|
|
@@ -311,3 +338,29 @@ class FaultToleranceModel(Enum):
|
|
|
311
338
|
raise ValueError(
|
|
312
339
|
f"Unknown value '{value}' for enum {FaultToleranceModel.__name__}"
|
|
313
340
|
)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class PipelineFieldSelector(Enum):
|
|
344
|
+
ALL = "all"
|
|
345
|
+
"""Select all fields of a pipeline."""
|
|
346
|
+
|
|
347
|
+
STATUS = "status"
|
|
348
|
+
"""Select only the fields required to know the status of a pipeline."""
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class BootstrapPolicy(Enum):
|
|
352
|
+
AWAIT_APPROVAL = "await_approval"
|
|
353
|
+
ALLOW = "allow"
|
|
354
|
+
REJECT = "reject"
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
class CompletionTokenStatus(Enum):
|
|
358
|
+
COMPLETE = "complete"
|
|
359
|
+
"""
|
|
360
|
+
Feldera has completed processing all inputs represented by this token.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
IN_PROGRESS = "inprogress"
|
|
364
|
+
"""
|
|
365
|
+
Feldera is still processing the inputs represented by this token.
|
|
366
|
+
"""
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
|
+
|
|
2
3
|
from typing import Optional
|
|
4
|
+
from threading import Event
|
|
3
5
|
|
|
4
|
-
from queue import Queue
|
|
5
6
|
from feldera import FelderaClient
|
|
6
7
|
from feldera._callback_runner import CallbackRunner
|
|
7
8
|
|
|
@@ -12,7 +13,6 @@ class OutputHandler:
|
|
|
12
13
|
client: FelderaClient,
|
|
13
14
|
pipeline_name: str,
|
|
14
15
|
view_name: str,
|
|
15
|
-
queue: Optional[Queue],
|
|
16
16
|
):
|
|
17
17
|
"""
|
|
18
18
|
Initializes the output handler, but doesn't start it.
|
|
@@ -22,17 +22,26 @@ class OutputHandler:
|
|
|
22
22
|
self.client: FelderaClient = client
|
|
23
23
|
self.pipeline_name: str = pipeline_name
|
|
24
24
|
self.view_name: str = view_name
|
|
25
|
-
self.queue: Optional[Queue] = queue
|
|
26
25
|
self.buffer: list[pd.DataFrame] = []
|
|
26
|
+
self.exception: Optional[BaseException] = None
|
|
27
|
+
self.event = Event()
|
|
27
28
|
|
|
28
29
|
# the callback that is passed to the `CallbackRunner`
|
|
29
30
|
def callback(df: pd.DataFrame, _: int):
|
|
30
31
|
if not df.empty:
|
|
31
32
|
self.buffer.append(df)
|
|
32
33
|
|
|
34
|
+
def exception_callback(exception: BaseException):
|
|
35
|
+
self.exception = exception
|
|
36
|
+
|
|
33
37
|
# sets up the callback runner
|
|
34
38
|
self.handler = CallbackRunner(
|
|
35
|
-
self.client,
|
|
39
|
+
self.client,
|
|
40
|
+
self.pipeline_name,
|
|
41
|
+
self.view_name,
|
|
42
|
+
callback,
|
|
43
|
+
exception_callback,
|
|
44
|
+
self.event,
|
|
36
45
|
)
|
|
37
46
|
|
|
38
47
|
def start(self):
|
|
@@ -41,6 +50,7 @@ class OutputHandler:
|
|
|
41
50
|
"""
|
|
42
51
|
|
|
43
52
|
self.handler.start()
|
|
53
|
+
_ = self.event.wait()
|
|
44
54
|
|
|
45
55
|
def to_pandas(self, clear_buffer: bool = True):
|
|
46
56
|
"""
|
|
@@ -49,6 +59,8 @@ class OutputHandler:
|
|
|
49
59
|
:param clear_buffer: Whether to clear the buffer after getting the output.
|
|
50
60
|
"""
|
|
51
61
|
|
|
62
|
+
if self.exception is not None:
|
|
63
|
+
raise self.exception
|
|
52
64
|
if len(self.buffer) == 0:
|
|
53
65
|
return pd.DataFrame()
|
|
54
66
|
res = pd.concat(self.buffer, ignore_index=True)
|