singlestoredb 1.12.4__py3-none-any.whl → 1.13.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.

Potentially problematic release.


This version of singlestoredb might be problematic. Click here for more details.

Files changed (35) hide show
  1. singlestoredb/__init__.py +1 -1
  2. singlestoredb/ai/__init__.py +1 -0
  3. singlestoredb/ai/chat.py +26 -0
  4. singlestoredb/ai/embeddings.py +18 -15
  5. singlestoredb/apps/__init__.py +1 -0
  6. singlestoredb/apps/_config.py +6 -0
  7. singlestoredb/apps/_connection_info.py +8 -0
  8. singlestoredb/apps/_python_udfs.py +85 -0
  9. singlestoredb/config.py +14 -2
  10. singlestoredb/functions/__init__.py +15 -1
  11. singlestoredb/functions/decorator.py +102 -252
  12. singlestoredb/functions/dtypes.py +545 -198
  13. singlestoredb/functions/ext/asgi.py +421 -129
  14. singlestoredb/functions/ext/json.py +29 -36
  15. singlestoredb/functions/ext/mmap.py +1 -1
  16. singlestoredb/functions/ext/rowdat_1.py +50 -70
  17. singlestoredb/functions/signature.py +816 -144
  18. singlestoredb/functions/typing.py +41 -0
  19. singlestoredb/functions/utils.py +421 -0
  20. singlestoredb/http/connection.py +3 -1
  21. singlestoredb/management/inference_api.py +101 -0
  22. singlestoredb/management/manager.py +6 -1
  23. singlestoredb/management/organization.py +17 -0
  24. singlestoredb/management/utils.py +2 -2
  25. singlestoredb/tests/ext_funcs/__init__.py +476 -237
  26. singlestoredb/tests/test_ext_func.py +192 -3
  27. singlestoredb/tests/test_management.py +5 -5
  28. singlestoredb/tests/test_udf.py +101 -131
  29. singlestoredb/tests/test_udf_returns.py +459 -0
  30. {singlestoredb-1.12.4.dist-info → singlestoredb-1.13.1.dist-info}/METADATA +2 -1
  31. {singlestoredb-1.12.4.dist-info → singlestoredb-1.13.1.dist-info}/RECORD +35 -29
  32. {singlestoredb-1.12.4.dist-info → singlestoredb-1.13.1.dist-info}/LICENSE +0 -0
  33. {singlestoredb-1.12.4.dist-info → singlestoredb-1.13.1.dist-info}/WHEEL +0 -0
  34. {singlestoredb-1.12.4.dist-info → singlestoredb-1.13.1.dist-info}/entry_points.txt +0 -0
  35. {singlestoredb-1.12.4.dist-info → singlestoredb-1.13.1.dist-info}/top_level.txt +0 -0
@@ -1,183 +1,113 @@
1
- import dataclasses
2
- import datetime
3
1
  import functools
4
2
  import inspect
5
3
  from typing import Any
6
4
  from typing import Callable
7
- from typing import Dict
8
5
  from typing import List
9
6
  from typing import Optional
10
- from typing import Tuple
7
+ from typing import Type
11
8
  from typing import Union
12
9
 
