diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py b/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py index 973e0e525f19..9c65651f3d3e 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_constants.py @@ -83,6 +83,12 @@ IS_PYTHON_STACKLESS = "stackless" in sys.version.lower() CYTHON_SUPPORTED = False NUMPY_NUMERIC_TYPES = "biufc" +NUMPY_FLOATING_POINT_TYPES = "fc" +# b boolean +# i signed integer +# u unsigned integer +# f floating-point +# c complex floating-point try: import platform diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_thrift.py b/python/helpers/pydev/_pydevd_bundle/pydevd_thrift.py index 0a2f11975f36..c5c4e6f43f5d 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_thrift.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_thrift.py @@ -15,7 +15,8 @@ from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, IS_PY3K, NUMPY_NUMERIC_TYPES from _pydevd_bundle.pydevd_extension_api import TypeResolveProvider, StrPresentationProvider from _pydevd_bundle.pydevd_utils import take_first_n_coll_elements, is_numeric_container, is_pandas_container, pandas_to_str, is_string -from _pydevd_bundle.pydevd_vars import get_label, array_default_format, is_able_to_format_number, MAXIMUM_ARRAY_SIZE +from _pydevd_bundle.pydevd_vars import get_label, array_default_format, is_able_to_format_number, MAXIMUM_ARRAY_SIZE, \ + get_column_formatter_by_type, DEFAULT_DF_FORMAT from pydev_console.pydev_protocol import DebugValue, GetArrayResponse, ArrayData, ArrayHeaders, ColHeader, RowHeader, \ UnsupportedArrayTypeException, ExceedingArrayDimensionsException @@ -529,7 +530,7 @@ def dataframe_to_thrift_struct(df, name, roffset, coffset, rows, cols, format): kind = "O" format = array_default_format(kind) else: - format = array_default_format("f") + format = array_default_format(DEFAULT_DF_FORMAT) array_chunk.format = "%" + format if (rows, cols) == (-1, -1): @@ -560,7 +561,7 @@ def dataframe_to_thrift_struct(df, name, roffset, coffset, rows, cols, format): cols = df.shape[1] if dim > 1 else 1 def col_to_format(c): - return format if dtypes[c] in NUMPY_NUMERIC_TYPES and format else array_default_format(dtypes[c]) + return get_column_formatter_by_type(format, dtypes[c]) iat = df.iat if dim == 1 or len(df.columns.unique()) == len(df.columns) else df.iloc diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py index c4d17e364cf7..6720c457202b 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_vars.py @@ -4,9 +4,9 @@ import math import pickle -from _pydev_imps._pydev_saved_modules import thread from _pydev_bundle.pydev_imports import quote -from _pydevd_bundle.pydevd_constants import get_frame, get_current_thread_id, xrange, NUMPY_NUMERIC_TYPES +from _pydev_imps._pydev_saved_modules import thread +from _pydevd_bundle.pydevd_constants import get_frame, get_current_thread_id, xrange, NUMPY_NUMERIC_TYPES, NUMPY_FLOATING_POINT_TYPES from _pydevd_bundle.pydevd_custom_frames import get_custom_frame from _pydevd_bundle.pydevd_xml import ExceptionOnEvaluate, get_type, var_to_xml @@ -28,6 +28,7 @@ from _pydev_bundle.pydev_imports import Exec, execfile from _pydevd_bundle.pydevd_utils import to_string, VariableWithOffset SENTINEL_VALUE = [] +DEFAULT_DF_FORMAT = "s" # ------------------------------------------------------------------------------------------------------ class for errors @@ -584,6 +585,16 @@ def array_to_meta_xml(array, name, format): return array, slice_to_xml(slice, rows, cols, format, type, bounds), rows, cols, format +def get_column_formatter_by_type(initial_format, column_type): + if column_type in NUMPY_NUMERIC_TYPES and initial_format: + if column_type in NUMPY_FLOATING_POINT_TYPES and initial_format.strip() == DEFAULT_DF_FORMAT: + # use custom formatting for floats when default formatting is set + return array_default_format(column_type) + return initial_format + else: + return array_default_format(column_type) + + def array_default_format(type): if type == 'f': return '.5f' @@ -625,7 +636,7 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): kind = 'O' format = array_default_format(kind) else: - format = array_default_format('f') + format = array_default_format(DEFAULT_DF_FORMAT) xml = slice_to_xml(name, num_rows, num_cols, format, "", (0, 0)) @@ -657,7 +668,7 @@ def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format): cols = df.shape[1] if dim > 1 else 1 def col_to_format(c): - return format if dtypes[c] in NUMPY_NUMERIC_TYPES and format else array_default_format(dtypes[c]) + return get_column_formatter_by_type(format, dtypes[c]) iat = df.iat if dim == 1 or len(df.columns.unique()) == len(df.columns) else df.iloc diff --git a/python/testData/debug/test_dataframe.py b/python/testData/debug/test_dataframe.py index b0995ab3463f..f2f7e24cf4ac 100644 --- a/python/testData/debug/test_dataframe.py +++ b/python/testData/debug/test_dataframe.py @@ -2,8 +2,8 @@ import pandas as pd import numpy as np df1 = pd.DataFrame({'row': [0, 1, 2], 'One_X': [1.1, 1.1, 1.1], - 'One_Y': [1.2, 1.2, 1.2], - 'Two_X': [1.11, 1.11, 1.11], + 'Year': [2018, 2019, 2020], + 'Winner': [True, False, True], 'Two_Y': [1.22, 1.22, 1.22]}) print(df1) ###line 8 diff --git a/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java index 1f5c7ec79438..a3db8cb8caf5 100644 --- a/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java +++ b/python/testSrc/com/jetbrains/env/python/PythonDataViewerTest.java @@ -111,15 +111,33 @@ public class PythonDataViewerTest extends PyEnvTestCase { @Test @Staging - public void testDataFrameFormatting() { + public void testDataFrameFloatFormatting() { runPythonTest(new PyDataFrameDebuggerTask(getRelativeTestDataPath(), "test_dataframe.py", ImmutableSet.of(7)) { @Override public void testing() throws Exception { doTest("df1", 3, 5, (varName, session) -> getChunk(varName, "%.2f", session), arrayChunk -> { Object[][] data = arrayChunk.getData(); assertEquals("'1.10'", data[0][1].toString()); - assertEquals("'1.20'", data[0][2].toString()); assertEquals("'1.22'", data[1][4].toString()); + assertEquals("'2019.00'", data[1][2].toString()); + assertEquals("'1.00'", data[2][3].toString()); + }); + } + }); + } + + @Test + @Staging + public void testDataFrameDefaultFormatting() { + runPythonTest(new PyDataFrameDebuggerTask(getRelativeTestDataPath(), "test_dataframe.py", ImmutableSet.of(7)) { + @Override + public void testing() throws Exception { + doTest("df1", 3, 5, (varName, session) -> getChunk(varName, "%", session), arrayChunk -> { + Object[][] data = arrayChunk.getData(); + assertEquals("'1.10000'", data[0][1].toString()); + assertEquals("'1.22000'", data[1][4].toString()); + assertEquals("'2019'", data[1][2].toString()); + assertEquals("'True'", data[2][3].toString()); }); } });