crate 0.29.0__py2.py3-none-any.whl → 0.30.1__py2.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 +1 -1
- crate/client/_pep440.py +501 -0
- crate/client/connection.py +3 -3
- crate/client/sqlalchemy/__init__.py +24 -0
- crate/client/sqlalchemy/compat/__init__.py +0 -0
- crate/client/sqlalchemy/compat/api13.py +156 -0
- crate/client/sqlalchemy/compat/core10.py +264 -0
- crate/client/sqlalchemy/compat/core14.py +359 -0
- crate/client/sqlalchemy/compat/core20.py +447 -0
- crate/client/sqlalchemy/compiler.py +1 -481
- crate/client/sqlalchemy/dialect.py +32 -17
- crate/client/sqlalchemy/sa_version.py +4 -3
- crate/client/sqlalchemy/tests/__init__.py +17 -6
- crate/client/sqlalchemy/tests/array_test.py +6 -3
- crate/client/sqlalchemy/tests/bulk_test.py +7 -4
- crate/client/sqlalchemy/tests/compiler_test.py +10 -9
- crate/client/sqlalchemy/tests/connection_test.py +25 -11
- crate/client/sqlalchemy/tests/create_table_test.py +19 -16
- crate/client/sqlalchemy/tests/datetime_test.py +6 -3
- crate/client/sqlalchemy/tests/dialect_test.py +42 -13
- crate/client/sqlalchemy/tests/dict_test.py +17 -13
- crate/client/sqlalchemy/tests/function_test.py +6 -3
- crate/client/sqlalchemy/tests/insert_from_select_test.py +9 -6
- crate/client/sqlalchemy/tests/match_test.py +6 -3
- crate/client/sqlalchemy/tests/update_test.py +6 -3
- crate/client/sqlalchemy/tests/warnings_test.py +33 -0
- crate/client/test_connection.py +25 -0
- crate/client/tests.py +98 -119
- crate/testing/layer.py +1 -1
- crate/testing/settings.py +51 -0
- crate/testing/test_layer.py +188 -2
- crate/testing/tests.py +2 -38
- crate/testing/util.py +20 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/METADATA +10 -8
- crate-0.30.1.dist-info/RECORD +53 -0
- crate-0.29.0.dist-info/RECORD +0 -44
- /crate-0.29.0-py3.9-nspkg.pth → /crate-0.30.1-py3.9-nspkg.pth +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/LICENSE +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/NOTICE +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/WHEEL +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/entry_points.txt +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/namespace_packages.txt +0 -0
- {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/top_level.txt +0 -0
crate/client/__init__.py
CHANGED
crate/client/_pep440.py
ADDED
@@ -0,0 +1,501 @@
|
|
1
|
+
"""Utility to compare pep440 compatible version strings.
|
2
|
+
|
3
|
+
The LooseVersion and StrictVersion classes that distutils provides don't
|
4
|
+
work; they don't recognize anything like alpha/beta/rc/dev versions.
|
5
|
+
|
6
|
+
This specific file has been vendored from NumPy on 2023-02-10 [1].
|
7
|
+
Its reference location is in `packaging` [2,3].
|
8
|
+
|
9
|
+
[1] https://github.com/numpy/numpy/blob/v1.25.0.dev0/numpy/compat/_pep440.py
|
10
|
+
[2] https://github.com/pypa/packaging/blob/23.0/src/packaging/_structures.py
|
11
|
+
[3] https://github.com/pypa/packaging/blob/23.0/src/packaging/version.py
|
12
|
+
"""
|
13
|
+
|
14
|
+
# Copyright (c) Donald Stufft and individual contributors.
|
15
|
+
# All rights reserved.
|
16
|
+
|
17
|
+
# Redistribution and use in source and binary forms, with or without
|
18
|
+
# modification, are permitted provided that the following conditions are met:
|
19
|
+
|
20
|
+
# 1. Redistributions of source code must retain the above copyright notice,
|
21
|
+
# this list of conditions and the following disclaimer.
|
22
|
+
|
23
|
+
# 2. Redistributions in binary form must reproduce the above copyright
|
24
|
+
# notice, this list of conditions and the following disclaimer in the
|
25
|
+
# documentation and/or other materials provided with the distribution.
|
26
|
+
|
27
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
28
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
29
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
30
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
31
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
32
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
33
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
34
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
35
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
36
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
37
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
38
|
+
|
39
|
+
import collections
|
40
|
+
import itertools
|
41
|
+
import re
|
42
|
+
|
43
|
+
|
44
|
+
__all__ = [
|
45
|
+
"parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN",
|
46
|
+
]
|
47
|
+
|
48
|
+
|
49
|
+
# BEGIN packaging/_structures.py
|
50
|
+
|
51
|
+
|
52
|
+
class Infinity:
|
53
|
+
def __repr__(self):
|
54
|
+
return "Infinity"
|
55
|
+
|
56
|
+
def __hash__(self):
|
57
|
+
return hash(repr(self))
|
58
|
+
|
59
|
+
def __lt__(self, other):
|
60
|
+
return False
|
61
|
+
|
62
|
+
def __le__(self, other):
|
63
|
+
return False
|
64
|
+
|
65
|
+
def __eq__(self, other):
|
66
|
+
return isinstance(other, self.__class__)
|
67
|
+
|
68
|
+
def __ne__(self, other):
|
69
|
+
return not isinstance(other, self.__class__)
|
70
|
+
|
71
|
+
def __gt__(self, other):
|
72
|
+
return True
|
73
|
+
|
74
|
+
def __ge__(self, other):
|
75
|
+
return True
|
76
|
+
|
77
|
+
def __neg__(self):
|
78
|
+
return NegativeInfinity
|
79
|
+
|
80
|
+
|
81
|
+
Infinity = Infinity()
|
82
|
+
|
83
|
+
|
84
|
+
class NegativeInfinity:
|
85
|
+
def __repr__(self):
|
86
|
+
return "-Infinity"
|
87
|
+
|
88
|
+
def __hash__(self):
|
89
|
+
return hash(repr(self))
|
90
|
+
|
91
|
+
def __lt__(self, other):
|
92
|
+
return True
|
93
|
+
|
94
|
+
def __le__(self, other):
|
95
|
+
return True
|
96
|
+
|
97
|
+
def __eq__(self, other):
|
98
|
+
return isinstance(other, self.__class__)
|
99
|
+
|
100
|
+
def __ne__(self, other):
|
101
|
+
return not isinstance(other, self.__class__)
|
102
|
+
|
103
|
+
def __gt__(self, other):
|
104
|
+
return False
|
105
|
+
|
106
|
+
def __ge__(self, other):
|
107
|
+
return False
|
108
|
+
|
109
|
+
def __neg__(self):
|
110
|
+
return Infinity
|
111
|
+
|
112
|
+
|
113
|
+
# BEGIN packaging/version.py
|
114
|
+
|
115
|
+
|
116
|
+
NegativeInfinity = NegativeInfinity()
|
117
|
+
|
118
|
+
_Version = collections.namedtuple(
|
119
|
+
"_Version",
|
120
|
+
["epoch", "release", "dev", "pre", "post", "local"],
|
121
|
+
)
|
122
|
+
|
123
|
+
|
124
|
+
def parse(version):
|
125
|
+
"""
|
126
|
+
Parse the given version string and return either a :class:`Version` object
|
127
|
+
or a :class:`LegacyVersion` object depending on if the given version is
|
128
|
+
a valid PEP 440 version or a legacy version.
|
129
|
+
"""
|
130
|
+
try:
|
131
|
+
return Version(version)
|
132
|
+
except InvalidVersion:
|
133
|
+
return LegacyVersion(version)
|
134
|
+
|
135
|
+
|
136
|
+
class InvalidVersion(ValueError):
|
137
|
+
"""
|
138
|
+
An invalid version was found, users should refer to PEP 440.
|
139
|
+
"""
|
140
|
+
|
141
|
+
|
142
|
+
class _BaseVersion:
|
143
|
+
|
144
|
+
def __hash__(self):
|
145
|
+
return hash(self._key)
|
146
|
+
|
147
|
+
def __lt__(self, other):
|
148
|
+
return self._compare(other, lambda s, o: s < o)
|
149
|
+
|
150
|
+
def __le__(self, other):
|
151
|
+
return self._compare(other, lambda s, o: s <= o)
|
152
|
+
|
153
|
+
def __eq__(self, other):
|
154
|
+
return self._compare(other, lambda s, o: s == o)
|
155
|
+
|
156
|
+
def __ge__(self, other):
|
157
|
+
return self._compare(other, lambda s, o: s >= o)
|
158
|
+
|
159
|
+
def __gt__(self, other):
|
160
|
+
return self._compare(other, lambda s, o: s > o)
|
161
|
+
|
162
|
+
def __ne__(self, other):
|
163
|
+
return self._compare(other, lambda s, o: s != o)
|
164
|
+
|
165
|
+
def _compare(self, other, method):
|
166
|
+
if not isinstance(other, _BaseVersion):
|
167
|
+
return NotImplemented
|
168
|
+
|
169
|
+
return method(self._key, other._key)
|
170
|
+
|
171
|
+
|
172
|
+
class LegacyVersion(_BaseVersion):
|
173
|
+
|
174
|
+
def __init__(self, version):
|
175
|
+
self._version = str(version)
|
176
|
+
self._key = _legacy_cmpkey(self._version)
|
177
|
+
|
178
|
+
def __str__(self):
|
179
|
+
return self._version
|
180
|
+
|
181
|
+
def __repr__(self):
|
182
|
+
return "<LegacyVersion({0})>".format(repr(str(self)))
|
183
|
+
|
184
|
+
@property
|
185
|
+
def public(self):
|
186
|
+
return self._version
|
187
|
+
|
188
|
+
@property
|
189
|
+
def base_version(self):
|
190
|
+
return self._version
|
191
|
+
|
192
|
+
@property
|
193
|
+
def local(self):
|
194
|
+
return None
|
195
|
+
|
196
|
+
@property
|
197
|
+
def is_prerelease(self):
|
198
|
+
return False
|
199
|
+
|
200
|
+
@property
|
201
|
+
def is_postrelease(self):
|
202
|
+
return False
|
203
|
+
|
204
|
+
|
205
|
+
_legacy_version_component_re = re.compile(
|
206
|
+
r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE,
|
207
|
+
)
|
208
|
+
|
209
|
+
_legacy_version_replacement_map = {
|
210
|
+
"pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@",
|
211
|
+
}
|
212
|
+
|
213
|
+
|
214
|
+
def _parse_version_parts(s):
|
215
|
+
for part in _legacy_version_component_re.split(s):
|
216
|
+
part = _legacy_version_replacement_map.get(part, part)
|
217
|
+
|
218
|
+
if not part or part == ".":
|
219
|
+
continue
|
220
|
+
|
221
|
+
if part[:1] in "0123456789":
|
222
|
+
# pad for numeric comparison
|
223
|
+
yield part.zfill(8)
|
224
|
+
else:
|
225
|
+
yield "*" + part
|
226
|
+
|
227
|
+
# ensure that alpha/beta/candidate are before final
|
228
|
+
yield "*final"
|
229
|
+
|
230
|
+
|
231
|
+
def _legacy_cmpkey(version):
|
232
|
+
# We hardcode an epoch of -1 here. A PEP 440 version can only have an epoch
|
233
|
+
# greater than or equal to 0. This will effectively put the LegacyVersion,
|
234
|
+
# which uses the defacto standard originally implemented by setuptools,
|
235
|
+
# as before all PEP 440 versions.
|
236
|
+
epoch = -1
|
237
|
+
|
238
|
+
# This scheme is taken from pkg_resources.parse_version setuptools prior to
|
239
|
+
# its adoption of the packaging library.
|
240
|
+
parts = []
|
241
|
+
for part in _parse_version_parts(version.lower()):
|
242
|
+
if part.startswith("*"):
|
243
|
+
# remove "-" before a prerelease tag
|
244
|
+
if part < "*final":
|
245
|
+
while parts and parts[-1] == "*final-":
|
246
|
+
parts.pop()
|
247
|
+
|
248
|
+
# remove trailing zeros from each series of numeric parts
|
249
|
+
while parts and parts[-1] == "00000000":
|
250
|
+
parts.pop()
|
251
|
+
|
252
|
+
parts.append(part)
|
253
|
+
parts = tuple(parts)
|
254
|
+
|
255
|
+
return epoch, parts
|
256
|
+
|
257
|
+
|
258
|
+
# Deliberately not anchored to the start and end of the string, to make it
|
259
|
+
# easier for 3rd party code to reuse
|
260
|
+
VERSION_PATTERN = r"""
|
261
|
+
v?
|
262
|
+
(?:
|
263
|
+
(?:(?P<epoch>[0-9]+)!)? # epoch
|
264
|
+
(?P<release>[0-9]+(?:\.[0-9]+)*) # release segment
|
265
|
+
(?P<pre> # pre-release
|
266
|
+
[-_\.]?
|
267
|
+
(?P<pre_l>(a|b|c|rc|alpha|beta|pre|preview))
|
268
|
+
[-_\.]?
|
269
|
+
(?P<pre_n>[0-9]+)?
|
270
|
+
)?
|
271
|
+
(?P<post> # post release
|
272
|
+
(?:-(?P<post_n1>[0-9]+))
|
273
|
+
|
|
274
|
+
(?:
|
275
|
+
[-_\.]?
|
276
|
+
(?P<post_l>post|rev|r)
|
277
|
+
[-_\.]?
|
278
|
+
(?P<post_n2>[0-9]+)?
|
279
|
+
)
|
280
|
+
)?
|
281
|
+
(?P<dev> # dev release
|
282
|
+
[-_\.]?
|
283
|
+
(?P<dev_l>dev)
|
284
|
+
[-_\.]?
|
285
|
+
(?P<dev_n>[0-9]+)?
|
286
|
+
)?
|
287
|
+
)
|
288
|
+
(?:\+(?P<local>[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version
|
289
|
+
"""
|
290
|
+
|
291
|
+
|
292
|
+
class Version(_BaseVersion):
|
293
|
+
|
294
|
+
_regex = re.compile(
|
295
|
+
r"^\s*" + VERSION_PATTERN + r"\s*$",
|
296
|
+
re.VERBOSE | re.IGNORECASE,
|
297
|
+
)
|
298
|
+
|
299
|
+
def __init__(self, version):
|
300
|
+
# Validate the version and parse it into pieces
|
301
|
+
match = self._regex.search(version)
|
302
|
+
if not match:
|
303
|
+
raise InvalidVersion("Invalid version: '{0}'".format(version))
|
304
|
+
|
305
|
+
# Store the parsed out pieces of the version
|
306
|
+
self._version = _Version(
|
307
|
+
epoch=int(match.group("epoch")) if match.group("epoch") else 0,
|
308
|
+
release=tuple(int(i) for i in match.group("release").split(".")),
|
309
|
+
pre=_parse_letter_version(
|
310
|
+
match.group("pre_l"),
|
311
|
+
match.group("pre_n"),
|
312
|
+
),
|
313
|
+
post=_parse_letter_version(
|
314
|
+
match.group("post_l"),
|
315
|
+
match.group("post_n1") or match.group("post_n2"),
|
316
|
+
),
|
317
|
+
dev=_parse_letter_version(
|
318
|
+
match.group("dev_l"),
|
319
|
+
match.group("dev_n"),
|
320
|
+
),
|
321
|
+
local=_parse_local_version(match.group("local")),
|
322
|
+
)
|
323
|
+
|
324
|
+
# Generate a key which will be used for sorting
|
325
|
+
self._key = _cmpkey(
|
326
|
+
self._version.epoch,
|
327
|
+
self._version.release,
|
328
|
+
self._version.pre,
|
329
|
+
self._version.post,
|
330
|
+
self._version.dev,
|
331
|
+
self._version.local,
|
332
|
+
)
|
333
|
+
|
334
|
+
def __repr__(self):
|
335
|
+
return "<Version({0})>".format(repr(str(self)))
|
336
|
+
|
337
|
+
def __str__(self):
|
338
|
+
parts = []
|
339
|
+
|
340
|
+
# Epoch
|
341
|
+
if self._version.epoch != 0:
|
342
|
+
parts.append("{0}!".format(self._version.epoch))
|
343
|
+
|
344
|
+
# Release segment
|
345
|
+
parts.append(".".join(str(x) for x in self._version.release))
|
346
|
+
|
347
|
+
# Pre-release
|
348
|
+
if self._version.pre is not None:
|
349
|
+
parts.append("".join(str(x) for x in self._version.pre))
|
350
|
+
|
351
|
+
# Post-release
|
352
|
+
if self._version.post is not None:
|
353
|
+
parts.append(".post{0}".format(self._version.post[1]))
|
354
|
+
|
355
|
+
# Development release
|
356
|
+
if self._version.dev is not None:
|
357
|
+
parts.append(".dev{0}".format(self._version.dev[1]))
|
358
|
+
|
359
|
+
# Local version segment
|
360
|
+
if self._version.local is not None:
|
361
|
+
parts.append(
|
362
|
+
"+{0}".format(".".join(str(x) for x in self._version.local))
|
363
|
+
)
|
364
|
+
|
365
|
+
return "".join(parts)
|
366
|
+
|
367
|
+
@property
|
368
|
+
def public(self):
|
369
|
+
return str(self).split("+", 1)[0]
|
370
|
+
|
371
|
+
@property
|
372
|
+
def base_version(self):
|
373
|
+
parts = []
|
374
|
+
|
375
|
+
# Epoch
|
376
|
+
if self._version.epoch != 0:
|
377
|
+
parts.append("{0}!".format(self._version.epoch))
|
378
|
+
|
379
|
+
# Release segment
|
380
|
+
parts.append(".".join(str(x) for x in self._version.release))
|
381
|
+
|
382
|
+
return "".join(parts)
|
383
|
+
|
384
|
+
@property
|
385
|
+
def local(self):
|
386
|
+
version_string = str(self)
|
387
|
+
if "+" in version_string:
|
388
|
+
return version_string.split("+", 1)[1]
|
389
|
+
|
390
|
+
@property
|
391
|
+
def is_prerelease(self):
|
392
|
+
return bool(self._version.dev or self._version.pre)
|
393
|
+
|
394
|
+
@property
|
395
|
+
def is_postrelease(self):
|
396
|
+
return bool(self._version.post)
|
397
|
+
|
398
|
+
@property
|
399
|
+
def version(self) -> tuple:
|
400
|
+
"""
|
401
|
+
PATCH: Return version tuple for backward-compatibility.
|
402
|
+
"""
|
403
|
+
return self._version.release
|
404
|
+
|
405
|
+
|
406
|
+
def _parse_letter_version(letter, number):
|
407
|
+
if letter:
|
408
|
+
# We assume there is an implicit 0 in a pre-release if there is
|
409
|
+
# no numeral associated with it.
|
410
|
+
if number is None:
|
411
|
+
number = 0
|
412
|
+
|
413
|
+
# We normalize any letters to their lower-case form
|
414
|
+
letter = letter.lower()
|
415
|
+
|
416
|
+
# We consider some words to be alternate spellings of other words and
|
417
|
+
# in those cases we want to normalize the spellings to our preferred
|
418
|
+
# spelling.
|
419
|
+
if letter == "alpha":
|
420
|
+
letter = "a"
|
421
|
+
elif letter == "beta":
|
422
|
+
letter = "b"
|
423
|
+
elif letter in ["c", "pre", "preview"]:
|
424
|
+
letter = "rc"
|
425
|
+
elif letter in ["rev", "r"]:
|
426
|
+
letter = "post"
|
427
|
+
|
428
|
+
return letter, int(number)
|
429
|
+
if not letter and number:
|
430
|
+
# We assume that if we are given a number but not given a letter,
|
431
|
+
# then this is using the implicit post release syntax (e.g., 1.0-1)
|
432
|
+
letter = "post"
|
433
|
+
|
434
|
+
return letter, int(number)
|
435
|
+
|
436
|
+
|
437
|
+
_local_version_seperators = re.compile(r"[\._-]")
|
438
|
+
|
439
|
+
|
440
|
+
def _parse_local_version(local):
|
441
|
+
"""
|
442
|
+
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
|
443
|
+
"""
|
444
|
+
if local is not None:
|
445
|
+
return tuple(
|
446
|
+
part.lower() if not part.isdigit() else int(part)
|
447
|
+
for part in _local_version_seperators.split(local)
|
448
|
+
)
|
449
|
+
|
450
|
+
|
451
|
+
def _cmpkey(epoch, release, pre, post, dev, local):
|
452
|
+
# When we compare a release version, we want to compare it with all of the
|
453
|
+
# trailing zeros removed. So we'll use a reverse the list, drop all the now
|
454
|
+
# leading zeros until we come to something non-zero, then take the rest,
|
455
|
+
# re-reverse it back into the correct order, and make it a tuple and use
|
456
|
+
# that for our sorting key.
|
457
|
+
release = tuple(
|
458
|
+
reversed(list(
|
459
|
+
itertools.dropwhile(
|
460
|
+
lambda x: x == 0,
|
461
|
+
reversed(release),
|
462
|
+
)
|
463
|
+
))
|
464
|
+
)
|
465
|
+
|
466
|
+
# We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
|
467
|
+
# We'll do this by abusing the pre-segment, but we _only_ want to do this
|
468
|
+
# if there is no pre- or a post-segment. If we have one of those, then
|
469
|
+
# the normal sorting rules will handle this case correctly.
|
470
|
+
if pre is None and post is None and dev is not None:
|
471
|
+
pre = -Infinity
|
472
|
+
# Versions without a pre-release (except as noted above) should sort after
|
473
|
+
# those with one.
|
474
|
+
elif pre is None:
|
475
|
+
pre = Infinity
|
476
|
+
|
477
|
+
# Versions without a post-segment should sort before those with one.
|
478
|
+
if post is None:
|
479
|
+
post = -Infinity
|
480
|
+
|
481
|
+
# Versions without a development segment should sort after those with one.
|
482
|
+
if dev is None:
|
483
|
+
dev = Infinity
|
484
|
+
|
485
|
+
if local is None:
|
486
|
+
# Versions without a local segment should sort before those with one.
|
487
|
+
local = -Infinity
|
488
|
+
else:
|
489
|
+
# Versions with a local segment need that segment parsed to implement
|
490
|
+
# the sorting rules in PEP440.
|
491
|
+
# - Alphanumeric segments sort before numeric segments
|
492
|
+
# - Alphanumeric segments sort lexicographically
|
493
|
+
# - Numeric segments sort numerically
|
494
|
+
# - Shorter versions sort before longer versions when the prefixes
|
495
|
+
# match exactly
|
496
|
+
local = tuple(
|
497
|
+
(i, "") if isinstance(i, int) else (-Infinity, i)
|
498
|
+
for i in local
|
499
|
+
)
|
500
|
+
|
501
|
+
return epoch, release, pre, post, dev, local
|
crate/client/connection.py
CHANGED
@@ -23,7 +23,7 @@ from .cursor import Cursor
|
|
23
23
|
from .exceptions import ProgrammingError, ConnectionError
|
24
24
|
from .http import Client
|
25
25
|
from .blob import BlobContainer
|
26
|
-
from
|
26
|
+
from ._pep440 import Version
|
27
27
|
|
28
28
|
|
29
29
|
class Connection(object):
|
@@ -192,12 +192,12 @@ class Connection(object):
|
|
192
192
|
for server in self.client.active_servers:
|
193
193
|
try:
|
194
194
|
_, _, version = self.client.server_infos(server)
|
195
|
-
version =
|
195
|
+
version = Version(version)
|
196
196
|
except (ValueError, ConnectionError):
|
197
197
|
continue
|
198
198
|
if not lowest or version < lowest:
|
199
199
|
lowest = version
|
200
|
-
return lowest or
|
200
|
+
return lowest or Version('0.0.0')
|
201
201
|
|
202
202
|
def __repr__(self):
|
203
203
|
return '<Connection {0}>'.format(repr(self.client))
|
@@ -19,7 +19,31 @@
|
|
19
19
|
# with Crate these terms will supersede the license and you may use the
|
20
20
|
# software solely pursuant to the terms of the relevant commercial agreement.
|
21
21
|
|
22
|
+
from .compat.api13 import monkeypatch_add_exec_driver_sql
|
22
23
|
from .dialect import CrateDialect
|
24
|
+
from .sa_version import SA_1_4, SA_VERSION
|
25
|
+
|
26
|
+
|
27
|
+
if SA_VERSION < SA_1_4:
|
28
|
+
import textwrap
|
29
|
+
import warnings
|
30
|
+
|
31
|
+
# SQLAlchemy 1.3 is effectively EOL.
|
32
|
+
SA13_DEPRECATION_WARNING = textwrap.dedent("""
|
33
|
+
WARNING: SQLAlchemy 1.3 is effectively EOL.
|
34
|
+
|
35
|
+
SQLAlchemy 1.3 is EOL since 2023-01-27.
|
36
|
+
Future versions of the CrateDB SQLAlchemy dialect will drop support for SQLAlchemy 1.3.
|
37
|
+
It is recommended that you transition to using SQLAlchemy 1.4 or 2.0:
|
38
|
+
|
39
|
+
- https://docs.sqlalchemy.org/en/14/changelog/migration_14.html
|
40
|
+
- https://docs.sqlalchemy.org/en/20/changelog/migration_20.html
|
41
|
+
""".lstrip("\n"))
|
42
|
+
warnings.warn(message=SA13_DEPRECATION_WARNING, category=DeprecationWarning)
|
43
|
+
|
44
|
+
# SQLAlchemy 1.3 does not have the `exec_driver_sql` method, so add it.
|
45
|
+
monkeypatch_add_exec_driver_sql()
|
46
|
+
|
23
47
|
|
24
48
|
__all__ = [
|
25
49
|
CrateDialect,
|
File without changes
|