13
- from . import dtypes
14
- from .dtypes import DataType
15
- from .signature import simplify_dtype
16
-
17
- try:
18
- import pydantic
19
- has_pydantic = True
20
- except ImportError:
21
- has_pydantic = False
22
-
23
- python_type_map: Dict[Any, Callable[..., str]] = {
24
- str: dtypes.TEXT,
25
- int: dtypes.BIGINT,
26
- float: dtypes.DOUBLE,
27
- bool: dtypes.BOOL,
28
- bytes: dtypes.BINARY,
29
- bytearray: dtypes.BINARY,
30
- datetime.datetime: dtypes.DATETIME,
31
- datetime.date: dtypes.DATE,
32
- datetime.timedelta: dtypes.TIME,
33
- }
34
-
35
-
36
- def listify(x: Any) -> List[Any]:
37
- """Make sure sure value is a list."""
38
- if x is None:
39
- return []
40
- if isinstance(x, (list, tuple, set)):
41
- return list(x)
42
- return [x]
43
-
44
-
45
- def process_annotation(annotation: Any) -> Tuple[Any, bool]:
46
- types = simplify_dtype(annotation)
47
- if isinstance(types, list):
48
- nullable = False
49
- if type(None) in types:
50
- nullable = True
51
- types = [x for x in types if x is not type(None)]
52
- if len(types) > 1:
53
- raise ValueError(f'multiple types not supported: {annotation}')
54
- return types[0], nullable
55
- return types, True
56
-
57
-
58
- def process_types(params: Any) -> Any:
59
- if params is None:
60
- return params, []
61
-
62
- elif isinstance(params, (list, tuple)):
63
- params = list(params)
64
- for i, item in enumerate(params):
65
- if params[i] in python_type_map:
66
- params[i] = python_type_map[params[i]]()
67
- elif callable(item):
68
- params[i] = item()
69
- for item in params:
70
- if not isinstance(item, str):
71
- raise TypeError(f'unrecognized type for parameter: {item}')
72
- return params, []
73
-
74
- elif isinstance(params, dict):
75
- names = []
76
- params = dict(params)
77
- for k, v in list(params.items()):
78
- names.append(k)
79
- if params[k] in python_type_map:
80
- params[k] = python_type_map[params[k]]()
81
- elif callable(v):
82
- params[k] = v()
83
- for item in params.values():
84
- if not isinstance(item, str):
85
- raise TypeError(f'unrecognized type for parameter: {item}')
86
- return params, names
87
-
88
- elif dataclasses.is_dataclass(params):
89
- names = []
90
- out = []
91
- for item in dataclasses.fields(params):
92
- typ, nullable = process_annotation(item.type)
93
- sql_type = process_types(typ)[0]
94
- if not nullable:
95
- sql_type = sql_type.replace('NULL', 'NOT NULL')
96
- out.append(sql_type)
97
- names.append(item.name)
98
- return out, names
99
-
100
- elif has_pydantic and inspect.isclass(params) \
101
- and issubclass(params, pydantic.BaseModel):
102
- names = []
103
- out = []
104
- for name, item in params.model_fields.items():
105
- typ, nullable = process_annotation(item.annotation)
106
- sql_type = process_types(typ)[0]
107
- if not nullable:
108
- sql_type = sql_type.replace('NULL', 'NOT NULL')
109
- out.append(sql_type)
110
- names.append(name)
111
- return out, names
112
-
113
- elif params in python_type_map:
114
- return python_type_map[params](), []
115
-
116
- elif callable(params):
117
- return params(), []
118
-
119
- elif isinstance(params, str):
120
- return params, []
121
-
122
- raise TypeError(f'unrecognized data type for args: {params}')
10
+ from . import utils
11
+ from .dtypes import SQLString
12
+
13
+
14
+ ParameterType = Union[
15
+ str,
16
+ Callable[..., SQLString],
17
+ List[Union[str, Callable[..., SQLString]]],
18
+ Type[Any],
19
+ ]
20
+
21
+ ReturnType = ParameterType
22
+
23
+
24
+ def is_valid_type(obj: Any) -> bool:
25
+ """Check if the object is a valid type for a schema definition."""
26
+ if not inspect.isclass(obj):
27
+ return False
28
+
29
+ if utils.is_typeddict(obj):
30
+ return True
31
+
32
+ if utils.is_namedtuple(obj):
33
+ return True
34
+
35
+ if utils.is_dataclass(obj):
36
+ return True
37
+
38
+ # We don't want to import pydantic here, so we check if
39
+ # the class is a subclass
40
+ if utils.is_pydantic(obj):
41
+ return True
42
+
43
+ return False
44
+
45
+
46
+ def is_valid_callable(obj: Any) -> bool:
47
+ """Check if the object is a valid callable for a parameter type."""
48
+ if not callable(obj):
49
+ return False
50
+
51
+ returns = utils.get_annotations(obj).get('return', None)
52
+
53
+ if inspect.isclass(returns) and issubclass(returns, str):
54
+ return True
55
+
56
+ raise TypeError(
57
+ f'callable {obj} must return a str, '
58
+ f'but got {returns}',
59
+ )
60
+
61
+
62
+ def expand_types(args: Any) -> Optional[Union[List[str], Type[Any]]]:
63
+ """Expand the types for the function arguments / return values."""
64
+ if args is None:
65
+ return None
66
+
67
+ # SQL string
68
+ if isinstance(args, str):
69
+ return [args]
70
+
71
+ # General way of accepting pydantic.BaseModel, NamedTuple, TypedDict
72
+ elif is_valid_type(args):
73
+ return args
74
+
75
+ # List of SQL strings or callables
76
+ elif isinstance(args, list):
77
+ new_args = []
78
+ for arg in args:
79
+ if isinstance(arg, str):
80
+ new_args.append(arg)
81
+ elif callable(arg):
82
+ new_args.append(arg())
83
+ else:
84
+ raise TypeError(f'unrecognized type for parameter: {arg}')
85
+ return new_args
86
+
87
+ # Callable that returns a SQL string
88
+ elif is_valid_callable(args):
89
+ out = args()
90
+ if not isinstance(out, str):
91
+ raise TypeError(f'unrecognized type for parameter: {args}')
92
+ return [out]
93
+
94
+ raise TypeError(f'unrecognized type for parameter: {args}')
123
95
 
