Files
openide/python/helpers/pydev/_pydevd_bundle/pydevd_repr_utils.py
Egor Eliseev 23780d7a8d PY-77038 __repr__ value is truncated on copy
Merge-request: IJ-MR-157669
Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com>
(cherry picked from commit 5b27f0dc362d8c023c8933dd22fe36b12c83e7e7)


Merge-request: IJ-MR-159313
Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com>

GitOrigin-RevId: 4cdf4806ca54bb35fb42d977e1f43e7be82bbd43
2025-04-03 16:35:54 +00:00

284 lines
9.4 KiB
Python

# Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
from array import array
from collections import deque
from _pydev_bundle import pydev_log
from _pydevd_bundle.pydevd_constants import IS_PY3K
from _pydevd_bundle.pydevd_utils import take_first_n_coll_elements
# Maximum final result string length
MAX_REPR_LENGTH = 1000
# Maximum number of value elements
MAX_REPR_ITEM_SIZE = 256
DEFAULT_FORMAT = '%s'
def _get_ndarray_variable_repr(num_array, max_items=MAX_REPR_ITEM_SIZE):
# ndarray.__str__() is already optimised and works fast enough
if num_array.ndim == 0:
return str(num_array).replace('\n', ',').strip()
else:
return str(num_array[:max_items]).replace('\n', ',').strip()
def _get_series_variable_repr(series, max_items=MAX_REPR_ITEM_SIZE):
res = []
total_length = 0
series = series.iloc[:max_items]
for item in series.items():
# item: (index, value)
item_repr = str(item)
res.append(item_repr)
total_length += len(item_repr)
if total_length > MAX_REPR_LENGTH:
break
return ' '.join(res)
def _get_df_variable_repr(data_frame):
# Avoid using df.iteritems() or df.values[i], because it works very slow for
# large data frames. df.__str__() is already optimised and works fast enough.
data_preview = []
column_row = 0
shape_rows, shape_cols = data_frame.shape
if shape_cols > 1000 or shape_rows > 10000:
head_number = 1
else:
head_number = 3
rows = str(data_frame.head(head_number)).split('\n')
for (i, r) in enumerate(rows):
if i != column_row:
data_preview.append("[%s]" % r)
if r == '':
column_row = i + 1
# The string provided is used for column name completion
# by JupyterVarsFrameExecutor.parseFrameVars
return '%s %s' % (list(data_frame.columns), ' '.join(data_preview))
def _trim_string_repr_if_needed(value, do_trim=True, max_length=MAX_REPR_LENGTH):
if len(value) > max_length and do_trim:
value = value[:max_length]
value += '...'
return value
def _get_external_collection_repr(collection, raise_exception=False):
typename = type(collection).__name__
typename_with_package = type(collection)
# pandas var
try:
if typename == "Series" or typename == "GeoSeries":
return _get_series_variable_repr(collection)
if typename == "DataFrame" or typename == "GeoDataFrame":
return _get_df_variable_repr(collection)
except Exception as e:
pydev_log.warn("Failed to format pandas variable: " + str(e))
if raise_exception:
raise e
# ndarray and other numpy types
try:
if typename == 'ndarray' or "numpy." in str(typename_with_package):
return _get_ndarray_variable_repr(collection)
except Exception as e:
pydev_log.warn("Failed to format numpy ndarray: " + str(e))
if raise_exception:
raise e
return None
pydevd_repr_function_python2 = None
if IS_PY3K:
from reprlib import Repr
from itertools import islice
def _possibly_sorted(x):
# Since not all sequences of items can be sorted and comparison
# functions may raise arbitrary exceptions, return an unsorted
# sequence in that case.
try:
return sorted(x)
except Exception:
return list(x)
class PydevdRepr(Repr):
def __init__(self, do_trim):
super(PydevdRepr, self).__init__()
self.fillvalue = '...'
self.maxdict = MAX_REPR_ITEM_SIZE
self.maxlist = MAX_REPR_ITEM_SIZE
self.maxtuple = MAX_REPR_ITEM_SIZE
self.maxset = MAX_REPR_ITEM_SIZE
self.maxfrozenset = MAX_REPR_ITEM_SIZE
self.maxdeque = MAX_REPR_ITEM_SIZE
self.maxarray = MAX_REPR_ITEM_SIZE
self.maxlong = MAX_REPR_ITEM_SIZE
self.maxstring = MAX_REPR_ITEM_SIZE
self.maxother = MAX_REPR_ITEM_SIZE
self.do_trim = do_trim
def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
n = len(x)
if level <= 0 and n:
s = self.fillvalue
else:
newlevel = level - 1
repr1 = self.repr1
pieces = []
curr_length = 0
max_elements = maxiter if self.do_trim else n
for elem in islice(x, max_elements):
elem_repr = repr1(elem, newlevel)
curr_length += len(elem_repr)
pieces.append(elem_repr)
if curr_length >= MAX_REPR_LENGTH and self.do_trim:
break
if (n > maxiter or curr_length >= MAX_REPR_LENGTH) and self.do_trim:
pieces.append(self.fillvalue)
s = ', '.join(pieces)
if n == 1 and trail:
right = trail + right
return '%s%s%s' % (left, s, right)
def repr_str(self, x, level):
if level == self.maxlevel:
if self.do_trim:
return x[:self.maxstring]
else:
return x
else:
if self.do_trim:
return super().repr_str(x, level)
else:
return "'{x}'".format(x=x)
def repr_dict(self, x, level):
n = len(x)
if n == 0: return '{}'
if level <= 0: return '{...}'
newlevel = level - 1
repr1 = self.repr1
pieces = []
curr_length = 0
max_elements = self.maxdict if self.do_trim else n
for key in islice(_possibly_sorted(x), max_elements):
keyrepr = repr1(key, newlevel)
valrepr = repr1(x[key], newlevel)
elem_repr = '%s: %s' % (keyrepr, valrepr)
pieces.append(elem_repr)
curr_length += len(elem_repr)
if curr_length >= MAX_REPR_LENGTH and self.do_trim:
break
if (n > self.maxdict or curr_length >= MAX_REPR_LENGTH) and self.do_trim:
pieces.append(self.fillvalue)
s = ', '.join(pieces)
return '{%s}' % (s,)
def repr_instance(self, x, level):
# pandas series, ds | ndarray
result = _get_external_collection_repr(x)
if result is not None:
return result
# if `__repr__` is overridden, then use `reprlib`
if x.__class__.__repr__ != object.__repr__:
if self.do_trim:
return super().repr_instance(x, level)
return repr(x)
# if `__str__` is overridden, then return str(x)
if x.__class__.__str__ != object.__str__:
return str(x)
if self.do_trim:
return super().repr_instance(x, level)
return '%s' % x
else:
def pydevd_repr_function(value, do_trim=True):
# pandas series, ds | ndarray
result = _get_external_collection_repr(value, True)
if result is not None:
return result
limited_size_collection_classes = [
list, tuple, set, frozenset, dict, array, deque, str,
]
if IS_PY3K:
limited_size_collection_classes.append(bytes)
else:
limited_size_collection_classes.append(unicode)
if hasattr(value, '__class__'):
if value.__class__ in limited_size_collection_classes:
if len(value) > MAX_REPR_ITEM_SIZE and do_trim:
return ('%s' % take_first_n_coll_elements(value, MAX_REPR_ITEM_SIZE)).rstrip(')]}') + '...'
return None
# if `__repr__` is overridden, then return repr(value)
if hasattr(value.__class__, "__repr__"):
if do_trim:
return repr(value)[:MAX_REPR_LENGTH]
else:
return repr(value)
# else
if do_trim:
return str(value)[:MAX_REPR_LENGTH]
else:
return str(value)
pydevd_repr_function_python2 = pydevd_repr_function
def get_value_repr(value, do_trim=True, format=DEFAULT_FORMAT):
"""
Returns string representation of any value
:param value: target value
:param bool do_trim: is truncated representation
:param str format: formatting string (format % value)
:return: string representation of target value
:rtype: str
"""
value_representation = None
try:
try:
if format != DEFAULT_FORMAT:
value_representation = format % value
else:
if IS_PY3K:
pydevd_repr_fun = PydevdRepr(do_trim).repr
value_representation = pydevd_repr_fun(value)
else:
value_representation = pydevd_repr_function_python2(value, do_trim)
except Exception as e:
pydev_log.warn("Failed to get repr for a value: " + str(e))
if value_representation is None:
value_representation = format % value
if do_trim:
return _trim_string_repr_if_needed(value_representation, do_trim)
else:
return value_representation
except:
try:
return _trim_string_repr_if_needed(repr(value), do_trim)
except:
return 'Unable to get repr for %s' % value.__class__