Skip to content

CLN: Simplify optional dependency #39083

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

Merged
merged 2 commits into from
Jan 11, 2021
Merged
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
31 changes: 15 additions & 16 deletions pandas/compat/_optional.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ def _get_version(module: types.ModuleType) -> str:
def import_optional_dependency(
name: str,
extra: str = "",
raise_on_missing: bool = True,
on_version: str = "raise",
errors: str = "raise",
min_version: Optional[str] = None,
):
"""
Expand All @@ -79,29 +78,30 @@ def import_optional_dependency(
The module name.
extra : str
Additional text to include in the ImportError message.
raise_on_missing : bool, default True
Whether to raise if the optional dependency is not found.
When False and the module is not present, None is returned.
on_version : str {'raise', 'warn'}
What to do when a dependency's version is too old.
errors : str {'raise', 'warn', 'ignore'}
What to do when a dependency is not found or its version is too old.

* raise : Raise an ImportError
* warn : Warn that the version is too old. Returns None
* ignore: Return the module, even if the version is too old.
* warn : Only applicable when a module's version is to old.
Warns that the version is too old and returns None
* ignore: If the module is not installed, return None, otherwise,
return the module, even if the version is too old.
It's expected that users validate the version locally when
using ``on_version="ignore"`` (see. ``io/html.py``)
using ``errors="ignore"`` (see. ``io/html.py``)
min_version : str, default None
Specify a minimum version that is different from the global pandas
minimum version required.
Returns
-------
maybe_module : Optional[ModuleType]
The imported module, when found and the version is correct.
None is returned when the package is not found and `raise_on_missing`
is False, or when the package's version is too old and `on_version`
None is returned when the package is not found and `errors`
is False, or when the package's version is too old and `errors`
is ``'warn'``.
"""

assert errors in {"warn", "raise", "ignore"}

package_name = INSTALL_MAPPING.get(name)
install_name = package_name if package_name is not None else name

