crate 1.0.1__tar.gz → 2.0.0__tar.gz

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.
Files changed (48) hide show
  1. {crate-1.0.1 → crate-2.0.0}/CHANGES.rst +20 -0
  2. {crate-1.0.1 → crate-2.0.0}/DEVELOP.rst +9 -12
  3. {crate-1.0.1 → crate-2.0.0}/PKG-INFO +19 -6
  4. {crate-1.0.1 → crate-2.0.0}/docs/by-example/http.rst +1 -1
  5. {crate-1.0.1 → crate-2.0.0}/pyproject.toml +7 -0
  6. {crate-1.0.1 → crate-2.0.0}/setup.py +4 -4
  7. {crate-1.0.1 → crate-2.0.0}/src/crate/client/__init__.py +1 -1
  8. {crate-1.0.1 → crate-2.0.0}/src/crate/client/http.py +55 -27
  9. {crate-1.0.1 → crate-2.0.0}/src/crate.egg-info/PKG-INFO +19 -6
  10. {crate-1.0.1 → crate-2.0.0}/src/crate.egg-info/requires.txt +4 -4
  11. {crate-1.0.1 → crate-2.0.0}/CONTRIBUTING.rst +0 -0
  12. {crate-1.0.1 → crate-2.0.0}/LICENSE +0 -0
  13. {crate-1.0.1 → crate-2.0.0}/MANIFEST.in +0 -0
  14. {crate-1.0.1 → crate-2.0.0}/NOTICE +0 -0
  15. {crate-1.0.1 → crate-2.0.0}/README.rst +0 -0
  16. {crate-1.0.1 → crate-2.0.0}/docs/_extra/robots.txt +0 -0
  17. {crate-1.0.1 → crate-2.0.0}/docs/blobs.rst +0 -0
  18. {crate-1.0.1 → crate-2.0.0}/docs/by-example/blob.rst +0 -0
  19. {crate-1.0.1 → crate-2.0.0}/docs/by-example/client.rst +0 -0
  20. {crate-1.0.1 → crate-2.0.0}/docs/by-example/connection.rst +0 -0
  21. {crate-1.0.1 → crate-2.0.0}/docs/by-example/cursor.rst +0 -0
  22. {crate-1.0.1 → crate-2.0.0}/docs/by-example/https.rst +0 -0
  23. {crate-1.0.1 → crate-2.0.0}/docs/by-example/index.rst +0 -0
  24. {crate-1.0.1 → crate-2.0.0}/docs/conf.py +0 -0
  25. {crate-1.0.1 → crate-2.0.0}/docs/connect.rst +0 -0
  26. {crate-1.0.1 → crate-2.0.0}/docs/data-types.rst +0 -0
  27. {crate-1.0.1 → crate-2.0.0}/docs/docutils.conf +0 -0
  28. {crate-1.0.1 → crate-2.0.0}/docs/getting-started.rst +0 -0
  29. {crate-1.0.1 → crate-2.0.0}/docs/index-all.rst +0 -0
  30. {crate-1.0.1 → crate-2.0.0}/docs/index.rst +0 -0
  31. {crate-1.0.1 → crate-2.0.0}/docs/other-options.rst +0 -0
  32. {crate-1.0.1 → crate-2.0.0}/docs/query.rst +0 -0
  33. {crate-1.0.1 → crate-2.0.0}/docs/requirements.txt +0 -0
  34. {crate-1.0.1 → crate-2.0.0}/docs/sqlalchemy.rst +0 -0
  35. {crate-1.0.1 → crate-2.0.0}/requirements.txt +0 -0
  36. {crate-1.0.1 → crate-2.0.0}/setup.cfg +0 -0
  37. {crate-1.0.1 → crate-2.0.0}/src/crate/client/_pep440.py +0 -0
  38. {crate-1.0.1 → crate-2.0.0}/src/crate/client/blob.py +0 -0
  39. {crate-1.0.1 → crate-2.0.0}/src/crate/client/connection.py +0 -0
  40. {crate-1.0.1 → crate-2.0.0}/src/crate/client/converter.py +0 -0
  41. {crate-1.0.1 → crate-2.0.0}/src/crate/client/cursor.py +0 -0
  42. {crate-1.0.1 → crate-2.0.0}/src/crate/client/exceptions.py +0 -0
  43. {crate-1.0.1 → crate-2.0.0}/src/crate/testing/__init__.py +0 -0
  44. {crate-1.0.1 → crate-2.0.0}/src/crate/testing/layer.py +0 -0
  45. {crate-1.0.1 → crate-2.0.0}/src/crate/testing/util.py +0 -0
  46. {crate-1.0.1 → crate-2.0.0}/src/crate.egg-info/SOURCES.txt +0 -0
  47. {crate-1.0.1 → crate-2.0.0}/src/crate.egg-info/dependency_links.txt +0 -0
  48. {crate-1.0.1 → crate-2.0.0}/src/crate.egg-info/top_level.txt +0 -0
