snowpark-connect 0.27.0__py3-none-any.whl → 1.6.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.
Files changed (192) hide show
  1. snowflake/snowpark_connect/__init__.py +1 -0
  2. snowflake/snowpark_connect/analyze_plan/map_tree_string.py +8 -4
  3. snowflake/snowpark_connect/client/__init__.py +15 -0
  4. snowflake/snowpark_connect/client/error_utils.py +30 -0
  5. snowflake/snowpark_connect/client/exceptions.py +36 -0
  6. snowflake/snowpark_connect/client/query_results.py +90 -0
  7. snowflake/snowpark_connect/client/server.py +680 -0
  8. snowflake/snowpark_connect/client/utils/__init__.py +10 -0
  9. snowflake/snowpark_connect/client/utils/session.py +85 -0
  10. snowflake/snowpark_connect/column_name_handler.py +404 -243
  11. snowflake/snowpark_connect/column_qualifier.py +43 -0
  12. snowflake/snowpark_connect/config.py +237 -23
  13. snowflake/snowpark_connect/constants.py +2 -0
  14. snowflake/snowpark_connect/dataframe_container.py +102 -8
  15. snowflake/snowpark_connect/date_time_format_mapping.py +71 -13
  16. snowflake/snowpark_connect/error/error_codes.py +50 -0
  17. snowflake/snowpark_connect/error/error_utils.py +172 -23
  18. snowflake/snowpark_connect/error/exceptions.py +13 -4
  19. snowflake/snowpark_connect/execute_plan/map_execution_command.py +15 -160
  20. snowflake/snowpark_connect/execute_plan/map_execution_root.py +26 -20
  21. snowflake/snowpark_connect/execute_plan/utils.py +5 -1
  22. snowflake/snowpark_connect/expression/function_defaults.py +9 -2
  23. snowflake/snowpark_connect/expression/hybrid_column_map.py +53 -5
  24. snowflake/snowpark_connect/expression/literal.py +37 -13
  25. snowflake/snowpark_connect/expression/map_cast.py +123 -5
  26. snowflake/snowpark_connect/expression/map_expression.py +80 -27
  27. snowflake/snowpark_connect/expression/map_extension.py +322 -12
  28. snowflake/snowpark_connect/expression/map_sql_expression.py +316 -81
  29. snowflake/snowpark_connect/expression/map_udf.py +85 -20
  30. snowflake/snowpark_connect/expression/map_unresolved_attribute.py +451 -173
  31. snowflake/snowpark_connect/expression/map_unresolved_function.py +2748 -746
  32. snowflake/snowpark_connect/expression/map_unresolved_star.py +87 -23
  33. snowflake/snowpark_connect/expression/map_update_fields.py +70 -18
  34. snowflake/snowpark_connect/expression/map_window_function.py +18 -3
  35. snowflake/snowpark_connect/includes/jars/{scala-library-2.12.18.jar → sas-scala-udf_2.12-0.2.0.jar} +0 -0
  36. snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/foreach_batch_worker.py +1 -1
  37. snowflake/snowpark_connect/includes/python/pyspark/sql/connect/streaming/worker/listener_worker.py +1 -1
  38. snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.py +12 -10
  39. snowflake/snowpark_connect/proto/snowflake_expression_ext_pb2.pyi +14 -2
  40. snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.py +10 -8
  41. snowflake/snowpark_connect/proto/snowflake_relation_ext_pb2.pyi +13 -6
  42. snowflake/snowpark_connect/relation/catalogs/abstract_spark_catalog.py +65 -17
  43. snowflake/snowpark_connect/relation/catalogs/snowflake_catalog.py +297 -49
  44. snowflake/snowpark_connect/relation/catalogs/utils.py +12 -4
  45. snowflake/snowpark_connect/relation/io_utils.py +110 -10
  46. snowflake/snowpark_connect/relation/map_aggregate.py +196 -255
  47. snowflake/snowpark_connect/relation/map_catalog.py +5 -1
  48. snowflake/snowpark_connect/relation/map_column_ops.py +264 -96
  49. snowflake/snowpark_connect/relation/map_extension.py +263 -29
  50. snowflake/snowpark_connect/relation/map_join.py +683 -442
  51. snowflake/snowpark_connect/relation/map_local_relation.py +28 -1
  52. snowflake/snowpark_connect/relation/map_map_partitions.py +83 -8
  53. snowflake/snowpark_connect/relation/map_relation.py +48 -19
  54. snowflake/snowpark_connect/relation/map_row_ops.py +310 -91
  55. snowflake/snowpark_connect/relation/map_show_string.py +13 -6
  56. snowflake/snowpark_connect/relation/map_sql.py +1233 -222
  57. snowflake/snowpark_connect/relation/map_stats.py +48 -9
  58. snowflake/snowpark_connect/relation/map_subquery_alias.py +11 -2
  59. snowflake/snowpark_connect/relation/map_udtf.py +14 -4
  60. snowflake/snowpark_connect/relation/read/jdbc_read_dbapi.py +53 -14
  61. snowflake/snowpark_connect/relation/read/map_read.py +134 -43
  62. snowflake/snowpark_connect/relation/read/map_read_csv.py +255 -45
  63. snowflake/snowpark_connect/relation/read/map_read_jdbc.py +17 -5
  64. snowflake/snowpark_connect/relation/read/map_read_json.py +320 -85
  65. snowflake/snowpark_connect/relation/read/map_read_parquet.py +142 -27
  66. snowflake/snowpark_connect/relation/read/map_read_partitioned_parquet.py +142 -0
  67. snowflake/snowpark_connect/relation/read/map_read_socket.py +11 -3
  68. snowflake/snowpark_connect/relation/read/map_read_table.py +82 -5
  69. snowflake/snowpark_connect/relation/read/map_read_text.py +18 -3
  70. snowflake/snowpark_connect/relation/read/metadata_utils.py +170 -0
  71. snowflake/snowpark_connect/relation/read/reader_config.py +36 -3
  72. snowflake/snowpark_connect/relation/read/utils.py +50 -5
  73. snowflake/snowpark_connect/relation/stage_locator.py +91 -55
  74. snowflake/snowpark_connect/relation/utils.py +128 -5
  75. snowflake/snowpark_connect/relation/write/jdbc_write_dbapi.py +19 -3
  76. snowflake/snowpark_connect/relation/write/map_write.py +929 -319
  77. snowflake/snowpark_connect/relation/write/map_write_jdbc.py +8 -2
  78. snowflake/snowpark_connect/resources/java_udfs-1.0-SNAPSHOT.jar +0 -0
  79. snowflake/snowpark_connect/resources_initializer.py +110 -48
  80. snowflake/snowpark_connect/server.py +546 -456
  81. snowflake/snowpark_connect/server_common/__init__.py +500 -0
  82. snowflake/snowpark_connect/snowflake_session.py +65 -0
  83. snowflake/snowpark_connect/start_server.py +53 -5
  84. snowflake/snowpark_connect/type_mapping.py +349 -27
  85. snowflake/snowpark_connect/typed_column.py +9 -7
  86. snowflake/snowpark_connect/utils/artifacts.py +9 -8
  87. snowflake/snowpark_connect/utils/cache.py +49 -27
  88. snowflake/snowpark_connect/utils/concurrent.py +36 -1
  89. snowflake/snowpark_connect/utils/context.py +187 -37
  90. snowflake/snowpark_connect/utils/describe_query_cache.py +68 -53
  91. snowflake/snowpark_connect/utils/env_utils.py +5 -1
  92. snowflake/snowpark_connect/utils/expression_transformer.py +172 -0
  93. snowflake/snowpark_connect/utils/identifiers.py +137 -3
  94. snowflake/snowpark_connect/utils/io_utils.py +57 -1
  95. snowflake/snowpark_connect/utils/java_stored_procedure.py +125 -0
  96. snowflake/snowpark_connect/utils/java_udaf_utils.py +303 -0
  97. snowflake/snowpark_connect/utils/java_udtf_utils.py +239 -0
  98. snowflake/snowpark_connect/utils/jvm_udf_utils.py +248 -0
  99. snowflake/snowpark_connect/utils/open_telemetry.py +516 -0
  100. snowflake/snowpark_connect/utils/pandas_udtf_utils.py +8 -4
  101. snowflake/snowpark_connect/utils/patch_spark_line_number.py +181 -0
  102. snowflake/snowpark_connect/utils/profiling.py +25 -8
  103. snowflake/snowpark_connect/utils/scala_udf_utils.py +101 -332
  104. snowflake/snowpark_connect/utils/sequence.py +21 -0
  105. snowflake/snowpark_connect/utils/session.py +64 -28
  106. snowflake/snowpark_connect/utils/snowpark_connect_logging.py +51 -9
  107. snowflake/snowpark_connect/utils/spcs_logger.py +290 -0
  108. snowflake/snowpark_connect/utils/telemetry.py +163 -22
  109. snowflake/snowpark_connect/utils/temporary_view_cache.py +67 -0
  110. snowflake/snowpark_connect/utils/temporary_view_helper.py +334 -0
  111. snowflake/snowpark_connect/utils/udf_cache.py +117 -41
  112. snowflake/snowpark_connect/utils/udf_helper.py +39 -37
  113. snowflake/snowpark_connect/utils/udf_utils.py +133 -14
  114. snowflake/snowpark_connect/utils/udtf_helper.py +8 -1
  115. snowflake/snowpark_connect/utils/udtf_utils.py +46 -31
  116. snowflake/snowpark_connect/utils/upload_java_jar.py +57 -0
  117. snowflake/snowpark_connect/version.py +1 -1
  118. snowflake/snowpark_decoder/dp_session.py +6 -2
  119. snowflake/snowpark_decoder/spark_decoder.py +12 -0
  120. {snowpark_connect-0.27.0.data → snowpark_connect-1.6.0.data}/scripts/snowpark-submit +2 -2
  121. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.6.0.dist-info}/METADATA +14 -7
  122. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.6.0.dist-info}/RECORD +129 -167
  123. snowflake/snowpark_connect/hidden_column.py +0 -39
  124. snowflake/snowpark_connect/includes/jars/antlr4-runtime-4.9.3.jar +0 -0
  125. snowflake/snowpark_connect/includes/jars/commons-cli-1.5.0.jar +0 -0
  126. snowflake/snowpark_connect/includes/jars/commons-codec-1.16.1.jar +0 -0
  127. snowflake/snowpark_connect/includes/jars/commons-collections-3.2.2.jar +0 -0
  128. snowflake/snowpark_connect/includes/jars/commons-collections4-4.4.jar +0 -0
  129. snowflake/snowpark_connect/includes/jars/commons-compiler-3.1.9.jar +0 -0
  130. snowflake/snowpark_connect/includes/jars/commons-compress-1.26.0.jar +0 -0
  131. snowflake/snowpark_connect/includes/jars/commons-crypto-1.1.0.jar +0 -0
  132. snowflake/snowpark_connect/includes/jars/commons-dbcp-1.4.jar +0 -0
  133. snowflake/snowpark_connect/includes/jars/commons-io-2.16.1.jar +0 -0
  134. snowflake/snowpark_connect/includes/jars/commons-lang-2.6.jar +0 -0
  135. snowflake/snowpark_connect/includes/jars/commons-lang3-3.12.0.jar +0 -0
  136. snowflake/snowpark_connect/includes/jars/commons-logging-1.1.3.jar +0 -0
  137. snowflake/snowpark_connect/includes/jars/commons-math3-3.6.1.jar +0 -0
  138. snowflake/snowpark_connect/includes/jars/commons-pool-1.5.4.jar +0 -0
  139. snowflake/snowpark_connect/includes/jars/commons-text-1.10.0.jar +0 -0
  140. snowflake/snowpark_connect/includes/jars/hadoop-client-api-trimmed-3.3.4.jar +0 -0
  141. snowflake/snowpark_connect/includes/jars/jackson-annotations-2.15.2.jar +0 -0
  142. snowflake/snowpark_connect/includes/jars/jackson-core-2.15.2.jar +0 -0
  143. snowflake/snowpark_connect/includes/jars/jackson-core-asl-1.9.13.jar +0 -0
  144. snowflake/snowpark_connect/includes/jars/jackson-databind-2.15.2.jar +0 -0
  145. snowflake/snowpark_connect/includes/jars/jackson-dataformat-yaml-2.15.2.jar +0 -0
  146. snowflake/snowpark_connect/includes/jars/jackson-datatype-jsr310-2.15.2.jar +0 -0
  147. snowflake/snowpark_connect/includes/jars/jackson-module-scala_2.12-2.15.2.jar +0 -0
  148. snowflake/snowpark_connect/includes/jars/json4s-ast_2.12-3.7.0-M11.jar +0 -0
  149. snowflake/snowpark_connect/includes/jars/json4s-core_2.12-3.7.0-M11.jar +0 -0
  150. snowflake/snowpark_connect/includes/jars/json4s-jackson_2.12-3.7.0-M11.jar +0 -0
  151. snowflake/snowpark_connect/includes/jars/json4s-native_2.12-3.7.0-M11.jar +0 -0
  152. snowflake/snowpark_connect/includes/jars/json4s-scalap_2.12-3.7.0-M11.jar +0 -0
  153. snowflake/snowpark_connect/includes/jars/kryo-shaded-4.0.2.jar +0 -0
  154. snowflake/snowpark_connect/includes/jars/log4j-1.2-api-2.20.0.jar +0 -0
  155. snowflake/snowpark_connect/includes/jars/log4j-api-2.20.0.jar +0 -0
  156. snowflake/snowpark_connect/includes/jars/log4j-core-2.20.0.jar +0 -0
  157. snowflake/snowpark_connect/includes/jars/log4j-slf4j2-impl-2.20.0.jar +0 -0
  158. snowflake/snowpark_connect/includes/jars/paranamer-2.8.3.jar +0 -0
  159. snowflake/snowpark_connect/includes/jars/paranamer-2.8.jar +0 -0
  160. snowflake/snowpark_connect/includes/jars/sas-scala-udf_2.12-0.1.0.jar +0 -0
  161. snowflake/snowpark_connect/includes/jars/scala-collection-compat_2.12-2.7.0.jar +0 -0
  162. snowflake/snowpark_connect/includes/jars/scala-parser-combinators_2.12-2.3.0.jar +0 -0
  163. snowflake/snowpark_connect/includes/jars/scala-reflect-2.12.18.jar +0 -0
  164. snowflake/snowpark_connect/includes/jars/scala-xml_2.12-2.1.0.jar +0 -0
  165. snowflake/snowpark_connect/includes/jars/slf4j-api-2.0.7.jar +0 -0
  166. snowflake/snowpark_connect/includes/jars/spark-catalyst_2.12-3.5.6.jar +0 -0
  167. snowflake/snowpark_connect/includes/jars/spark-common-utils_2.12-3.5.6.jar +0 -0
  168. snowflake/snowpark_connect/includes/jars/spark-connect-client-jvm_2.12-3.5.6.jar +0 -0
  169. snowflake/snowpark_connect/includes/jars/spark-core_2.12-3.5.6.jar +0 -0
  170. snowflake/snowpark_connect/includes/jars/spark-graphx_2.12-3.5.6.jar +0 -0
  171. snowflake/snowpark_connect/includes/jars/spark-hive-thriftserver_2.12-3.5.6.jar +0 -0
  172. snowflake/snowpark_connect/includes/jars/spark-hive_2.12-3.5.6.jar +0 -0
  173. snowflake/snowpark_connect/includes/jars/spark-kvstore_2.12-3.5.6.jar +0 -0
  174. snowflake/snowpark_connect/includes/jars/spark-launcher_2.12-3.5.6.jar +0 -0
  175. snowflake/snowpark_connect/includes/jars/spark-mesos_2.12-3.5.6.jar +0 -0
  176. snowflake/snowpark_connect/includes/jars/spark-mllib-local_2.12-3.5.6.jar +0 -0
  177. snowflake/snowpark_connect/includes/jars/spark-network-common_2.12-3.5.6.jar +0 -0
  178. snowflake/snowpark_connect/includes/jars/spark-network-shuffle_2.12-3.5.6.jar +0 -0
  179. snowflake/snowpark_connect/includes/jars/spark-repl_2.12-3.5.6.jar +0 -0
  180. snowflake/snowpark_connect/includes/jars/spark-sketch_2.12-3.5.6.jar +0 -0
  181. snowflake/snowpark_connect/includes/jars/spark-sql-api_2.12-3.5.6.jar +0 -0
  182. snowflake/snowpark_connect/includes/jars/spark-sql_2.12-3.5.6.jar +0 -0
  183. snowflake/snowpark_connect/includes/jars/spark-tags_2.12-3.5.6.jar +0 -0
  184. snowflake/snowpark_connect/includes/jars/spark-unsafe_2.12-3.5.6.jar +0 -0
  185. snowflake/snowpark_connect/includes/jars/spark-yarn_2.12-3.5.6.jar +0 -0
  186. {snowpark_connect-0.27.0.data → snowpark_connect-1.6.0.data}/scripts/snowpark-connect +0 -0
  187. {snowpark_connect-0.27.0.data → snowpark_connect-1.6.0.data}/scripts/snowpark-session +0 -0
  188. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.6.0.dist-info}/WHEEL +0 -0
  189. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.6.0.dist-info}/licenses/LICENSE-binary +0 -0
  190. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.6.0.dist-info}/licenses/LICENSE.txt +0 -0
  191. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.6.0.dist-info}/licenses/NOTICE-binary +0 -0
  192. {snowpark_connect-0.27.0.dist-info → snowpark_connect-1.6.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::UDF_RETURN_TYPE Return type of the user-defined function should be "
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::UDF_RETURN_TYPE Return type of the user-defined function should "
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::UDF_RETURN_TYPE Return type of the user-defined function should be "
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]