From aad77d61303e84a9cffeee990145b84e5fa9cc9c Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Mon, 17 Jun 2019 09:41:36 -0500 Subject: [PATCH 1/3] update --- doc/source/install.rst | 1 + pandas/compat/_optional.py | 1 + pandas/io/pytables.py | 7 ++----- pandas/tests/test_optional_dependency.py | 10 ++++++++++ pandas/util/_test_decorators.py | 17 +++++++++++++++++ 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/doc/source/install.rst b/doc/source/install.rst index 1c1f0c1d4cf8e..ee4b36f898e31 100644 --- a/doc/source/install.rst +++ b/doc/source/install.rst @@ -286,6 +286,7 @@ psycopg2 PostgreSQL engine for sqlalchemy pyarrow 0.9.0 Parquet and feather reading / writing pymysql MySQL engine for sqlalchemy pyreadstat SPSS files (.sav) reading +pytables 3.4.2 HDF5 reading / writing qtpy Clipboard I/O s3fs 0.0.8 Amazon S3 access xarray 0.8.2 pandas-like API for N-dimensional data diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index 4a7b8c4e88649..875edb3d3f1dd 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -19,6 +19,7 @@ "s3fs": "0.0.8", "scipy": "0.19.0", "sqlalchemy": "1.1.4", + "tables": "3.4.2", "xarray": "0.8.2", "xlrd": "1.1.0", "xlwt": "1.2.0", diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 983b1286eec91..79d6d8563a162 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -19,6 +19,7 @@ from pandas._libs import lib, writers as libwriters from pandas._libs.tslibs import timezones +from pandas.compat._optional import import_optional_dependency from pandas.errors import PerformanceWarning from pandas.core.dtypes.common import ( @@ -448,11 +449,7 @@ def __init__(self, path, mode=None, complevel=None, complib=None, if 'format' in kwargs: raise ValueError('format is not a defined argument for HDFStore') - try: - import tables # noqa - except ImportError as ex: # pragma: no cover - raise ImportError('HDFStore requires PyTables, "{ex!s}" problem ' - 'importing'.format(ex=ex)) + tables = import_optional_dependency("tables") if complib is not None and complib not in tables.filters.all_complibs: raise ValueError( diff --git a/pandas/tests/test_optional_dependency.py b/pandas/tests/test_optional_dependency.py index 3916bedb8e44b..74edc30f3e83d 100644 --- a/pandas/tests/test_optional_dependency.py +++ b/pandas/tests/test_optional_dependency.py @@ -4,7 +4,9 @@ import pytest from pandas.compat._optional import VERSIONS, import_optional_dependency +import pandas.util._test_decorators as td +import pandas as pd import pandas.util.testing as tm @@ -50,3 +52,11 @@ def test_no_version_raises(): with pytest.raises(ImportError, match="Can't determine .* fakemodule"): import_optional_dependency(name) + + +@td.skip_if_installed("tables") +def test_pytables_raises(): + df = pd.DataFrame({"A": [1, 2]}) + with pytest.raises(ImportError, match="tables"): + with tm.ensure_clean("foo.h5") as path: + df.to_hdf(path, "df") diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 4cc316ffdd7ab..455d04458691b 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -99,6 +99,23 @@ def _skip_if_no_scipy(): safe_import('scipy.signal')) +def skip_if_installed( + package: str, +) -> MarkDecorator: + """ + Skip a test if a package is installed. + + Parameters + ---------- + package : str + The name of the package. + """ + return pytest.mark.skipif( + safe_import(package), + reason="Skipping because {} is installed.".format(package) + ) + + def skip_if_no( package: str, min_version: Optional[str] = None From f98830470c89cb1efc88d7043c6169229c30909b Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 20 Jun 2019 08:38:38 -0500 Subject: [PATCH 2/3] restore cleanup --- pandas/util/_print_versions.py | 102 +++++++++++++++------------------ 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/pandas/util/_print_versions.py b/pandas/util/_print_versions.py index a5c86c2cc80b3..5e2e013c4afcc 100644 --- a/pandas/util/_print_versions.py +++ b/pandas/util/_print_versions.py @@ -1,5 +1,4 @@ import codecs -import importlib import locale import os import platform @@ -7,6 +6,9 @@ import subprocess import sys +from pandas.compat._optional import ( + VERSIONS, _get_version, import_optional_dependency) + def get_sys_info(): "Returns system information as a dict" @@ -58,60 +60,49 @@ def get_sys_info(): def show_versions(as_json=False): sys_info = get_sys_info() - deps = [ - # (MODULE_NAME, f(mod) -> mod version) - ("pandas", lambda mod: mod.__version__), - ("pytest", lambda mod: mod.__version__), - ("pip", lambda mod: mod.__version__), - ("setuptools", lambda mod: mod.__version__), - ("Cython", lambda mod: mod.__version__), - ("numpy", lambda mod: mod.version.version), - ("scipy", lambda mod: mod.version.version), - ("pyarrow", lambda mod: mod.__version__), - ("xarray", lambda mod: mod.__version__), - ("IPython", lambda mod: mod.__version__), - ("sphinx", lambda mod: mod.__version__), - ("patsy", lambda mod: mod.__version__), - ("dateutil", lambda mod: mod.__version__), - ("pytz", lambda mod: mod.VERSION), - ("blosc", lambda mod: mod.__version__), - ("bottleneck", lambda mod: mod.__version__), - ("tables", lambda mod: mod.__version__), - ("numexpr", lambda mod: mod.__version__), - ("feather", lambda mod: mod.__version__), - ("matplotlib", lambda mod: mod.__version__), - ("openpyxl", lambda mod: mod.__version__), - ("xlrd", lambda mod: mod.__VERSION__), - ("xlwt", lambda mod: mod.__VERSION__), - ("xlsxwriter", lambda mod: mod.__version__), - ("lxml.etree", lambda mod: mod.__version__), - ("bs4", lambda mod: mod.__version__), - ("html5lib", lambda mod: mod.__version__), - ("sqlalchemy", lambda mod: mod.__version__), - ("pymysql", lambda mod: mod.__version__), - ("psycopg2", lambda mod: mod.__version__), - ("jinja2", lambda mod: mod.__version__), - ("s3fs", lambda mod: mod.__version__), - ("fastparquet", lambda mod: mod.__version__), - ("pandas_gbq", lambda mod: mod.__version__), - ("pandas_datareader", lambda mod: mod.__version__), - ("gcsfs", lambda mod: mod.__version__), + 'pandas', + # required + 'numpy', + 'pytz', + 'dateutil', + # install / build, + 'pip', + 'setuptools', + 'Cython', + # test + 'pytest', + 'hypothesis', + # docs + "sphinx", + # Other, need a min version + "blosc", + "feather", + "xlsxwriter", + "lxml.etree", + "html5lib", + "pymysql", + "psycopg2", + "jinja2", + # Other, not imported. + "IPython", + "pandas_datareader", ] - deps_blob = list() - for (modname, ver_f) in deps: - try: - if modname in sys.modules: - mod = sys.modules[modname] - else: - mod = importlib.import_module(modname) - ver = ver_f(mod) - deps_blob.append((modname, ver)) - except ImportError: - deps_blob.append((modname, None)) + deps.extend(list(VERSIONS)) + deps_blob = [] - if (as_json): + for modname in deps: + mod = import_optional_dependency(modname, + raise_on_missing=False, + on_version="ignore") + if mod: + ver = _get_version(mod) + else: + ver = None + deps_blob.append((modname, ver)) + + if as_json: try: import json except ImportError: @@ -126,16 +117,15 @@ def show_versions(as_json=False): json.dump(j, f, indent=2) else: - + maxlen = max(len(x) for x in deps) + tpl = '{{k:<{maxlen}}}: {{stat}}'.format(maxlen=maxlen) print("\nINSTALLED VERSIONS") print("------------------") - for k, stat in sys_info: - print("{k}: {stat}".format(k=k, stat=stat)) - + print(tpl.format(k=k, stat=stat)) print("") for k, stat in deps_blob: - print("{k}: {stat}".format(k=k, stat=stat)) + print(tpl.format(k=k, stat=stat)) def main(): From e384c40328dfd07b127dcf7400dd22438181ce96 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 20 Jun 2019 08:41:36 -0500 Subject: [PATCH 3/3] move test --- pandas/tests/io/test_pytables_missing.py | 14 ++++++++++++++ pandas/tests/test_optional_dependency.py | 10 ---------- 2 files changed, 14 insertions(+), 10 deletions(-) create mode 100644 pandas/tests/io/test_pytables_missing.py diff --git a/pandas/tests/io/test_pytables_missing.py b/pandas/tests/io/test_pytables_missing.py new file mode 100644 index 0000000000000..4ceb80889c989 --- /dev/null +++ b/pandas/tests/io/test_pytables_missing.py @@ -0,0 +1,14 @@ +import pytest + +import pandas.util._test_decorators as td + +import pandas as pd +import pandas.util.testing as tm + + +@td.skip_if_installed("tables") +def test_pytables_raises(): + df = pd.DataFrame({"A": [1, 2]}) + with pytest.raises(ImportError, match="tables"): + with tm.ensure_clean("foo.h5") as path: + df.to_hdf(path, "df") diff --git a/pandas/tests/test_optional_dependency.py b/pandas/tests/test_optional_dependency.py index 74edc30f3e83d..3916bedb8e44b 100644 --- a/pandas/tests/test_optional_dependency.py +++ b/pandas/tests/test_optional_dependency.py @@ -4,9 +4,7 @@ import pytest from pandas.compat._optional import VERSIONS, import_optional_dependency -import pandas.util._test_decorators as td -import pandas as pd import pandas.util.testing as tm @@ -52,11 +50,3 @@ def test_no_version_raises(): with pytest.raises(ImportError, match="Can't determine .* fakemodule"): import_optional_dependency(name) - - -@td.skip_if_installed("tables") -def test_pytables_raises(): - df = pd.DataFrame({"A": [1, 2]}) - with pytest.raises(ImportError, match="tables"): - with tm.ensure_clean("foo.h5") as path: - df.to_hdf(path, "df")