@@ -5,6 +5,26 @@ Changes for crate
5
5
  Unreleased
6
6
  ==========
7
7
 
8
+ 2025/01/30 2.0.0
9
+ ================
10
+
11
+ - Switched JSON encoder to use the `orjson`_ library, to improve JSON
12
+ marshalling performance. Thanks, @widmogrod.
13
+
14
+ orjson is fast and in some spots even more correct when compared against
15
+ Python's stdlib ``json`` module. Contrary to the stdlib variant, orjson
16
+ will serialize to ``bytes`` instead of ``str``. When sending data to CrateDB,
17
+ ``crate-python`` uses a custom encoder to add support for additional data
18
+ types.
19
+
20
+ - Python's ``Decimal`` type will be serialized to ``str``.
21
+ - Python's ``dt.datetime`` and ``dt.date`` types will be serialized to
22
+ ``int`` (``LONG``) after converting to milliseconds since epoch, to
23
+ optimally accommodate CrateDB's `TIMESTAMP`_ representation.
24
+ - NumPy's data types will be handled by ``orjson`` without any ado.
25
+
26
+ .. _orjson: https://github.com/ijl/orjson
27
+ .. _TIMESTAMP: https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#type-timestamp
8
28
 
9
29
  2024/11/23 1.0.1
10
30
  ================
@@ -5,7 +5,7 @@ CrateDB Python developer guide
5
5
  Setup
6
6
  =====
7
7
 
8
- Optionally install Python package and project manager ``uv``,
8
+ Optionally install Python package and project manager `uv`_,
9
9
  in order to significantly speed up the package installation::
10
10
 
11
11
  {apt,brew,pip,zypper} install uv
@@ -67,16 +67,11 @@ To inspect the whole list of test cases, run::
67
67
 
68
68
  bin/test --list-tests
69
69
 
70
- You can run the tests against multiple Python interpreters with `tox`_::
71
-
72
- tox
73
-
74
- To do this, you will need the respective Python interpreter versions available
75
- on your ``$PATH``.
76
-
77
- To run against a single interpreter, you can also invoke::
78
-
79
- tox -e py37
70
+ The CI setup on GitHub Actions (GHA) provides a full test matrix covering
71
+ relevant Python versions. You can invoke the software tests against a specific
72
+ Python interpreter or multiple `Python versions`_ on your workstation using
73
+ `uv`_, by supplying the ``--python`` command-line option, or by defining the
74
+ `UV_PYTHON`_ environment variable prior to invoking ``source bootstrap.sh``.
80
75
 
81
76
  *Note*: Before running the tests, make sure to stop all CrateDB instances which
82
77
  are listening on the default CrateDB transport port to avoid side effects with
@@ -168,12 +163,14 @@ nothing special you need to do to get the live docs to update.
168
163
  .. _@crate/docs: https://github.com/orgs/crate/teams/docs
169
164
  .. _buildout: https://pypi.python.org/pypi/zc.buildout
170
165
  .. _PyPI: https://pypi.python.org/pypi
166
+ .. _Python versions: https://docs.astral.sh/uv/concepts/python-versions/
171
167
  .. _Read the Docs: http://readthedocs.org
172
168
  .. _ReStructuredText: http://docutils.sourceforge.net/rst.html
173
169
  .. _Sphinx: http://sphinx-doc.org/
