fakesnow 0.9.39__py3-none-any.whl → 0.9.41__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.
- fakesnow/copy_into.py +2 -31
- fakesnow/cursor.py +19 -2
- fakesnow/macros.py +11 -0
- fakesnow/server.py +14 -0
- fakesnow/transforms/__init__.py +5 -1
- fakesnow/transforms/merge.py +3 -1
- fakesnow/transforms/stage.py +97 -2
- fakesnow/transforms/transforms.py +18 -0
- {fakesnow-0.9.39.dist-info → fakesnow-0.9.41.dist-info}/METADATA +2 -2
- {fakesnow-0.9.39.dist-info → fakesnow-0.9.41.dist-info}/RECORD +14 -14
- {fakesnow-0.9.39.dist-info → fakesnow-0.9.41.dist-info}/WHEEL +0 -0
- {fakesnow-0.9.39.dist-info → fakesnow-0.9.41.dist-info}/entry_points.txt +0 -0
- {fakesnow-0.9.39.dist-info → fakesnow-0.9.41.dist-info}/licenses/LICENSE +0 -0
- {fakesnow-0.9.39.dist-info → fakesnow-0.9.41.dist-info}/top_level.txt +0 -0
fakesnow/copy_into.py
CHANGED
@@ -11,6 +11,7 @@ import snowflake.connector.errors
|
|
11
11
|
from duckdb import DuckDBPyConnection
|
12
12
|
from sqlglot import exp
|
13
13
|
|
14
|
+
import fakesnow.transforms.stage as stage
|
14
15
|
from fakesnow import logger
|
15
16
|
|
16
17
|
|
@@ -196,40 +197,10 @@ def _from_source(expr: exp.Copy) -> str:
|
|
196
197
|
return from_.name
|
197
198
|
|
198
199
|
|
199
|
-
def normalise_ident(name: str) -> str:
|
200
|
-
"""
|
201
|
-
Strip double quotes if present else return uppercased.
|
202
|
-
Snowflake treats quoted identifiers as case-sensitive and un-quoted identifiers as case-insensitive
|
203
|
-
"""
|
204
|
-
if name.startswith('"') and name.endswith('"'):
|
205
|
-
return name[1:-1] # Strip quotes
|
206
|
-
|
207
|
-
return name.upper()
|
208
|
-
|
209
|
-
|
210
200
|
def stage_url_from_var(
|
211
201
|
from_source: str, duck_conn: DuckDBPyConnection, current_database: str | None, current_schema: str | None
|
212
202
|
) -> str:
|
213
|
-
|
214
|
-
if len(parts) == 3:
|
215
|
-
# Fully qualified name
|
216
|
-
database_name, schema_name, name = parts
|
217
|
-
elif len(parts) == 2:
|
218
|
-
# Schema + stage name
|
219
|
-
assert current_database, "Current database must be set when stage name is not fully qualified"
|
220
|
-
database_name, schema_name, name = current_database, parts[0], parts[1]
|
221
|
-
elif len(parts) == 1:
|
222
|
-
# Stage name only
|
223
|
-
assert current_database, "Current database must be set when stage name is not fully qualified"
|
224
|
-
assert current_schema, "Current schema must be set when stage name is not fully qualified"
|
225
|
-
database_name, schema_name, name = current_database, current_schema, parts[0]
|
226
|
-
else:
|
227
|
-
raise ValueError(f"Invalid stage name: {from_source}")
|
228
|
-
|
229
|
-
# Normalize names to uppercase if not wrapped in double quotes
|
230
|
-
database_name = normalise_ident(database_name)
|
231
|
-
schema_name = normalise_ident(schema_name)
|
232
|
-
name = normalise_ident(name)
|
203
|
+
database_name, schema_name, name = stage.parts_from_var(from_source, current_database, current_schema)
|
233
204
|
|
234
205
|
# Look up the stage URL
|
235
206
|
duck_conn.execute(
|
fakesnow/cursor.py
CHANGED
@@ -76,6 +76,7 @@ class FakeSnowflakeCursor:
|
|
76
76
|
self._use_dict_result = use_dict_result
|
77
77
|
self._last_sql = None
|
78
78
|
self._last_params = None
|
79
|
+
self._last_transformed = None
|
79
80
|
self._sqlstate = None
|
80
81
|
self._arraysize = 1
|
81
82
|
self._arrow_table = None
|
@@ -106,6 +107,7 @@ class FakeSnowflakeCursor:
|
|
106
107
|
def close(self) -> bool:
|
107
108
|
self._last_sql = None
|
108
109
|
self._last_params = None
|
110
|
+
self._last_transformed = None
|
109
111
|
return True
|
110
112
|
|
111
113
|
def describe(self, command: str, *args: Any, **kwargs: Any) -> list[ResultMetadata]:
|
@@ -239,6 +241,7 @@ class FakeSnowflakeCursor:
|
|
239
241
|
.transform(transforms.identifier)
|
240
242
|
.transform(transforms.array_agg_within_group)
|
241
243
|
.transform(transforms.array_agg)
|
244
|
+
.transform(transforms.array_construct_etc)
|
242
245
|
.transform(transforms.dateadd_date_cast)
|
243
246
|
.transform(transforms.dateadd_string_literal_timestamp_cast)
|
244
247
|
.transform(transforms.datediff_string_literal_timestamp_cast)
|
@@ -261,6 +264,7 @@ class FakeSnowflakeCursor:
|
|
261
264
|
.transform(transforms.alias_in_join)
|
262
265
|
.transform(transforms.alter_table_strip_cluster_by)
|
263
266
|
.transform(lambda e: transforms.create_stage(e, self._conn.database, self._conn.schema))
|
267
|
+
.transform(lambda e: transforms.put_stage(e, self._conn.database, self._conn.schema))
|
264
268
|
)
|
265
269
|
|
266
270
|
def _transform_explode(self, expression: exp.Expression) -> list[exp.Expression]:
|
@@ -330,10 +334,10 @@ class FakeSnowflakeCursor:
|
|
330
334
|
self._duck_conn.execute(info_schema.per_db_creation_sql(create_db_name))
|
331
335
|
result_sql = SQL_CREATED_DATABASE.substitute(name=create_db_name)
|
332
336
|
|
333
|
-
elif stage_name := transformed.args.get("
|
337
|
+
elif stage_name := transformed.args.get("create_stage_name"):
|
334
338
|
if stage_name == "?":
|
335
339
|
assert isinstance(params, (tuple, list)) and len(params) == 1, (
|
336
|
-
"Expected single parameter for
|
340
|
+
"Expected single parameter for create_stage_name"
|
337
341
|
)
|
338
342
|
result_sql = SQL_CREATED_STAGE.substitute(name=params[0].upper())
|
339
343
|
else:
|
@@ -411,8 +415,21 @@ class FakeSnowflakeCursor:
|
|
411
415
|
self._rowcount = affected_count or self._arrow_table.num_rows
|
412
416
|
self._sfqid = str(uuid.uuid4())
|
413
417
|
|
418
|
+
if stage_name := transformed.args.get("put_stage_name"):
|
419
|
+
if stage_name == "?":
|
420
|
+
assert isinstance(params, (tuple, list)) and len(params) == 1, (
|
421
|
+
"Expected single parameter for put_stage_name"
|
422
|
+
)
|
423
|
+
if self._arrow_table.num_rows != 1:
|
424
|
+
raise snowflake.connector.errors.ProgrammingError(
|
425
|
+
msg=f"SQL compilation error:\nStage '{stage_name}' does not exist or not authorized.",
|
426
|
+
errno=2003,
|
427
|
+
sqlstate="02000",
|
428
|
+
)
|
429
|
+
|
414
430
|
self._last_sql = result_sql or sql
|
415
431
|
self._last_params = None if result_sql else params
|
432
|
+
self._last_transformed = transformed
|
416
433
|
|
417
434
|
def executemany(
|
418
435
|
self,
|
fakesnow/macros.py
CHANGED
@@ -29,9 +29,20 @@ CREATE OR REPLACE MACRO ${catalog}._fs_flatten(input) AS TABLE
|
|
29
29
|
"""
|
30
30
|
)
|
31
31
|
|
32
|
+
# emulates https://docs.snowflake.com/en/sql-reference/functions/array_construct_compact
|
33
|
+
# requires transforms.array_construct_compact
|
34
|
+
ARRAY_CONSTRUCT_COMPACT = Template(
|
35
|
+
"""
|
36
|
+
CREATE OR REPLACE MACRO ${catalog}.array_construct_compact(list) AS (
|
37
|
+
SELECT ARRAY_AGG(x)::JSON FROM UNNEST(list) AS t(x) WHERE x IS NOT NULL
|
38
|
+
);
|
39
|
+
"""
|
40
|
+
)
|
41
|
+
|
32
42
|
|
33
43
|
def creation_sql(catalog: str) -> str:
|
34
44
|
return f"""
|
35
45
|
{EQUAL_NULL.substitute(catalog=catalog)};
|
36
46
|
{FS_FLATTEN.substitute(catalog=catalog)};
|
47
|
+
{ARRAY_CONSTRUCT_COMPACT.substitute(catalog=catalog)};
|
37
48
|
"""
|
fakesnow/server.py
CHANGED
@@ -9,6 +9,7 @@ from dataclasses import dataclass
|
|
9
9
|
from typing import Any
|
10
10
|
|
11
11
|
import snowflake.connector.errors
|
12
|
+
from sqlglot import parse_one
|
12
13
|
from starlette.applications import Starlette
|
13
14
|
from starlette.concurrency import run_in_threadpool
|
14
15
|
from starlette.requests import Request
|
@@ -89,11 +90,24 @@ async def query_request(request: Request) -> JSONResponse:
|
|
89
90
|
else:
|
90
91
|
params = None
|
91
92
|
|
93
|
+
expr = parse_one(sql_text, read="snowflake")
|
94
|
+
|
92
95
|
try:
|
93
96
|
# only a single sql statement is sent at a time by the python snowflake connector
|
94
97
|
cur = await run_in_threadpool(conn.cursor().execute, sql_text, binding_params=params)
|
95
98
|
rowtype = describe_as_rowtype(cur._describe_last_sql()) # noqa: SLF001
|
96
99
|
|
100
|
+
expr = cur._last_transformed # noqa: SLF001
|
101
|
+
assert expr
|
102
|
+
if put_stage_data := expr.args.get("put_stage_data"):
|
103
|
+
# this is a PUT command, so return the stage data
|
104
|
+
return JSONResponse(
|
105
|
+
{
|
106
|
+
"data": put_stage_data,
|
107
|
+
"success": True,
|
108
|
+
}
|
109
|
+
)
|
110
|
+
|
97
111
|
except snowflake.connector.errors.ProgrammingError as e:
|
98
112
|
logger.info(f"{sql_text=} ProgrammingError {e}")
|
99
113
|
code = f"{e.errno:06d}"
|
fakesnow/transforms/__init__.py
CHANGED
@@ -13,13 +13,17 @@ from fakesnow.transforms.show import (
|
|
13
13
|
show_users as show_users,
|
14
14
|
show_warehouses as show_warehouses,
|
15
15
|
)
|
16
|
-
from fakesnow.transforms.stage import
|
16
|
+
from fakesnow.transforms.stage import (
|
17
|
+
create_stage as create_stage,
|
18
|
+
put_stage as put_stage,
|
19
|
+
)
|
17
20
|
from fakesnow.transforms.transforms import (
|
18
21
|
SUCCESS_NOP as SUCCESS_NOP,
|
19
22
|
alias_in_join as alias_in_join,
|
20
23
|
alter_table_strip_cluster_by as alter_table_strip_cluster_by,
|
21
24
|
array_agg as array_agg,
|
22
25
|
array_agg_within_group as array_agg_within_group,
|
26
|
+
array_construct_etc as array_construct_etc,
|
23
27
|
array_size as array_size,
|
24
28
|
create_clone as create_clone,
|
25
29
|
create_database as create_database,
|
fakesnow/transforms/merge.py
CHANGED
@@ -78,7 +78,9 @@ def _create_merge_candidates(merge_expr: exp.Merge) -> exp.Expression:
|
|
78
78
|
insert_values = then.expression.expressions
|
79
79
|
values.update([str(c) for c in insert_values if isinstance(c, exp.Column)])
|
80
80
|
predicate = f"AND {condition}" if condition else ""
|
81
|
-
case_when_clauses.append(
|
81
|
+
case_when_clauses.append(
|
82
|
+
f"WHEN {target_tbl.alias or target_tbl.name}.rowid is NULL {predicate} THEN {w_idx}"
|
83
|
+
)
|
82
84
|
|
83
85
|
sql = f"""
|
84
86
|
CREATE OR REPLACE TEMPORARY TABLE merge_candidates AS
|
fakesnow/transforms/stage.py
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import datetime
|
4
|
+
from urllib.parse import urlparse
|
5
|
+
from urllib.request import url2pathname
|
4
6
|
|
7
|
+
import snowflake.connector.errors
|
5
8
|
import sqlglot
|
6
9
|
from sqlglot import exp
|
7
10
|
|
11
|
+
LOCAL_BUCKET_PATH = "/tmp/fakesnow_bucket"
|
12
|
+
|
8
13
|
|
9
14
|
def create_stage(
|
10
|
-
expression: exp.Expression, current_database: str | None
|
15
|
+
expression: exp.Expression, current_database: str | None, current_schema: str | None
|
11
16
|
) -> exp.Expression:
|
12
17
|
"""Transform CREATE STAGE to an INSERT statement for the fake stages table."""
|
13
18
|
if not (
|
@@ -64,5 +69,95 @@ def create_stage(
|
|
64
69
|
)
|
65
70
|
"""
|
66
71
|
transformed = sqlglot.parse_one(insert_sql, read="duckdb")
|
67
|
-
transformed.args["
|
72
|
+
transformed.args["create_stage_name"] = stage_name
|
73
|
+
return transformed
|
74
|
+
|
75
|
+
|
76
|
+
# TODO: handle ?
|
77
|
+
|
78
|
+
|
79
|
+
def put_stage(expression: exp.Expression, current_database: str | None, current_schema: str | None) -> exp.Expression:
|
80
|
+
"""Transform PUT to a SELECT statement to locate the stage.
|
81
|
+
|
82
|
+
See https://docs.snowflake.com/en/sql-reference/sql/put
|
83
|
+
"""
|
84
|
+
if not isinstance(expression, exp.Put):
|
85
|
+
return expression
|
86
|
+
|
87
|
+
assert isinstance(expression.this, exp.Literal), "PUT command requires a file path as a literal"
|
88
|
+
src_url = urlparse(expression.this.this)
|
89
|
+
src_path = url2pathname(src_url.path)
|
90
|
+
target = expression.args["target"]
|
91
|
+
|
92
|
+
assert isinstance(target, exp.Var), f"{target} is not a exp.Var"
|
93
|
+
var = target.text("this")
|
94
|
+
if not var.startswith("@"):
|
95
|
+
msg = f"SQL compilation error:\n{var} does not start with @"
|
96
|
+
raise snowflake.connector.errors.ProgrammingError(
|
97
|
+
msg=msg,
|
98
|
+
errno=1003,
|
99
|
+
sqlstate="42000",
|
100
|
+
)
|
101
|
+
catalog, schema, stage_name = parts_from_var(var, current_database=current_database, current_schema=current_schema)
|
102
|
+
|
103
|
+
query = f"""
|
104
|
+
SELECT *
|
105
|
+
from _fs_global._fs_information_schema._fs_stages
|
106
|
+
where database_name = '{catalog}' and schema_name = '{schema}' and name = '{stage_name}'
|
107
|
+
"""
|
108
|
+
|
109
|
+
transformed = sqlglot.parse_one(query, read="duckdb")
|
110
|
+
transformed.args["put_stage_name"] = f"{catalog}.{schema}.{stage_name}"
|
111
|
+
transformed.args["put_stage_data"] = {
|
112
|
+
"stageInfo": {
|
113
|
+
# use LOCAL_FS otherwise we need to mock S3 with HTTPS which requires a certificate
|
114
|
+
"locationType": "LOCAL_FS",
|
115
|
+
"location": f"{LOCAL_BUCKET_PATH}/{stage_name}/",
|
116
|
+
"creds": {},
|
117
|
+
},
|
118
|
+
"src_locations": [src_path],
|
119
|
+
# defaults as per https://docs.snowflake.com/en/sql-reference/sql/put TODO: support other values
|
120
|
+
"parallel": 4,
|
121
|
+
"autoCompress": True,
|
122
|
+
"sourceCompression": "auto_detect",
|
123
|
+
"overwrite": False,
|
124
|
+
"command": "UPLOAD",
|
125
|
+
}
|
126
|
+
|
68
127
|
return transformed
|
128
|
+
|
129
|
+
|
130
|
+
def normalise_ident(name: str) -> str:
|
131
|
+
"""
|
132
|
+
Strip double quotes if present else return uppercased.
|
133
|
+
Snowflake treats quoted identifiers as case-sensitive and un-quoted identifiers as case-insensitive
|
134
|
+
"""
|
135
|
+
if name.startswith('"') and name.endswith('"'):
|
136
|
+
return name[1:-1] # Strip quotes
|
137
|
+
|
138
|
+
return name.upper()
|
139
|
+
|
140
|
+
|
141
|
+
def parts_from_var(var: str, current_database: str | None, current_schema: str | None) -> tuple[str, str, str]:
|
142
|
+
parts = var[1:].split(".")
|
143
|
+
if len(parts) == 3:
|
144
|
+
# Fully qualified name
|
145
|
+
database_name, schema_name, name = parts
|
146
|
+
elif len(parts) == 2:
|
147
|
+
# Schema + stage name
|
148
|
+
assert current_database, "Current database must be set when stage name is not fully qualified"
|
149
|
+
database_name, schema_name, name = current_database, parts[0], parts[1]
|
150
|
+
elif len(parts) == 1:
|
151
|
+
# Stage name only
|
152
|
+
assert current_database, "Current database must be set when stage name is not fully qualified"
|
153
|
+
assert current_schema, "Current schema must be set when stage name is not fully qualified"
|
154
|
+
database_name, schema_name, name = current_database, current_schema, parts[0]
|
155
|
+
else:
|
156
|
+
raise ValueError(f"Invalid stage name: {var}")
|
157
|
+
|
158
|
+
# Normalize names to uppercase if not wrapped in double quotes
|
159
|
+
database_name = normalise_ident(database_name)
|
160
|
+
schema_name = normalise_ident(schema_name)
|
161
|
+
name = normalise_ident(name)
|
162
|
+
|
163
|
+
return database_name, schema_name, name
|
@@ -45,6 +45,24 @@ def alter_table_strip_cluster_by(expression: exp.Expression) -> exp.Expression:
|
|
45
45
|
return expression
|
46
46
|
|
47
47
|
|
48
|
+
def array_construct_etc(expression: exp.Expression) -> exp.Expression:
|
49
|
+
"""Handle ARRAY_CONSTRUCT_* and ARRAY_CAT
|
50
|
+
|
51
|
+
Convert ARRAY_CONSTRUCT args to json_array.
|
52
|
+
Convert ARRAY_CONSTRUCT_COMPACT args to a list.
|
53
|
+
Because the macro expects a single argument to use with UNNEST.
|
54
|
+
|
55
|
+
TODO: fix ARRAY_CONSTRUCT_COMPACT to handle args of differing types.
|
56
|
+
"""
|
57
|
+
if isinstance(expression, exp.ArrayConstructCompact):
|
58
|
+
return exp.ArrayConstructCompact(expressions=[exp.Array(expressions=expression.expressions)])
|
59
|
+
elif isinstance(expression, exp.Array) and isinstance(expression.parent, exp.Select):
|
60
|
+
return exp.Anonymous(this="json_array", expressions=expression.expressions)
|
61
|
+
elif isinstance(expression, exp.ArrayConcat) and isinstance(expression.parent, exp.Select):
|
62
|
+
return exp.Cast(this=expression, to=exp.DataType(this=exp.DataType.Type.JSON, nested=False))
|
63
|
+
return expression
|
64
|
+
|
65
|
+
|
48
66
|
def array_size(expression: exp.Expression) -> exp.Expression:
|
49
67
|
if isinstance(expression, exp.ArraySize):
|
50
68
|
# return null if not json array
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fakesnow
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.41
|
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
|
@@ -214,7 +214,7 @@ License-File: LICENSE
|
|
214
214
|
Requires-Dist: duckdb~=1.2.0
|
215
215
|
Requires-Dist: pyarrow
|
216
216
|
Requires-Dist: snowflake-connector-python
|
217
|
-
Requires-Dist: sqlglot~=26.
|
217
|
+
Requires-Dist: sqlglot~=26.24.0
|
218
218
|
Provides-Extra: server
|
219
219
|
Requires-Dist: starlette; extra == "server"
|
220
220
|
Requires-Dist: uvicorn; extra == "server"
|
@@ -5,29 +5,29 @@ fakesnow/checks.py,sha256=bOJPMp46AvjJV_bXXjx2njO2dXNjffLrznwRuKyYZ4g,2889
|
|
5
5
|
fakesnow/cli.py,sha256=9qfI-Ssr6mo8UmIlXkUAOz2z2YPBgDsrEVaZv9FjGFs,2201
|
6
6
|
fakesnow/conn.py,sha256=diCwcjaCBrlCn9PyjbScfIQTNQjqiPTkQanUTqcvblE,6009
|
7
7
|
fakesnow/converter.py,sha256=wPOfsFXIUJNJSx5oFNAxh13udxmAVIIHsLK8BiGkXGA,1635
|
8
|
-
fakesnow/copy_into.py,sha256=
|
9
|
-
fakesnow/cursor.py,sha256=
|
8
|
+
fakesnow/copy_into.py,sha256=YIr5Bq3JwKOPYWm5t2QXUuFGxLL-1ioEXEumNjGbBvM,13648
|
9
|
+
fakesnow/cursor.py,sha256=mOwyXnBFXp79nDh0vtbmxS_hmFNy4j7hPlskENWeIrI,23818
|
10
10
|
fakesnow/expr.py,sha256=CAxuYIUkwI339DQIBzvFF0F-m1tcVGKEPA5rDTzmH9A,892
|
11
11
|
fakesnow/fakes.py,sha256=JQTiUkkwPeQrJ8FDWhPFPK6pGwd_aR2oiOrNzCWznlM,187
|
12
12
|
fakesnow/fixtures.py,sha256=2rj0MTZlaZc4PNWhaqC5IiiLa7E9G0QZT3g45YawsL0,633
|
13
13
|
fakesnow/info_schema.py,sha256=lqEYD5aWK2MamjALbj6ct7pz_1yyAq3tAk51kLa8NKk,9872
|
14
14
|
fakesnow/instance.py,sha256=OKoYXwaI6kL9HQpnHx44yzpON_xNfuIT_F4oJNF_XXQ,2114
|
15
15
|
fakesnow/logger.py,sha256=U6EjUENQuTrDeNYqER2hxazoySmXzLmZJ-t-SDZgjkg,363
|
16
|
-
fakesnow/macros.py,sha256=
|
16
|
+
fakesnow/macros.py,sha256=lxtznTCYryjecFkwswbqWMzCVamDLWyQZRKWtkWCWEk,1397
|
17
17
|
fakesnow/pandas_tools.py,sha256=wI203UQHC8JvDzxE_VjE1NeV4rThek2P-u52oTg2foo,3481
|
18
18
|
fakesnow/py.typed,sha256=B-DLSjYBi7pkKjwxCSdpVj2J02wgfJr-E7B1wOUyxYU,80
|
19
19
|
fakesnow/rowtype.py,sha256=QUp8EaXD5LT0Xv8BXk5ze4WseEn52xoJ6R05pJjs5mM,2729
|
20
|
-
fakesnow/server.py,sha256=
|
20
|
+
fakesnow/server.py,sha256=PGNuYEpmI0L0ZrwBCP1pDTc_lnFrtfSnlZ6zyVuCTqk,7173
|
21
21
|
fakesnow/variables.py,sha256=BGnD4LAdVByfJ2GXL6qpGBaTF8ZJRjt3pdJsd9sIAcw,3134
|
22
|
-
fakesnow/transforms/__init__.py,sha256=
|
23
|
-
fakesnow/transforms/merge.py,sha256=
|
22
|
+
fakesnow/transforms/__init__.py,sha256=OE-dunCuum8lv832s2cjEzThgBnpKELo5aaTXD_bMNg,2807
|
23
|
+
fakesnow/transforms/merge.py,sha256=H2yYyzGsxjpggS6PY91rAUtYCFuOkBm8uGi0EO0r6T0,8375
|
24
24
|
fakesnow/transforms/show.py,sha256=ejvs9S2l2Wcal4fhnNSVs3JkZwKsFxMEU35ufUV3-kg,20421
|
25
|
-
fakesnow/transforms/stage.py,sha256=
|
26
|
-
fakesnow/transforms/transforms.py,sha256=
|
27
|
-
fakesnow-0.9.
|
25
|
+
fakesnow/transforms/stage.py,sha256=FSIyI5kpthD_pdbVfzYCrby5HaikMZUpdRr6oBSrgk4,6176
|
26
|
+
fakesnow/transforms/transforms.py,sha256=t99pHmNm8aG89o738zVSXCG6a0dOXpnY1OqAz28EQHc,48072
|
27
|
+
fakesnow-0.9.41.dist-info/licenses/LICENSE,sha256=kW-7NWIyaRMQiDpryfSmF2DObDZHGR1cJZ39s6B1Svg,11344
|
28
28
|
tools/decode.py,sha256=kC5kUvLQxdCkMRsnH6BqCajlKxKeN77w6rwCKsY6gqU,1781
|
29
|
-
fakesnow-0.9.
|
30
|
-
fakesnow-0.9.
|
31
|
-
fakesnow-0.9.
|
32
|
-
fakesnow-0.9.
|
33
|
-
fakesnow-0.9.
|
29
|
+
fakesnow-0.9.41.dist-info/METADATA,sha256=cOvEP3Mo_H0yn37XeRPozuvwY6OS7tqDnleGP8Vgplg,20680
|
30
|
+
fakesnow-0.9.41.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
31
|
+
fakesnow-0.9.41.dist-info/entry_points.txt,sha256=2riAUgu928ZIHawtO8EsfrMEJhi-EH-z_Vq7Q44xKPM,47
|
32
|
+
fakesnow-0.9.41.dist-info/top_level.txt,sha256=Yos7YveA3f03xVYuURqnBsfMV2DePXfu_yGcsj3pPzI,30
|
33
|
+
fakesnow-0.9.41.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|