crate 1.0.0__py3-none-any.whl → 1.0.0.dev1__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.
crate/testing/layer.py CHANGED
@@ -19,44 +19,38 @@
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
- # ruff: noqa: S603 # `subprocess` call: check for execution of untrusted input
23
- # ruff: noqa: S202 # Uses of `tarfile.extractall()`
24
-
25
- import io
26
- import json
27
- import logging
28
22
  import os
29
23
  import re
24
+ import sys
25
+ import time
26
+ import json
27
+ import urllib3
28
+ import tempfile
30
29
  import shutil
31
30
  import subprocess
32
- import sys
33
31
  import tarfile
34
- import tempfile
32
+ import io
35
33
  import threading
36
- import time
37
-
38
- import urllib3
34
+ import logging
39
35
 
40
36
  try:
41
37
  from urllib.request import urlopen
42
38
  except ImportError:
43
- from urllib import urlopen # type: ignore[attr-defined,no-redef]
39
+ from urllib import urlopen
44
40
 
45
41
 
46
42
  log = logging.getLogger(__name__)
47
43
 
48
44
 
49
- CRATE_CONFIG_ERROR = (
50
- 'crate_config must point to a folder or to a file named "crate.yml"'
51
- )
45
+ CRATE_CONFIG_ERROR = 'crate_config must point to a folder or to a file named "crate.yml"'
52
46
  HTTP_ADDRESS_RE = re.compile(
53
- r".*\[(http|.*HttpServer.*)\s*] \[.*\] .*"
54
- "publish_address {"
55
- r"(?:inet\[[\w\d\.-]*/|\[)?"
56
- r"(?:[\w\d\.-]+/)?"
57
- r"(?P<addr>[\d\.:]+)"
58
- r"(?:\])?"
59
- "}"
47
+ r'.*\[(http|.*HttpServer.*)\s*] \[.*\] .*'
48
+ 'publish_address {'
49
+ r'(?:inet\[[\w\d\.-]*/|\[)?'
50
+ r'(?:[\w\d\.-]+/)?'
51
+ r'(?P<addr>[\d\.:]+)'
52
+ r'(?:\])?'
53
+ '}'
60
54
  )
61
55
 
62
56
 
@@ -67,22 +61,18 @@ def http_url_from_host_port(host, port):
67
61
  port = int(port)
68
62
  except ValueError:
69
63
  return None
70
- return "{}:{}".format(prepend_http(host), port)
64
+ return '{}:{}'.format(prepend_http(host), port)
71
65
  return None
72
66
 
73
67
 
74
68
  def prepend_http(host):
75
- if not re.match(r"^https?\:\/\/.*", host):
76
- return "http://{}".format(host)
69
+ if not re.match(r'^https?\:\/\/.*', host):
70
+ return 'http://{}'.format(host)
77
71
  return host
78
72
 
79
73
 
80
74
  def _download_and_extract(uri, directory):
81
- sys.stderr.write(
82
- "\nINFO: Downloading CrateDB archive from {} into {}".format(
83
- uri, directory
84
- )
85
- )
75
+ sys.stderr.write("\nINFO: Downloading CrateDB archive from {} into {}".format(uri, directory))
86
76
  sys.stderr.flush()
87
77
  with io.BytesIO(urlopen(uri).read()) as tmpfile:
88
78
  with tarfile.open(fileobj=tmpfile) as t:
@@ -92,18 +82,19 @@ def _download_and_extract(uri, directory):
92
82
  def wait_for_http_url(log, timeout=30, verbose=False):
93
83
  start = time.monotonic()
94
84
  while True:
95
- line = log.readline().decode("utf-8").strip()
85
+ line = log.readline().decode('utf-8').strip()
96
86
  elapsed = time.monotonic() - start
97
87
  if verbose:
98
- sys.stderr.write("[{:>4.1f}s]{}\n".format(elapsed, line))
88
+ sys.stderr.write('[{:>4.1f}s]{}\n'.format(elapsed, line))
99
89
  m = HTTP_ADDRESS_RE.match(line)
