singlestoredb 1.6.3__cp38-abi3-win32.whl → 1.7.1__cp38-abi3-win32.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 CHANGED
Binary file
singlestoredb/__init__.py CHANGED
@@ -13,7 +13,7 @@ Examples
13
13
 
14
14
  """
15
15
 
16
- __version__ = '1.6.3'
16
+ __version__ = '1.7.1'
17
17
 
18
18
  from typing import Any
19
19
 
@@ -0,0 +1,2 @@
1
+ from ._cloud_functions import run_function_app # noqa: F401
2
+ from ._dashboards import run_dashboard_app # noqa: F401
@@ -0,0 +1,92 @@
1
+ import asyncio
2
+ import textwrap
3
+ import typing
4
+ import urllib.parse
5
+
6
+ from ._config import AppConfig
7
+ from ._connection_info import ConnectionInfo
8
+ from ._process import kill_process_by_port
9
+
10
+ if typing.TYPE_CHECKING:
11
+ from fastapi import FastAPI
12
+ from ._uvicorn_util import AwaitableUvicornServer
13
+
14
+ # Keep track of currently running server
15
+ _running_server: 'typing.Optional[AwaitableUvicornServer]' = None
16
+
17
+
18
+ async def run_function_app(
19
+ app: 'FastAPI',
20
+ log_level: str = 'error',
21
+ kill_existing_app_server: bool = True,
22
+ ) -> ConnectionInfo:
23
+ global _running_server
24
+ from ._uvicorn_util import AwaitableUvicornServer
25
+
26
+ try:
27
+ import uvicorn
28
+ except ImportError:
29
+ raise ImportError('package uvicorn is required to run cloud functions')
30
+ try:
31
+ import fastapi
32
+ except ImportError:
33
+ raise ImportError('package fastapi is required to run cloud functions')
34
+
35
+ if not isinstance(app, fastapi.FastAPI):
36
+ raise TypeError('app is not an instance of FastAPI')
37
+
38
+ app_config = AppConfig.from_env()
39
+
40
+ if kill_existing_app_server:
41
+ # Shutdown the server gracefully if it was started by us.
42
+ # Since the uvicorn server doesn't start a new subprocess
43
+ # killing the process would result in kernel dying.
44
+ if _running_server is not None:
45
+ await _running_server.shutdown()
46
+ _running_server = None
47
+
48
+ # Kill if any other process is occupying the port
49
+ kill_process_by_port(app_config.listen_port)
50
+
51
+ # Add `GET /` route, used for liveness check
52
+ @app.get('/')
53
+ def ping() -> str:
54
+ return 'Success!'
55
+
56
+ base_path = urllib.parse.urlparse(app_config.base_url).path
57
+ app.root_path = base_path
58
+
59
+ config = uvicorn.Config(
60
+ app,
61
+ host='0.0.0.0',
62
+ port=app_config.listen_port,
63
+ log_level=log_level,
64
+ )
65
+ _running_server = AwaitableUvicornServer(config)
66
+
67
+ asyncio.create_task(_running_server.serve())
68
+ await _running_server.wait_for_startup()
69
+
70
+ connection_info = ConnectionInfo(app_config.base_url, app_config.token)
71
+
72
+ if app_config.running_interactively:
73
+ if app_config.is_gateway_enabled:
74
+ print(
75
+ 'Cloud function available at '
76
+ f'{app_config.base_url}docs?authToken={app_config.token}',
77
+ )
78
+ else:
79
+ curl_header = f'-H "Authorization: Bearer {app_config.token}"'
80
+ curl_example = f'curl "{app_config.base_url}" {curl_header}'
81
+ print(
82
+ textwrap.dedent(f"""
83
+ Cloud function available at {app_config.base_url}
84
+
85
+ Auth Token: {app_config.token}
86
+
87
+ Curl example: {curl_example}
88
+
89
+ """).strip(),
90
+ )
91
+
92
+ return connection_info
@@ -0,0 +1,63 @@
1
+ import os
2
+ from dataclasses import dataclass
3
+ from typing import Optional
4
+
5
+
6
+ @dataclass
7
+ class AppConfig:
8
+ listen_port: int
9
+ base_url: str
10
+ app_token: Optional[str]
11
+ user_token: Optional[str]
12
+ running_interactively: bool
13
+ is_gateway_enabled: bool
14
+
15
+ @staticmethod
16
+ def _read_variable(name: str) -> str:
17
+ value = os.environ.get(name)
18
+ if value is None:
19
+ raise RuntimeError(
20
+ f'Missing {name} environment variable. '
21
+ 'Is the code running outside SingleStoreDB notebook environment?',
22
+ )
23
+ return value
24
+
25
+ @classmethod
26
+ def from_env(cls) -> 'AppConfig':
27
+ port = cls._read_variable('SINGLESTOREDB_APP_LISTEN_PORT')
28
+ base_url = cls._read_variable('SINGLESTOREDB_APP_BASE_URL')
29
+
30
+ workload_type = os.environ.get('SINGLESTOREDB_WORKLOAD_TYPE')
31
+ running_interactively = workload_type == 'InteractiveNotebook'
32
+
33
+ is_gateway_enabled = 'SINGLESTOREDB_NOVA_GATEWAY_ENDPOINT' in os.environ
34
+
35
+ app_token = os.environ.get('SINGLESTOREDB_APP_TOKEN')
36
+ user_token = os.environ.get('SINGLESTOREDB_USER_TOKEN')
37
+
38
+ # Make sure the required variables are present
39
+ # and present useful error message if not
40
+ if running_interactively:
41
+ if is_gateway_enabled:
42
+ app_token = cls._read_variable('SINGLESTOREDB_APP_TOKEN')
43
+ else:
44
+ user_token = cls._read_variable('SINGLESTOREDB_USER_TOKEN')
45
+
46
+ return cls(
47
+ listen_port=int(port),
48
+ base_url=base_url,
49
+ app_token=app_token,
50
+ user_token=user_token,
51
+ running_interactively=running_interactively,
52
+ is_gateway_enabled=is_gateway_enabled,
53
+ )
54
+
55
+ @property
56
+ def token(self) -> Optional[str]:
57
+ """
58
+ Returns None if running non-interactively
59
+ """
60
+ if self.is_gateway_enabled:
61
+ return self.app_token
62
+ else:
63
+ return self.user_token
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+
5
+ @dataclass
6
+ class ConnectionInfo:
7
+ url: str
8
+
9
+ # Only present in interactive mode
10
+ token: Optional[str]
@@ -0,0 +1,55 @@
1
+ import typing
2
+ import urllib.parse
3
+
4
+ from ._config import AppConfig
5
+ from ._process import kill_process_by_port
6
+ from ._stdout_supress import StdoutSuppressor
7
+ from singlestoredb.apps._connection_info import ConnectionInfo
8
+
9
+ if typing.TYPE_CHECKING:
10
+ from plotly.graph_objs import Figure
11
+
12
+
13
+ async def run_dashboard_app(
14
+ figure: 'Figure',
15
+ debug: bool = False,
16
+ kill_existing_app_server: bool = True,
17
+ ) -> ConnectionInfo:
18
+ try:
19
+ import dash
20
+ except ImportError:
21
+ raise ImportError('package dash is required to run dashboards')
22
+
23
+ try:
24
+ from plotly.graph_objs import Figure
25
+ except ImportError:
26
+ raise ImportError('package dash is required to run dashboards')
27
+
28
+ if not isinstance(figure, Figure):
29
+ raise TypeError('figure is not an instance of plotly Figure')
30
+
31
+ app_config = AppConfig.from_env()
32
+
33
+ if kill_existing_app_server:
34
+ kill_process_by_port(app_config.listen_port)
35
+
36
+ base_path = urllib.parse.urlparse(app_config.base_url).path
37
+
38
+ app = dash.Dash(requests_pathname_prefix=base_path)
39
+ app.layout = dash.html.Div(
40
+ [
41
+ dash.dcc.Graph(figure=figure),
42
+ ],
43
+ )
44
+
45
+ with StdoutSuppressor():
46
+ app.run(
47
+ host='0.0.0.0',
48
+ debug=debug,
49
+ port=str(app_config.listen_port),
50
+ jupyter_mode='external',
51
+ )
52
+
53
+ if app_config.running_interactively:
54
+ print(f'Dash app available at {app_config.base_url}?authToken={app_config.token}')
55
+ return ConnectionInfo(app_config.base_url, app_config.token)
@@ -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,30 @@
1
+ import io
2
+ import sys
3
+ from typing import Optional
4
+
5
+
6
+ class StdoutSuppressor:
7
+ """
8
+ Supresses the stdout for code executed within the context.
9
+ This should not be used for asynchronous or threaded executions.
10
+
11
+ ```py
12
+ with Supressor():
13
+ print("This won't be printed")
14
+ ```
15
+
16
+ """
17
+
18
+ def __enter__(self) -> None:
19
+ self.stdout = sys.stdout
20
+ self.buffer = io.StringIO()
21
+ sys.stdout = self.buffer
22
+
23
+ def __exit__(
24
+ self,
25
+ exc_type: Optional[object],
26
+ exc_value: Optional[Exception],
27
+ exc_traceback: Optional[str],
28
+ ) -> None:
29
+ del self.buffer
30
+ sys.stdout = self.stdout
@@ -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
@@ -1432,6 +1432,8 @@ class TestConnection(unittest.TestCase):
1432
1432
  conn.close()
1433
1433
 
1434
1434
  def test_alltypes_polars(self):
1435
+ self.skipTest('Polars API needs to be fixed')
1436
+
1435
1437
  if self.conn.driver in ['http', 'https']:
1436
1438
  self.skipTest('Data API does not surface unsigned int information')
1437
1439
 
@@ -1574,6 +1576,8 @@ class TestConnection(unittest.TestCase):
1574
1576
  conn.close()
1575
1577
 
1576
1578
  def test_alltypes_no_nulls_polars(self):
1579
+ self.skipTest('Polars API needs to be fixed')
1580
+
1577
1581
  if self.conn.driver in ['http', 'https']:
1578
1582
  self.skipTest('Data API does not surface unsigned int information')
1579
1583
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: singlestoredb
3
- Version: 1.6.3
3
+ Version: 1.7.1
4
4
  Summary: Interface to the SingleStoreDB database and workspace management APIs
5
5
  Home-page: https://github.com/singlestore-labs/singlestoredb-python
6
6
  Author: SingleStore
@@ -1,5 +1,5 @@
1
- _singlestoredb_accel.pyd,sha256=l1XvUL0_WR4DaSMvC6OgddP4vAsZPnV70dBcs1dYJjs,59904
2
- singlestoredb/__init__.py,sha256=SpL4-XLoAIa7QepM8vcdktkpliC5inG2FiqFYdZ181o,1697
1
+ _singlestoredb_accel.pyd,sha256=KdlkBJxYQkN4_MADnJivWKHOzB00c426-dHZyF80ILI,59904
2
+ singlestoredb/__init__.py,sha256=4dlpi9Hk7ik-4oxgBJpbDyiHtAcj-ubp6VxrfeU6GQQ,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,14 @@ 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=vhAnGaRPQ0_mC15oP7xH6hlyaakoaIzXBXEJOmF_Jro,2869
15
+ singlestoredb/apps/_config.py,sha256=jwWA45sx5blwelF-aE5nkzlqhi5bjuUbOiGg4j1EsRA,2059
16
+ singlestoredb/apps/_connection_info.py,sha256=t8hFOSRALXt5tqvDX0fholKoT148xktE5r__SYdH6Dk,185
17
+ singlestoredb/apps/_dashboards.py,sha256=I2UDSWdC8fRTt8Y8QKz77boFMZ-mAOBZgiZDirTkaS4,1609
18
+ singlestoredb/apps/_process.py,sha256=eMiBO4piaRX1S6zdnMx0X0E4J7E1XrXndnVW0GRYq1Y,976
19
+ singlestoredb/apps/_stdout_supress.py,sha256=QRV-IHQQMvWMeJfqORuVE2-Il6ohO2Ti4IokFoTCJWE,689
20
+ singlestoredb/apps/_uvicorn_util.py,sha256=Petkmq5keBPfXZsHBrnZfY3O2rUHvb3Cw6o-BRz5MP0,994
13
21
  singlestoredb/functions/__init__.py,sha256=EVxqWOCcXiIX4Yj7rljAYBBoVbTvm2KSuKSkMBDnEeU,42
14
22
  singlestoredb/functions/decorator.py,sha256=M103c1JAZfyGFQAU4uJ_J8XGGH3InhcfrNUCoEORNFQ,5335
15
23
  singlestoredb/functions/dtypes.py,sha256=5IwMSaSzxtSowxXrm5hZXW1lpNm6QILxiU4mAUEkBO0,32854
@@ -93,7 +101,7 @@ singlestoredb/tests/test.sql,sha256=winJzhZ2W52PcQ1j8X_NNIDRBmEa-xMAYrS_hoUtAP8,
93
101
  singlestoredb/tests/test2.sql,sha256=CEM8_lX189iQU65G3Pod7lig6osfrveQyoDz6HC35YQ,38
94
102
  singlestoredb/tests/test_basics.py,sha256=tLiR46qUy8-OHHRSHsQTBp5q9NAwNPR9HvfutI6YgnM,47629
95
103
  singlestoredb/tests/test_config.py,sha256=Ad0PDmCnJMOyy9f7WTKiRasSR_3mYRByUlSb7k5ZySg,11502
96
- singlestoredb/tests/test_connection.py,sha256=UmoNo8erkcifEMbHZl83yAaRsyh6HANRdEtY3aViOK8,122792
104
+ singlestoredb/tests/test_connection.py,sha256=BxbQDR9wav43CJTLuC-LRpqbN13uXAWdyC68Uby7zq0,122906
97
105
  singlestoredb/tests/test_dbapi.py,sha256=cNJoTEZvYG7ckcwT7xqlkJX-2TDEYGTDDU1Igucp48k,679
98
106
  singlestoredb/tests/test_exceptions.py,sha256=vscMYmdOJr0JmkTAJrNI2w0Q96Nfugjkrt5_lYnw8i0,1176
99
107
  singlestoredb/tests/test_ext_func.py,sha256=gQErR-wAN8BqLNG5U4pNbg4qkQEo6Re8Hd9_Ztqo1RM,38550
@@ -117,9 +125,9 @@ singlestoredb/utils/events.py,sha256=rC9cHAetua_E1f-EiFkFM-gJzQSQIH5Uk-4sspC3KjI
117
125
  singlestoredb/utils/mogrify.py,sha256=gCcn99-vgsGVjTUV7RHJ6hH4vCNrsGB_Xo4z8kiSPDQ,4201
118
126
  singlestoredb/utils/results.py,sha256=wR70LhCqlobniZf52r67zYLBOKjWHQm68NAskdRQND8,15862
119
127
  singlestoredb/utils/xdict.py,sha256=-wi1lSPTnY99fhVMBhPKJ8cCsQhNG4GMUfkEBDKYgCw,13321
120
- singlestoredb-1.6.3.dist-info/LICENSE,sha256=Bojenzui8aPNjlF3w4ojguDP7sTf8vFV_9Gc2UAG1sg,11542
121
- singlestoredb-1.6.3.dist-info/METADATA,sha256=vTNnIJWmEAPO7KExTeaoD9Xp2h8-_BNHUUhDq2lZU-U,5710
122
- singlestoredb-1.6.3.dist-info/WHEEL,sha256=c4k7z5HB0t-y0nBCv6KyJ6KCjn8SEGPddD0lhaPtU3E,96
123
- singlestoredb-1.6.3.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
124
- singlestoredb-1.6.3.dist-info/top_level.txt,sha256=SDtemIXf-Kp-_F2f_S6x0db33cHGOILdAEsIQZe2LZc,35
125
- singlestoredb-1.6.3.dist-info/RECORD,,
128
+ singlestoredb-1.7.1.dist-info/LICENSE,sha256=Bojenzui8aPNjlF3w4ojguDP7sTf8vFV_9Gc2UAG1sg,11542
129
+ singlestoredb-1.7.1.dist-info/METADATA,sha256=CxgmpzV16Zhzganmk01nIGuSNxxYKyrejdc2efZxPss,5710
130
+ singlestoredb-1.7.1.dist-info/WHEEL,sha256=c4k7z5HB0t-y0nBCv6KyJ6KCjn8SEGPddD0lhaPtU3E,96
131
+ singlestoredb-1.7.1.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
132
+ singlestoredb-1.7.1.dist-info/top_level.txt,sha256=SDtemIXf-Kp-_F2f_S6x0db33cHGOILdAEsIQZe2LZc,35
133
+ singlestoredb-1.7.1.dist-info/RECORD,,