singlestoredb 0.8.9__cp36-abi3-win32.whl → 0.9.0__cp36-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.

Files changed (37) hide show
  1. _singlestoredb_accel.pyd +0 -0
  2. singlestoredb/__init__.py +1 -1
  3. singlestoredb/exceptions.py +24 -0
  4. singlestoredb/functions/__init__.py +1 -0
  5. singlestoredb/functions/decorator.py +165 -0
  6. singlestoredb/functions/dtypes.py +1396 -0
  7. singlestoredb/functions/ext/__init__.py +2 -0
  8. singlestoredb/functions/ext/asgi.py +357 -0
  9. singlestoredb/functions/ext/json.py +49 -0
  10. singlestoredb/functions/ext/rowdat_1.py +111 -0
  11. singlestoredb/functions/signature.py +607 -0
  12. singlestoredb/management/billing_usage.py +148 -0
  13. singlestoredb/management/manager.py +42 -1
  14. singlestoredb/management/organization.py +85 -0
  15. singlestoredb/management/utils.py +118 -1
  16. singlestoredb/management/workspace.py +881 -5
  17. singlestoredb/mysql/__init__.py +12 -10
  18. singlestoredb/mysql/charset.py +12 -11
  19. singlestoredb/mysql/constants/CLIENT.py +0 -1
  20. singlestoredb/mysql/constants/COMMAND.py +0 -1
  21. singlestoredb/mysql/constants/CR.py +0 -2
  22. singlestoredb/mysql/constants/ER.py +0 -1
  23. singlestoredb/mysql/constants/FIELD_TYPE.py +0 -1
  24. singlestoredb/mysql/constants/FLAG.py +0 -1
  25. singlestoredb/mysql/constants/SERVER_STATUS.py +0 -1
  26. singlestoredb/mysql/converters.py +49 -28
  27. singlestoredb/mysql/err.py +3 -3
  28. singlestoredb/mysql/optionfile.py +4 -4
  29. singlestoredb/mysql/times.py +3 -4
  30. singlestoredb/tests/test2.sql +1 -0
  31. singlestoredb/tests/test_management.py +393 -3
  32. singlestoredb/tests/test_udf.py +698 -0
  33. {singlestoredb-0.8.9.dist-info → singlestoredb-0.9.0.dist-info}/METADATA +1 -1
  34. {singlestoredb-0.8.9.dist-info → singlestoredb-0.9.0.dist-info}/RECORD +37 -25
  35. {singlestoredb-0.8.9.dist-info → singlestoredb-0.9.0.dist-info}/LICENSE +0 -0
  36. {singlestoredb-0.8.9.dist-info → singlestoredb-0.9.0.dist-info}/WHEEL +0 -0
  37. {singlestoredb-0.8.9.dist-info → singlestoredb-0.9.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python
2
+ """SingleStoreDB Cloud Billing Usage."""
3
+ import datetime
4
+ from typing import Any
5
+ from typing import Dict
6
+ from typing import List
7
+ from typing import Optional
8
+
9
+ from .manager import Manager
10
+ from .utils import camel_to_snake
11
+ from .utils import vars_to_str
12
+
13
+
14
+ class UsageItem(object):
15
+ """Usage statistics."""
16
+
17
+ def __init__(
18
+ self,
19
+ start_time: datetime.datetime,
20
+ end_time: datetime.datetime,
21
+ owner_id: str,
22
+ resource_id: str,
23
+ resource_name: str,
24
+ resource_type: str,
25
+ value: str,
26
+ ):
27
+ #: Starting time for the usage duration
28
+ self.start_time = start_time
29
+
30
+ #: Ending time for the usage duration
31
+ self.end_time = end_time
32
+
33
+ #: Owner ID
34
+ self.owner_id = owner_id
35
+
36
+ #: Resource ID
37
+ self.resource_id = resource_id
38
+
39
+ #: Resource name
40
+ self.resource_name = resource_name
41
+
42
+ #: Resource type
43
+ self.resource_type = resource_type
44
+
45
+ #: Usage statistic value
46
+ self.value = value
47
+
48
+ self._manager: Optional[Manager] = None
49
+
50
+ def __str__(self) -> str:
51
+ """Return string representation."""
52
+ return vars_to_str(self)
53
+
54
+ def __repr__(self) -> str:
55
+ """Return string representation."""
56
+ return str(self)
57
+
58
+ @classmethod
59
+ def from_dict(
60
+ cls,
61
+ obj: Dict[str, Any],
62
+ manager: Manager,
63
+ ) -> 'UsageItem':
64
+ """
65
+ Convert dictionary to a ``UsageItem`` object.
66
+
67
+ Parameters
68
+ ----------
69
+ obj : dict
70
+ Key-value pairs to retrieve billling usage information from
71
+ manager : WorkspaceManager, optional
72
+ The WorkspaceManager the UsageItem belongs to
73
+
74
+ Returns
75
+ -------
76
+ :class:`UsageItem`
77
+
78
+ """
79
+ out = cls(
80
+ end_time=datetime.datetime.fromisoformat(obj['endTime']),
81
+ start_time=datetime.datetime.fromisoformat(obj['startTime']),
82
+ owner_id=obj['ownerId'],
83
+ resource_id=obj['resourceId'],
84
+ resource_name=obj['resourceName'],
85
+ resource_type=obj['resource_type'],
86
+ value=obj['value'],
87
+ )
88
+ out._manager = manager
89
+ return out
90
+
91
+
92
+ class BillingUsageItem(object):
93
+ """Billing usage item."""
94
+
95
+ def __init__(
96
+ self,
97
+ description: str,
98
+ metric: str,
99
+ usage: List[UsageItem],
100
+ ):
101
+ """Use :attr:`WorkspaceManager.billing.usage` instead."""
102
+ #: Description of the usage metric
103
+ self.description = description
104
+
105
+ #: Name of the usage metric
106
+ self.metric = metric
107
+
108
+ #: Usage statistics
109
+ self.usage = list(usage)
110
+
111
+ self._manager: Optional[Manager] = None
112
+
113
+ def __str__(self) -> str:
114
+ """Return string representation."""
115
+ return vars_to_str(self)
116
+
117
+ def __repr__(self) -> str:
118
+ """Return string representation."""
119
+ return str(self)
120
+
121
+ @ classmethod
122
+ def from_dict(
123
+ cls,
124
+ obj: Dict[str, Any],
125
+ manager: Manager,
126
+ ) -> 'BillingUsageItem':
127
+ """
128
+ Convert dictionary to a ``BillingUsageItem`` object.
129
+
130
+ Parameters
131
+ ----------
132
+ obj : dict
133
+ Key-value pairs to retrieve billling usage information from
134
+ manager : WorkspaceManager, optional
135
+ The WorkspaceManager the BillingUsageItem belongs to
136
+
137
+ Returns
138
+ -------
139
+ :class:`BillingUsageItem`
140
+
141
+ """
142
+ out = cls(
143
+ description=obj['description'],
144
+ metric=str(camel_to_snake(obj['metric'])),
145
+ usage=[UsageItem.from_dict(x, manager) for x in obj['Usage']],
146
+ )
147
+ out._manager = manager
148
+ return out
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  """SingleStoreDB Base Manager."""
3
+ import os
4
+ import sys
3
5
  import time
4
6
  from typing import Any
5
7
  from typing import Dict
@@ -48,6 +50,7 @@ class Manager(object):
48
50
  base_url or type(self).default_base_url,
49
51
  version or type(self).default_version,
50
52
  ) + '/'
