singlestoredb 1.15.0__cp38-abi3-win32.whl → 1.15.2__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 +0 -0
- singlestoredb/__init__.py +1 -1
- singlestoredb/ai/chat.py +14 -0
- singlestoredb/apps/_python_udfs.py +18 -3
- singlestoredb/apps/_stdout_supress.py +1 -1
- singlestoredb/apps/_uvicorn_util.py +4 -0
- singlestoredb/config.py +24 -0
- singlestoredb/converters.py +1 -1
- singlestoredb/docstring/__init__.py +33 -0
- singlestoredb/docstring/attrdoc.py +126 -0
- singlestoredb/docstring/common.py +230 -0
- singlestoredb/docstring/epydoc.py +267 -0
- singlestoredb/docstring/google.py +412 -0
- singlestoredb/docstring/numpydoc.py +562 -0
- singlestoredb/docstring/parser.py +100 -0
- singlestoredb/docstring/py.typed +1 -0
- singlestoredb/docstring/rest.py +256 -0
- singlestoredb/docstring/tests/__init__.py +1 -0
- singlestoredb/docstring/tests/_pydoctor.py +21 -0
- singlestoredb/docstring/tests/test_epydoc.py +729 -0
- singlestoredb/docstring/tests/test_google.py +1007 -0
- singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
- singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
- singlestoredb/docstring/tests/test_parser.py +248 -0
- singlestoredb/docstring/tests/test_rest.py +547 -0
- singlestoredb/docstring/tests/test_util.py +70 -0
- singlestoredb/docstring/util.py +141 -0
- singlestoredb/functions/decorator.py +19 -18
- singlestoredb/functions/ext/asgi.py +304 -32
- singlestoredb/functions/ext/timer.py +2 -11
- singlestoredb/functions/ext/utils.py +55 -6
- singlestoredb/functions/signature.py +374 -241
- singlestoredb/fusion/handlers/files.py +4 -4
- singlestoredb/fusion/handlers/models.py +1 -1
- singlestoredb/fusion/handlers/stage.py +4 -4
- singlestoredb/management/cluster.py +1 -1
- singlestoredb/management/manager.py +15 -5
- singlestoredb/management/region.py +12 -2
- singlestoredb/management/workspace.py +17 -25
- singlestoredb/tests/ext_funcs/__init__.py +39 -0
- singlestoredb/tests/test_connection.py +18 -8
- singlestoredb/tests/test_management.py +24 -57
- singlestoredb/tests/test_udf.py +43 -15
- {singlestoredb-1.15.0.dist-info → singlestoredb-1.15.2.dist-info}/METADATA +1 -1
- {singlestoredb-1.15.0.dist-info → singlestoredb-1.15.2.dist-info}/RECORD +49 -30
- {singlestoredb-1.15.0.dist-info → singlestoredb-1.15.2.dist-info}/LICENSE +0 -0
- {singlestoredb-1.15.0.dist-info → singlestoredb-1.15.2.dist-info}/WHEEL +0 -0
- {singlestoredb-1.15.0.dist-info → singlestoredb-1.15.2.dist-info}/entry_points.txt +0 -0
- {singlestoredb-1.15.0.dist-info → singlestoredb-1.15.2.dist-info}/top_level.txt +0 -0
|
@@ -89,7 +89,7 @@ class ShowPersonalFilesHandler(ShowFilesHandler):
|
|
|
89
89
|
specified number.
|
|
90
90
|
* Use the ``ORDER BY`` clause to sort the results by the specified
|
|
91
91
|
key. By default, the results are sorted in the ascending order.
|
|
92
|
-
* The ``AT
|
|
92
|
+
* The ``AT`` clause specifies the path in the personal/shared
|
|
93
93
|
space to list the files from.
|
|
94
94
|
* Use the ``RECURSIVE`` clause to list the files recursively.
|
|
95
95
|
* To return more information about the files, use the ``EXTENDED``
|
|
@@ -99,7 +99,7 @@ class ShowPersonalFilesHandler(ShowFilesHandler):
|
|
|
99
99
|
--------
|
|
100
100
|
The following command lists the files at a specific path::
|
|
101
101
|
|
|
102
|
-
SHOW PERSONAL FILES AT
|
|
102
|
+
SHOW PERSONAL FILES AT "/data/";
|
|
103
103
|
|
|
104
104
|
The following command lists the files recursively with
|
|
105
105
|
additional information::
|
|
@@ -154,7 +154,7 @@ class ShowSharedFilesHandler(ShowFilesHandler):
|
|
|
154
154
|
specified number.
|
|
155
155
|
* Use the ``ORDER BY`` clause to sort the results by the specified
|
|
156
156
|
key. By default, the results are sorted in the ascending order.
|
|
157
|
-
* The ``AT
|
|
157
|
+
* The ``AT`` clause specifies the path in the personal/shared
|
|
158
158
|
space to list the files from.
|
|
159
159
|
* Use the ``RECURSIVE`` clause to list the files recursively.
|
|
160
160
|
* To return more information about the files, use the ``EXTENDED``
|
|
@@ -164,7 +164,7 @@ class ShowSharedFilesHandler(ShowFilesHandler):
|
|
|
164
164
|
--------
|
|
165
165
|
The following command lists the files at a specific path::
|
|
166
166
|
|
|
167
|
-
SHOW SHARED FILES AT
|
|
167
|
+
SHOW SHARED FILES AT "/data/";
|
|
168
168
|
|
|
169
169
|
The following command lists the files recursively with
|
|
170
170
|
additional information::
|
|
@@ -44,7 +44,7 @@ class ShowModelsHandler(ShowFilesHandler):
|
|
|
44
44
|
specified number.
|
|
45
45
|
* Use the ``ORDER BY`` clause to sort the results by the specified
|
|
46
46
|
key. By default, the results are sorted in the ascending order.
|
|
47
|
-
* The ``AT
|
|
47
|
+
* The ``AT`` clause specifies the path in the models
|
|
48
48
|
space to list the files from.
|
|
49
49
|
* To return more information about the files, use the ``EXTENDED``
|
|
50
50
|
clause.
|
|
@@ -62,7 +62,7 @@ class ShowStageFilesHandler(SQLHandler):
|
|
|
62
62
|
specified number.
|
|
63
63
|
* Use the ``ORDER BY`` clause to sort the results by the specified
|
|
64
64
|
key. By default, the results are sorted in the ascending order.
|
|
65
|
-
* The ``AT
|
|
65
|
+
* The ``AT`` clause specifies the path in the Stage to list
|
|
66
66
|
the files from.
|
|
67
67
|
* The ``IN`` clause specifies the ID or the name of the
|
|
68
68
|
deployment in which the Stage is attached.
|
|
@@ -74,7 +74,7 @@ class ShowStageFilesHandler(SQLHandler):
|
|
|
74
74
|
--------
|
|
75
75
|
The following command lists the files at a specific path::
|
|
76
76
|
|
|
77
|
-
SHOW STAGE FILES IN 'wsg1' AT
|
|
77
|
+
SHOW STAGE FILES IN 'wsg1' AT "/data/";
|
|
78
78
|
|
|
79
79
|
The following command lists the files recursively with
|
|
80
80
|
additional information::
|
|
@@ -393,7 +393,7 @@ class DropStageFolderHandler(SQLHandler):
|
|
|
393
393
|
# Name of deployment
|
|
394
394
|
deployment_name = '<deployment-name>'
|
|
395
395
|
|
|
396
|
-
# Should
|
|
396
|
+
# Should folders be deleted recursively?
|
|
397
397
|
recursive = RECURSIVE
|
|
398
398
|
|
|
399
399
|
Description
|
|
@@ -474,7 +474,7 @@ class CreateStageFolderHandler(SQLHandler):
|
|
|
474
474
|
is created. The path must end with a trailing slash (/).
|
|
475
475
|
* ``<deployment-id>``: The ID of the deployment in which
|
|
476
476
|
the Stage is attached.
|
|
477
|
-
* ``<deployment-name>``: The name of the deployment in
|
|
477
|
+
* ``<deployment-name>``: The name of the deployment in
|
|
478
478
|
which the Stage is attached.
|
|
479
479
|
|
|
480
480
|
Remarks
|
|
@@ -148,7 +148,9 @@ class Manager(object):
|
|
|
148
148
|
|
|
149
149
|
"""
|
|
150
150
|
if self._params:
|
|
151
|
-
|
|
151
|
+
params = dict(self._params)
|
|
152
|
+
params.update(kwargs.get('params', {}))
|
|
153
|
+
kwargs['params'] = params
|
|
152
154
|
set_organization(kwargs)
|
|
153
155
|
return self._check(self._doit('get', path, *args, **kwargs), path, kwargs)
|
|
154
156
|
|
|
@@ -171,7 +173,9 @@ class Manager(object):
|
|
|
171
173
|
|
|
172
174
|
"""
|
|
173
175
|
if self._params:
|
|
174
|
-
|
|
176
|
+
params = dict(self._params)
|
|
177
|
+
params.update(kwargs.get('params', {}))
|
|
178
|
+
kwargs['params'] = params
|
|
175
179
|
set_organization(kwargs)
|
|
176
180
|
return self._check(self._doit('post', path, *args, **kwargs), path, kwargs)
|
|
177
181
|
|
|
@@ -194,7 +198,9 @@ class Manager(object):
|
|
|
194
198
|
|
|
195
199
|
"""
|
|
196
200
|
if self._params:
|
|
197
|
-
|
|
201
|
+
params = dict(self._params)
|
|
202
|
+
params.update(kwargs.get('params', {}))
|
|
203
|
+
kwargs['params'] = params
|
|
198
204
|
set_organization(kwargs)
|
|
199
205
|
return self._check(self._doit('put', path, *args, **kwargs), path, kwargs)
|
|
200
206
|
|
|
@@ -217,7 +223,9 @@ class Manager(object):
|
|
|
217
223
|
|
|
218
224
|
"""
|
|
219
225
|
if self._params:
|
|
220
|
-
|
|
226
|
+
params = dict(self._params)
|
|
227
|
+
params.update(kwargs.get('params', {}))
|
|
228
|
+
kwargs['params'] = params
|
|
221
229
|
set_organization(kwargs)
|
|
222
230
|
return self._check(self._doit('delete', path, *args, **kwargs), path, kwargs)
|
|
223
231
|
|
|
@@ -240,7 +248,9 @@ class Manager(object):
|
|
|
240
248
|
|
|
241
249
|
"""
|
|
242
250
|
if self._params:
|
|
243
|
-
|
|
251
|
+
params = dict(self._params)
|
|
252
|
+
params.update(kwargs.get('params', {}))
|
|
253
|
+
kwargs['params'] = params
|
|
244
254
|
set_organization(kwargs)
|
|
245
255
|
return self._check(self._doit('patch', path, *args, **kwargs), path, kwargs)
|
|
246
256
|
|
|
@@ -21,7 +21,10 @@ class Region(object):
|
|
|
21
21
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
def __init__(
|
|
24
|
+
def __init__(
|
|
25
|
+
self, name: str, provider: str, id: Optional[str] = None,
|
|
26
|
+
region_name: Optional[str] = None,
|
|
27
|
+
) -> None:
|
|
25
28
|
"""Use :attr:`WorkspaceManager.regions` instead."""
|
|
26
29
|
#: Unique ID of the region
|
|
27
30
|
self.id = id
|
|
@@ -32,6 +35,9 @@ class Region(object):
|
|
|
32
35
|
#: Name of the cloud provider
|
|
33
36
|
self.provider = provider
|
|
34
37
|
|
|
38
|
+
#: Name of the provider region
|
|
39
|
+
self.region_name = region_name
|
|
40
|
+
|
|
35
41
|
self._manager: Optional[Manager] = None
|
|
36
42
|
|
|
37
43
|
def __str__(self) -> str:
|
|
@@ -59,10 +65,14 @@ class Region(object):
|
|
|
59
65
|
:class:`Region`
|
|
60
66
|
|
|
61
67
|
"""
|
|
68
|
+
id = obj.get('regionID', None)
|
|
69
|
+
region_name = obj.get('regionName', None)
|
|
70
|
+
|
|
62
71
|
out = cls(
|
|
63
|
-
id=
|
|
72
|
+
id=id,
|
|
64
73
|
name=obj['region'],
|
|
65
74
|
provider=obj['provider'],
|
|
75
|
+
region_name=region_name,
|
|
66
76
|
)
|
|
67
77
|
out._manager = manager
|
|
68
78
|
return out
|
|
@@ -1074,7 +1074,7 @@ class WorkspaceGroup(object):
|
|
|
1074
1074
|
try:
|
|
1075
1075
|
region = [x for x in manager.regions if x.id == obj['regionID']][0]
|
|
1076
1076
|
except IndexError:
|
|
1077
|
-
region = Region(
|
|
1077
|
+
region = Region('<unknown>', '<unknown>', obj.get('regionID', '<unknown>'))
|
|
1078
1078
|
out = cls(
|
|
1079
1079
|
name=obj['name'],
|
|
1080
1080
|
id=obj['workspaceGroupID'],
|
|
@@ -1391,13 +1391,10 @@ class StarterWorkspace(object):
|
|
|
1391
1391
|
msg='An endpoint has not been set in this '
|
|
1392
1392
|
'starter workspace configuration',
|
|
1393
1393
|
)
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
kwargs['port'] = int(port)
|
|
1399
|
-
else:
|
|
1400
|
-
kwargs['host'] = self.endpoint
|
|
1394
|
+
|
|
1395
|
+
kwargs['host'] = self.endpoint
|
|
1396
|
+
kwargs['database'] = self.database_name
|
|
1397
|
+
|
|
1401
1398
|
return connection.connect(**kwargs)
|
|
1402
1399
|
|
|
1403
1400
|
def terminate(self) -> None:
|
|
@@ -1455,7 +1452,7 @@ class StarterWorkspace(object):
|
|
|
1455
1452
|
|
|
1456
1453
|
def create_user(
|
|
1457
1454
|
self,
|
|
1458
|
-
|
|
1455
|
+
username: str,
|
|
1459
1456
|
password: Optional[str] = None,
|
|
1460
1457
|
) -> Dict[str, str]:
|
|
1461
1458
|
"""
|
|
@@ -1463,7 +1460,7 @@ class StarterWorkspace(object):
|
|
|
1463
1460
|
|
|
1464
1461
|
Parameters
|
|
1465
1462
|
----------
|
|
1466
|
-
|
|
1463
|
+
username : str
|
|
1467
1464
|
The starter workspace user name to connect the new user to the database
|
|
1468
1465
|
password : str, optional
|
|
1469
1466
|
Password for the new user. If not provided, a password will be
|
|
@@ -1485,7 +1482,7 @@ class StarterWorkspace(object):
|
|
|
1485
1482
|
)
|
|
1486
1483
|
|
|
1487
1484
|
payload = {
|
|
1488
|
-
'userName':
|
|
1485
|
+
'userName': username,
|
|
1489
1486
|
}
|
|
1490
1487
|
if password is not None:
|
|
1491
1488
|
payload['password'] = password
|
|
@@ -1715,7 +1712,7 @@ class WorkspaceManager(Manager):
|
|
|
1715
1712
|
:class:`WorkspaceGroup`
|
|
1716
1713
|
|
|
1717
1714
|
"""
|
|
1718
|
-
if isinstance(region, Region):
|
|
1715
|
+
if isinstance(region, Region) and region.id:
|
|
1719
1716
|
region = region.id
|
|
1720
1717
|
res = self._post(
|
|
1721
1718
|
'workspaceGroups', json=dict(
|
|
@@ -1854,7 +1851,8 @@ class WorkspaceManager(Manager):
|
|
|
1854
1851
|
self,
|
|
1855
1852
|
name: str,
|
|
1856
1853
|
database_name: str,
|
|
1857
|
-
|
|
1854
|
+
provider: str,
|
|
1855
|
+
region_name: str,
|
|
1858
1856
|
) -> 'StarterWorkspace':
|
|
1859
1857
|
"""
|
|
1860
1858
|
Create a new starter (shared tier) workspace.
|
|
@@ -1865,27 +1863,21 @@ class WorkspaceManager(Manager):
|
|
|
1865
1863
|
Name of the starter workspace
|
|
1866
1864
|
database_name : str
|
|
1867
1865
|
Name of the database for the starter workspace
|
|
1868
|
-
|
|
1869
|
-
|
|
1866
|
+
provider : str
|
|
1867
|
+
Cloud provider for the starter workspace (e.g., 'aws', 'gcp', 'azure')
|
|
1868
|
+
region_name : str
|
|
1869
|
+
Cloud provider region for the starter workspace (e.g., 'us-east-1')
|
|
1870
1870
|
|
|
1871
1871
|
Returns
|
|
1872
1872
|
-------
|
|
1873
1873
|
:class:`StarterWorkspace`
|
|
1874
1874
|
"""
|
|
1875
|
-
if not workspace_group or not isinstance(workspace_group, dict):
|
|
1876
|
-
raise ValueError(
|
|
1877
|
-
'workspace_group must be a dict with keys: '
|
|
1878
|
-
"'cell_id'",
|
|
1879
|
-
)
|
|
1880
|
-
if set(workspace_group.keys()) != {'cell_id'}:
|
|
1881
|
-
raise ValueError("workspace_group must contain only 'cell_id'")
|
|
1882
1875
|
|
|
1883
1876
|
payload = {
|
|
1884
1877
|
'name': name,
|
|
1885
1878
|
'databaseName': database_name,
|
|
1886
|
-
'
|
|
1887
|
-
|
|
1888
|
-
},
|
|
1879
|
+
'provider': provider,
|
|
1880
|
+
'regionName': region_name,
|
|
1889
1881
|
}
|
|
1890
1882
|
|
|
1891
1883
|
res = self._post('sharedtier/virtualWorkspaces', json=payload)
|
|
@@ -28,6 +28,45 @@ from singlestoredb.functions.typing import polars as plt
|
|
|
28
28
|
from singlestoredb.functions.typing import pyarrow as pat
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
@udf
|
|
32
|
+
def doc_test(x: int, y: float) -> int:
|
|
33
|
+
"""
|
|
34
|
+
A simple function to test the decorator and documentation.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
x : int
|
|
39
|
+
An integer to be multiplied by 2.
|
|
40
|
+
y : float
|
|
41
|
+
A float that is not used in the computation.
|
|
42
|
+
|
|
43
|
+
Examples
|
|
44
|
+
--------
|
|
45
|
+
Basic usage of the function:
|
|
46
|
+
>>> doc_test(
|
|
47
|
+
... 3, 4.5,
|
|
48
|
+
... )
|
|
49
|
+
6
|
|
50
|
+
|
|
51
|
+
Another example with different values:
|
|
52
|
+
>>> doc_test(5, 2.0)
|
|
53
|
+
10
|
|
54
|
+
|
|
55
|
+
SQL Example
|
|
56
|
+
sql> SELECT doc_test(3, 4.5);
|
|
57
|
+
6
|
|
58
|
+
|
|
59
|
+
Final text
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
int
|
|
64
|
+
The input integer multiplied by 2.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
return x * 2
|
|
68
|
+
|
|
69
|
+
|
|
31
70
|
@udf
|
|
32
71
|
def int_mult(x: int, y: int) -> int:
|
|
33
72
|
return x * y
|
|
@@ -1443,6 +1443,11 @@ class TestConnection(unittest.TestCase):
|
|
|
1443
1443
|
out = cur.fetchone()
|
|
1444
1444
|
row = dict(zip(names, out.row(0)))
|
|
1445
1445
|
|
|
1446
|
+
# Recent versions of polars have a problem with decimals
|
|
1447
|
+
class FixCompare(str):
|
|
1448
|
+
def __eq__(self, other):
|
|
1449
|
+
return super().__eq__(other.replace('precision=None', 'precision=22'))
|
|
1450
|
+
|
|
1446
1451
|
dtypes = [
|
|
1447
1452
|
('id', 'Int32'),
|
|
1448
1453
|
('tinyint', 'Int8'),
|
|
@@ -1464,10 +1469,10 @@ class TestConnection(unittest.TestCase):
|
|
|
1464
1469
|
('float', 'Float32'),
|
|
1465
1470
|
('double', 'Float64'),
|
|
1466
1471
|
('real', 'Float64'),
|
|
1467
|
-
('decimal', 'Decimal(precision=22, scale=6)'),
|
|
1468
|
-
('dec', 'Decimal(precision=22, scale=6)'),
|
|
1469
|
-
('fixed', 'Decimal(precision=22, scale=6)'),
|
|
1470
|
-
('numeric', 'Decimal(precision=22, scale=6)'),
|
|
1472
|
+
('decimal', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1473
|
+
('dec', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1474
|
+
('fixed', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1475
|
+
('numeric', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1471
1476
|
('date', 'Date'),
|
|
1472
1477
|
('time', "Duration(time_unit='us')"),
|
|
1473
1478
|
('time_6', "Duration(time_unit='us')"),
|
|
@@ -1585,6 +1590,11 @@ class TestConnection(unittest.TestCase):
|
|
|
1585
1590
|
out = cur.fetchone()
|
|
1586
1591
|
row = dict(zip(names, out.row(0)))
|
|
1587
1592
|
|
|
1593
|
+
# Recent versions of polars have a problem with decimals
|
|
1594
|
+
class FixCompare(str):
|
|
1595
|
+
def __eq__(self, other):
|
|
1596
|
+
return super().__eq__(other.replace('precision=None', 'precision=22'))
|
|
1597
|
+
|
|
1588
1598
|
dtypes = [
|
|
1589
1599
|
('id', 'Int32'),
|
|
1590
1600
|
('tinyint', 'Int8'),
|
|
@@ -1606,10 +1616,10 @@ class TestConnection(unittest.TestCase):
|
|
|
1606
1616
|
('float', 'Float32'),
|
|
1607
1617
|
('double', 'Float64'),
|
|
1608
1618
|
('real', 'Float64'),
|
|
1609
|
-
('decimal', 'Decimal(precision=22, scale=6)'),
|
|
1610
|
-
('dec', 'Decimal(precision=22, scale=6)'),
|
|
1611
|
-
('fixed', 'Decimal(precision=22, scale=6)'),
|
|
1612
|
-
('numeric', 'Decimal(precision=22, scale=6)'),
|
|
1619
|
+
('decimal', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1620
|
+
('dec', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1621
|
+
('fixed', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1622
|
+
('numeric', FixCompare('Decimal(precision=22, scale=6)')),
|
|
1613
1623
|
('date', 'Date'),
|
|
1614
1624
|
('time', "Duration(time_unit='us')"),
|
|
1615
1625
|
('time_6', "Duration(time_unit='us')"),
|
|
@@ -14,7 +14,6 @@ import singlestoredb as s2
|
|
|
14
14
|
from singlestoredb.management.job import Status
|
|
15
15
|
from singlestoredb.management.job import TargetType
|
|
16
16
|
from singlestoredb.management.region import Region
|
|
17
|
-
from singlestoredb.management.region import RegionManager
|
|
18
17
|
from singlestoredb.management.utils import NamedList
|
|
19
18
|
|
|
20
19
|
|
|
@@ -26,6 +25,11 @@ def clean_name(s):
|
|
|
26
25
|
return re.sub(r'[^\w]', r'-', s).replace('_', '-').lower()
|
|
27
26
|
|
|
28
27
|
|
|
28
|
+
def shared_database_name(s):
|
|
29
|
+
"""Return a shared database name. Cannot contain special characters except -"""
|
|
30
|
+
return re.sub(r'[^\w]', '', s).replace('-', '_').lower()
|
|
31
|
+
|
|
32
|
+
|
|
29
33
|
@pytest.mark.management
|
|
30
34
|
class TestCluster(unittest.TestCase):
|
|
31
35
|
|
|
@@ -366,42 +370,36 @@ class TestWorkspace(unittest.TestCase):
|
|
|
366
370
|
assert 'endpoint' in cm.exception.msg, cm.exception.msg
|
|
367
371
|
|
|
368
372
|
|
|
369
|
-
@pytest.mark.skip('Not implemented in server yet')
|
|
370
373
|
@pytest.mark.management
|
|
371
374
|
class TestStarterWorkspace(unittest.TestCase):
|
|
372
375
|
|
|
373
376
|
manager = None
|
|
374
377
|
starter_workspace = None
|
|
375
|
-
starter_workspace_user = {
|
|
376
|
-
'username': 'starter_user',
|
|
377
|
-
'password': None,
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
@property
|
|
381
|
-
def starter_username(self):
|
|
382
|
-
"""Return the username for the starter workspace user."""
|
|
383
|
-
return self.starter_workspace_user['username']
|
|
384
|
-
|
|
385
|
-
@property
|
|
386
|
-
def password(self):
|
|
387
|
-
"""Return the password for the starter workspace user."""
|
|
388
|
-
return self.starter_workspace_user['password']
|
|
389
378
|
|
|
390
379
|
@classmethod
|
|
391
380
|
def setUpClass(cls):
|
|
392
381
|
cls.manager = s2.manage_workspaces()
|
|
393
382
|
|
|
394
|
-
|
|
395
|
-
|
|
383
|
+
shared_tier_regions: NamedList[Region] = [
|
|
384
|
+
x for x in cls.manager.shared_tier_regions if 'US' in x.name
|
|
385
|
+
]
|
|
386
|
+
cls.starter_username = 'starter_user'
|
|
387
|
+
cls.password = secrets.token_urlsafe(20)
|
|
396
388
|
|
|
397
|
-
name =
|
|
389
|
+
name = shared_database_name(secrets.token_urlsafe(20)[:20])
|
|
390
|
+
|
|
391
|
+
cls.database_name = f'starter_db_{name}'
|
|
392
|
+
|
|
393
|
+
shared_tier_region: Region = random.choice(shared_tier_regions)
|
|
394
|
+
|
|
395
|
+
if not shared_tier_region:
|
|
396
|
+
raise ValueError('No shared tier regions found')
|
|
398
397
|
|
|
399
398
|
cls.starter_workspace = cls.manager.create_starter_workspace(
|
|
400
399
|
f'starter-ws-test-{name}',
|
|
401
|
-
database_name=
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
},
|
|
400
|
+
database_name=cls.database_name,
|
|
401
|
+
provider=shared_tier_region.provider,
|
|
402
|
+
region_name=shared_tier_region.region_name,
|
|
405
403
|
)
|
|
406
404
|
|
|
407
405
|
cls.starter_workspace.create_user(
|
|
@@ -469,14 +467,14 @@ class TestStarterWorkspace(unittest.TestCase):
|
|
|
469
467
|
) as conn:
|
|
470
468
|
with conn.cursor() as cur:
|
|
471
469
|
cur.execute('show databases')
|
|
472
|
-
assert
|
|
470
|
+
assert self.database_name in [x[0] for x in list(cur)]
|
|
473
471
|
|
|
474
472
|
# Test missing endpoint
|
|
475
473
|
workspace = self.manager.get_starter_workspace(self.starter_workspace.id)
|
|
476
474
|
workspace.endpoint = None
|
|
477
475
|
|
|
478
476
|
with self.assertRaises(s2.ManagementError) as cm:
|
|
479
|
-
workspace.connect(user=
|
|
477
|
+
workspace.connect(user=self.starter_username, password=self.password)
|
|
480
478
|
|
|
481
479
|
assert 'endpoint' in cm.exception.msg, cm.exception.msg
|
|
482
480
|
|
|
@@ -1492,7 +1490,6 @@ class TestFileSpaces(unittest.TestCase):
|
|
|
1492
1490
|
space.remove('obj_test_2.ipynb')
|
|
1493
1491
|
|
|
1494
1492
|
|
|
1495
|
-
@pytest.mark.skip('Not implemented in server yet')
|
|
1496
1493
|
@pytest.mark.management
|
|
1497
1494
|
class TestRegions(unittest.TestCase):
|
|
1498
1495
|
"""Test cases for region management."""
|
|
@@ -1530,14 +1527,6 @@ class TestRegions(unittest.TestCase):
|
|
|
1530
1527
|
providers = {x.provider for x in regions}
|
|
1531
1528
|
assert 'Azure' in providers or 'GCP' in providers or 'AWS' in providers
|
|
1532
1529
|
|
|
1533
|
-
# Verify region can be accessed by name or ID
|
|
1534
|
-
region_by_name = regions[region.name]
|
|
1535
|
-
region_by_id = regions[region.id]
|
|
1536
|
-
assert region_by_name == region_by_id
|
|
1537
|
-
assert region_by_name.id == region.id
|
|
1538
|
-
assert region_by_name.name == region.name
|
|
1539
|
-
assert region_by_name.provider == region.provider
|
|
1540
|
-
|
|
1541
1530
|
def test_list_shared_tier_regions(self):
|
|
1542
1531
|
"""Test listing shared tier regions."""
|
|
1543
1532
|
regions = self.manager.list_shared_tier_regions()
|
|
@@ -1549,22 +1538,14 @@ class TestRegions(unittest.TestCase):
|
|
|
1549
1538
|
if regions:
|
|
1550
1539
|
region = regions[0]
|
|
1551
1540
|
assert isinstance(region, Region)
|
|
1552
|
-
assert hasattr(region, 'id')
|
|
1553
1541
|
assert hasattr(region, 'name')
|
|
1554
1542
|
assert hasattr(region, 'provider')
|
|
1543
|
+
assert hasattr(region, 'region_name')
|
|
1555
1544
|
|
|
1556
1545
|
# Verify provider values
|
|
1557
1546
|
providers = {x.provider for x in regions}
|
|
1558
1547
|
assert any(p in providers for p in ['Azure', 'GCP', 'AWS'])
|
|
1559
1548
|
|
|
1560
|
-
# Verify region can be accessed by name or ID
|
|
1561
|
-
region_by_name = regions[region.name]
|
|
1562
|
-
region_by_id = regions[region.id]
|
|
1563
|
-
assert region_by_name == region_by_id
|
|
1564
|
-
assert region_by_name.id == region.id
|
|
1565
|
-
assert region_by_name.name == region.name
|
|
1566
|
-
assert region_by_name.provider == region.provider
|
|
1567
|
-
|
|
1568
1549
|
def test_str_repr(self):
|
|
1569
1550
|
"""Test string representation of regions."""
|
|
1570
1551
|
regions = self.manager.list_regions()
|
|
@@ -1581,17 +1562,3 @@ class TestRegions(unittest.TestCase):
|
|
|
1581
1562
|
|
|
1582
1563
|
# Test __repr__
|
|
1583
1564
|
assert repr(region) == str(region)
|
|
1584
|
-
|
|
1585
|
-
def test_no_manager(self):
|
|
1586
|
-
"""Test behavior when manager is not available."""
|
|
1587
|
-
regions = self.manager.list_regions()
|
|
1588
|
-
if not regions:
|
|
1589
|
-
self.skipTest('No regions available for testing')
|
|
1590
|
-
|
|
1591
|
-
region = regions[0]
|
|
1592
|
-
region._manager = None
|
|
1593
|
-
|
|
1594
|
-
# Verify from_dict class method
|
|
1595
|
-
with self.assertRaises(s2.ManagementError) as cm:
|
|
1596
|
-
RegionManager.list_shared_tier_regions(None)
|
|
1597
|
-
assert 'No workspace manager' in str(cm.exception)
|
singlestoredb/tests/test_udf.py
CHANGED
|
@@ -36,6 +36,32 @@ def to_sql(x):
|
|
|
36
36
|
|
|
37
37
|
class TestUDF(unittest.TestCase):
|
|
38
38
|
|
|
39
|
+
def test_invalid_signature(self):
|
|
40
|
+
|
|
41
|
+
def foo(x: np.ndarray, y: np.ndarray) -> str: ...
|
|
42
|
+
with self.assertRaises(TypeError):
|
|
43
|
+
to_sql(foo)
|
|
44
|
+
|
|
45
|
+
def foo(x: str, y: str) -> np.ndarray: ...
|
|
46
|
+
with self.assertRaises(TypeError):
|
|
47
|
+
to_sql(foo)
|
|
48
|
+
|
|
49
|
+
def foo(x: str, y: np.ndarray) -> np.ndarray: ...
|
|
50
|
+
with self.assertRaises(TypeError):
|
|
51
|
+
to_sql(foo)
|
|
52
|
+
|
|
53
|
+
def foo(x: np.ndarray, y: str) -> np.ndarray: ...
|
|
54
|
+
with self.assertRaises(TypeError):
|
|
55
|
+
to_sql(foo)
|
|
56
|
+
|
|
57
|
+
def foo(x: str, y: np.ndarray) -> str: ...
|
|
58
|
+
with self.assertRaises(TypeError):
|
|
59
|
+
to_sql(foo)
|
|
60
|
+
|
|
61
|
+
def foo(x: np.ndarray, y: str) -> str: ...
|
|
62
|
+
with self.assertRaises(TypeError):
|
|
63
|
+
to_sql(foo)
|
|
64
|
+
|
|
39
65
|
def test_return_annotations(self):
|
|
40
66
|
|
|
41
67
|
# No annotations
|
|
@@ -76,24 +102,25 @@ class TestUDF(unittest.TestCase):
|
|
|
76
102
|
assert to_sql(foo) == '`foo`() RETURNS DOUBLE NULL'
|
|
77
103
|
|
|
78
104
|
# Optional return value with collection type
|
|
79
|
-
def foo() -> Optional[List[str]]: ...
|
|
80
|
-
assert to_sql(foo) == '`foo`() RETURNS ARRAY(TEXT NOT NULL) NULL'
|
|
105
|
+
# def foo() -> Optional[List[str]]: ...
|
|
106
|
+
# assert to_sql(foo) == '`foo`() RETURNS ARRAY(TEXT NOT NULL) NULL'
|
|
81
107
|
|
|
82
108
|
# Optional return value with nested collection type
|
|
83
|
-
def foo() -> Optional[List[List[str]]]: ...
|
|
84
|
-
assert to_sql(foo) == '`foo`()
|
|
109
|
+
# def foo() -> Optional[List[List[str]]]: ...
|
|
110
|
+
# assert to_sql(foo) == '`foo`()
|
|
111
|
+
# RETURNS ARRAY(ARRAY(TEXT NOT NULL) NOT NULL) NULL'
|
|
85
112
|
|
|
86
113
|
# Optional return value with collection type with nulls
|
|
87
|
-
def foo() -> Optional[List[Optional[str]]]: ...
|
|
88
|
-
assert to_sql(foo) == '`foo`() RETURNS ARRAY(TEXT NULL) NULL'
|
|
114
|
+
# def foo() -> Optional[List[Optional[str]]]: ...
|
|
115
|
+
# assert to_sql(foo) == '`foo`() RETURNS ARRAY(TEXT NULL) NULL'
|
|
89
116
|
|
|
90
117
|
# Custom type with bound
|
|
91
118
|
def foo() -> D: ...
|
|
92
119
|
assert to_sql(foo) == '`foo`() RETURNS TEXT NOT NULL'
|
|
93
120
|
|
|
94
121
|
# Return value with custom collection type with nulls
|
|
95
|
-
def foo() -> E: ...
|
|
96
|
-
assert to_sql(foo) == '`foo`() RETURNS ARRAY(DOUBLE NULL) NULL'
|
|
122
|
+
# def foo() -> E: ...
|
|
123
|
+
# assert to_sql(foo) == '`foo`() RETURNS ARRAY(DOUBLE NULL) NULL'
|
|
97
124
|
|
|
98
125
|
# Incompatible types
|
|
99
126
|
def foo() -> Union[int, str]: ...
|
|
@@ -158,17 +185,18 @@ class TestUDF(unittest.TestCase):
|
|
|
158
185
|
assert to_sql(foo) == '`foo`(`x` DOUBLE NULL) RETURNS TINYINT NULL'
|
|
159
186
|
|
|
160
187
|
# Optional parameter with collection type
|
|
161
|
-
def foo(x: Optional[List[str]]) -> None: ...
|
|
162
|
-
assert to_sql(foo) == '`foo`(`x`
|
|
188
|
+
# def foo(x: Optional[List[str]]) -> None: ...
|
|
189
|
+
# assert to_sql(foo) == '`foo`(`x`
|
|
190
|
+
# ARRAY(TEXT NOT NULL) NULL) RETURNS TINYINT NULL'
|
|
163
191
|
|
|
164
192
|
# Optional parameter with nested collection type
|
|
165
|
-
def foo(x: Optional[List[List[str]]]) -> None: ...
|
|
166
|
-
assert to_sql(foo) == '`foo`(`x` ARRAY(ARRAY(TEXT NOT NULL) NOT NULL) NULL) ' \
|
|
167
|
-
|
|
193
|
+
# def foo(x: Optional[List[List[str]]]) -> None: ...
|
|
194
|
+
# assert to_sql(foo) == '`foo`(`x` ARRAY(ARRAY(TEXT NOT NULL) NOT NULL) NULL) ' \
|
|
195
|
+
# 'RETURNS TINYINT NULL'
|
|
168
196
|
|
|
169
197
|
# Optional parameter with collection type with nulls
|
|
170
|
-
def foo(x: Optional[List[Optional[str]]]) -> None: ...
|
|
171
|
-
assert to_sql(foo) == '`foo`(`x` ARRAY(TEXT NULL) NULL) RETURNS TINYINT NULL'
|
|
198
|
+
# def foo(x: Optional[List[Optional[str]]]) -> None: ...
|
|
199
|
+
# assert to_sql(foo) == '`foo`(`x` ARRAY(TEXT NULL) NULL) RETURNS TINYINT NULL'
|
|
172
200
|
|
|
173
201
|
# Custom type with bound
|
|
174
202
|
def foo(x: D) -> None: ...
|