3434from snowflake .snowpark ._internal .data_source import DataSourceReader
3535from snowflake .snowpark ._internal .type_utils import convert_sp_to_sf_type
3636from snowflake .snowpark ._internal .utils import get_temp_type_for_object
37- from snowflake .snowpark .exceptions import SnowparkDataframeReaderException
37+ from snowflake .snowpark .context import _PYPI_SHARED_REPOSITORY
38+ from snowflake .snowpark .exceptions import (
39+ SnowparkClientException ,
40+ SnowparkDataframeReaderException ,
41+ )
3842from snowflake .snowpark .types import StructType
3943
4044from typing import TYPE_CHECKING
@@ -98,7 +102,11 @@ class DRIVER_TYPE(str, Enum):
98102 DRIVER_TYPE .PYMYSQL : PymysqlDriver ,
99103}
100104
101- UDTF_PACKAGE_MAP = {
105+ # Default UDTF package list, suitable for Snowflake's Anaconda shared
106+ # repository. The Snowflake Anaconda channel ships conda builds of these
107+ # packages with the necessary native libraries (e.g., libpq for psycopg2,
108+ # msodbcsql for pyodbc) bundled, so the source distribution names work.
109+ _ANACONDA_UDTF_PACKAGE_MAP = {
102110 DBMS_TYPE .ORACLE_DB : ["oracledb>=2.0.0,<4.0.0" , "snowflake-snowpark-python" ],
103111 DBMS_TYPE .SQLITE_DB : ["snowflake-snowpark-python" ],
104112 DBMS_TYPE .SQL_SERVER_DB : [
@@ -114,6 +122,67 @@ class DRIVER_TYPE(str, Enum):
114122 DBMS_TYPE .MYSQL_DB : ["pymysql>=1.0.0,<2.0.0" , "snowflake-snowpark-python" ],
115123}
116124
125+ # UDTF package list when using the PyPI shared repository. The server-side
126+ # UDTF install sandbox refuses to compile source distributions (sdists), so
127+ # every package here must be wheel-installable from PyPI. Differences from
128+ # the Anaconda map:
129+ # - Postgres uses ``psycopg2-binary`` because ``psycopg2`` on PyPI is
130+ # sdist-only; ``psycopg2-binary`` is the wheel-packaged equivalent.
131+ # - SQL Server has no PyPI-installable equivalent of ``msodbcsql`` (it is
132+ # Microsoft's ODBC driver, distributed as a system package), so the
133+ # UDTF path cannot work on PyPI today.
134+ # - Databricks depends on ``databricks-sql-connector``, which transitively
135+ # requires ``thrift``; ``thrift`` on PyPI is sdist-only for every
136+ # version, so the server cannot install it.
137+ # These PyPI gaps are independent of the Python version; they apply to any
138+ # session whose default artifact repository is PyPI (most commonly Python
139+ # 3.14+, where PyPI is the global default).
140+ _PYPI_UDTF_PACKAGE_MAP = {
141+ DBMS_TYPE .ORACLE_DB : ["oracledb>=2.0.0,<4.0.0" , "snowflake-snowpark-python" ],
142+ DBMS_TYPE .SQLITE_DB : ["snowflake-snowpark-python" ],
143+ DBMS_TYPE .POSTGRES_DB : [
144+ "psycopg2-binary>=2.0.0,<3.0.0" ,
145+ "snowflake-snowpark-python" ,
146+ ],
147+ DBMS_TYPE .MYSQL_DB : ["pymysql>=1.0.0,<2.0.0" , "snowflake-snowpark-python" ],
148+ # SQL_SERVER_DB and DATABRICKS_DB intentionally omitted - see
149+ # resolve_udtf_packages for the user-facing error.
150+ }
151+
152+ # Backwards-compatible alias for callers (and external code) that imported
153+ # the old map. New code should use :func:`resolve_udtf_packages`.
154+ UDTF_PACKAGE_MAP = _ANACONDA_UDTF_PACKAGE_MAP
155+
156+
157+ def resolve_udtf_packages (
158+ dbms_type : "DBMS_TYPE" , artifact_repository : Optional [str ]
159+ ) -> List [str ]:
160+ """Return the default UDTF package list for ``dbms_type``.
161+
162+ Picks the package list appropriate for ``artifact_repository``. When the
163+ repository is the PyPI shared repository, some DBMSes have no working
164+ package set (their dependencies are not wheel-installable from PyPI, and
165+ the server-side UDTF sandbox refuses to compile sdists); this raises
166+ :class:`SnowparkClientException` with guidance to switch repositories.
167+ """
168+ if artifact_repository == _PYPI_SHARED_REPOSITORY :
169+ packages = _PYPI_UDTF_PACKAGE_MAP .get (dbms_type )
170+ if packages is None :
171+ raise SnowparkClientException (
172+ f"DataFrameReader.dbapi server-side UDTF ingestion for "
173+ f"{ dbms_type .value } is not supported when the session's "
174+ f"default artifact repository is PyPI: the required "
175+ f"packages are not wheel-installable from PyPI, and the "
176+ f"server-side UDTF install sandbox refuses to compile "
177+ f"source distributions. Switch to the Anaconda artifact "
178+ f"repository (on Python 3.14+, PyPI is the client-side "
179+ f"default; on older Python versions, Anaconda is the "
180+ f"default but may have been overridden at the account, "
181+ f"database, or schema level)."
182+ )
183+ return packages
184+ return _ANACONDA_UDTF_PACKAGE_MAP .get (dbms_type )
185+
117186
118187def get_jdbc_dbms (jdbc_url : str ) -> str :
119188 """
0 commit comments