100
90
  if m:
101
- return prepend_http(m.group("addr"))
91
+ return prepend_http(m.group('addr'))
102
92
  elif elapsed > timeout:
103
93
  return None
104
94
 
105
95
 
106
96
  class OutputMonitor:
97
+
107
98
  def __init__(self):
108
99
  self.consumers = []
109
100
 
@@ -114,9 +105,7 @@ class OutputMonitor:
114
105
 
115
106
  def start(self, proc):
116
107
  self._stop_out_thread = threading.Event()
117
- self._out_thread = threading.Thread(
118
- target=self.consume, args=(proc.stdout,)
119
- )
108
+ self._out_thread = threading.Thread(target=self.consume, args=(proc.stdout,))
120
109
  self._out_thread.daemon = True
121
110
  self._out_thread.start()
122
111
 
@@ -127,6 +116,7 @@ class OutputMonitor:
127
116
 
128
117
 
129
118
  class LineBuffer:
119
+
130
120
  def __init__(self):
131
121
  self.lines = []
132
122
 
@@ -134,7 +124,7 @@ class LineBuffer:
134
124
  self.lines.append(line.strip())
135
125
 
136
126
 
137
- class CrateLayer:
127
+ class CrateLayer(object):
138
128
  """
139
129
  This layer starts a Crate server.
140
130
  """
@@ -145,16 +135,14 @@ class CrateLayer:
145
135
  wait_interval = 0.2
146
136
 
147
137
  @staticmethod
148
- def from_uri(
149
- uri,
150
- name,
151
- http_port="4200-4299",
152
- transport_port="4300-4399",
153
- settings=None,
154
- directory=None,
155
- cleanup=True,
156
- verbose=False,
157
- ):
138
+ def from_uri(uri,
139
+ name,
140
+ http_port='4200-4299',
141
+ transport_port='4300-4399',
142
+ settings=None,
143
+ directory=None,
144
+ cleanup=True,
145
+ verbose=False):
158
146
  """Download the Crate tarball from a URI and create a CrateLayer
159
147
 
160
148
  :param uri: The uri that points to the Crate tarball
@@ -170,14 +158,11 @@ class CrateLayer:
170
158
  """
171
159
  directory = directory or tempfile.mkdtemp()
172
160
  filename = os.path.basename(uri)
173
- crate_dir = re.sub(r"\.tar(\.gz)?$", "", filename)
161
+ crate_dir = re.sub(r'\.tar(\.gz)?$', '', filename)
174
162
  crate_home = os.path.join(directory, crate_dir)
175
163
 
176
164
  if os.path.exists(crate_home):
177
- sys.stderr.write(
178
- "\nWARNING: Not extracting CrateDB tarball"
179
- " because folder already exists"
180
- )
165
+ sys.stderr.write("\nWARNING: Not extracting Crate tarball because folder already exists")
181
166
  sys.stderr.flush()
182
167
  else:
183
168
  _download_and_extract(uri, directory)
@@ -188,33 +173,29 @@ class CrateLayer:
188
173
  port=http_port,
189
174
  transport_port=transport_port,
190
175
  settings=settings,
191
- verbose=verbose,
192
- )
176
+ verbose=verbose)
193
177
  if cleanup:
194
178
  tearDown = layer.tearDown
195
179
 
196
180
  def new_teardown(*args, **kws):
197
181
  shutil.rmtree(directory)
198
182
  tearDown(*args, **kws)
199
-
200
- layer.tearDown = new_teardown # type: ignore[method-assign]
183
+ layer.tearDown = new_teardown
201
184
  return layer
202
185
 
