snowpark-connect 0.27.0__py3-none-any.whl → 1.7.0__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.
- snowflake/snowpark_connect/__init__.py +1 -0
- snowflake/snowpark_connect/analyze_plan/map_tree_string.py +8 -4
- snowflake/snowpark_connect/client/__init__.py +15 -0
- snowflake/snowpark_connect/client/error_utils.py +30 -0
- snowflake/snowpark_connect/client/exceptions.py +36 -0
- snowflake/snowpark_connect/client/query_results.py +90 -0
- snowflake/snowpark_connect/client/server.py +717 -0
- snowflake/snowpark_connect/client/utils/__init__.py +10 -0
- snowflake/snowpark_connect/client/utils/session.py +85 -0
- snowflake/snowpark_connect/column_name_handler.py +404 -243
- snowflake/snowpark_connect/column_qualifier.py +43 -0
- snowflake/snowpark_connect/config.py +309 -26
- snowflake/snowpark_connect/constants.py +2 -0
- snowflake/snowpark_connect/dataframe_container.py +102 -8
- snowflake/snowpark_connect/date_time_format_mapping.py +71 -13
- snowflake/snowpark_connect/error/error_codes.py +50 -0
- snowflake/snowpark_connect/error/error_utils.py +172 -23
- snowflake/snowpark_connect/error/exceptions.py +13 -4
- snowflake/snowpark_connect/execute_plan/map_execution_command.py +15 -160
- snowflake/snowpark_connect/execute_plan/map_execution_root.py +26 -20
- snowflake/snowpark_connect/execute_plan/utils.py +5 -1
- snowflake/snowpark_connect/expression/error_utils.py +28 -0
- snowflake/snowpark_connect/expression/function_defaults.py +9 -2
- snowflake/snowpark_connect/expression/hybrid_column_map.py +53 -5
- snowflake/snowpark_connect/expression/integral_types_support.py +219 -0
- snowflake/snowpark_connect/expression/literal.py +37 -13
- snowflake/snowpark_connect/expression/map_cast.py +224 -15
- snowflake/snowpark_connect/expression/map_expression.py +80 -27
- snowflake/snowpark_connect/expression/map_extension.py +322 -12
- snowflake/snowpark_connect/expression/map_sql_expression.py +316 -81
- snowflake/snowpark_connect/expression/map_udf.py +86 -20
- snowflake/snowpark_connect/expression/map_unresolved_attribute.py +451 -173
- snowflake/snowpark_connect/expression/map_unresolved_function.py +2964 -829
- snowflake/snowpark_connect/expression/map_unresolved_star.py +87 -23
- snowflake/snowpark_connect/expression/map_update_fields.py +70 -18
- snowflake/snowpark_connect/expression/map_window_function.py +18 -3
- snowflake/snowpark_connect/includes/jars/json4s-ast_2.13-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/{scala-library-2.12.18.jar → sas-scala-udf_2.12-0.2.0.jar} +0 -0
- snowflake/snowpark_connect/includes/jars/sas-scala-udf_2.13-0.2.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-reflect-2.13.16.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-common-utils_2.13-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/{spark-connect-client-jvm_2.12-3.5.6.jar → spark-connect-client-jvm_2.13-3.5.6.jar} +0 -0
- snowflake/snowpark_connect/includes/jars/{spark-sql_2.12-3.5.6.jar → spark-sql_2.13-3.5.6.jar} +0 -0
- snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/foreach_batch_worker.py +1 -1
- snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/listener_worker.py +1 -1
- snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.py +12 -10
- snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.pyi +14 -2
- snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.py +10 -8
- snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.pyi +13 -6
- snowflake/snowpark_connect/relation/catalogs/abstract_spark_catalog.py +65 -17
- snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py +297 -49
- snowflake/snowpark_connect/relation/catalogs/utils.py +12 -4
- snowflake/snowpark_connect/relation/io_utils.py +110 -10
- snowflake/snowpark_connect/relation/map_aggregate.py +239 -256
- snowflake/snowpark_connect/relation/map_catalog.py +5 -1
- snowflake/snowpark_connect/relation/map_column_ops.py +264 -96
- snowflake/snowpark_connect/relation/map_extension.py +263 -29
- snowflake/snowpark_connect/relation/map_join.py +683 -442
- snowflake/snowpark_connect/relation/map_local_relation.py +28 -1
- snowflake/snowpark_connect/relation/map_map_partitions.py +83 -8
- snowflake/snowpark_connect/relation/map_relation.py +48 -19
- snowflake/snowpark_connect/relation/map_row_ops.py +310 -91
- snowflake/snowpark_connect/relation/map_show_string.py +13 -6
- snowflake/snowpark_connect/relation/map_sql.py +1233 -222
- snowflake/snowpark_connect/relation/map_stats.py +48 -9
- snowflake/snowpark_connect/relation/map_subquery_alias.py +11 -2
- snowflake/snowpark_connect/relation/map_udtf.py +14 -4
- snowflake/snowpark_connect/relation/read/jdbc_read_dbapi.py +53 -14
- snowflake/snowpark_connect/relation/read/map_read.py +134 -43
- snowflake/snowpark_connect/relation/read/map_read_csv.py +326 -47
- snowflake/snowpark_connect/relation/read/map_read_jdbc.py +21 -6
- snowflake/snowpark_connect/relation/read/map_read_json.py +324 -86
- snowflake/snowpark_connect/relation/read/map_read_parquet.py +146 -28
- snowflake/snowpark_connect/relation/read/map_read_partitioned_parquet.py +142 -0
- snowflake/snowpark_connect/relation/read/map_read_socket.py +15 -3
- snowflake/snowpark_connect/relation/read/map_read_table.py +86 -6
- snowflake/snowpark_connect/relation/read/map_read_text.py +22 -4
- snowflake/snowpark_connect/relation/read/metadata_utils.py +170 -0
- snowflake/snowpark_connect/relation/read/reader_config.py +42 -3
- snowflake/snowpark_connect/relation/read/utils.py +50 -5
- snowflake/snowpark_connect/relation/stage_locator.py +91 -55
- snowflake/snowpark_connect/relation/utils.py +128 -5
- snowflake/snowpark_connect/relation/write/jdbc_write_dbapi.py +19 -3
- snowflake/snowpark_connect/relation/write/map_write.py +929 -319
- snowflake/snowpark_connect/relation/write/map_write_jdbc.py +8 -2
- snowflake/snowpark_connect/resources/java_udfs-1.0-SNAPSHOT.jar +0 -0
- snowflake/snowpark_connect/resources_initializer.py +171 -48
- snowflake/snowpark_connect/server.py +528 -473
- snowflake/snowpark_connect/server_common/__init__.py +503 -0
- snowflake/snowpark_connect/snowflake_session.py +65 -0
- snowflake/snowpark_connect/start_server.py +53 -5
- snowflake/snowpark_connect/type_mapping.py +349 -27
- snowflake/snowpark_connect/type_support.py +130 -0
- snowflake/snowpark_connect/typed_column.py +9 -7
- snowflake/snowpark_connect/utils/artifacts.py +9 -8
- snowflake/snowpark_connect/utils/cache.py +49 -27
- snowflake/snowpark_connect/utils/concurrent.py +36 -1
- snowflake/snowpark_connect/utils/context.py +195 -37
- snowflake/snowpark_connect/utils/describe_query_cache.py +68 -53
- snowflake/snowpark_connect/utils/env_utils.py +5 -1
- snowflake/snowpark_connect/utils/expression_transformer.py +172 -0
- snowflake/snowpark_connect/utils/identifiers.py +137 -3
- snowflake/snowpark_connect/utils/io_utils.py +57 -1
- snowflake/snowpark_connect/utils/java_stored_procedure.py +151 -0
- snowflake/snowpark_connect/utils/java_udaf_utils.py +321 -0
- snowflake/snowpark_connect/utils/java_udtf_utils.py +239 -0
- snowflake/snowpark_connect/utils/jvm_udf_utils.py +281 -0
- snowflake/snowpark_connect/utils/open_telemetry.py +516 -0
- snowflake/snowpark_connect/utils/pandas_udtf_utils.py +8 -4
- snowflake/snowpark_connect/utils/patch_spark_line_number.py +181 -0
- snowflake/snowpark_connect/utils/profiling.py +25 -8
- snowflake/snowpark_connect/utils/scala_udf_utils.py +185 -340
- snowflake/snowpark_connect/utils/sequence.py +21 -0
- snowflake/snowpark_connect/utils/session.py +64 -28
- snowflake/snowpark_connect/utils/snowpark_connect_logging.py +51 -9
- snowflake/snowpark_connect/utils/spcs_logger.py +290 -0
- snowflake/snowpark_connect/utils/telemetry.py +192 -40
- snowflake/snowpark_connect/utils/temporary_view_cache.py +67 -0
- snowflake/snowpark_connect/utils/temporary_view_helper.py +334 -0
- snowflake/snowpark_connect/utils/udf_cache.py +117 -41
- snowflake/snowpark_connect/utils/udf_helper.py +39 -37
- snowflake/snowpark_connect/utils/udf_utils.py +133 -14
- snowflake/snowpark_connect/utils/udtf_helper.py +8 -1
- snowflake/snowpark_connect/utils/udtf_utils.py +46 -31
- snowflake/snowpark_connect/utils/udxf_import_utils.py +9 -2
- snowflake/snowpark_connect/utils/upload_java_jar.py +57 -0
- snowflake/snowpark_connect/version.py +1 -1
- snowflake/snowpark_decoder/dp_session.py +6 -2
- snowflake/snowpark_decoder/spark_decoder.py +12 -0
- {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-submit +14 -4
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/METADATA +16 -7
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/RECORD +139 -168
- snowflake/snowpark_connect/hidden_column.py +0 -39
- snowflake/snowpark_connect/includes/jars/antlr4-runtime-4.9.3.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-cli-1.5.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-codec-1.16.1.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-collections-3.2.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-collections4-4.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-compiler-3.1.9.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-compress-1.26.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-crypto-1.1.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-dbcp-1.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-io-2.16.1.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-lang-2.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-lang3-3.12.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-logging-1.1.3.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-math3-3.6.1.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-pool-1.5.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/commons-text-1.10.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/hadoop-client-api-trimmed-3.3.4.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-annotations-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-core-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-core-asl-1.9.13.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-databind-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-dataformat-yaml-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-datatype-jsr310-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/jackson-module-scala_2.12-2.15.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-ast_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-core_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-jackson_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-native_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/json4s-scalap_2.12-3.7.0-M11.jar +0 -0
- snowflake/snowpark_connect/includes/jars/kryo-shaded-4.0.2.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-1.2-api-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-api-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-core-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/log4j-slf4j2-impl-2.20.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/paranamer-2.8.3.jar +0 -0
- snowflake/snowpark_connect/includes/jars/paranamer-2.8.jar +0 -0
- snowflake/snowpark_connect/includes/jars/sas-scala-udf_2.12-0.1.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-collection-compat_2.12-2.7.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-parser-combinators_2.12-2.3.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-reflect-2.12.18.jar +0 -0
- snowflake/snowpark_connect/includes/jars/scala-xml_2.12-2.1.0.jar +0 -0
- snowflake/snowpark_connect/includes/jars/slf4j-api-2.0.7.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-catalyst_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-common-utils_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-core_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-graphx_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-hive-thriftserver_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-hive_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-kvstore_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-launcher_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-mesos_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-mllib-local_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-network-common_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-network-shuffle_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-repl_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-sketch_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-sql-api_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-tags_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-unsafe_2.12-3.5.6.jar +0 -0
- snowflake/snowpark_connect/includes/jars/spark-yarn_2.12-3.5.6.jar +0 -0
- {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-connect +0 -0
- {snowpark_connect-0.27.0.data → snowpark_connect-1.7.0.data}/scripts/snowpark-session +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/WHEEL +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/LICENSE-binary +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/licenses/NOTICE-binary +0 -0
- {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (c) 2012-2025 Snowflake Computing Inc. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
OpenTelemetry context management for Snowpark Connect.
|
|
7
|
+
Handles OpenTelemetry initialization, root span creation, and context propagation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
from snowflake.snowpark_connect.utils.snowpark_connect_logging import logger
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
from opentelemetry import context, trace
|
|
17
|
+
from opentelemetry.trace import Status, StatusCode
|
|
18
|
+
|
|
19
|
+
OTEL_AVAILABLE = True
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import (
|
|
23
|
+
OTLPMetricExporter,
|
|
24
|
+
)
|
|
25
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
|
|
26
|
+
OTLPSpanExporter,
|
|
27
|
+
)
|
|
28
|
+
from opentelemetry.metrics import set_meter_provider
|
|
29
|
+
from opentelemetry.sdk.metrics import MeterProvider
|
|
30
|
+
from opentelemetry.sdk.metrics._internal.export import (
|
|
31
|
+
PeriodicExportingMetricReader,
|
|
32
|
+
)
|
|
33
|
+
from opentelemetry.sdk.resources import Resource
|
|
34
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
35
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
36
|
+
from snowflake.telemetry.trace import SnowflakeTraceIdGenerator
|
|
37
|
+
|
|
38
|
+
OTEL_EXPORTERS_AVAILABLE = True
|
|
39
|
+
logger.debug("OpenTelemetry exporters available")
|
|
40
|
+
except ImportError as e:
|
|
41
|
+
logger.warning(f"OpenTelemetry exporters not available: {e}")
|
|
42
|
+
OTEL_EXPORTERS_AVAILABLE = False
|
|
43
|
+
|
|
44
|
+
except ImportError as e:
|
|
45
|
+
logger.warning(f"OpenTelemetry basic modules not available: {e}")
|
|
46
|
+
OTEL_AVAILABLE = False
|
|
47
|
+
OTEL_EXPORTERS_AVAILABLE = False
|
|
48
|
+
trace = None
|
|
49
|
+
context = None
|
|
50
|
+
|
|
51
|
+
# Environment variable configuration
|
|
52
|
+
TELEMETRY_ENV_VAR = "SNOWPARK_TELEMETRY_ENABLED"
|
|
53
|
+
DEFAULT_TELEMETRY_ENABLED = True
|
|
54
|
+
|
|
55
|
+
# Span name constants
|
|
56
|
+
SNOWPARK_CONNECT_CREATED_ROOT_SPAN = "snowpark_connect_span"
|
|
57
|
+
|
|
58
|
+
# Global State Variables
|
|
59
|
+
_root_span_otel_context = None # OpenTelemetry context containing root span
|
|
60
|
+
_root_span = None # Main root span for all operations
|
|
61
|
+
_root_span_context_token = None # Token for context activation/deactivation
|
|
62
|
+
_tracer_provider = None # TracerProvider instance for telemetry cleanup
|
|
63
|
+
_meter_provider = None # MeterProvider instance for metrics
|
|
64
|
+
_root_span_ended = False # Flag to ensure span ends only once
|
|
65
|
+
_is_inherited_context = False # Track if using existing Snowflake context
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def otel_create_context_wrapper(func):
|
|
69
|
+
"""
|
|
70
|
+
Create a wrapper function that transfers OpenTelemetry context to any thread function.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
func: Any function to wrap
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
function: Either the wrapped function with context transfer or the original function
|
|
77
|
+
"""
|
|
78
|
+
if is_telemetry_enabled():
|
|
79
|
+
try:
|
|
80
|
+
current_context = context.get_current()
|
|
81
|
+
|
|
82
|
+
def func_with_telemetry_context(*args, **kwargs):
|
|
83
|
+
"""Run function with OpenTelemetry context attached"""
|
|
84
|
+
context_token = None
|
|
85
|
+
try:
|
|
86
|
+
context_token = context.attach(current_context)
|
|
87
|
+
return func(*args, **kwargs)
|
|
88
|
+
finally:
|
|
89
|
+
if context_token:
|
|
90
|
+
context.detach(context_token)
|
|
91
|
+
|
|
92
|
+
return func_with_telemetry_context
|
|
93
|
+
|
|
94
|
+
except ImportError:
|
|
95
|
+
logger.warning("Failed to import OpenTelemetry context for thread transfer")
|
|
96
|
+
return func
|
|
97
|
+
else:
|
|
98
|
+
return func
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Helper Functions
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _setup_tracer_provider():
|
|
105
|
+
"""Set up basic TracerProvider if not already initialized"""
|
|
106
|
+
current_provider = trace.get_tracer_provider()
|
|
107
|
+
provider_type = current_provider.__class__.__name__
|
|
108
|
+
|
|
109
|
+
if provider_type not in ["NoOpTracerProvider", "ProxyTracerProvider"]:
|
|
110
|
+
return current_provider
|
|
111
|
+
|
|
112
|
+
# Initialize TracerProvider
|
|
113
|
+
resource = Resource.create({"service.name": "snowpark-telemetry"})
|
|
114
|
+
tracer_provider = TracerProvider(
|
|
115
|
+
resource=resource, id_generator=SnowflakeTraceIdGenerator()
|
|
116
|
+
)
|
|
117
|
+
trace.set_tracer_provider(tracer_provider)
|
|
118
|
+
|
|
119
|
+
global _tracer_provider
|
|
120
|
+
_tracer_provider = tracer_provider
|
|
121
|
+
|
|
122
|
+
return tracer_provider
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _check_existing_telemetry_context():
|
|
126
|
+
"""Check if we're running within existing Snowflake telemetry context"""
|
|
127
|
+
if not OTEL_AVAILABLE:
|
|
128
|
+
return False, None
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
current_span = trace.get_current_span()
|
|
132
|
+
|
|
133
|
+
if current_span and current_span.is_recording():
|
|
134
|
+
# Check if this is a Snowflake span (not our own root span)
|
|
135
|
+
span_name = getattr(current_span, "name", "unknown")
|
|
136
|
+
|
|
137
|
+
# Don't inherit if it's our own sas_spcs span
|
|
138
|
+
if span_name == SNOWPARK_CONNECT_CREATED_ROOT_SPAN:
|
|
139
|
+
return False, None
|
|
140
|
+
|
|
141
|
+
existing_context = context.get_current()
|
|
142
|
+
return True, existing_context
|
|
143
|
+
else:
|
|
144
|
+
return False, None
|
|
145
|
+
except Exception as e:
|
|
146
|
+
logger.warning(f"Failed to check existing context: {e}")
|
|
147
|
+
return False, None
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _create_root_span() -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Create the main application span for Snowpark Connect operations.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
bool: True if root span was successfully created, False otherwise
|
|
156
|
+
"""
|
|
157
|
+
if not is_telemetry_enabled():
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
tracer = trace.get_tracer("snowpark-connect-telemetry")
|
|
162
|
+
# This span gets triggered for snowpark-submit applications
|
|
163
|
+
span = tracer.start_span(SNOWPARK_CONNECT_CREATED_ROOT_SPAN)
|
|
164
|
+
|
|
165
|
+
if span.is_recording():
|
|
166
|
+
span.set_attribute("application.type", "snowpark-connect")
|
|
167
|
+
span.set_attribute("service.name", "snowpark-connect-telemetry")
|
|
168
|
+
span.set_attribute("operation.name", "snowpark_connect_application")
|
|
169
|
+
|
|
170
|
+
span_context = trace.set_span_in_context(span)
|
|
171
|
+
activation_token = context.attach(span_context)
|
|
172
|
+
|
|
173
|
+
global _root_span, _root_span_context_token
|
|
174
|
+
_root_span = span
|
|
175
|
+
_root_span_context_token = activation_token
|
|
176
|
+
return True
|
|
177
|
+
|
|
178
|
+
except Exception as e:
|
|
179
|
+
logger.warning(f"Failed to create root span: {e}")
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _get_current_otel_context():
|
|
184
|
+
"""Capture current OpenTelemetry context for thread transfer"""
|
|
185
|
+
if not OTEL_AVAILABLE:
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
current_span = trace.get_current_span()
|
|
190
|
+
if current_span:
|
|
191
|
+
current_otel_context = context.get_current()
|
|
192
|
+
return current_otel_context
|
|
193
|
+
else:
|
|
194
|
+
return None
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logger.warning(f"Failed to capture telemetry context: {e}")
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _setup_exporters_for_spcs():
|
|
201
|
+
"""Set up OTLP exporters for SPCS mode"""
|
|
202
|
+
if not OTEL_EXPORTERS_AVAILABLE or not _tracer_provider:
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
otlp_exporter = OTLPSpanExporter(insecure=True)
|
|
207
|
+
batch_processor = BatchSpanProcessor(otlp_exporter, schedule_delay_millis=1000)
|
|
208
|
+
_tracer_provider.add_span_processor(batch_processor)
|
|
209
|
+
|
|
210
|
+
resource = Resource.create({"service.name": "snowpark-telemetry"})
|
|
211
|
+
meter_provider = MeterProvider(
|
|
212
|
+
resource=resource,
|
|
213
|
+
metric_readers=[
|
|
214
|
+
PeriodicExportingMetricReader(
|
|
215
|
+
OTLPMetricExporter(insecure=True), export_interval_millis=30000
|
|
216
|
+
)
|
|
217
|
+
],
|
|
218
|
+
)
|
|
219
|
+
set_meter_provider(meter_provider)
|
|
220
|
+
|
|
221
|
+
global _meter_provider
|
|
222
|
+
_meter_provider = meter_provider
|
|
223
|
+
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
logger.warning(f"Failed to setup exporters: {e}")
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# Main Initialization Functions
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def otel_initialize():
|
|
235
|
+
"""
|
|
236
|
+
Initialize OpenTelemetry for Snowpark Connect.
|
|
237
|
+
|
|
238
|
+
This function:
|
|
239
|
+
1. Checks for existing Snowflake telemetry context (e.g., stored procs/notebooks)
|
|
240
|
+
2. Sets up OTLP exporters if running in SPCS
|
|
241
|
+
3. Creates root span for tracking operations
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
bool: True if telemetry was successfully initialized, False otherwise
|
|
245
|
+
"""
|
|
246
|
+
if not is_telemetry_enabled():
|
|
247
|
+
return False
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
# First check for existing context BEFORE setting up our own TracerProvider
|
|
251
|
+
# This is important for stored procedures and notebooks that already have Snowflake context
|
|
252
|
+
has_existing_context, existing_context = _check_existing_telemetry_context()
|
|
253
|
+
|
|
254
|
+
global _is_inherited_context, _root_span_otel_context
|
|
255
|
+
|
|
256
|
+
if has_existing_context:
|
|
257
|
+
# Inherit existing Snowflake context - don't create new span or TracerProvider
|
|
258
|
+
logger.debug("Using existing Snowflake telemetry context")
|
|
259
|
+
_is_inherited_context = True
|
|
260
|
+
_root_span_otel_context = existing_context
|
|
261
|
+
return True
|
|
262
|
+
|
|
263
|
+
# No existing context found, set up our own TracerProvider
|
|
264
|
+
_setup_tracer_provider()
|
|
265
|
+
|
|
266
|
+
# Set up OTLP exporters if running in SPCS
|
|
267
|
+
from snowflake.snowpark_connect.utils.session import _is_running_in_SPCS
|
|
268
|
+
|
|
269
|
+
if _is_running_in_SPCS():
|
|
270
|
+
logger.debug("Running in SPCS, setting up OTLP exporters")
|
|
271
|
+
_setup_exporters_for_spcs()
|
|
272
|
+
|
|
273
|
+
# Create root span for tracking operations
|
|
274
|
+
if _create_root_span():
|
|
275
|
+
_root_span_otel_context = _get_current_otel_context()
|
|
276
|
+
return True
|
|
277
|
+
else:
|
|
278
|
+
return False
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
logger.warning(f"Telemetry initialization failed: {e}")
|
|
282
|
+
return False
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
# API Functions
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def otel_get_root_span_context():
|
|
289
|
+
"""Get the stored root span context for thread transfer"""
|
|
290
|
+
return _root_span_otel_context
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def otel_attach_context(otel_context):
|
|
294
|
+
"""Attach OpenTelemetry context and return token"""
|
|
295
|
+
if not OTEL_AVAILABLE or not otel_context:
|
|
296
|
+
return None
|
|
297
|
+
|
|
298
|
+
try:
|
|
299
|
+
token = context.attach(otel_context)
|
|
300
|
+
return token
|
|
301
|
+
except Exception as e:
|
|
302
|
+
logger.warning(f"Failed to attach context: {e}")
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def otel_detach_context(context_token):
|
|
307
|
+
"""Detach OpenTelemetry context using token"""
|
|
308
|
+
if not OTEL_AVAILABLE or not context_token:
|
|
309
|
+
return
|
|
310
|
+
|
|
311
|
+
try:
|
|
312
|
+
context.detach(context_token)
|
|
313
|
+
except Exception as e:
|
|
314
|
+
logger.warning(f"Failed to detach context: {e}")
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def otel_end_root_span(flush_timeout_millis: int = 5000):
|
|
318
|
+
"""
|
|
319
|
+
End the application span with final duration and status
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
flush_timeout_millis: Maximum time to wait for telemetry flush in milliseconds (default: 5000)
|
|
323
|
+
"""
|
|
324
|
+
if not is_telemetry_enabled():
|
|
325
|
+
return False
|
|
326
|
+
|
|
327
|
+
global _root_span_ended, _root_span_context_token
|
|
328
|
+
|
|
329
|
+
if _root_span_ended or _is_inherited_context:
|
|
330
|
+
# Don't end inherited spans - they're managed by Snowflake
|
|
331
|
+
return False
|
|
332
|
+
|
|
333
|
+
if _root_span:
|
|
334
|
+
try:
|
|
335
|
+
if _root_span.is_recording():
|
|
336
|
+
final_duration_ms = int(
|
|
337
|
+
(time.time() * 1000) - (_root_span.start_time / 1_000_000)
|
|
338
|
+
)
|
|
339
|
+
_root_span.set_attribute("execution.duration_ms", final_duration_ms)
|
|
340
|
+
_root_span.set_status(Status(StatusCode.OK))
|
|
341
|
+
|
|
342
|
+
_root_span.end()
|
|
343
|
+
_root_span_ended = True
|
|
344
|
+
|
|
345
|
+
# Detach the root context token that was attached during span creation
|
|
346
|
+
if _root_span_context_token is not None:
|
|
347
|
+
try:
|
|
348
|
+
context.detach(_root_span_context_token)
|
|
349
|
+
_root_span_context_token = None
|
|
350
|
+
except Exception as detach_error:
|
|
351
|
+
logger.warning(
|
|
352
|
+
f"Failed to detach root span context token: {detach_error}"
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
otel_flush_telemetry(timeout_millis=flush_timeout_millis)
|
|
356
|
+
|
|
357
|
+
return True
|
|
358
|
+
except Exception as e:
|
|
359
|
+
logger.warning(f"Failed to end root span: {e}")
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# Utility Functions
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def is_telemetry_enabled():
|
|
367
|
+
"""
|
|
368
|
+
Check if OpenTelemetry telemetry is both available (installed) and enabled.
|
|
369
|
+
|
|
370
|
+
This combines multiple checks:
|
|
371
|
+
1. OTEL_AVAILABLE: Whether OpenTelemetry packages are installed
|
|
372
|
+
2. Environment variable: Whether telemetry is enabled via SNOWPARK_TELEMETRY_ENABLED
|
|
373
|
+
3. Runtime environment: Must be running in SPCS or stored procedure/notebook
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
bool: True if telemetry can and should be used, False otherwise
|
|
377
|
+
"""
|
|
378
|
+
if not OTEL_AVAILABLE:
|
|
379
|
+
return False
|
|
380
|
+
|
|
381
|
+
# Check environment variable setting
|
|
382
|
+
env_value = os.getenv(TELEMETRY_ENV_VAR, str(DEFAULT_TELEMETRY_ENABLED)).lower()
|
|
383
|
+
if env_value not in ("true", "1", "yes", "on"):
|
|
384
|
+
return False
|
|
385
|
+
|
|
386
|
+
# Only enable telemetry in SPCS or stored procedure/notebook environments
|
|
387
|
+
from snowflake.snowpark_connect.utils.session import (
|
|
388
|
+
_is_running_in_SPCS,
|
|
389
|
+
_is_running_in_stored_procedure_or_notebook,
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
return _is_running_in_SPCS() or _is_running_in_stored_procedure_or_notebook()
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def otel_get_current_span():
|
|
396
|
+
"""
|
|
397
|
+
Get the current OpenTelemetry span if telemetry is enabled.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Span object if telemetry is enabled and a span is active, None otherwise
|
|
401
|
+
"""
|
|
402
|
+
if not is_telemetry_enabled():
|
|
403
|
+
return None
|
|
404
|
+
if trace is None:
|
|
405
|
+
return None
|
|
406
|
+
try:
|
|
407
|
+
return trace.get_current_span()
|
|
408
|
+
except Exception as e:
|
|
409
|
+
logger.warning(f"Failed to get current span: {e}")
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def otel_get_tracer(name: str):
|
|
414
|
+
"""
|
|
415
|
+
Get an OpenTelemetry tracer if telemetry is enabled.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
name: The tracer name
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
Tracer object if telemetry is enabled, None otherwise
|
|
422
|
+
"""
|
|
423
|
+
if not is_telemetry_enabled():
|
|
424
|
+
return None
|
|
425
|
+
if trace is None:
|
|
426
|
+
return None
|
|
427
|
+
try:
|
|
428
|
+
return trace.get_tracer(name)
|
|
429
|
+
except Exception as e:
|
|
430
|
+
logger.warning(f"Failed to get tracer: {e}")
|
|
431
|
+
return None
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def otel_start_span_as_current(tracer, span_name: str):
|
|
435
|
+
"""
|
|
436
|
+
Start a span as current context if telemetry is enabled.
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
tracer: The tracer object (can be None)
|
|
440
|
+
span_name: Name of the span
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
Context manager for the span, or None
|
|
444
|
+
"""
|
|
445
|
+
if not is_telemetry_enabled() or tracer is None:
|
|
446
|
+
return None
|
|
447
|
+
try:
|
|
448
|
+
return tracer.start_as_current_span(span_name)
|
|
449
|
+
except Exception as e:
|
|
450
|
+
logger.warning(f"Failed to start span: {e}")
|
|
451
|
+
return None
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def otel_create_status(status_code, description: str = ""):
|
|
455
|
+
"""
|
|
456
|
+
Create an OpenTelemetry Status object if telemetry is enabled.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
status_code: StatusCode enum value
|
|
460
|
+
description: Optional description string
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Status object if telemetry is enabled, None otherwise
|
|
464
|
+
"""
|
|
465
|
+
if not is_telemetry_enabled():
|
|
466
|
+
return None
|
|
467
|
+
if trace is None:
|
|
468
|
+
return None
|
|
469
|
+
try:
|
|
470
|
+
from opentelemetry.trace import Status
|
|
471
|
+
|
|
472
|
+
return Status(status_code, description)
|
|
473
|
+
except Exception as e:
|
|
474
|
+
logger.warning(f"Failed to create status: {e}")
|
|
475
|
+
return None
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def otel_get_status_code():
|
|
479
|
+
"""
|
|
480
|
+
Get the StatusCode enum if telemetry is enabled.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
StatusCode enum if available, None otherwise
|
|
484
|
+
"""
|
|
485
|
+
if not is_telemetry_enabled():
|
|
486
|
+
return None
|
|
487
|
+
try:
|
|
488
|
+
from opentelemetry.trace import StatusCode
|
|
489
|
+
|
|
490
|
+
return StatusCode
|
|
491
|
+
except Exception as e:
|
|
492
|
+
logger.warning(f"Failed to get StatusCode: {e}")
|
|
493
|
+
return None
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def otel_flush_telemetry(timeout_millis: int = 5000):
|
|
497
|
+
"""
|
|
498
|
+
Force flush telemetry traces and metrics to ensure export
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
timeout_millis: Maximum time to wait for flush in milliseconds (default: 5000)
|
|
502
|
+
"""
|
|
503
|
+
if not is_telemetry_enabled():
|
|
504
|
+
return
|
|
505
|
+
|
|
506
|
+
if _tracer_provider:
|
|
507
|
+
try:
|
|
508
|
+
_tracer_provider.force_flush(timeout_millis=timeout_millis)
|
|
509
|
+
except Exception as e:
|
|
510
|
+
logger.warning(f"Failed to force flush traces: {e}")
|
|
511
|
+
|
|
512
|
+
if _meter_provider:
|
|
513
|
+
try:
|
|
514
|
+
_meter_provider.force_flush(timeout_millis=timeout_millis)
|
|
515
|
+
except Exception as e:
|
|
516
|
+
logger.warning(f"Failed to force flush metrics: {e}")
|
|
@@ -13,6 +13,10 @@ import snowflake.snowpark.functions as snowpark_fn
|
|
|
13
13
|
from snowflake import snowpark
|
|
14
14
|
from snowflake.snowpark.types import IntegerType, PandasDataFrameType, StructType
|
|
15
15
|
|
|
16
|
+
# Removed error imports to avoid UDF serialization issues
|
|
17
|
+
# from snowflake.snowpark_connect.error.error_codes import ErrorCodes
|
|
18
|
+
# from snowflake.snowpark_connect.error.error_utils import attach_custom_error_code
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
def get_map_in_arrow_udtf(
|
|
18
22
|
user_function: Callable,
|
|
@@ -60,14 +64,14 @@ def get_map_in_arrow_udtf(
|
|
|
60
64
|
result_iterator, "__iter__"
|
|
61
65
|
):
|
|
62
66
|
raise RuntimeError(
|
|
63
|
-
f"snowpark_connect::
|
|
67
|
+
f"[snowpark_connect::type_mismatch] Return type of the user-defined function should be "
|
|
64
68
|
f"iterator of pyarrow.RecordBatch, but is {type(result_iterator).__name__}"
|
|
65
69
|
)
|
|
66
70
|
|
|
67
71
|
for batch in result_iterator:
|
|
68
72
|
if not isinstance(batch, pa.RecordBatch):
|
|
69
73
|
raise RuntimeError(
|
|
70
|
-
f"snowpark_connect::
|
|
74
|
+
f"[snowpark_connect::type_mismatch] Return type of the user-defined function should "
|
|
71
75
|
f"be iterator of pyarrow.RecordBatch, but is iterator of {type(batch).__name__}"
|
|
72
76
|
)
|
|
73
77
|
if batch.num_rows > 0:
|
|
@@ -121,7 +125,7 @@ def create_pandas_udtf(
|
|
|
121
125
|
result_iterator, "__iter__"
|
|
122
126
|
):
|
|
123
127
|
raise RuntimeError(
|
|
124
|
-
f"snowpark_connect::
|
|
128
|
+
f"[snowpark_connect::type_mismatch] Return type of the user-defined function should be "
|
|
125
129
|
f"iterator of pandas.DataFrame, but is {type(result_iterator).__name__}"
|
|
126
130
|
)
|
|
127
131
|
|
|
@@ -140,7 +144,7 @@ def create_pandas_udtf(
|
|
|
140
144
|
if column not in self.output_column_original_names
|
|
141
145
|
]
|
|
142
146
|
raise RuntimeError(
|
|
143
|
-
f"[RESULT_COLUMNS_MISMATCH_FOR_PANDAS_UDF] Column names of the returned pandas.DataFrame do not match specified schema. Missing: {', '.join(sorted(missing_columns))}. Unexpected: {', '.join(sorted(unexpected_columns))}"
|
|
147
|
+
f"[snowpark_connect::invalid_operation] [RESULT_COLUMNS_MISMATCH_FOR_PANDAS_UDF] Column names of the returned pandas.DataFrame do not match specified schema. Missing: {', '.join(sorted(missing_columns))}. Unexpected: {', '.join(sorted(unexpected_columns))}"
|
|
144
148
|
"."
|
|
145
149
|
)
|
|
146
150
|
reordered_df = output_df[self.output_column_original_names]
|