fakesnow 0.9.5__tar.gz → 0.9.6__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.5/fakesnow.egg-info → fakesnow-0.9.6}/PKG-INFO +8 -6
- {fakesnow-0.9.5 → fakesnow-0.9.6}/README.md +2 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/fakes.py +4 -1
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/info_schema.py +1 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/transforms.py +105 -60
- {fakesnow-0.9.5 → fakesnow-0.9.6/fakesnow.egg-info}/PKG-INFO +8 -6
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow.egg-info/SOURCES.txt +0 -1
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow.egg-info/requires.txt +5 -5
- {fakesnow-0.9.5 → fakesnow-0.9.6}/pyproject.toml +38 -51
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_fakes.py +135 -8
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_sqlalchemy.py +16 -9
- fakesnow-0.9.5/MANIFEST.in +0 -1
- {fakesnow-0.9.5 → fakesnow-0.9.6}/LICENSE +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/__init__.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/__main__.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/checks.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/cli.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/expr.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/fixtures.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/global_database.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/macros.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow/py.typed +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow.egg-info/dependency_links.txt +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow.egg-info/entry_points.txt +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/fakesnow.egg-info/top_level.txt +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/setup.cfg +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_checks.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_cli.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_expr.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_info_schema.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_patch.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_transforms.py +0 -0
- {fakesnow-0.9.5 → fakesnow-0.9.6}/tests/test_users.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.6
|
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,15 +213,15 @@ License-File: LICENSE
|
|
213
213
|
Requires-Dist: duckdb~=0.10.0
|
214
214
|
Requires-Dist: pyarrow
|
215
215
|
Requires-Dist: snowflake-connector-python
|
216
|
-
Requires-Dist: sqlglot~=21.
|
216
|
+
Requires-Dist: sqlglot~=21.2.0
|
217
217
|
Provides-Extra: dev
|
218
|
-
Requires-Dist: black~=23.9; extra == "dev"
|
219
218
|
Requires-Dist: build~=1.0; extra == "dev"
|
219
|
+
Requires-Dist: pandas-stubs; extra == "dev"
|
220
220
|
Requires-Dist: snowflake-connector-python[pandas,secure-local-storage]; extra == "dev"
|
221
221
|
Requires-Dist: pre-commit~=3.4; extra == "dev"
|
222
|
-
Requires-Dist: pytest~=
|
223
|
-
Requires-Dist: ruff~=0.
|
224
|
-
Requires-Dist: twine~=
|
222
|
+
Requires-Dist: pytest~=8.0; extra == "dev"
|
223
|
+
Requires-Dist: ruff~=0.3.2; extra == "dev"
|
224
|
+
Requires-Dist: twine~=5.0; extra == "dev"
|
225
225
|
Requires-Dist: snowflake-sqlalchemy~=1.5.0; extra == "dev"
|
226
226
|
Provides-Extra: notebook
|
227
227
|
Requires-Dist: duckdb-engine; extra == "notebook"
|
@@ -234,6 +234,8 @@ Requires-Dist: jupysql; extra == "notebook"
|
|
234
234
|
[](https://github.com/tekumara/fakesnow/actions/workflows/release.yml)
|
235
235
|
[](https://pypi.org/project/fakesnow/)
|
236
236
|
|
237
|
+
[](../../actions/workflows/ci.yml)
|
238
|
+
|
237
239
|
Fake [Snowflake Connector for Python](https://docs.snowflake.com/en/user-guide/python-connector). Run and mock Snowflake DB locally.
|
238
240
|
|
239
241
|
## Install
|
@@ -4,6 +4,8 @@
|
|
4
4
|
[](https://github.com/tekumara/fakesnow/actions/workflows/release.yml)
|
5
5
|
[](https://pypi.org/project/fakesnow/)
|
6
6
|
|
7
|
+
[](../../actions/workflows/ci.yml)
|
8
|
+
|
7
9
|
Fake [Snowflake Connector for Python](https://docs.snowflake.com/en/user-guide/python-connector). Run and mock Snowflake DB locally.
|
8
10
|
|
9
11
|
## Install
|
@@ -198,7 +198,10 @@ class FakeSnowflakeCursor:
|
|
198
198
|
.transform(transforms.identifier)
|
199
199
|
.transform(lambda e: transforms.show_schemas(e, self._conn.database))
|
200
200
|
.transform(lambda e: transforms.show_objects_tables(e, self._conn.database))
|
201
|
-
|
201
|
+
# TODO collapse into a single show_keys function
|
202
|
+
.transform(lambda e: transforms.show_keys(e, self._conn.database, kind="PRIMARY"))
|
203
|
+
.transform(lambda e: transforms.show_keys(e, self._conn.database, kind="UNIQUE"))
|
204
|
+
.transform(lambda e: transforms.show_keys(e, self._conn.database, kind="FOREIGN"))
|
202
205
|
.transform(transforms.show_users)
|
203
206
|
.transform(transforms.create_user)
|
204
207
|
)
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from pathlib import Path
|
4
4
|
from string import Template
|
5
|
-
from typing import cast
|
5
|
+
from typing import Literal, cast
|
6
6
|
|
7
7
|
import sqlglot
|
8
8
|
from sqlglot import exp
|
@@ -38,7 +38,8 @@ def create_database(expression: exp.Expression, db_path: Path | None = None) ->
|
|
38
38
|
"""
|
39
39
|
|
40
40
|
if isinstance(expression, exp.Create) and str(expression.args.get("kind")).upper() == "DATABASE":
|
41
|
-
|
41
|
+
ident = expression.find(exp.Identifier)
|
42
|
+
assert ident, f"No identifier in {expression.sql}"
|
42
43
|
db_name = ident.this
|
43
44
|
db_file = f"{db_path/db_name}.db" if db_path else ":memory:"
|
44
45
|
|
@@ -681,55 +682,61 @@ def set_schema(expression: exp.Expression, current_database: str | None) -> exp.
|
|
681
682
|
return expression
|
682
683
|
|
683
684
|
|
684
|
-
SQL_SHOW_OBJECTS = """
|
685
|
-
select
|
686
|
-
to_timestamp(0)::timestamptz as 'created_on',
|
687
|
-
table_name as 'name',
|
688
|
-
case when table_type='BASE TABLE' then 'TABLE' else table_type end as 'kind',
|
689
|
-
table_catalog as 'database_name',
|
690
|
-
table_schema as 'schema_name'
|
691
|
-
from information_schema.tables
|
692
|
-
"""
|
693
|
-
|
694
|
-
|
695
685
|
def show_objects_tables(expression: exp.Expression, current_database: str | None = None) -> exp.Expression:
|
696
686
|
"""Transform SHOW OBJECTS/TABLES to a query against the information_schema.tables table.
|
697
687
|
|
698
688
|
See https://docs.snowflake.com/en/sql-reference/sql/show-objects
|
699
689
|
https://docs.snowflake.com/en/sql-reference/sql/show-tables
|
700
690
|
"""
|
701
|
-
if (
|
691
|
+
if not (
|
702
692
|
isinstance(expression, exp.Show)
|
703
693
|
and isinstance(expression.this, str)
|
704
|
-
and expression.this.upper()
|
694
|
+
and (show := expression.this.upper())
|
695
|
+
and show in {"OBJECTS", "TABLES"}
|
705
696
|
):
|
706
|
-
|
707
|
-
table = expression.find(exp.Table)
|
708
|
-
|
709
|
-
if scope_kind == "DATABASE":
|
710
|
-
catalog = (table and table.name) or current_database
|
711
|
-
schema = None
|
712
|
-
elif scope_kind == "SCHEMA" and table:
|
713
|
-
catalog = table.db or current_database
|
714
|
-
schema = table.name
|
715
|
-
else:
|
716
|
-
# all objects / tables
|
717
|
-
catalog = None
|
718
|
-
schema = None
|
719
|
-
|
720
|
-
tables_only = "table_type = 'BASE TABLE' and " if expression.this.upper() == "TABLES" else ""
|
721
|
-
exclude_fakesnow_tables = "not (table_schema == 'information_schema' and table_name like '_fs_%%')"
|
722
|
-
# without a database will show everything in the "account"
|
723
|
-
table_catalog = f" and table_catalog = '{catalog}'" if catalog else ""
|
724
|
-
schema = f" and table_schema = '{schema}'" if schema else ""
|
725
|
-
limit = limit.sql() if (limit := expression.args.get("limit")) and isinstance(limit, exp.Expression) else ""
|
726
|
-
|
727
|
-
return sqlglot.parse_one(
|
728
|
-
f"{SQL_SHOW_OBJECTS} where {tables_only}{exclude_fakesnow_tables}{table_catalog}{schema}{limit}",
|
729
|
-
read="duckdb",
|
730
|
-
)
|
697
|
+
return expression
|
731
698
|
|
732
|
-
|
699
|
+
scope_kind = expression.args.get("scope_kind")
|
700
|
+
table = expression.find(exp.Table)
|
701
|
+
|
702
|
+
if scope_kind == "DATABASE":
|
703
|
+
catalog = (table and table.name) or current_database
|
704
|
+
schema = None
|
705
|
+
elif scope_kind == "SCHEMA" and table:
|
706
|
+
catalog = table.db or current_database
|
707
|
+
schema = table.name
|
708
|
+
else:
|
709
|
+
# all objects / tables
|
710
|
+
catalog = None
|
711
|
+
schema = None
|
712
|
+
|
713
|
+
tables_only = "table_type = 'BASE TABLE' and " if show == "TABLES" else ""
|
714
|
+
exclude_fakesnow_tables = "not (table_schema == 'information_schema' and table_name like '_fs_%%')"
|
715
|
+
# without a database will show everything in the "account"
|
716
|
+
table_catalog = f" and table_catalog = '{catalog}'" if catalog else ""
|
717
|
+
schema = f" and table_schema = '{schema}'" if schema else ""
|
718
|
+
limit = limit.sql() if (limit := expression.args.get("limit")) and isinstance(limit, exp.Expression) else ""
|
719
|
+
|
720
|
+
columns = [
|
721
|
+
"to_timestamp(0)::timestamptz as 'created_on'",
|
722
|
+
"table_name as 'name'",
|
723
|
+
"case when table_type='BASE TABLE' then 'TABLE' else table_type end as 'kind'",
|
724
|
+
"table_catalog as 'database_name'",
|
725
|
+
"table_schema as 'schema_name'",
|
726
|
+
]
|
727
|
+
|
728
|
+
terse = expression.args["terse"]
|
729
|
+
if not terse:
|
730
|
+
columns.append('null as "comment"')
|
731
|
+
|
732
|
+
columns_str = ", ".join(columns)
|
733
|
+
|
734
|
+
query = (
|
735
|
+
f"SELECT {columns_str} from information_schema.tables "
|
736
|
+
f"where {tables_only}{exclude_fakesnow_tables}{table_catalog}{schema}{limit}"
|
737
|
+
)
|
738
|
+
|
739
|
+
return sqlglot.parse_one(query, read="duckdb")
|
733
740
|
|
734
741
|
|
735
742
|
SQL_SHOW_SCHEMAS = """
|
@@ -999,32 +1006,70 @@ def create_user(expression: exp.Expression) -> exp.Expression:
|
|
999
1006
|
return expression
|
1000
1007
|
|
1001
1008
|
|
1002
|
-
def
|
1003
|
-
|
1009
|
+
def show_keys(
|
1010
|
+
expression: exp.Expression,
|
1011
|
+
current_database: str | None = None,
|
1012
|
+
*,
|
1013
|
+
kind: Literal["PRIMARY", "UNIQUE", "FOREIGN"],
|
1014
|
+
) -> exp.Expression:
|
1015
|
+
"""Transform SHOW <kind> KEYS to a query against the duckdb_constraints meta-table.
|
1004
1016
|
|
1005
1017
|
https://docs.snowflake.com/en/sql-reference/sql/show-primary-keys
|
1006
1018
|
"""
|
1019
|
+
snowflake_kind = kind
|
1020
|
+
if kind == "FOREIGN":
|
1021
|
+
snowflake_kind = "IMPORTED"
|
1022
|
+
|
1007
1023
|
if (
|
1008
1024
|
isinstance(expression, exp.Show)
|
1009
1025
|
and isinstance(expression.this, str)
|
1010
|
-
and expression.this.upper() == "
|
1026
|
+
and expression.this.upper() == f"{snowflake_kind} KEYS"
|
1011
1027
|
):
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
+
if kind == "FOREIGN":
|
1029
|
+
statement = f"""
|
1030
|
+
SELECT
|
1031
|
+
to_timestamp(0)::timestamptz as created_on,
|
1032
|
+
|
1033
|
+
'' as pk_database_name,
|
1034
|
+
'' as pk_schema_name,
|
1035
|
+
'' as pk_table_name,
|
1036
|
+
'' as pk_column_name,
|
1037
|
+
unnest(constraint_column_names) as pk_column_name,
|
1038
|
+
|
1039
|
+
database_name as fk_database_name,
|
1040
|
+
schema_name as fk_schema_name,
|
1041
|
+
table_name as fk_table_name,
|
1042
|
+
unnest(constraint_column_names) as fk_column_name,
|
1043
|
+
1 as key_sequence,
|
1044
|
+
'NO ACTION' as update_rule,
|
1045
|
+
'NO ACTION' as delete_rule,
|
1046
|
+
LOWER(CONCAT(database_name, '_', schema_name, '_', table_name, '_pkey')) AS fk_name,
|
1047
|
+
LOWER(CONCAT(database_name, '_', schema_name, '_', table_name, '_pkey')) AS pk_name,
|
1048
|
+
'NOT DEFERRABLE' as deferrability,
|
1049
|
+
'false' as rely,
|
1050
|
+
null as "comment"
|
1051
|
+
FROM duckdb_constraints
|
1052
|
+
WHERE constraint_type = 'PRIMARY KEY'
|
1053
|
+
AND database_name = '{current_database}'
|
1054
|
+
AND table_name NOT LIKE '_fs_%'
|
1055
|
+
"""
|
1056
|
+
else:
|
1057
|
+
statement = f"""
|
1058
|
+
SELECT
|
1059
|
+
to_timestamp(0)::timestamptz as created_on,
|
1060
|
+
database_name as database_name,
|
1061
|
+
schema_name as schema_name,
|
1062
|
+
table_name as table_name,
|
1063
|
+
unnest(constraint_column_names) as column_name,
|
1064
|
+
1 as key_sequence,
|
1065
|
+
LOWER(CONCAT(database_name, '_', schema_name, '_', table_name, '_pkey')) AS constraint_name,
|
1066
|
+
'false' as rely,
|
1067
|
+
null as "comment"
|
1068
|
+
FROM duckdb_constraints
|
1069
|
+
WHERE constraint_type = '{kind} KEY'
|
1070
|
+
AND database_name = '{current_database}'
|
1071
|
+
AND table_name NOT LIKE '_fs_%'
|
1072
|
+
"""
|
1028
1073
|
|
1029
1074
|
scope_kind = expression.args.get("scope_kind")
|
1030
1075
|
if scope_kind:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fakesnow
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.6
|
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,15 +213,15 @@ License-File: LICENSE
|
|
213
213
|
Requires-Dist: duckdb~=0.10.0
|
214
214
|
Requires-Dist: pyarrow
|
215
215
|
Requires-Dist: snowflake-connector-python
|
216
|
-
Requires-Dist: sqlglot~=21.
|
216
|
+
Requires-Dist: sqlglot~=21.2.0
|
217
217
|
Provides-Extra: dev
|
218
|
-
Requires-Dist: black~=23.9; extra == "dev"
|
219
218
|
Requires-Dist: build~=1.0; extra == "dev"
|
219
|
+
Requires-Dist: pandas-stubs; extra == "dev"
|
220
220
|
Requires-Dist: snowflake-connector-python[pandas,secure-local-storage]; extra == "dev"
|
221
221
|
Requires-Dist: pre-commit~=3.4; extra == "dev"
|
222
|
-
Requires-Dist: pytest~=
|
223
|
-
Requires-Dist: ruff~=0.
|
224
|
-
Requires-Dist: twine~=
|
222
|
+
Requires-Dist: pytest~=8.0; extra == "dev"
|
223
|
+
Requires-Dist: ruff~=0.3.2; extra == "dev"
|
224
|
+
Requires-Dist: twine~=5.0; extra == "dev"
|
225
225
|
Requires-Dist: snowflake-sqlalchemy~=1.5.0; extra == "dev"
|
226
226
|
Provides-Extra: notebook
|
227
227
|
Requires-Dist: duckdb-engine; extra == "notebook"
|
@@ -234,6 +234,8 @@ Requires-Dist: jupysql; extra == "notebook"
|
|
234
234
|
[](https://github.com/tekumara/fakesnow/actions/workflows/release.yml)
|
235
235
|
[](https://pypi.org/project/fakesnow/)
|
236
236
|
|
237
|
+
[](../../actions/workflows/ci.yml)
|
238
|
+
|
237
239
|
Fake [Snowflake Connector for Python](https://docs.snowflake.com/en/user-guide/python-connector). Run and mock Snowflake DB locally.
|
238
240
|
|
239
241
|
## Install
|
@@ -1,16 +1,16 @@
|
|
1
1
|
duckdb~=0.10.0
|
2
2
|
pyarrow
|
3
3
|
snowflake-connector-python
|
4
|
-
sqlglot~=21.
|
4
|
+
sqlglot~=21.2.0
|
5
5
|
|
6
6
|
[dev]
|
7
|
-
black~=23.9
|
8
7
|
build~=1.0
|
8
|
+
pandas-stubs
|
9
9
|
snowflake-connector-python[pandas,secure-local-storage]
|
10
10
|
pre-commit~=3.4
|
11
|
-
pytest~=
|
12
|
-
ruff~=0.
|
13
|
-
twine~=
|
11
|
+
pytest~=8.0
|
12
|
+
ruff~=0.3.2
|
13
|
+
twine~=5.0
|
14
14
|
snowflake-sqlalchemy~=1.5.0
|
15
15
|
|
16
16
|
[notebook]
|
@@ -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.6"
|
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~=0.10.0",
|
12
12
|
"pyarrow",
|
13
13
|
"snowflake-connector-python",
|
14
|
-
"sqlglot~=21.
|
14
|
+
"sqlglot~=21.2.0",
|
15
15
|
]
|
16
16
|
|
17
17
|
[project.urls]
|
@@ -22,29 +22,26 @@ fakesnow = "fakesnow.cli:main"
|
|
22
22
|
|
23
23
|
[project.optional-dependencies]
|
24
24
|
dev = [
|
25
|
-
"black~=23.9",
|
26
25
|
"build~=1.0",
|
26
|
+
# to fix https://github.com/pandas-dev/pandas/issues/56995
|
27
|
+
"pandas-stubs",
|
27
28
|
# include compatible version of pandas, and secure-local-storage for token caching
|
28
29
|
"snowflake-connector-python[pandas, secure-local-storage]",
|
29
30
|
"pre-commit~=3.4",
|
30
|
-
"pytest~=
|
31
|
-
"ruff~=0.
|
32
|
-
"twine~=
|
31
|
+
"pytest~=8.0",
|
32
|
+
"ruff~=0.3.2",
|
33
|
+
"twine~=5.0",
|
33
34
|
"snowflake-sqlalchemy~=1.5.0",
|
34
35
|
]
|
35
36
|
# for debugging, see https://duckdb.org/docs/guides/python/jupyter.html
|
36
37
|
notebook = ["duckdb-engine", "ipykernel", "jupysql"]
|
37
38
|
|
38
|
-
[tool.setuptools.packages.find]
|
39
|
-
where = ["."]
|
40
|
-
exclude = ["tests*"]
|
41
|
-
|
42
39
|
[build-system]
|
43
|
-
requires = ["setuptools~=
|
40
|
+
requires = ["setuptools~=69.1", "wheel~=0.42"]
|
44
41
|
|
45
|
-
[tool.
|
46
|
-
|
47
|
-
|
42
|
+
[tool.setuptools.packages.find]
|
43
|
+
where = ["."]
|
44
|
+
exclude = ["tests*", "node_modules*", "build*"]
|
48
45
|
|
49
46
|
[tool.pyright]
|
50
47
|
venvPath = "."
|
@@ -54,63 +51,53 @@ strictListInference = true
|
|
54
51
|
strictDictionaryInference = true
|
55
52
|
strictParameterNoneValue = true
|
56
53
|
reportTypedDictNotRequiredAccess = false
|
54
|
+
reportIncompatibleMethodOverride = true
|
55
|
+
reportUnnecessaryTypeIgnoreComment = true
|
57
56
|
|
58
57
|
[tool.ruff]
|
59
|
-
# Compatibility between Ruff and Black
|
60
|
-
# https://beta.ruff.rs/docs/faq/#is-ruff-compatible-with-black
|
61
58
|
line-length = 120
|
59
|
+
# first-party imports for sorting
|
60
|
+
src = ["."]
|
61
|
+
fix = true
|
62
|
+
show-fixes = true
|
62
63
|
|
64
|
+
[tool.ruff.lint]
|
63
65
|
# rules to enable/ignore
|
64
66
|
select = [
|
65
|
-
# pyflakes
|
66
|
-
"
|
67
|
-
# pycodestyle
|
68
|
-
"
|
69
|
-
"
|
70
|
-
#
|
71
|
-
"
|
72
|
-
#
|
73
|
-
"
|
74
|
-
#
|
75
|
-
"
|
76
|
-
#
|
77
|
-
"
|
78
|
-
#
|
79
|
-
|
80
|
-
# flake8-self
|
81
|
-
"SLF",
|
82
|
-
# pyupgrade
|
83
|
-
"UP",
|
84
|
-
# perflint
|
85
|
-
"PERF",
|
86
|
-
# ruff-specific
|
87
|
-
"RUF",
|
88
|
-
# flake8-simplify
|
89
|
-
"SIM",
|
90
|
-
# flake8-builtins
|
91
|
-
"A"
|
67
|
+
"F", # pyflakes
|
68
|
+
"E", # pycodestyle
|
69
|
+
"W", # pycodestyle
|
70
|
+
"ANN", # type annotations
|
71
|
+
"N", # pep8-naming
|
72
|
+
"B", # bugbear
|
73
|
+
"I", # isort
|
74
|
+
# "ARG", # flake8-unused-arguments - disabled because our fakes don't use all arguments
|
75
|
+
"SLF", # flake8-self
|
76
|
+
"UP", # pyupgrade
|
77
|
+
"PERF", # perflint
|
78
|
+
"RUF", # ruff-specific
|
79
|
+
"SIM", # flake8-simplify
|
80
|
+
"S113", # request-without-timeout
|
81
|
+
"A", # flake8-builtins
|
92
82
|
]
|
93
83
|
ignore = [
|
94
|
-
# allow untyped self and cls args
|
84
|
+
# allow untyped self and cls args
|
95
85
|
"ANN101",
|
96
86
|
"ANN102",
|
87
|
+
# allow no return type from dunder methods
|
97
88
|
"ANN204",
|
98
89
|
# allow == True because pandas dataframes overload equality
|
99
90
|
"E712",
|
100
91
|
]
|
101
|
-
# first-party imports for sorting
|
102
|
-
src = ["."]
|
103
|
-
fix = true
|
104
|
-
show-fixes = true
|
105
92
|
|
106
|
-
[tool.ruff.isort]
|
93
|
+
[tool.ruff.lint.isort]
|
107
94
|
combine-as-imports = true
|
108
95
|
force-wrap-aliases = true
|
109
96
|
|
110
|
-
[tool.ruff.per-file-ignores]
|
97
|
+
[tool.ruff.lint.per-file-ignores]
|
111
98
|
# test functions don't need return types
|
112
99
|
"tests/*" = ["ANN201", "ANN202"]
|
113
100
|
|
114
|
-
[tool.ruff.flake8-annotations]
|
101
|
+
[tool.ruff.lint.flake8-annotations]
|
115
102
|
# allow *args: Any, **kwargs: Any
|
116
103
|
allow-star-arg-any = true
|
@@ -115,17 +115,21 @@ def test_connect_different_sessions_use_database(_fakesnow_no_auto_create: None)
|
|
115
115
|
|
116
116
|
def test_connect_reuse_db():
|
117
117
|
with tempfile.TemporaryDirectory(prefix="fakesnow-test") as db_path:
|
118
|
-
with
|
119
|
-
|
120
|
-
|
118
|
+
with (
|
119
|
+
fakesnow.patch(db_path=db_path),
|
120
|
+
snowflake.connector.connect(database="db1", schema="schema1") as conn,
|
121
|
+
conn.cursor() as cur,
|
122
|
+
):
|
121
123
|
# creates db1.schema1.example
|
122
124
|
cur.execute("create table example (x int)")
|
123
125
|
cur.execute("insert into example values (420)")
|
124
126
|
|
125
127
|
# reconnect
|
126
|
-
with
|
127
|
-
|
128
|
-
|
128
|
+
with (
|
129
|
+
fakesnow.patch(db_path=db_path),
|
130
|
+
snowflake.connector.connect(database="db1", schema="schema1") as conn,
|
131
|
+
conn.cursor() as cur,
|
132
|
+
):
|
129
133
|
assert cur.execute("select * from example").fetchall() == [(420,)]
|
130
134
|
|
131
135
|
|
@@ -864,6 +868,76 @@ def test_semi_structured_types(cur: snowflake.connector.cursor.SnowflakeCursor):
|
|
864
868
|
]
|
865
869
|
|
866
870
|
|
871
|
+
@pytest.mark.xfail(
|
872
|
+
reason="only partial supports exists to support sqlalchemy, see test_reflect",
|
873
|
+
)
|
874
|
+
def test_show_keys(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
875
|
+
dcur.execute("CREATE TABLE test_table (id INT PRIMARY KEY, name TEXT UNIQUE)")
|
876
|
+
dcur.execute("CREATE TABLE test_table2 (id INT, other_id INT, FOREIGN KEY (other_id) REFERENCES test_table(id))")
|
877
|
+
|
878
|
+
dcur.execute("SHOW PRIMARY KEYS")
|
879
|
+
primary_keys = dcur.fetchall()
|
880
|
+
assert primary_keys == [
|
881
|
+
{
|
882
|
+
"created_on": datetime.datetime(1970, 1, 1, 0, 0, tzinfo=pytz.utc),
|
883
|
+
"database_name": "DB1",
|
884
|
+
"schema_name": "SCHEMA1",
|
885
|
+
"table_name": "TEST_TABLE",
|
886
|
+
"column_name": "ID",
|
887
|
+
"key_sequence": 1,
|
888
|
+
"constraint_name": "SYS_CONSTRAINT_DB1_SCHEMA1_TEST_TABLE_ID_pk",
|
889
|
+
"rely": "false",
|
890
|
+
"comment": None,
|
891
|
+
}
|
892
|
+
]
|
893
|
+
|
894
|
+
dcur.execute("SHOW UNIQUE KEYS")
|
895
|
+
unique_keys = dcur.fetchall()
|
896
|
+
assert unique_keys == [
|
897
|
+
{
|
898
|
+
"created_on": datetime.datetime(1970, 1, 1, 0, 0, tzinfo=pytz.utc),
|
899
|
+
"database_name": "DB1",
|
900
|
+
"schema_name": "SCHEMA1",
|
901
|
+
"table_name": "TEST_TABLE",
|
902
|
+
"column_name": "NAME",
|
903
|
+
"key_sequence": 1,
|
904
|
+
"constraint_name": "SYS_CONSTRAINT_DB1_SCHEMA1_TEST_TABLE_NAME_uk",
|
905
|
+
"rely": "false",
|
906
|
+
"comment": None,
|
907
|
+
}
|
908
|
+
]
|
909
|
+
|
910
|
+
dcur.execute("SHOW IMPORTED KEYS")
|
911
|
+
foreign_keys = dcur.fetchall()
|
912
|
+
assert foreign_keys == [
|
913
|
+
{
|
914
|
+
"created_on": datetime.datetime(1970, 1, 1, 0, 0, tzinfo=pytz.utc),
|
915
|
+
"pk_database_name": "DB1",
|
916
|
+
"pk_schema_name": "SCHEMA1",
|
917
|
+
"pk_table_name": "TEST_TABLE",
|
918
|
+
"pk_column_name": "ID",
|
919
|
+
"fk_database_name": "DB1",
|
920
|
+
"fk_schema_name": "SCHEMA1",
|
921
|
+
"fk_table_name": "TEST_TABLE2",
|
922
|
+
"fk_column_name": "OTHER_ID",
|
923
|
+
"key_sequence": 1,
|
924
|
+
"update_rule": "NO ACTION",
|
925
|
+
"delete_rule": "NO ACTION",
|
926
|
+
"fk_name": "SYS_CONSTRAINT_DB1_SCHEMA1_TEST_TABLE2_OTHER_ID_fk",
|
927
|
+
"pk_name": "SYS_CONSTRAINT_DB1_SCHEMA1_TEST_TABLE_ID_pk",
|
928
|
+
"deferrability": "NOT DEFERRABLE",
|
929
|
+
"rely": "false",
|
930
|
+
"comment": None,
|
931
|
+
}
|
932
|
+
]
|
933
|
+
|
934
|
+
dcur.execute("SHOW PRIMARY KEYS IN SCHEMA")
|
935
|
+
assert dcur.fetchall() == primary_keys
|
936
|
+
|
937
|
+
dcur.execute("SHOW PRIMARY KEYS IN DATABASE")
|
938
|
+
assert dcur.fetchall() == primary_keys
|
939
|
+
|
940
|
+
|
867
941
|
def test_show_objects(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
868
942
|
dcur.execute("create table example(x int)")
|
869
943
|
dcur.execute("create view view1 as select * from example")
|
@@ -885,6 +959,7 @@ def test_show_objects(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
|
885
959
|
},
|
886
960
|
]
|
887
961
|
assert dcur.fetchall() == objects
|
962
|
+
|
888
963
|
dcur.execute("show terse objects in database")
|
889
964
|
assert dcur.fetchall() == [
|
890
965
|
*objects,
|
@@ -905,6 +980,24 @@ def test_show_objects(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
|
905
980
|
]
|
906
981
|
assert [r.name for r in dcur.description] == ["created_on", "name", "kind", "database_name", "schema_name"]
|
907
982
|
|
983
|
+
dcur.execute("show objects").fetchall()
|
984
|
+
assert [r.name for r in dcur.description] == [
|
985
|
+
"created_on",
|
986
|
+
"name",
|
987
|
+
"kind",
|
988
|
+
"database_name",
|
989
|
+
"schema_name",
|
990
|
+
"comment",
|
991
|
+
# TODO: include these columns
|
992
|
+
# "cluster_by",
|
993
|
+
# "rows",
|
994
|
+
# "bytes",
|
995
|
+
# "owner",
|
996
|
+
# "retention_time",
|
997
|
+
# "owner_role_type",
|
998
|
+
# "budget"
|
999
|
+
]
|
1000
|
+
|
908
1001
|
|
909
1002
|
def test_show_schemas(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
910
1003
|
dcur.execute("show terse schemas in database db1 limit 100")
|
@@ -943,7 +1036,41 @@ def test_show_tables(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
|
943
1036
|
# assert dcur.fetchall() == objects
|
944
1037
|
dcur.execute("show terse tables in db1.schema1")
|
945
1038
|
assert dcur.fetchall() == objects
|
946
|
-
assert [r.name for r in dcur.description] == [
|
1039
|
+
assert [r.name for r in dcur.description] == [
|
1040
|
+
"created_on",
|
1041
|
+
"name",
|
1042
|
+
"kind",
|
1043
|
+
"database_name",
|
1044
|
+
"schema_name",
|
1045
|
+
]
|
1046
|
+
|
1047
|
+
dcur.execute("show tables in db1.schema1")
|
1048
|
+
assert [r.name for r in dcur.description] == [
|
1049
|
+
"created_on",
|
1050
|
+
"name",
|
1051
|
+
"kind",
|
1052
|
+
"database_name",
|
1053
|
+
"schema_name",
|
1054
|
+
"comment",
|
1055
|
+
# TODO: include these columns
|
1056
|
+
# "cluster_by",
|
1057
|
+
# "rows",
|
1058
|
+
# "bytes",
|
1059
|
+
# "owner",
|
1060
|
+
# "retention_time",
|
1061
|
+
# "automatic_clustering",
|
1062
|
+
# "change_tracking",
|
1063
|
+
# "search_optimization",
|
1064
|
+
# "search_optimization_progress",
|
1065
|
+
# "search_optimization_bytes",
|
1066
|
+
# "is_external",
|
1067
|
+
# "enable_schema_evolution",
|
1068
|
+
# "owner_role_type",
|
1069
|
+
# "is_event",
|
1070
|
+
# "budget",
|
1071
|
+
# "is_hybrid",
|
1072
|
+
# "is_iceberg",
|
1073
|
+
]
|
947
1074
|
|
948
1075
|
|
949
1076
|
def test_show_primary_keys(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
@@ -981,7 +1108,7 @@ def test_show_primary_keys(dcur: snowflake.connector.cursor.SnowflakeCursor):
|
|
981
1108
|
result2 = dcur.fetchall()
|
982
1109
|
assert result == result2
|
983
1110
|
|
984
|
-
# Assertion to sanity check that the above "in schema" filter
|
1111
|
+
# Assertion to sanity check that the above "in schema" filter isn't wrong, and in fact filters
|
985
1112
|
dcur.execute("show primary keys in schema db1.information_schema")
|
986
1113
|
result3 = dcur.fetchall()
|
987
1114
|
assert result3 == []
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import
|
2
|
-
|
1
|
+
from typing import cast
|
2
|
+
|
3
3
|
from sqlalchemy import Column, MetaData, Table, types
|
4
4
|
from sqlalchemy.engine import Engine
|
5
5
|
from sqlalchemy.sql.expression import TextClause
|
@@ -18,7 +18,7 @@ def test_engine(snowflake_engine: Engine):
|
|
18
18
|
def test_metadata_create_all(snowflake_engine: Engine):
|
19
19
|
metadata = MetaData()
|
20
20
|
|
21
|
-
table = Table("foo", metadata, Column(types.Integer, name="id"), Column(types.String, name="name"))
|
21
|
+
table = cast(Table, Table("foo", metadata, Column(types.Integer, name="id"), Column(types.String, name="name")))
|
22
22
|
metadata.create_all(bind=snowflake_engine)
|
23
23
|
|
24
24
|
with snowflake_engine.connect() as conn:
|
@@ -27,13 +27,20 @@ def test_metadata_create_all(snowflake_engine: Engine):
|
|
27
27
|
assert result.fetchall() == []
|
28
28
|
|
29
29
|
|
30
|
-
@pytest.mark.xfail(
|
31
|
-
reason="sqlglot currently has unsupported SHOW variants",
|
32
|
-
strict=True,
|
33
|
-
raises=duckdb.ParserException,
|
34
|
-
)
|
35
30
|
def test_reflect(snowflake_engine: Engine):
|
36
|
-
snowflake_engine.
|
31
|
+
with snowflake_engine.connect() as conn:
|
32
|
+
conn.execute(TextClause("CREATE TABLE foo (id INTEGER, name VARCHAR)"))
|
37
33
|
|
38
34
|
metadata = MetaData()
|
39
35
|
metadata.reflect(bind=snowflake_engine, only=["foo"])
|
36
|
+
|
37
|
+
assert metadata.tables
|
38
|
+
foo_table: Table = metadata.tables["foo"]
|
39
|
+
|
40
|
+
with snowflake_engine.connect() as conn:
|
41
|
+
result = conn.execute(foo_table.insert().values(id=1, name="one"))
|
42
|
+
|
43
|
+
result = conn.execute(foo_table.select())
|
44
|
+
|
45
|
+
assert result
|
46
|
+
assert result.fetchall() == [(1, "one")]
|
fakesnow-0.9.5/MANIFEST.in
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
recursive-include fakesnow py.typed
|
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
|