Skip to content

Commit bd0f8a6

Browse files
committed
Refactor missing dependency
1 parent 2513dcc commit bd0f8a6

File tree

5 files changed

+53
-43
lines changed

5 files changed

+53
-43
lines changed

pandas/conftest.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
import builtins
2+
from contextlib import contextmanager
13
from datetime import date, time, timedelta, timezone
24
from decimal import Decimal
35
import os
46

57
from dateutil.tz import tzlocal, tzutc
6-
import hypothesis
7-
from hypothesis import strategies as st
88
import numpy as np
99
import pytest
1010
from pytz import FixedOffset, utc
1111

1212
import pandas.util._test_decorators as td
1313

14+
import hypothesis
15+
from hypothesis import strategies as st
1416
import pandas as pd
1517
from pandas import DataFrame
1618
import pandas.util.testing as tm
@@ -287,6 +289,38 @@ def strict_data_files(pytestconfig):
287289
return pytestconfig.getoption("--strict-data-files")
288290

289291

292+
def _missing_dependency(monkeypatch):
293+
import_ = __import__
294+
295+
@contextmanager
296+
def inner(module_name):
297+
def fail(name, *args, **kwargs):
298+
if name == module_name or name in module_name:
299+
raise ImportError("No module {}".format(module_name))
300+
else:
301+
return import_(name, *args, **kwargs)
302+
303+
with monkeypatch.context() as m:
304+
m.setattr(builtins, "__import__", fail)
305+
yield
306+
return inner
307+
308+
309+
@pytest.fixture
310+
def missing_dependency(monkeypatch):
311+
"""
312+
Simulate a missing dependency.
313+
314+
Returns
315+
-------
316+
callable
317+
A callable that can be used as a context manager.
318+
The callable expects one argument, the module name
319+
or names to raise an ImportError on.
320+
"""
321+
yield _missing_dependency(monkeypatch)
322+
323+
290324
@pytest.fixture
291325
def datapath(strict_data_files):
292326
"""Get the path to a data file.

pandas/tests/test_downstream.py

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""
22
Testing that we work in the downstream packages
33
"""
4-
import builtins
54
import importlib
65
import subprocess
76
import sys
@@ -134,30 +133,10 @@ def test_pyarrow(df):
134133
tm.assert_frame_equal(result, df)
135134

136135

137-
def test_missing_required_dependency(monkeypatch):
136+
def test_missing_required_dependency(missing_dependency):
138137
# GH 23868
139-
original_import = __import__
140-
141-
def mock_import_fail(name, *args, **kwargs):
142-
if name == "numpy":
143-
raise ImportError("cannot import name numpy")
144-
elif name == "pytz":
145-
raise ImportError("cannot import name some_dependency")
146-
elif name == "dateutil":
147-
raise ImportError("cannot import name some_other_dependency")
148-
else:
149-
return original_import(name, *args, **kwargs)
150-
151-
expected_msg = (
152-
"Unable to import required dependencies:"
153-
"\nnumpy: cannot import name numpy"
154-
"\npytz: cannot import name some_dependency"
155-
"\ndateutil: cannot import name some_other_dependency"
156-
)
157-
158138
import pandas as pd
159139

160-
with monkeypatch.context() as m:
161-
m.setattr(builtins, "__import__", mock_import_fail)
162-
with pytest.raises(ImportError, match=expected_msg):
140+
with missing_dependency(['numpy', 'dateutil', 'pytz']):
141+
with pytest.raises(ImportError, match='numpy'):
163142
importlib.reload(pd)

pandas/tests/test_optional_dependency.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import pytest
55

66
from pandas.compat._optional import VERSIONS, import_optional_dependency
7-
import pandas.util._test_decorators as td
87

98
import pandas as pd
109
import pandas.util.testing as tm
@@ -54,9 +53,9 @@ def test_no_version_raises():
5453
import_optional_dependency(name)
5554

5655

57-
@td.skip_if_installed("tables")
58-
def test_pytables_raises():
56+
def test_pytables_raises(missing_dependency):
5957
df = pd.DataFrame({"A": [1, 2]})
60-
with pytest.raises(ImportError, match="tables"):
61-
with tm.ensure_clean("foo.h5") as path:
62-
df.to_hdf(path, "df")
58+
with missing_dependency("tables"):
59+
with pytest.raises(ImportError, match="tables"):
60+
with tm.ensure_clean("foo.h5") as path:
61+
df.to_hdf(path, "df")

pandas/tests/util/test_util.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import importlib
12
import os
23
import sys
34

@@ -124,3 +125,11 @@ def test_rng_context():
124125
with tm.RNGContext(1):
125126
assert np.random.randn() == expected1
126127
assert np.random.randn() == expected0
128+
129+
130+
def test_missing_dependency(missing_dependency):
131+
with missing_dependency('os'):
132+
# other libraries are OK
133+
import collections # noqa
134+
with pytest.raises(ImportError, match='os'):
135+
importlib.reload(os)

pandas/util/_test_decorators.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -140,17 +140,6 @@ def skip_if_no(
140140
)
141141

142142

143-
def skip_if_installed(
144-
package: str,
145-
min_version: Optional[str] = None
146-
) -> MarkDecorator:
147-
"""Skip if a package *is* present."""
148-
msg = "Skipping because {} is installed.".format(package)
149-
return pytest.mark.skipif(
150-
safe_import(package, min_version=min_version), reason=msg
151-
)
152-
153-
154143
skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(),
155144
reason="Missing matplotlib dependency")
156145
skip_if_np_lt_115 = pytest.mark.skipif(_np_version_under1p15,

0 commit comments

Comments
 (0)