fakesnow 0.9.21__tar.gz → 0.9.22__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.
- {fakesnow-0.9.21 → fakesnow-0.9.22}/PKG-INFO +2 -2
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/fakes.py +12 -9
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/info_schema.py +1 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/server.py +1 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/transforms.py +30 -7
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow.egg-info/PKG-INFO +2 -2
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow.egg-info/requires.txt +1 -1
- {fakesnow-0.9.21 → fakesnow-0.9.22}/pyproject.toml +5 -2
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_fakes.py +51 -4
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_info_schema.py +37 -3
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_server.py +26 -12
- {fakesnow-0.9.21 → fakesnow-0.9.22}/LICENSE +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/README.md +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/__init__.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/__main__.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/arrow.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/checks.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/cli.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/expr.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/fixtures.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/instance.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/macros.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/py.typed +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow/variables.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow.egg-info/SOURCES.txt +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow.egg-info/dependency_links.txt +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow.egg-info/entry_points.txt +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/fakesnow.egg-info/top_level.txt +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/setup.cfg +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_arrow.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_checks.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_cli.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_connect.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_expr.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_patch.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_sqlalchemy.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_transforms.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_users.py +0 -0
- {fakesnow-0.9.21 → fakesnow-0.9.22}/tests/test_write_pandas.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fakesnow
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.22
|
4
4
|
Summary: Fake Snowflake Connector for Python. Run, mock and test Snowflake DB locally.
|
5
5
|
License: Apache License
|
6
6
|
Version 2.0, January 2004
|
@@ -213,7 +213,7 @@ License-File: LICENSE
|
|
213
213
|
Requires-Dist: duckdb~=1.0.0
|
214
214
|
Requires-Dist: pyarrow
|
215
215
|
Requires-Dist: snowflake-connector-python
|
216
|
-
Requires-Dist: sqlglot~=25.
|
216
|
+
Requires-Dist: sqlglot~=25.9.0
|
217
217
|
Provides-Extra: dev
|
218
218
|
Requires-Dist: build~=1.0; extra == "dev"
|
219
219
|
Requires-Dist: pandas-stubs; extra == "dev"
|
@@ -114,7 +114,6 @@ class FakeSnowflakeCursor:
|
|
114
114
|
def description(self) -> list[ResultMetadata]:
|
115
115
|
# use a separate cursor to avoid consuming the result set on this cursor
|
116
116
|
with self._conn.cursor() as cur:
|
117
|
-
# self._duck_conn.execute(sql, params)
|
118
117
|
expression = sqlglot.parse_one(f"DESCRIBE {self._last_sql}", read="duckdb")
|
119
118
|
cur._execute(expression, self._last_params) # noqa: SLF001
|
120
119
|
meta = FakeSnowflakeCursor._describe_as_result_metadata(cur.fetchall())
|
@@ -235,12 +234,10 @@ class FakeSnowflakeCursor:
|
|
235
234
|
if transformed.find(exp.Select) and (seed := transformed.args.get("seed")):
|
236
235
|
sql = f"SELECT setseed({seed}); {sql}"
|
237
236
|
|
238
|
-
if (fs_debug := os.environ.get("FAKESNOW_DEBUG")) and fs_debug != "snowflake":
|
239
|
-
print(f"{sql};{params=}" if params else f"{sql};", file=sys.stderr)
|
240
|
-
|
241
237
|
result_sql = None
|
242
238
|
|
243
239
|
try:
|
240
|
+
self._log_sql(sql, params)
|
244
241
|
self._duck_conn.execute(sql, params)
|
245
242
|
except duckdb.BinderException as e:
|
246
243
|
msg = e.args[0]
|
@@ -287,9 +284,9 @@ class FakeSnowflakeCursor:
|
|
287
284
|
(affected_count,) = self._duck_conn.fetchall()[0]
|
288
285
|
result_sql = SQL_DELETED_ROWS.substitute(count=affected_count)
|
289
286
|
|
290
|
-
elif cmd
|
291
|
-
# DESCRIBE TABLE has already been run above to detect and error if the table exists
|
292
|
-
# We now rerun DESCRIBE TABLE but transformed with columns to match Snowflake
|
287
|
+
elif cmd in ("DESCRIBE TABLE", "DESCRIBE VIEW"):
|
288
|
+
# DESCRIBE TABLE/VIEW has already been run above to detect and error if the table exists
|
289
|
+
# We now rerun DESCRIBE TABLE/VIEW but transformed with columns to match Snowflake
|
293
290
|
result_sql = transformed.transform(
|
294
291
|
lambda e: transforms.describe_table(e, self._conn.database, self._conn.schema)
|
295
292
|
).sql(dialect="duckdb")
|
@@ -337,6 +334,7 @@ class FakeSnowflakeCursor:
|
|
337
334
|
self._duck_conn.execute(info_schema.insert_text_lengths_sql(catalog, schema, table.name, text_lengths))
|
338
335
|
|
339
336
|
if result_sql:
|
337
|
+
self._log_sql(result_sql, params)
|
340
338
|
self._duck_conn.execute(result_sql)
|
341
339
|
|
342
340
|
self._arrow_table = self._duck_conn.fetch_arrow_table()
|
@@ -347,6 +345,10 @@ class FakeSnowflakeCursor:
|
|
347
345
|
|
348
346
|
return self
|
349
347
|
|
348
|
+
def _log_sql(self, sql: str, params: Sequence[Any] | dict[Any, Any] | None = None) -> None:
|
349
|
+
if (fs_debug := os.environ.get("FAKESNOW_DEBUG")) and fs_debug != "snowflake":
|
350
|
+
print(f"{sql};{params=}" if params else f"{sql};", file=sys.stderr)
|
351
|
+
|
350
352
|
def executemany(
|
351
353
|
self,
|
352
354
|
command: str,
|
@@ -389,12 +391,13 @@ class FakeSnowflakeCursor:
|
|
389
391
|
if self._arrow_table is None:
|
390
392
|
# mimic snowflake python connector error type
|
391
393
|
raise TypeError("No open result set")
|
394
|
+
tslice = self._arrow_table.slice(offset=self._arrow_table_fetch_index or 0, length=size).to_pylist()
|
395
|
+
|
392
396
|
if self._arrow_table_fetch_index is None:
|
393
|
-
self._arrow_table_fetch_index =
|
397
|
+
self._arrow_table_fetch_index = size
|
394
398
|
else:
|
395
399
|
self._arrow_table_fetch_index += size
|
396
400
|
|
397
|
-
tslice = self._arrow_table.slice(offset=self._arrow_table_fetch_index, length=size).to_pylist()
|
398
401
|
return tslice if self._use_dict_result else [tuple(d.values()) for d in tslice]
|
399
402
|
|
400
403
|
def get_result_batches(self) -> list[ResultBatch] | None:
|
@@ -159,22 +159,41 @@ SELECT
|
|
159
159
|
column_default AS "default",
|
160
160
|
'N' AS "primary key",
|
161
161
|
'N' AS "unique key",
|
162
|
-
NULL AS "check",
|
163
|
-
NULL AS "expression",
|
164
|
-
NULL AS "comment",
|
165
|
-
NULL AS "policy name",
|
166
|
-
NULL AS "privacy domain",
|
162
|
+
NULL::VARCHAR AS "check",
|
163
|
+
NULL::VARCHAR AS "expression",
|
164
|
+
NULL::VARCHAR AS "comment",
|
165
|
+
NULL::VARCHAR AS "policy name",
|
166
|
+
NULL::JSON AS "privacy domain",
|
167
167
|
FROM information_schema._fs_columns_snowflake
|
168
168
|
WHERE table_catalog = '${catalog}' AND table_schema = '${schema}' AND table_name = '${table}'
|
169
169
|
ORDER BY ordinal_position
|
170
170
|
"""
|
171
171
|
)
|
172
172
|
|
173
|
+
SQL_DESCRIBE_INFO_SCHEMA = Template(
|
174
|
+
"""
|
175
|
+
SELECT
|
176
|
+
column_name AS "name",
|
177
|
+
column_type as "type",
|
178
|
+
'COLUMN' AS "kind",
|
179
|
+
CASE WHEN "null" = 'YES' THEN 'Y' ELSE 'N' END AS "null?",
|
180
|
+
NULL::VARCHAR AS "default",
|
181
|
+
'N' AS "primary key",
|
182
|
+
'N' AS "unique key",
|
183
|
+
NULL::VARCHAR AS "check",
|
184
|
+
NULL::VARCHAR AS "expression",
|
185
|
+
NULL::VARCHAR AS "comment",
|
186
|
+
NULL::VARCHAR AS "policy name",
|
187
|
+
NULL::JSON AS "privacy domain",
|
188
|
+
FROM (DESCRIBE information_schema.${view})
|
189
|
+
"""
|
190
|
+
)
|
191
|
+
|
173
192
|
|
174
193
|
def describe_table(
|
175
194
|
expression: exp.Expression, current_database: str | None = None, current_schema: str | None = None
|
176
195
|
) -> exp.Expression:
|
177
|
-
"""Redirect to the information_schema.
|
196
|
+
"""Redirect to the information_schema._fs_columns_snowflake to match snowflake.
|
178
197
|
|
179
198
|
See https://docs.snowflake.com/en/sql-reference/sql/desc-table
|
180
199
|
"""
|
@@ -183,12 +202,16 @@ def describe_table(
|
|
183
202
|
isinstance(expression, exp.Describe)
|
184
203
|
and (kind := expression.args.get("kind"))
|
185
204
|
and isinstance(kind, str)
|
186
|
-
and kind.upper()
|
205
|
+
and kind.upper() in ("TABLE", "VIEW")
|
187
206
|
and (table := expression.find(exp.Table))
|
188
207
|
):
|
189
208
|
catalog = table.catalog or current_database
|
190
209
|
schema = table.db or current_schema
|
191
210
|
|
211
|
+
if schema and schema.upper() == "INFORMATION_SCHEMA":
|
212
|
+
# information schema views don't exist in _fs_columns_snowflake
|
213
|
+
return sqlglot.parse_one(SQL_DESCRIBE_INFO_SCHEMA.substitute(view=table.name), read="duckdb")
|
214
|
+
|
192
215
|
return sqlglot.parse_one(
|
193
216
|
SQL_DESCRIBE_TABLE.substitute(catalog=catalog, schema=schema, table=table.name),
|
194
217
|
read="duckdb",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fakesnow
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.22
|
4
4
|
Summary: Fake Snowflake Connector for Python. Run, mock and test Snowflake DB locally.
|
5
5
|
License: Apache License
|
6
6
|
Version 2.0, January 2004
|
@@ -213,7 +213,7 @@ License-File: LICENSE
|
|
213
213
|
Requires-Dist: duckdb~=1.0.0
|
214
214
|
Requires-Dist: pyarrow
|
215
215
|
Requires-Dist: snowflake-connector-python
|
216
|
-
Requires-Dist: sqlglot~=25.
|
216
|
+
Requires-Dist: sqlglot~=25.9.0
|
217
217
|
Provides-Extra: dev
|
218
218
|
Requires-Dist: build~=1.0; extra == "dev"
|
219
219
|
Requires-Dist: pandas-stubs; extra == "dev"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
[project]
|
2
2
|
name = "fakesnow"
|
3
3
|
description = "Fake Snowflake Connector for Python. Run, mock and test Snowflake DB locally."
|
4
|
-
version = "0.9.
|
4
|
+
version = "0.9.22"
|
5
5
|
readme = "README.md"
|
6
6
|
license = { file = "LICENSE" }
|
7
7
|
classifiers = ["License :: OSI Approved :: MIT License"]
|
@@ -11,7 +11,7 @@ dependencies = [
|
|
11
11
|
"duckdb~=1.0.0",
|
12
12
|
"pyarrow",
|
13
13
|
"snowflake-connector-python",
|
14
|
-
"sqlglot~=25.
|
14
|
+
"sqlglot~=25.9.0",
|
15
15
|
]
|
16
16
|
|
17
17
|
[project.urls]
|
@@ -62,6 +62,9 @@ reportUnnecessaryTypeIgnoreComment = true
|
|
62
62
|
|
63
63
|
[tool.pytest.ini_options]
|
64
64
|
asyncio_mode = "auto"
|
65
|
+
# error on unhandled exceptions in background threads
|
66
|
+
# useful for catching errors in server or snowflake connector threads
|
67
|
+
filterwarnings = ["error::pytest.PytestUnhandledThreadExceptionWarning"]
|
65
68
|
|
66
69
|
[tool.ruff]
|
67
70
|
line-length = 120
|
@@ -436,6 +436,52 @@ def test_describe_table(dcur: snowflake.connector.cursor.DictCursor):
|
|
436
436
|
assert "002003 (42S02): Catalog Error: Table with name THIS_DOES_NOT_EXIST does not exist!" in str(excinfo.value)
|
437
437
|
|
438
438
|
|
439
|
+
def test_describe_view(dcur: snowflake.connector.cursor.DictCursor):
|
440
|
+
dcur.execute(
|
441
|
+
"""
|
442
|
+
create or replace table example (
|
443
|
+
XVARCHAR VARCHAR
|
444
|
+
-- ,XVARCHAR20 VARCHAR(20) -- TODO: preserve varchar size
|
445
|
+
)
|
446
|
+
"""
|
447
|
+
)
|
448
|
+
|
449
|
+
common = {
|
450
|
+
"kind": "COLUMN",
|
451
|
+
"null?": "Y",
|
452
|
+
"default": None,
|
453
|
+
"primary key": "N",
|
454
|
+
"unique key": "N",
|
455
|
+
"check": None,
|
456
|
+
"expression": None,
|
457
|
+
"comment": None,
|
458
|
+
"policy name": None,
|
459
|
+
"privacy domain": None,
|
460
|
+
}
|
461
|
+
expected = [
|
462
|
+
{"name": "XVARCHAR", "type": "VARCHAR(16777216)", **common},
|
463
|
+
# TODO: preserve varchar size
|
464
|
+
# {"name": "XVARCHAR20", "type": "VARCHAR(20)", **common},
|
465
|
+
]
|
466
|
+
|
467
|
+
dcur.execute("create view v1 as select * from example")
|
468
|
+
assert dcur.execute("describe view v1").fetchall() == expected
|
469
|
+
assert [r.name for r in dcur.description] == [
|
470
|
+
"name",
|
471
|
+
"type",
|
472
|
+
"kind",
|
473
|
+
"null?",
|
474
|
+
"default",
|
475
|
+
"primary key",
|
476
|
+
"unique key",
|
477
|
+
"check",
|
478
|
+
"expression",
|
479
|
+
"comment",
|
480
|
+
"policy name",
|
481
|
+
"privacy domain",
|
482
|
+
]
|
483
|
+
|
484
|
+
|
439
485
|
## descriptions are needed for ipython-sql/jupysql which describes every statement
|
440
486
|
def test_description_create_drop_database(dcur: snowflake.connector.cursor.DictCursor):
|
441
487
|
dcur.execute("create database example")
|
@@ -605,9 +651,10 @@ def test_fetchmany(conn: snowflake.connector.SnowflakeConnection):
|
|
605
651
|
cur.execute("insert into customers values (3, 'Jeremy', 'K')")
|
606
652
|
cur.execute("select id, first_name, last_name from customers")
|
607
653
|
|
654
|
+
# mimic jupysql fetchmany behaviour
|
608
655
|
assert cur.fetchmany(2) == [(1, "Jenny", "P"), (2, "Jasper", "M")]
|
609
|
-
assert cur.fetchmany(
|
610
|
-
assert cur.fetchmany(
|
656
|
+
assert cur.fetchmany(5) == [(3, "Jeremy", "K")]
|
657
|
+
assert cur.fetchmany(5) == []
|
611
658
|
|
612
659
|
with conn.cursor(snowflake.connector.cursor.DictCursor) as cur:
|
613
660
|
cur.execute("select id, first_name, last_name from customers")
|
@@ -615,10 +662,10 @@ def test_fetchmany(conn: snowflake.connector.SnowflakeConnection):
|
|
615
662
|
{"ID": 1, "FIRST_NAME": "Jenny", "LAST_NAME": "P"},
|
616
663
|
{"ID": 2, "FIRST_NAME": "Jasper", "LAST_NAME": "M"},
|
617
664
|
]
|
618
|
-
assert cur.fetchmany(
|
665
|
+
assert cur.fetchmany(5) == [
|
619
666
|
{"ID": 3, "FIRST_NAME": "Jeremy", "LAST_NAME": "K"},
|
620
667
|
]
|
621
|
-
assert cur.fetchmany(
|
668
|
+
assert cur.fetchmany(5) == []
|
622
669
|
|
623
670
|
|
624
671
|
def test_fetch_pandas_all(cur: snowflake.connector.cursor.SnowflakeCursor):
|
@@ -38,11 +38,45 @@ def test_info_schema_columns_describe(cur: snowflake.connector.cursor.SnowflakeC
|
|
38
38
|
|
39
39
|
assert cur.description == expected_metadata
|
40
40
|
|
41
|
+
|
42
|
+
def test_describe_view_columns(dcur: snowflake.connector.cursor.DictCursor):
|
43
|
+
cols = [
|
44
|
+
"name",
|
45
|
+
"type",
|
46
|
+
"kind",
|
47
|
+
"null?",
|
48
|
+
"default",
|
49
|
+
"primary key",
|
50
|
+
"unique key",
|
51
|
+
"check",
|
52
|
+
"expression",
|
53
|
+
"comment",
|
54
|
+
"policy name",
|
55
|
+
"privacy domain",
|
56
|
+
]
|
57
|
+
dcur.execute("describe view information_schema.columns")
|
58
|
+
result: list[dict] = dcur.fetchall() # type: ignore
|
59
|
+
assert list(result[0].keys()) == cols
|
60
|
+
names = [r["name"] for r in result]
|
41
61
|
# should contain snowflake-specific columns (from _FS_COLUMNS_SNOWFLAKE)
|
42
|
-
cur.execute("describe view information_schema.columns")
|
43
|
-
result = cur.fetchall()
|
44
|
-
names = [name for (name, *_) in result]
|
45
62
|
assert "comment" in names
|
63
|
+
# fmt: off
|
64
|
+
assert dcur.description[:-1] == [
|
65
|
+
ResultMetadata(name='name', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
66
|
+
ResultMetadata(name='type', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
67
|
+
ResultMetadata(name='kind', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
68
|
+
ResultMetadata(name='null?', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
69
|
+
ResultMetadata(name='default', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
70
|
+
ResultMetadata(name='primary key', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
71
|
+
ResultMetadata(name='unique key', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
72
|
+
ResultMetadata(name='check', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
73
|
+
ResultMetadata(name='expression', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
74
|
+
ResultMetadata(name='comment', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
75
|
+
ResultMetadata(name='policy name', type_code=2, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True),
|
76
|
+
# TODO: ignore the following, see https://github.com/tekumara/fakesnow/issues/26
|
77
|
+
# ResultMetadata(name='privacy domain', type_code=9, display_size=None, internal_size=16777216, precision=None, scale=None, is_nullable=True)
|
78
|
+
]
|
79
|
+
# fmt: on
|
46
80
|
|
47
81
|
|
48
82
|
def test_info_schema_columns_numeric(cur: snowflake.connector.cursor.SnowflakeCursor):
|
@@ -18,7 +18,7 @@ def unused_port(unused_tcp_port_factory: Callable[[], int]) -> int:
|
|
18
18
|
|
19
19
|
|
20
20
|
@pytest.fixture(scope="session")
|
21
|
-
def server(unused_tcp_port_factory: Callable[[], int]) -> Iterator[
|
21
|
+
def server(unused_tcp_port_factory: Callable[[], int]) -> Iterator[dict]:
|
22
22
|
port = unused_tcp_port_factory()
|
23
23
|
server = uvicorn.Server(uvicorn.Config(fakesnow.server.app, port=port, log_level="info"))
|
24
24
|
thread = threading.Thread(target=server.run, name="Server", daemon=True)
|
@@ -26,28 +26,42 @@ def server(unused_tcp_port_factory: Callable[[], int]) -> Iterator[int]:
|
|
26
26
|
|
27
27
|
while not server.started:
|
28
28
|
sleep(0.1)
|
29
|
-
yield
|
29
|
+
yield dict(
|
30
|
+
user="fake",
|
31
|
+
password="snow",
|
32
|
+
account="fakesnow",
|
33
|
+
host="localhost",
|
34
|
+
port=port,
|
35
|
+
protocol="http",
|
36
|
+
# disable telemetry
|
37
|
+
session_parameters={"CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED": False},
|
38
|
+
)
|
30
39
|
|
31
40
|
server.should_exit = True
|
32
41
|
# wait for server thread to end
|
33
42
|
thread.join()
|
34
43
|
|
35
44
|
|
36
|
-
def test_server_connect(server:
|
45
|
+
def test_server_connect(server: dict) -> None:
|
37
46
|
with (
|
38
47
|
snowflake.connector.connect(
|
39
|
-
|
40
|
-
password="snow",
|
41
|
-
account="fakesnow",
|
42
|
-
host="localhost",
|
43
|
-
port=server,
|
44
|
-
protocol="http",
|
45
|
-
# disable telemetry
|
46
|
-
session_parameters={"CLIENT_OUT_OF_BAND_TELEMETRY_ENABLED": False},
|
48
|
+
**server,
|
47
49
|
# disable infinite retries on error
|
48
|
-
network_timeout=
|
50
|
+
network_timeout=1,
|
49
51
|
) as conn1,
|
50
52
|
conn1.cursor() as cur,
|
51
53
|
):
|
52
54
|
cur.execute("select 'hello', to_decimal('12.3456', 10,2)")
|
53
55
|
assert cur.fetchall() == [("hello", Decimal("12.35"))]
|
56
|
+
|
57
|
+
|
58
|
+
def test_server_abort_request(server: dict) -> None:
|
59
|
+
with (
|
60
|
+
snowflake.connector.connect(
|
61
|
+
**server,
|
62
|
+
# triggers an abort request
|
63
|
+
network_timeout=0,
|
64
|
+
) as conn1,
|
65
|
+
conn1.cursor() as cur,
|
66
|
+
):
|
67
|
+
cur.execute("select 'will abort'")
|
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
|