53
+ self._params: Dict[str, str] = {}
51
54
 
52
55
  def _check(
53
56
  self, res: requests.Response, url: str, params: Dict[str, Any],
@@ -65,6 +68,8 @@ class Manager(object):
65
68
  requests.Response
66
69
 
67
70
  """
71
+ if config.get_option('debug.queries'):
72
+ print(os.path.join(self._base_url, url), params, file=sys.stderr)
68
73
  if res.status_code >= 400:
69
74
  txt = res.text.strip()
70
75
  msg = f'{txt}: /{url}'
@@ -75,7 +80,7 @@ class Manager(object):
75
80
  if 'password' in k.lower() and v:
76
81
  new_params['json'][k] = '*' * len(v)
77
82
  msg += ': {}'.format(str(new_params))
78
- raise ManagementError(errno=res.status_code, msg=msg)
83
+ raise ManagementError(errno=res.status_code, msg=msg, response=txt)
79
84
  return res
80
85
 
81
86
  def _get(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
@@ -96,6 +101,8 @@ class Manager(object):
96
101
  requests.Response
97
102
 
98
103
  """
104
+ if self._params:
105
+ kwargs['params'] = self._params
99
106
  return self._check(
100
107
  self._sess.get(
101
108
  urljoin(self._base_url, path),
@@ -122,6 +129,8 @@ class Manager(object):
122
129
  requests.Response
123
130
 
124
131
  """
132
+ if self._params:
133
+ kwargs['params'] = self._params
125
134
  return self._check(
126
135
  self._sess.post(
127
136
  urljoin(self._base_url, path),
@@ -130,6 +139,34 @@ class Manager(object):
130
139
  path, kwargs,
131
140
  )
132
141
 
142
+ def _put(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
143
+ """
144
+ Invoke a PUT request.
145
+
146
+ Parameters
147
+ ----------
148
+ path : str
149
+ Path of the resource
150
+ *args : positional arguments, optional
151
+ Arguments to add to the POST request
152
+ **kwargs : keyword arguments, optional
153
+ Keyword arguments to add to the POST request
154
+
155
+ Returns
156
+ -------
157
+ requests.Response
158
+
159
+ """
160
+ if self._params:
161
+ kwargs['params'] = self._params
162
+ return self._check(
163
+ self._sess.put(
164
+ urljoin(self._base_url, path),
165
+ *args, **kwargs,
166
+ ),
167
+ path, kwargs,
168
+ )
169
+
133
170
  def _delete(self, path: str, *args: Any, **kwargs: Any) -> requests.Response:
134
171
  """
135
172
  Invoke a DELETE request.
@@ -148,6 +185,8 @@ class Manager(object):
148
185
  requests.Response
149
186
 
150
187
  """
188
+ if self._params:
189
+ kwargs['params'] = self._params
151
190
  return self._check(
152
191
  self._sess.delete(
153
192
  urljoin(self._base_url, path),
@@ -174,6 +213,8 @@ class Manager(object):
174
213
  requests.Response
175
214
 
176
215
  """
216
+ if self._params:
217
+ kwargs['params'] = self._params
177
218
  return self._check(
178
219
  self._sess.patch(
179
220
  urljoin(self._base_url, path),
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python
2
+ """SingleStoreDB Cloud Organization."""
3
+ from typing import Dict
4
+ from typing import List
5
+ from typing import Optional
6
+ from typing import Union
7
+
8
+ from .manager import Manager
9
+ from .utils import vars_to_str
10
+
11
+
12
+ def listify(x: Union[str, List[str]]) -> List[str]:
13
+ if isinstance(x, list):
14
+ return x
15
+ return [x]
16
+
17
+
18
+ def stringify(x: Union[str, List[str]]) -> str:
19
+ if isinstance(x, list):
20
+ return x[0]
21
+ return x
22
+
23
+
24
+ class Organization(object):
25
+ """
26
+ Organization in SingleStoreDB Cloud portal.
27
+
28
+ This object is not directly instantiated. It is used in results
29
+ of ``WorkspaceManager`` API calls.
30
+
31
+ See Also
32
+ --------
33
+ :attr:`WorkspaceManager.organization`
34
+
35
+ """
36
+
37
+ def __init__(self, id: str, name: str, firewall_ranges: List[str]):
38
+ """Use :attr:`WorkspaceManager.organization` instead."""
39
+ #: Unique ID of the organization
40
+ self.id = id
41
+
42
+ #: Name of the organization
43
+ self.name = name
44
+
45
+ #: Firewall ranges of the organization
46
+ self.firewall_ranges = list(firewall_ranges)
47
+
48
+ self._manager: Optional[Manager] = None
49
+
50
+ def __str__(self) -> str:
51
+ """Return string representation."""
52
+ return vars_to_str(self)
53
+
54
+ def __repr__(self) -> str:
55
+ """Return string representation."""
56
+ return str(self)
57
+
58
+ @classmethod
59
+ def from_dict(
60
+ cls,
61
+ obj: Dict[str, Union[str, List[str]]],
62
+ manager: Manager,
63
+ ) -> 'Organization':
64
+ """
65
+ Convert dictionary to an ``Organization`` object.
66
+
67
+ Parameters
68
+ ----------
69
+ obj : dict
70
+ Key-value pairs to retrieve organization information from
71
+ manager : WorkspaceManager, optional
72
+ The WorkspaceManager the Organization belongs to
73
+
74
+ Returns
75
+ -------
76
+ :class:`Organization`
77
+
78
+ """
79
+ out = cls(
80
+ id=stringify(obj['orgID']),
81
+ name=stringify(obj.get('name', '<unknown>')),
82
+ firewall_ranges=listify(obj.get('firewallRanges', [])),
83
+ )
84
+ out._manager = manager
85
+ return out
@@ -1,12 +1,40 @@
1
1
  #!/usr/bin/env python
2
2
  """SingleStoreDB Cluster Management."""
3
3
  import datetime
4
+ import os
5
+ import re
6
+ import sys
4
7
  from typing import Any
8
+ from typing import Dict
9
+ from typing import List
5
10
  from typing import Optional
6
11
  from typing import Union
7
12
 
8
13
  from .. import converters
9
14
 
15
+ JSON = Union[str, List[str], Dict[str, 'JSON']]
16
+ JSONObj = Dict[str, JSON]
17
+ JSONList = List[JSON]
18
+
19
+ if sys.version_info < (3, 10):
20
+ PathLike = Union[str, os.PathLike]
21
+ PathLikeABC = os.PathLike
22
+ else:
23
+ PathLike = Union[str, os.PathLike[str]]
24
+ PathLikeABC = os.PathLike[str]
25
+
26
+
27
+ def enable_http_tracing() -> None:
28
+ """Enable tracing of HTTP requests."""
29
+ import logging
30
+ import http.client as http_client
31
+ http_client.HTTPConnection.debuglevel = 1
32
+ logging.basicConfig()
33
+ logging.getLogger().setLevel(logging.DEBUG)
34
+ requests_log = logging.getLogger('requests.packages.urllib3')
35
+ requests_log.setLevel(logging.DEBUG)
36
+ requests_log.propagate = True
37
+
10
38
 
11
39
  def to_datetime(
12
40
  obj: Optional[Union[str, datetime.datetime]],
@@ -16,6 +44,8 @@ def to_datetime(
16
44
  return None
17
45
  if isinstance(obj, datetime.datetime):
18
46
  return obj
47
+ if obj == '0001-01-01T00:00:00Z':
48
+ return None
19
49
  obj = obj.replace('Z', '')
20
50
  # Fix datetimes with truncated zeros
21
51
  if '.' in obj:
@@ -30,11 +60,98 @@ def to_datetime(
30
60
  return out
31
61
 
32
62
 
63
+ def from_datetime(
64
+ obj: Union[str, datetime.datetime],
65
+ ) -> Optional[str]:
66
+ """Convert datetime to string."""
67
+ if not obj:
68
+ return None
69
+ if isinstance(obj, str):
70
+ return obj
71
+ out = obj.isoformat()
72
+ if not re.search(r'[A-Za-z]$', out):
73
+ out = f'{out}Z'
74
+ return out
75
+
76
+
33
77
  def vars_to_str(obj: Any) -> str:
34
78
  """Render a string representation of vars(obj)."""
35
79
  attrs = []
36
- for name, value in sorted(vars(obj).items()):
80
+ obj_vars = vars(obj)
81
+ if 'name' in obj_vars:
82
+ attrs.append('name={}'.format(repr(obj_vars['name'])))
83
+ if 'id' in obj_vars:
84
+ attrs.append('id={}'.format(repr(obj_vars['id'])))
85
+ for name, value in sorted(obj_vars.items()):
86
+ if name in ('name', 'id'):
87
+ continue
37
88
  if not value or name.startswith('_'):
38
89
  continue
39
90
  attrs.append('{}={}'.format(name, repr(value)))
40
91
  return '{}({})'.format(type(obj).__name__, ', '.join(attrs))
92
+
93
+
94
+ def single_item(s: Any) -> Any:
95
+ """Return only item if ``s`` is a list, otherwise return ``s``."""
96
+ if isinstance(s, list):
97
+ if len(s) != 1:
98
+ raise ValueError('list must only contain a singleitem')
99
+ return s[0]
100
+ return s
101
+
102
+
103
+ def stringify(s: JSON) -> str:
104
+ """Convert list of strings to single string."""
105
+ if isinstance(s, (tuple, list)):
106
+ if len(s) > 1:
107
+ raise ValueError('list contains more than one item')
108
+ return s[0]
109
+ if isinstance(s, dict):
110
+ raise TypeError('only strings and lists are valid arguments')
111
+ return s
112
+
113
+
114
+ def listify(s: JSON) -> List[str]:
115
+ """Convert string to list of strings."""
116
+ if isinstance(s, (tuple, list)):
117
+ return list(s)
118
+ if isinstance(s, dict):
119
+ raise TypeError('only strings and lists are valid arguments')
120
+ return [s]
121
+
122
+
123
+ def listify_obj(s: JSON) -> List[JSONObj]:
124
+ """Convert object to list of objects."""
125
+ if isinstance(s, (tuple, list)):
126
+ for item in s:
127
+ if not isinstance(item, dict):
128
+ raise TypeError('only dicts and lists of dicts are valid parameters')
129
+ return list(s) # type: ignore
130
+ if not isinstance(s, dict):
131
+ raise TypeError('only dicts and lists of dicts are valid parameters')
132
+ return [s]
133
+
134
+
135
+ def _upper_match(m: Any) -> str:
136
+ """Upper-case the first match group."""
137
+ return m.group(1).upper()
138
+
139
+
140
+ def snake_to_camel(s: Optional[str], cap_first: bool = False) -> Optional[str]:
141
+ """Convert snake-case to camel-case."""
142
+ if s is None:
143
+ return None
144
+ out = re.sub(r'_[A-Za-z]', _upper_match, s.lower())
145
+ if cap_first and out:
146
+ return out[0].upper() + out[1:]
147
+ return out
148
+
149
+
150
+ def camel_to_snake(s: Optional[str]) -> Optional[str]:
151
+ """Convert camel-case to snake-case."""
152
+ if s is None:
153
+ return None
154
+ out = re.sub(r'([A-Z]+)', r'_\1', s).lower()
155
+ if out and out[0] == '_':
156
+ return out[1:]
157
+ return out