Skip to content

SNOW-2012666: Implemented lazy import for pandas in the connector #2351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
- Bumped numpy dependency from <2.1.0 to <=2.2.4
- Added Windows support for Python 3.13.
- Add `bulk_upload_chunks` parameter to `write_pandas` function. Setting this parameter to True changes the behaviour of write_pandas function to first write all the data chunks to the local disk and then perform the wildcard upload of the chunks folder to the stage. In default behaviour the chunks are being saved, uploaded and deleted one by one.
- Implemented lazy import for pandas to improve loading performance.


- v3.15.1(May 20, 2025)
Expand Down
35 changes: 32 additions & 3 deletions src/snowflake/connector/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

from packaging.requirements import Requirement

from . import errors

logger = getLogger(__name__)

"""This module helps to manage optional dependencies.
Expand All @@ -33,6 +31,8 @@ class MissingOptionalDependency:
_dep_name = "not set"

def __getattr__(self, item):
from . import errors

raise errors.MissingDependencyError(self._dep_name)


Expand Down Expand Up @@ -114,6 +114,15 @@ def _import_or_missing_pandas_option() -> (
return MissingPandas(), MissingPandas(), False


def installed_pandas() -> bool:
"""This function checks if pandas is available and compatible."""
try:
importlib.import_module("pandas")
return True
except ImportError:
return False


def _import_or_missing_keyring_option() -> tuple[ModuleLikeObject, bool]:
"""This function tries importing the following packages: keyring.

Expand All @@ -127,5 +136,25 @@ def _import_or_missing_keyring_option() -> tuple[ModuleLikeObject, bool]:


# Create actual constants to be imported from this file
pandas, pyarrow, installed_pandas = _import_or_missing_pandas_option()
keyring, installed_keyring = _import_or_missing_keyring_option()


def __getattr__(name):
if name == "pandas":
try:
return importlib.import_module("pandas")
except ImportError:
return MissingPandas()

elif name == "pyarrow":
try:
return importlib.import_module("pyarrow")
except ImportError:
from . import errors

raise errors.MissingDependencyError("pyarrow")

elif name == "installed_pandas":
return installed_pandas()

raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
3 changes: 2 additions & 1 deletion src/snowflake/connector/result_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

from .constants import IterUnit
from .errors import NotSupportedError
from .options import pandas
from .options import pyarrow as pa
from .result_batch import (
ArrowResultBatch,
Expand Down Expand Up @@ -215,6 +214,8 @@ def _fetch_pandas_batches(self, **kwargs) -> Iterator[DataFrame]:

def _fetch_pandas_all(self, **kwargs) -> DataFrame:
"""Fetches a single Pandas dataframe."""
from .options import pandas

concat_args = list(inspect.signature(pandas.concat).parameters)
concat_kwargs = {k: kwargs.pop(k) for k in dict(kwargs) if k in concat_args}
dataframes = list(self._fetch_pandas_batches(is_fetch_all=True, **kwargs))
Expand Down
Loading