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.
- crate/client/__init__.py +37 -0
- crate/client/_pep440.py +1 -0
- crate/client/blob.py +105 -0
- crate/client/connection.py +221 -0
- crate/client/converter.py +143 -0
- crate/client/cursor.py +321 -0
- crate/client/exceptions.py +101 -0
- crate/client/http.py +694 -0
- crate/testing/__init__.py +0 -0
- crate/testing/layer.py +428 -0
- crate/testing/util.py +95 -0
- crate-2.0.0.dev0.dist-info/LICENSE +178 -0
- crate-2.0.0.dev0.dist-info/METADATA +168 -0
- crate-2.0.0.dev0.dist-info/NOTICE +24 -0
- crate-2.0.0.dev0.dist-info/RECORD +17 -0
- crate-2.0.0.dev0.dist-info/WHEEL +5 -0
- crate-2.0.0.dev0.dist-info/top_level.txt +1 -0
crate/client/__init__.py
ADDED
@@ -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"
|
crate/client/_pep440.py
ADDED
@@ -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)
|