singlestoredb 1.16.1__py3-none-any.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.
Files changed (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ SingleStoreDB module.
4
+
5
+ Examples
6
+ --------
7
+ >>> import singlestoredb as s2
8
+ >>> conn = s2.connect('user:password@host/dbname')
9
+ >>> cur = conn.cursor()
10
+ >>> cur.execute('select * from customers')
11
+ >>> for row in cur:
12
+ ... print(row)
13
+
14
+ """
15
+
16
+ __version__ = '1.16.1'
17
+
18
+ from typing import Any
19
+
20
+ from .config import options, get_option, set_option, describe_option
21
+ from .connection import connect, apilevel, threadsafety, paramstyle
22
+ from .exceptions import (
23
+ Warning, Error, InterfaceError, DatabaseError, OperationalError,
24
+ IntegrityError, InternalError, ProgrammingError, NotSupportedError,
25
+ DataError, ManagementError,
26
+ )
27
+ from .management import (
28
+ manage_cluster, manage_workspaces, manage_files, manage_regions,
29
+ )
30
+ from .types import (
31
+ Date, Time, Timestamp, DateFromTicks, TimeFromTicks, TimestampFromTicks,
32
+ Binary, STRING, BINARY, NUMBER, DATETIME, ROWID,
33
+ )
34
+ # These are only loaded if the singlestore-vectorstore package is available
35
+ try:
36
+ from .vectorstore import (
37
+ vector_db, IndexInterface, IndexList, IndexModel, MatchTypedDict,
38
+ Metric, IndexStatsTypedDict, NamespaceStatsTypedDict, Vector,
39
+ VectorDictMetadataValue, VectorMetadataTypedDict, VectorTuple,
40
+ VectorTupleWithMetadata, DeletionProtection, AndFilter, EqFilter,
41
+ ExactMatchFilter, FilterTypedDict, GteFilter, GtFilter, InFilter,
42
+ LteFilter, LtFilter, NeFilter, NinFilter, OrFilter, SimpleFilter,
43
+ )
44
+ except (ImportError, ModuleNotFoundError):
45
+ pass
46
+
47
+
48
+ #
49
+ # This function is defined here to prevent the side-effect of
50
+ # attempting to load the SQLAlchemy dialect in the core SDK.
51
+ #
52
+ def create_engine(*args: Any, **kwargs: Any) -> Any:
53
+ """
54
+ Create an SQLAlchemy engine for SingleStoreDB.
55
+
56
+ Parameters
57
+ ----------
58
+ **kwargs : Any
59
+ The parameters taken here are the same as for
60
+ `sqlalchemy.create_engine`. However, this function can be
61
+ called without any parameters in order to inherit parameters
62
+ set by environment variables or parameters set in by
63
+ options in Python code.
64
+
65
+ See Also
66
+ --------
67
+ `sqlalchemy.create_engine`
68
+
69
+ Returns
70
+ -------
71
+ SQLAlchemy engine
72
+
73
+ """
74
+ from .alchemy import create_engine
75
+ return create_engine(*args, **kwargs)
@@ -0,0 +1,2 @@
1
+ from .chat import SingleStoreChatFactory # noqa: F401
2
+ from .embeddings import SingleStoreEmbeddingsFactory # noqa: F401
@@ -0,0 +1,139 @@
1
+ import os
2
+ from typing import Any
3
+ from typing import Callable
4
+ from typing import Optional
5
+ from typing import Union
6
+
7
+ import httpx
8
+
9
+ from singlestoredb import manage_workspaces
10
+
11
+ try:
12
+ from langchain_openai import ChatOpenAI
13
+ except ImportError:
14
+ raise ImportError(
15
+ 'Could not import langchain_openai python package. '
16
+ 'Please install it with `pip install langchain_openai`.',
17
+ )
18
+
19
+ try:
20
+ from langchain_aws import ChatBedrockConverse
21
+ except ImportError:
22
+ raise ImportError(
23
+ 'Could not import langchain-aws python package. '
24
+ 'Please install it with `pip install langchain-aws`.',
25
+ )
26
+
27
+ import boto3
28
+ from botocore import UNSIGNED
29
+ from botocore.config import Config
30
+
31
+
32
+ def SingleStoreChatFactory(
33
+ model_name: str,
34
+ api_key: Optional[str] = None,
35
+ streaming: bool = True,
36
+ http_client: Optional[httpx.Client] = None,
37
+ obo_token_getter: Optional[Callable[[], Optional[str]]] = None,
38
+ **kwargs: Any,
39
+ ) -> Union[ChatOpenAI, ChatBedrockConverse]:
40
+ """Return a chat model instance (ChatOpenAI or ChatBedrockConverse).
41
+ """
42
+ inference_api_manager = (
43
+ manage_workspaces().organizations.current.inference_apis
44
+ )
45
+ info = inference_api_manager.get(model_name=model_name)
46
+ token_env = os.environ.get('SINGLESTOREDB_USER_TOKEN')
47
+ token = api_key if api_key is not None else token_env
48
+
49
+ if info.hosting_platform == 'Amazon':
50
+ # Instantiate Bedrock client
51
+ cfg_kwargs = {
52
+ 'signature_version': UNSIGNED,
53
+ 'retries': {'max_attempts': 1, 'mode': 'standard'},
54
+ }
55
+ # Extract timeouts from http_client if provided
56
+ t = http_client.timeout if http_client is not None else None
57
+ connect_timeout = None
58
+ read_timeout = None
59
+ if t is not None:
60
+ if isinstance(t, httpx.Timeout):
61
+ if t.connect is not None:
62
+ connect_timeout = float(t.connect)
63
+ if t.read is not None:
64
+ read_timeout = float(t.read)
65
+ if connect_timeout is None and read_timeout is not None:
66
+ connect_timeout = read_timeout
67
+ if read_timeout is None and connect_timeout is not None:
68
+ read_timeout = connect_timeout
69
+ elif isinstance(t, (int, float)):
70
+ connect_timeout = float(t)
71
+ read_timeout = float(t)
72
+ if read_timeout is not None:
73
+ cfg_kwargs['read_timeout'] = read_timeout
74
+ if connect_timeout is not None:
75
+ cfg_kwargs['connect_timeout'] = connect_timeout
76
+
77
+ cfg = Config(**cfg_kwargs)
78
+ client = boto3.client(
79
+ 'bedrock-runtime',
80
+ endpoint_url=info.connection_url,
81
+ region_name='us-east-1',
82
+ aws_access_key_id='placeholder',
83
+ aws_secret_access_key='placeholder',
84
+ config=cfg,
85
+ )
86
+
87
+ def _inject_headers(request: Any, **_ignored: Any) -> None:
88
+ """Inject dynamic auth/OBO headers prior to Bedrock sending."""
89
+ if obo_token_getter is not None:
90
+ obo_val = obo_token_getter()
91
+ if obo_val:
92
+ request.headers['X-S2-OBO'] = obo_val
93
+ if token:
94
+ request.headers['Authorization'] = f'Bearer {token}'
95
+ request.headers.pop('X-Amz-Date', None)
96
+ request.headers.pop('X-Amz-Security-Token', None)
97
+
98
+ emitter = client._endpoint._event_emitter
99
+ emitter.register_first(
100
+ 'before-send.bedrock-runtime.Converse',
101
+ _inject_headers,
102
+ )
103
+ emitter.register_first(
104
+ 'before-send.bedrock-runtime.ConverseStream',
105
+ _inject_headers,
106
+ )
107
+ emitter.register_first(
108
+ 'before-send.bedrock-runtime.InvokeModel',
109
+ _inject_headers,
110
+ )
111
+ emitter.register_first(
112
+ 'before-send.bedrock-runtime.InvokeModelWithResponseStream',
113
+ _inject_headers,
114
+ )
115
+
116
+ return ChatBedrockConverse(
117
+ model_id=model_name,
118
+ endpoint_url=info.connection_url,
119
+ region_name='us-east-1',
120
+ aws_access_key_id='placeholder',
121
+ aws_secret_access_key='placeholder',
122
+ disable_streaming=not streaming,
123
+ client=client,
124
+ **kwargs,
125
+ )
126
+
127
+ # OpenAI / Azure OpenAI path
128
+ openai_kwargs = dict(
129
+ base_url=info.connection_url,
130
+ api_key=token,
131
+ model=model_name,
132
+ streaming=streaming,
133
+ )
134
+ if http_client is not None:
135
+ openai_kwargs['http_client'] = http_client
136
+ return ChatOpenAI(
137
+ **openai_kwargs,
138
+ **kwargs,
139
+ )
@@ -0,0 +1,128 @@
1
+ import os
2
+ from typing import Any
3
+ from typing import Callable
4
+ from typing import Optional
5
+ from typing import Union
6
+
7
+ import httpx
8
+
9
+ from singlestoredb import manage_workspaces
10
+
11
+ try:
12
+ from langchain_openai import OpenAIEmbeddings
13
+ except ImportError:
14
+ raise ImportError(
15
+ 'Could not import langchain_openai python package. '
16
+ 'Please install it with `pip install langchain_openai`.',
17
+ )
18
+
19
+ try:
20
+ from langchain_aws import BedrockEmbeddings
21
+ except ImportError:
22
+ raise ImportError(
23
+ 'Could not import langchain-aws python package. '
24
+ 'Please install it with `pip install langchain-aws`.',
25
+ )
26
+
27
+ import boto3
28
+ from botocore import UNSIGNED
29
+ from botocore.config import Config
30
+
31
+
32
+ def SingleStoreEmbeddingsFactory(
33
+ model_name: str,
34
+ api_key: Optional[str] = None,
35
+ http_client: Optional[httpx.Client] = None,
36
+ obo_token_getter: Optional[Callable[[], Optional[str]]] = None,
37
+ **kwargs: Any,
38
+ ) -> Union[OpenAIEmbeddings, BedrockEmbeddings]:
39
+ """Return an embeddings model instance (OpenAIEmbeddings or BedrockEmbeddings).
40
+ """
41
+ inference_api_manager = (
42
+ manage_workspaces().organizations.current.inference_apis
43
+ )
44
+ info = inference_api_manager.get(model_name=model_name)
45
+ token_env = os.environ.get('SINGLESTOREDB_USER_TOKEN')
46
+ token = api_key if api_key is not None else token_env
47
+
48
+ if info.hosting_platform == 'Amazon':
49
+ # Instantiate Bedrock client
50
+ cfg_kwargs = {
51
+ 'signature_version': UNSIGNED,
52
+ 'retries': {'max_attempts': 1, 'mode': 'standard'},
53
+ }
54
+ # Extract timeouts from http_client if provided
55
+ t = http_client.timeout if http_client is not None else None
56
+ connect_timeout = None
57
+ read_timeout = None
58
+ if t is not None:
59
+ if isinstance(t, httpx.Timeout):
60
+ if t.connect is not None:
61
+ connect_timeout = float(t.connect)
62
+ if t.read is not None:
63
+ read_timeout = float(t.read)
64
+ if connect_timeout is None and read_timeout is not None:
65
+ connect_timeout = read_timeout
66
+ if read_timeout is None and connect_timeout is not None:
67
+ read_timeout = connect_timeout
68
+ elif isinstance(t, (int, float)):
69
+ connect_timeout = float(t)
70
+ read_timeout = float(t)
71
+ if read_timeout is not None:
72
+ cfg_kwargs['read_timeout'] = read_timeout
73
+ if connect_timeout is not None:
74
+ cfg_kwargs['connect_timeout'] = connect_timeout
75
+
76
+ cfg = Config(**cfg_kwargs)
77
+ client = boto3.client(
78
+ 'bedrock-runtime',
79
+ endpoint_url=info.connection_url,
80
+ region_name='us-east-1',
81
+ aws_access_key_id='placeholder',
82
+ aws_secret_access_key='placeholder',
83
+ config=cfg,
84
+ )
85
+
86
+ def _inject_headers(request: Any, **_ignored: Any) -> None:
87
+ """Inject dynamic auth/OBO headers prior to Bedrock sending."""
88
+ if obo_token_getter is not None:
89
+ obo_val = obo_token_getter()
90
+ if obo_val:
91
+ request.headers['X-S2-OBO'] = obo_val
92
+ if token:
93
+ request.headers['Authorization'] = f'Bearer {token}'
94
+ request.headers.pop('X-Amz-Date', None)
95
+ request.headers.pop('X-Amz-Security-Token', None)
96
+
97
+ emitter = client._endpoint._event_emitter
98
+ emitter.register_first(
99
+ 'before-send.bedrock-runtime.InvokeModel',
100
+ _inject_headers,
101
+ )
102
+ emitter.register_first(
103
+ 'before-send.bedrock-runtime.InvokeModelWithResponseStream',
104
+ _inject_headers,
105
+ )
106
+
107
+ return BedrockEmbeddings(
108
+ model_id=model_name,
109
+ endpoint_url=info.connection_url,
110
+ region_name='us-east-1',
111
+ aws_access_key_id='placeholder',
112
+ aws_secret_access_key='placeholder',
113
+ client=client,
114
+ **kwargs,
115
+ )
116
+
117
+ # OpenAI / Azure OpenAI path
118
+ openai_kwargs = dict(
119
+ base_url=info.connection_url,
120
+ api_key=token,
121
+ model=model_name,
122
+ )
123
+ if http_client is not None:
124
+ openai_kwargs['http_client'] = http_client
125
+ return OpenAIEmbeddings(
126
+ **openai_kwargs,
127
+ **kwargs,
128
+ )
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env python3
2
+ import inspect
3
+ from typing import Any
4
+ from urllib.parse import quote
5
+
6
+ try:
7
+ import sqlalchemy
8
+ from sqlalchemy_singlestoredb import * # noqa: F403, F401
9
+ has_sqlalchemy = True
10
+ except ImportError:
11
+ import warnings
12
+ warnings.warn(
13
+ 'sqlalchemy_singlestoredb must be installed to use this module',
14
+ RuntimeWarning,
15
+ )
16
+ has_sqlalchemy = False
17
+
18
+ from ..connection import build_params
19
+ from ..connection import connect
20
+
21
+
22
+ def create_engine(*args: Any, **kwargs: Any) -> Any:
23
+ """
24
+ Create an SQLAlchemy engine for SingleStoreDB.
25
+
26
+ Parameters
27
+ ----------
28
+ **kwargs : Any
29
+ The parameters taken here are the same as for
30
+ `sqlalchemy.create_engine`. However, this function can be
31
+ called without any parameters in order to inherit parameters
32
+ set by environment variables or parameters set in by
33
+ options in Python code.
34
+
35
+ See Also
36
+ --------
37
+ `sqlalchemy.create_engine`
38
+
39
+ Returns
40
+ -------
41
+ SQLAlchemy engine
42
+
43
+ """
44
+ if not has_sqlalchemy:
45
+ raise RuntimeError('sqlalchemy_singlestoredb package is not installed')
46
+
47
+ if len(args) > 1:
48
+ raise ValueError(
49
+ '`args` can only have a single element '
50
+ 'containing the database URL',
51
+ )
52
+
53
+ if args:
54
+ kwargs['host'] = args[0]
55
+
56
+ conn_params = {}
57
+ sa_params = {}
58
+
59
+ conn_args = inspect.getfullargspec(connect).args
60
+
61
+ for key, value in kwargs.items():
62
+ if key in conn_args:
63
+ conn_params[key] = value
64
+ else:
65
+ sa_params[key] = value
66
+
67
+ params = build_params(**conn_params)
68
+ driver = params.pop('driver', None)
69
+ host = params.pop('host')
70
+ port = params.pop('port')
71
+ user = params.pop('user', None)
72
+ password = params.pop('password', None)
73
+ database = params.pop('database', '')
74
+
75
+ if not driver:
76
+ driver = 'singlestoredb+mysql'
77
+ elif not driver.startswith('singlestoredb'):
78
+ driver = f'singlestoredb+{driver}'
79
+
80
+ if user is not None and password is not None:
81
+ url = f'{driver}://{quote(user)}:{quote(password)}@' \
82
+ f'{host}:{port}/{quote(database)}'
83
+ elif user is not None:
84
+ url = f'{driver}://{quote(user)}@{host}:{port}/{quote(database)}'
85
+ elif password is not None:
86
+ url = f'{driver}://:{quote(password)}@{host}:{port}/{quote(database)}'
87
+ else:
88
+ url = f'{driver}://{host}:{port}/{quote(database)}'
89
+
90
+ return sqlalchemy.create_engine(url, connect_args=params, **sa_params)
@@ -0,0 +1,3 @@
1
+ from ._cloud_functions import run_function_app # noqa: F401
2
+ from ._dashboards import run_dashboard_app # noqa: F401
3
+ from ._python_udfs import run_udf_app # noqa: F401
@@ -0,0 +1,90 @@
1
+ import asyncio
2
+ import textwrap
3
+ import typing
4
+
5
+ from ._config import AppConfig
6
+ from ._connection_info import ConnectionInfo
7
+ from ._process import kill_process_by_port
8
+
9
+ if typing.TYPE_CHECKING:
10
+ from fastapi import FastAPI
11
+ from ._uvicorn_util import AwaitableUvicornServer
12
+
13
+ # Keep track of currently running server
14
+ _running_server: 'typing.Optional[AwaitableUvicornServer]' = None
15
+
16
+
17
+ async def run_function_app(
18
+ app: 'FastAPI',
19
+ log_level: str = 'error',
20
+ kill_existing_app_server: bool = True,
21
+ ) -> ConnectionInfo:
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
+ app.root_path = app_config.base_path
56
+
57
+ config = uvicorn.Config(
58
+ app,
59
+ host='0.0.0.0',
60
+ port=app_config.listen_port,
61
+ log_level=log_level,
62
+ )
63
+ _running_server = AwaitableUvicornServer(config)
64
+
65
+ asyncio.create_task(_running_server.serve())
66
+ await _running_server.wait_for_startup()
67
+
68
+ connection_info = ConnectionInfo(app_config.base_url, app_config.token)
69
+
70
+ if app_config.running_interactively:
71
+ if app_config.is_gateway_enabled:
72
+ print(
73
+ 'Cloud function available at '
74
+ f'{app_config.base_url}docs?authToken={app_config.token}',
75
+ )
76
+ else:
77
+ curl_header = f'-H "Authorization: Bearer {app_config.token}"'
78
+ curl_example = f'curl "{app_config.base_url}" {curl_header}'
79
+ print(
80
+ textwrap.dedent(f"""
81
+ Cloud function available at {app_config.base_url}
82
+
83
+ Auth Token: {app_config.token}
84
+
85
+ Curl example: {curl_example}
86
+
87
+ """).strip(),
88
+ )
89
+
90
+ return connection_info
@@ -0,0 +1,72 @@
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
+ base_path: str
11
+ notebook_server_id: str
12
+ app_token: Optional[str]
13
+ user_token: Optional[str]
14
+ running_interactively: bool
15
+ is_gateway_enabled: bool
16
+ is_local_dev: bool
17
+
18
+ @staticmethod
19
+ def _read_variable(name: str) -> str:
20
+ value = os.environ.get(name)
21
+ if value is None:
22
+ raise RuntimeError(
23
+ f'Missing {name} environment variable. '
24
+ 'Is the code running outside SingleStoreDB notebook environment?',
25
+ )
26
+ return value
27
+
28
+ @classmethod
29
+ def from_env(cls) -> 'AppConfig':
30
+ port = cls._read_variable('SINGLESTOREDB_APP_LISTEN_PORT')
31
+ base_url = cls._read_variable('SINGLESTOREDB_APP_BASE_URL')
32
+ base_path = cls._read_variable('SINGLESTOREDB_APP_BASE_PATH')
33
+ notebook_server_id = cls._read_variable('SINGLESTOREDB_NOTEBOOK_SERVER_ID')
34
+ is_local_dev_env_var = cls._read_variable('SINGLESTOREDB_IS_LOCAL_DEV')
35
+
36
+ workload_type = os.environ.get('SINGLESTOREDB_WORKLOAD_TYPE')
37
+ running_interactively = workload_type == 'InteractiveNotebook'
38
+
39
+ is_gateway_enabled = 'SINGLESTOREDB_NOVA_GATEWAY_ENDPOINT' in os.environ
40
+
41
+ app_token = os.environ.get('SINGLESTOREDB_APP_TOKEN')
42
+ user_token = os.environ.get('SINGLESTOREDB_USER_TOKEN')
43
+
44
+ # Make sure the required variables are present
45
+ # and present useful error message if not
46
+ if running_interactively:
47
+ if is_gateway_enabled:
48
+ app_token = cls._read_variable('SINGLESTOREDB_APP_TOKEN')
49
+ else:
50
+ user_token = cls._read_variable('SINGLESTOREDB_USER_TOKEN')
51
+
52
+ return cls(
53
+ listen_port=int(port),
54
+ base_url=base_url,
55
+ base_path=base_path,
56
+ notebook_server_id=notebook_server_id,
57
+ app_token=app_token,
58
+ user_token=user_token,
59
+ running_interactively=running_interactively,
60
+ is_gateway_enabled=is_gateway_enabled,
61
+ is_local_dev=is_local_dev_env_var == 'true',
62
+ )
63
+
64
+ @property
65
+ def token(self) -> Optional[str]:
66
+ """
67
+ Returns None if running non-interactively
68
+ """
69
+ if self.is_gateway_enabled:
70
+ return self.app_token
71
+ else:
72
+ return self.user_token
@@ -0,0 +1,18 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import Optional
5
+
6
+
7
+ @dataclass
8
+ class ConnectionInfo:
9
+ url: str
10
+
11
+ # Only present in interactive mode
12
+ token: Optional[str]
13
+
14
+
15
+ @dataclass
16
+ class UdfConnectionInfo:
17
+ url: str
18
+ functions: Dict[str, Any]
@@ -0,0 +1,47 @@
1
+ import typing
2
+
3
+ from ._config import AppConfig
4
+ from ._process import kill_process_by_port
5
+ from ._stdout_supress import StdoutSuppressor
6
+ from singlestoredb.apps._connection_info import ConnectionInfo
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from dash import Dash
10
+
11
+
12
+ async def run_dashboard_app(
13
+ app: 'Dash',
14
+ debug: bool = False,
15
+ kill_existing_app_server: bool = True,
16
+ ) -> ConnectionInfo:
17
+ try:
18
+ from dash import Dash
19
+ except ImportError:
20
+ raise ImportError('package dash is required to run dashboards')
21
+
22
+ if not isinstance(app, Dash):
23
+ raise TypeError('app is not an instance of Dash App')
24
+
25
+ app_config = AppConfig.from_env()
26
+
27
+ if kill_existing_app_server:
28
+ kill_process_by_port(app_config.listen_port)
29
+
30
+ if app.config.requests_pathname_prefix is None or \
31
+ app.config.requests_pathname_prefix != app_config.base_path:
32
+ raise RuntimeError('''
33
+ requests_pathname_prefix of the Dash App is invalid. Please set
34
+ requests_pathname_prefix=os.environ['SINGLESTOREDB_APP_BASE_PATH']
35
+ while initializing the Dash App and retry''')
36
+
37
+ with StdoutSuppressor():
38
+ app.run(
39
+ host='0.0.0.0',
40
+ debug=debug,
41
+ port=str(app_config.listen_port),
42
+ jupyter_mode='external',
43
+ )
44
+
45
+ if app_config.running_interactively:
46
+ print(f'Dash app available at {app_config.base_url}?authToken={app_config.token}')
47
+ return ConnectionInfo(app_config.base_url, app_config.token)