singlestoredb 1.6.3__cp38-abi3-win_amd64.whl → 1.7.0__cp38-abi3-win_amd64.whl
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 singlestoredb might be problematic. Click here for more details.
- _singlestoredb_accel.pyd +0 -0
- singlestoredb/__init__.py +1 -1
- singlestoredb/apps/__init__.py +2 -0
- singlestoredb/apps/_cloud_functions.py +70 -0
- singlestoredb/apps/_config.py +33 -0
- singlestoredb/apps/_dashboards.py +51 -0
- singlestoredb/apps/_process.py +32 -0
- singlestoredb/apps/_uvicorn_util.py +32 -0
- {singlestoredb-1.6.3.dist-info → singlestoredb-1.7.0.dist-info}/METADATA +1 -1
- {singlestoredb-1.6.3.dist-info → singlestoredb-1.7.0.dist-info}/RECORD +14 -8
- {singlestoredb-1.6.3.dist-info → singlestoredb-1.7.0.dist-info}/LICENSE +0 -0
- {singlestoredb-1.6.3.dist-info → singlestoredb-1.7.0.dist-info}/WHEEL +0 -0
- {singlestoredb-1.6.3.dist-info → singlestoredb-1.7.0.dist-info}/entry_points.txt +0 -0
- {singlestoredb-1.6.3.dist-info → singlestoredb-1.7.0.dist-info}/top_level.txt +0 -0
_singlestoredb_accel.pyd
CHANGED
|
Binary file
|
singlestoredb/__init__.py
CHANGED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import typing
|
|
3
|
+
import urllib.parse
|
|
4
|
+
|
|
5
|
+
from ._config import AppConfig
|
|
6
|
+
from ._process import kill_process_by_port
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from fastapi import FastAPI
|
|
10
|
+
from ._uvicorn_util import AwaitableUvicornServer
|
|
11
|
+
|
|
12
|
+
# Keep track of currently running server
|
|
13
|
+
_running_server: 'typing.Optional[AwaitableUvicornServer]' = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
async def run_function_app(
|
|
17
|
+
app: 'FastAPI',
|
|
18
|
+
log_level: str = 'error',
|
|
19
|
+
kill_existing_app_server: bool = True,
|
|
20
|
+
) -> None:
|
|
21
|
+
|
|
22
|
+
global _running_server
|
|
23
|
+
from ._uvicorn_util import AwaitableUvicornServer
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import uvicorn
|
|
27
|
+
except ImportError:
|
|
28
|
+
raise ImportError('package uvicorn is required to run cloud functions')
|
|
29
|
+
try:
|
|
30
|
+
import fastapi
|
|
31
|
+
except ImportError:
|
|
32
|
+
raise ImportError('package fastapi is required to run cloud functions')
|
|
33
|
+
|
|
34
|
+
if not isinstance(app, fastapi.FastAPI):
|
|
35
|
+
raise TypeError('app is not an instance of FastAPI')
|
|
36
|
+
|
|
37
|
+
app_config = AppConfig.from_env()
|
|
38
|
+
|
|
39
|
+
if kill_existing_app_server:
|
|
40
|
+
# Shutdown the server gracefully if it was started by us.
|
|
41
|
+
# Since the uvicorn server doesn't start a new subprocess
|
|
42
|
+
# killing the process would result in kernel dying.
|
|
43
|
+
if _running_server is not None:
|
|
44
|
+
await _running_server.shutdown()
|
|
45
|
+
_running_server = None
|
|
46
|
+
|
|
47
|
+
# Kill if any other process is occupying the port
|
|
48
|
+
kill_process_by_port(app_config.listen_port)
|
|
49
|
+
|
|
50
|
+
# Add `GET /` route, used for liveness check
|
|
51
|
+
@app.get('/')
|
|
52
|
+
def ping() -> str:
|
|
53
|
+
return 'Success!'
|
|
54
|
+
|
|
55
|
+
base_path = urllib.parse.urlparse(app_config.url).path
|
|
56
|
+
app.root_path = base_path
|
|
57
|
+
|
|
58
|
+
config = uvicorn.Config(
|
|
59
|
+
app,
|
|
60
|
+
host='0.0.0.0',
|
|
61
|
+
port=app_config.listen_port,
|
|
62
|
+
log_level=log_level,
|
|
63
|
+
)
|
|
64
|
+
_running_server = AwaitableUvicornServer(config)
|
|
65
|
+
|
|
66
|
+
asyncio.create_task(_running_server.serve())
|
|
67
|
+
await _running_server.wait_for_startup()
|
|
68
|
+
|
|
69
|
+
if app_config.running_interactively:
|
|
70
|
+
print(f'Cloud function available at {app_config.url}')
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class AppConfig:
|
|
7
|
+
listen_port: int
|
|
8
|
+
url: str
|
|
9
|
+
running_interactively: bool
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
def from_env(cls) -> 'AppConfig':
|
|
13
|
+
port = os.environ.get('SINGLESTOREDB_APP_LISTEN_PORT')
|
|
14
|
+
if port is None:
|
|
15
|
+
raise RuntimeError(
|
|
16
|
+
'Missing SINGLESTOREDB_APP_LISTEN_PORT environment variable. '
|
|
17
|
+
'Is the code running outside SingleStoreDB notebook environment?',
|
|
18
|
+
)
|
|
19
|
+
url = os.environ.get('SINGLESTOREDB_APP_URL')
|
|
20
|
+
if url is None:
|
|
21
|
+
raise RuntimeError(
|
|
22
|
+
'Missing SINGLESTOREDB_APP_URL environment variable. '
|
|
23
|
+
'Is the code running outside SingleStoreDB notebook environment?',
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
workload_type = os.environ.get('SINGLESTOREDB_WORKLOAD_TYPE')
|
|
27
|
+
running_interactively = workload_type == 'InteractiveNotebook'
|
|
28
|
+
|
|
29
|
+
return cls(
|
|
30
|
+
listen_port=int(port),
|
|
31
|
+
url=url,
|
|
32
|
+
running_interactively=running_interactively,
|
|
33
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
import urllib.parse
|
|
3
|
+
|
|
4
|
+
from ._config import AppConfig
|
|
5
|
+
from ._process import kill_process_by_port
|
|
6
|
+
|
|
7
|
+
if typing.TYPE_CHECKING:
|
|
8
|
+
from plotly.graph_objs import Figure
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def run_dashboard_app(
|
|
12
|
+
figure: 'Figure',
|
|
13
|
+
debug: bool = False,
|
|
14
|
+
kill_existing_app_server: bool = True,
|
|
15
|
+
) -> None:
|
|
16
|
+
try:
|
|
17
|
+
import dash
|
|
18
|
+
except ImportError:
|
|
19
|
+
raise ImportError('package dash is required to run dashboards')
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from plotly.graph_objs import Figure
|
|
23
|
+
except ImportError:
|
|
24
|
+
raise ImportError('package dash is required to run dashboards')
|
|
25
|
+
|
|
26
|
+
if not isinstance(figure, Figure):
|
|
27
|
+
raise TypeError('figure is not an instance of plotly Figure')
|
|
28
|
+
|
|
29
|
+
app_config = AppConfig.from_env()
|
|
30
|
+
|
|
31
|
+
if kill_existing_app_server:
|
|
32
|
+
kill_process_by_port(app_config.listen_port)
|
|
33
|
+
|
|
34
|
+
base_path = urllib.parse.urlparse(app_config.url).path
|
|
35
|
+
|
|
36
|
+
app = dash.Dash(requests_pathname_prefix=base_path)
|
|
37
|
+
app.layout = dash.html.Div(
|
|
38
|
+
[
|
|
39
|
+
dash.dcc.Graph(figure=figure),
|
|
40
|
+
],
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
app.run(
|
|
44
|
+
host='0.0.0.0',
|
|
45
|
+
debug=debug,
|
|
46
|
+
port=str(app_config.listen_port),
|
|
47
|
+
jupyter_mode='external',
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if app_config.running_interactively:
|
|
51
|
+
print(f'Dash app available at {app_config.url}')
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import signal
|
|
3
|
+
import typing
|
|
4
|
+
if typing.TYPE_CHECKING:
|
|
5
|
+
from psutil import Process
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def kill_process_by_port(port: int) -> None:
|
|
9
|
+
existing_process = _find_process_by_port(port)
|
|
10
|
+
kernel_pid = os.getpid()
|
|
11
|
+
# Make sure we are not killing current kernel
|
|
12
|
+
if existing_process is not None and kernel_pid != existing_process.pid:
|
|
13
|
+
print(f'Killing process {existing_process.pid} which is using port {port}')
|
|
14
|
+
os.kill(existing_process.pid, signal.SIGKILL)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _find_process_by_port(port: int) -> 'Process | None':
|
|
18
|
+
try:
|
|
19
|
+
import psutil
|
|
20
|
+
except ImportError:
|
|
21
|
+
raise ImportError('package psutil is required')
|
|
22
|
+
|
|
23
|
+
for proc in psutil.process_iter(['pid']):
|
|
24
|
+
try:
|
|
25
|
+
connections = proc.connections()
|
|
26
|
+
for conn in connections:
|
|
27
|
+
if conn.laddr.port == port:
|
|
28
|
+
return proc
|
|
29
|
+
except psutil.AccessDenied:
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
return None
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import socket
|
|
3
|
+
from typing import List
|
|
4
|
+
from typing import Optional
|
|
5
|
+
try:
|
|
6
|
+
import uvicorn
|
|
7
|
+
except ImportError:
|
|
8
|
+
raise ImportError('package uvicorn is required')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AwaitableUvicornServer(uvicorn.Server):
|
|
12
|
+
"""
|
|
13
|
+
Adds `wait_for_startup` method.
|
|
14
|
+
The function (asynchronously) blocks until the server
|
|
15
|
+
starts listening or throws an error.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, config: 'uvicorn.Config') -> None:
|
|
19
|
+
super().__init__(config)
|
|
20
|
+
self._startup_future = asyncio.get_event_loop().create_future()
|
|
21
|
+
|
|
22
|
+
async def startup(self, sockets: Optional[List[socket.socket]] = None) -> None:
|
|
23
|
+
try:
|
|
24
|
+
result = await super().startup(sockets)
|
|
25
|
+
self._startup_future.set_result(True)
|
|
26
|
+
return result
|
|
27
|
+
except Exception as error:
|
|
28
|
+
self._startup_future.set_exception(error)
|
|
29
|
+
raise error
|
|
30
|
+
|
|
31
|
+
async def wait_for_startup(self) -> None:
|
|
32
|
+
await self._startup_future
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
_singlestoredb_accel.pyd,sha256=
|
|
2
|
-
singlestoredb/__init__.py,sha256=
|
|
1
|
+
_singlestoredb_accel.pyd,sha256=gJJyyabIixf5VSFu-TZUyDH7e81IWGWM2AM5_XWlvaY,59392
|
|
2
|
+
singlestoredb/__init__.py,sha256=QfF5_hgd3Qij5ssWBZesWxdych7o-gKPfWx4G1uR-XM,1697
|
|
3
3
|
singlestoredb/auth.py,sha256=RmYiH0Wlc2RXc4pTlRMysxtBI445ggCIwojWKC_eDLE,7844
|
|
4
4
|
singlestoredb/config.py,sha256=LlrwKor_23uA9u7jWBYb-IaOKs30CjWIM7o9xCXEPcc,12721
|
|
5
5
|
singlestoredb/connection.py,sha256=QC5YQemwJOhdW_-ZBFpNLE15xFxwI1fB2LuvE1hNi9k,46812
|
|
@@ -10,6 +10,12 @@ singlestoredb/types.py,sha256=Lv0BEQl6aSZBiAe0OSI07FEJhcHZ9HX45iT9NU_mxHQ,10334
|
|
|
10
10
|
singlestoredb/ai/__init__.py,sha256=nT048t90xqjaNhz7KJ10KfSVW4RcZRoujyC6po6Nmb8,61
|
|
11
11
|
singlestoredb/ai/embeddings.py,sha256=KVvQY3viyYWXDBobFpj0xqiGRijt36zcHHlPNAfFAxA,770
|
|
12
12
|
singlestoredb/alchemy/__init__.py,sha256=bUmCl1xUn2v36RMbXLIrvgKzZSqx71mp1ReUw9JeVA8,2613
|
|
13
|
+
singlestoredb/apps/__init__.py,sha256=IKQrPKb1d_LQvmr7jXQvgPRrB5Ja_1kGAXwYvdjp6ok,120
|
|
14
|
+
singlestoredb/apps/_cloud_functions.py,sha256=v8PEBz8MXryarE0nfqjwwnZiAO1S659WeITXGjJRcC4,2106
|
|
15
|
+
singlestoredb/apps/_config.py,sha256=DzWlFdW0_wANnE5XddLQeUv8y_zRUvw8KNz7B4l9KnU,1089
|
|
16
|
+
singlestoredb/apps/_dashboards.py,sha256=FOEPjxpiQL9vBaMQBlTfpmA0b4a-cadSdTFrfjdIcY4,1323
|
|
17
|
+
singlestoredb/apps/_process.py,sha256=eMiBO4piaRX1S6zdnMx0X0E4J7E1XrXndnVW0GRYq1Y,976
|
|
18
|
+
singlestoredb/apps/_uvicorn_util.py,sha256=Petkmq5keBPfXZsHBrnZfY3O2rUHvb3Cw6o-BRz5MP0,994
|
|
13
19
|
singlestoredb/functions/__init__.py,sha256=EVxqWOCcXiIX4Yj7rljAYBBoVbTvm2KSuKSkMBDnEeU,42
|
|
14
20
|
singlestoredb/functions/decorator.py,sha256=M103c1JAZfyGFQAU4uJ_J8XGGH3InhcfrNUCoEORNFQ,5335
|
|
15
21
|
singlestoredb/functions/dtypes.py,sha256=5IwMSaSzxtSowxXrm5hZXW1lpNm6QILxiU4mAUEkBO0,32854
|
|
@@ -117,9 +123,9 @@ singlestoredb/utils/events.py,sha256=rC9cHAetua_E1f-EiFkFM-gJzQSQIH5Uk-4sspC3KjI
|
|
|
117
123
|
singlestoredb/utils/mogrify.py,sha256=gCcn99-vgsGVjTUV7RHJ6hH4vCNrsGB_Xo4z8kiSPDQ,4201
|
|
118
124
|
singlestoredb/utils/results.py,sha256=wR70LhCqlobniZf52r67zYLBOKjWHQm68NAskdRQND8,15862
|
|
119
125
|
singlestoredb/utils/xdict.py,sha256=-wi1lSPTnY99fhVMBhPKJ8cCsQhNG4GMUfkEBDKYgCw,13321
|
|
120
|
-
singlestoredb-1.
|
|
121
|
-
singlestoredb-1.
|
|
122
|
-
singlestoredb-1.
|
|
123
|
-
singlestoredb-1.
|
|
124
|
-
singlestoredb-1.
|
|
125
|
-
singlestoredb-1.
|
|
126
|
+
singlestoredb-1.7.0.dist-info/LICENSE,sha256=Bojenzui8aPNjlF3w4ojguDP7sTf8vFV_9Gc2UAG1sg,11542
|
|
127
|
+
singlestoredb-1.7.0.dist-info/METADATA,sha256=dx6R3uQafE_V3NoNKsT5mJCqTo1jrF6NdTdBqZ0rgY4,5710
|
|
128
|
+
singlestoredb-1.7.0.dist-info/WHEEL,sha256=UyMHzmWA0xVqVPKfTiLs2eN3OWWZUl-kQemNbpIqlKo,100
|
|
129
|
+
singlestoredb-1.7.0.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
|
|
130
|
+
singlestoredb-1.7.0.dist-info/top_level.txt,sha256=SDtemIXf-Kp-_F2f_S6x0db33cHGOILdAEsIQZe2LZc,35
|
|
131
|
+
singlestoredb-1.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|