singlestoredb 1.12.2__py3-none-any.whl → 1.12.3__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.

singlestoredb/__init__.py CHANGED
@@ -13,7 +13,7 @@ Examples
13
13
 
14
14
  """
15
15
 
16
- __version__ = '1.12.2'
16
+ __version__ = '1.12.3'
17
17
 
18
18
  from typing import Any
19
19
 
singlestoredb/config.py CHANGED
@@ -80,7 +80,7 @@ register_option(
80
80
  )
81
81
 
82
82
  register_option(
83
- 'charset', 'string', check_str, 'utf8',
83
+ 'charset', 'string', check_str, 'utf8mb4',
84
84
  'Specifies the character set for the session.',
85
85
  environ='SINGLESTOREDB_CHARSET',
86
86
  )
@@ -69,6 +69,12 @@ try:
69
69
  except ImportError:
70
70
  has_cloudpickle = False
71
71
 
72
+ try:
73
+ from pydantic import BaseModel
74
+ has_pydantic = True
75
+ except ImportError:
76
+ has_pydantic = False
77
+
72
78
 
73
79
  logger = utils.get_logger('singlestoredb.functions.ext.asgi')
74
80
 
@@ -138,13 +144,24 @@ def get_func_names(funcs: str) -> List[Tuple[str, str]]:
138
144
 
139
145
 
140
146
  def as_tuple(x: Any) -> Any:
141
- if hasattr(x, 'model_fields'):
142
- return tuple(x.model_fields.values())
147
+ """Convert object to tuple."""
148
+ if has_pydantic and isinstance(x, BaseModel):
149
+ return tuple(x.model_dump().values())
143
150
  if dataclasses.is_dataclass(x):
144
151
  return dataclasses.astuple(x)
145
152
  return x
146
153
 
147
154
 
155
+ def as_list_of_tuples(x: Any) -> Any:
156
+ """Convert object to a list of tuples."""
157
+ if isinstance(x, (list, tuple)) and len(x) > 0:
158
+ if has_pydantic and isinstance(x[0], BaseModel):
159
+ return [tuple(y.model_dump().values()) for y in x]
160
+ if dataclasses.is_dataclass(x[0]):
161
+ return [dataclasses.astuple(y) for y in x]
162
+ return x
163
+
164
+
148
165
  def make_func(
149
166
  name: str,
150
167
  func: Callable[..., Any],
@@ -183,7 +200,7 @@ def make_func(
183
200
  out_ids: List[int] = []
184
201
  out = []
185
202
  for i, res in zip(row_ids, func_map(func, rows)):
186
- out.extend(as_tuple(res))
203
+ out.extend(as_list_of_tuples(res))
187
204
  out_ids.extend([row_ids[i]] * (len(out)-len(out_ids)))
188
205
  return out_ids, out
189
206
 
@@ -6,6 +6,8 @@ import numbers
6
6
  import os
7
7
  import re
8
8
  import string
9
+ import sys
10
+ import types
9
11
  import typing
10
12
  from typing import Any
11
13
  from typing import Callable
@@ -32,6 +34,11 @@ except ImportError:
32
34
  from . import dtypes as dt
33
35
  from ..mysql.converters import escape_item # type: ignore
34
36
 
37
+ if sys.version_info >= (3, 10):
38
+ _UNION_TYPES = {typing.Union, types.UnionType}
39
+ else:
40
+ _UNION_TYPES = {typing.Union}
41
+
35
42
 
36
43
  array_types: Tuple[Any, ...]
37
44
 
@@ -211,7 +218,7 @@ def simplify_dtype(dtype: Any) -> List[Any]:
211
218
  args = []
212
219
 
213
220
  # Flatten Unions
214
- if origin is Union:
221
+ if origin in _UNION_TYPES:
215
222
  for x in typing.get_args(dtype):
216
223
  args.extend(simplify_dtype(x))
217
224
 
@@ -43,6 +43,12 @@ try:
43
43
  except ImportError:
44
44
  has_shapely = False
45
45
 
46
+ try:
47
+ import pydantic
48
+ has_pydantic = True
49
+ except ImportError:
50
+ has_pydantic = False
51
+
46
52
  from .. import connection
47
53
  from .. import fusion
48
54
  from .. import types
@@ -533,6 +539,9 @@ class Cursor(connection.Cursor):
533
539
  self._expect_results = True
534
540
  sql_type = 'query'
535
541
 
542
+ if has_pydantic and isinstance(params, pydantic.BaseModel):
543
+ params = params.model_dump()
544
+
536
545
  self._validate_param_subs(oper, params)
537
546
 
538
547
  handler = fusion.get_handler(oper)
@@ -8,6 +8,12 @@ from ..utils import results
8
8
  from ..utils.debug import log_query
9
9
  from ..utils.results import get_schema
10
10
 
11
+ try:
12
+ from pydantic import BaseModel
13
+ has_pydantic = True
14
+ except ImportError:
15
+ has_pydantic = False
16
+
11
17
 
12
18
  #: Regular expression for :meth:`Cursor.executemany`.
13
19
  #: executemany only supports simple bulk insert.
@@ -149,6 +155,8 @@ class Cursor(BaseCursor):
149
155
  return tuple(literal(arg) for arg in args)
150
156
  elif dtype is dict or isinstance(args, dict):
151
157
  return {key: literal(val) for (key, val) in args.items()}
158
+ elif has_pydantic and isinstance(args, BaseModel):
159
+ return {key: literal(val) for (key, val) in args.model_dump().items()}
152
160
  # If it's not a dictionary let's try escaping it anyways.
153
161
  # Worst case it will throw a Value error
154
162
  return conn.escape(args)
@@ -6,6 +6,7 @@ import decimal
6
6
  import math
7
7
  import os
8
8
  import unittest
9
+ from typing import Optional
9
10
 
10
11
  from requests.exceptions import InvalidJSONError
11
12
 
@@ -28,6 +29,12 @@ try:
28
29
  except ImportError:
29
30
  has_pygeos = False
30
31
 
32
+ try:
33
+ import pydantic
34
+ has_pydantic = True
35
+ except ImportError:
36
+ has_pydantic = False
37
+
31
38
  import singlestoredb as s2
32
39
  from . import utils
33
40
  # import traceback
@@ -1255,6 +1262,68 @@ class TestBasics(unittest.TestCase):
1255
1262
  except Exception:
1256
1263
  pass
1257
1264
 
1265
+ def test_pydantic(self):
1266
+ if not has_pydantic:
1267
+ self.skipTest('Test requires pydantic')
1268
+
1269
+ tblname = 'foo_' + str(id(self))
1270
+
1271
+ class FooData(pydantic.BaseModel):
1272
+ x: Optional[int]
1273
+ y: Optional[float]
1274
+ z: Optional[str] = None
1275
+
1276
+ self.cur.execute(f'''
1277
+ CREATE TABLE {tblname}(
1278
+ x INT,
1279
+ y DOUBLE,
1280
+ z TEXT
1281
+ )
1282
+ ''')
1283
+
1284
+ self.cur.execute(
1285
+ f'INSERT INTO {tblname}(x, y) VALUES (%(x)s, %(y)s)',
1286
+ FooData(x=2, y=3.23),
1287
+ )
1288
+
1289
+ self.cur.execute('SELECT * FROM ' + tblname)
1290
+
1291
+ assert list(sorted(self.cur.fetchall())) == \
1292
+ list(sorted([(2, 3.23, None)]))
1293
+
1294
+ self.cur.executemany(
1295
+ f'INSERT INTO {tblname}(x) VALUES (%(x)s)',
1296
+ [FooData(x=3, y=3.12), FooData(x=10, y=100.11)],
1297
+ )
1298
+
1299
+ self.cur.execute('SELECT * FROM ' + tblname)
1300
+
1301
+ assert list(sorted(self.cur.fetchall())) == \
1302
+ list(
1303
+ sorted([
1304
+ (2, 3.23, None),
1305
+ (3, None, None),
1306
+ (10, None, None),
1307
+ ]),
1308
+ )
1309
+
1310
+ def test_charset(self):
1311
+ with s2.connect(database=type(self).dbname) as conn:
1312
+ with conn.cursor() as cur:
1313
+ cur.execute('''
1314
+ select json_extract_string('{"foo":"😀"}', "bar");
1315
+ ''')
1316
+
1317
+ if 'http' in self.conn.driver:
1318
+ self.skipTest('Charset is not use in HTTP interface')
1319
+
1320
+ with self.assertRaises(s2.OperationalError):
1321
+ with s2.connect(database=type(self).dbname, charset='utf8') as conn:
1322
+ with conn.cursor() as cur:
1323
+ cur.execute('''
1324
+ select json_extract_string('{"foo":"😀"}', "bar");
1325
+ ''')
1326
+
1258
1327
 
1259
1328
  if __name__ == '__main__':
1260
1329
  import nose2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: singlestoredb
3
- Version: 1.12.2
3
+ Version: 1.12.3
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,6 +1,6 @@
1
- singlestoredb/__init__.py,sha256=Ms1Jw6Pv01LlItPE5vSqQ59kqTYvN3HrSbQSN1i1qv0,1649
1
+ singlestoredb/__init__.py,sha256=LXYFwtLhjKsxv739J4mxgRFhDzBRIMM-XBnm-RqwLtk,1649
2
2
  singlestoredb/auth.py,sha256=u8D9tpKzrqa4ssaHjyZnGDX1q8XBpGtuoOkTkSv7B28,7599
3
- singlestoredb/config.py,sha256=rlF69SiclYyKghNRckX77Ls1ZT23RhSssO1cyYBiHmA,12589
3
+ singlestoredb/config.py,sha256=0XooNOf1dUee26C0io7unfBbdXUyfDIYc_mSpxsVsTI,12592
4
4
  singlestoredb/connection.py,sha256=0HEpjBZXLqQwOTEfveMkgej1H3Kyof47prIHvJJZtoo,45831
5
5
  singlestoredb/converters.py,sha256=Ui-AqdW3pRAQ8A_YcK9EqVYyM4Pt1_Q-tjlotbpK6Cw,20686
6
6
  singlestoredb/exceptions.py,sha256=HuoA6sMRL5qiCiee-_5ddTGmFbYC9Euk8TYUsh5GvTw,3234
@@ -21,10 +21,10 @@ singlestoredb/apps/_uvicorn_util.py,sha256=rEK4nEmq5hbpRgsmK16UVlxe2DyQSq7C5w5WZ
21
21
  singlestoredb/functions/__init__.py,sha256=nPaLVgtb5XDxbRucDFFwjePPh4n40_6jcbxE8HPebkQ,82
22
22
  singlestoredb/functions/decorator.py,sha256=0gfYVCi_GvxKcuDmAcpfIFTTElLD0CBT8RYN_dUrEvU,11485
23
23
  singlestoredb/functions/dtypes.py,sha256=a2vevIug8NhiUCFiSOKwRPpdWU69Gn13ZoQ6Aovskhc,31408
24
- singlestoredb/functions/signature.py,sha256=y3QKjjM9weZjIZyFE-E4SEFx5i3sgP8rqTC0iJ0hhaM,22396
24
+ singlestoredb/functions/signature.py,sha256=qjdCBxtJvNnDWCAruX9kFNuYuGYVCWGEJ-heKRu4MXk,22551
25
25
  singlestoredb/functions/ext/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
26
26
  singlestoredb/functions/ext/arrow.py,sha256=WB7n1ACslyd8nlbFzUvlbxn1BVuEjA9-BGBEqCWlSOo,9061
27
- singlestoredb/functions/ext/asgi.py,sha256=UMZVrw2G_a5SDNy4X7uVJeidfR5HobJN1qLMLqPBAeM,43535
27
+ singlestoredb/functions/ext/asgi.py,sha256=AK7dxe_m8TnFh7IuKKzKO1lCyfJVzs4hRJKW1vTO130,44073
28
28
  singlestoredb/functions/ext/json.py,sha256=XkI8jirxi1T9F-M0p9NpLezph0MRAhYmDiPuU2Id0Uo,10404
29
29
  singlestoredb/functions/ext/mmap.py,sha256=OB6CIYoLe_AYuJM10lE0I6QhZJ5kMhLNbQo2Sp1wiZA,13711
30
30
  singlestoredb/functions/ext/rowdat_1.py,sha256=JgKRsVSQYczFD6cmo2xLilbNPYpyLL2tPOWO1Gh25ow,22306
@@ -43,7 +43,7 @@ singlestoredb/fusion/handlers/stage.py,sha256=kYVjbPys83kf3jX6jWwN8Ju0oEocKVZ3TI
43
43
  singlestoredb/fusion/handlers/utils.py,sha256=ozHOWUraoN8XGTK9JZdhv5HV8AQR8zfUd1yh1kLvUXY,10685
44
44
  singlestoredb/fusion/handlers/workspace.py,sha256=4xN2TFO4yF7KZB2Fcht7IuvoDdAT6fDfDLjixiHZN8w,27506
45
45
  singlestoredb/http/__init__.py,sha256=A_2ZUCCpvRYIA6YDpPy57wL5R1eZ5SfP6I1To5nfJ2s,912
46
- singlestoredb/http/connection.py,sha256=gSqlCxHbjR1745LgUugfDAXU47Uoksu3jVicYI-gO1M,39609
46
+ singlestoredb/http/connection.py,sha256=dNn6OAjCSueR_CIv1T28oDr_TkqXUV05yv1t-XoPrYE,39814
47
47
  singlestoredb/magics/__init__.py,sha256=lZjkT3Webo9c1EQAzlRCRh6B2pckQH8uvNrrB__abcI,1210
48
48
  singlestoredb/magics/run_personal.py,sha256=2f7u1T7iblxGzZurHNgNXLrPBvsvPADZKo_RD_IjYuE,1844
49
49
  singlestoredb/magics/run_shared.py,sha256=SI8dCBRMaGn-xZU7dto4jsAqKBi-Ll14htUsMUSBpJM,1752
@@ -63,7 +63,7 @@ singlestoredb/mysql/_auth.py,sha256=AugRitoUwgRIDFuJxuAH4MWIAmckY7Ji2pP6r_Ng9dY,
63
63
  singlestoredb/mysql/charset.py,sha256=-FlONDS_oAUF5B3mIgeHBPb_SCt4zHD33arUeBNctU0,10510
64
64
  singlestoredb/mysql/connection.py,sha256=TN-_c8JSFSEnpsHNtQ_3DQyOshp-BTx2PlF8g_hDeGQ,73087
65
65
  singlestoredb/mysql/converters.py,sha256=CVe8SDmjbIAhy1xpQ2N5OKWw6t5eWpw-EU3QTlA0Hh0,7500
66
- singlestoredb/mysql/cursors.py,sha256=Eqe7jITRvOo4P_TxIarTumg_2PG1DcCfZ4Uo9IFdDa8,26794
66
+ singlestoredb/mysql/cursors.py,sha256=aOLfHkj83aYZPOVuhJPkZ83CWByszIBRynq0fqSaWvY,27046
67
67
  singlestoredb/mysql/err.py,sha256=-m5rqXi8yhq6b8SCEJ2h0E5Rudh_15dlAU_WbJ1YrM8,2388
68
68
  singlestoredb/mysql/optionfile.py,sha256=DqL-rOQcqQncD5eVbPRkwZqo7Pj3Vh40VLx3E_e87TU,655
69
69
  singlestoredb/mysql/protocol.py,sha256=2GG8qTXy5npqo7z2D2K5T0S8PtoUOS-hFDEXy8VConw,14451
@@ -112,7 +112,7 @@ singlestoredb/tests/test.ipynb,sha256=jrkI2WoSsUA9xQpKTBCHnsDptryQhPdM5QaxfvYRGp
112
112
  singlestoredb/tests/test.sql,sha256=dfMehVCQ9wObSVTQKyQi-fRFDZeqRxV4Cj8doBCPEFM,17679
113
113
  singlestoredb/tests/test2.ipynb,sha256=yd1PE1VK-DwiRd6mYS4_0cPBtuVkvcDtycvTwD-YnDo,218
114
114
  singlestoredb/tests/test2.sql,sha256=D4U2GSlOVeo39U8-RMM4YziJzYFfi4Ztm2YXJVJVAS8,37
115
- singlestoredb/tests/test_basics.py,sha256=1__lEF7FmQF4_pFi5R53TtJidtQznmQ592Ci6aDVgrc,46368
115
+ singlestoredb/tests/test_basics.py,sha256=QMPTSgE8sVdL3q9YNfjsI6kJovHEGfsANjJRYGl1wns,48373
116
116
  singlestoredb/tests/test_config.py,sha256=63lyIQ2KrvGE6C9403B_4Mc90mX4tp42ys5Bih2sXrE,11184
117
117
  singlestoredb/tests/test_connection.py,sha256=fvn-kPdeIMI9RGNz0dNk5ZmTCep1amwWQDHYfPdqO60,119699
118
118
  singlestoredb/tests/test_dbapi.py,sha256=IKq5Hcwx8WikASP8_AB5fo3TXv7ryWPCVGonoly00gI,652
@@ -140,9 +140,9 @@ singlestoredb/utils/results.py,sha256=bJtaUaDiFq26IsPAKZ2FHGB7csMn94EAxLKrP4HaEE
140
140
  singlestoredb/utils/xdict.py,sha256=S9HKgrPrnu_6b7iOwa2KrW8CmU1Uqx0BWdEyogFzWbE,12896
141
141
  sqlx/__init__.py,sha256=aBYiU8DZXCogvWu3yWafOz7bZS5WWwLZXj7oL0dXGyU,85
142
142
  sqlx/magic.py,sha256=JsS9_9aBFaOt91Torm1JPN0c8qB2QmYJmNSKtbSQIY0,3509
143
- singlestoredb-1.12.2.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
144
- singlestoredb-1.12.2.dist-info/METADATA,sha256=sSTAPKm9LSUt3agc3cMgLUEB2IM6Ydp9x9lEojSPAjo,5622
145
- singlestoredb-1.12.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
146
- singlestoredb-1.12.2.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
147
- singlestoredb-1.12.2.dist-info/top_level.txt,sha256=DfFGz7bM4XrshloiCeTABgylT3BUnS8T5pJam3ewT6Q,19
148
- singlestoredb-1.12.2.dist-info/RECORD,,
143
+ singlestoredb-1.12.3.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
144
+ singlestoredb-1.12.3.dist-info/METADATA,sha256=Pvf9NtyodQw9SX57VfloCGWhRSK_C4AhymBOd3TtKTA,5622
145
+ singlestoredb-1.12.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
146
+ singlestoredb-1.12.3.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
147
+ singlestoredb-1.12.3.dist-info/top_level.txt,sha256=DfFGz7bM4XrshloiCeTABgylT3BUnS8T5pJam3ewT6Q,19
148
+ singlestoredb-1.12.3.dist-info/RECORD,,