def test_dataframeeditor_edit_bool(qtbot, monkeypatch): """Test that bools are editible in df and false-y strs are detected""" MockQMessageBox = Mock() attr_to_patch = ('spyder.widgets.variableexplorer' + '.dataframeeditor.QMessageBox') monkeypatch.setattr(attr_to_patch, MockQMessageBox) test_params = [numpy.bool_, numpy.bool, bool] test_strs = ['foo', 'false', 'f', '0', '0.', '0.0', '', ' '] expected_df = DataFrame([1, 0, 0, 0, 0, 0, 0, 0, 0], dtype=bool) for bool_type in test_params: test_df = DataFrame([0, 1, 1, 1, 1, 1, 1, 1, 0], dtype=bool_type) dialog = DataFrameEditor() assert dialog.setup_and_check(test_df, 'Test Dataframe') dialog.show() qtbot.waitForWindowShown(dialog) view = dialog.dataTable qtbot.keyPress(view, Qt.Key_Right) for test_str in test_strs: qtbot.keyPress(view, Qt.Key_Space) qtbot.keyPress(view.focusWidget(), Qt.Key_Backspace) qtbot.keyClicks(view.focusWidget(), test_str) qtbot.keyPress(view.focusWidget(), Qt.Key_Down) assert not MockQMessageBox.critical.called qtbot.keyPress(view, Qt.Key_Return) qtbot.wait(1000) assert (numpy.sum(expected_df[0].as_matrix() == dialog.get_value().as_matrix()[:, 0]) == len(expected_df))
def test_dataframe_multiindex(): """Test to validate proper creation and handling of a multiindex.""" arrays = [numpy.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']), numpy.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])] tuples = list(zip(*arrays)) index = MultiIndex.from_tuples(tuples, names=['first', 'second']) df = DataFrame(numpy.random.randn(6, 6), index=index[:6], columns=index[:6]) editor = DataFrameEditor(None) editor.setup_and_check(df) header = editor.table_header.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) == 0 assert data_header(header, 0, 0) == 'bar' assert data_header(header, 1, 0) == 'one' assert data_header(header, 0, 1) == 'bar' assert data_header(header, 1, 1) == 'two' assert data_header(header, 0, 2) == 'baz' assert data_header(header, 1, 2) == 'one' assert data_header(header, 0, 3) == 'baz' assert data_header(header, 1, 3) == 'two' assert data_header(header, 0, 4) == 'foo' assert data_header(header, 1, 4) == 'one' assert data_header(header, 0, 5) == 'foo' assert data_header(header, 1, 5) == 'two'
def test_header_bom(): """Test for BOM data in the headers.""" df = read_csv(os.path.join(FILES_PATH, 'issue_2514.csv')) editor = DataFrameEditor(None) editor.setup_and_check(df) header = editor.table_header.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) == "Date (MMM-YY)"
def test_change_format_emits_signal(qtbot, monkeypatch): mockQInputDialog = Mock() mockQInputDialog.getText = lambda parent, title, label, mode, text: ('%10.3e', True) monkeypatch.setattr('spyder.widgets.variableexplorer.dataframeeditor.QInputDialog', mockQInputDialog) df = DataFrame([[0]]) editor = DataFrameEditor(None) editor.setup_and_check(df) with qtbot.waitSignal(editor.sig_option_changed) as blocker: editor.change_format() assert blocker.args == ['dataframe_format', '%10.3e']
def test_sort_dataframe_with_category_dtypes(qtbot): # cf. issue 5361 df = DataFrame({'A': [1, 2, 3, 4], 'B': ['a', 'b', 'c', 'd']}) df = df.astype(dtype={'B': 'category'}) df_cols = df.dtypes editor = DataFrameEditor(None) editor.setup_and_check(df_cols) dfm = editor.dataModel QTimer.singleShot(1000, lambda: close_message_box(qtbot)) editor.dataModel.sort(0) assert data(dfm, 0, 0) == 'int64' assert data(dfm, 1, 0) == 'category'
def test_dataframe_simpleindex(): """Test to validate proper creation and handling of a simpleindex.""" df = DataFrame(numpy.random.randn(6, 6)) editor = DataFrameEditor(None) editor.setup_and_check(df) header = editor.table_header.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) == "0" assert header.headerData(1, Qt.Horizontal, Qt.DisplayRole) == "1" assert header.headerData(5, Qt.Horizontal, Qt.DisplayRole) == "5"
def test_header_encoding(): df = read_csv(os.path.join(FILES_PATH, 'issue_3896.csv')) editor = DataFrameEditor(None) editor.setup_and_check(df) model = editor.dataModel assert model.headerData(0, orientation=Qt.Horizontal) == "Index" assert model.headerData(1, orientation=Qt.Horizontal) == "Unnamed: 0" assert model.headerData(2, orientation=Qt.Horizontal) == "Unieke_Idcode" assert model.headerData(3, orientation=Qt.Horizontal) == "a" assert model.headerData(4, orientation=Qt.Horizontal) == "b" assert model.headerData(5, orientation=Qt.Horizontal) == "c" assert model.headerData(6, orientation=Qt.Horizontal) == "d"
def test_change_format_with_format_not_starting_with_percent(qtbot, monkeypatch): mockQInputDialog = Mock() mockQInputDialog.getText = lambda parent, title, label, mode, text: ('xxx%f', True) monkeypatch.setattr('spyder.widgets.variableexplorer.dataframeeditor' '.QInputDialog', mockQInputDialog) monkeypatch.setattr('spyder.widgets.variableexplorer.dataframeeditor' '.QMessageBox.critical', Mock()) df = DataFrame([[0]]) editor = DataFrameEditor(None) editor.setup_and_check(df) with qtbot.assertNotEmitted(editor.sig_option_changed): editor.change_format()
def test_header_encoding(): """Test for header encoding handling.""" df = read_csv(os.path.join(FILES_PATH, 'issue_3896.csv')) editor = DataFrameEditor(None) editor.setup_and_check(df) header = editor.table_header.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) == "Unnamed: 0" assert header.headerData(1, Qt.Horizontal, Qt.DisplayRole) == "Unieke_Idcode" assert header.headerData(2, Qt.Horizontal, Qt.DisplayRole) == "a" assert header.headerData(3, Qt.Horizontal, Qt.DisplayRole) == "b" assert header.headerData(4, Qt.Horizontal, Qt.DisplayRole) == "c" assert header.headerData(5, Qt.Horizontal, Qt.DisplayRole) == "d"
def test_dataframe_simpleindex_custom_columns(): """Test to validate proper creation and handling of custom simpleindex.""" df = DataFrame(numpy.random.randn(10, 5), columns=['a', 'b', 'c', 'd', 'e']) editor = DataFrameEditor(None) editor.setup_and_check(df) header = editor.table_header.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) == "a" assert header.headerData(1, Qt.Horizontal, Qt.DisplayRole) == "b" assert header.headerData(4, Qt.Horizontal, Qt.DisplayRole) == "e"
def test_sort_dataframe_with_duplicate_column(qtbot): df = DataFrame({'A': [1, 3, 2], 'B': [4, 6, 5]}) df = concat((df, df.A), axis=1) editor = DataFrameEditor(None) editor.setup_and_check(df) dfm = editor.dataModel QTimer.singleShot(1000, lambda: close_message_box(qtbot)) editor.dataModel.sort(0) assert [data(dfm, row, 0) for row in range(len(df))] == ['1', '3', '2'] assert [data(dfm, row, 1) for row in range(len(df))] == ['4', '6', '5'] editor.dataModel.sort(1) assert [data(dfm, row, 0) for row in range(len(df))] == ['1', '2', '3'] assert [data(dfm, row, 1) for row in range(len(df))] == ['4', '5', '6']
def test_sort_dataframe_with_duplicate_column(qtbot): df = DataFrame({'A': [1, 3, 2], 'B': [4, 6, 5]}) df = concat((df, df.A), axis=1) editor = DataFrameEditor(None) editor.setup_and_check(df) dfm = editor.dataModel QTimer.singleShot(1000, lambda: close_message_box(qtbot)) editor.dataModel.sort(1) assert [data(dfm, row, 1) for row in range(len(df))] == ['1', '3', '2'] assert [data(dfm, row, 2) for row in range(len(df))] == ['4', '6', '5'] editor.dataModel.sort(2) assert [data(dfm, row, 1) for row in range(len(df))] == ['1', '2', '3'] assert [data(dfm, row, 2) for row in range(len(df))] == ['4', '5', '6']
def test_dataframeeditor_edit_bool(qtbot, monkeypatch): """Test that bools are editible in df and false-y strs are detected.""" MockQMessageBox = Mock() attr_to_patch = ('spyder.widgets.variableexplorer' + '.dataframeeditor.QMessageBox') monkeypatch.setattr(attr_to_patch, MockQMessageBox) test_params = [numpy.bool_, numpy.bool, bool] test_strs = ['foo', 'false', 'f', '0', '0.', '0.0', '', ' '] expected_df = DataFrame([1, 0, 0, 0, 0, 0, 0, 0, 0], dtype=bool) for bool_type in test_params: test_df = DataFrame([0, 1, 1, 1, 1, 1, 1, 1, 0], dtype=bool_type) dialog = DataFrameEditor() assert dialog.setup_and_check(test_df, 'Test Dataframe') dialog.show() qtbot.waitForWindowShown(dialog) view = dialog.dataTable qtbot.keyClick(view, Qt.Key_Right) for test_str in test_strs: qtbot.keyClick(view, Qt.Key_Space) qtbot.keyClick(view.focusWidget(), Qt.Key_Backspace) qtbot.keyClicks(view.focusWidget(), test_str) qtbot.keyClick(view.focusWidget(), Qt.Key_Down) assert not MockQMessageBox.critical.called qtbot.wait(200) dialog.accept() qtbot.wait(500) assert (numpy.sum(expected_df[0].as_matrix() == dialog.get_value(). as_matrix()[:, 0]) == len(expected_df))
def test_non_ascii_index(): """ Test that there are no errors when displaying a dataframe with a non-ascii index and header. """ df = read_csv(os.path.join(FILES_PATH, 'issue_5833.csv'), index_col=0) editor = DataFrameEditor(None) editor.setup_and_check(df) index = editor.table_index.model() header = editor.table_header.model() dfm = editor.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) == "кодирование" assert data_index(index, 0, 0) == 'пример' assert data(dfm, 0, 0) == 'файла'
def test_dataframeeditor_edit_overflow(qtbot, monkeypatch): """ Test that entry of an overflowing integer is caught and handled properly. Integration regression test for issue #6114 . """ MockQMessageBox = Mock() attr_to_patch = ('spyder.widgets.variableexplorer' + '.dataframeeditor.QMessageBox') monkeypatch.setattr(attr_to_patch, MockQMessageBox) # Numpy doesn't raise the OverflowError for ints smaller than 64 bits if not os.name == 'nt': int32_bit_exponent = 66 else: int32_bit_exponent = 34 test_parameters = [(1, numpy.int32, int32_bit_exponent), (2, numpy.int64, 66)] expected_df = DataFrame([5, 6, 7, 3, 4]) for idx, int_type, bit_exponet in test_parameters: test_df = DataFrame(numpy.arange(0, 5), dtype=int_type) dialog = DataFrameEditor() assert dialog.setup_and_check(test_df, 'Test Dataframe') dialog.show() qtbot.waitForWindowShown(dialog) view = dialog.dataTable qtbot.keyClick(view, Qt.Key_Right) qtbot.keyClicks(view, '5') qtbot.keyClick(view, Qt.Key_Down) qtbot.keyClick(view, Qt.Key_Space) qtbot.keyClick(view.focusWidget(), Qt.Key_Backspace) qtbot.keyClicks(view.focusWidget(), str(int(2**bit_exponet))) qtbot.keyClick(view.focusWidget(), Qt.Key_Down) MockQMessageBox.critical.assert_called_with(ANY, "Error", ANY) assert MockQMessageBox.critical.call_count == idx qtbot.keyClicks(view, '7') qtbot.keyClick(view, Qt.Key_Up) qtbot.keyClicks(view, '6') qtbot.keyClick(view, Qt.Key_Down) qtbot.wait(200) dialog.accept() qtbot.wait(500) try: assert numpy.sum(expected_df[0].values == dialog.get_value().values) == len(expected_df) except AttributeError: assert numpy.sum(expected_df[0].as_matrix() == dialog.get_value(). as_matrix()) == len(expected_df)
def test_no_convert_strings_to_unicode(): """ Test that we don't apply any conversion to strings in headers, indexes or data. """ df = read_csv(os.path.join(FILES_PATH, 'issue_5833.csv'), index_col=0, encoding='koi8_r') editor = DataFrameEditor(None) editor.setup_and_check(df) index = editor.table_index.model() header = editor.table_header.model() dfm = editor.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) != u"кодирование" assert data_index(index, 0, 0) != u'пример' assert data(dfm, 0, 0) != u'файла'
def test_dataframeeditor_with_various_indexes(): for rng_name, rng in generate_pandas_indexes().items(): editor = DataFrameEditor(None) editor.setup_and_check(rng) dfm = editor.dataModel assert dfm.rowCount() == 20 assert dfm.columnCount() == 1 header = editor.table_header.model() assert header.headerData(0, Qt.Horizontal, Qt.DisplayRole) == "0" if rng_name == "Index": assert data(dfm, 0, 0) == 'A' assert data(dfm, 1, 0) == 'B' assert data(dfm, 2, 0) == 'C' assert data(dfm, 19, 0) == 'T' elif rng_name == "RangeIndex": assert data(dfm, 0, 0) == '0' assert data(dfm, 1, 0) == '1' assert data(dfm, 2, 0) == '2' assert data(dfm, 19, 0) == '19' elif rng_name == "Float64Index": assert data(dfm, 0, 0) == '0' assert data(dfm, 1, 0) == '0.1' assert data(dfm, 2, 0) == '0.2' assert data(dfm, 19, 0) == '1.9' elif rng_name == "DatetimeIndex": assert data(dfm, 0, 0) == '2017-01-01 00:00:00' assert data(dfm, 1, 0) == '2017-01-02 00:00:00' assert data(dfm, 2, 0) == '2017-01-03 00:00:00' assert data(dfm, 19, 0) == '2017-01-20 00:00:00' elif rng_name == "MultiIndex": assert data(dfm, 0, 0) == "('A', 'foo')" assert data(dfm, 1, 0) == "('A', 'bar')" assert data(dfm, 2, 0) == "('B', 'foo')" assert data(dfm, 19, 0) == "('J', 'bar')" elif rng_name == "CategoricalIndex": assert data(dfm, 0, 0) == 'a' assert data(dfm, 1, 0) == 'b' assert data(dfm, 2, 0) == 'c' assert data(dfm, 19, 0) == 'b'
def test_dataframeeditor_edit_overflow(qtbot, monkeypatch): """Test #6114: Entry of an overflow int is caught and handled properly""" MockQMessageBox = Mock() attr_to_patch = ('spyder.widgets.variableexplorer' + '.dataframeeditor.QMessageBox') monkeypatch.setattr(attr_to_patch, MockQMessageBox) # Numpy doesn't raise the OverflowError for ints smaller than 64 bits if platform.startswith('linux'): int32_bit_exponent = 66 else: int32_bit_exponent = 34 test_parameters = [(1, numpy.int32, int32_bit_exponent), (2, numpy.int64, 66)] expected_df = DataFrame([5, 6, 7, 3, 4]) for idx, int_type, bit_exponet in test_parameters: test_df = DataFrame(numpy.arange(0, 5), dtype=int_type) dialog = DataFrameEditor() assert dialog.setup_and_check(test_df, 'Test Dataframe') dialog.show() qtbot.waitForWindowShown(dialog) view = dialog.dataTable qtbot.keyPress(view, Qt.Key_Right) qtbot.keyClicks(view, '5') qtbot.keyPress(view, Qt.Key_Down) qtbot.keyPress(view, Qt.Key_Space) qtbot.keyPress(view.focusWidget(), Qt.Key_Backspace) qtbot.keyClicks(view.focusWidget(), str(int(2 ** bit_exponet))) qtbot.keyPress(view.focusWidget(), Qt.Key_Down) MockQMessageBox.critical.assert_called_with(ANY, "Error", ANY) assert MockQMessageBox.critical.call_count == idx qtbot.keyClicks(view, '7') qtbot.keyPress(view, Qt.Key_Up) qtbot.keyClicks(view, '6') qtbot.keyPress(view, Qt.Key_Down) qtbot.keyPress(view, Qt.Key_Return) assert numpy.sum(expected_df[0].as_matrix() == dialog.get_value().as_matrix()) == len(expected_df)
def test_dataframeeditor_edit_complex(qtbot, monkeypatch): """ Test that editing complex dtypes is handled gracefully in df editor. Integration regression test for issue #6115 . """ MockQMessageBox = Mock() attr_to_patch = ('spyder.widgets.variableexplorer' + '.dataframeeditor.QMessageBox') monkeypatch.setattr(attr_to_patch, MockQMessageBox) test_params = [(1, numpy.complex128), (2, numpy.complex64), (3, complex)] for count, complex_type in test_params: test_df = DataFrame(numpy.arange(10, 15), dtype=complex_type) dialog = DataFrameEditor() assert dialog.setup_and_check(test_df, 'Test Dataframe') dialog.show() qtbot.waitForWindowShown(dialog) view = dialog.dataTable qtbot.keyClick(view, Qt.Key_Right) qtbot.keyClick(view, Qt.Key_Down) qtbot.keyClick(view, Qt.Key_Space) qtbot.keyClick(view.focusWidget(), Qt.Key_Backspace) qtbot.keyClicks(view.focusWidget(), "42") qtbot.keyClick(view.focusWidget(), Qt.Key_Down) MockQMessageBox.critical.assert_called_with(ANY, "Error", ANY) assert MockQMessageBox.critical.call_count == count * 2 - 1 qtbot.keyClick(view, Qt.Key_Down) qtbot.keyClick(view, '1') qtbot.keyClick(view.focusWidget(), Qt.Key_Down) MockQMessageBox.critical.assert_called_with( ANY, "Error", ("Editing dtype {0!s} not yet supported.".format( type(test_df.iloc[1, 0]).__name__))) assert MockQMessageBox.critical.call_count == count * 2 qtbot.wait(200) dialog.accept() qtbot.wait(500) try: assert numpy.sum( test_df[0].values == dialog.get_value().values) == len(test_df) except AttributeError: assert numpy.sum(test_df[0].as_matrix() == dialog.get_value().as_matrix()) == len(test_df)
def create_dialog(obj, obj_name): """Creates the editor dialog and returns a tuple (dialog, func) where func is the function to be called with the dialog instance as argument, after quitting the dialog box The role of this intermediate function is to allow easy monkey-patching. (uschmitt suggested this indirection here so that he can monkey patch oedit to show eMZed related data) """ # Local import from spyder.widgets.variableexplorer.texteditor import TextEditor from spyder.widgets.variableexplorer.utils import (ndarray, FakeObject, Image, is_known_type, DataFrame, Series) from spyder.widgets.variableexplorer.collectionseditor import CollectionsEditor from spyder.widgets.variableexplorer.arrayeditor import ArrayEditor if DataFrame is not FakeObject: from spyder.widgets.variableexplorer.dataframeeditor import DataFrameEditor conv_func = lambda data: data readonly = not is_known_type(obj) if isinstance(obj, ndarray) and ndarray is not FakeObject: dialog = ArrayEditor() if not dialog.setup_and_check(obj, title=obj_name, readonly=readonly): return elif isinstance(obj, Image) and Image is not FakeObject \ and ndarray is not FakeObject: dialog = ArrayEditor() import numpy as np data = np.array(obj) if not dialog.setup_and_check(data, title=obj_name, readonly=readonly): return from spyder.pil_patch import Image conv_func = lambda data: Image.fromarray(data, mode=obj.mode) elif isinstance(obj, (DataFrame, Series)) and DataFrame is not FakeObject: dialog = DataFrameEditor() if not dialog.setup_and_check(obj): return elif is_text_string(obj): dialog = TextEditor(obj, title=obj_name, readonly=readonly) else: dialog = CollectionsEditor() dialog.setup(obj, title=obj_name, readonly=readonly) def end_func(dialog): return conv_func(dialog.get_value()) return dialog, end_func
def test_dataframeeditor_edit_complex(qtbot, monkeypatch): """Test for #6115: editing complex dtypes raises error in df editor""" MockQMessageBox = Mock() attr_to_patch = ('spyder.widgets.variableexplorer' + '.dataframeeditor.QMessageBox') monkeypatch.setattr(attr_to_patch, MockQMessageBox) test_params = [(1, numpy.complex128), (2, numpy.complex64), (3, complex)] for count, complex_type in test_params: test_df = DataFrame(numpy.arange(10, 15), dtype=complex_type) dialog = DataFrameEditor() assert dialog.setup_and_check(test_df, 'Test Dataframe') dialog.show() qtbot.waitForWindowShown(dialog) view = dialog.dataTable qtbot.keyPress(view, Qt.Key_Right) qtbot.keyPress(view, Qt.Key_Down) qtbot.keyPress(view, Qt.Key_Space) qtbot.keyPress(view.focusWidget(), Qt.Key_Backspace) qtbot.keyClicks(view.focusWidget(), "42") qtbot.keyPress(view.focusWidget(), Qt.Key_Down) MockQMessageBox.critical.assert_called_with(ANY, "Error", ANY) assert MockQMessageBox.critical.call_count == count * 2 - 1 qtbot.keyPress(view, Qt.Key_Down) qtbot.keyClick(view, '1') qtbot.keyPress(view.focusWidget(), Qt.Key_Down) MockQMessageBox.critical.assert_called_with( ANY, "Error", ("Editing dtype {0!s} not yet supported." .format(type(test_df.iloc[1, 0]).__name__))) assert MockQMessageBox.critical.call_count == count * 2 qtbot.keyPress(view, Qt.Key_Return) qtbot.wait(1000) assert numpy.sum(test_df[0].as_matrix() == dialog.get_value().as_matrix()) == len(test_df)
def test_header_bom(): df = read_csv(os.path.join(FILES_PATH, 'issue_2514.csv')) editor = DataFrameEditor(None) editor.setup_and_check(df) model = editor.dataModel assert model.headerData(1, orientation=Qt.Horizontal) == "Date (MMM-YY)"