singlestoredb 0.3.3__py3-none-any.whl → 1.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of singlestoredb might be problematic. Click here for more details.

Files changed (121) hide show
  1. singlestoredb/__init__.py +33 -2
  2. singlestoredb/alchemy/__init__.py +90 -0
  3. singlestoredb/auth.py +6 -4
  4. singlestoredb/config.py +116 -16
  5. singlestoredb/connection.py +489 -523
  6. singlestoredb/converters.py +275 -26
  7. singlestoredb/exceptions.py +30 -4
  8. singlestoredb/functions/__init__.py +1 -0
  9. singlestoredb/functions/decorator.py +142 -0
  10. singlestoredb/functions/dtypes.py +1639 -0
  11. singlestoredb/functions/ext/__init__.py +2 -0
  12. singlestoredb/functions/ext/arrow.py +375 -0
  13. singlestoredb/functions/ext/asgi.py +661 -0
  14. singlestoredb/functions/ext/json.py +427 -0
  15. singlestoredb/functions/ext/mmap.py +306 -0
  16. singlestoredb/functions/ext/rowdat_1.py +744 -0
  17. singlestoredb/functions/signature.py +673 -0
  18. singlestoredb/fusion/__init__.py +11 -0
  19. singlestoredb/fusion/graphql.py +213 -0
  20. singlestoredb/fusion/handler.py +621 -0
  21. singlestoredb/fusion/handlers/__init__.py +0 -0
  22. singlestoredb/fusion/handlers/stage.py +257 -0
  23. singlestoredb/fusion/handlers/utils.py +162 -0
  24. singlestoredb/fusion/handlers/workspace.py +412 -0
  25. singlestoredb/fusion/registry.py +164 -0
  26. singlestoredb/fusion/result.py +399 -0
  27. singlestoredb/http/__init__.py +27 -0
  28. singlestoredb/http/connection.py +1192 -0
  29. singlestoredb/management/__init__.py +3 -2
  30. singlestoredb/management/billing_usage.py +148 -0
  31. singlestoredb/management/cluster.py +19 -14
  32. singlestoredb/management/manager.py +100 -40
  33. singlestoredb/management/organization.py +188 -0
  34. singlestoredb/management/region.py +6 -8
  35. singlestoredb/management/utils.py +253 -4
  36. singlestoredb/management/workspace.py +1153 -35
  37. singlestoredb/mysql/__init__.py +177 -0
  38. singlestoredb/mysql/_auth.py +298 -0
  39. singlestoredb/mysql/charset.py +214 -0
  40. singlestoredb/mysql/connection.py +1814 -0
  41. singlestoredb/mysql/constants/CLIENT.py +38 -0
  42. singlestoredb/mysql/constants/COMMAND.py +32 -0
  43. singlestoredb/mysql/constants/CR.py +78 -0
  44. singlestoredb/mysql/constants/ER.py +474 -0
  45. singlestoredb/mysql/constants/FIELD_TYPE.py +32 -0
  46. singlestoredb/mysql/constants/FLAG.py +15 -0
  47. singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
  48. singlestoredb/mysql/constants/__init__.py +0 -0
  49. singlestoredb/mysql/converters.py +271 -0
  50. singlestoredb/mysql/cursors.py +713 -0
  51. singlestoredb/mysql/err.py +92 -0
  52. singlestoredb/mysql/optionfile.py +20 -0
  53. singlestoredb/mysql/protocol.py +388 -0
  54. singlestoredb/mysql/tests/__init__.py +19 -0
  55. singlestoredb/mysql/tests/base.py +126 -0
  56. singlestoredb/mysql/tests/conftest.py +37 -0
  57. singlestoredb/mysql/tests/test_DictCursor.py +132 -0
  58. singlestoredb/mysql/tests/test_SSCursor.py +141 -0
  59. singlestoredb/mysql/tests/test_basic.py +452 -0
  60. singlestoredb/mysql/tests/test_connection.py +851 -0
  61. singlestoredb/mysql/tests/test_converters.py +58 -0
  62. singlestoredb/mysql/tests/test_cursor.py +141 -0
  63. singlestoredb/mysql/tests/test_err.py +16 -0
  64. singlestoredb/mysql/tests/test_issues.py +514 -0
  65. singlestoredb/mysql/tests/test_load_local.py +75 -0
  66. singlestoredb/mysql/tests/test_nextset.py +88 -0
  67. singlestoredb/mysql/tests/test_optionfile.py +27 -0
  68. singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
  69. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
  70. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
  71. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
  72. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
  73. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
  74. singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
  75. singlestoredb/mysql/times.py +23 -0
  76. singlestoredb/pytest.py +283 -0
  77. singlestoredb/tests/empty.sql +0 -0
  78. singlestoredb/tests/ext_funcs/__init__.py +385 -0
  79. singlestoredb/tests/test.sql +210 -0
  80. singlestoredb/tests/test2.sql +1 -0
  81. singlestoredb/tests/test_basics.py +482 -117
  82. singlestoredb/tests/test_config.py +13 -15
  83. singlestoredb/tests/test_connection.py +241 -289
  84. singlestoredb/tests/test_dbapi.py +27 -0
  85. singlestoredb/tests/test_exceptions.py +0 -2
  86. singlestoredb/tests/test_ext_func.py +1193 -0
  87. singlestoredb/tests/test_ext_func_data.py +1101 -0
  88. singlestoredb/tests/test_fusion.py +465 -0
  89. singlestoredb/tests/test_http.py +32 -28
  90. singlestoredb/tests/test_management.py +588 -10
  91. singlestoredb/tests/test_plugin.py +33 -0
  92. singlestoredb/tests/test_results.py +11 -14
  93. singlestoredb/tests/test_types.py +0 -2
  94. singlestoredb/tests/test_udf.py +687 -0
  95. singlestoredb/tests/test_xdict.py +0 -2
  96. singlestoredb/tests/utils.py +3 -4
  97. singlestoredb/types.py +4 -5
  98. singlestoredb/utils/config.py +71 -12
  99. singlestoredb/utils/convert_rows.py +0 -2
  100. singlestoredb/utils/debug.py +13 -0
  101. singlestoredb/utils/mogrify.py +151 -0
  102. singlestoredb/utils/results.py +4 -3
  103. singlestoredb/utils/xdict.py +12 -12
  104. singlestoredb-1.0.3.dist-info/METADATA +139 -0
  105. singlestoredb-1.0.3.dist-info/RECORD +112 -0
  106. {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/WHEEL +1 -1
  107. singlestoredb-1.0.3.dist-info/entry_points.txt +2 -0
  108. singlestoredb/drivers/__init__.py +0 -46
  109. singlestoredb/drivers/base.py +0 -200
  110. singlestoredb/drivers/cymysql.py +0 -40
  111. singlestoredb/drivers/http.py +0 -49
  112. singlestoredb/drivers/mariadb.py +0 -42
  113. singlestoredb/drivers/mysqlconnector.py +0 -51
  114. singlestoredb/drivers/mysqldb.py +0 -62
  115. singlestoredb/drivers/pymysql.py +0 -39
  116. singlestoredb/drivers/pyodbc.py +0 -67
  117. singlestoredb/http.py +0 -794
  118. singlestoredb-0.3.3.dist-info/METADATA +0 -105
  119. singlestoredb-0.3.3.dist-info/RECORD +0 -46
  120. {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/LICENSE +0 -0
  121. {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env python
2
2
  # type: ignore
3
3
  """Utilities for testing."""
4
- from __future__ import annotations
5
-
6
4
  import os
7
5
  import uuid
8
6
  from typing import Any
@@ -88,11 +86,12 @@ def load_sql(sql_file: str) -> str:
88
86
  if cmd:
89
87
  cmd += ';'
90
88
  cur.execute(cmd)
91
- else:
89
+
90
+ elif not conn.driver.startswith('http'):
92
91
  cur.execute(f'USE {dbname};')
93
92
 
94
93
  # Start HTTP server as needed.
95
- if http_port:
94
+ if http_port and not conn.driver.startswith('http'):
96
95
  cur.execute(f'SET GLOBAL HTTP_PROXY_PORT={http_port};')
97
96
  cur.execute('SET GLOBAL HTTP_API=ON;')
98
97
  cur.execute('RESTART PROXY;')
singlestoredb/types.py CHANGED
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env python
2
2
  """SingleStoreDB data type utilities."""
3
- from __future__ import annotations
4
-
5
3
  import datetime
6
4
  import decimal
7
5
  import time
8
6
  from typing import Dict
9
7
  from typing import List
10
8
  from typing import Set
9
+ from typing import Union
11
10
 
12
11
 
13
12
  Date = datetime.date
@@ -78,8 +77,8 @@ class DBAPIType(object):
78
77
 
79
78
  """
80
79
 
81
- def __init__(self, *values: int | str | type | DBAPIType):
82
- self.values: Set[int | str | type] = set()
80
+ def __init__(self, *values: Union[int, str, type, 'DBAPIType']):
81
+ self.values: Set[Union[int, str, type]] = set()
83
82
  name: str = ''
84
83
  code: int = -1
85
84
  for item in values:
@@ -135,7 +134,7 @@ class DBAPIType(object):
135
134
  bool
136
135
 
137
136
  """
138
- return not(self.__eq__(other))
137
+ return not (self.__eq__(other))
139
138
 
140
139
  def __str__(self) -> str:
141
140
  """Return string representation."""
@@ -24,15 +24,17 @@ reset_option(...). The describe_option(...) function can be used to display
24
24
  a description of one or more options.
25
25
 
26
26
  """
27
- from __future__ import annotations
28
-
29
27
  import contextlib
30
28
  import os
31
29
  import re
32
30
  from typing import Any
33
31
  from typing import Callable
32
+ from typing import Dict
34
33
  from typing import Iterator
34
+ from typing import List
35
+ from typing import Mapping
35
36
  from typing import Optional
37
+ from typing import Tuple
36
38
  from typing import Union
37
39
  from urllib.parse import urlparse
38
40
 
@@ -45,7 +47,7 @@ _config = xdict()
45
47
  items_types = (list, tuple, set)
46
48
 
47
49
 
48
- def _getenv(names: Union[str, list[str]], *args: Any) -> str:
50
+ def _getenv(names: Union[str, List[str]], *args: Any) -> str:
49
51
  """
50
52
  Check for multiple environment variable values.
51
53
 
@@ -79,7 +81,7 @@ def _getenv(names: Union[str, list[str]], *args: Any) -> str:
79
81
  raise KeyError(names[0])
80
82
 
81
83
 
82
- def _setenv(names: Union[str, list[str]], value: Any) -> None:
84
+ def _setenv(names: Union[str, List[str]], value: Any) -> None:
83
85
  """
84
86
  Set environment variable.
85
87
 
@@ -109,7 +111,7 @@ def _setenv(names: Union[str, list[str]], value: Any) -> None:
109
111
  os.environ[name] = value
110
112
 
111
113
 
112
- def _delenv(names: Union[str, list[str]]) -> None:
114
+ def _delenv(names: Union[str, List[str]]) -> None:
113
115
  """Delete given environment variables."""
114
116
  if not isinstance(names, items_types):
115
117
  names = [names]
@@ -118,7 +120,7 @@ def _delenv(names: Union[str, list[str]]) -> None:
118
120
  os.environ.pop(name.replace('_', ''), None)
119
121
 
120
122
 
121
- def iteroptions(*args: Any, **kwargs: Any) -> Iterator[tuple[str, Any]]:
123
+ def iteroptions(*args: Any, **kwargs: Any) -> Iterator[Tuple[str, Any]]:
122
124
  """
123
125
  Iterate through name / value pairs of options
124
126
 
@@ -264,7 +266,7 @@ def get_option(key: str) -> Any:
264
266
  return opt.get()
265
267
 
266
268
 
267
- def get_suboptions(key: str) -> dict[str, Any]:
269
+ def get_suboptions(key: str) -> Dict[str, Any]:
268
270
  """
269
271
  Get the dictionary of options at the level `key`.
270
272
 
@@ -561,12 +563,34 @@ def check_bool(value: Union[bool, int]) -> bool:
561
563
  raise ValueError('%s is not a recognized bool value')
562
564
 
563
565
 
566
+ def check_optional_bool(value: Optional[Union[bool, int]]) -> Optional[bool]:
567
+ """
568
+ Validate an optional bool value.
569
+
570
+ Parameters
571
+ ----------
572
+ value : int or bool or None
573
+ The value to validate. If specified as an integer, it must
574
+ be either 0 for False or 1 for True.
575
+
576
+ Returns
577
+ -------
578
+ bool
579
+ The validated bool
580
+
581
+ """
582
+ if value is None:
583
+ return None
584
+
585
+ return check_bool(value)
586
+
587
+
564
588
  def check_str(
565
589
  value: Any,
566
590
  pattern: Optional[str] = None,
567
591
  max_length: Optional[int] = None,
568
592
  min_length: Optional[int] = None,
569
- valid_values: Optional[list[str]] = None,
593
+ valid_values: Optional[List[str]] = None,
570
594
  ) -> Optional[str]:
571
595
  """
572
596
  Validate a string value.
@@ -622,12 +646,47 @@ def check_str(
622
646
  return out
623
647
 
624
648
 
649
+ def check_dict_str_str(
650
+ value: Any,
651
+ ) -> Optional[Dict[str, str]]:
652
+ """
653
+ Validate a string value.
654
+
655
+ Parameters
656
+ ----------
657
+ value : dict
658
+ The value to validate. Keys and values must be strings.
659
+
660
+ Returns
661
+ -------
662
+ dict
663
+ The validated dict value
664
+ """
665
+ if value is None:
666
+ return None
667
+
668
+ if not isinstance(value, Mapping):
669
+ raise ValueError(
670
+ 'value {} must be of type dict'.format(value),
671
+ )
672
+
673
+ out = {}
674
+ for k, v in value.items():
675
+ if not isinstance(k, str) or not isinstance(v, str):
676
+ raise ValueError(
677
+ 'keys and values in {} must be strings'.format(value),
678
+ )
679
+ out[k] = v
680
+
681
+ return out
682
+
683
+
625
684
  def check_url(
626
685
  value: str,
627
686
  pattern: Optional[str] = None,
628
687
  max_length: Optional[int] = None,
629
688
  min_length: Optional[int] = None,
630
- valid_values: Optional[list[str]] = None,
689
+ valid_values: Optional[List[str]] = None,
631
690
  ) -> Optional[str]:
632
691
  """
633
692
  Validate a URL value.
@@ -696,7 +755,7 @@ class Option(object):
696
755
  validator: Callable[[str], Any],
697
756
  default: Any,
698
757
  doc: str,
699
- environ: Optional[Union[str, list[str]]] = None,
758
+ environ: Optional[Union[str, List[str]]] = None,
700
759
  ):
701
760
  self._name = name
702
761
  self._typedesc = typedesc
@@ -776,7 +835,7 @@ def register_option(
776
835
  validator: Callable[[Any], Any],
777
836
  default: Any,
778
837
  doc: str,
779
- environ: Optional[Union[str, list[str]]] = None,
838
+ environ: Optional[Union[str, List[str]]] = None,
780
839
  ) -> None:
781
840
  """
782
841
  Register a new option.
@@ -819,7 +878,7 @@ class AttrOption(object):
819
878
  def __init__(self, name: str):
820
879
  object.__setattr__(self, '_name', name)
821
880
 
822
- def __dir__(self) -> list[str]:
881
+ def __dir__(self) -> List[str]:
823
882
  """Return list of flattened keys."""
824
883
  if self._name in _config:
825
884
  return _config[self._name].flatkeys()
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env python
2
2
  """Data value conversion utilities."""
3
- from __future__ import annotations
4
-
5
3
  from typing import Any
6
4
  from typing import List
7
5
  from typing import Optional
@@ -0,0 +1,13 @@
1
+ import sys
2
+ from typing import Any
3
+
4
+ from ..config import get_option
5
+
6
+
7
+ def log_query(query: str, args: Any = None) -> None:
8
+ """Log the query and parameters."""
9
+ if get_option('debug.queries'):
10
+ if args is None:
11
+ print('[QUERY]', query, file=sys.stderr)
12
+ else:
13
+ print('[QUERY]', query, args, file=sys.stderr)
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env python3
2
+ from typing import Any
3
+ from typing import Dict
4
+ from typing import Optional
5
+ from typing import Sequence
6
+ from typing import Union
7
+
8
+ from ..mysql import converters
9
+ from ..mysql.constants import SERVER_STATUS
10
+
11
+
12
+ Encoders = converters.Encoders
13
+
14
+
15
+ def escape(
16
+ obj: Any,
17
+ charset: str = 'utf8',
18
+ mapping: Optional[Encoders] = None,
19
+ server_status: int = 0,
20
+ binary_prefix: bool = False,
21
+ ) -> str:
22
+ """
23
+ Escape whatever value is passed.
24
+
25
+ Non-standard, for internal use; do not use this in your applications.
26
+
27
+ """
28
+ dtype = type(obj)
29
+ if dtype is str or isinstance(obj, str):
30
+ return "'{}'".format(escape_string(obj, server_status=server_status))
31
+ if dtype is bytes or dtype is bytearray or isinstance(obj, (bytes, bytearray)):
32
+ return _quote_bytes(
33
+ obj,
34
+ server_status=server_status,
35
+ binary_prefix=binary_prefix,
36
+ )
37
+ if mapping is None:
38
+ mapping = converters.encoders
39
+ return converters.escape_item(obj, charset, mapping=mapping)
40
+
41
+
42
+ def literal(
43
+ obj: Any,
44
+ charset: str = 'utf8',
45
+ encoders: Optional[Encoders] = None,
46
+ server_status: int = 0,
47
+ binary_prefix: bool = False,
48
+ ) -> str:
49
+ """
50
+ Alias for escape().
51
+
52
+ Non-standard, for internal use; do not use this in your applications.
53
+
54
+ """
55
+ return escape(
56
+ obj, charset=charset, mapping=encoders,
57
+ server_status=server_status, binary_prefix=binary_prefix,
58
+ )
59
+
60
+
61
+ def escape_string(
62
+ s: str,
63
+ server_status: int = 0,
64
+ ) -> str:
65
+ """Escape a string value."""
66
+ if server_status & SERVER_STATUS.SERVER_STATUS_NO_BACKSLASH_ESCAPES:
67
+ return s.replace("'", "''")
68
+ return converters.escape_string(s)
69
+
70
+
71
+ def _quote_bytes(
72
+ s: bytes,
73
+ server_status: int = 0,
74
+ binary_prefix: bool = False,
75
+ ) -> str:
76
+ if server_status & SERVER_STATUS.SERVER_STATUS_NO_BACKSLASH_ESCAPES:
77
+ if binary_prefix:
78
+ return "_binary X'{}'".format(s.hex())
79
+ return "X'{}'".format(s.hex())
80
+ return converters.escape_bytes(s)
81
+
82
+
83
+ def _escape_args(
84
+ args: Union[Sequence[Any], Dict[str, Any], None],
85
+ charset: str = 'utf8',
86
+ encoders: Optional[Encoders] = None,
87
+ server_status: int = 0,
88
+ binary_prefix: bool = False,
89
+ ) -> Any:
90
+ if encoders is None:
91
+ encoders = converters.encoders
92
+
93
+ if isinstance(args, (tuple, list)):
94
+ return tuple(
95
+ literal(
96
+ arg, charset=charset, encoders=encoders,
97
+ server_status=server_status,
98
+ binary_prefix=binary_prefix,
99
+ ) for arg in args
100
+ )
101
+
102
+ elif isinstance(args, dict):
103
+ return {
104
+ key: literal(
105
+ val, charset=charset, encoders=encoders,
106
+ server_status=server_status,
107
+ binary_prefix=binary_prefix,
108
+ ) for (key, val) in args.items()
109
+ }
110
+
111
+ # If it's not a dictionary let's try escaping it anyways.
112
+ # Worst case it will throw a Value error
113
+ return escape(
114
+ args, charset=charset, mapping=encoders,
115
+ server_status=server_status, binary_prefix=binary_prefix,
116
+ )
117
+
118
+
119
+ def mogrify(
120
+ query: Union[str, bytes],
121
+ args: Union[Sequence[Any], Dict[str, Any], None] = None,
122
+ charset: str = 'utf8',
123
+ encoders: Optional[Encoders] = None,
124
+ server_status: int = 0,
125
+ binary_prefix: bool = False,
126
+ ) -> Union[str, bytes]:
127
+ """
128
+ Returns the exact string sent to the database by calling the execute() method.
129
+
130
+ This method follows the extension to the DB API 2.0 followed by Psycopg.
131
+
132
+ Parameters
133
+ ----------
134
+ query : str
135
+ Query to mogrify.
136
+ args : Sequence[Any] or Dict[str, Any] or Any, optional
137
+ Parameters used with query. (optional)
138
+
139
+ Returns
140
+ -------
141
+ str : The query with argument binding applied.
142
+
143
+ """
144
+ if args:
145
+ query = query % _escape_args(
146
+ args, charset=charset,
147
+ encoders=encoders,
148
+ server_status=server_status,
149
+ binary_prefix=binary_prefix,
150
+ )
151
+ return query
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env python
2
2
  """SingleStoreDB package utilities."""
3
- from __future__ import annotations
4
-
5
3
  import collections
6
4
  import warnings
7
5
  from typing import Any
@@ -30,7 +28,7 @@ class Description(NamedTuple):
30
28
  """Column definition."""
31
29
 
32
30
  name: str
33
- type_code: str
31
+ type_code: int
34
32
  display_size: Optional[int]
35
33
  internal_size: Optional[int]
36
34
  precision: Optional[int]
@@ -192,8 +190,11 @@ _converters: Dict[
192
190
  ],
193
191
  ] = {
194
192
  'tuple': results_to_tuple,
193
+ 'tuples': results_to_tuple,
195
194
  'namedtuple': results_to_namedtuple,
195
+ 'namedtuples': results_to_namedtuple,
196
196
  'dict': results_to_dict,
197
+ 'dicts': results_to_dict,
197
198
  'dataframe': results_to_dataframe,
198
199
  }
199
200
 
@@ -17,8 +17,6 @@
17
17
  # This file originally copied from https://github.com/sassoftware/python-swat
18
18
  #
19
19
  """Dictionary that allows setting nested keys by period (.) delimited strings."""
20
- from __future__ import annotations
21
-
22
20
  import copy
23
21
  import re
24
22
  from typing import Any
@@ -26,6 +24,8 @@ from typing import Dict
26
24
  from typing import ItemsView
27
25
  from typing import Iterable
28
26
  from typing import KeysView
27
+ from typing import List
28
+ from typing import Tuple
29
29
  from typing import ValuesView
30
30
 
31
31
 
@@ -74,7 +74,7 @@ class xdict(Dict[str, Any]):
74
74
 
75
75
  """
76
76
 
77
- _dir: list[str]
77
+ _dir: List[str]
78
78
 
79
79
  def __init__(self, *args: Any, **kwargs: Any):
80
80
  super(xdict, self).__init__()
@@ -86,7 +86,7 @@ class xdict(Dict[str, Any]):
86
86
  return list(self._dir)
87
87
  return super(xdict, self).__dir__()
88
88
 
89
- def set_dir_values(self, values: list[str]) -> None:
89
+ def set_dir_values(self, values: List[str]) -> None:
90
90
  """
91
91
  Set the valid values for keys to display in tab-completion.
92
92
 
@@ -102,11 +102,11 @@ class xdict(Dict[str, Any]):
102
102
  """Set the docstring for the xdict."""
103
103
  super(xdict, self).__setattr__('__doc__', docstring)
104
104
 
105
- def __copy__(self) -> xdict:
105
+ def __copy__(self) -> 'xdict':
106
106
  """Return a copy of self."""
107
107
  return type(self)(**self)
108
108
 
109
- def __deepcopy__(self, memo: Any) -> xdict:
109
+ def __deepcopy__(self, memo: Any) -> 'xdict':
110
110
  """Return a deep copy of self."""
111
111
  out = type(self)()
112
112
  for key, value in self.items():
@@ -116,7 +116,7 @@ class xdict(Dict[str, Any]):
116
116
  return out
117
117
 
118
118
  @classmethod
119
- def from_json(cls, jsonstr: str) -> xdict:
119
+ def from_json(cls, jsonstr: str) -> 'xdict':
120
120
  """
121
121
  Create an xdict object from a JSON string.
122
122
 
@@ -309,7 +309,7 @@ class xdict(Dict[str, Any]):
309
309
  self._flatten(self, output)
310
310
  return output
311
311
 
312
- def allkeys(self) -> list[str]:
312
+ def allkeys(self) -> List[str]:
313
313
  """Return a list of all possible keys (even sub-keys) in the xdict."""
314
314
  out = set()
315
315
  for key in self.flatkeys():
@@ -321,15 +321,15 @@ class xdict(Dict[str, Any]):
321
321
  out.add(re.sub(r'\[\d+\]', r'', key))
322
322
  return list(out)
323
323
 
324
- def flatkeys(self) -> list[str]:
324
+ def flatkeys(self) -> List[str]:
325
325
  """Return a list of flattened keys in the xdict."""
326
326
  return list(self.flattened().keys())
327
327
 
328
- def flatvalues(self) -> list[Any]:
328
+ def flatvalues(self) -> List[Any]:
329
329
  """Return a list of flattened values in the xdict."""
330
330
  return list(self.flattened().values())
331
331
 
332
- def flatitems(self) -> list[tuple[str, Any]]:
332
+ def flatitems(self) -> List[Tuple[str, Any]]:
333
333
  """Return tuples of flattened key/value pairs."""
334
334
  return list(self.flattened().items())
335
335
 
@@ -341,7 +341,7 @@ class xdict(Dict[str, Any]):
341
341
  """Return iterator of flattened values."""
342
342
  return iter(self.flattened().values())
343
343
 
344
- def iterflatitems(self) -> Iterable[tuple[str, Any]]:
344
+ def iterflatitems(self) -> Iterable[Tuple[str, Any]]:
345
345
  """Return iterator of flattened items."""
346
346
  return iter(self.flattened().items())
347
347
 
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.1
2
+ Name: singlestoredb
3
+ Version: 1.0.3
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
8
+ License: Apache-2.0
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Topic :: Database
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: PyJWT
18
+ Requires-Dist: build
19
+ Requires-Dist: parsimonious
20
+ Requires-Dist: requests
21
+ Requires-Dist: setuptools
22
+ Requires-Dist: sqlparams
23
+ Requires-Dist: wheel
24
+ Provides-Extra: dataframe
25
+ Requires-Dist: ibis-singlestoredb ; extra == 'dataframe'
26
+ Provides-Extra: dbt
27
+ Requires-Dist: dbt-singlestore ; extra == 'dbt'
28
+ Provides-Extra: ed22519
29
+ Requires-Dist: PyNaCl >=1.4.0 ; extra == 'ed22519'
30
+ Provides-Extra: gssapi
31
+ Requires-Dist: gssapi ; extra == 'gssapi'
32
+ Provides-Extra: ibis
33
+ Requires-Dist: ibis-singlestoredb ; extra == 'ibis'
34
+ Provides-Extra: kerberos
35
+ Requires-Dist: gssapi ; extra == 'kerberos'
36
+ Provides-Extra: pytest
37
+ Requires-Dist: pytest ; extra == 'pytest'
38
+ Provides-Extra: rsa
39
+ Requires-Dist: cryptography ; extra == 'rsa'
40
+ Provides-Extra: sqlalchemy
41
+ Requires-Dist: sqlalchemy-singlestoredb >=1.0.0 ; extra == 'sqlalchemy'
42
+
43
+ # <img src="https://github.com/singlestore-labs/singlestoredb-python/blob/main/resources/singlestore-logo.png" height="60" valign="middle"/> SingleStoreDB Python SDK
44
+
45
+ This project contains a [DB-API 2.0](https://www.python.org/dev/peps/pep-0249/)
46
+ compatible Python interface to the SingleStore database and workspace management API.
47
+
48
+ ## Install
49
+
50
+ This package can be install from PyPI using `pip`:
51
+ ```
52
+ pip install singlestoredb
53
+ ```
54
+
55
+ ## Documentation
56
+
57
+ https://singlestore-labs.github.io/singlestoredb-python
58
+
59
+ ## Usage
60
+
61
+ Connections to the SingleStore database are made using the DB-API parameters
62
+ `host`, `port`, `user`, `password`, etc, but they may also be done using
63
+ URLs that specify these parameters as well (much like the
64
+ [SQLAlchemy](https://www.sqlalchemy.org) package).
65
+ ```
66
+ import singlestoredb as s2
67
+
68
+ # Connect using the default connector
69
+ conn = s2.connect('user:password@host:3306/db_name')
70
+
71
+ # Create a cursor
72
+ cur = conn.cursor()
73
+
74
+ # Execute SQL
75
+ cur.execute('select * from foo')
76
+
77
+ # Fetch the results
78
+ print(cur.description)
79
+ for item in cur:
80
+ print(item)
81
+
82
+ # Close the connection
83
+ conn.close()
84
+ ```
85
+
86
+ Connecting to the HTTP API is done as follows:
87
+ ```
88
+ # Use the HTTP API connector
89
+ conn = s2.connect('https://user:password@host:8080/db_name')
90
+ ```
91
+
92
+ ## Performance
93
+
94
+ While this package is based on [PyMySQL](https://github.com/PyMySQL/PyMySQL)
95
+ which is a pure Python-based MySQL connector, it adds various performance
96
+ enhancements that make it faster than most other connectors. The performance
97
+ improvements come from changes to the data conversion functions, cursor implementations,
98
+ and a C extension that is highly optimized to improve row data reading.
99
+
100
+ The package can be used both in a pure Python mode and as well as a C accelerated
101
+ mode. Generally speaking, the C accelerated version of the client can read
102
+ data 10X faster than PyMySQL, 2X faster than MySQLdb, and 1.5X faster than
103
+ mysql.connector. All of this is done without having to install any 3rd party
104
+ MySQL libraries!
105
+
106
+ Benchmarking was done with a table of 3,533,286 rows each containing a datetime,
107
+ a float, and eight character columns. The data is the same data set used in
108
+ [this article](https://www.singlestore.com/blog/how-to-get-started-with-singlestore/).
109
+ The client and server were running on the same machine and queries were made
110
+ using `fetchone`, `fetchall`, `fetchmany(1000)`, and an iterator over the cursor
111
+ object (e.g., `iter(cur)`). The results are shown below.
112
+
113
+ ### Buffered
114
+
115
+ | | PyMySQL | MySQLdb | mysql.connector | SingleStore (pure Python) | SingleStore |
116
+ |-------------------------|---------|---------|-----------------|---------------------------|-------------|
117
+ | fetchall | 37.0s | 8.7s | 5.6s | 29.0s | 3.7s |
118
+ | fetchmany(1000) | 37.4s | 9.2s | 6.2s | 29.6s | 3.6s |
119
+ | fetchone | 38.2s | 10.1s | 10.2s | 30.9s | 4.8s |
120
+ | iter(cur) | 38.3s | 9.1s | 10.2s | 30.4s | 4.4s |
121
+
122
+ ### Unbuffered
123
+
124
+ | | PyMySQL | MySQLdb | mysql.connector | SingleStore (pure Python) | SingleStore |
125
+ |-------------------------|---------|---------|-----------------|---------------------------|-------------|
126
+ | fetchall | 39.0s | 6.5s | 5.5s | 30.3s | 5.5s |
127
+ | fetchmany(1000) | 39.4s | 7.0s | 6.0s | 30.4s | 4.1s |
128
+ | fetchone | 34.5s | 8.9s | 10.1s | 30.8s | 6.6s |
129
+ | iter(cur) | 39.0s | 9.0s | 10.2s | 31.4s | 6.0s |
130
+
131
+
132
+ ## License
133
+
134
+ This library is licensed under the [Apache 2.0 License](https://raw.githubusercontent.com/singlestore-labs/singlestoredb-python/main/LICENSE?token=GHSAT0AAAAAABMGV6QPNR6N23BVICDYK5LAYTVK5EA).
135
+
136
+ ## Resources
137
+
138
+ * [SingleStore](https://singlestore.com)
139
+ * [Python](https://python.org)