crate 2.0.0.dev0__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.
@@ -0,0 +1,37 @@
1
+ # -*- coding: utf-8; -*-
2
+ #
3
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
+ # license agreements. See the NOTICE file distributed with this work for
5
+ # additional information regarding copyright ownership. Crate licenses
6
+ # this file to you under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # However, if you have executed another commercial license agreement
19
+ # with Crate these terms will supersede the license and you may use the
20
+ # software solely pursuant to the terms of the relevant commercial agreement.
21
+
22
+ from .connection import Connection as connect
23
+ from .exceptions import Error
24
+
25
+ __all__ = [
26
+ "connect",
27
+ "Error",
28
+ ]
29
+
30
+ # version string read from setup.py using a regex. Take care not to break the
31
+ # regex!
32
+ __version__ = "2.0.0.dev0"
33
+
34
+ # codeql[py/unused-global-variable]
35
+ apilevel = "2.0"
36
+ threadsafety = 1
37
+ paramstyle = "qmark"
@@ -0,0 +1 @@
1
+ from verlib2 import Version # noqa: F401
crate/client/blob.py ADDED
@@ -0,0 +1,105 @@
1
+ # -*- coding: utf-8; -*-
2
+ #
3
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
+ # license agreements. See the NOTICE file distributed with this work for
5
+ # additional information regarding copyright ownership. Crate licenses
6
+ # this file to you under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # However, if you have executed another commercial license agreement
19
+ # with Crate these terms will supersede the license and you may use the
20
+ # software solely pursuant to the terms of the relevant commercial agreement.
21
+
22
+ import hashlib
23
+
24
+
25
+ class BlobContainer:
26
+ """class that represents a blob collection in crate.
27
+
28
+ can be used to download, upload and delete blobs
29
+ """
30
+
31
+ def __init__(self, container_name, connection):
32
+ self.container_name = container_name
33
+ self.conn = connection
34
+
35
+ def _compute_digest(self, f):
36
+ f.seek(0)
37
+ m = hashlib.sha1() # noqa: S324
38
+ while True:
39
+ d = f.read(1024 * 32)
40
+ if not d:
41
+ break
42
+ m.update(d)
43
+ f.seek(0)
44
+ return m.hexdigest()
45
+
46
+ def put(self, f, digest=None):
47
+ """
48
+ Upload a blob
49
+
50
+ :param f:
51
+ File object to be uploaded (required to support seek if digest is
52
+ not provided).
53
+ :param digest:
54
+ Optional SHA-1 hex digest of the file contents. Gets computed
55
+ before actual upload if not provided, which requires an extra file
56
+ read.
57
+ :return:
58
+ The hex digest of the uploaded blob if not provided in the call.
59
+ Otherwise a boolean indicating if the blob has been newly created.
60
+ """
61
+
62
+ if digest:
63
+ actual_digest = digest
64
+ else:
65
+ actual_digest = self._compute_digest(f)
66
+
67
+ created = self.conn.client.blob_put(
68
+ self.container_name, actual_digest, f
69
+ )
70
+ if digest:
71
+ return created
72
+ return actual_digest
73
+
74
+ def get(self, digest, chunk_size=1024 * 128):
75
+ """
76
+ Return the contents of a blob
77
+
78
+ :param digest: the hex digest of the blob to return
79
+ :param chunk_size: the size of the chunks returned on each iteration
80
+ :return: generator returning chunks of data
81
+ """
82
+ return self.conn.client.blob_get(
83
+ self.container_name, digest, chunk_size
84
+ )
85
+
86
+ def delete(self, digest):
87
+ """
88
+ Delete a blob
89
+
90
+ :param digest: the hex digest of the blob to be deleted
91
+ :return: True if blob existed
92
+ """
93
+ return self.conn.client.blob_del(self.container_name, digest)
94
+
95
+ def exists(self, digest):
96
+ """
97
+ Check if a blob exists
98
+
99
+ :param digest: Hex digest of the blob
100
+ :return: Boolean indicating existence of the blob
101
+ """
102
+ return self.conn.client.blob_exists(self.container_name, digest)
103
+
104
+ def __repr__(self):
105
+ return "<BlobContainer '{0}'>".format(self.container_name)
@@ -0,0 +1,221 @@
1
+ # -*- coding: utf-8; -*-
2
+ #
3
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
+ # license agreements. See the NOTICE file distributed with this work for
5
+ # additional information regarding copyright ownership. Crate licenses
6
+ # this file to you under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # However, if you have executed another commercial license agreement
19
+ # with Crate these terms will supersede the license and you may use the
20
+ # software solely pursuant to the terms of the relevant commercial agreement.
21
+
22
+ from verlib2 import Version
23
+
24
+ from .blob import BlobContainer
25
+ from .cursor import Cursor
26
+ from .exceptions import ConnectionError, ProgrammingError
27
+ from .http import Client
28
+
29
+
30
+ class Connection:
31
+ def __init__(
32
+ self,
33
+ servers=None,
34
+ timeout=None,
35
+ backoff_factor=0,
36
+ client=None,
37
+ verify_ssl_cert=True,
38
+ ca_cert=None,
39
+ error_trace=False,
40
+ cert_file=None,
41
+ key_file=None,
42
+ ssl_relax_minimum_version=False,
43
+ username=None,
44
+ password=None,
45
+ schema=None,
46
+ pool_size=None,
47
+ socket_keepalive=True,
48
+ socket_tcp_keepidle=None,
49
+ socket_tcp_keepintvl=None,
50
+ socket_tcp_keepcnt=None,
51
+ converter=None,
52
+ time_zone=None,
53
+ ):
54
+ """
55
+ :param servers:
56
+ either a string in the form of '<hostname>:<port>'
57
+ or a list of servers in the form of ['<hostname>:<port>', '...']
58
+ :param timeout:
59
+ (optional)
60
+ define the retry timeout for unreachable servers in seconds
61
+ :param backoff_factor:
62
+ (optional)
63
+ define the retry interval for unreachable servers in seconds
64
+ :param client:
65
+ (optional - for testing)
66
+ client used to communicate with crate.
67
+ :param verify_ssl_cert:
68
+ if set to ``False``, disable SSL server certificate verification.
69
+ defaults to ``True``
70
+ :param ca_cert:
71
+ a path to a CA certificate to use when verifying the SSL server
72
+ certificate.
73
+ :param error_trace:
74
+ if set to ``True`` return a whole stacktrace of any server error if
75
+ one occurs
76
+ :param cert_file:
77
+ a path to the client certificate to present to the server.
78
+ :param key_file:
79
+ a path to the client key to use when communicating with the server.
80
+ :param username:
81
+ the username in the database.
82
+ :param password:
83
+ the password of the user in the database.
84
+ :param pool_size:
85
+ (optional)
86
+ Number of connections to save that can be reused.
87
+ More than 1 is useful in multithreaded situations.
88
+ :param socket_keepalive:
89
+ (optional, defaults to ``True``)
90
+ Enable TCP keepalive on socket level.
91
+ :param socket_tcp_keepidle:
92
+ (optional)
93
+ Set the ``TCP_KEEPIDLE`` socket option, which overrides
94
+ ``net.ipv4.tcp_keepalive_time`` kernel setting if ``socket_keepalive``
95
+ is ``True``.
96
+ :param socket_tcp_keepintvl:
97
+ (optional)
98
+ Set the ``TCP_KEEPINTVL`` socket option, which overrides
99
+ ``net.ipv4.tcp_keepalive_intvl`` kernel setting if ``socket_keepalive``
100
+ is ``True``.
101
+ :param socket_tcp_keepcnt:
102
+ (optional)
103
+ Set the ``TCP_KEEPCNT`` socket option, which overrides
104
+ ``net.ipv4.tcp_keepalive_probes`` kernel setting if ``socket_keepalive``
105
+ is ``True``.
106
+ :param converter:
107
+ (optional, defaults to ``None``)
108
+ A `Converter` object to propagate to newly created `Cursor` objects.
109
+ :param time_zone:
110
+ (optional, defaults to ``None``)
111
+ A time zone specifier used for returning `TIMESTAMP` types as
112
+ timezone-aware native Python `datetime` objects.
113
+
114
+ Different data types are supported. Available options are:
115
+
116
+ - ``datetime.timezone.utc``
117
+ - ``datetime.timezone(datetime.timedelta(hours=7), name="MST")``
118
+ - ``pytz.timezone("Australia/Sydney")``
119
+ - ``zoneinfo.ZoneInfo("Australia/Sydney")``
120
+ - ``+0530`` (UTC offset in string format)
121
+
122
+ The driver always returns timezone-"aware" `datetime` objects,
123
+ with their `tzinfo` attribute set.
124
+
125
+ When `time_zone` is `None`, the returned `datetime` objects are
126
+ using Coordinated Universal Time (UTC), because CrateDB is storing
127
+ timestamp values in this format.
128
+
129
+ When `time_zone` is given, the timestamp values will be transparently
130
+ converted from UTC to use the given time zone.
131
+ """ # noqa: E501
132
+
133
+ self._converter = converter
134
+ self.time_zone = time_zone
135
+
136
+ if client:
137
+ self.client = client
138
+ else:
139
+ self.client = Client(
140
+ servers,
141
+ timeout=timeout,
142
+ backoff_factor=backoff_factor,
143
+ verify_ssl_cert=verify_ssl_cert,
144
+ ca_cert=ca_cert,
145
+ error_trace=error_trace,
146
+ cert_file=cert_file,
147
+ key_file=key_file,
148
+ ssl_relax_minimum_version=ssl_relax_minimum_version,
149
+ username=username,
150
+ password=password,
151
+ schema=schema,
152
+ pool_size=pool_size,
153
+ socket_keepalive=socket_keepalive,
154
+ socket_tcp_keepidle=socket_tcp_keepidle,
155
+ socket_tcp_keepintvl=socket_tcp_keepintvl,
156
+ socket_tcp_keepcnt=socket_tcp_keepcnt,
157
+ )
158
+ self.lowest_server_version = self._lowest_server_version()
159
+ self._closed = False
160
+
161
+ def cursor(self, **kwargs) -> Cursor:
162
+ """
163
+ Return a new Cursor Object using the connection.
164
+ """
165
+ converter = kwargs.pop("converter", self._converter)
166
+ time_zone = kwargs.pop("time_zone", self.time_zone)
167
+ if not self._closed:
168
+ return Cursor(
169
+ connection=self,
170
+ converter=converter,
171
+ time_zone=time_zone,
172
+ )
173
+ else:
174
+ raise ProgrammingError("Connection closed")
175
+
176
+ def close(self):
177
+ """
178
+ Close the connection now
179
+ """
180
+ self._closed = True
181
+ self.client.close()
182
+
183
+ def commit(self):
184
+ """
185
+ Transactions are not supported, so ``commit`` is not implemented.
186
+ """
187
+ if self._closed:
188
+ raise ProgrammingError("Connection closed")
189
+
190
+ def get_blob_container(self, container_name):
191
+ """Retrieve a BlobContainer for `container_name`
192
+
193
+ :param container_name: the name of the BLOB container.
194
+ :returns: a :class:ContainerObject
195
+ """
196
+ return BlobContainer(container_name, self)
197
+
198
+ def _lowest_server_version(self):
199
+ lowest = None
200
+ for server in self.client.active_servers:
201
+ try:
202
+ _, _, version = self.client.server_infos(server)
203
+ version = Version(version)
204
+ except (ValueError, ConnectionError):
205
+ continue
206
+ if not lowest or version < lowest:
207
+ lowest = version
208
+ return lowest or Version("0.0.0")
209
+
210
+ def __repr__(self):
211
+ return "<Connection {0}>".format(repr(self.client))
212
+
213
+ def __enter__(self):
214
+ return self
215
+
216
+ def __exit__(self, *excs):
217
+ self.close()
218
+
219
+
220
+ # For backwards compatibility and not to break existing imports
221
+ connect = Connection
@@ -0,0 +1,143 @@
1
+ # -*- coding: utf-8; -*-
2
+ #
3
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
4
+ # license agreements. See the NOTICE file distributed with this work for
5
+ # additional information regarding copyright ownership. Crate licenses
6
+ # this file to you under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License. You may
8
+ # obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
+ # License for the specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # However, if you have executed another commercial license agreement
19
+ # with Crate these terms will supersede the license and you may use the
20
+ # software solely pursuant to the terms of the relevant commercial agreement.
21
+ """
22
+ Machinery for converting CrateDB database types to native Python data types.
23
+
24
+ https://crate.io/docs/crate/reference/en/latest/interfaces/http.html#column-types
25
+ """
26
+
27
+ import datetime as dt
28
+ import ipaddress
29
+ from copy import deepcopy
30
+ from enum import Enum
31
+ from typing import Any, Callable, Dict, List, Optional, Union
32
+
33
+ ConverterFunction = Callable[[Optional[Any]], Optional[Any]]
34
+ ColTypesDefinition = Union[int, List[Union[int, "ColTypesDefinition"]]]
35
+
36
+
37
+ def _to_ipaddress(
38
+ value: Optional[str],
39
+ ) -> Optional[Union[ipaddress.IPv4Address, ipaddress.IPv6Address]]:
40
+ """
41
+ https://docs.python.org/3/library/ipaddress.html
42
+ """
43
+ if value is None:
44
+ return None
45
+ return ipaddress.ip_address(value)
46
+
47
+
48
+ def _to_datetime(value: Optional[float]) -> Optional[dt.datetime]:
49
+ """
50
+ https://docs.python.org/3/library/datetime.html
51
+ """
52
+ if value is None:
53
+ return None
54
+ return dt.datetime.fromtimestamp(value / 1e3, tz=dt.timezone.utc)
55
+
56
+
57
+ def _to_default(value: Optional[Any]) -> Optional[Any]:
58
+ return value
59
+
60
+
61
+ # Data type identifiers defined by the CrateDB HTTP interface.
62
+ # https://crate.io/docs/crate/reference/en/latest/interfaces/http.html#column-types
63
+ class DataType(Enum):
64
+ NULL = 0
65
+ NOT_SUPPORTED = 1
66
+ CHAR = 2
67
+ BOOLEAN = 3
68
+ TEXT = 4
69
+ IP = 5
70
+ DOUBLE = 6
71
+ REAL = 7
72
+ SMALLINT = 8
73
+ INTEGER = 9
74
+ BIGINT = 10
75
+ TIMESTAMP_WITH_TZ = 11
76
+ OBJECT = 12
77
+ GEOPOINT = 13
78
+ GEOSHAPE = 14
79
+ TIMESTAMP_WITHOUT_TZ = 15
80
+ UNCHECKED_OBJECT = 16
81
+ REGPROC = 19
82
+ TIME = 20
83
+ OIDVECTOR = 21
84
+ NUMERIC = 22
85
+ REGCLASS = 23
86
+ DATE = 24
87
+ BIT = 25
88
+ JSON = 26
89
+ CHARACTER = 27
90
+ ARRAY = 100
91
+
92
+
93
+ ConverterMapping = Dict[DataType, ConverterFunction]
94
+
95
+
96
+ # Map data type identifier to converter function.
97
+ _DEFAULT_CONVERTERS: ConverterMapping = {
98
+ DataType.IP: _to_ipaddress,
99
+ DataType.TIMESTAMP_WITH_TZ: _to_datetime,
100
+ DataType.TIMESTAMP_WITHOUT_TZ: _to_datetime,
101
+ }
102
+
103
+
104
+ class Converter:
105
+ def __init__(
106
+ self,
107
+ mappings: Optional[ConverterMapping] = None,
108
+ default: ConverterFunction = _to_default,
109
+ ) -> None:
110
+ self._mappings = mappings or {}
111
+ self._default = default
112
+
113
+ def get(self, type_: ColTypesDefinition) -> ConverterFunction:
114
+ if isinstance(type_, int):
115
+ return self._mappings.get(DataType(type_), self._default)
116
+ type_, inner_type = type_
117
+ if DataType(type_) is not DataType.ARRAY:
118
+ raise ValueError(
119
+ f"Data type {type_} is not implemented as collection type"
120
+ )
121
+
122
+ inner_convert = self.get(inner_type)
123
+
124
+ def convert(value: Any) -> Optional[List[Any]]:
125
+ if value is None:
126
+ return None
127
+ return [inner_convert(x) for x in value]
128
+
129
+ return convert
130
+
131
+ def set(self, type_: DataType, converter: ConverterFunction):
132
+ self._mappings[type_] = converter
133
+
134
+
135
+ class DefaultTypeConverter(Converter):
136
+ def __init__(
137
+ self, more_mappings: Optional[ConverterMapping] = None
138
+ ) -> None:
139
+ mappings: ConverterMapping = {}
140
+ mappings.update(deepcopy(_DEFAULT_CONVERTERS))
141
+ if more_mappings:
142
+ mappings.update(deepcopy(more_mappings))
143
+ super().__init__(mappings=mappings, default=_to_default)