crate 2.0.0.dev0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)