124
96
 
125
97
  def _func(
126
98
  func: Optional[Callable[..., Any]] = None,
127
99
  *,
128
100
  name: Optional[str] = None,
129
- args: Optional[
130
- Union[
131
- DataType,
132
- List[DataType],
133
- Dict[str, DataType],
134
- 'pydantic.BaseModel',
135
- type,
136
- ]
137
- ] = None,
138
- returns: Optional[
139
- Union[
140
- str,
141
- List[DataType],
142
- List[type],
143
- 'pydantic.BaseModel',
144
- type,
145
- ]
146
- ] = None,
147
- data_format: Optional[str] = None,
148
- include_masks: bool = False,
149
- function_type: str = 'udf',
150
- output_fields: Optional[List[str]] = None,
101
+ args: Optional[ParameterType] = None,
102
+ returns: Optional[ReturnType] = None,
151
103
  ) -> Callable[..., Any]:
152
104
  """Generic wrapper for UDF and TVF decorators."""
153
- args, _ = process_types(args)
154
- returns, fields = process_types(returns)
155
-
156
- if not output_fields and fields:
157
- output_fields = fields
158
-
159
- if isinstance(returns, list) \
160
- and isinstance(output_fields, list) \
161
- and len(output_fields) != len(returns):
162
- raise ValueError(
163
- 'The number of output fields must match the number of return types',
164
- )
165
-
166
- if include_masks and data_format == 'python':
167
- raise RuntimeError(
168
- 'include_masks is only valid when using '
169
- 'vectors for input parameters',
170
- )
171
105
 
172
106
  _singlestoredb_attrs = { # type: ignore
173
107
  k: v for k, v in dict(
174
108
  name=name,
175
- args=args,
176
- returns=returns,
177
- data_format=data_format,
178
- include_masks=include_masks,
179
- function_type=function_type,
180
- output_fields=output_fields or None,
109
+ args=expand_types(args),
110
+ returns=expand_types(returns),
181
111
  ).items() if v is not None
182
112
  }
183
113
 
