crate 0.29.0__py2.py3-none-any.whl → 0.30.1__py2.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.
Files changed (43) hide show
  1. crate/client/__init__.py +1 -1
  2. crate/client/_pep440.py +501 -0
  3. crate/client/connection.py +3 -3
  4. crate/client/sqlalchemy/__init__.py +24 -0
  5. crate/client/sqlalchemy/compat/__init__.py +0 -0
  6. crate/client/sqlalchemy/compat/api13.py +156 -0
  7. crate/client/sqlalchemy/compat/core10.py +264 -0
  8. crate/client/sqlalchemy/compat/core14.py +359 -0
  9. crate/client/sqlalchemy/compat/core20.py +447 -0
  10. crate/client/sqlalchemy/compiler.py +1 -481
  11. crate/client/sqlalchemy/dialect.py +32 -17
  12. crate/client/sqlalchemy/sa_version.py +4 -3
  13. crate/client/sqlalchemy/tests/__init__.py +17 -6
  14. crate/client/sqlalchemy/tests/array_test.py +6 -3
  15. crate/client/sqlalchemy/tests/bulk_test.py +7 -4
  16. crate/client/sqlalchemy/tests/compiler_test.py +10 -9
  17. crate/client/sqlalchemy/tests/connection_test.py +25 -11
  18. crate/client/sqlalchemy/tests/create_table_test.py +19 -16
  19. crate/client/sqlalchemy/tests/datetime_test.py +6 -3
  20. crate/client/sqlalchemy/tests/dialect_test.py +42 -13
  21. crate/client/sqlalchemy/tests/dict_test.py +17 -13
  22. crate/client/sqlalchemy/tests/function_test.py +6 -3
  23. crate/client/sqlalchemy/tests/insert_from_select_test.py +9 -6
  24. crate/client/sqlalchemy/tests/match_test.py +6 -3
  25. crate/client/sqlalchemy/tests/update_test.py +6 -3
  26. crate/client/sqlalchemy/tests/warnings_test.py +33 -0
  27. crate/client/test_connection.py +25 -0
  28. crate/client/tests.py +98 -119
  29. crate/testing/layer.py +1 -1
  30. crate/testing/settings.py +51 -0
  31. crate/testing/test_layer.py +188 -2
  32. crate/testing/tests.py +2 -38
  33. crate/testing/util.py +20 -0
  34. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/METADATA +10 -8
  35. crate-0.30.1.dist-info/RECORD +53 -0
  36. crate-0.29.0.dist-info/RECORD +0 -44
  37. /crate-0.29.0-py3.9-nspkg.pth → /crate-0.30.1-py3.9-nspkg.pth +0 -0
  38. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/LICENSE +0 -0
  39. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/NOTICE +0 -0
  40. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/WHEEL +0 -0
  41. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/entry_points.txt +0 -0
  42. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/namespace_packages.txt +0 -0
  43. {crate-0.29.0.dist-info → crate-0.30.1.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,10 @@ from unittest.mock import MagicMock
25
25
 
26
26
  import sqlalchemy as sa
27
27
  from sqlalchemy.orm import Session
28
- from sqlalchemy.ext.declarative import declarative_base
28
+ try:
29
+ from sqlalchemy.orm import declarative_base
30
+ except ImportError:
31
+ from sqlalchemy.ext.declarative import declarative_base
29
32
 
30
33
  from crate.client.sqlalchemy.types import Craty
31
34
  from crate.client.sqlalchemy.predicates import match
@@ -52,14 +55,14 @@ class SqlAlchemyMatchTest(TestCase):
52
55
  self.assertEqual(expected_str, str(actual_expr).replace('\n', ''))
53
56
 
54
57
  def set_up_character_and_session(self):
55
- Base = declarative_base(bind=self.engine)
58
+ Base = declarative_base()
56
59
 
57
60
  class Character(Base):
58
61
  __tablename__ = 'characters'
59
62
  name = sa.Column(sa.String, primary_key=True)
60
63
  info = sa.Column(Craty)
61
64
 
62
- session = Session()
65
+ session = Session(bind=self.engine)
63
66
  return session, Character
64
67
 
65
68
  def test_simple_match(self):
@@ -27,7 +27,10 @@ from crate.client.sqlalchemy.types import Object
27
27
 
28
28
  import sqlalchemy as sa
29
29
  from sqlalchemy.orm import Session
30
- from sqlalchemy.ext.declarative import declarative_base
30
+ try:
31
+ from sqlalchemy.orm import declarative_base
32
+ except ImportError:
33
+ from sqlalchemy.ext.declarative import declarative_base
31
34
 
32
35
  from crate.client.cursor import Cursor
33
36
 
@@ -42,7 +45,7 @@ class SqlAlchemyUpdateTest(TestCase):
42
45
 
43
46
  def setUp(self):
44
47
  self.engine = sa.create_engine('crate://')
45
- self.base = declarative_base(bind=self.engine)
48
+ self.base = declarative_base()
46
49
 
47
50
  class Character(self.base):
48
51
  __tablename__ = 'characters'
@@ -53,7 +56,7 @@ class SqlAlchemyUpdateTest(TestCase):
53
56
  ts = sa.Column(sa.DateTime, onupdate=datetime.utcnow)
54
57
 
55
58
  self.character = Character
56
- self.session = Session()
59
+ self.session = Session(bind=self.engine)
57
60
 
58
61
  @patch('crate.client.connection.Cursor', FakeCursor)
59
62
  def test_onupdate_is_triggered(self):
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8; -*-
2
+ import sys
3
+ import warnings
4
+ from unittest import TestCase, skipIf
5
+
6
+ from crate.client.sqlalchemy import SA_1_4, SA_VERSION
7
+ from crate.testing.util import ExtraAssertions
8
+
9
+
10
+ class SqlAlchemyWarningsTest(TestCase, ExtraAssertions):
11
+
12
+ @skipIf(SA_VERSION >= SA_1_4, "There is no deprecation warning for "
13
+ "SQLAlchemy 1.3 on higher versions")
14
+ def test_sa13_deprecation_warning(self):
15
+ """
16
+ Verify that a `DeprecationWarning` is issued when running SQLAlchemy 1.3.
17
+
18
+ https://docs.python.org/3/library/warnings.html#testing-warnings
19
+ """
20
+ with warnings.catch_warnings(record=True) as w:
21
+
22
+ # Cause all warnings to always be triggered.
23
+ warnings.simplefilter("always")
24
+
25
+ # Trigger a warning by importing the SQLAlchemy dialect module.
26
+ # Because it already has been loaded, unload it beforehand.
27
+ del sys.modules["crate.client.sqlalchemy"]
28
+ import crate.client.sqlalchemy # noqa: F401
29
+
30
+ # Verify details of the SA13 EOL/deprecation warning.
31
+ self.assertEqual(len(w), 1)
32
+ self.assertIsSubclass(w[-1].category, DeprecationWarning)
33
+ self.assertIn("SQLAlchemy 1.3 is effectively EOL.", str(w[-1].message))
@@ -1,12 +1,37 @@
1
1
  import datetime
2
2
 
3
+ from .connection import Connection
3
4
  from .http import Client
4
5
  from crate.client import connect
5
6
  from unittest import TestCase
6
7
 
8
+ from ..testing.settings import crate_host
9
+
7
10
 
8
11
  class ConnectionTest(TestCase):
9
12
 
13
+ def test_connection_mock(self):
14
+ """
15
+ For testing purposes it is often useful to replace the client used for
16
+ communication with the CrateDB server with a stub or mock.
17
+
18
+ This can be done by passing an object of the Client class when calling the
19
+ ``connect`` method.
20
+ """
21
+
22
+ class MyConnectionClient:
23
+ active_servers = ["localhost:4200"]
24
+
25
+ def __init__(self):
26
+ pass
27
+
28
+ def server_infos(self, server):
29
+ return ("localhost:4200", "my server", "0.42.0")
30
+
31
+ connection = connect([crate_host], client=MyConnectionClient())
32
+ self.assertIsInstance(connection, Connection)
33
+ self.assertEqual(connection.client.server_infos("foo"), ('localhost:4200', 'my server', '0.42.0'))
34
+
10
35
  def test_lowest_server_version(self):
11
36
  infos = [(None, None, '0.42.3'),
12
37
  (None, None, '0.41.8'),
crate/client/tests.py CHANGED
@@ -27,7 +27,6 @@ import socket
27
27
  import unittest
28
28
  import doctest
29
29
  from pprint import pprint
30
- from datetime import datetime
31
30
  from http.server import HTTPServer, BaseHTTPRequestHandler
32
31
  import ssl
33
32
  import time
@@ -37,12 +36,11 @@ import logging
37
36
  import stopit
38
37
 
39
38
  from crate.testing.layer import CrateLayer
40
- from crate.testing.tests import crate_path, docs_path
39
+ from crate.testing.settings import \
40
+ crate_host, crate_path, crate_port, \
41
+ crate_transport_port, docs_path, localhost
41
42
  from crate.client import connect
42
- from crate.client.sqlalchemy.dialect import CrateDialect
43
- from crate.client.test_util import ClientMocked
44
43
 
45
- from . import http
46
44
  from .test_cursor import CursorTest
47
45
  from .test_connection import ConnectionTest
48
46
  from .test_http import (
@@ -56,7 +54,6 @@ from .test_http import (
56
54
  TestDefaultSchemaHeader,
57
55
  )
58
56
  from .sqlalchemy.tests import test_suite as sqlalchemy_test_suite
59
- from .sqlalchemy.types import ObjectArray
60
57
 
61
58
  log = logging.getLogger('crate.testing.layer')
62
59
  ch = logging.StreamHandler()
@@ -70,10 +67,6 @@ def cprint(s):
70
67
  print(s)
71
68
 
72
69
 
73
- def setUpMocked(test):
74
- test.globs['connection_client_mocked'] = ClientMocked()
75
-
76
-
77
70
  settings = {
78
71
  'udc.enabled': 'false',
79
72
  'lang.js.enabled': 'true',
@@ -85,11 +78,6 @@ settings = {
85
78
  'auth.host_based.config.99.user': 'me',
86
79
  'auth.host_based.config.99.method': 'password',
87
80
  }
88
- crate_port = 44209
89
- crate_transport_port = 44309
90
- local = '127.0.0.1'
91
- crate_host = "{host}:{port}".format(host=local, port=crate_port)
92
- crate_uri = "http://%s" % crate_host
93
81
  crate_layer = None
94
82
 
95
83
 
@@ -113,24 +101,16 @@ def ensure_cratedb_layer():
113
101
  crate_layer = CrateLayer('crate',
114
102
  crate_home=crate_path(),
115
103
  port=crate_port,
116
- host=local,
104
+ host=localhost,
117
105
  transport_port=crate_transport_port,
118
106
  settings=settings)
119
107
  return crate_layer
120
108
 
121
109
 
122
- def refresh(table):
123
- with connect(crate_host) as conn:
124
- cursor = conn.cursor()
125
- cursor.execute("refresh table %s" % table)
126
-
127
-
128
- def setUpWithCrateLayer(test):
129
- test.globs['HttpClient'] = http.Client
110
+ def setUpCrateLayerBaseline(test):
130
111
  test.globs['crate_host'] = crate_host
131
112
  test.globs['pprint'] = pprint
132
113
  test.globs['print'] = cprint
133
- test.globs["refresh"] = refresh
134
114
 
135
115
  with connect(crate_host) as conn:
136
116
  cursor = conn.cursor()
@@ -151,62 +131,69 @@ def setUpWithCrateLayer(test):
151
131
  # create blob table
152
132
  cursor.execute("create blob table myfiles clustered into 1 shards " +
153
133
  "with (number_of_replicas=0)")
134
+
154
135
  # create users
155
136
  cursor.execute("CREATE USER me WITH (password = 'my_secret_pw')")
156
137
  cursor.execute("CREATE USER trusted_me")
157
138
 
139
+ cursor.close()
158
140
 
159
- def setUpCrateLayerAndSqlAlchemy(test):
160
- setUpWithCrateLayer(test)
161
- import sqlalchemy as sa
162
- from sqlalchemy.ext.declarative import declarative_base
163
- from sqlalchemy.orm import sessionmaker
164
141
 
165
- with connect(crate_host) as conn:
166
- cursor = conn.cursor()
167
- cursor.execute("""create table characters (
168
- id string primary key,
169
- name string,
170
- quote string,
171
- details object,
172
- more_details array(object),
173
- INDEX name_ft using fulltext(name) with (analyzer = 'english'),
174
- INDEX quote_ft using fulltext(quote) with (analyzer = 'english')
175
- )""")
176
- cursor.execute("CREATE VIEW characters_view AS SELECT * FROM characters")
142
+ def setUpCrateLayerSqlAlchemy(test):
143
+ """
144
+ Setup tables and views needed for SQLAlchemy tests.
145
+ """
146
+ setUpCrateLayerBaseline(test)
177
147
 
178
- with connect(crate_host) as conn:
179
- cursor = conn.cursor()
180
- cursor.execute("""create table cities (
181
- name string primary key,
182
- coordinate geo_point,
183
- area geo_shape
184
- ) """)
185
-
186
- engine = sa.create_engine('crate://{0}'.format(crate_host))
187
- Base = declarative_base()
188
-
189
- class Location(Base):
190
- __tablename__ = 'locations'
191
- name = sa.Column(sa.String, primary_key=True)
192
- kind = sa.Column(sa.String)
193
- date = sa.Column(sa.Date, default=lambda: datetime.utcnow().date())
194
- datetime_tz = sa.Column(sa.DateTime, default=datetime.utcnow)
195
- datetime_notz = sa.Column(sa.DateTime, default=datetime.utcnow)
196
- nullable_datetime = sa.Column(sa.DateTime)
197
- nullable_date = sa.Column(sa.Date)
198
- flag = sa.Column(sa.Boolean)
199
- details = sa.Column(ObjectArray)
200
-
201
- Session = sessionmaker(engine)
202
- session = Session()
203
- test.globs['sa'] = sa
204
- test.globs['engine'] = engine
205
- test.globs['Location'] = Location
206
- test.globs['Base'] = Base
207
- test.globs['session'] = session
208
- test.globs['Session'] = Session
209
- test.globs['CrateDialect'] = CrateDialect
148
+ ddl_statements = [
149
+ """
150
+ CREATE TABLE characters (
151
+ id STRING PRIMARY KEY,
152
+ name STRING,
153
+ quote STRING,
154
+ details OBJECT,
155
+ more_details ARRAY(OBJECT),
156
+ INDEX name_ft USING fulltext(name) WITH (analyzer = 'english'),
157
+ INDEX quote_ft USING fulltext(quote) WITH (analyzer = 'english')
158
+ )""",
159
+ """
160
+ CREATE VIEW characters_view
161
+ AS SELECT * FROM characters
162
+ """,
163
+ """
164
+ CREATE TABLE cities (
165
+ name STRING PRIMARY KEY,
166
+ coordinate GEO_POINT,
167
+ area GEO_SHAPE
168
+ )"""
169
+ ]
170
+ _execute_statements(ddl_statements, on_error="raise")
171
+
172
+
173
+ def tearDownDropEntitiesBaseline(test):
174
+ """
175
+ Drop all tables, views, and users created by `setUpWithCrateLayer*`.
176
+ """
177
+ ddl_statements = [
178
+ "DROP TABLE locations",
179
+ "DROP BLOB TABLE myfiles",
180
+ "DROP USER me",
181
+ "DROP USER trusted_me",
182
+ ]
183
+ _execute_statements(ddl_statements)
184
+
185
+
186
+ def tearDownDropEntitiesSqlAlchemy(test):
187
+ """
188
+ Drop all tables, views, and users created by `setUpWithCrateLayer*`.
189
+ """
190
+ tearDownDropEntitiesBaseline(test)
191
+ ddl_statements = [
192
+ "DROP TABLE characters",
193
+ "DROP VIEW characters_view",
194
+ "DROP TABLE cities",
195
+ ]
196
+ _execute_statements(ddl_statements)
210
197
 
211
198
 
212
199
  class HttpsTestServerLayer:
@@ -298,7 +285,6 @@ class HttpsTestServerLayer:
298
285
 
299
286
 
300
287
  def setUpWithHttps(test):
301
- test.globs['HttpClient'] = http.Client
302
288
  test.globs['crate_host'] = "https://{0}:{1}".format(
303
289
  HttpsTestServerLayer.HOST, HttpsTestServerLayer.PORT
304
290
  )
@@ -319,43 +305,33 @@ def setUpWithHttps(test):
319
305
  )
320
306
 
321
307
 
322
- def _try_execute(cursor, stmt):
308
+ def _execute_statements(statements, on_error="ignore"):
309
+ with connect(crate_host) as conn:
310
+ cursor = conn.cursor()
311
+ for stmt in statements:
312
+ _execute_statement(cursor, stmt, on_error=on_error)
313
+ cursor.close()
314
+
315
+
316
+ def _execute_statement(cursor, stmt, on_error="ignore"):
323
317
  try:
324
318
  cursor.execute(stmt)
325
- except Exception:
319
+ except Exception: # pragma: no cover
326
320
  # FIXME: Why does this croak on statements like ``DROP TABLE cities``?
327
321
  # Note: When needing to debug the test environment, you may want to
328
322
  # enable this logger statement.
329
323
  # log.exception("Executing SQL statement failed")
330
- pass
331
-
332
-
333
- def tearDownWithCrateLayer(test):
334
- # clear testing data
335
- with connect(crate_host) as conn:
336
- for stmt in ["DROP TABLE locations",
337
- "DROP BLOB TABLE myfiles",
338
- "DROP TABLE characters",
339
- "DROP VIEW characters_view",
340
- "DROP TABLE cities",
341
- "DROP USER me",
342
- "DROP USER trusted_me",
343
- ]:
344
- _try_execute(conn.cursor(), stmt)
324
+ if on_error == "ignore":
325
+ pass
326
+ elif on_error == "raise":
327
+ raise
345
328
 
346
329
 
347
330
  def test_suite():
348
331
  suite = unittest.TestSuite()
349
332
  flags = (doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS)
350
333
 
351
- s = doctest.DocFileSuite(
352
- 'doctests/cursor.txt',
353
- 'doctests/connection.txt',
354
- setUp=setUpMocked,
355
- optionflags=flags,
356
- encoding='utf-8'
357
- )
358
- suite.addTest(s)
334
+ # Unit tests.
359
335
  suite.addTest(unittest.makeSuite(CursorTest))
360
336
  suite.addTest(unittest.makeSuite(HttpClientTest))
361
337
  suite.addTest(unittest.makeSuite(KeepAliveClientTest))
@@ -371,34 +347,32 @@ def test_suite():
371
347
  suite.addTest(doctest.DocTestSuite('crate.client.http'))
372
348
 
373
349
  s = doctest.DocFileSuite(
374
- 'doctests/https.txt',
375
- setUp=setUpWithHttps,
350
+ 'docs/by-example/connection.rst',
351
+ 'docs/by-example/cursor.rst',
352
+ module_relative=False,
376
353
  optionflags=flags,
377
354
  encoding='utf-8'
378
355
  )
379
- s.layer = HttpsTestServerLayer()
380
356
  suite.addTest(s)
381
357
 
382
358
  s = doctest.DocFileSuite(
383
- 'sqlalchemy/doctests/itests.txt',
384
- 'sqlalchemy/doctests/dialect.txt',
385
- 'sqlalchemy/doctests/reflection.txt',
386
- setUp=setUpCrateLayerAndSqlAlchemy,
387
- tearDown=tearDownWithCrateLayer,
359
+ 'docs/by-example/https.rst',
360
+ module_relative=False,
361
+ setUp=setUpWithHttps,
388
362
  optionflags=flags,
389
363
  encoding='utf-8'
390
364
  )
391
- s.layer = ensure_cratedb_layer()
365
+ s.layer = HttpsTestServerLayer()
392
366
  suite.addTest(s)
393
367
 
368
+ # Integration tests.
394
369
  s = doctest.DocFileSuite(
395
- 'doctests/http.txt',
396
- 'doctests/blob.txt',
397
- 'doctests/client.txt',
398
- 'doctests/mocking.txt',
399
- 'doctests/blob.txt',
400
- setUp=setUpWithCrateLayer,
401
- tearDown=tearDownWithCrateLayer,
370
+ 'docs/by-example/http.rst',
371
+ 'docs/by-example/client.rst',
372
+ 'docs/by-example/blob.rst',
373
+ module_relative=False,
374
+ setUp=setUpCrateLayerBaseline,
375
+ tearDown=tearDownDropEntitiesBaseline,
402
376
  optionflags=flags,
403
377
  encoding='utf-8'
404
378
  )
@@ -406,9 +380,14 @@ def test_suite():
406
380
  suite.addTest(s)
407
381
 
408
382
  s = doctest.DocFileSuite(
409
- 'doctests/sqlalchemy.txt',
410
- setUp=setUpCrateLayerAndSqlAlchemy,
411
- tearDown=tearDownWithCrateLayer,
383
+ 'docs/by-example/sqlalchemy/getting-started.rst',
384
+ 'docs/by-example/sqlalchemy/crud.rst',
385
+ 'docs/by-example/sqlalchemy/working-with-types.rst',
386
+ 'docs/by-example/sqlalchemy/advanced-querying.rst',
387
+ 'docs/by-example/sqlalchemy/inspection-reflection.rst',
388
+ module_relative=False,
389
+ setUp=setUpCrateLayerSqlAlchemy,
390
+ tearDown=tearDownDropEntitiesSqlAlchemy,
412
391
  optionflags=flags,
413
392
  encoding='utf-8'
414
393
  )
crate/testing/layer.py CHANGED
@@ -321,12 +321,12 @@ class CrateLayer(object):
321
321
  sys.stderr.write('\nCrate instance ready.\n')
322
322
 
323
323
  def stop(self):
324
+ self.conn_pool.clear()
324
325
  if self.process:
325
326
  self.process.terminate()
326
327
  self.process.communicate(timeout=10)
327
328
  self.process.stdout.close()
328
329
  self.process = None
329
- self.conn_pool.clear()
330
330
  self.monitor.stop()
331
331
  self._clean()
332
332
 
@@ -0,0 +1,51 @@
1
+ # vi: set encoding=utf-8
2
+ # -*- coding: utf-8; -*-
3
+ #
4
+ # Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
5
+ # license agreements. See the NOTICE file distributed with this work for
6
+ # additional information regarding copyright ownership. Crate licenses
7
+ # this file to you under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License. You may
9
+ # obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
+ # License for the specific language governing permissions and limitations
17
+ # under the License.
18
+ #
19
+ # However, if you have executed another commercial license agreement
20
+ # with Crate these terms will supersede the license and you may use the
21
+ # software solely pursuant to the terms of the relevant commercial agreement.
22
+ from __future__ import absolute_import
23
+
24
+ import os
25
+
26
+
27
+ def docs_path(*parts):
28
+ return os.path.abspath(
29
+ os.path.join(
30
+ os.path.dirname(os.path.dirname(__file__)), *parts
31
+ )
32
+ )
33
+
34
+
35
+ def project_root(*parts):
36
+ return os.path.abspath(
37
+ os.path.join(docs_path("..", ".."), *parts)
38
+ )
39
+
40
+
41
+ def crate_path(*parts):
42
+ return os.path.abspath(
43
+ project_root("parts", "crate", *parts)
44
+ )
45
+
46
+
47
+ crate_port = 44209
48
+ crate_transport_port = 44309
49
+ localhost = '127.0.0.1'
50
+ crate_host = "{host}:{port}".format(host=localhost, port=crate_port)
51
+ crate_uri = "http://%s" % crate_host