174
170
  .. _tests/assets/pki/*.pem: https://github.com/crate/crate-python/tree/main/tests/assets/pki
175
- .. _tox: http://testrun.org/tox/latest/
176
171
  .. _twine: https://pypi.python.org/pypi/twine
177
172
  .. _useful command-line options for zope-testrunner: https://pypi.org/project/zope.testrunner/#some-useful-command-line-options-to-get-you-started
173
+ .. _uv: https://docs.astral.sh/uv/
174
+ .. _UV_PYTHON: https://docs.astral.sh/uv/configuration/environment/#uv_python
178
175
  .. _versions hosted on ReadTheDocs: https://readthedocs.org/projects/crate-python/versions/
179
176
  .. _zope.testrunner: https://pypi.org/project/zope.testrunner/
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: crate
3
- Version: 1.0.1
3
+ Version: 2.0.0
4
4
  Summary: CrateDB Python Client
5
5
  Home-page: https://github.com/crate/crate-python
6
6
  Author: Crate.io
@@ -29,6 +29,7 @@ Requires-Python: >=3.6
29
29
  Description-Content-Type: text/x-rst
30
30
  License-File: LICENSE
31
31
  License-File: NOTICE
32
+ Requires-Dist: orjson<4
32
33
  Requires-Dist: urllib3
33
34
  Requires-Dist: verlib2
34
35
  Provides-Extra: doc
@@ -38,15 +39,27 @@ Provides-Extra: test
38
39
  Requires-Dist: backports.zoneinfo<1; python_version < "3.9" and extra == "test"
39
40
  Requires-Dist: certifi; extra == "test"
40
41
  Requires-Dist: createcoverage<2,>=1; extra == "test"
41
- Requires-Dist: mypy<1.14; extra == "test"
42
- Requires-Dist: poethepoet<0.31; extra == "test"
43
- Requires-Dist: ruff<0.8; extra == "test"
42
+ Requires-Dist: mypy<1.15; extra == "test"
43
+ Requires-Dist: poethepoet<0.33; extra == "test"
44
+ Requires-Dist: ruff<0.10; extra == "test"
44
45
  Requires-Dist: stopit<2,>=1.1.2; extra == "test"
45
- Requires-Dist: tox<5,>=3; extra == "test"
46
46
  Requires-Dist: pytz; extra == "test"
47
47
  Requires-Dist: zc.customdoctests<2,>=1.0.1; extra == "test"
48
48
  Requires-Dist: zope.testing<6,>=4; extra == "test"
49
49
  Requires-Dist: zope.testrunner<7,>=5; extra == "test"
50
+ Dynamic: author
51
+ Dynamic: author-email
52
+ Dynamic: classifier
53
+ Dynamic: description
54
+ Dynamic: description-content-type
55
+ Dynamic: home-page
56
+ Dynamic: keywords
57
+ Dynamic: license
58
+ Dynamic: platform
59
+ Dynamic: provides-extra
60
+ Dynamic: requires-dist
61
+ Dynamic: requires-python
62
+ Dynamic: summary
50
63
 
51
64
  =====================
52
65
  CrateDB Python Client
@@ -228,7 +228,7 @@ When connecting to non-CrateDB servers, the HttpClient will raise a ConnectionEr
228
228
  >>> http_client.server_infos(http_client._get_server())
229
229
  Traceback (most recent call last):
230
230
  ...
231
- crate.client.exceptions.ProgrammingError: Invalid server response of content-type 'text/html; charset=UTF-8':
231
+ crate.client.exceptions.ProgrammingError: Invalid server response of content-type 'text/html':
232
232
  ...
233
233
  >>> http_client.close()
234
234
 
@@ -71,6 +71,13 @@ lint.per-file-ignores."tests/*" = [
71
71
  "S106", # Possible hardcoded password assigned to argument: "password"
72
72
  "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
73
73
  ]
74
+ lint.per-file-ignores."src/crate/client/{connection.py,http.py}" = [
75
+ "A004", # Import `ConnectionError` is shadowing a Python builtin
76
+ "A005", # Import `ConnectionError` is shadowing a Python builtin
77
+ ]
78
+ lint.per-file-ignores."tests/client/test_http.py" = [
79
+ "A004", # Import `ConnectionError` is shadowing a Python builtin
80
+ ]
74
81
 
75
82
 
76
83
  # ===================
@@ -54,6 +54,7 @@ setup(
54
54
  packages=find_namespace_packages("src"),
55
55
  package_dir={"": "src"},
56
56
  install_requires=[
57
+ "orjson<4",
57
58
  "urllib3",
58
59
  "verlib2",
59
60
  ],
@@ -66,11 +67,10 @@ setup(
66
67
  'backports.zoneinfo<1; python_version<"3.9"',
67
68
  "certifi",
68
69
  "createcoverage>=1,<2",
69
- "mypy<1.14",
70
- "poethepoet<0.31",
71
- "ruff<0.8",
70
+ "mypy<1.15",
71
+ "poethepoet<0.33",
72
+ "ruff<0.10",
72
73
  "stopit>=1.1.2,<2",
73
- "tox>=3,<5",
74
74
  "pytz",
75
75
  "zc.customdoctests>=1.0.1,<2",
76
76
  "zope.testing>=4,<6",
@@ -29,7 +29,7 @@ __all__ = [
29
29
 
30
30
  # version string read from setup.py using a regex. Take care not to break the
31
31
  # regex!
32
- __version__ = "1.0.1"
32
+ __version__ = "2.0.0"
33
33
 
34
34
  # codeql[py/unused-global-variable]
35
35
  apilevel = "2.0"
@@ -21,22 +21,22 @@
21
21
 
22
22
 
23
23
  import calendar
24
+ import datetime as dt
24
25
  import heapq
25
26
  import io
26
- import json
27
27
  import logging
28
28
  import os
29
29
  import re
30
30
  import socket
31
31
  import ssl
32
32
  import threading
33
+ import typing as t
33
34
  from base64 import b64encode
34
- from datetime import date, datetime, timezone
35
35
  from decimal import Decimal
36
36
  from time import time
37
37
  from urllib.parse import urlparse
38
- from uuid import UUID
39
38
 
39
+ import orjson
40
40
  import urllib3
41
41
  from urllib3 import connection_from_url
42
42
  from urllib3.connection import HTTPConnection
@@ -86,25 +86,53 @@ def super_len(o):
86
86
  return None
87
87
 
88
88
 
89
- class CrateJsonEncoder(json.JSONEncoder):
90
- epoch_aware = datetime(1970, 1, 1, tzinfo=timezone.utc)
91
- epoch_naive = datetime(1970, 1, 1)
92
-
93
- def default(self, o):
94
- if isinstance(o, (Decimal, UUID)):
95
- return str(o)
96
- if isinstance(o, datetime):
97
- if o.tzinfo is not None:
98
- delta = o - self.epoch_aware
99
- else:
100
- delta = o - self.epoch_naive
101
- return int(
102
- delta.microseconds / 1000.0
103
- + (delta.seconds + delta.days * 24 * 3600) * 1000.0
104
- )
105
- if isinstance(o, date):
106
- return calendar.timegm(o.timetuple()) * 1000
107
- return json.JSONEncoder.default(self, o)
89
+ epoch_aware = dt.datetime(1970, 1, 1, tzinfo=dt.timezone.utc)
90
+ epoch_naive = dt.datetime(1970, 1, 1)
91
+
92
+
93
+ def json_encoder(obj: t.Any) -> t.Union[int, str]:
94
+ """
95
+ Encoder function for orjson, with additional type support.
96
+
97
+ - Python's `Decimal` type will be serialized to `str`.
98
+ - Python's `dt.datetime` and `dt.date` types will be
99
+ serialized to `int` after converting to milliseconds
100
+ since epoch.
101
+
102
+ https://github.com/ijl/orjson#default
103
+ https://cratedb.com/docs/crate/reference/en/latest/general/ddl/data-types.html#type-timestamp
104
+ """
105
+ if isinstance(obj, Decimal):
106
+ return str(obj)
107
+ if isinstance(obj, dt.datetime):
108
+ if obj.tzinfo is not None:
109
+ delta = obj - epoch_aware
110
+ else:
111
+ delta = obj - epoch_naive
112
+ return int(
113
+ delta.microseconds / 1000.0
114
+ + (delta.seconds + delta.days * 24 * 3600) * 1000.0
115
+ )
116
+ if isinstance(obj, dt.date):
117
+ return calendar.timegm(obj.timetuple()) * 1000
118
+ raise TypeError
119
+
120
+
121
+ def json_dumps(obj: t.Any) -> bytes:
122
+ """
123
+ Serialize to JSON format, using `orjson`, with additional type support.
124
+
125
+ https://github.com/ijl/orjson
126
+ """
127
+ return orjson.dumps(
128
+ obj,
129
+ default=json_encoder,
130
+ option=(
131
+ orjson.OPT_PASSTHROUGH_DATETIME
132
+ | orjson.OPT_NON_STR_KEYS
133
+ | orjson.OPT_SERIALIZE_NUMPY
134
+ ),
135
+ )
108
136
 
109
137
 
110
138
  class Server:
@@ -180,7 +208,7 @@ class Server:
180
208
 
181
209
  def _json_from_response(response):
182
210
  try:
183
- return json.loads(response.data.decode("utf-8"))
211
+ return orjson.loads(response.data)
184
212
  except ValueError as ex:
185
213
  raise ProgrammingError(
186
214
  "Invalid server response of content-type '{}':\n{}".format(
@@ -223,7 +251,7 @@ def _raise_for_status_real(response):
223
251
  if response.status == 503:
224
252
  raise ConnectionError(message)
225
253
  if response.headers.get("content-type", "").startswith("application/json"):
226
- data = json.loads(response.data.decode("utf-8"))
254
+ data = orjson.loads(response.data)
227
255
  error = data.get("error", {})
228
256
  error_trace = data.get("error_trace", None)
229
257
  if "results" in data:
@@ -323,7 +351,7 @@ def _update_pool_kwargs_for_ssl_minimum_version(server, kwargs):
323
351
  kwargs["ssl_minimum_version"] = ssl.TLSVersion.MINIMUM_SUPPORTED
324
352
 
325
353
 
326
- def _create_sql_payload(stmt, args, bulk_args):
354
+ def _create_sql_payload(stmt, args, bulk_args) -> bytes:
327
355
  if not isinstance(stmt, str):
328
356
  raise ValueError("stmt is not a string")
329
357
  if args and bulk_args:
@@ -334,7 +362,7 @@ def _create_sql_payload(stmt, args, bulk_args):
334
362
  data["args"] = args
335
363
  if bulk_args:
336
364
  data["bulk_args"] = bulk_args
337
- return json.dumps(data, cls=CrateJsonEncoder)
365
+ return json_dumps(data)
338
366
 
339
367
 
340
368
  def _get_socket_opts(
@@ -670,7 +698,7 @@ class Client:
670
698
  # if this is the last server raise exception, otherwise try next
671
699
  if not self._active_servers:
672
700
  raise ConnectionError(
673
- ("No more Servers available, " "exception from last server: %s")
701
+ ("No more Servers available, exception from last server: %s")
674
702
  % message
675
703
  )
676
704
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: crate
3
- Version: 1.0.1
3
+ Version: 2.0.0
4
4
  Summary: CrateDB Python Client
5
5
  Home-page: https://github.com/crate/crate-python
6
6
  Author: Crate.io
@@ -29,6 +29,7 @@ Requires-Python: >=3.6
29
29
  Description-Content-Type: text/x-rst
30
30
  License-File: LICENSE
31
31
  License-File: NOTICE
32
+ Requires-Dist: orjson<4
32
33
  Requires-Dist: urllib3
33
34
  Requires-Dist: verlib2
34
35
  Provides-Extra: doc
@@ -38,15 +39,27 @@ Provides-Extra: test
38
39
  Requires-Dist: backports.zoneinfo<1; python_version < "3.9" and extra == "test"
39
40
  Requires-Dist: certifi; extra == "test"
40
41
  Requires-Dist: createcoverage<2,>=1; extra == "test"
41
- Requires-Dist: mypy<1.14; extra == "test"
42
- Requires-Dist: poethepoet<0.31; extra == "test"
43
- Requires-Dist: ruff<0.8; extra == "test"
42
+ Requires-Dist: mypy<1.15; extra == "test"
43
+ Requires-Dist: poethepoet<0.33; extra == "test"
44
+ Requires-Dist: ruff<0.10; extra == "test"
44
45
  Requires-Dist: stopit<2,>=1.1.2; extra == "test"
45
- Requires-Dist: tox<5,>=3; extra == "test"
46
46
  Requires-Dist: pytz; extra == "test"
47
47
  Requires-Dist: zc.customdoctests<2,>=1.0.1; extra == "test"
48
48
  Requires-Dist: zope.testing<6,>=4; extra == "test"
49
49
  Requires-Dist: zope.testrunner<7,>=5; extra == "test"
50
+ Dynamic: author
51
+ Dynamic: author-email
52
+ Dynamic: classifier
53
+ Dynamic: description
54
+ Dynamic: description-content-type
55
+ Dynamic: home-page
56
+ Dynamic: keywords
57
+ Dynamic: license
58
+ Dynamic: platform
59
+ Dynamic: provides-extra
60
+ Dynamic: requires-dist
61
+ Dynamic: requires-python
62
+ Dynamic: summary
50
63
 
51
64
  =====================
52
65
  CrateDB Python Client
@@ -1,3 +1,4 @@
1
+ orjson<4
1
2
  urllib3
2
3
  verlib2
3
4
 
@@ -8,11 +9,10 @@ sphinx<9,>=3.5
8
9
  [test]
9
10
  certifi
10
11
  createcoverage<2,>=1
11
- mypy<1.14
12
- poethepoet<0.31
13
- ruff<0.8
12
+ mypy<1.15
13
+ poethepoet<0.33
14
+ ruff<0.10
14
15
  stopit<2,>=1.1.2
15
- tox<5,>=3
16
16
  pytz
17
17
  zc.customdoctests<2,>=1.0.1
18
18
  zope.testing<6,>=4
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes