diff --git a/pandas/core/config_init.py b/pandas/core/config_init.py index d77d17aa4d00e..81fb8090a7afe 100644 --- a/pandas/core/config_init.py +++ b/pandas/core/config_init.py @@ -9,6 +9,7 @@ module is imported, register them here rather then in the module. """ +import sys import warnings import pandas.core.config as cf @@ -341,18 +342,36 @@ def mpl_style_cb(key): def table_schema_cb(key): - # Having _ipython_display_ defined messes with the return value - # from cells, so the Out[x] dictionary breaks. - # Currently table schema is the only thing using it, so we'll - # monkey patch `_ipython_display_` onto NDFrame when config option - # is set - # see https://github.com/pandas-dev/pandas/issues/16168 - from pandas.core.generic import NDFrame, _ipython_display_ + # first, check if we are in IPython + if 'IPython' not in sys.modules: + # definitely not in IPython + return + from IPython import get_ipython + ip = get_ipython() + if ip is None: + # still not in IPython + return + + formatters = ip.display_formatter.formatters + + mimetype = "application/vnd.dataresource+json" if cf.get_option(key): - NDFrame._ipython_display_ = _ipython_display_ - elif getattr(NDFrame, '_ipython_display_', None): - del NDFrame._ipython_display_ + if mimetype not in formatters: + # define tableschema formatter + from IPython.core.formatters import BaseFormatter + + class TableSchemaFormatter(BaseFormatter): + print_method = '_repr_table_schema_' + _return_type = (dict,) + # register it: + formatters[mimetype] = TableSchemaFormatter() + # enable it if it's been disabled: + formatters[mimetype].enabled = True + else: + # unregister tableschema mime-type + if mimetype in formatters: + formatters[mimetype].enabled = False with cf.config_prefix('display'): diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 48ee1842dc4a0..b3498583f6e14 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -43,7 +43,6 @@ import pandas.core.algorithms as algos import pandas.core.common as com import pandas.core.missing as missing -from pandas.errors import UnserializableWarning from pandas.io.formats.printing import pprint_thing from pandas.io.formats.format import format_percentiles from pandas.tseries.frequencies import to_offset @@ -6279,38 +6278,6 @@ def logical_func(self, axis=None, bool_only=None, skipna=None, level=None, return set_function_name(logical_func, name, cls) -def _ipython_display_(self): - # Having _ipython_display_ defined messes with the return value - # from cells, so the Out[x] dictionary breaks. - # Currently table schema is the only thing using it, so we'll - # monkey patch `_ipython_display_` onto NDFrame when config option - # is set - # see https://github.com/pandas-dev/pandas/issues/16168 - try: - from IPython.display import display - except ImportError: - return None - - # Series doesn't define _repr_html_ or _repr_latex_ - latex = self._repr_latex_() if hasattr(self, '_repr_latex_') else None - html = self._repr_html_() if hasattr(self, '_repr_html_') else None - try: - table_schema = self._repr_table_schema_() - except Exception as e: - warnings.warn("Cannot create table schema representation. " - "{}".format(e), UnserializableWarning) - table_schema = None - # We need the inital newline since we aren't going through the - # usual __repr__. See - # https://github.com/pandas-dev/pandas/pull/14904#issuecomment-277829277 - text = "\n" + repr(self) - - reprs = {"text/plain": text, "text/html": html, "text/latex": latex, - "application/vnd.dataresource+json": table_schema} - reprs = {k: v for k, v in reprs.items() if v} - display(reprs, raw=True) - - # install the indexes for _name, _indexer in indexing.get_indexers_list(): NDFrame._create_indexer(_name, _indexer) diff --git a/pandas/errors/__init__.py b/pandas/errors/__init__.py index 9b6c9c5be319c..805e689dca840 100644 --- a/pandas/errors/__init__.py +++ b/pandas/errors/__init__.py @@ -57,9 +57,3 @@ class ParserWarning(Warning): """ -class UnserializableWarning(Warning): - """ - Warning that is raised when a DataFrame cannot be serialized. - - .. versionadded:: 0.20.0 - """ diff --git a/pandas/tests/io/formats/test_printing.py b/pandas/tests/io/formats/test_printing.py index b8d6e9578339f..3acd5c7a5e8c5 100644 --- a/pandas/tests/io/formats/test_printing.py +++ b/pandas/tests/io/formats/test_printing.py @@ -5,7 +5,6 @@ import pandas as pd from pandas import compat -from pandas.errors import UnserializableWarning import pandas.io.formats.printing as printing import pandas.io.formats.format as fmt import pandas.util.testing as tm @@ -137,8 +136,11 @@ def setUpClass(cls): except ImportError: pytest.skip("Mock is not installed") cls.mock = mock + from IPython.core.interactiveshell import InteractiveShell + cls.display_formatter = InteractiveShell.instance().display_formatter def test_publishes(self): + df = pd.DataFrame({"A": [1, 2]}) objects = [df['A'], df, df] # dataframe / series expected_keys = [ @@ -146,29 +148,20 @@ def test_publishes(self): {'text/plain', 'text/html', 'application/vnd.dataresource+json'}, ] - make_patch = self.mock.patch('IPython.display.display') opt = pd.option_context('display.html.table_schema', True) for obj, expected in zip(objects, expected_keys): - with opt, make_patch as mock_display: - handle = obj._ipython_display_() - assert mock_display.call_count == 1 - assert handle is None - args, kwargs = mock_display.call_args - arg, = args # just one argument - - assert kwargs == {"raw": True} - assert set(arg.keys()) == expected + with opt: + formatted = self.display_formatter.format(obj) + assert set(formatted[0].keys()) == expected with_latex = pd.option_context('display.latex.repr', True) - with opt, with_latex, make_patch as mock_display: - handle = obj._ipython_display_() - args, kwargs = mock_display.call_args - arg, = args + with opt, with_latex: + formatted = self.display_formatter.format(obj) expected = {'text/plain', 'text/html', 'text/latex', 'application/vnd.dataresource+json'} - assert set(arg.keys()) == expected + assert set(formatted[0].keys()) == expected def test_publishes_not_implemented(self): # column MultiIndex @@ -176,18 +169,13 @@ def test_publishes_not_implemented(self): midx = pd.MultiIndex.from_product([['A', 'B'], ['a', 'b', 'c']]) df = pd.DataFrame(np.random.randn(5, len(midx)), columns=midx) - make_patch = self.mock.patch('IPython.display.display') opt = pd.option_context('display.html.table_schema', True) - with opt, make_patch as mock_display: - with pytest.warns(UnserializableWarning) as record: - df._ipython_display_() - args, _ = mock_display.call_args - arg, = args # just one argument + + with opt: + formatted = self.display_formatter.format(df) expected = {'text/plain', 'text/html'} - assert set(arg.keys()) == expected - assert "orient='table' is not supported for MultiIndex" in ( - record[-1].message.args[0]) + assert set(formatted[0].keys()) == expected def test_config_on(self): df = pd.DataFrame({"A": [1, 2]}) @@ -209,26 +197,23 @@ def test_config_monkeypatches(self): assert not hasattr(df, '_ipython_display_') assert not hasattr(df['A'], '_ipython_display_') + formatters = self.display_formatter.formatters + mimetype = 'application/vnd.dataresource+json' + with pd.option_context('display.html.table_schema', True): - assert hasattr(df, '_ipython_display_') - # smoke test that it works - df._ipython_display_() - assert hasattr(df['A'], '_ipython_display_') - df['A']._ipython_display_() + assert 'application/vnd.dataresource+json' in formatters + assert formatters[mimetype].enabled - assert not hasattr(df, '_ipython_display_') - assert not hasattr(df['A'], '_ipython_display_') - # re-unsetting is OK - assert not hasattr(df, '_ipython_display_') - assert not hasattr(df['A'], '_ipython_display_') + # still there, just disabled + assert 'application/vnd.dataresource+json' in formatters + assert not formatters[mimetype].enabled # able to re-set with pd.option_context('display.html.table_schema', True): - assert hasattr(df, '_ipython_display_') + assert 'application/vnd.dataresource+json' in formatters + assert formatters[mimetype].enabled # smoke test that it works - df._ipython_display_() - assert hasattr(df['A'], '_ipython_display_') - df['A']._ipython_display_() + self.display_formatter.format(cf) # TODO: fix this broken test