singlestoredb 1.15.7__py3-none-any.whl → 1.16.0__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.15.7'
16
+ __version__ = '1.16.0'
17
17
 
18
18
  from typing import Any
19
19
 
@@ -1,5 +1,2 @@
1
- from .chat import SingleStoreChat # noqa: F401
2
1
  from .chat import SingleStoreChatFactory # noqa: F401
3
- from .chat import SingleStoreChatOpenAI # noqa: F401
4
- from .embeddings import SingleStoreEmbeddings # noqa: F401
5
2
  from .embeddings import SingleStoreEmbeddingsFactory # noqa: F401
singlestoredb/ai/chat.py CHANGED
@@ -29,44 +29,6 @@ from botocore import UNSIGNED
29
29
  from botocore.config import Config
30
30
 
31
31
 
32
- class SingleStoreChatOpenAI(ChatOpenAI):
33
- def __init__(self, model_name: str, api_key: Optional[str] = None, **kwargs: Any):
34
- inference_api_manger = (
35
- manage_workspaces().organizations.current.inference_apis
36
- )
37
- info = inference_api_manger.get(model_name=model_name)
38
- token = (
39
- api_key
40
- if api_key is not None
41
- else os.environ.get('SINGLESTOREDB_USER_TOKEN')
42
- )
43
- super().__init__(
44
- base_url=info.connection_url,
45
- api_key=token,
46
- model=model_name,
47
- **kwargs,
48
- )
49
-
50
-
51
- class SingleStoreChat(ChatOpenAI):
52
- def __init__(self, model_name: str, api_key: Optional[str] = None, **kwargs: Any):
53
- inference_api_manger = (
54
- manage_workspaces().organizations.current.inference_apis
55
- )
56
- info = inference_api_manger.get(model_name=model_name)
57
- token = (
58
- api_key
59
- if api_key is not None
60
- else os.environ.get('SINGLESTOREDB_USER_TOKEN')
61
- )
62
- super().__init__(
63
- base_url=info.connection_url,
64
- api_key=token,
65
- model=model_name,
66
- **kwargs,
67
- )
68
-
69
-
70
32
  def SingleStoreChatFactory(
71
33
  model_name: str,
72
34
  api_key: Optional[str] = None,
@@ -90,9 +52,27 @@ def SingleStoreChatFactory(
90
52
  'signature_version': UNSIGNED,
91
53
  'retries': {'max_attempts': 1, 'mode': 'standard'},
92
54
  }
93
- if http_client is not None and http_client.timeout is not None:
94
- cfg_kwargs['read_timeout'] = http_client.timeout
95
- cfg_kwargs['connect_timeout'] = http_client.timeout
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
96
76
 
97
77
  cfg = Config(**cfg_kwargs)
98
78
  client = boto3.client(
@@ -29,21 +29,6 @@ from botocore import UNSIGNED
29
29
  from botocore.config import Config
30
30
 
31
31
 
32
- class SingleStoreEmbeddings(OpenAIEmbeddings):
33
-
34
- def __init__(self, model_name: str, **kwargs: Any):
35
- inference_api_manger = (
36
- manage_workspaces().organizations.current.inference_apis
37
- )
38
- info = inference_api_manger.get(model_name=model_name)
39
- super().__init__(
40
- base_url=info.connection_url,
41
- api_key=os.environ.get('SINGLESTOREDB_USER_TOKEN'),
42
- model=model_name,
43
- **kwargs,
44
- )
45
-
46
-
47
32
  def SingleStoreEmbeddingsFactory(
48
33
  model_name: str,
49
34
  api_key: Optional[str] = None,
@@ -66,9 +51,27 @@ def SingleStoreEmbeddingsFactory(
66
51
  'signature_version': UNSIGNED,
67
52
  'retries': {'max_attempts': 1, 'mode': 'standard'},
68
53
  }
69
- if http_client is not None and http_client.timeout is not None:
70
- cfg_kwargs['read_timeout'] = http_client.timeout
71
- cfg_kwargs['connect_timeout'] = http_client.timeout
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
72
75
 
73
76
  cfg = Config(**cfg_kwargs)
74
77
  client = boto3.client(
@@ -9,15 +9,15 @@ import re
9
9
  import sys
10
10
  import warnings
11
11
  import weakref
12
+ from collections.abc import Iterator
12
13
  from collections.abc import Mapping
13
14
  from collections.abc import MutableMapping
15
+ from collections.abc import Sequence
14
16
  from typing import Any
15
17
  from typing import Callable
16
18
  from typing import Dict
17
- from typing import Iterator
18
19
  from typing import List
19
20
  from typing import Optional
20
- from typing import Sequence
21
21
  from typing import Tuple
22
22
  from typing import Union
23
23
  from urllib.parse import parse_qs
@@ -48,15 +48,15 @@ import urllib
48
48
  import uuid
49
49
  import zipfile
50
50
  import zipimport
51
+ from collections.abc import Awaitable
52
+ from collections.abc import Iterable
53
+ from collections.abc import Sequence
51
54
  from types import ModuleType
52
55
  from typing import Any
53
- from typing import Awaitable
54
56
  from typing import Callable
55
57
  from typing import Dict
56
- from typing import Iterable
57
58
  from typing import List
58
59
  from typing import Optional
59
- from typing import Sequence
60
60
  from typing import Set
61
61
  from typing import Tuple
62
62
  from typing import Union
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env python3
2
2
  import struct
3
3
  import warnings
4
+ from collections.abc import Sequence
4
5
  from io import BytesIO
5
6
  from typing import Any
6
7
  from typing import List
7
8
  from typing import Optional
8
- from typing import Sequence
9
9
  from typing import Tuple
10
10
  from typing import TYPE_CHECKING
11
11
 
@@ -9,12 +9,12 @@ import string
9
9
  import sys
10
10
  import types
11
11
  import typing
12
+ from collections.abc import Sequence
12
13
  from typing import Any
13
14
  from typing import Callable
14
15
  from typing import Dict
15
16
  from typing import List
16
17
  from typing import Optional
17
- from typing import Sequence
18
18
  from typing import Tuple
19
19
  from typing import TypeVar
20
20
  from typing import Union
@@ -1,5 +1,5 @@
1
+ from collections.abc import Iterable
1
2
  from typing import Any
2
- from typing import Iterable
3
3
  from typing import Tuple
4
4
  from typing import TypeVar
5
5
 
@@ -4,10 +4,10 @@ import struct
4
4
  import sys
5
5
  import types
6
6
  import typing
7
+ from collections.abc import Iterable
7
8
  from enum import Enum
8
9
  from typing import Any
9
10
  from typing import Dict
10
- from typing import Iterable
11
11
  from typing import Tuple
12
12
  from typing import Union
13
13
 
@@ -5,10 +5,10 @@ import os
5
5
  import re
6
6
  import sys
7
7
  import textwrap
8
+ from collections.abc import Iterable
8
9
  from typing import Any
9
10
  from typing import Callable
10
11
  from typing import Dict
11
- from typing import Iterable
12
12
  from typing import List
13
13
  from typing import Optional
14
14
  from typing import Set
@@ -2,8 +2,8 @@
2
2
  from __future__ import annotations
3
3
 
4
4
  import re
5
+ from collections.abc import Iterable
5
6
  from typing import Any
6
- from typing import Iterable
7
7
  from typing import List
8
8
  from typing import Optional
9
9
  from typing import Tuple
@@ -10,13 +10,13 @@ import os
10
10
  import re
11
11
  import time
12
12
  from base64 import b64decode
13
+ from collections.abc import Iterable
14
+ from collections.abc import Sequence
13
15
  from typing import Any
14
16
  from typing import Callable
15
17
  from typing import Dict
16
- from typing import Iterable
17
18
  from typing import List
18
19
  from typing import Optional
19
- from typing import Sequence
20
20
  from typing import Tuple
21
21
  from typing import Union
22
22
  from urllib.parse import urljoin
@@ -15,6 +15,7 @@ import requests
15
15
 
16
16
  from .. import config
17
17
  from ..exceptions import ManagementError
18
+ from ..exceptions import OperationalError
18
19
  from .utils import get_token
19
20
 
20
21
 
@@ -310,3 +311,63 @@ class Manager(object):
310
311
  out = getattr(self, f'get_{self.obj_type}')(out.id)
311
312
 
312
313
  return out
314
+
315
+ def _wait_on_endpoint(
316
+ self,
317
+ out: Any,
318
+ interval: int = 10,
319
+ timeout: int = 300,
320
+ ) -> Any:
321
+ """
322
+ Wait for the endpoint to be ready by attempting to connect.
323
+
324
+ Parameters
325
+ ----------
326
+ out : Any
327
+ Workspace object with a connect method
328
+ interval : int, optional
329
+ Interval between each connection attempt (default: 10 seconds)
330
+ timeout : int, optional
331
+ Maximum time to wait before raising an exception (default: 300 seconds)
332
+
333
+ Raises
334
+ ------
335
+ ManagementError
336
+ If timeout is reached or endpoint is not available
337
+
338
+ Returns
339
+ -------
340
+ Same object type as `out`
341
+
342
+ """
343
+ # Only wait if workload type is set which means we are in the
344
+ # notebook environment. Outside of the environment, the endpoint
345
+ # may not be reachable directly.
346
+ if not os.environ.get('SINGLESTOREDB_WORKLOAD_TYPE', ''):
347
+ return out
348
+
349
+ if not hasattr(out, 'connect') or not out.connect:
350
+ raise ManagementError(
351
+ msg=f'{type(out).__name__} object does not have a valid endpoint',
352
+ )
353
+
354
+ while True:
355
+ try:
356
+ # Try to establish a connection to the endpoint using context manager
357
+ with out.connect(connect_timeout=5):
358
+ pass
359
+ except Exception as exc:
360
+ # If we get an 'access denied' error, that means that the server is
361
+ # up and we just aren't authenticating.
362
+ if isinstance(exc, OperationalError) and exc.errno == 1045:
363
+ break
364
+ # If connection fails, check timeout and retry
365
+ if timeout <= 0:
366
+ raise ManagementError(
367
+ msg=f'Exceeded waiting time for {self.obj_type} endpoint '
368
+ 'to become ready',
369
+ )
370
+ time.sleep(interval)
371
+ timeout -= interval
372
+
373
+ return out
@@ -6,11 +6,11 @@ import itertools
6
6
  import os
7
7
  import re
8
8
  import sys
9
+ from collections.abc import Mapping
9
10
  from typing import Any
10
11
  from typing import Callable
11
12
  from typing import Dict
12
13
  from typing import List
13
- from typing import Mapping
14
14
  from typing import Optional
15
15
  from typing import SupportsIndex
16
16
  from typing import Tuple
@@ -1794,6 +1794,12 @@ class WorkspaceManager(Manager):
1794
1794
  interval=wait_interval,
1795
1795
  timeout=wait_timeout,
1796
1796
  )
1797
+ # After workspace is active, wait for endpoint to be ready
1798
+ out = self._wait_on_endpoint(
1799
+ out,
1800
+ interval=wait_interval,
1801
+ timeout=wait_timeout,
1802
+ )
1797
1803
  return out
1798
1804
 
1799
1805
  def get_workspace_group(self, id: str) -> WorkspaceGroup:
@@ -13,9 +13,9 @@ import struct
13
13
  import sys
14
14
  import traceback
15
15
  import warnings
16
+ from collections.abc import Iterable
16
17
  from typing import Any
17
18
  from typing import Dict
18
- from typing import Iterable
19
19
 
20
20
  try:
21
21
  import _singlestoredb_accel
singlestoredb/pytest.py CHANGED
@@ -2,10 +2,12 @@
2
2
  """Pytest plugin"""
3
3
  import logging
4
4
  import os
5
+ import socket
5
6
  import subprocess
6
7
  import time
8
+ import uuid
9
+ from collections.abc import Iterator
7
10
  from enum import Enum
8
- from typing import Iterator
9
11
  from typing import Optional
10
12
 
11
13
  import pytest
@@ -28,6 +30,14 @@ TEARDOWN_WAIT_ATTEMPTS = 20
28
30
  TEARDOWN_WAIT_SECONDS = 2
29
31
 
30
32
 
33
+ def _find_free_port() -> int:
34
+ """Find a free port by binding to port 0 and getting the assigned port."""
35
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
36
+ s.bind(('', 0))
37
+ s.listen(1)
38
+ return s.getsockname()[1]
39
+
40
+
31
41
  class ExecutionMode(Enum):
32
42
  SEQUENTIAL = 1
33
43
  LEADER = 2
@@ -79,7 +89,11 @@ class _TestContainerManager():
79
89
  """Manages the setup and teardown of a SingleStoreDB Dev Container"""
80
90
 
81
91
  def __init__(self) -> None:
82
- self.container_name = 'singlestoredb-test-container'
92
+ # Generate unique container name using UUID and worker ID
93
+ worker = os.environ.get('PYTEST_XDIST_WORKER', 'master')
94
+ unique_id = uuid.uuid4().hex[:8]
95
+ self.container_name = f'singlestoredb-test-{worker}-{unique_id}'
96
+
83
97
  self.dev_image_name = 'ghcr.io/singlestore-labs/singlestoredb-dev'
84
98
 
85
99
  assert 'SINGLESTORE_LICENSE' in os.environ, 'SINGLESTORE_LICENSE not set'
@@ -91,14 +105,69 @@ class _TestContainerManager():
91
105
  'SINGLESTORE_SET_GLOBAL_DEFAULT_PARTITIONS_PER_LEAF': '1',
92
106
  }
93
107
 
94
- self.ports = ['3306', '8080', '9000']
108
+ # Use dynamic port allocation to avoid conflicts
109
+ self.mysql_port = _find_free_port()
110
+ self.http_port = _find_free_port()
111
+ self.studio_port = _find_free_port()
112
+ self.ports = [
113
+ (self.mysql_port, '3306'), # External port -> Internal port
114
+ (self.http_port, '8080'),
115
+ (self.studio_port, '9000'),
116
+ ]
117
+
118
+ self.url = f'root:{self.root_password}@127.0.0.1:{self.mysql_port}'
119
+
120
+ def _container_exists(self) -> bool:
121
+ """Check if a container with this name already exists."""
122
+ try:
123
+ result = subprocess.run(
124
+ [
125
+ 'docker', 'ps', '-a', '--filter',
126
+ f'name={self.container_name}',
127
+ '--format', '{{.Names}}',
128
+ ],
129
+ capture_output=True,
130
+ text=True,
131
+ check=True,
132
+ )
133
+ return self.container_name in result.stdout
134
+ except subprocess.CalledProcessError:
135
+ return False
136
+
137
+ def _cleanup_existing_container(self) -> None:
138
+ """Stop and remove any existing container with the same name."""
139
+ if not self._container_exists():
140
+ return
95
141
 
96
- self.url = f'root:{self.root_password}@127.0.0.1:3306'
142
+ logger.info(f'Found existing container {self.container_name}, cleaning up')
143
+ try:
144
+ # Try to stop the container (ignore if it's already stopped)
145
+ subprocess.run(
146
+ ['docker', 'stop', self.container_name],
147
+ capture_output=True,
148
+ check=False,
149
+ )
150
+ # Remove the container
151
+ subprocess.run(
152
+ ['docker', 'rm', self.container_name],
153
+ capture_output=True,
154
+ check=True,
155
+ )
156
+ logger.debug(f'Cleaned up existing container {self.container_name}')
157
+ except subprocess.CalledProcessError as e:
158
+ logger.warning(f'Failed to cleanup existing container: {e}')
159
+ # Continue anyway - the unique name should prevent most conflicts
97
160
 
98
161
  def start(self) -> None:
162
+ # Clean up any existing container with the same name
163
+ self._cleanup_existing_container()
164
+
99
165
  command = ' '.join(self._start_command())
100
166
 
101
- logger.info(f'Starting container {self.container_name}')
167
+ logger.info(
168
+ f'Starting container {self.container_name} on ports {self.mysql_port}, '
169
+ f'{self.http_port}, {self.studio_port}',
170
+ )
102
171
  try:
103
172
  license = os.environ['SINGLESTORE_LICENSE']
104
173
  env = {
@@ -108,8 +177,8 @@ class _TestContainerManager():
108
177
  except Exception as e:
109
178
  logger.exception(e)
110
179
  raise RuntimeError(
111
- 'Failed to start container. '
112
- 'Is one already running?',
180
+ f'Failed to start container {self.container_name}. '
181
+ f'Command: {command}',
113
182
  ) from e
114
183
  logger.debug('Container started')
115
184
 
@@ -123,9 +192,9 @@ class _TestContainerManager():
123
192
  else:
124
193
  yield f'{key}={value}'
125
194
 
126
- for port in self.ports:
195
+ for external_port, internal_port in self.ports:
127
196
  yield '-p'
128
- yield f'{port}:{port}'
197
+ yield f'{external_port}:{internal_port}'
129
198
 
130
199
  yield self.dev_image_name
131
200
 
@@ -30,6 +30,7 @@ def shared_database_name(s):
30
30
  return re.sub(r'[^\w]', '', s).replace('-', '_').lower()
31
31
 
32
32
 
33
+ @pytest.mark.skip(reason='Legacy cluster Management API is going away')
33
34
  @pytest.mark.management
34
35
  class TestCluster(unittest.TestCase):
35
36
 
@@ -675,184 +676,201 @@ class TestStage(unittest.TestCase):
675
676
  def test_os_directories(self):
676
677
  st = self.wg.stage
677
678
 
679
+ mkdir_test_1 = f'mkdir_test_1_{id(self)}'
680
+ mkdir_test_2 = f'mkdir_test_2_{id(self)}'
681
+ mkdir_test_3 = f'mkdir_test_3_{id(self)}'
682
+
678
683
  # mkdir
679
- st.mkdir('mkdir_test_1')
680
- st.mkdir('mkdir_test_2')
684
+ st.mkdir(mkdir_test_1)
685
+ st.mkdir(mkdir_test_2)
681
686
  with self.assertRaises(s2.ManagementError):
682
- st.mkdir('mkdir_test_2/nest_1/nest_2')
683
- st.mkdir('mkdir_test_2/nest_1')
684
- st.mkdir('mkdir_test_2/nest_1/nest_2')
685
- st.mkdir('mkdir_test_3')
686
-
687
- assert st.exists('mkdir_test_1/')
688
- assert st.exists('mkdir_test_2/')
689
- assert st.exists('mkdir_test_2/nest_1/')
690
- assert st.exists('mkdir_test_2/nest_1/nest_2/')
687
+ st.mkdir(f'{mkdir_test_2}/nest_1/nest_2')
688
+ st.mkdir(f'{mkdir_test_2}/nest_1')
689
+ st.mkdir(f'{mkdir_test_2}/nest_1/nest_2')
690
+ st.mkdir(f'{mkdir_test_3}')
691
+
692
+ assert st.exists(f'{mkdir_test_1}/')
693
+ assert st.exists(f'{mkdir_test_2}/')
694
+ assert st.exists(f'{mkdir_test_2}/nest_1/')
695
+ assert st.exists(f'{mkdir_test_2}/nest_1/nest_2/')
691
696
  assert not st.exists('foo/')
692
697
  assert not st.exists('foo/bar/')
693
698
 
694
- assert st.is_dir('mkdir_test_1/')
695
- assert st.is_dir('mkdir_test_2/')
696
- assert st.is_dir('mkdir_test_2/nest_1/')
697
- assert st.is_dir('mkdir_test_2/nest_1/nest_2/')
699
+ assert st.is_dir(f'{mkdir_test_1}/')
700
+ assert st.is_dir(f'{mkdir_test_2}/')
701
+ assert st.is_dir(f'{mkdir_test_2}/nest_1/')
702
+ assert st.is_dir(f'{mkdir_test_2}/nest_1/nest_2/')
698
703
 
699
- assert not st.is_file('mkdir_test_1/')
700
- assert not st.is_file('mkdir_test_2/')
701
- assert not st.is_file('mkdir_test_2/nest_1/')
702
- assert not st.is_file('mkdir_test_2/nest_1/nest_2/')
704
+ assert not st.is_file(f'{mkdir_test_1}/')
705
+ assert not st.is_file(f'{mkdir_test_2}/')
706
+ assert not st.is_file(f'{mkdir_test_2}/nest_1/')
707
+ assert not st.is_file(f'{mkdir_test_2}/nest_1/nest_2/')
703
708
 
704
709
  out = st.listdir('/')
705
- assert 'mkdir_test_1/' in out
706
- assert 'mkdir_test_2/' in out
707
- assert 'mkdir_test_2/nest_1/nest_2/' not in out
710
+ assert f'{mkdir_test_1}/' in out
711
+ assert f'{mkdir_test_2}/' in out
712
+ assert f'{mkdir_test_2}/nest_1/nest_2/' not in out
708
713
 
709
714
  out = st.listdir('/', recursive=True)
710
- assert 'mkdir_test_1/' in out
711
- assert 'mkdir_test_2/' in out
712
- assert 'mkdir_test_2/nest_1/nest_2/' in out
715
+ assert f'{mkdir_test_1}/' in out
716
+ assert f'{mkdir_test_2}/' in out
717
+ assert f'{mkdir_test_2}/nest_1/nest_2/' in out
713
718
 
714
- out = st.listdir('mkdir_test_2')
715
- assert 'mkdir_test_1/' not in out
719
+ out = st.listdir(mkdir_test_2)
720
+ assert f'{mkdir_test_1}/' not in out
716
721
  assert 'nest_1/' in out
717
722
  assert 'nest_2/' not in out
718
723
  assert 'nest_1/nest_2/' not in out
719
724
 
720
- out = st.listdir('mkdir_test_2', recursive=True)
721
- assert 'mkdir_test_1/' not in out
725
+ out = st.listdir(mkdir_test_2, recursive=True)
726
+ assert f'{mkdir_test_1}/' not in out
722
727
  assert 'nest_1/' in out
723
728
  assert 'nest_2/' not in out
724
729
  assert 'nest_1/nest_2/' in out
725
730
 
726
731
  # rmdir
727
732
  before = st.listdir('/', recursive=True)
728
- st.rmdir('mkdir_test_1/')
733
+ st.rmdir(f'{mkdir_test_1}/')
729
734
  after = st.listdir('/', recursive=True)
730
- assert 'mkdir_test_1/' in before
731
- assert 'mkdir_test_1/' not in after
732
- assert list(sorted(before)) == list(sorted(after + ['mkdir_test_1/']))
735
+ assert f'{mkdir_test_1}/' in before
736
+ assert f'{mkdir_test_1}/' not in after
737
+ assert list(sorted(before)) == list(sorted(after + [f'{mkdir_test_1}/']))
733
738
 
734
739
  with self.assertRaises(OSError):
735
- st.rmdir('mkdir_test_2/')
740
+ st.rmdir(f'{mkdir_test_2}/')
741
+
742
+ mkdir_test_sql = f'mkdir_test_{id(self)}.sql'
736
743
 
737
- st.upload_file(TEST_DIR / 'test.sql', 'mkdir_test.sql')
744
+ st.upload_file(TEST_DIR / 'test.sql', mkdir_test_sql)
738
745
 
739
746
  with self.assertRaises(NotADirectoryError):
740
- st.rmdir('mkdir_test.sql')
747
+ st.rmdir(mkdir_test_sql)
741
748
 
742
749
  # removedirs
743
750
  before = st.listdir('/')
744
- st.removedirs('mkdir_test_2/')
751
+ st.removedirs(f'{mkdir_test_2}/')
745
752
  after = st.listdir('/')
746
- assert 'mkdir_test_2/' in before
747
- assert 'mkdir_test_2/' not in after
748
- assert list(sorted(before)) == list(sorted(after + ['mkdir_test_2/']))
753
+ assert f'{mkdir_test_2}/' in before
754
+ assert f'{mkdir_test_2}/' not in after
755
+ assert list(sorted(before)) == list(sorted(after + [f'{mkdir_test_2}/']))
749
756
 
750
757
  with self.assertRaises(s2.ManagementError):
751
- st.removedirs('mkdir_test.sql')
758
+ st.removedirs(mkdir_test_sql)
752
759
 
753
760
  def test_os_files(self):
754
761
  st = self.wg.stage
755
762
 
756
- st.mkdir('files_test_1')
757
- st.mkdir('files_test_1/nest_1')
763
+ files_test_sql = f'files_test_{id(self)}.sql'
764
+ files_test_1_dir = f'files_test_1_{id(self)}'
765
+
766
+ st.mkdir(files_test_1_dir)
767
+ st.mkdir(f'{files_test_1_dir}/nest_1')
758
768
 
759
- st.upload_file(TEST_DIR / 'test.sql', 'files_test.sql')
760
- st.upload_file(TEST_DIR / 'test.sql', 'files_test_1/nest_1/nested_files_test.sql')
769
+ st.upload_file(TEST_DIR / 'test.sql', files_test_sql)
770
+ st.upload_file(
771
+ TEST_DIR / 'test.sql',
772
+ f'{files_test_1_dir}/nest_1/nested_files_test.sql',
773
+ )
761
774
  st.upload_file(
762
775
  TEST_DIR / 'test.sql',
763
- 'files_test_1/nest_1/nested_files_test_2.sql',
776
+ f'{files_test_1_dir}/nest_1/nested_files_test_2.sql',
764
777
  )
765
778
 
766
779
  # remove
767
780
  with self.assertRaises(IsADirectoryError):
768
- st.remove('files_test_1/')
781
+ st.remove(f'{files_test_1_dir}/')
769
782
 
770
783
  before = st.listdir('/')
771
- st.remove('files_test.sql')
784
+ st.remove(files_test_sql)
772
785
  after = st.listdir('/')
773
- assert 'files_test.sql' in before
774
- assert 'files_test.sql' not in after
775
- assert list(sorted(before)) == list(sorted(after + ['files_test.sql']))
786
+ assert files_test_sql in before
787
+ assert files_test_sql not in after
788
+ assert list(sorted(before)) == list(sorted(after + [files_test_sql]))
776
789
 
777
- before = st.listdir('files_test_1/nest_1/')
778
- st.remove('files_test_1/nest_1/nested_files_test.sql')
779
- after = st.listdir('files_test_1/nest_1/')
790
+ before = st.listdir(f'{files_test_1_dir}/nest_1/')
791
+ st.remove(f'{files_test_1_dir}/nest_1/nested_files_test.sql')
792
+ after = st.listdir(f'{files_test_1_dir}/nest_1/')
780
793
  assert 'nested_files_test.sql' in before
781
794
  assert 'nested_files_test.sql' not in after
782
- assert st.is_dir('files_test_1/nest_1/')
795
+ assert st.is_dir(f'{files_test_1_dir}/nest_1/')
783
796
 
784
797
  # Removing the last file does not remove empty directories
785
- st.remove('files_test_1/nest_1/nested_files_test_2.sql')
786
- assert not st.is_file('files_test_1/nest_1/nested_files_test_2.sql')
787
- assert st.is_dir('files_test_1/nest_1/')
788
- assert st.is_dir('files_test_1/')
798
+ st.remove(f'{files_test_1_dir}/nest_1/nested_files_test_2.sql')
799
+ assert not st.is_file(f'{files_test_1_dir}/nest_1/nested_files_test_2.sql')
800
+ assert st.is_dir(f'{files_test_1_dir}/nest_1/')
801
+ assert st.is_dir(f'{files_test_1_dir}/')
789
802
 
790
- st.removedirs('files_test_1')
791
- assert not st.is_dir('files_test_1/nest_1/')
792
- assert not st.is_dir('files_test_1/')
803
+ st.removedirs(files_test_1_dir)
804
+ assert not st.is_dir(f'{files_test_1_dir}/nest_1/')
805
+ assert not st.is_dir(f'{files_test_1_dir}/')
793
806
 
794
807
  def test_os_rename(self):
795
808
  st = self.wg.stage
796
809
 
797
- st.upload_file(TEST_DIR / 'test.sql', 'rename_test.sql')
810
+ rename_test_sql = f'rename_test_{id(self)}.sql'
811
+ rename_test_2_sql = f'rename_test_2_{id(self)}.sql'
812
+ rename_test_1_dir = f'rename_test_1_{id(self)}'
813
+ rename_test_2_dir = f'rename_test_2_{id(self)}'
814
+
815
+ st.upload_file(TEST_DIR / 'test.sql', rename_test_sql)
798
816
 
799
817
  with self.assertRaises(s2.ManagementError):
800
818
  st.upload_file(
801
819
  TEST_DIR / 'test.sql',
802
- 'rename_test_1/nest_1/nested_rename_test.sql',
820
+ f'{rename_test_1_dir}/nest_1/nested_rename_test.sql',
803
821
  )
804
822
 
805
- st.mkdir('rename_test_1')
806
- st.mkdir('rename_test_1/nest_1')
823
+ st.mkdir(rename_test_1_dir)
824
+ st.mkdir(f'{rename_test_1_dir}/nest_1')
807
825
 
808
- assert st.exists('/rename_test_1/nest_1/')
826
+ assert st.exists(f'/{rename_test_1_dir}/nest_1/')
809
827
 
810
828
  st.upload_file(
811
829
  TEST_DIR / 'test.sql',
812
- 'rename_test_1/nest_1/nested_rename_test.sql',
830
+ f'{rename_test_1_dir}/nest_1/nested_rename_test.sql',
813
831
  )
814
832
 
815
833
  st.upload_file(
816
834
  TEST_DIR / 'test.sql',
817
- 'rename_test_1/nest_1/nested_rename_test_2.sql',
835
+ f'{rename_test_1_dir}/nest_1/nested_rename_test_2.sql',
818
836
  )
819
837
 
820
838
  # rename file
821
- assert 'rename_test.sql' in st.listdir('/')
822
- assert 'rename_test_2.sql' not in st.listdir('/')
823
- st.rename('rename_test.sql', 'rename_test_2.sql')
824
- assert 'rename_test.sql' not in st.listdir('/')
825
- assert 'rename_test_2.sql' in st.listdir('/')
839
+ assert rename_test_sql in st.listdir('/')
840
+ assert rename_test_2_sql not in st.listdir('/')
841
+ st.rename(rename_test_sql, rename_test_2_sql)
842
+ assert rename_test_sql not in st.listdir('/')
843
+ assert rename_test_2_sql in st.listdir('/')
826
844
 
827
845
  # rename directory
828
- assert 'rename_test_1/' in st.listdir('/')
829
- assert 'rename_test_2/' not in st.listdir('/')
830
- st.rename('rename_test_1/', 'rename_test_2/')
831
- assert 'rename_test_1/' not in st.listdir('/')
832
- assert 'rename_test_2/' in st.listdir('/')
833
- assert st.is_file('rename_test_2/nest_1/nested_rename_test.sql')
834
- assert st.is_file('rename_test_2/nest_1/nested_rename_test_2.sql')
846
+ assert f'{rename_test_1_dir}/' in st.listdir('/')
847
+ assert f'{rename_test_2_dir}/' not in st.listdir('/')
848
+ st.rename(f'{rename_test_1_dir}/', f'{rename_test_2_dir}/')
849
+ assert f'{rename_test_1_dir}/' not in st.listdir('/')
850
+ assert f'{rename_test_2_dir}/' in st.listdir('/')
851
+ assert st.is_file(f'{rename_test_2_dir}/nest_1/nested_rename_test.sql')
852
+ assert st.is_file(f'{rename_test_2_dir}/nest_1/nested_rename_test_2.sql')
835
853
 
836
854
  # rename nested
837
- assert 'rename_test_2/nest_1/nested_rename_test.sql' in st.listdir(
855
+ assert f'{rename_test_2_dir}/nest_1/nested_rename_test.sql' in st.listdir(
838
856
  '/', recursive=True,
839
857
  )
840
- assert 'rename_test_2/nest_1/nested_rename_test_3.sql' not in st.listdir(
858
+ assert f'{rename_test_2_dir}/nest_1/nested_rename_test_3.sql' not in st.listdir(
841
859
  '/', recursive=True,
842
860
  )
843
861
  st.rename(
844
- 'rename_test_2/nest_1/nested_rename_test.sql',
845
- 'rename_test_2/nest_1/nested_rename_test_3.sql',
862
+ f'{rename_test_2_dir}/nest_1/nested_rename_test.sql',
863
+ f'{rename_test_2_dir}/nest_1/nested_rename_test_3.sql',
846
864
  )
847
- assert 'rename_test_2/nest_1/nested_rename_test.sql' not in st.listdir(
865
+ assert f'{rename_test_2_dir}/nest_1/nested_rename_test.sql' not in st.listdir(
848
866
  '/', recursive=True,
849
867
  )
850
- assert 'rename_test_2/nest_1/nested_rename_test_3.sql' in st.listdir(
868
+ assert f'{rename_test_2_dir}/nest_1/nested_rename_test_3.sql' in st.listdir(
851
869
  '/', recursive=True,
852
870
  )
853
- assert not st.is_file('rename_test_2/nest_1/nested_rename_test.sql')
854
- assert st.is_file('rename_test_2/nest_1/nested_rename_test_2.sql')
855
- assert st.is_file('rename_test_2/nest_1/nested_rename_test_3.sql')
871
+ assert not st.is_file(f'{rename_test_2_dir}/nest_1/nested_rename_test.sql')
872
+ assert st.is_file(f'{rename_test_2_dir}/nest_1/nested_rename_test_2.sql')
873
+ assert st.is_file(f'{rename_test_2_dir}/nest_1/nested_rename_test_3.sql')
856
874
 
857
875
  # non-existent file
858
876
  with self.assertRaises(OSError):
@@ -861,13 +879,13 @@ class TestStage(unittest.TestCase):
861
879
  # overwrite
862
880
  with self.assertRaises(OSError):
863
881
  st.rename(
864
- 'rename_test_2.sql',
865
- 'rename_test_2/nest_1/nested_rename_test_3.sql',
882
+ rename_test_2_sql,
883
+ f'{rename_test_2_dir}/nest_1/nested_rename_test_3.sql',
866
884
  )
867
885
 
868
886
  st.rename(
869
- 'rename_test_2.sql',
870
- 'rename_test_2/nest_1/nested_rename_test_3.sql', overwrite=True,
887
+ rename_test_2_sql,
888
+ f'{rename_test_2_dir}/nest_1/nested_rename_test_3.sql', overwrite=True,
871
889
  )
872
890
 
873
891
  def test_file_object(self):
@@ -1399,35 +1417,41 @@ class TestFileSpaces(unittest.TestCase):
1399
1417
  space.remove(obj_open_test_ipynb)
1400
1418
 
1401
1419
  def test_os_directories(self):
1420
+ mkdir_test_1_dir = f'mkdir_test_1_{id(self)}'
1421
+
1402
1422
  for space in [self.personal_space, self.shared_space]:
1403
1423
  # Make sure directories error out
1404
1424
  with self.assertRaises(s2.ManagementError):
1405
- space.mkdir('mkdir_test_1')
1425
+ space.mkdir(mkdir_test_1_dir)
1406
1426
 
1407
1427
  with self.assertRaises(s2.ManagementError):
1408
- space.exists('mkdir_test_1/')
1428
+ space.exists(f'{mkdir_test_1_dir}/')
1409
1429
 
1410
1430
  out = space.listdir('/')
1411
- assert 'mkdir_test_1/' not in out
1431
+ assert f'{mkdir_test_1_dir}/' not in out
1412
1432
 
1413
1433
  with self.assertRaises(s2.ManagementError):
1414
- space.rmdir('mkdir_test_1/')
1434
+ space.rmdir(f'{mkdir_test_1_dir}/')
1415
1435
 
1416
1436
  def test_os_rename(self):
1437
+ rename_test_ipynb = f'rename_test_{id(self)}.ipynb'
1438
+ rename_test_2_ipynb = f'rename_test_2_{id(self)}.ipynb'
1439
+ rename_test_3_ipynb = f'rename_test_3_{id(self)}.ipynb'
1440
+
1417
1441
  for space in [self.personal_space, self.shared_space]:
1418
1442
  space.upload_file(
1419
1443
  TEST_DIR / 'test.ipynb',
1420
- 'rename_test.ipynb',
1444
+ rename_test_ipynb,
1421
1445
  )
1422
- assert 'rename_test.ipynb' in space.listdir('/')
1423
- assert 'rename_test_2.ipynb' not in space.listdir('/')
1446
+ assert rename_test_ipynb in space.listdir('/')
1447
+ assert rename_test_2_ipynb not in space.listdir('/')
1424
1448
 
1425
1449
  space.rename(
1426
- 'rename_test.ipynb',
1427
- 'rename_test_2.ipynb',
1450
+ rename_test_ipynb,
1451
+ rename_test_2_ipynb,
1428
1452
  )
1429
- assert 'rename_test.ipynb' not in space.listdir('/')
1430
- assert 'rename_test_2.ipynb' in space.listdir('/')
1453
+ assert rename_test_ipynb not in space.listdir('/')
1454
+ assert rename_test_2_ipynb in space.listdir('/')
1431
1455
 
1432
1456
  # non-existent file
1433
1457
  with self.assertRaises(OSError):
@@ -1435,37 +1459,40 @@ class TestFileSpaces(unittest.TestCase):
1435
1459
 
1436
1460
  space.upload_file(
1437
1461
  TEST_DIR / 'test.ipynb',
1438
- 'rename_test_3.ipynb',
1462
+ rename_test_3_ipynb,
1439
1463
  )
1440
1464
 
1441
1465
  # overwrite
1442
1466
  with self.assertRaises(OSError):
1443
1467
  space.rename(
1444
- 'rename_test_2.ipynb',
1445
- 'rename_test_3.ipynb',
1468
+ rename_test_2_ipynb,
1469
+ rename_test_3_ipynb,
1446
1470
  )
1447
1471
 
1448
1472
  space.rename(
1449
- 'rename_test_2.ipynb',
1450
- 'rename_test_3.ipynb', overwrite=True,
1473
+ rename_test_2_ipynb,
1474
+ rename_test_3_ipynb, overwrite=True,
1451
1475
  )
1452
1476
 
1453
1477
  # Cleanup
1454
- space.remove('rename_test_3.ipynb')
1478
+ space.remove(rename_test_3_ipynb)
1455
1479
 
1456
1480
  def test_file_object(self):
1481
+ obj_test_ipynb = f'obj_test_{id(self)}.ipynb'
1482
+ obj_test_2_ipynb = f'obj_test_2_{id(self)}.ipynb'
1483
+
1457
1484
  for space in [self.personal_space, self.shared_space]:
1458
1485
  f = space.upload_file(
1459
1486
  TEST_DIR / 'test.ipynb',
1460
- 'obj_test.ipynb',
1487
+ obj_test_ipynb,
1461
1488
  )
1462
1489
 
1463
1490
  assert not f.is_dir()
1464
1491
  assert f.is_file()
1465
1492
 
1466
1493
  # abspath / basename / dirname / exists
1467
- assert f.abspath() == 'obj_test.ipynb'
1468
- assert f.basename() == 'obj_test.ipynb'
1494
+ assert f.abspath() == obj_test_ipynb
1495
+ assert f.basename() == obj_test_ipynb
1469
1496
  assert f.dirname() == '/'
1470
1497
  assert f.exists()
1471
1498
 
@@ -1474,9 +1501,9 @@ class TestFileSpaces(unittest.TestCase):
1474
1501
  open(TEST_DIR / 'test.ipynb', 'r').read()
1475
1502
  assert f.download() == open(TEST_DIR / 'test.ipynb', 'rb').read()
1476
1503
 
1477
- assert space.is_file('obj_test.ipynb')
1504
+ assert space.is_file(obj_test_ipynb)
1478
1505
  f.remove()
1479
- assert not space.is_file('obj_test.ipynb')
1506
+ assert not space.is_file(obj_test_ipynb)
1480
1507
 
1481
1508
  # mtime / ctime
1482
1509
  assert f.getmtime() > 0
@@ -1485,17 +1512,17 @@ class TestFileSpaces(unittest.TestCase):
1485
1512
  # rename
1486
1513
  f = space.upload_file(
1487
1514
  TEST_DIR / 'test.ipynb',
1488
- 'obj_test.ipynb',
1515
+ obj_test_ipynb,
1489
1516
  )
1490
- assert space.exists('obj_test.ipynb')
1491
- assert not space.exists('obj_test_2.ipynb')
1492
- f.rename('obj_test_2.ipynb')
1493
- assert not space.exists('obj_test.ipynb')
1494
- assert space.exists('obj_test_2.ipynb')
1495
- assert f.abspath() == 'obj_test_2.ipynb'
1517
+ assert space.exists(obj_test_ipynb)
1518
+ assert not space.exists(obj_test_2_ipynb)
1519
+ f.rename(obj_test_2_ipynb)
1520
+ assert not space.exists(obj_test_ipynb)
1521
+ assert space.exists(obj_test_2_ipynb)
1522
+ assert f.abspath() == obj_test_2_ipynb
1496
1523
 
1497
1524
  # Cleanup
1498
- space.remove('obj_test_2.ipynb')
1525
+ space.remove(obj_test_2_ipynb)
1499
1526
 
1500
1527
 
1501
1528
  @pytest.mark.management
@@ -27,12 +27,12 @@ a description of one or more options.
27
27
  import contextlib
28
28
  import os
29
29
  import re
30
+ from collections.abc import Iterator
31
+ from collections.abc import Mapping
30
32
  from typing import Any
31
33
  from typing import Callable
32
34
  from typing import Dict
33
- from typing import Iterator
34
35
  from typing import List
35
- from typing import Mapping
36
36
  from typing import Optional
37
37
  from typing import Tuple
38
38
  from typing import Union
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env python3
2
+ from collections.abc import Sequence
2
3
  from typing import Any
3
4
  from typing import Dict
4
5
  from typing import Optional
5
- from typing import Sequence
6
6
  from typing import Union
7
7
 
8
8
  from ..mysql import converters
@@ -300,9 +300,9 @@ def results_to_polars(
300
300
  if has_polars:
301
301
  schema = _description_to_polars_schema(desc) if schema is None else schema
302
302
  if single:
303
- out = pl.DataFrame([res], **schema.get('schema', {}))
303
+ out = pl.DataFrame([res], orient='row', **schema.get('schema', {}))
304
304
  else:
305
- out = pl.DataFrame(res, **schema.get('schema', {}))
305
+ out = pl.DataFrame(res, orient='row', **schema.get('schema', {}))
306
306
  with_columns = schema.get('with_columns')
307
307
  if with_columns:
308
308
  return out.with_columns(**with_columns)
@@ -19,14 +19,14 @@
19
19
  """Dictionary that allows setting nested keys by period (.) delimited strings."""
20
20
  import copy
21
21
  import re
22
+ from collections.abc import ItemsView
23
+ from collections.abc import Iterable
24
+ from collections.abc import KeysView
25
+ from collections.abc import ValuesView
22
26
  from typing import Any
23
27
  from typing import Dict
24
- from typing import ItemsView
25
- from typing import Iterable
26
- from typing import KeysView
27
28
  from typing import List
28
29
  from typing import Tuple
29
- from typing import ValuesView
30
30
 
31
31
 
32
32
  def _is_compound_key(key: str) -> bool:
@@ -1,13 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: singlestoredb
3
- Version: 1.15.7
3
+ Version: 1.16.0
4
4
  Summary: Interface to the SingleStoreDB database and workspace management APIs
5
- Home-page: https://github.com/singlestore-labs/singlestoredb-python
6
- Author: SingleStore
7
- Author-email: support@singlestore.com
5
+ Author-email: SingleStore <support@singlestore.com>
8
6
  License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/singlestore-labs/singlestoredb-python
9
8
  Classifier: Development Status :: 5 - Production/Stable
10
- Classifier: License :: OSI Approved :: Apache Software License
11
9
  Classifier: Programming Language :: Python :: 3
12
10
  Classifier: Programming Language :: Python :: 3 :: Only
13
11
  Classifier: Topic :: Database
@@ -15,20 +13,26 @@ Requires-Python: >=3.9
15
13
  Description-Content-Type: text/markdown
16
14
  License-File: LICENSE
17
15
  Requires-Dist: PyJWT
18
- Requires-Dist: build
19
16
  Requires-Dist: parsimonious
20
17
  Requires-Dist: requests
21
- Requires-Dist: setuptools
22
18
  Requires-Dist: sqlparams
23
- Requires-Dist: wheel
24
19
  Requires-Dist: tomli>=1.1.0; python_version < "3.11"
25
20
  Requires-Dist: typing-extensions<=4.13.2; python_version < "3.11"
21
+ Provides-Extra: build
22
+ Requires-Dist: build; extra == "build"
23
+ Requires-Dist: setuptools>=61.0; extra == "build"
24
+ Requires-Dist: wheel; extra == "build"
26
25
  Provides-Extra: dataframe
27
26
  Requires-Dist: ibis-singlestoredb; extra == "dataframe"
28
27
  Provides-Extra: dbt
29
28
  Requires-Dist: dbt-singlestore; extra == "dbt"
29
+ Provides-Extra: dev
30
+ Requires-Dist: singlestoredb[build,docs,test]; extra == "dev"
30
31
  Provides-Extra: docker
31
32
  Requires-Dist: docker; extra == "docker"
33
+ Provides-Extra: docs
34
+ Requires-Dist: sphinx; extra == "docs"
35
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
32
36
  Provides-Extra: ed22519
33
37
  Requires-Dist: PyNaCl>=1.4.0; extra == "ed22519"
34
38
  Provides-Extra: gssapi
@@ -43,6 +47,21 @@ Provides-Extra: rsa
43
47
  Requires-Dist: cryptography; extra == "rsa"
44
48
  Provides-Extra: sqlalchemy
45
49
  Requires-Dist: sqlalchemy-singlestoredb>=1.0.0; extra == "sqlalchemy"
50
+ Provides-Extra: test
51
+ Requires-Dist: coverage; extra == "test"
52
+ Requires-Dist: dash; extra == "test"
53
+ Requires-Dist: fastapi; extra == "test"
54
+ Requires-Dist: ipython; extra == "test"
55
+ Requires-Dist: jupysql; extra == "test"
56
+ Requires-Dist: pandas; extra == "test"
57
+ Requires-Dist: parameterized; extra == "test"
58
+ Requires-Dist: polars; extra == "test"
59
+ Requires-Dist: pyarrow; extra == "test"
60
+ Requires-Dist: pydantic; extra == "test"
61
+ Requires-Dist: pytest; extra == "test"
62
+ Requires-Dist: pytest-cov; extra == "test"
63
+ Requires-Dist: singlestore-vectorstore>=0.1.2; extra == "test"
64
+ Requires-Dist: uvicorn; extra == "test"
46
65
  Provides-Extra: vectorstore
47
66
  Requires-Dist: singlestore-vectorstore>=0.1.2; extra == "vectorstore"
48
67
 
@@ -1,16 +1,16 @@
1
- singlestoredb/__init__.py,sha256=UYEMO4CiO41DrAiO6xne5VttZqH7BvSD2Q-skkFRNeU,2272
1
+ singlestoredb/__init__.py,sha256=0wFnKYBn3l4FkRMajzT-k31Naq9c5V3bn8hzG9sOcps,2272
2
2
  singlestoredb/auth.py,sha256=u8D9tpKzrqa4ssaHjyZnGDX1q8XBpGtuoOkTkSv7B28,7599
3
3
  singlestoredb/config.py,sha256=aBdMrPEaNSH-QLi1AXoQaSJsZ9f6ZXoFPN-74Trr6sQ,13935
4
- singlestoredb/connection.py,sha256=ELk3-UpM6RaB993aIt08MydKiiDnejHQ1s8EFiacrAI,46055
4
+ singlestoredb/connection.py,sha256=2AW28D1v6nxHaba_eoEl4C5hMeqXZBFyayZ2FSAtixo,46073
5
5
  singlestoredb/converters.py,sha256=0D54e-3E2iVzlMYPK0RbGilE9B-kcP380c1Mpze2Nz4,20704
6
6
  singlestoredb/exceptions.py,sha256=HuoA6sMRL5qiCiee-_5ddTGmFbYC9Euk8TYUsh5GvTw,3234
7
7
  singlestoredb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- singlestoredb/pytest.py,sha256=OyF3BO9mgxenifYhOihnzGk8WzCJ_zN5_mxe8XyFPOc,9074
8
+ singlestoredb/pytest.py,sha256=uhquZ1pDxhZIsh6nWQvr2XSKQsIx1HqFPK9ILGPna3w,11740
9
9
  singlestoredb/types.py,sha256=Qp_PWYjSYG6PRnmXAZZ7K2QehUqfoG4KSllI3O1stPE,10397
10
10
  singlestoredb/vectorstore.py,sha256=BZb8e7m02_XVHqOyu8tA94R6kHb3n-BC8F08JyJwDzY,8408
11
- singlestoredb/ai/__init__.py,sha256=zNSUVDzDsyIfW7-obV5iRRhDjPqgPzG_EJJaT0_jtLw,284
12
- singlestoredb/ai/chat.py,sha256=uI_j1sySFO4jpUA3bcUL0Gki0F3jEbLHAq0hQLxqv3w,5074
13
- singlestoredb/ai/embeddings.py,sha256=yj9fQzwiocy78cBPfoOB9dok4gnWyE0IghCw7JIlTGI,4020
11
+ singlestoredb/ai/__init__.py,sha256=qZkdcvFwL8GdTwc5f5t91gMDIIihYoaLfMpM1KAERtU,122
12
+ singlestoredb/ai/chat.py,sha256=G462g4jdma1Faqm2undNGUaY2d86xUWUVj4A3KKThck,4681
13
+ singlestoredb/ai/embeddings.py,sha256=Bpoe7kndogPePwkWdxICPXAoP2o6Fyyh1OYZiIqcSUM,4356
14
14
  singlestoredb/alchemy/__init__.py,sha256=dXRThusYrs_9GjrhPOw0-vw94in_T8yY9jE7SGCqiQk,2523
15
15
  singlestoredb/apps/__init__.py,sha256=dfN97AZz7Np6JML3i9GJrv22ZbNCUletXmsJpQnKhKg,170
16
16
  singlestoredb/apps/_cloud_functions.py,sha256=NJJu0uJsK9TjY3yZjgftpFPR-ga-FrOyaiDD4jWFCtE,2704
@@ -43,26 +43,26 @@ singlestoredb/docstring/tests/test_util.py,sha256=ES-bb99Qt1AXtCTDO8CdcWInJJLhH1
43
43
  singlestoredb/functions/__init__.py,sha256=I2GnxOhLb4_7xhgOxdIwmwD5NiK7QYPYaE3PUIX-7xk,471
44
44
  singlestoredb/functions/decorator.py,sha256=GrbTMIhXRPlVeVlENrgCjt9aZjZQC7Z4tLOvODAre5Y,6396
45
45
  singlestoredb/functions/dtypes.py,sha256=DgJaNXouJ2t-qIqDiQlUYU9IhkXXUTigWeE_MAcmvHM,39814
46
- singlestoredb/functions/signature.py,sha256=h2vFVNP07d5a3gi7zMiM_sztDUNK_HlJWR-Rl3nMxPA,45545
47
- singlestoredb/functions/utils.py,sha256=1L0Phgzq0XdWK3ecfOOydq4zV955yCwpDoAaCYRGldk,10769
46
+ singlestoredb/functions/signature.py,sha256=zctCuwthMN7sFW8x0SXHulXE-CuQCcbUsIrvF3UZOYc,45554
47
+ singlestoredb/functions/utils.py,sha256=akVBRz8g42N-_7hrTA7FmzGXnjsr5CS2usUfuBEOFz0,10778
48
48
  singlestoredb/functions/ext/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
49
49
  singlestoredb/functions/ext/arrow.py,sha256=WB7n1ACslyd8nlbFzUvlbxn1BVuEjA9-BGBEqCWlSOo,9061
50
- singlestoredb/functions/ext/asgi.py,sha256=C6UJXunHY-dISp4k4CH-poEt8dyzqIjLQpuj06_ttRA,72268
50
+ singlestoredb/functions/ext/asgi.py,sha256=i7OsuKd8AtmUfoF9I4N1PqU9nNHdmOUehpIZPQw5ey0,72295
51
51
  singlestoredb/functions/ext/json.py,sha256=RIuZdDybEdHuC-f2p6BdjhFjM3iGb3a1PRQ4k11P6N8,10102
52
52
  singlestoredb/functions/ext/mmap.py,sha256=RzyNSLRpI5ZJ8YN6k-AvZlRTLjj80j52byHLtW8c3ps,13710
53
- singlestoredb/functions/ext/rowdat_1.py,sha256=SlXbJ2042jEoaXw81y5llw1625w0aU2nZ8vI_O3qA-M,21112
53
+ singlestoredb/functions/ext/rowdat_1.py,sha256=zDgN5utI1zs4pkjjKnqMYP0D3SYqsb2qg2P0QcGhLX0,21121
54
54
  singlestoredb/functions/ext/timer.py,sha256=-PR__KbhwAMW4PXJ4fGri2FfrU0jRyz6e6yvmySmjaw,2706
55
55
  singlestoredb/functions/ext/utils.py,sha256=oU2NVmkjcS0QHLfdB8SBiRylVq-r0VzTy8nxGvAgjow,6938
56
- singlestoredb/functions/typing/__init__.py,sha256=gT_Sz5YH-L-9WeIHwWYMEx-hUCZqis7ec5Ipk3JXpnM,1339
56
+ singlestoredb/functions/typing/__init__.py,sha256=7_6Cuf8o07SBP2ExQwqqAIfonW3U9SgezrzB8GpxipI,1348
57
57
  singlestoredb/functions/typing/numpy.py,sha256=WO64_HziveGk0dqRrkuZ51aohULy9qYuqaKHAoiiA3A,661
58
58
  singlestoredb/functions/typing/pandas.py,sha256=wZUTMbte937EKtGdnFFWB0fFut5unTOyAbn8fSBsfro,83
59
59
  singlestoredb/functions/typing/polars.py,sha256=b_UOIXLkvptHiAB7sXSzC7XPHMWNOglCz6h9amCA6Kg,83
60
60
  singlestoredb/functions/typing/pyarrow.py,sha256=WkqQrUPS__jYzUJntLLUVDgYIcnqR9HU6Q5grZojZrc,80
61
61
  singlestoredb/fusion/__init__.py,sha256=Qo7SuqGw-l-vE8-EI2jhm6hXJkYfOLUKIws9c7LFNX0,356
62
62
  singlestoredb/fusion/graphql.py,sha256=ZA3HcDq5rER-dCEavwTqnF7KM0D2LCYIY7nLQk7lSso,5207
63
- singlestoredb/fusion/handler.py,sha256=M5iyNP4zOaGqUqnZg_b5xhRE-8tHgfZSHDH0zKTiJmE,27692
63
+ singlestoredb/fusion/handler.py,sha256=HW2M7Ty1QFzJBeKn9vsg3AQdJuTY5ToSKhUwGJd9O7I,27701
64
64
  singlestoredb/fusion/registry.py,sha256=jjdRTYZ3ylhy6gAoW5xBj0tkxGFBT-2yLQ0tztTgDIY,6112
65
- singlestoredb/fusion/result.py,sha256=p5I65C-Dhhl1yeZwetXXZabwritr8Ph2mFvJJ3ovcBM,11790
65
+ singlestoredb/fusion/result.py,sha256=hyrbBunlhP5trLs6nj6azvlHD29NVztStIqCWHRhiSM,11799
66
66
  singlestoredb/fusion/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
67
  singlestoredb/fusion/handlers/export.py,sha256=Af4eIMPGKEOpmf4LXnvQsgmnvx5F8B5FkRI20RvEa7o,15309
68
68
  singlestoredb/fusion/handlers/files.py,sha256=6SMVn80DrnBWabbd1vevicR827P0Y6-B1bTfqr4VuL4,18971
@@ -72,7 +72,7 @@ singlestoredb/fusion/handlers/stage.py,sha256=edViRGlBF7xZUd3ClmpBlMcBc6O4JKdvA9
72
72
  singlestoredb/fusion/handlers/utils.py,sha256=ozHOWUraoN8XGTK9JZdhv5HV8AQR8zfUd1yh1kLvUXY,10685
73
73
  singlestoredb/fusion/handlers/workspace.py,sha256=8NZwKg9YE9LL1c1JCnO-p4RuNBQ0V4gicQeFXKEZxGA,29672
74
74
  singlestoredb/http/__init__.py,sha256=A_2ZUCCpvRYIA6YDpPy57wL5R1eZ5SfP6I1To5nfJ2s,912
75
- singlestoredb/http/connection.py,sha256=fCgh3PdYJNYVPMaUe_NlMb7ducxC06se1XfyqOpPpJw,39727
75
+ singlestoredb/http/connection.py,sha256=WrdDA_2dhGvlf5BAXkvWXjjiNeEvYbGNqM12Z2uQu8g,39745
76
76
  singlestoredb/magics/__init__.py,sha256=lZjkT3Webo9c1EQAzlRCRh6B2pckQH8uvNrrB__abcI,1210
77
77
  singlestoredb/magics/run_personal.py,sha256=Y5lVpJ8vqOyEjtZkip04Hwi4uZ7CQLU5Rd1MrCmpNvs,5222
78
78
  singlestoredb/magics/run_shared.py,sha256=czoO4z6gtoq9ek_41efRBRk-XQiHKuHdY0BOdfKkFrc,5130
@@ -83,15 +83,15 @@ singlestoredb/management/export.py,sha256=yR-yZUE9USFrP5OR_5iLFqEc8GLiKDQypSEp08
83
83
  singlestoredb/management/files.py,sha256=89IhpGw9WdwxVeksavHEDMVn9wb_jxb-utZuIDqkLHw,30477
84
84
  singlestoredb/management/inference_api.py,sha256=px4JrziJQMwG73iNnYw_95np9DDrp5zo5mKz8c5EF9o,2742
85
85
  singlestoredb/management/job.py,sha256=4-xLWzbE8odQogVVaFer80UEoTAZY1T28VZ9Ug4rbmM,24611
86
- singlestoredb/management/manager.py,sha256=k8JKPXyJVHI7gFwqIhxhUtQr_KdBCXw7Dw4GCdCtdKg,9352
86
+ singlestoredb/management/manager.py,sha256=g7Zj5mLatS3kefNngi5XmrXz1dyZwWnjHgy1JB-Qh-Y,11464
87
87
  singlestoredb/management/organization.py,sha256=_JvW0Znu5emR5uYGVEcZvakQqftNb_vRhzmkOoPRPfc,5869
88
88
  singlestoredb/management/region.py,sha256=ji3u6ZjQoNJ9i_X4C9ojciYPq4Oz7gKkeeUDndO8dZU,4114
89
- singlestoredb/management/utils.py,sha256=QIhZCZSRaDbAG35xu1_n7ihmRXON8swc-gEK2FGYutI,13203
90
- singlestoredb/management/workspace.py,sha256=frVNqwfUgth1WWhcf5rZqz9nIcPkMtmCb5hKVx1h5SI,61953
89
+ singlestoredb/management/utils.py,sha256=tJVr6pRGC5RS7jxmlNoYmtB5UKOTGdwTFqygVmCkxEY,13212
90
+ singlestoredb/management/workspace.py,sha256=Cx3Qv3FOOagzIIlblXYN2BbSP-nj6crN7h5OAhOpB_8,62179
91
91
  singlestoredb/mysql/__init__.py,sha256=olUTAvkiERhDW41JXQMawkg-i0tvBEkoTkII1tt6lxU,4492
92
92
  singlestoredb/mysql/_auth.py,sha256=AugRitoUwgRIDFuJxuAH4MWIAmckY7Ji2pP6r_Ng9dY,8043
93
93
  singlestoredb/mysql/charset.py,sha256=-FlONDS_oAUF5B3mIgeHBPb_SCt4zHD33arUeBNctU0,10510
94
- singlestoredb/mysql/connection.py,sha256=6WlhOF-oEoF7V4bl4MTxU6qr6H15-KZvPC-snPmbXVg,73355
94
+ singlestoredb/mysql/connection.py,sha256=1qzXu5fu1f_rr45-CV4hcP9oUcRAyWw-Ch8zS7axcVM,73364
95
95
  singlestoredb/mysql/converters.py,sha256=CVe8SDmjbIAhy1xpQ2N5OKWw6t5eWpw-EU3QTlA0Hh0,7500
96
96
  singlestoredb/mysql/cursors.py,sha256=aOLfHkj83aYZPOVuhJPkZ83CWByszIBRynq0fqSaWvY,27046
97
97
  singlestoredb/mysql/err.py,sha256=-m5rqXi8yhq6b8SCEJ2h0E5Rudh_15dlAU_WbJ1YrM8,2388
@@ -153,7 +153,7 @@ singlestoredb/tests/test_ext_func.py,sha256=_YREceW1Llwx9Wcamj0up2IXLuBTnuvQqCFO
153
153
  singlestoredb/tests/test_ext_func_data.py,sha256=kyNklkX1RxSiahI0LZjpqhg3LGDs0iwv8iHuXW3AcSo,47515
154
154
  singlestoredb/tests/test_fusion.py,sha256=7YQ_nOQoV_7yD4OEpJz2Ov-zok-cBFK9IOJ3FgZ0xo0,50593
155
155
  singlestoredb/tests/test_http.py,sha256=RXasTqBWRn__omj0eLFTJYIbZjd0PPdIV2d4Cqz0MC8,8580
156
- singlestoredb/tests/test_management.py,sha256=BK58UWs6L_9h1ePUNjnIJ1fVAEvBvC8rP7HtnfoANYQ,52205
156
+ singlestoredb/tests/test_management.py,sha256=wjJIr9KG_jNGnW6b4EPWf_jrdG9XDcEMex3pjRH8I10,53513
157
157
  singlestoredb/tests/test_plugin.py,sha256=qpO9wmWc62VaijN1sJ97YSYIX7I7Y5C6sY-WzwrutDQ,812
158
158
  singlestoredb/tests/test_results.py,sha256=wg93sujwt-R9_eJCgSCElgAZhLDkIiAo3qPkPydOv78,6582
159
159
  singlestoredb/tests/test_types.py,sha256=jqoAaSjhbgwB3vt0KsTcl7XBWoMMIa0mPFKhEi5bBjo,4500
@@ -164,19 +164,19 @@ singlestoredb/tests/test_xdict.py,sha256=fqHspoi39nbX3fIDVkkRXcd5H50xdOsSvK0bxAM
164
164
  singlestoredb/tests/utils.py,sha256=2A2tEdD3t8aXWUnHtAIcFlWrflsz2MlMcCbUDaAG29c,4995
165
165
  singlestoredb/tests/ext_funcs/__init__.py,sha256=Gg15gcphVnW5ZXgFT8P8hYSOmgvmVIaUA8jxTC_UgRc,15581
166
166
  singlestoredb/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
167
- singlestoredb/utils/config.py,sha256=m3Xn6hsbdKyLufSnbokhFJ9Vfaz9Qpkj1IEnIiH9oJQ,24503
167
+ singlestoredb/utils/config.py,sha256=m6Rpv1Qm1gtPG60tjT8k0r5hKdSh-5b7gIDo6FHj-8Y,24521
168
168
  singlestoredb/utils/convert_rows.py,sha256=A6up7a8Bq-eV2BXdGCotQviqp1Q7XdJ2MA9339hLYVQ,1816
169
169
  singlestoredb/utils/debug.py,sha256=0JiLA37u_9CKiDGiN9BK_PtFMUku3vIcNjERWaTNRSU,349
170
170
  singlestoredb/utils/dtypes.py,sha256=1qUiB4BJFJ7rOVh2mItQssYbJupV7uq1x8uwX-Eu2Ks,5898
171
171
  singlestoredb/utils/events.py,sha256=kSrdOf1PgGKaUf8Cauj3KhPEzA9dWlKIytpUMRGZ_cU,1454
172
- singlestoredb/utils/mogrify.py,sha256=-a56IF70U6CkfadeaZgfjRSVsAD3PuqRrzPpjZlgbwY,4050
173
- singlestoredb/utils/results.py,sha256=bJtaUaDiFq26IsPAKZ2FHGB7csMn94EAxLKrP4HaEEA,15277
174
- singlestoredb/utils/xdict.py,sha256=S9HKgrPrnu_6b7iOwa2KrW8CmU1Uqx0BWdEyogFzWbE,12896
172
+ singlestoredb/utils/mogrify.py,sha256=k2nN6w8OnkaXRtNmA9OiXslBpYeqM8mBEy9rLSLDiB0,4059
173
+ singlestoredb/utils/results.py,sha256=WXc3wsxxeL3-rVo7P7ga67i1J7fXQDM0a8JRf_Cm03s,15305
174
+ singlestoredb/utils/xdict.py,sha256=j1N2cn15DFwPcpm8X83NJmtMFUopIFEeMnwmIiNtjSY,12932
175
175
  sqlx/__init__.py,sha256=aBYiU8DZXCogvWu3yWafOz7bZS5WWwLZXj7oL0dXGyU,85
176
176
  sqlx/magic.py,sha256=JsS9_9aBFaOt91Torm1JPN0c8qB2QmYJmNSKtbSQIY0,3509
177
- singlestoredb-1.15.7.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
178
- singlestoredb-1.15.7.dist-info/METADATA,sha256=mY9ccVuo2MtWa69X1B44KEAXubGcZpcQ5I8CO942860,5786
179
- singlestoredb-1.15.7.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
180
- singlestoredb-1.15.7.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
181
- singlestoredb-1.15.7.dist-info/top_level.txt,sha256=DfFGz7bM4XrshloiCeTABgylT3BUnS8T5pJam3ewT6Q,19
182
- singlestoredb-1.15.7.dist-info/RECORD,,
177
+ singlestoredb-1.16.0.dist-info/LICENSE,sha256=Mlq78idURT-9G026aMYswwwnnrLcgzTLuXeAs5hjDLM,11341
178
+ singlestoredb-1.16.0.dist-info/METADATA,sha256=DOhsP2MgzRJ2o83yA4zX_fdGQsA3CRRB0mzXc90ptls,6611
179
+ singlestoredb-1.16.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
180
+ singlestoredb-1.16.0.dist-info/entry_points.txt,sha256=bSLaTWB5zGjpVYPAaI46MkkDup0su-eb3uAhCNYuRV0,48
181
+ singlestoredb-1.16.0.dist-info/top_level.txt,sha256=4NWrNYfmbIQ129HTH5DBS_VQQVY9rMrCMnN7zIzoSVY,24
182
+ singlestoredb-1.16.0.dist-info/RECORD,,