Skip to content

Commit

Permalink
lazily import pandas
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoGorelli committed Apr 28, 2024
1 parent c7ee605 commit 91c7b27
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 14 deletions.
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ dependencies = [
"contourpy >=1.2",
"numpy >=1.16",
"packaging >=16.8",
"pandas >=1.2",
"pillow >=7.1.0",
"PyYAML >=3.10",
"tornado >=6.2",
Expand Down
7 changes: 4 additions & 3 deletions src/bokeh/models/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
String,
)
from ..model import Model
from ..util.dependencies import get_pandas
from ..util.deprecation import deprecated
from ..util.serialization import convert_datetime_array
from ..util.warnings import BokehUserWarning, warn
Expand Down Expand Up @@ -232,11 +233,11 @@ def __init__(self, *args: TAny, **kwargs: TAny) -> None:
# TODO (bev) invalid to pass args and "data", check and raise exception
raw_data: DataDict = kwargs.pop("data", {})

import pandas as pd
pd = get_pandas()
if not isinstance(raw_data, dict):
if isinstance(raw_data, pd.DataFrame):
if pd is not None and isinstance(raw_data, pd.DataFrame):
raw_data = self._data_from_df(raw_data)
elif isinstance(raw_data, pd.core.groupby.GroupBy):
elif pd is not None and isinstance(raw_data, pd.core.groupby.GroupBy):
raw_data = self._data_from_groupby(raw_data)
else:
raise ValueError(f"expected a dict or pandas.DataFrame, got {raw_data}")
Expand Down
9 changes: 5 additions & 4 deletions src/bokeh/plotting/_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
Range1d,
Scale,
)
from ..util.dependencies import get_pandas

if TYPE_CHECKING:
import pandas as pd
Expand Down Expand Up @@ -77,16 +78,16 @@
#-----------------------------------------------------------------------------

def get_range(range_input: Range | tuple[float, float] | Sequence[str] | pd.Series[Any] | GroupBy | None) -> Range:
import pandas as pd
from pandas.core.groupby import GroupBy

pandas = get_pandas()

if range_input is None:
return DataRange1d()
if isinstance(range_input, GroupBy):
if pandas is not None and isinstance(range_input, pandas.core.groupby.GroupBy):
return FactorRange(factors=sorted(list(range_input.groups.keys())))
if isinstance(range_input, Range):
return range_input
if isinstance(range_input, pd.Series):
if pandas is not None and isinstance(range_input, pandas.Series):
range_input = range_input.values
if isinstance(range_input, (Sequence, np.ndarray)):
if all(isinstance(x, str) for x in range_input):
Expand Down
16 changes: 15 additions & 1 deletion src/bokeh/util/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
# Standard library imports
from importlib import import_module
from types import ModuleType
from typing import Any
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
import pandas as pd

#-----------------------------------------------------------------------------
# Globals and constants
Expand All @@ -32,6 +35,7 @@
__all__ = (
'import_optional',
'import_required',
'get_pandas',
'uses_pandas',
)

Expand Down Expand Up @@ -92,6 +96,16 @@ def uses_pandas(obj: Any) -> bool:
module = type(obj).__module__
return module is not None and module.startswith("pandas.")

def get_pandas() -> pd | None:
"""
Import pandas (if available, else return None).
"""
try:
import pandas
except ImportError:
return None
return pandas

#-----------------------------------------------------------------------------
# Dev API
#-----------------------------------------------------------------------------
Expand Down
12 changes: 7 additions & 5 deletions src/bokeh/util/serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
# Bokeh imports
from ..core.types import ID
from ..settings import settings
from ..util.dependencies import get_pandas
from .strings import format_docstring

if TYPE_CHECKING:
Expand All @@ -52,13 +53,14 @@

@lru_cache(None)
def _compute_datetime_types() -> set[type]:
import pandas as pd
pd = get_pandas()

result = {dt.time, dt.datetime, np.datetime64}
result.add(pd.Timestamp)
result.add(pd.Timedelta)
result.add(pd.Period)
result.add(type(pd.NaT))
if pd is not None:
result.add(pd.Timestamp)
result.add(pd.Timedelta)
result.add(pd.Period)
result.add(type(pd.NaT))
return result

def __getattr__(name: str) -> Any:
Expand Down

0 comments on commit 91c7b27

Please sign in to comment.