|
24 | 24 |
|
25 | 25 | import abc |
26 | 26 | import collections |
| 27 | +import importlib |
27 | 28 | import logging |
| 29 | +from functools import cache |
28 | 30 | from typing import TYPE_CHECKING, Any |
29 | 31 |
|
30 | 32 | if TYPE_CHECKING: |
|
46 | 48 | logger = logging.getLogger(__name__) |
47 | 49 |
|
48 | 50 |
|
49 | | -def _get_pandas(): |
50 | | - import pandas as pd |
51 | | - |
52 | | - return pd |
53 | | - |
54 | | - |
55 | | -def _get_pandas_extension(): |
56 | | - from pandas.core.indexes import extension as pd_extension |
57 | | - |
58 | | - return pd_extension |
59 | | - |
60 | | - |
61 | | -def _get_numpy(): |
62 | | - import numpy as np |
63 | | - |
64 | | - return np |
| 51 | +@cache |
| 52 | +def _lazy_import(module: str, attr: str | None = None) -> Any: |
| 53 | + """Lazy import a module. Cached after 1st call.""" |
| 54 | + mod = importlib.import_module(module) |
| 55 | + return mod if attr is None else getattr(mod, attr) |
65 | 56 |
|
66 | 57 |
|
67 | 58 | class ResultMixin(lifecycle_api.LegacyResultMixin): |
@@ -148,8 +139,8 @@ def pandas_index_types( |
148 | 139 | all_index_types = collections.defaultdict(list) |
149 | 140 | time_indexes = collections.defaultdict(list) |
150 | 141 | no_indexes = collections.defaultdict(list) |
151 | | - pd = _get_pandas() |
152 | | - pd_extension = _get_pandas_extension() |
| 142 | + pd = _lazy_import("pandas") |
| 143 | + pd_extension = _lazy_import("pandas.core.indexes.extension") |
153 | 144 |
|
154 | 145 | def index_key_name(pd_object: pd.DataFrame | pd.Series) -> str: |
155 | 146 | """Creates a string helping identify the index and it's type. |
@@ -248,7 +239,7 @@ def build_result(**outputs: dict[str, Any]) -> pd.DataFrame: |
248 | 239 | :param outputs: the outputs to build a dataframe from. |
249 | 240 | """ |
250 | 241 | # TODO check inputs are pd.Series, arrays, or scalars -- else error |
251 | | - pd = _get_pandas() |
| 242 | + pd = _lazy_import("pandas") |
252 | 243 | output_index_type_tuple = PandasDataFrameResult.pandas_index_types(outputs) |
253 | 244 | # this next line just log warnings |
254 | 245 | # we don't actually care about the result since this is the current default behavior. |
@@ -283,7 +274,7 @@ def build_dataframe_with_dataframes(outputs: dict[str, Any]) -> pd.DataFrame: |
283 | 274 | :param outputs: The outputs to build the dataframe from. |
284 | 275 | :return: A dataframe with the outputs. |
285 | 276 | """ |
286 | | - pd = _get_pandas() |
| 277 | + pd = _lazy_import("pandas") |
287 | 278 |
|
288 | 279 | def get_output_name(output_name: str, column_name: str) -> str: |
289 | 280 | """Add function prefix to columns. |
@@ -329,7 +320,7 @@ def input_types(self) -> list[type[type]]: |
329 | 320 | return [Any] |
330 | 321 |
|
331 | 322 | def output_type(self) -> type: |
332 | | - return _get_pandas().DataFrame |
| 323 | + return _lazy_import("pandas", "DataFrame") |
333 | 324 |
|
334 | 325 |
|
335 | 326 | class StrictIndexTypePandasDataFrameResult(PandasDataFrameResult): |
@@ -395,7 +386,7 @@ def build_result(**outputs: dict[str, Any]) -> np.matrix: |
395 | 386 | :return: numpy matrix |
396 | 387 | """ |
397 | 388 | # TODO check inputs are all numpy arrays/array like things -- else error |
398 | | - np = _get_numpy() |
| 389 | + np = _lazy_import("numpy") |
399 | 390 | num_rows = -1 |
400 | 391 | columns_with_lengths = collections.OrderedDict() |
401 | 392 | for col, val in outputs.items(): # assumption is fixed order |
@@ -432,7 +423,7 @@ def input_types(self) -> list[type[type]]: |
432 | 423 | return [Any] # Typing |
433 | 424 |
|
434 | 425 | def output_type(self) -> type: |
435 | | - return _get_pandas().DataFrame |
| 426 | + return _lazy_import("pandas", "DataFrame") |
436 | 427 |
|
437 | 428 |
|
438 | 429 | class HamiltonGraphAdapter(lifecycle_api.GraphAdapter, abc.ABC): |
|
0 commit comments