splight-lib 4.0.0.dev6__tar.gz → 4.3.0.dev1__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.
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/PKG-INFO +1 -1
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/setup.py +1 -1
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/datalake/abstract.py +25 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/datalake/remote_client.py +50 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/component/abstract.py +33 -23
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/execution.py +26 -48
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/asset.py +2 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/base.py +21 -0
- splight-lib-4.3.0.dev1/splight_lib/models/metadata.py +20 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/native.py +5 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/restclient/client.py +84 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib.egg-info/PKG-INFO +1 -1
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib.egg-info/SOURCES.txt +1 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/LICENSE.txt +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/README.md +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/pyproject.toml +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/setup.cfg +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/abstract/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/abstract/client.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/auth/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/auth/exceptions.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/auth/mac_auth.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/auth/token.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/abstract.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/classmap.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/exceptions.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/local_client.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/remote_client.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/abstract.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/builder.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/classmap.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/local_client.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/remote_client.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/datalake/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/datalake/builder.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/datalake/local_client.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/exceptions.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/file_handler.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/filter.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/hub/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/hub/abstract.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/hub/client.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/communication/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/communication/event_handler.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/component/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/component/exceptions.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/component/spec.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/constants.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/encryption.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/logging/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/logging/_internal.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/logging/component.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/logging/constants.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/logging/logging.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/alert.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/attribute.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/communication.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/component.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/dashboard.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/data_address.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/event.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/exceptions.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/file.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/hub.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/pipeline.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/query.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/secret.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/models/setpoint.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/restclient/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/restclient/exceptions.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/restclient/types.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/settings.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/testing/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/utils/__init__.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/utils/custom_model.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/utils/hub.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/version.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/webhook.py +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib.egg-info/dependency_links.txt +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib.egg-info/not-zip-safe +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib.egg-info/requires.txt +0 -0
- {splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib.egg-info/top_level.txt +0 -0
|
@@ -13,12 +13,22 @@ class AbstractDatalakeClient(AbstractClient):
|
|
|
13
13
|
kwargs["count_func"] = "None"
|
|
14
14
|
return QuerySet(self, *args, **kwargs)
|
|
15
15
|
|
|
16
|
+
async def async_get(self, *args, **kwargs):
|
|
17
|
+
# TODO: consider using an async QuerySet
|
|
18
|
+
return await self._async_raw_get(*args, **kwargs)
|
|
19
|
+
|
|
16
20
|
@abstractmethod
|
|
17
21
|
def save(
|
|
18
22
|
self, collection: str, instances: Union[List[Dict], Dict]
|
|
19
23
|
) -> List[dict]:
|
|
20
24
|
pass
|
|
21
25
|
|
|
26
|
+
@abstractmethod
|
|
27
|
+
async def async_save(
|
|
28
|
+
self, collection: str, instances: Union[List[Dict], Dict]
|
|
29
|
+
) -> List[dict]:
|
|
30
|
+
pass
|
|
31
|
+
|
|
22
32
|
@abstractmethod
|
|
23
33
|
def delete(self, collection: str, **kwargs) -> None:
|
|
24
34
|
pass
|
|
@@ -65,6 +75,21 @@ class AbstractDatalakeClient(AbstractClient):
|
|
|
65
75
|
) -> List[Dict]:
|
|
66
76
|
pass
|
|
67
77
|
|
|
78
|
+
@abstractmethod
|
|
79
|
+
async def _async_raw_get(
|
|
80
|
+
self,
|
|
81
|
+
resource_name: str,
|
|
82
|
+
collection: str,
|
|
83
|
+
limit_: int = 50,
|
|
84
|
+
skip_: int = 0,
|
|
85
|
+
sort: Union[List, str] = ["timestamp__desc"],
|
|
86
|
+
group_id: Optional[Union[List, str]] = None,
|
|
87
|
+
group_fields: Optional[Union[List, str]] = None,
|
|
88
|
+
tzinfo: timezone = timezone(timedelta()),
|
|
89
|
+
**filters,
|
|
90
|
+
) -> List[Dict]:
|
|
91
|
+
pass
|
|
92
|
+
|
|
68
93
|
@abstractmethod
|
|
69
94
|
def execute_query(
|
|
70
95
|
self,
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/datalake/remote_client.py
RENAMED
|
@@ -49,6 +49,20 @@ class RemoteDatalakeClient(AbstractDatalakeClient, AbstractRemoteClient):
|
|
|
49
49
|
response.raise_for_status()
|
|
50
50
|
return instances
|
|
51
51
|
|
|
52
|
+
@retry(SPLIGHT_REQUEST_EXCEPTIONS, tries=3, delay=2, jitter=1)
|
|
53
|
+
async def async_save(
|
|
54
|
+
self,
|
|
55
|
+
collection: str,
|
|
56
|
+
instances: Union[List[Dict], Dict],
|
|
57
|
+
) -> List[dict]:
|
|
58
|
+
instances = instances if isinstance(instances, list) else [instances]
|
|
59
|
+
url = self._base_url / f"{self._PREFIX}/save/"
|
|
60
|
+
response = await self._restclient.async_post(
|
|
61
|
+
url, params={"source": collection}, json=instances
|
|
62
|
+
)
|
|
63
|
+
response.raise_for_status()
|
|
64
|
+
return instances
|
|
65
|
+
|
|
52
66
|
@retry(SPLIGHT_REQUEST_EXCEPTIONS, tries=3, delay=2, jitter=1)
|
|
53
67
|
def _raw_get(
|
|
54
68
|
self,
|
|
@@ -85,6 +99,42 @@ class RemoteDatalakeClient(AbstractDatalakeClient, AbstractRemoteClient):
|
|
|
85
99
|
|
|
86
100
|
return output
|
|
87
101
|
|
|
102
|
+
@retry(SPLIGHT_REQUEST_EXCEPTIONS, tries=3, delay=2, jitter=1)
|
|
103
|
+
async def _async_raw_get(
|
|
104
|
+
self,
|
|
105
|
+
resource_name: str,
|
|
106
|
+
collection: str,
|
|
107
|
+
limit_: int = 50,
|
|
108
|
+
skip_: int = 0,
|
|
109
|
+
sort: Union[List, str] = ["timestamp__desc"],
|
|
110
|
+
group_id: Optional[Union[List, str]] = None,
|
|
111
|
+
group_fields: Optional[Union[List, str]] = None,
|
|
112
|
+
tzinfo: timezone = timezone(timedelta()),
|
|
113
|
+
**filters,
|
|
114
|
+
) -> List[Dict]:
|
|
115
|
+
# GET /datalake/data/
|
|
116
|
+
url = self._base_url / f"{self._PREFIX}/data/"
|
|
117
|
+
|
|
118
|
+
filters.update(
|
|
119
|
+
{
|
|
120
|
+
"source": collection,
|
|
121
|
+
"output_format": resource_name,
|
|
122
|
+
"sort": sort,
|
|
123
|
+
"limit_": limit_,
|
|
124
|
+
"skip_": skip_,
|
|
125
|
+
"group_id": group_id,
|
|
126
|
+
"group_fields": group_fields,
|
|
127
|
+
# "tzinfo": tzinfo
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
params = self._parse_params(**filters)
|
|
132
|
+
response = await self._restclient.async_get(url, params=params)
|
|
133
|
+
response.raise_for_status()
|
|
134
|
+
output = response.json()["results"]
|
|
135
|
+
|
|
136
|
+
return output
|
|
137
|
+
|
|
88
138
|
@retry(SPLIGHT_REQUEST_EXCEPTIONS, tries=3, delay=2, jitter=1)
|
|
89
139
|
def delete(self, collection: str, **kwargs) -> None:
|
|
90
140
|
# DELETE /datalake/delete/
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
from abc import abstractmethod
|
|
2
1
|
import os
|
|
3
2
|
import sys
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
4
|
from functools import partial
|
|
5
5
|
from tempfile import NamedTemporaryFile
|
|
6
6
|
from time import sleep
|
|
7
|
-
from typing import Dict, List, Optional, Type
|
|
7
|
+
from typing import Callable, Dict, List, Optional, Type
|
|
8
8
|
|
|
9
9
|
from pydantic import BaseModel, create_model
|
|
10
10
|
|
|
@@ -37,11 +37,7 @@ from splight_lib.models.component import (
|
|
|
37
37
|
)
|
|
38
38
|
from splight_lib.models.event import EventNames
|
|
39
39
|
from splight_lib.models.setpoint import SetPoint
|
|
40
|
-
from splight_lib.restclient import
|
|
41
|
-
ConnectError,
|
|
42
|
-
HTTPError,
|
|
43
|
-
Timeout,
|
|
44
|
-
)
|
|
40
|
+
from splight_lib.restclient import ConnectError, HTTPError, Timeout
|
|
45
41
|
from splight_lib.settings import settings
|
|
46
42
|
|
|
47
43
|
REQUEST_EXCEPTIONS = (ConnectError, HTTPError, Timeout)
|
|
@@ -78,9 +74,7 @@ class HealthCheckProcessor:
|
|
|
78
74
|
if not is_alive:
|
|
79
75
|
exc = self._engine.get_last_exception()
|
|
80
76
|
self._log_exception(exc)
|
|
81
|
-
self._logger.info(
|
|
82
|
-
"Healthcheck finished", tags=LogTags.RUNTIME
|
|
83
|
-
)
|
|
77
|
+
self._logger.info("Healthcheck finished", tags=LogTags.RUNTIME)
|
|
84
78
|
self._health_file.close()
|
|
85
79
|
self._logger.info(
|
|
86
80
|
"Healthcheck file removed: %s",
|
|
@@ -101,7 +95,7 @@ class HealthCheckProcessor:
|
|
|
101
95
|
self._logger.exception(exc, exc_info=(exc_type, exc, stack))
|
|
102
96
|
|
|
103
97
|
|
|
104
|
-
class SplightBaseComponent:
|
|
98
|
+
class SplightBaseComponent(ABC):
|
|
105
99
|
def __init__(
|
|
106
100
|
self,
|
|
107
101
|
component_id: Optional[str] = None,
|
|
@@ -159,6 +153,8 @@ class SplightBaseComponent:
|
|
|
159
153
|
self._custom_types = self._get_custom_type_model(component_objects)
|
|
160
154
|
self._routines = self._get_routine_model(routines_objects)
|
|
161
155
|
|
|
156
|
+
self.start = self._wrap_start(self.start)
|
|
157
|
+
|
|
162
158
|
@property
|
|
163
159
|
def input(self) -> BaseModel:
|
|
164
160
|
return self._input
|
|
@@ -185,6 +181,30 @@ class SplightBaseComponent:
|
|
|
185
181
|
else:
|
|
186
182
|
sys.exit(0)
|
|
187
183
|
|
|
184
|
+
def _wrap_start(self, original_start: Callable) -> Callable:
|
|
185
|
+
"""Wraps the start method to wait for all threads to finish.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
original_start: Callable
|
|
190
|
+
The implemented start method from the component
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
Callable
|
|
195
|
+
The start method wrapped
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
def wrapper():
|
|
199
|
+
original_start()
|
|
200
|
+
for thread in self._execution_engine.threads:
|
|
201
|
+
thread.join()
|
|
202
|
+
|
|
203
|
+
self._health_check_thread.join()
|
|
204
|
+
self._register_exit()
|
|
205
|
+
|
|
206
|
+
return wrapper
|
|
207
|
+
|
|
188
208
|
def _get_custom_type_model(
|
|
189
209
|
self, component_object: Dict[str, Type[ComponentObjectInstance]]
|
|
190
210
|
) -> BaseModel:
|
|
@@ -300,20 +320,10 @@ class SplightBaseComponent:
|
|
|
300
320
|
),
|
|
301
321
|
)
|
|
302
322
|
|
|
323
|
+
@abstractmethod
|
|
303
324
|
def start(self):
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
# The following it for waiting for all threads to finish
|
|
307
|
-
for thread in self._execution_engine.threads:
|
|
308
|
-
thread.join()
|
|
309
|
-
|
|
310
|
-
self._health_check_thread.join()
|
|
311
|
-
self._register_exit()
|
|
325
|
+
raise NotImplementedError()
|
|
312
326
|
|
|
313
327
|
def stop(self):
|
|
314
328
|
self._execution_engine.terminate_all()
|
|
315
329
|
sys.exit(1)
|
|
316
|
-
|
|
317
|
-
@abstractmethod
|
|
318
|
-
def run_component(self) -> None:
|
|
319
|
-
raise NotImplementedError()
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import atexit
|
|
2
1
|
import sys
|
|
3
2
|
import threading
|
|
4
3
|
import time
|
|
@@ -153,17 +152,20 @@ class TaskMap:
|
|
|
153
152
|
|
|
154
153
|
|
|
155
154
|
class Scheduler:
|
|
156
|
-
def __init__(self,
|
|
155
|
+
def __init__(self, event: Event) -> None:
|
|
156
|
+
# The _event attr is used for controling the scheduler loop meanwhile
|
|
157
|
+
# the _tasks_event is used to notify the scheduler that a task should
|
|
158
|
+
# be executed
|
|
159
|
+
self._event = event
|
|
157
160
|
self._tasks = TaskMap()
|
|
158
|
-
self.
|
|
161
|
+
self._tasks_event = Event()
|
|
159
162
|
self._mutex = Lock()
|
|
160
163
|
self._to_add: List[Task] = []
|
|
161
164
|
self._to_remove: List[Task] = []
|
|
162
165
|
|
|
163
166
|
def start(self) -> None:
|
|
164
167
|
"""Scheduler infinite loop."""
|
|
165
|
-
self.
|
|
166
|
-
while not self._stop:
|
|
168
|
+
while self._event.is_set():
|
|
167
169
|
# Update the task list
|
|
168
170
|
self._update_task_list()
|
|
169
171
|
# Run the scheduler task
|
|
@@ -172,11 +174,12 @@ class Scheduler:
|
|
|
172
174
|
next_event_time = near_event - time.time()
|
|
173
175
|
|
|
174
176
|
# Wait until next event or someone trigger the event
|
|
175
|
-
self.
|
|
176
|
-
self.
|
|
177
|
+
self._tasks_event.wait(timeout=next_event_time)
|
|
178
|
+
self._tasks_event.clear()
|
|
177
179
|
|
|
178
180
|
def stop(self) -> None:
|
|
179
|
-
self.
|
|
181
|
+
if self._event.is_set():
|
|
182
|
+
self._event.clear()
|
|
180
183
|
|
|
181
184
|
def _schedule(self) -> float:
|
|
182
185
|
"""Scheduler main task."""
|
|
@@ -197,7 +200,7 @@ class Scheduler:
|
|
|
197
200
|
Unlock the scheduler if is locked.
|
|
198
201
|
This will run the scheduler main task.
|
|
199
202
|
"""
|
|
200
|
-
self.
|
|
203
|
+
self._tasks_event.set()
|
|
201
204
|
|
|
202
205
|
def schedule(self, task: Task) -> None:
|
|
203
206
|
"""
|
|
@@ -242,20 +245,8 @@ class Thread(DefaultThread):
|
|
|
242
245
|
def __init__(self, target: Callable, args: Tuple = (), **kwargs) -> None:
|
|
243
246
|
target = self.store_result(target)
|
|
244
247
|
self.result = Empty()
|
|
245
|
-
self._exc = None
|
|
246
248
|
super().__init__(target=target, args=args, name=target, **kwargs)
|
|
247
249
|
|
|
248
|
-
@property
|
|
249
|
-
def exc(self):
|
|
250
|
-
return self._exc
|
|
251
|
-
|
|
252
|
-
def run(self):
|
|
253
|
-
try:
|
|
254
|
-
super().run()
|
|
255
|
-
except Exception as exc:
|
|
256
|
-
self._exc = exc
|
|
257
|
-
raise exc
|
|
258
|
-
|
|
259
250
|
def store_result(self, func: Callable) -> Callable:
|
|
260
251
|
@wraps(func)
|
|
261
252
|
def wrapper(*args, **kwargs):
|
|
@@ -288,6 +279,7 @@ class Popen(DefaultPopen):
|
|
|
288
279
|
|
|
289
280
|
class ExecutionClient(AbstractClient):
|
|
290
281
|
def __init__(self, *args, **kwargs):
|
|
282
|
+
self._event = Event()
|
|
291
283
|
self.processes: List[Popen] = []
|
|
292
284
|
self.threads: List[Thread] = []
|
|
293
285
|
|
|
@@ -297,6 +289,10 @@ class ExecutionClient(AbstractClient):
|
|
|
297
289
|
self._exc = None
|
|
298
290
|
self._thread_exc = None
|
|
299
291
|
|
|
292
|
+
@property
|
|
293
|
+
def event(self) -> Event:
|
|
294
|
+
return self._event
|
|
295
|
+
|
|
300
296
|
def _register_exit_functions(self) -> None:
|
|
301
297
|
excepthook = sys.excepthook
|
|
302
298
|
thread_exchook = threading.excepthook
|
|
@@ -315,7 +311,6 @@ class ExecutionClient(AbstractClient):
|
|
|
315
311
|
self.terminate_all()
|
|
316
312
|
thread_exchook(args)
|
|
317
313
|
|
|
318
|
-
# atexit.register(self.terminate_all)
|
|
319
314
|
sys.excepthook = wrap_excepthook
|
|
320
315
|
threading.excepthook = wrap_thread_excepthook
|
|
321
316
|
|
|
@@ -323,13 +318,17 @@ class ExecutionClient(AbstractClient):
|
|
|
323
318
|
self.terminate_all()
|
|
324
319
|
|
|
325
320
|
def terminate_all(self) -> None:
|
|
326
|
-
|
|
327
|
-
|
|
321
|
+
# Set the event to False to stop all threads
|
|
322
|
+
# This will also stop the scheduler
|
|
323
|
+
self._event.clear()
|
|
328
324
|
|
|
329
325
|
for p in self.processes:
|
|
330
326
|
p.terminate()
|
|
331
327
|
|
|
332
328
|
def start(self, job=Union[Popen, Thread, Task]):
|
|
329
|
+
# Set the event in true so the threads can run
|
|
330
|
+
if not self._event.is_set():
|
|
331
|
+
self._event.set()
|
|
333
332
|
logger.info("Executing new job.", tags=LogTags.RUNTIME)
|
|
334
333
|
if isinstance(job, Popen):
|
|
335
334
|
return self._start_process(job)
|
|
@@ -361,7 +360,7 @@ class ExecutionClient(AbstractClient):
|
|
|
361
360
|
logger.debug("Starting Task", tags=LogTags.RUNTIME)
|
|
362
361
|
if not getattr(self, "_scheduler", None):
|
|
363
362
|
# Instantiate and start Scheduler thread
|
|
364
|
-
self._scheduler = Scheduler()
|
|
363
|
+
self._scheduler = Scheduler(self._event)
|
|
365
364
|
self._start_thread(
|
|
366
365
|
Thread(target=self._scheduler.start, daemon=False)
|
|
367
366
|
)
|
|
@@ -373,23 +372,8 @@ class ExecutionClient(AbstractClient):
|
|
|
373
372
|
return self._scheduler.unschedule(job)
|
|
374
373
|
|
|
375
374
|
def is_alive(self) -> bool:
|
|
376
|
-
|
|
377
|
-
# not using the execution client explicitly and is doing some
|
|
378
|
-
# processing directly in the main thread so the healtcheck should
|
|
379
|
-
# say the component is healthy.
|
|
380
|
-
# main_thread = threading.main_thread()
|
|
381
|
-
# threads_status = [
|
|
382
|
-
# p.is_alive() or p.exit_ok() for p in self.processes + self.threads
|
|
383
|
-
# ]
|
|
384
|
-
threads_status = [
|
|
385
|
-
p.is_alive() for p in self.processes + self.threads
|
|
386
|
-
]
|
|
375
|
+
threads_status = [p.is_alive() for p in self.processes + self.threads]
|
|
387
376
|
return all(threads_status)
|
|
388
|
-
# (
|
|
389
|
-
# main_thread.is_alive() or all(threads_status)
|
|
390
|
-
# if threads_status
|
|
391
|
-
# else main_thread.is_alive()
|
|
392
|
-
# )
|
|
393
377
|
|
|
394
378
|
def healthcheck(self) -> Tuple[bool, ComponentStatus]:
|
|
395
379
|
"""Check if the component is alive and return the status.
|
|
@@ -414,16 +398,10 @@ class ExecutionClient(AbstractClient):
|
|
|
414
398
|
def get_last_exception(self) -> Optional[Exception]:
|
|
415
399
|
"""Get the last exception thrown in one of the threads.
|
|
416
400
|
It assumes that there is only one thread that crashed
|
|
417
|
-
|
|
401
|
+
It only works for the thread not the processes.
|
|
418
402
|
"""
|
|
419
|
-
# broken_thread = [x.exc for x in self.threads if x.exc]
|
|
420
403
|
if self._exc:
|
|
421
404
|
return self._exc[1]
|
|
422
405
|
if self._thread_exc:
|
|
423
406
|
return self._thread_exc[1]
|
|
424
|
-
# if broken_thread:
|
|
425
|
-
# print("THREAD EXC")
|
|
426
|
-
# print(self._thread_exc)
|
|
427
|
-
# print()
|
|
428
|
-
# return broken_thread[0]
|
|
429
407
|
return None
|
|
@@ -4,6 +4,7 @@ from geojson_pydantic import GeometryCollection
|
|
|
4
4
|
|
|
5
5
|
from splight_lib.models.attribute import Attribute
|
|
6
6
|
from splight_lib.models.base import SplightDatabaseBaseModel
|
|
7
|
+
from splight_lib.models.metadata import Metadata
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class Asset(SplightDatabaseBaseModel):
|
|
@@ -12,6 +13,7 @@ class Asset(SplightDatabaseBaseModel):
|
|
|
12
13
|
description: Optional[str] = None
|
|
13
14
|
tags: List[str] = []
|
|
14
15
|
attributes: List[Attribute] = []
|
|
16
|
+
metadata: List[Metadata] = []
|
|
15
17
|
verified: bool = False
|
|
16
18
|
geometry: Optional[GeometryCollection]
|
|
17
19
|
centroid_coordinates: Optional[Tuple[float, float]]
|
|
@@ -94,6 +94,19 @@ class SplightDatalakeBaseModel(BaseModel):
|
|
|
94
94
|
instances = [cls.parse_obj(item) for item in instances]
|
|
95
95
|
return instances
|
|
96
96
|
|
|
97
|
+
@classmethod
|
|
98
|
+
async def async_get(
|
|
99
|
+
cls, **params: Dict
|
|
100
|
+
) -> List["SplightDatalakeBaseModel"]:
|
|
101
|
+
dl_client = cls.__get_datalake_client()
|
|
102
|
+
instances = await dl_client.async_get(
|
|
103
|
+
resource_name=cls.__name__,
|
|
104
|
+
collection=cls._collection_name,
|
|
105
|
+
**params,
|
|
106
|
+
)
|
|
107
|
+
instances = [cls.parse_obj(item) for item in instances]
|
|
108
|
+
return instances
|
|
109
|
+
|
|
97
110
|
@classmethod
|
|
98
111
|
def get_dataframe(cls, **params: Dict) -> pd.DataFrame:
|
|
99
112
|
dl_client = cls.__get_datalake_client()
|
|
@@ -111,6 +124,14 @@ class SplightDatalakeBaseModel(BaseModel):
|
|
|
111
124
|
instances=json.loads(self.json()),
|
|
112
125
|
)
|
|
113
126
|
|
|
127
|
+
async def async_save(self):
|
|
128
|
+
dl_client = self.__get_datalake_client()
|
|
129
|
+
|
|
130
|
+
await dl_client.async_save(
|
|
131
|
+
collection=self._collection_name,
|
|
132
|
+
instances=json.loads(self.json()),
|
|
133
|
+
)
|
|
134
|
+
|
|
114
135
|
@classmethod
|
|
115
136
|
def save_dataframe(cls, dataframe: pd.DataFrame):
|
|
116
137
|
dl_client = cls.__get_datalake_client()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from enum import auto
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from strenum import PascalCaseStrEnum
|
|
5
|
+
|
|
6
|
+
from splight_lib.models.base import SplightDatabaseBaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MetadataType(PascalCaseStrEnum):
|
|
10
|
+
NUMBER = auto()
|
|
11
|
+
BOOLEAN = auto()
|
|
12
|
+
STRING = auto()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Metadata(SplightDatabaseBaseModel):
|
|
16
|
+
id: Optional[str]
|
|
17
|
+
name: Optional[str]
|
|
18
|
+
asset: Optional[str]
|
|
19
|
+
type: MetadataType = MetadataType.NUMBER
|
|
20
|
+
value: Optional[str]
|
|
@@ -24,6 +24,11 @@ class NativeOutput(SplightDatalakeBaseModel):
|
|
|
24
24
|
params["output_format"] = cls._output_format
|
|
25
25
|
return super().get(**params)
|
|
26
26
|
|
|
27
|
+
@classmethod
|
|
28
|
+
async def async_get(cls, **params: Dict) -> List["NativeOutput"]:
|
|
29
|
+
params["output_format"] = cls._output_format
|
|
30
|
+
return await super().async_get(**params)
|
|
31
|
+
|
|
27
32
|
@classmethod
|
|
28
33
|
def get_dataframe(cls, **params: Dict) -> pd.DataFrame:
|
|
29
34
|
params["output_format"] = cls._output_format
|
|
@@ -183,9 +183,33 @@ class SplightRestClient:
|
|
|
183
183
|
default_encoding=default_encoding,
|
|
184
184
|
event_hooks=event_hooks,
|
|
185
185
|
)
|
|
186
|
+
self._async_client = httpx.AsyncClient(
|
|
187
|
+
auth=auth,
|
|
188
|
+
params=params,
|
|
189
|
+
headers=headers,
|
|
190
|
+
cookies=cookies,
|
|
191
|
+
timeout=timeout,
|
|
192
|
+
verify=verify,
|
|
193
|
+
cert=cert,
|
|
194
|
+
http1=http1,
|
|
195
|
+
http2=http2,
|
|
196
|
+
proxies=proxies,
|
|
197
|
+
follow_redirects=allow_redirects,
|
|
198
|
+
base_url=base_url,
|
|
199
|
+
limits=limits,
|
|
200
|
+
max_redirects=max_redirects,
|
|
201
|
+
transport=transport,
|
|
202
|
+
app=app,
|
|
203
|
+
trust_env=trust_env,
|
|
204
|
+
default_encoding=default_encoding,
|
|
205
|
+
event_hooks=event_hooks,
|
|
206
|
+
)
|
|
186
207
|
|
|
187
208
|
def update_headers(self, new_headers: HeaderTypes):
|
|
188
209
|
self._client.headers = self._client._merge_headers(new_headers)
|
|
210
|
+
self._async_client.headers = self._async_client._merge_headers(
|
|
211
|
+
new_headers
|
|
212
|
+
)
|
|
189
213
|
|
|
190
214
|
def get(
|
|
191
215
|
self,
|
|
@@ -214,6 +238,33 @@ class SplightRestClient:
|
|
|
214
238
|
)
|
|
215
239
|
return SplightResponse.from_response(raw_response)
|
|
216
240
|
|
|
241
|
+
async def async_get(
|
|
242
|
+
self,
|
|
243
|
+
url: URLTypes,
|
|
244
|
+
*,
|
|
245
|
+
params: Optional[QueryParamTypes] = None,
|
|
246
|
+
headers: Optional[HeaderTypes] = None,
|
|
247
|
+
cookies: Optional[CookieTypes] = None,
|
|
248
|
+
auth: Union[AuthTypes, DefaultClient] = DEFAULT_CLIENT,
|
|
249
|
+
allow_redirects: Union[bool, DefaultClient] = DEFAULT_CLIENT,
|
|
250
|
+
timeout: Union[TimeoutTypes, DefaultClient] = DEFAULT_CLIENT,
|
|
251
|
+
) -> SplightResponse:
|
|
252
|
+
"""Send a GET request to the specified URL.
|
|
253
|
+
|
|
254
|
+
Parameters: See class docstring.
|
|
255
|
+
"""
|
|
256
|
+
raw_response = await self._async_client.request(
|
|
257
|
+
self._GET_METHOD,
|
|
258
|
+
str(url),
|
|
259
|
+
params=params,
|
|
260
|
+
headers=headers,
|
|
261
|
+
cookies=cookies,
|
|
262
|
+
auth=auth,
|
|
263
|
+
follow_redirects=allow_redirects,
|
|
264
|
+
timeout=timeout,
|
|
265
|
+
)
|
|
266
|
+
return SplightResponse.from_response(raw_response)
|
|
267
|
+
|
|
217
268
|
def options(
|
|
218
269
|
self,
|
|
219
270
|
url: URLTypes,
|
|
@@ -301,6 +352,39 @@ class SplightRestClient:
|
|
|
301
352
|
)
|
|
302
353
|
return SplightResponse.from_response(raw_response)
|
|
303
354
|
|
|
355
|
+
async def async_post(
|
|
356
|
+
self,
|
|
357
|
+
url: URLTypes,
|
|
358
|
+
*,
|
|
359
|
+
data: Optional[RequestData] = None,
|
|
360
|
+
files: Optional[RequestFiles] = None,
|
|
361
|
+
json: Optional[Any] = None,
|
|
362
|
+
params: Optional[QueryParamTypes] = None,
|
|
363
|
+
headers: Optional[HeaderTypes] = None,
|
|
364
|
+
cookies: Optional[CookieTypes] = None,
|
|
365
|
+
auth: Union[AuthTypes, DefaultClient] = DEFAULT_CLIENT,
|
|
366
|
+
allow_redirects: Union[bool, DefaultClient] = DEFAULT_CLIENT,
|
|
367
|
+
timeout: Union[TimeoutTypes, DefaultClient] = DEFAULT_CLIENT,
|
|
368
|
+
) -> SplightResponse:
|
|
369
|
+
"""Send a POST request to the specified URL.
|
|
370
|
+
|
|
371
|
+
Parameters: See class docstring.
|
|
372
|
+
"""
|
|
373
|
+
raw_response = await self._async_client.request(
|
|
374
|
+
self._POST_METHOD,
|
|
375
|
+
str(url),
|
|
376
|
+
data=data,
|
|
377
|
+
files=files,
|
|
378
|
+
json=json,
|
|
379
|
+
params=params,
|
|
380
|
+
headers=headers,
|
|
381
|
+
cookies=cookies,
|
|
382
|
+
auth=auth,
|
|
383
|
+
follow_redirects=allow_redirects,
|
|
384
|
+
timeout=timeout,
|
|
385
|
+
)
|
|
386
|
+
return SplightResponse.from_response(raw_response)
|
|
387
|
+
|
|
304
388
|
def put(
|
|
305
389
|
self,
|
|
306
390
|
url: URLTypes,
|
|
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
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/__init__.py
RENAMED
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/abstract.py
RENAMED
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/classmap.py
RENAMED
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/exceptions.py
RENAMED
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/local_client.py
RENAMED
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/communication/remote_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/local_client.py
RENAMED
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/database/remote_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/client/datalake/local_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{splight-lib-4.0.0.dev6 → splight-lib-4.3.0.dev1}/splight_lib/communication/event_handler.py
RENAMED
|
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
|
|
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
|