singlestoredb 1.16.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. singlestoredb/__init__.py +75 -0
  2. singlestoredb/ai/__init__.py +2 -0
  3. singlestoredb/ai/chat.py +139 -0
  4. singlestoredb/ai/embeddings.py +128 -0
  5. singlestoredb/alchemy/__init__.py +90 -0
  6. singlestoredb/apps/__init__.py +3 -0
  7. singlestoredb/apps/_cloud_functions.py +90 -0
  8. singlestoredb/apps/_config.py +72 -0
  9. singlestoredb/apps/_connection_info.py +18 -0
  10. singlestoredb/apps/_dashboards.py +47 -0
  11. singlestoredb/apps/_process.py +32 -0
  12. singlestoredb/apps/_python_udfs.py +100 -0
  13. singlestoredb/apps/_stdout_supress.py +30 -0
  14. singlestoredb/apps/_uvicorn_util.py +36 -0
  15. singlestoredb/auth.py +245 -0
  16. singlestoredb/config.py +484 -0
  17. singlestoredb/connection.py +1487 -0
  18. singlestoredb/converters.py +950 -0
  19. singlestoredb/docstring/__init__.py +33 -0
  20. singlestoredb/docstring/attrdoc.py +126 -0
  21. singlestoredb/docstring/common.py +230 -0
  22. singlestoredb/docstring/epydoc.py +267 -0
  23. singlestoredb/docstring/google.py +412 -0
  24. singlestoredb/docstring/numpydoc.py +562 -0
  25. singlestoredb/docstring/parser.py +100 -0
  26. singlestoredb/docstring/py.typed +1 -0
  27. singlestoredb/docstring/rest.py +256 -0
  28. singlestoredb/docstring/tests/__init__.py +1 -0
  29. singlestoredb/docstring/tests/_pydoctor.py +21 -0
  30. singlestoredb/docstring/tests/test_epydoc.py +729 -0
  31. singlestoredb/docstring/tests/test_google.py +1007 -0
  32. singlestoredb/docstring/tests/test_numpydoc.py +1100 -0
  33. singlestoredb/docstring/tests/test_parse_from_object.py +109 -0
  34. singlestoredb/docstring/tests/test_parser.py +248 -0
  35. singlestoredb/docstring/tests/test_rest.py +547 -0
  36. singlestoredb/docstring/tests/test_util.py +70 -0
  37. singlestoredb/docstring/util.py +141 -0
  38. singlestoredb/exceptions.py +120 -0
  39. singlestoredb/functions/__init__.py +16 -0
  40. singlestoredb/functions/decorator.py +201 -0
  41. singlestoredb/functions/dtypes.py +1793 -0
  42. singlestoredb/functions/ext/__init__.py +1 -0
  43. singlestoredb/functions/ext/arrow.py +375 -0
  44. singlestoredb/functions/ext/asgi.py +2133 -0
  45. singlestoredb/functions/ext/json.py +420 -0
  46. singlestoredb/functions/ext/mmap.py +413 -0
  47. singlestoredb/functions/ext/rowdat_1.py +724 -0
  48. singlestoredb/functions/ext/timer.py +89 -0
  49. singlestoredb/functions/ext/utils.py +218 -0
  50. singlestoredb/functions/signature.py +1578 -0
  51. singlestoredb/functions/typing/__init__.py +41 -0
  52. singlestoredb/functions/typing/numpy.py +20 -0
  53. singlestoredb/functions/typing/pandas.py +2 -0
  54. singlestoredb/functions/typing/polars.py +2 -0
  55. singlestoredb/functions/typing/pyarrow.py +2 -0
  56. singlestoredb/functions/utils.py +421 -0
  57. singlestoredb/fusion/__init__.py +11 -0
  58. singlestoredb/fusion/graphql.py +213 -0
  59. singlestoredb/fusion/handler.py +916 -0
  60. singlestoredb/fusion/handlers/__init__.py +0 -0
  61. singlestoredb/fusion/handlers/export.py +525 -0
  62. singlestoredb/fusion/handlers/files.py +690 -0
  63. singlestoredb/fusion/handlers/job.py +660 -0
  64. singlestoredb/fusion/handlers/models.py +250 -0
  65. singlestoredb/fusion/handlers/stage.py +502 -0
  66. singlestoredb/fusion/handlers/utils.py +324 -0
  67. singlestoredb/fusion/handlers/workspace.py +956 -0
  68. singlestoredb/fusion/registry.py +249 -0
  69. singlestoredb/fusion/result.py +399 -0
  70. singlestoredb/http/__init__.py +27 -0
  71. singlestoredb/http/connection.py +1267 -0
  72. singlestoredb/magics/__init__.py +34 -0
  73. singlestoredb/magics/run_personal.py +137 -0
  74. singlestoredb/magics/run_shared.py +134 -0
  75. singlestoredb/management/__init__.py +9 -0
  76. singlestoredb/management/billing_usage.py +148 -0
  77. singlestoredb/management/cluster.py +462 -0
  78. singlestoredb/management/export.py +295 -0
  79. singlestoredb/management/files.py +1102 -0
  80. singlestoredb/management/inference_api.py +105 -0
  81. singlestoredb/management/job.py +887 -0
  82. singlestoredb/management/manager.py +373 -0
  83. singlestoredb/management/organization.py +226 -0
  84. singlestoredb/management/region.py +169 -0
  85. singlestoredb/management/utils.py +423 -0
  86. singlestoredb/management/workspace.py +1927 -0
  87. singlestoredb/mysql/__init__.py +177 -0
  88. singlestoredb/mysql/_auth.py +298 -0
  89. singlestoredb/mysql/charset.py +214 -0
  90. singlestoredb/mysql/connection.py +2032 -0
  91. singlestoredb/mysql/constants/CLIENT.py +38 -0
  92. singlestoredb/mysql/constants/COMMAND.py +32 -0
  93. singlestoredb/mysql/constants/CR.py +78 -0
  94. singlestoredb/mysql/constants/ER.py +474 -0
  95. singlestoredb/mysql/constants/EXTENDED_TYPE.py +3 -0
  96. singlestoredb/mysql/constants/FIELD_TYPE.py +48 -0
  97. singlestoredb/mysql/constants/FLAG.py +15 -0
  98. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  99. singlestoredb/mysql/constants/VECTOR_TYPE.py +6 -0
  100. singlestoredb/mysql/constants/__init__.py +0 -0
  101. singlestoredb/mysql/converters.py +271 -0
  102. singlestoredb/mysql/cursors.py +896 -0
  103. singlestoredb/mysql/err.py +92 -0
  104. singlestoredb/mysql/optionfile.py +20 -0
  105. singlestoredb/mysql/protocol.py +450 -0
  106. singlestoredb/mysql/tests/__init__.py +19 -0
  107. singlestoredb/mysql/tests/base.py +126 -0
  108. singlestoredb/mysql/tests/conftest.py +37 -0
  109. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  110. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  111. singlestoredb/mysql/tests/test_basic.py +452 -0
  112. singlestoredb/mysql/tests/test_connection.py +851 -0
  113. singlestoredb/mysql/tests/test_converters.py +58 -0
  114. singlestoredb/mysql/tests/test_cursor.py +141 -0
  115. singlestoredb/mysql/tests/test_err.py +16 -0
  116. singlestoredb/mysql/tests/test_issues.py +514 -0
  117. singlestoredb/mysql/tests/test_load_local.py +75 -0
  118. singlestoredb/mysql/tests/test_nextset.py +88 -0
  119. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  120. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  121. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  122. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  123. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  124. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  125. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  126. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  127. singlestoredb/mysql/times.py +23 -0
  128. singlestoredb/notebook/__init__.py +16 -0
  129. singlestoredb/notebook/_objects.py +213 -0
  130. singlestoredb/notebook/_portal.py +352 -0
  131. singlestoredb/py.typed +0 -0
  132. singlestoredb/pytest.py +352 -0
  133. singlestoredb/server/__init__.py +0 -0
  134. singlestoredb/server/docker.py +452 -0
  135. singlestoredb/server/free_tier.py +267 -0
  136. singlestoredb/tests/__init__.py +0 -0
  137. singlestoredb/tests/alltypes.sql +307 -0
  138. singlestoredb/tests/alltypes_no_nulls.sql +208 -0
  139. singlestoredb/tests/empty.sql +0 -0
  140. singlestoredb/tests/ext_funcs/__init__.py +702 -0
  141. singlestoredb/tests/local_infile.csv +3 -0
  142. singlestoredb/tests/test.ipynb +18 -0
  143. singlestoredb/tests/test.sql +680 -0
  144. singlestoredb/tests/test2.ipynb +18 -0
  145. singlestoredb/tests/test2.sql +1 -0
  146. singlestoredb/tests/test_basics.py +1332 -0
  147. singlestoredb/tests/test_config.py +318 -0
  148. singlestoredb/tests/test_connection.py +3103 -0
  149. singlestoredb/tests/test_dbapi.py +27 -0
  150. singlestoredb/tests/test_exceptions.py +45 -0
  151. singlestoredb/tests/test_ext_func.py +1472 -0
  152. singlestoredb/tests/test_ext_func_data.py +1101 -0
  153. singlestoredb/tests/test_fusion.py +1527 -0
  154. singlestoredb/tests/test_http.py +288 -0
  155. singlestoredb/tests/test_management.py +1599 -0
  156. singlestoredb/tests/test_plugin.py +33 -0
  157. singlestoredb/tests/test_results.py +171 -0
  158. singlestoredb/tests/test_types.py +132 -0
  159. singlestoredb/tests/test_udf.py +737 -0
  160. singlestoredb/tests/test_udf_returns.py +459 -0
  161. singlestoredb/tests/test_vectorstore.py +51 -0
  162. singlestoredb/tests/test_xdict.py +333 -0
  163. singlestoredb/tests/utils.py +141 -0
  164. singlestoredb/types.py +373 -0
  165. singlestoredb/utils/__init__.py +0 -0
  166. singlestoredb/utils/config.py +950 -0
  167. singlestoredb/utils/convert_rows.py +69 -0
  168. singlestoredb/utils/debug.py +13 -0
  169. singlestoredb/utils/dtypes.py +205 -0
  170. singlestoredb/utils/events.py +65 -0
  171. singlestoredb/utils/mogrify.py +151 -0
  172. singlestoredb/utils/results.py +585 -0
  173. singlestoredb/utils/xdict.py +425 -0
  174. singlestoredb/vectorstore.py +192 -0
  175. singlestoredb/warnings.py +5 -0
  176. singlestoredb-1.16.1.dist-info/METADATA +165 -0
  177. singlestoredb-1.16.1.dist-info/RECORD +183 -0
  178. singlestoredb-1.16.1.dist-info/WHEEL +5 -0
  179. singlestoredb-1.16.1.dist-info/entry_points.txt +2 -0
  180. singlestoredb-1.16.1.dist-info/licenses/LICENSE +201 -0
  181. singlestoredb-1.16.1.dist-info/top_level.txt +3 -0
  182. sqlx/__init__.py +4 -0
  183. sqlx/magic.py +113 -0
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env python
2
+ # type: ignore
3
+ # encoding: utf-8
4
+ #
5
+ # Copyright SAS Institute
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the License);
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ # This file originally copied from https://github.com/sassoftware/python-swat
20
+ #
21
+ import copy
22
+ import unittest
23
+
24
+ from singlestoredb.utils.xdict import xadict
25
+ from singlestoredb.utils.xdict import xdict
26
+
27
+ out1 = {
28
+ 'a': {'one': 1},
29
+ 'b': {'two': 2},
30
+ 'c': {
31
+ 'three': {'nest': 3},
32
+ 'four': {
33
+ 'nest': {'double': 4},
34
+ },
35
+ },
36
+ }
37
+
38
+ flatout1 = {
39
+ 'a.one': 1,
40
+ 'b.two': 2,
41
+ 'c.three.nest': 3,
42
+ 'c.four.nest.double': 4,
43
+ }
44
+
45
+
46
+ class TestXDict(unittest.TestCase):
47
+
48
+ def test_constructor(self):
49
+ x = xdict(one=1, two=2, three=3)
50
+ self.assertEqual(x, dict(one=1, two=2, three=3))
51
+
52
+ x = xdict([('a.one', 1), ('b.two', 2), ('c.three.nest', 3)])
53
+ self.assertEqual(
54
+ x, {
55
+ 'a': {'one': 1},
56
+ 'b': {'two': 2},
57
+ 'c': {
58
+ 'three': {'nest': 3},
59
+ },
60
+ },
61
+ )
62
+
63
+ x = xdict([
64
+ ('a.one', 1), ('b.two', 2), ('c.three.nest', 3),
65
+ ('c.four.nest.double', 4),
66
+ ])
67
+ self.assertEqual(x, out1)
68
+
69
+ x = xdict(out1)
70
+ self.assertEqual(x, out1)
71
+
72
+ x = xdict(flatout1)
73
+ self.assertEqual(x, out1)
74
+
75
+ x = xdict(**out1)
76
+ self.assertEqual(x, out1)
77
+
78
+ x = xdict(**flatout1)
79
+ self.assertEqual(x, out1)
80
+
81
+ def test_setitem(self):
82
+ x = xdict()
83
+ x['a.one'] = 1
84
+ x['b.two'] = 2
85
+ x['c.three.nest'] = 3
86
+ x['c.four.nest.double'] = 4
87
+ self.assertEqual(x, out1)
88
+
89
+ x[100] = 1000
90
+ self.assertEqual(x[100], 1000)
91
+
92
+ x = xdict()
93
+ x['a.one'] = 1
94
+ x['b.two'] = 2
95
+ x['c.three.nest'] = 3
96
+ x['c.four'] = {'nest': {'double': 4}}
97
+ self.assertEqual(x, out1)
98
+
99
+ def test_getitem(self):
100
+ x = xdict(out1)
101
+ self.assertEqual(x['a.one'], 1)
102
+ self.assertEqual(x['b.two'], 2)
103
+ self.assertEqual(x['c.three.nest'], 3)
104
+ self.assertEqual(x['c.four.nest.double'], 4)
105
+
106
+ self.assertEqual(x['a'], {'one': 1})
107
+ self.assertEqual(x['b'], {'two': 2})
108
+ self.assertEqual(x['c'], {'three': {'nest': 3}, 'four': {'nest': {'double': 4}}})
109
+ self.assertEqual(x['c.three'], {'nest': 3})
110
+ self.assertEqual(x['c.four'], {'nest': {'double': 4}})
111
+ self.assertEqual(x['c.four.nest'], {'double': 4})
112
+
113
+ self.assertEqual(x['a'], {'one': 1})
114
+ self.assertEqual(x['a']['one'], 1)
115
+ self.assertEqual(x['b'], {'two': 2})
116
+ self.assertEqual(x['b']['two'], 2)
117
+ self.assertEqual(x['c'], {'three': {'nest': 3}, 'four': {'nest': {'double': 4}}})
118
+ self.assertEqual(x['c']['three'], {'nest': 3})
119
+ self.assertEqual(x['c']['three']['nest'], 3)
120
+ self.assertEqual(x['c']['four'], {'nest': {'double': 4}})
121
+ self.assertEqual(x['c']['four']['nest'], {'double': 4})
122
+ self.assertEqual(x['c']['four']['nest']['double'], 4)
123
+
124
+ def test_get(self):
125
+ x = xdict(out1)
126
+
127
+ self.assertEqual(x.get('a', 1000), {'one': 1})
128
+ self.assertEqual(x.get('a.one', 1000), 1)
129
+ self.assertEqual(x.get('a.does.not.exist', 1000), 1000)
130
+
131
+ def test_delitem(self):
132
+ newout1 = copy.deepcopy(out1)
133
+ x = xdict(out1)
134
+
135
+ del x['a']
136
+ del newout1['a']
137
+ self.assertEqual(x, newout1)
138
+
139
+ del x['c.four.nest']
140
+ del newout1['c']['four']['nest']
141
+ self.assertEqual(x, newout1)
142
+
143
+ with self.assertRaises(KeyError):
144
+ del x['a.does.not.exist']
145
+
146
+ def test_flattened(self):
147
+ x = xdict(out1)
148
+ self.assertEqual(x.flattened(), flatout1)
149
+
150
+ def test_setdefault(self):
151
+ x = xdict(out1)
152
+
153
+ self.assertEqual(x.setdefault('a.one'), 1)
154
+ self.assertEqual(x.setdefault('b.two'), 2)
155
+ self.assertEqual(x.setdefault('b'), {'two': 2})
156
+
157
+ self.assertEqual(x.setdefault('w.none'), None)
158
+ self.assertEqual(x['w.none'], None)
159
+ self.assertEqual(x['w']['none'], None)
160
+ self.assertEqual(x.setdefault('x.y.z', 200), 200)
161
+ self.assertEqual(x['x.y.z'], 200)
162
+ self.assertEqual(x['x']['y']['z'], 200)
163
+ self.assertEqual(x.setdefault('x.y.z', 10), 200)
164
+ self.assertEqual(x['x.y.z'], 200)
165
+ self.assertEqual(x['x']['y']['z'], 200)
166
+ self.assertEqual(x['x']['y'], {'z': 200})
167
+
168
+ self.assertEqual(x.setdefault('does.not.exist', {'new': 'key'}), {'new': 'key'})
169
+ self.assertTrue(isinstance(x.setdefault('does.not.exist', {'new': 'key'}), xdict))
170
+
171
+ def test_json(self):
172
+ x = xdict(out1)
173
+ self.assertEqual(xdict.from_json(x.to_json()), out1)
174
+
175
+ def test_flat(self):
176
+ x = xdict(out1)
177
+
178
+ self.assertEqual(sorted(x.flatkeys()), sorted(flatout1.keys()))
179
+ self.assertEqual(sorted(x.flatvalues()), sorted(flatout1.values()))
180
+ self.assertEqual(sorted(x.flatitems()), sorted(flatout1.items()))
181
+
182
+ if hasattr(flatout1, 'iterkeys'):
183
+ self.assertEqual(sorted(x.iterflatkeys()), sorted(flatout1.iterkeys()))
184
+ if hasattr(flatout1, 'itervalues'):
185
+ self.assertEqual(sorted(x.iterflatvalues()), sorted(flatout1.itervalues()))
186
+ if hasattr(flatout1, 'iteritems'):
187
+ self.assertEqual(sorted(x.iterflatitems()), sorted(flatout1.iteritems()))
188
+
189
+ if hasattr(flatout1, 'iterkeys'):
190
+ self.assertEqual(sorted(x.viewflatkeys()), sorted(flatout1.iterkeys()))
191
+ if hasattr(flatout1, 'itervalues'):
192
+ self.assertEqual(sorted(x.viewflatvalues()), sorted(flatout1.itervalues()))
193
+ if hasattr(flatout1, 'iteritems'):
194
+ self.assertEqual(sorted(x.viewflatitems()), sorted(flatout1.iteritems()))
195
+
196
+ def test_contains(self):
197
+ x = xdict(out1)
198
+
199
+ self.assertTrue('a' in x)
200
+ self.assertTrue('a.one' in x)
201
+ self.assertTrue('b' in x)
202
+ self.assertTrue('c.four' in x)
203
+ self.assertTrue('c.four.nest' in x)
204
+ self.assertTrue('c.four.nest.double' in x)
205
+ self.assertFalse('z' in x)
206
+ self.assertFalse('four' in x)
207
+
208
+ self.assertTrue(x.has_key('a')) # noqa: W601
209
+ self.assertTrue(x.has_key('a.one')) # noqa: W601
210
+ self.assertTrue(x.has_key('b')) # noqa: W601
211
+ self.assertTrue(x.has_key('c.four')) # noqa: W601
212
+ self.assertTrue(x.has_key('c.four.nest')) # noqa: W601
213
+ self.assertTrue(x.has_key('c.four.nest.double')) # noqa: W601
214
+ self.assertFalse(x.has_key('z')) # noqa: W601
215
+ self.assertFalse(x.has_key('four')) # noqa: W601
216
+
217
+ def test_pop(self):
218
+ x = xdict(out1)
219
+
220
+ self.assertEqual(x, out1)
221
+
222
+ self.assertEqual(x.pop('a.one'), out1['a']['one'])
223
+ self.assertEqual(x.pop('b.two'), out1['b']['two'])
224
+ self.assertEqual(x.pop('c'), out1['c'])
225
+
226
+ self.assertEqual(x, {'a': {}, 'b': {}})
227
+
228
+ with self.assertRaises(KeyError):
229
+ x.pop('a.does.not.exist')
230
+
231
+ self.assertEqual(x.pop('a.does.not.exist', 1000), 1000)
232
+
233
+ def test_copy(self):
234
+ w = xdict(out1)
235
+
236
+ x = w.copy()
237
+
238
+ self.assertEqual(w, x)
239
+ self.assertTrue(w is not x)
240
+ self.assertTrue(w['a'] is x['a'])
241
+ self.assertTrue(w['b'] is x['b'])
242
+ self.assertTrue(w['c'] is x['c'])
243
+
244
+ y = copy.copy(w)
245
+
246
+ self.assertEqual(w, y)
247
+ self.assertTrue(w is not y)
248
+ self.assertTrue(w['a'] is y['a'])
249
+ self.assertTrue(w['a.one'] is y['a.one'])
250
+ self.assertTrue(w['b'] is y['b'])
251
+ self.assertTrue(w['b.two'] is y['b.two'])
252
+ self.assertTrue(w['c'] is y['c'])
253
+ self.assertTrue(w['c.three'] is y['c.three'])
254
+
255
+ z = copy.deepcopy(w)
256
+
257
+ # TODO: Deepcopy is only shallow copying
258
+ self.assertEqual(w, z)
259
+ self.assertTrue(w is not z)
260
+ self.assertTrue(w['a'] is not z['a'])
261
+ # self.assertTrue(w['a.one'] is not z['a.one'])
262
+ self.assertTrue(w['b'] is not z['b'])
263
+ # self.assertTrue(w['b.two'] is not z['b.two'])
264
+ self.assertTrue(w['c'] is not z['c'])
265
+ # self.assertTrue(w['c.three'] is not z['c.three'])
266
+
267
+ def test_attrs(self):
268
+ x = xadict(out1)
269
+
270
+ self.assertEqual(x.a, x['a'])
271
+ self.assertEqual(x.a.one, x['a.one'])
272
+ self.assertEqual(x.a.one, x['a']['one'])
273
+ self.assertEqual(x.b, x['b'])
274
+ self.assertEqual(x.b.two, x['b.two'])
275
+ self.assertEqual(x.b.two, x['b']['two'])
276
+ self.assertEqual(x.c, x['c'])
277
+ self.assertEqual(x.c.three, x['c.three'])
278
+ self.assertEqual(x.c.three, x['c']['three'])
279
+
280
+ x.a.one = 100
281
+ x.c.four.nest = {'float': 5}
282
+
283
+ self.assertEqual(x['a.one'], 100)
284
+ self.assertEqual(x['c.four.nest'], {'float': 5})
285
+
286
+ del x.c.three
287
+
288
+ self.assertEqual(x.c, {'four': {'nest': {'float': 5}}})
289
+
290
+ del x.c
291
+
292
+ self.assertEqual(x, {'a': {'one': 100}, 'b': {'two': 2}})
293
+
294
+ del x.a
295
+ del x.b
296
+
297
+ self.assertEqual(x, {})
298
+
299
+ # Non-existent keys
300
+ x.foo.bar.xxx = 10
301
+ x.foo.baz = 'hi there'
302
+
303
+ self.assertEqual(x, {'foo': {'bar': {'xxx': 10}, 'baz': 'hi there'}})
304
+
305
+ def test_ints(self):
306
+ x = xadict(out1)
307
+
308
+ out1int = dict(out1)
309
+ out1int['d'] = {}
310
+ out1int['d'][0] = dict(hi='there', bye='now')
311
+ out1int['d'][1] = dict(test='value')
312
+ out1int['d'][2] = 100
313
+
314
+ x.d[0].hi = 'there'
315
+ x.d[0].bye = 'now'
316
+ x.d[1].test = 'value'
317
+ x.d[2] = 100
318
+
319
+ self.assertEqual(x, out1int)
320
+
321
+ # Flat
322
+ flatout1int = dict(flatout1)
323
+ flatout1int['d[0].hi'] = 'there'
324
+ flatout1int['d[0].bye'] = 'now'
325
+ flatout1int['d[1].test'] = 'value'
326
+ flatout1int['d[2]'] = 100
327
+
328
+ self.assertEqual(x.flattened(), flatout1int)
329
+
330
+
331
+ if __name__ == '__main__':
332
+ import nose2
333
+ nose2.main()
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env python
2
+ # type: ignore
3
+ """Utilities for testing."""
4
+ import os
5
+ import uuid
6
+ from typing import Any
7
+ from typing import Dict
8
+ from urllib.parse import urlparse
9
+
10
+ import singlestoredb as s2
11
+ from singlestoredb.connection import build_params
12
+
13
+
14
+ def apply_template(content: str, vars: Dict[str, Any]) -> str:
15
+ for k, v in vars.items():
16
+ key = '{{%s}}' % k
17
+ if key in content:
18
+ content = content.replace(key, v)
19
+ return content
20
+
21
+
22
+ def load_sql(sql_file: str) -> str:
23
+ """
24
+ Load a file containing SQL code.
25
+
26
+ Parameters
27
+ ----------
28
+ sql_file : str
29
+ Name of the SQL file to load.
30
+
31
+ Returns
32
+ -------
33
+ (str, bool)
34
+ Name of database created for SQL file and a boolean indicating
35
+ whether the database already existed (meaning that it should not
36
+ be deleted when tests are finished).
37
+
38
+ """
39
+ dbname = None
40
+
41
+ # Use an existing database name if given.
42
+ if 'SINGLESTOREDB_URL' in os.environ:
43
+ dbname = build_params(host=os.environ['SINGLESTOREDB_URL']).get('database')
44
+ elif 'SINGLESTOREDB_HOST' in os.environ:
45
+ dbname = build_params(host=os.environ['SINGLESTOREDB_HOST']).get('database')
46
+ elif 'SINGLESTOREDB_DATABASE' in os.environ:
47
+ dbname = os.environ['SINGLESTOREDB_DATBASE']
48
+
49
+ # If no database name was specified, use initializer URL if given.
50
+ # HTTP can't change databases, so you can't initialize from HTTP
51
+ # while also creating a database.
52
+ args = {'local_infile': True}
53
+ if not dbname and 'SINGLESTOREDB_INIT_DB_URL' in os.environ:
54
+ args['host'] = os.environ['SINGLESTOREDB_INIT_DB_URL']
55
+
56
+ http_port = 0
57
+ if 'SINGLESTOREDB_URL' in os.environ:
58
+ url = os.environ['SINGLESTOREDB_URL']
59
+ if url.startswith('http:') or url.startswith('https:'):
60
+ urlp = urlparse(url)
61
+ if urlp.port:
62
+ http_port = urlp.port
63
+
64
+ if 'SINGLESTOREDB_HTTP_PORT' in os.environ:
65
+ http_port = int(os.environ['SINGLESTOREDB_HTTP_PORT'])
66
+
67
+ dbexisted = bool(dbname)
68
+
69
+ template_vars = dict(DATABASE_NAME=dbname, TEST_PATH=os.path.dirname(sql_file))
70
+
71
+ # Always use the default driver since not all operations are
72
+ # permitted in the HTTP API.
73
+ with open(sql_file, 'r') as infile:
74
+ with s2.connect(**args) as conn:
75
+ with conn.cursor() as cur:
76
+ try:
77
+ cur.execute('SET GLOBAL default_partitions_per_leaf=2')
78
+ cur.execute('SET GLOBAL log_file_size_partitions=1048576')
79
+ cur.execute('SET GLOBAL log_file_size_ref_dbs=1048576')
80
+ except s2.OperationalError:
81
+ pass
82
+
83
+ if not dbname:
84
+ dbname = 'TEST_{}'.format(uuid.uuid4()).replace('-', '_')
85
+ cur.execute(f'CREATE DATABASE {dbname};')
86
+ cur.execute(f'USE {dbname};')
87
+
88
+ template_vars['DATABASE_NAME'] = dbname
89
+
90
+ # Execute lines in SQL.
91
+ for cmd in infile.read().split(';\n'):
92
+ cmd = apply_template(cmd.strip(), template_vars)
93
+ if cmd:
94
+ cmd += ';'
95
+ cur.execute(cmd)
96
+
97
+ elif not conn.driver.startswith('http'):
98
+ cur.execute(f'USE {dbname};')
99
+
100
+ # Start HTTP server as needed.
101
+ if http_port and not conn.driver.startswith('http'):
102
+ cur.execute(f'SET GLOBAL HTTP_PROXY_PORT={http_port};')
103
+ cur.execute('SET GLOBAL HTTP_API=ON;')
104
+ cur.execute('RESTART PROXY;')
105
+
106
+ return dbname, dbexisted
107
+
108
+
109
+ def drop_database(name: str) -> None:
110
+ """Drop a database with the given name."""
111
+ if name:
112
+ args = {}
113
+ if 'SINGLESTOREDB_INIT_DB_URL' in os.environ:
114
+ args['host'] = os.environ['SINGLESTOREDB_INIT_DB_URL']
115
+ with s2.connect(**args) as conn:
116
+ with conn.cursor() as cur:
117
+ cur.execute(f'DROP DATABASE {name};')
118
+
119
+
120
+ def create_user(name: str, password: str, dbname: str) -> None:
121
+ """Create a user for the test database."""
122
+ if name:
123
+ args = {}
124
+ if 'SINGLESTOREDB_INIT_DB_URL' in os.environ:
125
+ args['host'] = os.environ['SINGLESTOREDB_INIT_DB_URL']
126
+ with s2.connect(**args) as conn:
127
+ with conn.cursor() as cur:
128
+ cur.execute(f'DROP USER IF EXISTS {name};')
129
+ cur.execute(f'CREATE USER "{name}"@"%" IDENTIFIED BY "{password}"')
130
+ cur.execute(f'GRANT ALL ON {dbname}.* to "{name}"@"%"')
131
+
132
+
133
+ def drop_user(name: str) -> None:
134
+ """Drop a database with the given name."""
135
+ if name:
136
+ args = {}
137
+ if 'SINGLESTOREDB_INIT_DB_URL' in os.environ:
138
+ args['host'] = os.environ['SINGLESTOREDB_INIT_DB_URL']
139
+ with s2.connect(**args) as conn:
140
+ with conn.cursor() as cur:
141
+ cur.execute(f'DROP USER IF EXISTS {name};')