Expand All @@ -112,7 +112,7 @@ def import_optional_dependency(
try:
module = importlib.import_module(name)
except ImportError:
if raise_on_missing:
if errors == "raise":
raise ImportError(msg) from None
else:
return None
Expand All @@ -128,15 +128,14 @@ def import_optional_dependency(
if minimum_version:
version = _get_version(module_to_get)
if distutils.version.LooseVersion(version) < minimum_version:
assert on_version in {"warn", "raise", "ignore"}
msg = (
f"Pandas requires version '{minimum_version}' or newer of '{parent}' "
f"(version '{version}' currently installed)."
)
if on_version == "warn":
if errors == "warn":
warnings.warn(msg, UserWarning)
return None
elif on_version == "raise":
elif errors == "raise":
raise ImportError(msg)

return module
2 changes: 1 addition & 1 deletion pandas/core/computation/check.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from pandas.compat._optional import import_optional_dependency

ne = import_optional_dependency("numexpr", raise_on_missing=False, on_version="warn")
ne = import_optional_dependency("numexpr", errors="warn")
NUMEXPR_INSTALLED = ne is not None
if NUMEXPR_INSTALLED:
NUMEXPR_VERSION = ne.__version__
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/nanops.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

from pandas.core.construction import extract_array

bn = import_optional_dependency("bottleneck", raise_on_missing=False, on_version="warn")
bn = import_optional_dependency("bottleneck", errors="warn")
_BOTTLENECK_INSTALLED = bn is not None
_USE_BOTTLENECK = False

Expand Down
7 changes: 1 addition & 6 deletions pandas/io/excel/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1053,12 +1053,7 @@ def __init__(
self._io = stringify_path(path_or_buffer)

# Determine xlrd version if installed
if (
import_optional_dependency(
"xlrd", raise_on_missing=False, on_version="ignore"
)
is None
):
if import_optional_dependency("xlrd", errors="ignore") is None:
xlrd_version = None
else:
import xlrd
Expand Down
14 changes: 3 additions & 11 deletions pandas/io/excel/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,14 @@ def get_default_engine(ext, mode="reader"):
assert mode in ["reader", "writer"]
if mode == "writer":
# Prefer xlsxwriter over openpyxl if installed
xlsxwriter = import_optional_dependency(
"xlsxwriter", raise_on_missing=False, on_version="warn"
)
xlsxwriter = import_optional_dependency("xlsxwriter", errors="warn")
if xlsxwriter:
_default_writers["xlsx"] = "xlsxwriter"
return _default_writers[ext]
else:
if (
import_optional_dependency(
"openpyxl", raise_on_missing=False, on_version="ignore"
)
is None
and import_optional_dependency(
"xlrd", raise_on_missing=False, on_version="ignore"
)
is not None
import_optional_dependency("openpyxl", errors="ignore") is None
and import_optional_dependency("xlrd", errors="ignore") is not None
):
# if no openpyxl but xlrd installed, return xlrd
# the version is handled elsewhere
Expand Down
10 changes: 3 additions & 7 deletions pandas/io/html.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,13 @@ def _importers():
return

global _HAS_BS4, _HAS_LXML, _HAS_HTML5LIB
bs4 = import_optional_dependency("bs4", raise_on_missing=False, on_version="ignore")
bs4 = import_optional_dependency("bs4", errors="ignore")
_HAS_BS4 = bs4 is not None

lxml = import_optional_dependency(
"lxml.etree", raise_on_missing=False, on_version="ignore"
)
lxml = import_optional_dependency("lxml.etree", errors="ignore")
_HAS_LXML = lxml is not None

html5lib = import_optional_dependency(
"html5lib", raise_on_missing=False, on_version="ignore"
)
html5lib = import_optional_dependency("html5lib", errors="ignore")
_HAS_HTML5LIB = html5lib is not None

_IMPORTS = True
Expand Down
5 changes: 1 addition & 4 deletions pandas/tests/io/excel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@
]


if (
import_optional_dependency("xlrd", raise_on_missing=False, on_version="ignore")
is None
):
if import_optional_dependency("xlrd", errors="ignore") is None:
xlrd_version = None
else:
import xlrd
Expand Down
14 changes: 2 additions & 12 deletions pandas/tests/io/excel/test_xlrd.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,7 @@ def test_excel_table_sheet_by_index(datapath, read_ext):
def test_excel_file_warning_with_xlsx_file(datapath):
# GH 29375
path = datapath("io", "data", "excel", "test1.xlsx")
has_openpyxl = (
import_optional_dependency(
"openpyxl", raise_on_missing=False, on_version="ignore"
)
is not None
)
has_openpyxl = import_optional_dependency("openpyxl", errors="ignore") is not None
if not has_openpyxl:
with tm.assert_produces_warning(
FutureWarning,
Expand All @@ -73,12 +68,7 @@ def test_excel_file_warning_with_xlsx_file(datapath):
def test_read_excel_warning_with_xlsx_file(datapath):
# GH 29375
path = datapath("io", "data", "excel", "test1.xlsx")
has_openpyxl = (
import_optional_dependency(
"openpyxl", raise_on_missing=False, on_version="ignore"
)
is not None
)
has_openpyxl = import_optional_dependency("openpyxl", errors="ignore") is not None
if not has_openpyxl:
if xlrd_version >= "2":
with pytest.raises(
Expand Down
6 changes: 3 additions & 3 deletions pandas/tests/test_optional_dependency.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def test_import_optional():
with pytest.raises(ImportError, match=match):
import_optional_dependency("notapackage")

result = import_optional_dependency("notapackage", raise_on_missing=False)
result = import_optional_dependency("notapackage", errors="ignore")
assert result is None


Expand All @@ -38,7 +38,7 @@ def test_bad_version(monkeypatch):
assert result is module

with tm.assert_produces_warning(UserWarning):
result = import_optional_dependency("fakemodule", on_version="warn")
result = import_optional_dependency("fakemodule", errors="warn")
assert result is None

module.__version__ = "1.0.0" # exact match is OK
Expand All @@ -63,7 +63,7 @@ def test_submodule(monkeypatch):
import_optional_dependency("fakemodule.submodule")

with tm.assert_produces_warning(UserWarning):
result = import_optional_dependency("fakemodule.submodule", on_version="warn")
result = import_optional_dependency("fakemodule.submodule", errors="warn")
assert result is None

module.__version__ = "1.0.0" # exact match is OK
Expand Down
4 changes: 1 addition & 3 deletions pandas/util/_print_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,7 @@ def _get_dependency_info() -> Dict[str, JSONSerializable]:

result: Dict[str, JSONSerializable] = {}
for modname in deps:
mod = import_optional_dependency(
modname, raise_on_missing=False, on_version="ignore"
)
mod = import_optional_dependency(modname, errors="ignore")
result[modname] = _get_version(mod) if mod else None
return result

Expand Down