fakesnow 0.9.10__tar.gz → 0.9.12__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.10 → fakesnow-0.9.12}/PKG-INFO +2 -2
- {fakesnow-0.9.10 → fakesnow-0.9.12}/README.md +1 -1
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/fakes.py +23 -4
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow.egg-info/PKG-INFO +2 -2
- {fakesnow-0.9.10 → fakesnow-0.9.12}/pyproject.toml +1 -1
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_fakes.py +35 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/LICENSE +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/__init__.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/__main__.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/checks.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/cli.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/expr.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/fixtures.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/global_database.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/info_schema.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/macros.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/py.typed +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow/transforms.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow.egg-info/SOURCES.txt +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow.egg-info/dependency_links.txt +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow.egg-info/entry_points.txt +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow.egg-info/requires.txt +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/fakesnow.egg-info/top_level.txt +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/setup.cfg +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_checks.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_cli.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_expr.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_info_schema.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_patch.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_sqlalchemy.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/tests/test_transforms.py +0 -0
- {fakesnow-0.9.10 → fakesnow-0.9.12}/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.12
|
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
|
@@ -357,7 +357,7 @@ For more detail see [tests/test_fakes.py](tests/test_fakes.py)
|
|
357
357
|
## Caveats
|
358
358
|
|
359
359
|
- The order of rows is non deterministic and may not match Snowflake unless ORDER BY is fully specified.
|
360
|
-
- A more liberal Snowflake SQL dialect than a real Snowflake instance is supported, ie: some queries might pass using fakesnow that a real Snowflake instance would reject.
|
360
|
+
- A more liberal Snowflake SQL dialect than used by a real Snowflake instance is supported, ie: some queries might pass using fakesnow that a real Snowflake instance would reject.
|
361
361
|
|
362
362
|
## Contributing
|
363
363
|
|
@@ -127,7 +127,7 @@ For more detail see [tests/test_fakes.py](tests/test_fakes.py)
|
|
127
127
|
## Caveats
|
128
128
|
|
129
129
|
- The order of rows is non deterministic and may not match Snowflake unless ORDER BY is fully specified.
|
130
|
-
- A more liberal Snowflake SQL dialect than a real Snowflake instance is supported, ie: some queries might pass using fakesnow that a real Snowflake instance would reject.
|
130
|
+
- A more liberal Snowflake SQL dialect than used by a real Snowflake instance is supported, ie: some queries might pass using fakesnow that a real Snowflake instance would reject.
|
131
131
|
|
132
132
|
## Contributing
|
133
133
|
|
@@ -16,6 +16,7 @@ from sqlglot import exp
|
|
16
16
|
if TYPE_CHECKING:
|
17
17
|
import pandas as pd
|
18
18
|
import pyarrow.lib
|
19
|
+
import numpy as np
|
19
20
|
import pyarrow
|
20
21
|
import snowflake.connector.converter
|
21
22
|
import snowflake.connector.errors
|
@@ -606,9 +607,7 @@ class FakeSnowflakeConnection:
|
|
606
607
|
def rollback(self) -> None:
|
607
608
|
self.cursor().execute("ROLLBACK")
|
608
609
|
|
609
|
-
def _insert_df(
|
610
|
-
self, df: pd.DataFrame, table_name: str, database: str | None = None, schema: str | None = None
|
611
|
-
) -> int:
|
610
|
+
def _insert_df(self, df: pd.DataFrame, table_name: str) -> int:
|
612
611
|
# Objects in dataframes are written as parquet structs, and snowflake loads parquet structs as json strings.
|
613
612
|
# Whereas duckdb analyses a dataframe see https://duckdb.org/docs/api/python/data_ingestion.html#pandas-dataframes--object-columns
|
614
613
|
# and converts a object to the most specific type possible, eg: dict -> STRUCT, MAP or varchar, and list -> LIST
|
@@ -680,6 +679,15 @@ WritePandasResult = tuple[
|
|
680
679
|
]
|
681
680
|
|
682
681
|
|
682
|
+
def sql_type(dtype: np.dtype) -> str:
|
683
|
+
if str(dtype) == "int64":
|
684
|
+
return "NUMBER"
|
685
|
+
elif str(dtype) == "object":
|
686
|
+
return "VARCHAR"
|
687
|
+
else:
|
688
|
+
raise NotImplementedError(f"sql_type {dtype=}")
|
689
|
+
|
690
|
+
|
683
691
|
def write_pandas(
|
684
692
|
conn: FakeSnowflakeConnection,
|
685
693
|
df: pd.DataFrame,
|
@@ -697,7 +705,18 @@ def write_pandas(
|
|
697
705
|
table_type: Literal["", "temp", "temporary", "transient"] = "",
|
698
706
|
**kwargs: Any,
|
699
707
|
) -> WritePandasResult:
|
700
|
-
|
708
|
+
name = table_name
|
709
|
+
if schema:
|
710
|
+
name = f"{schema}.{name}"
|
711
|
+
if database:
|
712
|
+
name = f"{database}.{name}"
|
713
|
+
|
714
|
+
if auto_create_table:
|
715
|
+
cols = [f"{c} {sql_type(t)}" for c, t in df.dtypes.to_dict().items()]
|
716
|
+
|
717
|
+
conn.cursor().execute(f"CREATE TABLE IF NOT EXISTS {name} ({','.join(cols)})")
|
718
|
+
|
719
|
+
count = conn._insert_df(df, name) # noqa: SLF001
|
701
720
|
|
702
721
|
# mocks https://docs.snowflake.com/en/sql-reference/sql/copy-into-table.html#output
|
703
722
|
mock_copy_results = [("fakesnow/file0.txt", "LOADED", count, count, 1, 0, None, None, None, None)]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: fakesnow
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.12
|
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
|
@@ -357,7 +357,7 @@ For more detail see [tests/test_fakes.py](tests/test_fakes.py)
|
|
357
357
|
## Caveats
|
358
358
|
|
359
359
|
- The order of rows is non deterministic and may not match Snowflake unless ORDER BY is fully specified.
|
360
|
-
- A more liberal Snowflake SQL dialect than a real Snowflake instance is supported, ie: some queries might pass using fakesnow that a real Snowflake instance would reject.
|
360
|
+
- A more liberal Snowflake SQL dialect than used by a real Snowflake instance is supported, ie: some queries might pass using fakesnow that a real Snowflake instance would reject.
|
361
361
|
|
362
362
|
## Contributing
|
363
363
|
|
@@ -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.12"
|
5
5
|
readme = "README.md"
|
6
6
|
license = { file = "LICENSE" }
|
7
7
|
classifiers = ["License :: OSI Approved :: MIT License"]
|
@@ -1502,6 +1502,21 @@ def test_json_extract_cast_as_varchar(dcur: snowflake.connector.cursor.DictCurso
|
|
1502
1502
|
assert dcur.fetchall() == [{"C_STR_NUMBER": 100, "C_NUM_NUMBER": 100}]
|
1503
1503
|
|
1504
1504
|
|
1505
|
+
def test_write_pandas_auto_create(conn: snowflake.connector.SnowflakeConnection):
|
1506
|
+
with conn.cursor() as cur:
|
1507
|
+
df = pd.DataFrame.from_records(
|
1508
|
+
[
|
1509
|
+
{"ID": 1, "FIRST_NAME": "Jenny"},
|
1510
|
+
{"ID": 2, "FIRST_NAME": "Jasper"},
|
1511
|
+
]
|
1512
|
+
)
|
1513
|
+
snowflake.connector.pandas_tools.write_pandas(conn, df, "CUSTOMERS", auto_create_table=True)
|
1514
|
+
|
1515
|
+
cur.execute("select id, first_name from customers")
|
1516
|
+
|
1517
|
+
assert cur.fetchall() == [(1, "Jenny"), (2, "Jasper")]
|
1518
|
+
|
1519
|
+
|
1505
1520
|
def test_write_pandas_quoted_column_names(conn: snowflake.connector.SnowflakeConnection):
|
1506
1521
|
with conn.cursor(snowflake.connector.cursor.DictCursor) as dcur:
|
1507
1522
|
# colunmn names with spaces
|
@@ -1611,6 +1626,26 @@ def test_write_pandas_dict_different_keys(conn: snowflake.connector.SnowflakeCon
|
|
1611
1626
|
assert indent(cur.fetchall()) == [('{\n "k": "v1"\n}',), ('{\n "k2": [\n "v\'2",\n "v\\"3"\n ]\n}',)]
|
1612
1627
|
|
1613
1628
|
|
1629
|
+
def test_write_pandas_db_schema(conn: snowflake.connector.SnowflakeConnection):
|
1630
|
+
with conn.cursor() as cur:
|
1631
|
+
cur.execute("create database db2")
|
1632
|
+
cur.execute("create schema db2.schema2")
|
1633
|
+
cur.execute("create or replace table db2.schema2.customers (ID int, FIRST_NAME varchar, LAST_NAME varchar)")
|
1634
|
+
|
1635
|
+
df = pd.DataFrame.from_records(
|
1636
|
+
[
|
1637
|
+
{"ID": 1, "FIRST_NAME": "Jenny"},
|
1638
|
+
{"ID": 2, "FIRST_NAME": "Jasper"},
|
1639
|
+
]
|
1640
|
+
)
|
1641
|
+
snowflake.connector.pandas_tools.write_pandas(conn, df, "CUSTOMERS", "DB2", "SCHEMA2")
|
1642
|
+
|
1643
|
+
cur.execute("select id, first_name, last_name from db2.schema2.customers")
|
1644
|
+
|
1645
|
+
# columns not in dataframe will receive their default value
|
1646
|
+
assert cur.fetchall() == [(1, "Jenny", None), (2, "Jasper", None)]
|
1647
|
+
|
1648
|
+
|
1614
1649
|
def indent(rows: Sequence[tuple] | Sequence[dict]) -> list[tuple]:
|
1615
1650
|
# indent duckdb json strings tuple values to match snowflake json strings
|
1616
1651
|
assert isinstance(rows[0], tuple)
|
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
|