203
- def __init__(
204
- self,
205
- name,
206
- crate_home,
207
- crate_config=None,
208
- port=None,
209
- keepRunning=False,
210
- transport_port=None,
211
- crate_exec=None,
212
- cluster_name=None,
213
- host="127.0.0.1",
214
- settings=None,
215
- verbose=False,
216
- env=None,
217
- ):
186
+ def __init__(self,
187
+ name,
188
+ crate_home,
189
+ crate_config=None,
190
+ port=None,
191
+ keepRunning=False,
192
+ transport_port=None,
193
+ crate_exec=None,
194
+ cluster_name=None,
195
+ host="127.0.0.1",
196
+ settings=None,
197
+ verbose=False,
198
+ env=None):
218
199
  """
219
200
  :param name: layer name, is also used as the cluser name
220
201
  :param crate_home: path to home directory of the crate installation
@@ -235,69 +216,52 @@ class CrateLayer:
235
216
  self.__name__ = name
236
217
  if settings and isinstance(settings, dict):
237
218
  # extra settings may override host/port specification!
238
- self.http_url = http_url_from_host_port(
239
- settings.get("network.host", host),
240
- settings.get("http.port", port),
241
- )
219
+ self.http_url = http_url_from_host_port(settings.get('network.host', host),
220
+ settings.get('http.port', port))
242
221
  else:
243
222
  self.http_url = http_url_from_host_port(host, port)
244
223
 
245
224
  self.process = None
246
225
  self.verbose = verbose
247
226
  self.env = env or {}
248
- self.env.setdefault("CRATE_USE_IPV4", "true")
249
- self.env.setdefault("JAVA_HOME", os.environ.get("JAVA_HOME", ""))
227
+ self.env.setdefault('CRATE_USE_IPV4', 'true')
228
+ self.env.setdefault('JAVA_HOME', os.environ.get('JAVA_HOME', ''))
250
229
  self._stdout_consumers = []
251
230
  self.conn_pool = urllib3.PoolManager(num_pools=1)
252
231
 
253
232
  crate_home = os.path.abspath(crate_home)
254
233
  if crate_exec is None:
255
- start_script = "crate.bat" if sys.platform == "win32" else "crate"
256
- crate_exec = os.path.join(crate_home, "bin", start_script)
234
+ start_script = 'crate.bat' if sys.platform == 'win32' else 'crate'
235
+ crate_exec = os.path.join(crate_home, 'bin', start_script)
257
236
  if crate_config is None:
258
- crate_config = os.path.join(crate_home, "config", "crate.yml")
259
- elif (
260
- os.path.isfile(crate_config)
261
- and os.path.basename(crate_config) != "crate.yml"
262
- ):
237
+ crate_config = os.path.join(crate_home, 'config', 'crate.yml')
238
+ elif (os.path.isfile(crate_config) and
239
+ os.path.basename(crate_config) != 'crate.yml'):
263
240
  raise ValueError(CRATE_CONFIG_ERROR)
264
241
  if cluster_name is None:
265
- cluster_name = "Testing{0}".format(port or "Dynamic")
266
- settings = self.create_settings(
267
- crate_config,
268
- cluster_name,
269
- name,
270
- host,
271
- port or "4200-4299",
272
- transport_port or "4300-4399",
273
- settings,
274
- )
242
+ cluster_name = "Testing{0}".format(port or 'Dynamic')
243
+ settings = self.create_settings(crate_config,
244
+ cluster_name,
245
+ name,
246
+ host,
247
+ port or '4200-4299',
248
+ transport_port or '4300-4399',
249
+ settings)
275
250
  # ES 5 cannot parse 'True'/'False' as booleans so convert to lowercase
276
- start_cmd = (crate_exec,) + tuple(
277
- [
278
- "-C%s=%s"
279
- % (
280
- (key, str(value).lower())
281
- if isinstance(value, bool)
282
- else (key, value)
283
- )
284
- for key, value in settings.items()
285
- ]
286
- )
287
-
288
- self._wd = wd = os.path.join(CrateLayer.tmpdir, "crate_layer", name)
289
- self.start_cmd = start_cmd + ("-Cpath.data=%s" % wd,)
290
-
291
- def create_settings(
292
- self,
293
- crate_config,
294
- cluster_name,
295
- node_name,
296
- host,
297
- http_port,
298
- transport_port,
299
- further_settings=None,
300
- ):
251
+ start_cmd = (crate_exec, ) + tuple(["-C%s=%s" % ((key, str(value).lower()) if isinstance(value, bool) else (key, value))
252
+ for key, value in settings.items()])
253
+
254
+ self._wd = wd = os.path.join(CrateLayer.tmpdir, 'crate_layer', name)
255
+ self.start_cmd = start_cmd + ('-Cpath.data=%s' % wd,)
256
+
257
+ def create_settings(self,
258
+ crate_config,
259
+ cluster_name,
260
+ node_name,
261
+ host,
262
+ http_port,
263
+ transport_port,
264
+ further_settings=None):
301
265
  settings = {
302
266
  "discovery.type": "zen",
303
267
  "discovery.initial_state_timeout": 0,
@@ -330,23 +294,20 @@ class CrateLayer:
330
294
 
331
295
  def start(self):
332
296
  self._clean()
333
- self.process = subprocess.Popen(
334
- self.start_cmd, env=self.env, stdout=subprocess.PIPE
335
- )
297
+ self.process = subprocess.Popen(self.start_cmd,
298
+ env=self.env,
299
+ stdout=subprocess.PIPE)
336
300
  returncode = self.process.poll()
337
301
  if returncode is not None:
338
302
  raise SystemError(
339
- "Failed to start server rc={0} cmd={1}".format(
340
- returncode, self.start_cmd
341
- )
303
+ 'Failed to start server rc={0} cmd={1}'.format(returncode,
304
+ self.start_cmd)
342
305
  )
343
306
 
344
307
  if not self.http_url:
345
308
  # try to read http_url from startup logs
346
309
  # this is necessary if no static port is assigned
347
- self.http_url = wait_for_http_url(
348
- self.process.stdout, verbose=self.verbose
349
- )
310
+ self.http_url = wait_for_http_url(self.process.stdout, verbose=self.verbose)
350
311
 
351
312
  self.monitor = OutputMonitor()
352
313
  self.monitor.start(self.process)
@@ -354,10 +315,10 @@ class CrateLayer:
354
315
  if not self.http_url:
355
316
  self.stop()
356
317
  else:
357
- sys.stderr.write("HTTP: {}\n".format(self.http_url))
318
+ sys.stderr.write('HTTP: {}\n'.format(self.http_url))
358
319
  self._wait_for_start()
359
320
  self._wait_for_master()
360
- sys.stderr.write("\nCrate instance ready.\n")
321
+ sys.stderr.write('\nCrate instance ready.\n')
361
322
 
362
323
  def stop(self):
363
324
  self.conn_pool.clear()
@@ -391,9 +352,10 @@ class CrateLayer:
391
352
  for line in line_buf.lines:
392
353
  log.error(line)
393
354
  self.stop()
394
- raise SystemError("Failed to start Crate instance in time.")
395
- sys.stderr.write(".")
396
- time.sleep(self.wait_interval)
355
+ raise SystemError('Failed to start Crate instance in time.')
356
+ else:
357
+ sys.stderr.write('.')
358
+ time.sleep(self.wait_interval)
397
359
 
398
360
  self.monitor.consumers.remove(line_buf)
399
361
 
@@ -405,7 +367,7 @@ class CrateLayer:
405
367
  # after the layer starts don't result in 503
406
368
  def validator():
407
369
  try:
408
- resp = self.conn_pool.request("HEAD", self.http_url)
370
+ resp = self.conn_pool.request('HEAD', self.http_url)
409
371
  return resp.status == 200
410
372
  except Exception:
411
373
  return False
@@ -417,12 +379,12 @@ class CrateLayer:
417
379
 
418
380
  def validator():
419
381
  resp = self.conn_pool.urlopen(
420
- "POST",
421
- "{server}/_sql".format(server=self.http_url),
422
- headers={"Content-Type": "application/json"},
423
- body='{"stmt": "select master_node from sys.cluster"}',
382
+ 'POST',
383
+ '{server}/_sql'.format(server=self.http_url),
384
+ headers={'Content-Type': 'application/json'},
385
+ body='{"stmt": "select master_node from sys.cluster"}'
424
386
  )
425
- data = json.loads(resp.data.decode("utf-8"))
426
- return resp.status == 200 and data["rows"][0][0]
387
+ data = json.loads(resp.data.decode('utf-8'))
388
+ return resp.status == 200 and data['rows'][0][0]
427
389
 
428
390
  self._wait_for(validator)
@@ -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
@@ -0,0 +1,90 @@
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 os
23
+ import sys
24
+ import unittest
25
+ from datetime import datetime, date
26
+ from unittest import TestCase, mock
27
+
28
+ import time_machine
29
+
30
+
31
+ class DateTodayTest(TestCase):
32
+ """
33
+ `date.today()` returns the current local date, as advertised in the
34
+ documentation [1]. Thus, it depends on the system time zone.
35
+
36
+ The following test cases demonstrate that the test suite previously
37
+ failed around midnight, where the UTC vs. non-UTC days overlapped,
38
+ and when running on machines with non-UTC time zone.
39
+
40
+ On the other hand, `datetime.utcnow().date()` works equally well in all
41
+ situations, so we want to use that within the SQLAlchemy test cases.
42
+
43
+ Funny enough, the problem is not observable on Linux?
44
+
45
+ [1] https://docs.python.org/3/library/datetime.html#datetime.date.today
46
+ """
47
+
48
+ @mock.patch.dict(os.environ, {"TZ": "UTC"})
49
+ @time_machine.travel("2022-07-22T00:42:00+0200")
50
+ def test_date_today_depends_on_system_timezone_success_on_utc(self):
51
+ today_local = date.today()
52
+ today_utc = datetime.utcnow().date()
53
+ self.assertEqual(today_local, today_utc)
54
+ self.assertEqual(today_local, date(2022, 7, 21))
55
+ self.assertEqual(today_utc, date(2022, 7, 21))
56
+
57
+ @unittest.skipIf(sys.platform == "linux", "Problem not observable on Linux")
58
+ @mock.patch.dict(os.environ, {"TZ": "Europe/Prague"})
59
+ @time_machine.travel("2022-07-22T00:42:00+0200")
60
+ def test_date_today_depends_on_system_timezone_failure_on_non_utc(self):
61
+ today_local = date.today()
62
+ today_utc = datetime.utcnow().date()
63
+ self.assertNotEqual(today_local, today_utc)
64
+ self.assertEqual(today_local, date(2022, 7, 22))
65
+ self.assertEqual(today_utc, date(2022, 7, 21))
66
+
67
+ @mock.patch.dict(os.environ, {"TZ": "UTC"})
68
+ @time_machine.travel("2022-07-22T00:42:00+0200")
69
+ def test_date_today_utc(self):
70
+ today_local = date.today()
71
+ self.assertEqual(today_local, date(2022, 7, 21))
72
+
73
+ @unittest.skipIf(sys.platform == "linux", "Problem not observable on Linux")
74
+ @mock.patch.dict(os.environ, {"TZ": "Europe/Prague"})
75
+ @time_machine.travel("2022-07-22T00:42:00+0200")
76
+ def test_date_today_non_utc(self):
77
+ today_local = date.today()
78
+ self.assertEqual(today_local, date(2022, 7, 22))
79
+
80
+ @mock.patch.dict(os.environ, {"TZ": "UTC"})
81
+ @time_machine.travel("2022-07-22T00:42:00+0200")
82
+ def test_utcnow_date_utc(self):
83
+ today_utc = datetime.utcnow().date()
84
+ self.assertEqual(today_utc, date(2022, 7, 21))
85
+
86
+ @mock.patch.dict(os.environ, {"TZ": "Europe/Prague"})
87
+ @time_machine.travel("2022-07-22T00:42:00+0200")
88
+ def test_utcnow_date_non_utc(self):
89
+ today_utc = datetime.utcnow().date()
90
+ self.assertEqual(today_utc, date(2022, 7, 21))