snowpark-connect 0.30.0__py3-none-any.whl → 0.30.1__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.
Potentially problematic release.
This version of snowpark-connect might be problematic. Click here for more details.
- snowflake/snowpark_connect/config.py +3 -0
- snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2_grpc.py +4 -0
- snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2_grpc.py +4 -0
- snowflake/snowpark_connect/relation/io_utils.py +61 -4
- snowflake/snowpark_connect/relation/map_row_ops.py +13 -2
- snowflake/snowpark_connect/relation/map_stats.py +20 -5
- snowflake/snowpark_connect/relation/read/map_read.py +9 -0
- snowflake/snowpark_connect/relation/utils.py +19 -2
- snowflake/snowpark_connect/relation/write/map_write.py +15 -29
- snowflake/snowpark_connect/utils/session.py +0 -1
- snowflake/snowpark_connect/utils/telemetry.py +75 -18
- snowflake/snowpark_connect/version.py +1 -1
- {snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/METADATA +1 -1
- {snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/RECORD +22 -20
- {snowpark_connect-0.30.0.data → snowpark_connect-0.30.1.data}/scripts/snowpark-connect +0 -0
- {snowpark_connect-0.30.0.data → snowpark_connect-0.30.1.data}/scripts/snowpark-session +0 -0
- {snowpark_connect-0.30.0.data → snowpark_connect-0.30.1.data}/scripts/snowpark-submit +0 -0
- {snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/WHEEL +0 -0
- {snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/licenses/LICENSE-binary +0 -0
- {snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/licenses/LICENSE.txt +0 -0
- {snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/licenses/NOTICE-binary +0 -0
- {snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/top_level.txt +0 -0
|
@@ -143,6 +143,9 @@ class GlobalConfig:
|
|
|
143
143
|
# Control whether repartition(n) on a DataFrame forces splitting into n files during writes
|
|
144
144
|
# This matches spark behavior more closely, but introduces overhead.
|
|
145
145
|
"snowflake.repartition.for.writes": "false",
|
|
146
|
+
"snowpark.connect.structured_types.fix": str(
|
|
147
|
+
snowpark.context._enable_fix_2360274
|
|
148
|
+
).lower(),
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
boolean_config_list = [
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
from urllib.parse import urlparse
|
|
6
6
|
|
|
7
|
+
from pyspark.errors.exceptions.base import AnalysisException
|
|
8
|
+
|
|
7
9
|
CLOUD_PREFIX_TO_CLOUD = {
|
|
8
10
|
"abfss": "azure",
|
|
9
11
|
"wasbs": "azure",
|
|
@@ -12,10 +14,28 @@ CLOUD_PREFIX_TO_CLOUD = {
|
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
SUPPORTED_COMPRESSION_PER_FORMAT = {
|
|
15
|
-
"csv": {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
"csv": {
|
|
18
|
+
"GZIP",
|
|
19
|
+
"BZ2",
|
|
20
|
+
"BROTLI",
|
|
21
|
+
"ZSTD",
|
|
22
|
+
"DEFLATE",
|
|
23
|
+
"RAW_DEFLATE",
|
|
24
|
+
"NONE",
|
|
25
|
+
"UNCOMPRESSED",
|
|
26
|
+
},
|
|
27
|
+
"json": {
|
|
28
|
+
"GZIP",
|
|
29
|
+
"BZ2",
|
|
30
|
+
"BROTLI",
|
|
31
|
+
"ZSTD",
|
|
32
|
+
"DEFLATE",
|
|
33
|
+
"RAW_DEFLATE",
|
|
34
|
+
"NONE",
|
|
35
|
+
"UNCOMPRESSED",
|
|
36
|
+
},
|
|
37
|
+
"parquet": {"LZO", "SNAPPY", "NONE", "UNCOMPRESSED"},
|
|
38
|
+
"text": {"NONE", "UNCOMPRESSED"},
|
|
19
39
|
}
|
|
20
40
|
|
|
21
41
|
|
|
@@ -29,6 +49,43 @@ def is_supported_compression(format: str, compression: str | None) -> bool:
|
|
|
29
49
|
return compression in supported_compressions_for_format(format)
|
|
30
50
|
|
|
31
51
|
|
|
52
|
+
def get_compression_for_source_and_options(
|
|
53
|
+
source: str, options: dict[str, str], from_read: bool = False
|
|
54
|
+
) -> str | None:
|
|
55
|
+
"""
|
|
56
|
+
Determines the compression type to use for a given data source and options.
|
|
57
|
+
Args:
|
|
58
|
+
source (str): The data source format (e.g., "csv", "json", "parquet", "text").
|
|
59
|
+
options (dict[str, str]): A dictionary of options that may include a "compression" key.
|
|
60
|
+
Returns:
|
|
61
|
+
str: The compression type to use (e.g., "GZIP", "SNAPPY", "NONE").
|
|
62
|
+
Raises:
|
|
63
|
+
AnalysisException: If the specified compression is not supported for the given source format.
|
|
64
|
+
"""
|
|
65
|
+
# From read, we don't have a default compression
|
|
66
|
+
if from_read and "compression" not in options:
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
# Get compression from options for proper filename generation
|
|
70
|
+
default_compression = "NONE" if source != "parquet" else "snappy"
|
|
71
|
+
compression = options.get("compression", default_compression).upper()
|
|
72
|
+
if compression == "UNCOMPRESSED":
|
|
73
|
+
compression = "NONE"
|
|
74
|
+
|
|
75
|
+
if not is_supported_compression(source, compression):
|
|
76
|
+
supported_compressions = supported_compressions_for_format(source)
|
|
77
|
+
raise AnalysisException(
|
|
78
|
+
f"Compression {compression} is not supported for {source} format. "
|
|
79
|
+
+ (
|
|
80
|
+
f"Supported compressions: {sorted(supported_compressions)}"
|
|
81
|
+
if supported_compressions
|
|
82
|
+
else "None compression supported for this format."
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return compression
|
|
87
|
+
|
|
88
|
+
|
|
32
89
|
def get_cloud_from_url(
|
|
33
90
|
url: str,
|
|
34
91
|
):
|
|
@@ -35,6 +35,9 @@ from snowflake.snowpark_connect.expression.map_expression import (
|
|
|
35
35
|
)
|
|
36
36
|
from snowflake.snowpark_connect.expression.typer import ExpressionTyper
|
|
37
37
|
from snowflake.snowpark_connect.relation.map_relation import map_relation
|
|
38
|
+
from snowflake.snowpark_connect.utils.identifiers import (
|
|
39
|
+
split_fully_qualified_spark_name,
|
|
40
|
+
)
|
|
38
41
|
from snowflake.snowpark_connect.utils.telemetry import (
|
|
39
42
|
SnowparkConnectNotImplementedError,
|
|
40
43
|
)
|
|
@@ -131,11 +134,19 @@ def map_fillna(
|
|
|
131
134
|
input_df = input_container.dataframe
|
|
132
135
|
|
|
133
136
|
if len(rel.fill_na.cols) > 0:
|
|
137
|
+
if rel.fill_na.cols == ["*"]:
|
|
138
|
+
# Expand "*" to all columns
|
|
139
|
+
spark_col_names = input_container.column_map.get_spark_columns()
|
|
140
|
+
else:
|
|
141
|
+
spark_col_names = list(rel.fill_na.cols)
|
|
142
|
+
|
|
143
|
+
# We don't validate the fully qualified spark name here as fillNa is no-op for structured type colums.
|
|
144
|
+
# It only works for scalar type columns like float, int, string or bool.
|
|
134
145
|
columns: list[str] = [
|
|
135
146
|
input_container.column_map.get_snowpark_column_name_from_spark_column_name(
|
|
136
|
-
c
|
|
147
|
+
split_fully_qualified_spark_name(c)[0]
|
|
137
148
|
)
|
|
138
|
-
for c in
|
|
149
|
+
for c in spark_col_names
|
|
139
150
|
]
|
|
140
151
|
values = [get_literal_field_and_name(v)[0] for v in rel.fill_na.values]
|
|
141
152
|
if len(values) == 1:
|
|
@@ -309,9 +309,28 @@ def map_freq_items(rel: relation_proto.Relation) -> DataFrameContainer:
|
|
|
309
309
|
cols = input_container.column_map.get_snowpark_column_names_from_spark_column_names(
|
|
310
310
|
list(rel.freq_items.cols)
|
|
311
311
|
)
|
|
312
|
+
|
|
313
|
+
# handle empty DataFrame case
|
|
314
|
+
row_count = input_df.count()
|
|
315
|
+
|
|
316
|
+
for sp_col_name in cols:
|
|
317
|
+
spark_col_names.append(
|
|
318
|
+
f"{input_container.column_map.get_spark_column_name_from_snowpark_column_name(sp_col_name)}_freqItems"
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
if row_count == 0:
|
|
322
|
+
# If DataFrame is empty, return empty arrays for each column
|
|
323
|
+
empty_values = [[] for _ in cols]
|
|
324
|
+
approx_top_k_df = session.createDataFrame([empty_values], spark_col_names)
|
|
325
|
+
return DataFrameContainer.create_with_column_mapping(
|
|
326
|
+
dataframe=approx_top_k_df,
|
|
327
|
+
spark_column_names=spark_col_names,
|
|
328
|
+
snowpark_column_names=spark_col_names,
|
|
329
|
+
)
|
|
330
|
+
|
|
312
331
|
approx_top_k_df = input_df.select(
|
|
313
332
|
*[
|
|
314
|
-
fn.function("approx_top_k")(fn.col(col), round(
|
|
333
|
+
fn.function("approx_top_k")(fn.col(col), round(row_count / support))
|
|
315
334
|
for col in cols
|
|
316
335
|
]
|
|
317
336
|
)
|
|
@@ -330,10 +349,6 @@ def map_freq_items(rel: relation_proto.Relation) -> DataFrameContainer:
|
|
|
330
349
|
for value in approx_top_k_values
|
|
331
350
|
]
|
|
332
351
|
|
|
333
|
-
for sp_col_name in cols:
|
|
334
|
-
spark_col_names.append(
|
|
335
|
-
f"{input_container.column_map.get_spark_column_name_from_snowpark_column_name(sp_col_name)}_freqItems"
|
|
336
|
-
)
|
|
337
352
|
approx_top_k_df = session.createDataFrame([filtered_values], spark_col_names)
|
|
338
353
|
|
|
339
354
|
return DataFrameContainer.create_with_column_mapping(
|
|
@@ -17,6 +17,7 @@ from snowflake.snowpark_connect.config import global_config
|
|
|
17
17
|
from snowflake.snowpark_connect.dataframe_container import DataFrameContainer
|
|
18
18
|
from snowflake.snowpark_connect.relation.io_utils import (
|
|
19
19
|
convert_file_prefix_path,
|
|
20
|
+
get_compression_for_source_and_options,
|
|
20
21
|
is_cloud_path,
|
|
21
22
|
)
|
|
22
23
|
from snowflake.snowpark_connect.relation.read.map_read_table import map_read_table
|
|
@@ -237,6 +238,14 @@ def _read_file(
|
|
|
237
238
|
)
|
|
238
239
|
upload_files_if_needed(paths, clean_source_paths, session, read_format)
|
|
239
240
|
paths = [_quote_stage_path(path) for path in paths]
|
|
241
|
+
|
|
242
|
+
if read_format in ("csv", "text", "json", "parquet"):
|
|
243
|
+
compression = get_compression_for_source_and_options(
|
|
244
|
+
read_format, options, from_read=True
|
|
245
|
+
)
|
|
246
|
+
if compression is not None:
|
|
247
|
+
options["compression"] = compression
|
|
248
|
+
|
|
240
249
|
match read_format:
|
|
241
250
|
case "csv":
|
|
242
251
|
from snowflake.snowpark_connect.relation.read.map_read_csv import (
|
|
@@ -92,6 +92,21 @@ TYPE_MAP_FOR_TO_SCHEMA = {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
# This mapping is used to map the compression type to the extension of the file.
|
|
96
|
+
FILE_COMPRESSION_TO_EXTENSION = {
|
|
97
|
+
"GZIP": "gz",
|
|
98
|
+
"BZ2": "bz2",
|
|
99
|
+
"BROTLI": "br",
|
|
100
|
+
"ZSTD": "zst",
|
|
101
|
+
"DEFLATE": "deflate",
|
|
102
|
+
"RAW_DEFLATE": "raw_deflate",
|
|
103
|
+
"SNAPPY": "snappy",
|
|
104
|
+
"LZO": "lzo",
|
|
105
|
+
"LZ4": "lz4",
|
|
106
|
+
"BZIP2": "bz2",
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
95
110
|
def get_df_with_partition_row_number(
|
|
96
111
|
container: DataFrameContainer,
|
|
97
112
|
plan_id: int | None,
|
|
@@ -186,13 +201,15 @@ def generate_spark_compatible_filename(
|
|
|
186
201
|
|
|
187
202
|
# Add compression if specified and not 'none'
|
|
188
203
|
if compression and compression.lower() not in ("none", "uncompressed"):
|
|
189
|
-
compression_part = f".{compression.lower()}"
|
|
204
|
+
compression_part = f".{FILE_COMPRESSION_TO_EXTENSION.get(compression.upper(), compression.lower())}"
|
|
190
205
|
else:
|
|
191
206
|
compression_part = ""
|
|
192
207
|
|
|
193
208
|
# Add format extension if specified
|
|
194
|
-
if format_ext:
|
|
209
|
+
if format_ext == "parquet":
|
|
195
210
|
return f"{base_name}{compression_part}.{format_ext}"
|
|
211
|
+
elif format_ext is not None and format_ext != "":
|
|
212
|
+
return f"{base_name}.{format_ext}{compression_part}"
|
|
196
213
|
else:
|
|
197
214
|
return f"{base_name}{compression_part}"
|
|
198
215
|
|
|
@@ -35,9 +35,8 @@ from snowflake.snowpark_connect.config import (
|
|
|
35
35
|
from snowflake.snowpark_connect.dataframe_container import DataFrameContainer
|
|
36
36
|
from snowflake.snowpark_connect.relation.io_utils import (
|
|
37
37
|
convert_file_prefix_path,
|
|
38
|
+
get_compression_for_source_and_options,
|
|
38
39
|
is_cloud_path,
|
|
39
|
-
is_supported_compression,
|
|
40
|
-
supported_compressions_for_format,
|
|
41
40
|
)
|
|
42
41
|
from snowflake.snowpark_connect.relation.map_relation import map_relation
|
|
43
42
|
from snowflake.snowpark_connect.relation.read.metadata_utils import (
|
|
@@ -147,6 +146,11 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
147
146
|
|
|
148
147
|
session: snowpark.Session = get_or_create_snowpark_session()
|
|
149
148
|
|
|
149
|
+
# Check for partition hint early to determine precedence over single option
|
|
150
|
+
partition_hint = (
|
|
151
|
+
result.partition_hint if hasattr(result, "partition_hint") else None
|
|
152
|
+
)
|
|
153
|
+
|
|
150
154
|
# Snowflake saveAsTable doesn't support format
|
|
151
155
|
if (
|
|
152
156
|
write_op.HasField("table")
|
|
@@ -176,8 +180,11 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
176
180
|
# Generate Spark-compatible filename with proper extension
|
|
177
181
|
extension = write_op.source if write_op.source != "text" else "txt"
|
|
178
182
|
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
compression = get_compression_for_source_and_options(
|
|
184
|
+
write_op.source, write_op.options, from_read=False
|
|
185
|
+
)
|
|
186
|
+
if compression is not None:
|
|
187
|
+
write_op.options["compression"] = compression
|
|
181
188
|
|
|
182
189
|
# Generate Spark-compatible filename or prefix
|
|
183
190
|
# we need a random prefix to support "append" mode
|
|
@@ -203,12 +210,12 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
203
210
|
except Exception as e:
|
|
204
211
|
logger.warning(f"Could not clear directory {write_path}: {e}")
|
|
205
212
|
|
|
206
|
-
if should_write_to_single_file:
|
|
213
|
+
if should_write_to_single_file and partition_hint is None:
|
|
207
214
|
# Single file: generate complete filename with extension
|
|
208
215
|
spark_filename = generate_spark_compatible_filename(
|
|
209
216
|
task_id=0,
|
|
210
217
|
attempt_number=0,
|
|
211
|
-
compression=
|
|
218
|
+
compression=compression,
|
|
212
219
|
format_ext=extension,
|
|
213
220
|
)
|
|
214
221
|
temp_file_prefix_on_stage = f"{write_path}/{spark_filename}"
|
|
@@ -217,29 +224,11 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
217
224
|
spark_filename_prefix = generate_spark_compatible_filename(
|
|
218
225
|
task_id=0,
|
|
219
226
|
attempt_number=0,
|
|
220
|
-
compression=
|
|
227
|
+
compression=None,
|
|
221
228
|
format_ext="", # No extension for prefix
|
|
222
229
|
)
|
|
223
230
|
temp_file_prefix_on_stage = f"{write_path}/{spark_filename_prefix}"
|
|
224
231
|
|
|
225
|
-
default_compression = "NONE" if write_op.source != "parquet" else "snappy"
|
|
226
|
-
compression = write_op.options.get(
|
|
227
|
-
"compression", default_compression
|
|
228
|
-
).upper()
|
|
229
|
-
|
|
230
|
-
if not is_supported_compression(write_op.source, compression):
|
|
231
|
-
supported_compressions = supported_compressions_for_format(
|
|
232
|
-
write_op.source
|
|
233
|
-
)
|
|
234
|
-
raise AnalysisException(
|
|
235
|
-
f"Compression {compression} is not supported for {write_op.source} format. "
|
|
236
|
-
+ (
|
|
237
|
-
f"Supported compressions: {sorted(supported_compressions)}"
|
|
238
|
-
if supported_compressions
|
|
239
|
-
else "No compression supported for this format."
|
|
240
|
-
)
|
|
241
|
-
)
|
|
242
|
-
|
|
243
232
|
parameters = {
|
|
244
233
|
"location": temp_file_prefix_on_stage,
|
|
245
234
|
"file_format_type": write_op.source
|
|
@@ -254,9 +243,6 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
254
243
|
# Using the base avoids coupling to exact filenames/prefixes.
|
|
255
244
|
download_stage_path = write_path
|
|
256
245
|
|
|
257
|
-
# Check for partition hint early to determine precedence over single option
|
|
258
|
-
partition_hint = result.partition_hint
|
|
259
|
-
|
|
260
246
|
# Apply max_file_size for both single and multi-file scenarios
|
|
261
247
|
# This helps control when Snowflake splits files into multiple parts
|
|
262
248
|
if max_file_size:
|
|
@@ -314,7 +300,7 @@ def map_write(request: proto_base.ExecutePlanRequest):
|
|
|
314
300
|
per_part_prefix = generate_spark_compatible_filename(
|
|
315
301
|
task_id=part_idx,
|
|
316
302
|
attempt_number=0,
|
|
317
|
-
compression=
|
|
303
|
+
compression=None,
|
|
318
304
|
format_ext="", # prefix only; Snowflake appends extension/counters
|
|
319
305
|
)
|
|
320
306
|
part_params["location"] = f"{write_path}/{per_part_prefix}"
|
|
@@ -120,7 +120,6 @@ def configure_snowpark_session(session: snowpark.Session):
|
|
|
120
120
|
"PYTHON_SNOWPARK_USE_SCOPED_TEMP_OBJECTS": "false", # this is required for creating udfs from sproc
|
|
121
121
|
"ENABLE_STRUCTURED_TYPES_IN_SNOWPARK_CONNECT_RESPONSE": "true",
|
|
122
122
|
"QUERY_TAG": f"'{query_tag}'",
|
|
123
|
-
"FEATURE_INTERVAL_TYPES": "enabled",
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
session.sql(
|
|
@@ -11,8 +11,8 @@ from abc import ABC, abstractmethod
|
|
|
11
11
|
from collections import defaultdict
|
|
12
12
|
from collections.abc import Iterable
|
|
13
13
|
from contextvars import ContextVar
|
|
14
|
+
from dataclasses import dataclass
|
|
14
15
|
from enum import Enum, unique
|
|
15
|
-
from typing import Dict
|
|
16
16
|
|
|
17
17
|
import google.protobuf.message
|
|
18
18
|
import pyspark.sql.connect.proto.base_pb2 as proto_base
|
|
@@ -57,6 +57,7 @@ class TelemetryType(Enum):
|
|
|
57
57
|
|
|
58
58
|
class EventType(Enum):
|
|
59
59
|
SERVER_STARTED = "scos_server_started"
|
|
60
|
+
WARNING = "scos_warning"
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
# global labels
|
|
@@ -106,7 +107,16 @@ REDACTED_PLAN_SUFFIXES = [
|
|
|
106
107
|
]
|
|
107
108
|
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
@dataclass
|
|
111
|
+
class TelemetryMessage:
|
|
112
|
+
"""Container for telemetry messages in the processing queue."""
|
|
113
|
+
|
|
114
|
+
message: dict
|
|
115
|
+
timestamp: int
|
|
116
|
+
is_warning: bool
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _basic_telemetry_data() -> dict:
|
|
110
120
|
return {
|
|
111
121
|
**STATIC_TELEMETRY_DATA,
|
|
112
122
|
TelemetryField.KEY_EVENT_ID.value: str(uuid.uuid4()),
|
|
@@ -123,9 +133,11 @@ def safe(func):
|
|
|
123
133
|
def wrap(*args, **kwargs):
|
|
124
134
|
try:
|
|
125
135
|
func(*args, **kwargs)
|
|
126
|
-
except Exception:
|
|
127
|
-
#
|
|
128
|
-
|
|
136
|
+
except Exception as e:
|
|
137
|
+
# report failed operation to telemetry
|
|
138
|
+
telemetry.send_warning_msg(
|
|
139
|
+
f"Telemetry operation {func} failed due to exception", e
|
|
140
|
+
)
|
|
129
141
|
|
|
130
142
|
return wrap
|
|
131
143
|
|
|
@@ -528,8 +540,8 @@ class Telemetry:
|
|
|
528
540
|
@safe
|
|
529
541
|
def send_request_summary_telemetry(self):
|
|
530
542
|
if self._not_in_request():
|
|
531
|
-
|
|
532
|
-
"
|
|
543
|
+
self.send_warning_msg(
|
|
544
|
+
"Trying to send request summary telemetry without initializing it"
|
|
533
545
|
)
|
|
534
546
|
return
|
|
535
547
|
|
|
@@ -541,14 +553,56 @@ class Telemetry:
|
|
|
541
553
|
}
|
|
542
554
|
self._send(message)
|
|
543
555
|
|
|
544
|
-
def
|
|
556
|
+
def send_warning_msg(self, msg: str, e: Exception = None) -> None:
|
|
557
|
+
# using this within @safe decorator may result in recursive loop
|
|
558
|
+
try:
|
|
559
|
+
message = self._build_warning_message(msg, e)
|
|
560
|
+
if not message:
|
|
561
|
+
return
|
|
562
|
+
|
|
563
|
+
self._send(message, is_warning=True)
|
|
564
|
+
except Exception:
|
|
565
|
+
# if there's an exception here, there's nothing we can really do about it
|
|
566
|
+
pass
|
|
567
|
+
|
|
568
|
+
def _build_warning_message(self, warning_msg: str, e: Exception = None) -> dict:
|
|
569
|
+
try:
|
|
570
|
+
data = {"warning_message": warning_msg}
|
|
571
|
+
if e is not None:
|
|
572
|
+
data["exception"] = repr(e)
|
|
573
|
+
|
|
574
|
+
# add session and operation id if available
|
|
575
|
+
spark_session_id = self._request_summary.get().get("spark_session_id", None)
|
|
576
|
+
if spark_session_id is not None:
|
|
577
|
+
data["spark_session_id"] = spark_session_id
|
|
578
|
+
|
|
579
|
+
spark_operation_id = self._request_summary.get().get(
|
|
580
|
+
"spark_operation_id", None
|
|
581
|
+
)
|
|
582
|
+
if spark_operation_id is not None:
|
|
583
|
+
data["spark_operation_id"] = spark_operation_id
|
|
584
|
+
|
|
585
|
+
message = {
|
|
586
|
+
**_basic_telemetry_data(),
|
|
587
|
+
TelemetryField.KEY_TYPE.value: TelemetryType.TYPE_EVENT.value,
|
|
588
|
+
TelemetryType.EVENT_TYPE.value: EventType.WARNING.value,
|
|
589
|
+
TelemetryField.KEY_DATA.value: data,
|
|
590
|
+
}
|
|
591
|
+
return message
|
|
592
|
+
except Exception:
|
|
593
|
+
return {}
|
|
594
|
+
|
|
595
|
+
def _send(self, msg: dict, is_warning: bool = False) -> None:
|
|
545
596
|
"""Queue a telemetry message for asynchronous processing."""
|
|
546
597
|
if not self._is_enabled:
|
|
547
598
|
return
|
|
548
599
|
|
|
549
600
|
timestamp = get_time_millis()
|
|
550
601
|
try:
|
|
551
|
-
|
|
602
|
+
telemetry_msg = TelemetryMessage(
|
|
603
|
+
message=msg, timestamp=timestamp, is_warning=is_warning
|
|
604
|
+
)
|
|
605
|
+
self._message_queue.put_nowait(telemetry_msg)
|
|
552
606
|
except queue.Full:
|
|
553
607
|
# If queue is full, drop the message to avoid blocking
|
|
554
608
|
logger.warning("Telemetry queue is full, dropping message")
|
|
@@ -566,13 +620,16 @@ class Telemetry:
|
|
|
566
620
|
while True:
|
|
567
621
|
try:
|
|
568
622
|
# block to allow the GIL to switch threads
|
|
569
|
-
|
|
570
|
-
if
|
|
571
|
-
# shutdown
|
|
623
|
+
telemetry_msg = self._message_queue.get()
|
|
624
|
+
if telemetry_msg is None:
|
|
625
|
+
# shutdown signal
|
|
572
626
|
break
|
|
573
|
-
self._sink.add_telemetry_data(
|
|
574
|
-
|
|
575
|
-
|
|
627
|
+
self._sink.add_telemetry_data(
|
|
628
|
+
telemetry_msg.message, telemetry_msg.timestamp
|
|
629
|
+
)
|
|
630
|
+
except Exception as e:
|
|
631
|
+
if not telemetry_msg.is_warning:
|
|
632
|
+
self.send_warning_msg("Failed to add telemetry message to sink", e)
|
|
576
633
|
finally:
|
|
577
634
|
self._message_queue.task_done()
|
|
578
635
|
|
|
@@ -585,7 +642,7 @@ class Telemetry:
|
|
|
585
642
|
return
|
|
586
643
|
|
|
587
644
|
try:
|
|
588
|
-
self._message_queue.put_nowait(
|
|
645
|
+
self._message_queue.put_nowait(None)
|
|
589
646
|
# Wait for worker thread to finish
|
|
590
647
|
self._worker_thread.join(timeout=3.0)
|
|
591
648
|
except Exception:
|
|
@@ -594,7 +651,7 @@ class Telemetry:
|
|
|
594
651
|
)
|
|
595
652
|
|
|
596
653
|
|
|
597
|
-
def _error_location(e: Exception) ->
|
|
654
|
+
def _error_location(e: Exception) -> dict | None:
|
|
598
655
|
"""
|
|
599
656
|
Inspect the exception traceback and extract the file name, line number, and function name
|
|
600
657
|
from the last frame (the one that raised the exception).
|
|
@@ -675,7 +732,7 @@ def _protobuf_to_json_with_redaction(
|
|
|
675
732
|
"""Recursively convert protobuf message to dict"""
|
|
676
733
|
|
|
677
734
|
if not isinstance(msg, google.protobuf.message.Message):
|
|
678
|
-
|
|
735
|
+
telemetry.send_warning_msg(f"Expected a protobuf message, got: {type(msg)}")
|
|
679
736
|
return {}
|
|
680
737
|
|
|
681
738
|
result = {}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
snowflake/snowpark_connect/__init__.py,sha256=Sml4x1LTNnxZyw6nnDeJrZWUi3eUAR46Rsw6N-wHUSA,605
|
|
2
2
|
snowflake/snowpark_connect/client.py,sha256=K0PK8aIppghBnY0uKlDOi4WZBUhKxJQ_vRuDpB3YsbQ,2045
|
|
3
3
|
snowflake/snowpark_connect/column_name_handler.py,sha256=eiqUe7YdYFxjyHOAbD9gNCstOIuroZA2INkV4KhqGMg,27407
|
|
4
|
-
snowflake/snowpark_connect/config.py,sha256=
|
|
4
|
+
snowflake/snowpark_connect/config.py,sha256=m2ZPZPaarj2GglLnsuBCgsoJ7SLq3roIC-Jr5YyKl2k,29585
|
|
5
5
|
snowflake/snowpark_connect/constants.py,sha256=FBDxNUxdqWxnf6d5-eanHlYdFFyQqCqvNyZG-uOiO6Q,598
|
|
6
6
|
snowflake/snowpark_connect/control_server.py,sha256=mz3huYo84hgqUB6maZxu3LYyGq7vVL1nv7-7-MjuSYY,1956
|
|
7
7
|
snowflake/snowpark_connect/dataframe_container.py,sha256=0ozyUXrWErzM7Gltnb-i2o5ZyXVVeT_HCqpuYliQXwc,8798
|
|
@@ -14,7 +14,7 @@ snowflake/snowpark_connect/start_server.py,sha256=udegO0rk2FeSnXsIcCIYQW3VRlGDjB
|
|
|
14
14
|
snowflake/snowpark_connect/tcm.py,sha256=ftncZFbVO-uyWMhF1_HYKQykB7KobHEYoyQsYbQj1EM,203
|
|
15
15
|
snowflake/snowpark_connect/type_mapping.py,sha256=hAkOz9_uPi6XnFbd_KcwHqiWoqjt5ht7hlMvD7zR7Cw,45093
|
|
16
16
|
snowflake/snowpark_connect/typed_column.py,sha256=Tavii8b4zMj5IWOvN6tlOVmC80W6eQct0pC_tF2nlhU,3867
|
|
17
|
-
snowflake/snowpark_connect/version.py,sha256=
|
|
17
|
+
snowflake/snowpark_connect/version.py,sha256=ihyPxJymHC107aRgWHpDuSBrYCYH4zvut1JtZOdgS5o,118
|
|
18
18
|
snowflake/snowpark_connect/analyze_plan/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
19
19
|
snowflake/snowpark_connect/analyze_plan/map_tree_string.py,sha256=RFSNdDwOTXOd3gm_rKRJNyDd65zQpBxBpVOOwmKaFVA,1661
|
|
20
20
|
snowflake/snowpark_connect/error/__init__.py,sha256=oQo6k4zztLmNF1c5IvJLcS99J6RWY9KBTN3RJ2pKimg,249
|
|
@@ -392,11 +392,13 @@ snowflake/snowpark_connect/proto/control_pb2.pyi,sha256=DQVcgVtvo1W1tpdi8_ogBO6D
|
|
|
392
392
|
snowflake/snowpark_connect/proto/control_pb2_grpc.py,sha256=IPHf2f27M1HElF55HH-Kvd8WCFRo6SDj_EjEzFEe_rg,5444
|
|
393
393
|
snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.py,sha256=Buyg8ACpSbx4A9S-xA5MBAhYhF6oe7YXRs7wCcAU2Wc,3632
|
|
394
394
|
snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.pyi,sha256=4LLUkliHNthv-H4yFTargdc8FIuytWrg9Ki4JU4Zm1E,4540
|
|
395
|
+
snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
395
396
|
snowflake/snowpark_connect/proto/snowflake_rdd_pb2.pyi,sha256=IA_TC6cYM50qTlpSL0UwhmbNkNYgEYuNcZLYTxbGfBc,1780
|
|
396
397
|
snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.py,sha256=now6RWB80CV5Jko0DBLFMdXYXzvfX30uhHqHb-hKe2o,4886
|
|
397
398
|
snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.pyi,sha256=4PZkACNf5ulshE8yP2Sl36CXH4UBpIKvZPhzFqeEbsM,7302
|
|
399
|
+
snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
398
400
|
snowflake/snowpark_connect/relation/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
399
|
-
snowflake/snowpark_connect/relation/io_utils.py,sha256=
|
|
401
|
+
snowflake/snowpark_connect/relation/io_utils.py,sha256=5FzR34-rEcZkxtLSWpArzpCwr0_Zi9jNY-KM80XBj_k,4266
|
|
400
402
|
snowflake/snowpark_connect/relation/map_aggregate.py,sha256=KElVYO62V3jkU8ldTCfTX2F_atP_GZAozPY1X0W42XQ,19189
|
|
401
403
|
snowflake/snowpark_connect/relation/map_catalog.py,sha256=mcx6An4fqHAxy2OhOC66Xe_0aRtYPDGkBEgMK5CfaXU,5822
|
|
402
404
|
snowflake/snowpark_connect/relation/map_column_ops.py,sha256=cQ9Cde2PMAGXoQWSd984nsp86JCZL7YsAHvfu8Vi50c,49199
|
|
@@ -406,22 +408,22 @@ snowflake/snowpark_connect/relation/map_join.py,sha256=I-_jifoCOtahG49maa1Ew4zQ-
|
|
|
406
408
|
snowflake/snowpark_connect/relation/map_local_relation.py,sha256=VBfwBT75GQUe01UOZptwcYsI7zZxaIMQyTOG6kmVuJ0,15219
|
|
407
409
|
snowflake/snowpark_connect/relation/map_map_partitions.py,sha256=y6WhtacyYRmV8zLv3dQtBP2asijLze363htQbgNyoak,4130
|
|
408
410
|
snowflake/snowpark_connect/relation/map_relation.py,sha256=6RncDhkGXnme6m3sCzOmyfm9x0aM-aqn9H60HY-jdzg,12733
|
|
409
|
-
snowflake/snowpark_connect/relation/map_row_ops.py,sha256=
|
|
411
|
+
snowflake/snowpark_connect/relation/map_row_ops.py,sha256=76qMXMLisEZpcjLRHkkqufKEIK8NAiGOKcwTI1N-qkY,34877
|
|
410
412
|
snowflake/snowpark_connect/relation/map_sample_by.py,sha256=8ALQbeUsB89sI3uiUFqG3w1A4TtOzOAL4umdKp6-c38,1530
|
|
411
413
|
snowflake/snowpark_connect/relation/map_show_string.py,sha256=-QgOCZ_Kf7qDBN6hd0-eTxXqvVvzoJxw5hGBdk9Upgs,3669
|
|
412
414
|
snowflake/snowpark_connect/relation/map_sql.py,sha256=fDGsl_ZpNo2_rkhXV1cO6i6lnUAnrK-bpiBcKiCnbGU,105507
|
|
413
|
-
snowflake/snowpark_connect/relation/map_stats.py,sha256=
|
|
415
|
+
snowflake/snowpark_connect/relation/map_stats.py,sha256=xc0ito9lxl3HtYUnWEVuIwV0h3vdD3inSnfmJmztO1g,14493
|
|
414
416
|
snowflake/snowpark_connect/relation/map_subquery_alias.py,sha256=rHgE9XUzuWWkjNPtJz3Sxzz2aFo690paHKZh9frqPXk,1456
|
|
415
417
|
snowflake/snowpark_connect/relation/map_udtf.py,sha256=cfDnbZ3TRJ6eb0EVResu6GL-OwQpaEabWLbrhgWnkRw,13316
|
|
416
418
|
snowflake/snowpark_connect/relation/stage_locator.py,sha256=3PnSmuubtU04wTf4B_kyFK1ag45QXBjzfoKY5mu5Gtk,8925
|
|
417
|
-
snowflake/snowpark_connect/relation/utils.py,sha256=
|
|
419
|
+
snowflake/snowpark_connect/relation/utils.py,sha256=I5June2uch3hsXoQk21y2v_UkLVsBbaJtpV8qEcwIok,8916
|
|
418
420
|
snowflake/snowpark_connect/relation/catalogs/__init__.py,sha256=0yJ5Nfg7SIxudI0P7_U5EWPyiTpkMet8tSq-IwutSZo,265
|
|
419
421
|
snowflake/snowpark_connect/relation/catalogs/abstract_spark_catalog.py,sha256=wujeuKotJZRzpPp_WqVB9TkfprFU2tIXTQJk_eFOR0o,9826
|
|
420
422
|
snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py,sha256=e9rXL0DOAf4VBZfa-idqehLAEZoCgIoE4IDiLVvR4dQ,28475
|
|
421
423
|
snowflake/snowpark_connect/relation/catalogs/utils.py,sha256=AgiBkK9Xwm9ZyjsKZYK9eTb4YZ9C5dAimD9DLlft-tY,1753
|
|
422
424
|
snowflake/snowpark_connect/relation/read/__init__.py,sha256=5J3IOTKu4Qmenouz1Oz_bUu_4c6KpxtaC63mPUGLyeY,132
|
|
423
425
|
snowflake/snowpark_connect/relation/read/jdbc_read_dbapi.py,sha256=nwvBYwdDTRQc8ljt4CLLaVl5M2uei1TDICJkz7CDSG8,24608
|
|
424
|
-
snowflake/snowpark_connect/relation/read/map_read.py,sha256=
|
|
426
|
+
snowflake/snowpark_connect/relation/read/map_read.py,sha256=9SBe9h1ZVv9QFQIfFwim2BQODToCIUclNYETZhEjKsA,15770
|
|
425
427
|
snowflake/snowpark_connect/relation/read/map_read_csv.py,sha256=3piGbSdzx5SPr10KCaqF8WrjWDlCeXQlUi3d4cfpeU8,8269
|
|
426
428
|
snowflake/snowpark_connect/relation/read/map_read_jdbc.py,sha256=rfuf2Gp89v-9YFhcXFGdpzmfUgYm1ml0wYN5fdu5HA8,3950
|
|
427
429
|
snowflake/snowpark_connect/relation/read/map_read_json.py,sha256=VWd_g7OeE2dBMxJFvLXt4ntdN9fu8Izerdt3-RGmDYA,13255
|
|
@@ -434,7 +436,7 @@ snowflake/snowpark_connect/relation/read/reader_config.py,sha256=PMh1R5IjqqTwiAA
|
|
|
434
436
|
snowflake/snowpark_connect/relation/read/utils.py,sha256=rIIM6d2WXHh7MLGyHNiRc9tS8b0dmyFQr7rHepIYJOU,4111
|
|
435
437
|
snowflake/snowpark_connect/relation/write/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
436
438
|
snowflake/snowpark_connect/relation/write/jdbc_write_dbapi.py,sha256=GI9FyGZuQQNV-6Q8Ob-Xr0im3iAPdH-Jkyx8bjwbOuE,11931
|
|
437
|
-
snowflake/snowpark_connect/relation/write/map_write.py,sha256=
|
|
439
|
+
snowflake/snowpark_connect/relation/write/map_write.py,sha256=w47pAPxGj1HMZggmxsMsKQEWw47bK3DBct24nMB48aA,47553
|
|
438
440
|
snowflake/snowpark_connect/relation/write/map_write_jdbc.py,sha256=1nOWRgjtZzfRwnSRGFP9V6mqBVlGhSBr2KHGHbe4JMU,1404
|
|
439
441
|
snowflake/snowpark_connect/resources/java_udfs-1.0-SNAPSHOT.jar,sha256=tVyOp6tXxu9nm6SDufwQiGzfH3pnuh_7PowsMZxOolY,9773
|
|
440
442
|
snowflake/snowpark_connect/utils/__init__.py,sha256=xsIE96jDASko3F-MeNf4T4Gg5ufthS8CejeiJDfri0M,76
|
|
@@ -451,9 +453,9 @@ snowflake/snowpark_connect/utils/io_utils.py,sha256=j8-t_Vvy0qJhA7EeSYlEqqmc2-os
|
|
|
451
453
|
snowflake/snowpark_connect/utils/pandas_udtf_utils.py,sha256=3WA_9IVRZL8fnwIHo048LTg62-bPGfCDUZzYd-zjzQQ,7564
|
|
452
454
|
snowflake/snowpark_connect/utils/profiling.py,sha256=ttdHzQUYarvTqJASLNuKFIax7ejO39Tv1mHKl0QjRkg,1519
|
|
453
455
|
snowflake/snowpark_connect/utils/scala_udf_utils.py,sha256=RFDDMmgQ_xBWk98kdfWaw4Hla3ZqYf3UAijU4uAUNdA,23011
|
|
454
|
-
snowflake/snowpark_connect/utils/session.py,sha256=
|
|
456
|
+
snowflake/snowpark_connect/utils/session.py,sha256=3-76jBcSQFOIJVmXvyClMI2kIz_5PJXWTHE5pcSo_8w,7431
|
|
455
457
|
snowflake/snowpark_connect/utils/snowpark_connect_logging.py,sha256=23bvbALGqixJ3Ap9QWM3OpcKNK-sog2mr9liSmvwqYU,1123
|
|
456
|
-
snowflake/snowpark_connect/utils/telemetry.py,sha256=
|
|
458
|
+
snowflake/snowpark_connect/utils/telemetry.py,sha256=Eh8v7kXbkvfZFcCk_fyMUHM7KjWg7CkeIxYKzvQ1vTo,25267
|
|
457
459
|
snowflake/snowpark_connect/utils/temporary_view_cache.py,sha256=M1czwb-uv0nP9w1meB72NR7cj0iryJc_OYFPztx5XuU,2080
|
|
458
460
|
snowflake/snowpark_connect/utils/udf_cache.py,sha256=AmUFc6iv0UvJF8iH6kAp4BWcZ3O465qyS1pcxf5mQZ8,13342
|
|
459
461
|
snowflake/snowpark_connect/utils/udf_helper.py,sha256=g-TxTs4ARyJWYgADrosfQQG-ykBBQdm1g5opslxJq_E,12563
|
|
@@ -467,17 +469,17 @@ snowflake/snowpark_decoder/dp_session.py,sha256=HIr3TfKgYl5zqaGR5xpFU9ZVkcaTB9I8
|
|
|
467
469
|
snowflake/snowpark_decoder/spark_decoder.py,sha256=EQiCvBiqB736Bc17o3gnYGtcYVcyfGxroO5e1kbe1Co,2885
|
|
468
470
|
snowflake/snowpark_decoder/_internal/proto/generated/DataframeProcessorMsg_pb2.py,sha256=2eSDqeyfMvmIJ6_rF663DrEe1dg_anrP4OpVJNTJHaQ,2598
|
|
469
471
|
snowflake/snowpark_decoder/_internal/proto/generated/DataframeProcessorMsg_pb2.pyi,sha256=aIH23k52bXdw5vO3RtM5UcOjDPaWsJFx1SRUSk3qOK8,6142
|
|
470
|
-
snowpark_connect-0.30.
|
|
471
|
-
snowpark_connect-0.30.
|
|
472
|
-
snowpark_connect-0.30.
|
|
473
|
-
snowpark_connect-0.30.
|
|
474
|
-
snowpark_connect-0.30.
|
|
475
|
-
snowpark_connect-0.30.
|
|
472
|
+
snowpark_connect-0.30.1.data/scripts/snowpark-connect,sha256=yZ94KqbWACxnwV8mpg8NjILvvRNjnF8B3cs3ZFNuIM4,1546
|
|
473
|
+
snowpark_connect-0.30.1.data/scripts/snowpark-session,sha256=NMAHSonTo-nmOZSkQNlszUC0jLJ8QWEDUsUmMe2UAOw,190
|
|
474
|
+
snowpark_connect-0.30.1.data/scripts/snowpark-submit,sha256=Zd98H9W_d0dIqMSkQLdHyW5G3myxF0t4c3vNBt2nD6A,12056
|
|
475
|
+
snowpark_connect-0.30.1.dist-info/licenses/LICENSE-binary,sha256=fmBlX39HwTlBUyiKEznaLZGuxQy-7ndLLG_rTXjF02Y,22916
|
|
476
|
+
snowpark_connect-0.30.1.dist-info/licenses/LICENSE.txt,sha256=Ff9cPv4xu0z7bnMTHzo4vDncOShsy33w4oJMA2xjn6c,11365
|
|
477
|
+
snowpark_connect-0.30.1.dist-info/licenses/NOTICE-binary,sha256=elMF8brgGNJwOz8YdorzBF6-U8ZhR8F-77FfGkZng7U,57843
|
|
476
478
|
spark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
477
479
|
spark/connect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
478
480
|
spark/connect/envelope_pb2.py,sha256=7Gc6OUA3vaCuTCIKamb_Iiw7W9jPTcWNEv1im20eWHM,2726
|
|
479
481
|
spark/connect/envelope_pb2.pyi,sha256=VXTJSPpcxzB_dWqVdvPY4KkPhJfh0WmkX7SNHWoLhx0,3358
|
|
480
|
-
snowpark_connect-0.30.
|
|
481
|
-
snowpark_connect-0.30.
|
|
482
|
-
snowpark_connect-0.30.
|
|
483
|
-
snowpark_connect-0.30.
|
|
482
|
+
snowpark_connect-0.30.1.dist-info/METADATA,sha256=GLLoHHRS1oJ03td7EIC8N3EsPqmw7KH3xaZbNNr2wi4,1625
|
|
483
|
+
snowpark_connect-0.30.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
484
|
+
snowpark_connect-0.30.1.dist-info/top_level.txt,sha256=ExnWqVpoTHRG99fu_AxXZVOz8c-De7nNu0yFCGylM8I,16
|
|
485
|
+
snowpark_connect-0.30.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/licenses/LICENSE-binary
RENAMED
|
File without changes
|
{snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/licenses/LICENSE.txt
RENAMED
|
File without changes
|
{snowpark_connect-0.30.0.dist-info → snowpark_connect-0.30.1.dist-info}/licenses/NOTICE-binary
RENAMED
|
File without changes
|
|
File without changes
|