@@ -186,10 +116,14 @@ def _func(
186
116
  # in at that time.
187
117
  if func is None:
188
118
  def decorate(func: Callable[..., Any]) -> Callable[..., Any]:
119
+
189
120
  def wrapper(*args: Any, **kwargs: Any) -> Callable[..., Any]:
190
121
  return func(*args, **kwargs) # type: ignore
122
+
191
123
  wrapper._singlestoredb_attrs = _singlestoredb_attrs # type: ignore
124
+
192
125
  return functools.wraps(func)(wrapper)
126
+
193
127
  return decorate
194
128
 
195
129
  def wrapper(*args: Any, **kwargs: Any) -> Callable[..., Any]:
@@ -204,13 +138,11 @@ def udf(
204
138
  func: Optional[Callable[..., Any]] = None,
205
139
  *,
206
140
  name: Optional[str] = None,
207
- args: Optional[Union[DataType, List[DataType], Dict[str, DataType]]] = None,
208
- returns: Optional[Union[str, List[DataType], List[type]]] = None,
209
- data_format: Optional[str] = None,
210
- include_masks: bool = False,
141
+ args: Optional[ParameterType] = None,
142
+ returns: Optional[ReturnType] = None,
211
143
  ) -> Callable[..., Any]:
212
144
  """
213
- Apply attributes to a UDF.
145
+ Define a user-defined function (UDF).
214
146
 
215
147
  Parameters
216
148
  ----------
@@ -218,71 +150,7 @@ def udf(
218
150
  The UDF to apply parameters to
219
151
  name : str, optional
220
152
  The name to use for the UDF in the database
221
- args : str | Callable | List[str | Callable] | Dict[str, str | Callable], optional
222
- Specifies the data types of the function arguments. Typically,
223
- the function data types are derived from the function parameter
224
- annotations. These annotations can be overridden. If the function
225
- takes a single type for all parameters, `args` can be set to a
226
- SQL string describing all parameters. If the function takes more
227
- than one parameter and all of the parameters are being manually
228
- defined, a list of SQL strings may be used (one for each parameter).
229
- A dictionary of SQL strings may be used to specify a parameter type
230
- for a subset of parameters; the keys are the names of the
231
- function parameters. Callables may also be used for datatypes. This
232
- is primarily for using the functions in the ``dtypes`` module that
233
- are associated with SQL types with all default options (e.g., ``dt.FLOAT``).
234
- returns : str, optional
235
- Specifies the return data type of the function. If not specified,
236
- the type annotation from the function is used.
237
- data_format : str, optional
238
- The data format of each parameter: python, pandas, arrow, polars
239
- include_masks : bool, optional
240
- Should boolean masks be included with each input parameter to indicate
241
- which elements are NULL? This is only used when a input parameters are
242
- configured to a vector type (numpy, pandas, polars, arrow).
243
-
244
- Returns
245
- -------
246
- Callable
247
-
248
- """
249
- return _func(
250
- func=func,
251
- name=name,
252
- args=args,
253
- returns=returns,
254
- data_format=data_format,
255
- include_masks=include_masks,
256
- function_type='udf',
257
- )
258
-
259
-
260
- udf.pandas = functools.partial(udf, data_format='pandas') # type: ignore
261
- udf.polars = functools.partial(udf, data_format='polars') # type: ignore
262
- udf.arrow = functools.partial(udf, data_format='arrow') # type: ignore
263
- udf.numpy = functools.partial(udf, data_format='numpy') # type: ignore
264
-
265
-
266
- def tvf(
267
- func: Optional[Callable[..., Any]] = None,
268
- *,
269
- name: Optional[str] = None,
270
- args: Optional[Union[DataType, List[DataType], Dict[str, DataType]]] = None,
271
- returns: Optional[Union[str, List[DataType], List[type]]] = None,
272
- data_format: Optional[str] = None,
273
- include_masks: bool = False,
274
- output_fields: Optional[List[str]] = None,
275
- ) -> Callable[..., Any]:
276
- """
277
- Apply attributes to a TVF.
278
-
279
- Parameters
280
- ----------
281
- func : callable, optional
282
- The TVF to apply parameters to
283
- name : str, optional
284
- The name to use for the TVF in the database
285
- args : str | Callable | List[str | Callable] | Dict[str, str | Callable], optional
153
+ args : str | Type | Callable | List[str | Callable], optional
286
154
  Specifies the data types of the function arguments. Typically,
287
155
  the function data types are derived from the function parameter
288
156
  annotations. These annotations can be overridden. If the function
@@ -295,18 +163,10 @@ def tvf(
295
163
  function parameters. Callables may also be used for datatypes. This
296
164
  is primarily for using the functions in the ``dtypes`` module that
297
165
  are associated with SQL types with all default options (e.g., ``dt.FLOAT``).
298
- returns : str, optional
299
- Specifies the return data type of the function. If not specified,
300
- the type annotation from the function is used.
301
- data_format : str, optional
302
- The data format of each parameter: python, pandas, arrow, polars
303
- include_masks : bool, optional
304
- Should boolean masks be included with each input parameter to indicate
305
- which elements are NULL? This is only used when a input parameters are
306
- configured to a vector type (numpy, pandas, polars, arrow).
307
- output_fields : List[str], optional
308
- The names of the output fields for the TVF. If not specified, the
309
- names are generated.
166
+ returns : str | Type | Callable | List[str | Callable] | Table, optional
167
+ Specifies the return data type of the function. This parameter
168
+ works the same way as `args`. If the function is a table-valued
169
+ function, the return type should be a `Table` object.
310
170
 
311
171
  Returns
312
172
  -------
@@ -318,14 +178,4 @@ def tvf(
318
178
  name=name,
319
179
  args=args,
320
180
  returns=returns,
321
- data_format=data_format,
322
- include_masks=include_masks,
323
- function_type='tvf',
324
- output_fields=output_fields,
325
181
  )
326
-
327
-
328
- tvf.pandas = functools.partial(tvf, data_format='pandas') # type: ignore
329
- tvf.polars = functools.partial(tvf, data_format='polars') # type: ignore
330
- tvf.arrow = functools.partial(tvf, data_format='arrow') # type: ignore
331
- tvf.numpy = functools.partial(tvf, data_format='numpy') # type: ignore