Skip to content
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

BUG: Improve ImportError message to suggest importing dependencies directly for full error details #61084

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
12 changes: 5 additions & 7 deletions pandas/__init__.py
Original file line number Diff line number Diff line change
@@ -4,19 +4,17 @@

# Let users know if they're missing any of our hard dependencies
_hard_dependencies = ("numpy", "dateutil")
_missing_dependencies = []

for _dependency in _hard_dependencies:
try:
__import__(_dependency)
except ImportError as _e: # pragma: no cover
_missing_dependencies.append(f"{_dependency}: {_e}")
raise ImportError(
f"Unable to import required dependency {_dependency}. "
"Please see the traceback for details."
) from _e

if _missing_dependencies: # pragma: no cover
raise ImportError(
"Unable to import required dependencies:\n" + "\n".join(_missing_dependencies)
)
del _hard_dependencies, _dependency, _missing_dependencies
del _hard_dependencies, _dependency

try:
# numpy compat
57 changes: 24 additions & 33 deletions pandas/tests/test_downstream.py
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

import array
from functools import partial
import importlib
import subprocess
import sys

@@ -186,41 +187,31 @@ def test_yaml_dump(df):
tm.assert_frame_equal(df, loaded2)


@pytest.mark.single_cpu
def test_missing_required_dependency():
# GH 23868
# To ensure proper isolation, we pass these flags
# -S : disable site-packages
# -s : disable user site-packages
# -E : disable PYTHON* env vars, especially PYTHONPATH
# https://github.com/MacPython/pandas-wheels/pull/50

pyexe = sys.executable.replace("\\", "/")

# We skip this test if pandas is installed as a site package. We first
# import the package normally and check the path to the module before
# executing the test which imports pandas with site packages disabled.
call = [pyexe, "-c", "import pandas;print(pandas.__file__)"]
output = subprocess.check_output(call).decode()
if "site-packages" in output:
pytest.skip("pandas installed as site package")

# This test will fail if pandas is installed as a site package. The flags
# prevent pandas being imported and the test will report Failed: DID NOT
# RAISE <class 'subprocess.CalledProcessError'>
call = [pyexe, "-sSE", "-c", "import pandas"]

msg = (
rf"Command '\['{pyexe}', '-sSE', '-c', 'import pandas'\]' "
"returned non-zero exit status 1."
)
@pytest.mark.parametrize("dependency", ["numpy", "dateutil"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we monkeypatch _hard_dependencies with a fake module and test that instead? I'm worried about this test having side effects by modifying sys.modules

def test_missing_required_dependency(monkeypatch, dependency):
# GH#61030
# test pandas raises appropriate error when a required dependency is missing
real_module = sys.modules.get(dependency)
mock_error = ImportError(f"Mock error for {dependency}")

with pytest.raises(subprocess.CalledProcessError, match=msg) as exc:
subprocess.check_output(call, stderr=subprocess.STDOUT)
def mock_import(name, *args, **kwargs):
if name == dependency:
raise mock_error
return importlib.import_module(name)

try:
monkeypatch.setattr("builtins.__import__", mock_import)

output = exc.value.stdout.decode()
for name in ["numpy", "dateutil"]:
assert name in output
if dependency in sys.modules:
del sys.modules[dependency]

with pytest.raises(ImportError) as excinfo:
importlib.reload(importlib.import_module("pandas"))

assert dependency in str(excinfo.value)
finally:
if real_module is not None:
sys.modules[dependency] = real_module


def test_frame_setitem_dask_array_into_new_col(request):
Loading
Oops, something went wrong.