mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[debugger] PY-84040 add custom variable rendering and variable view features to python dap, like it looks in default pydevd
maximpopov/PY-84040-debugpy-Implement-custom-variable-renderers-(like-it-looks-in-PyCharm) Merge-request: IJ-MR-180042 Merged-by: Maxim Popov <maxim.popov@jetbrains.com> GitOrigin-RevId: 42bdef24e1935592a14610e6a9dd80f70143e5dc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
140fdd8d75
commit
5e2fb82872
@@ -1,3 +1,5 @@
|
||||
import sys
|
||||
|
||||
try:
|
||||
xrange = xrange
|
||||
except:
|
||||
@@ -6,3 +8,105 @@ except:
|
||||
|
||||
NUMPY_NUMERIC_TYPES = "biufc"
|
||||
NUMPY_FLOATING_POINT_TYPES = "fc"
|
||||
IS_PYCHARM = True
|
||||
|
||||
#=======================================================================================================================
|
||||
# Python 3?
|
||||
#=======================================================================================================================
|
||||
IS_PY3K = False
|
||||
IS_PY34_OR_GREATER = False
|
||||
IS_PY36_OR_GREATER = False
|
||||
IS_PY37_OR_GREATER = False
|
||||
IS_PY36_OR_LESSER = False
|
||||
IS_PY38_OR_GREATER = False
|
||||
IS_PY38 = False
|
||||
IS_PY39 = False
|
||||
IS_PY39_OR_GREATER = False
|
||||
IS_PY310 = False
|
||||
IS_PY310_OR_GREATER = False
|
||||
IS_PY311 = False
|
||||
IS_PY311_OR_GREATER = False
|
||||
IS_PY312_OR_GREATER = False
|
||||
IS_PY312_OR_LESSER = False
|
||||
IS_PY313 = False
|
||||
IS_PY313_OR_GREATER = False
|
||||
IS_PY313_OR_LESSER = False
|
||||
IS_PY314 = False
|
||||
IS_PY2 = True
|
||||
IS_PY27 = False
|
||||
IS_PY24 = False
|
||||
try:
|
||||
if sys.version_info[0] >= 3:
|
||||
IS_PY3K = True
|
||||
IS_PY2 = False
|
||||
IS_PY34_OR_GREATER = sys.version_info >= (3, 4)
|
||||
IS_PY36_OR_GREATER = sys.version_info >= (3, 6)
|
||||
IS_PY37_OR_GREATER = sys.version_info >= (3, 7)
|
||||
IS_PY36_OR_LESSER = sys.version_info[:2] <= (3, 6)
|
||||
IS_PY38 = sys.version_info[0] == 3 and sys.version_info[1] == 8
|
||||
IS_PY38_OR_GREATER = sys.version_info >= (3, 8)
|
||||
IS_PY39 = sys.version_info[0] == 3 and sys.version_info[1] == 9
|
||||
IS_PY39_OR_GREATER = sys.version_info >= (3, 9)
|
||||
IS_PY310 = sys.version_info[0] == 3 and sys.version_info[1] == 10
|
||||
IS_PY310_OR_GREATER = sys.version_info >= (3, 10)
|
||||
IS_PY311 = sys.version_info[0] == 3 and sys.version_info[1] == 11
|
||||
IS_PY311_OR_GREATER = sys.version_info >= (3, 11)
|
||||
IS_PY312_OR_GREATER = sys.version_info >= (3, 12)
|
||||
IS_PY312_OR_LESSER = sys.version_info[:2] <= (3, 12)
|
||||
IS_PY313 = sys.version_info[0] == 3 and sys.version_info[1] == 13
|
||||
IS_PY313_OR_GREATER = sys.version_info >= (3, 13)
|
||||
IS_PY313_OR_LESSER = sys.version_info[:2] <= (3, 13)
|
||||
IS_PY314 = sys.version_info[0] == 3 and sys.version_info[1] == 14
|
||||
elif sys.version_info[0] == 2 and sys.version_info[1] == 7:
|
||||
IS_PY27 = True
|
||||
elif sys.version_info[0] == 2 and sys.version_info[1] == 4:
|
||||
IS_PY24 = True
|
||||
except AttributeError:
|
||||
pass # Not all versions have sys.version_info
|
||||
|
||||
|
||||
if IS_PY3K:
|
||||
|
||||
def dict_keys(d):
|
||||
return list(d.keys())
|
||||
|
||||
def dict_values(d):
|
||||
return list(d.values())
|
||||
|
||||
dict_iter_values = dict.values
|
||||
|
||||
def dict_iter_items(d):
|
||||
return d.items()
|
||||
|
||||
def dict_items(d):
|
||||
return list(d.items())
|
||||
|
||||
else:
|
||||
def dict_keys(d):
|
||||
return d.keys()
|
||||
|
||||
try:
|
||||
dict_iter_values = dict.itervalues
|
||||
except:
|
||||
try:
|
||||
dict_iter_values = dict.values # Older versions don't have the itervalues
|
||||
except:
|
||||
|
||||
def dict_iter_values(d):
|
||||
return d.values()
|
||||
|
||||
try:
|
||||
dict_values = dict.values
|
||||
except:
|
||||
|
||||
def dict_values(d):
|
||||
return d.values()
|
||||
|
||||
def dict_iter_items(d):
|
||||
try:
|
||||
return d.iteritems()
|
||||
except:
|
||||
return d.items()
|
||||
|
||||
def dict_items(d):
|
||||
return d.items()
|
||||
@@ -0,0 +1,283 @@
|
||||
# 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.custom.pydevd_constants import IS_PY3K
|
||||
from _pydevd_bundle.custom.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__
|
||||
@@ -1,3 +1,14 @@
|
||||
from array import array
|
||||
from collections import deque
|
||||
|
||||
from _pydevd_bundle.custom.pydevd_asyncio_provider import \
|
||||
get_eval_async_expression_in_context
|
||||
from _pydevd_bundle.custom.pydevd_constants import dict_iter_items
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except:
|
||||
OrderedDict = dict
|
||||
|
||||
class VariableWithOffset(object):
|
||||
def __init__(self, data, offset):
|
||||
self.data, self.offset = data, offset
|
||||
@@ -9,3 +20,28 @@ def eval_expression(expression, globals, locals):
|
||||
return eval_func(expression, globals, locals, False)
|
||||
|
||||
return eval(expression, globals, locals)
|
||||
|
||||
def get_var_and_offset(var):
|
||||
if isinstance(var, VariableWithOffset):
|
||||
return var.data, var.offset
|
||||
return var, 0
|
||||
|
||||
def take_first_n_coll_elements(coll, n):
|
||||
if coll.__class__ in (list, tuple, array, str):
|
||||
return coll[:n]
|
||||
elif coll.__class__ in (set, frozenset, deque):
|
||||
buf = []
|
||||
for i, x in enumerate(coll):
|
||||
if i >= n:
|
||||
break
|
||||
buf.append(x)
|
||||
return type(coll)(buf)
|
||||
elif coll.__class__ in (dict, OrderedDict):
|
||||
ret = type(coll)()
|
||||
for i, (k, v) in enumerate(dict_iter_items(coll)):
|
||||
if i >= n:
|
||||
break
|
||||
ret[k] = v
|
||||
return ret
|
||||
else:
|
||||
raise TypeError("Unsupported collection type: '%s'" % str(coll.__class__))
|
||||
|
||||
@@ -810,7 +810,7 @@ def get_var_scope(attr_name, attr_value, evaluate_name, handle_return_values):
|
||||
elif attr_name == GENERATED_LEN_ATTR_NAME:
|
||||
return ""
|
||||
|
||||
if attr_name.startswith("__") and attr_name.endswith("__"):
|
||||
if (attr_name.startswith("__") and attr_name.endswith("__")) or inspect.ismodule(attr_value):
|
||||
return DAPGrouper.SCOPE_SPECIAL_VARS
|
||||
|
||||
if attr_name.startswith("_") or attr_name.endswith("__"):
|
||||
@@ -818,10 +818,10 @@ def get_var_scope(attr_name, attr_value, evaluate_name, handle_return_values):
|
||||
|
||||
try:
|
||||
if inspect.isroutine(attr_value) or isinstance(attr_value, MethodWrapperType):
|
||||
return DAPGrouper.SCOPE_FUNCTION_VARS
|
||||
return DAPGrouper.SCOPE_SPECIAL_VARS # note: we changed the scope here to correspond the scopes of pycharm's pydevd
|
||||
|
||||
elif inspect.isclass(attr_value):
|
||||
return DAPGrouper.SCOPE_CLASS_VARS
|
||||
return DAPGrouper.SCOPE_SPECIAL_VARS # note: we changed the scope here to correspond the scopes of pycharm's pydevd
|
||||
except:
|
||||
# It's possible that isinstance throws an exception when dealing with user-code.
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL > 0:
|
||||
|
||||
@@ -330,10 +330,10 @@ class DAPGrouper(object):
|
||||
the xml protocol the type is just added to each variable and the UI can group/hide it as needed.
|
||||
"""
|
||||
|
||||
SCOPE_SPECIAL_VARS = "special variables"
|
||||
SCOPE_PROTECTED_VARS = "protected variables"
|
||||
SCOPE_FUNCTION_VARS = "function variables"
|
||||
SCOPE_CLASS_VARS = "class variables"
|
||||
SCOPE_SPECIAL_VARS = "Special Variables"
|
||||
SCOPE_PROTECTED_VARS = "Protected Attributes"
|
||||
SCOPE_FUNCTION_VARS = "Function Variables"
|
||||
SCOPE_CLASS_VARS = "Class Variables"
|
||||
|
||||
SCOPES_SORTED = [
|
||||
SCOPE_SPECIAL_VARS,
|
||||
|
||||
+111
-37
@@ -1,10 +1,23 @@
|
||||
from _pydevd_bundle.pydevd_extension_api import TypeResolveProvider
|
||||
from _pydevd_bundle.custom.pydevd_utils import get_var_and_offset
|
||||
from _pydevd_bundle.pydevd_extension_api import TypeResolveProvider, \
|
||||
StrPresentationProvider
|
||||
from _pydevd_bundle.pydevd_resolver import defaultResolver
|
||||
from .pydevd_helpers import find_mod_attr
|
||||
from _pydevd_bundle import pydevd_constants
|
||||
from _pydevd_bundle.custom.pydevd_repr_utils import get_value_repr
|
||||
|
||||
import inspect
|
||||
|
||||
try:
|
||||
from collections import OrderedDict
|
||||
except:
|
||||
OrderedDict = dict
|
||||
|
||||
|
||||
TOO_LARGE_MSG = "Maximum number of items (%s) reached. To show more items customize the value of the PYDEVD_CONTAINER_NUMPY_MAX_ITEMS environment variable."
|
||||
TOO_LARGE_ATTR = "Unable to handle:"
|
||||
IS_PYCHARM = True
|
||||
MAX_ITEMS_TO_HANDLE = 300 if not IS_PYCHARM else 100
|
||||
DEFAULT_PRECISION = 5
|
||||
|
||||
|
||||
class NdArrayItemsContainer(object):
|
||||
@@ -17,74 +30,135 @@ class NDArrayTypeResolveProvider(object):
|
||||
"""
|
||||
|
||||
def can_provide(self, type_object, type_name):
|
||||
nd_array = find_mod_attr("numpy", "ndarray")
|
||||
return nd_array is not None and issubclass(type_object, nd_array)
|
||||
nd_array = find_mod_attr('numpy', 'ndarray')
|
||||
return nd_array is not None and inspect.isclass(type_object) and issubclass(type_object, nd_array)
|
||||
|
||||
'''
|
||||
This resolves a numpy ndarray returning some metadata about the NDArray
|
||||
'''
|
||||
|
||||
def is_numeric(self, obj):
|
||||
if not hasattr(obj, "dtype"):
|
||||
if not hasattr(obj, 'dtype'):
|
||||
return False
|
||||
return obj.dtype.kind in "biufc"
|
||||
return obj.dtype.kind in 'biufc'
|
||||
|
||||
def round_if_possible(self, obj):
|
||||
try:
|
||||
return obj.round(DEFAULT_PRECISION)
|
||||
except TypeError:
|
||||
return obj
|
||||
|
||||
def resolve(self, obj, attribute):
|
||||
if attribute == "__internals__":
|
||||
return defaultResolver.get_dictionary(obj)
|
||||
if attribute == "min":
|
||||
if self.is_numeric(obj) and obj.size > 0:
|
||||
if attribute == '__internals__':
|
||||
if not IS_PYCHARM:
|
||||
return defaultResolver.get_dictionary(obj)
|
||||
if attribute == 'min':
|
||||
if self.is_numeric(obj):
|
||||
return obj.min()
|
||||
else:
|
||||
return None
|
||||
if attribute == "max":
|
||||
if self.is_numeric(obj) and obj.size > 0:
|
||||
if attribute == 'max':
|
||||
if self.is_numeric(obj):
|
||||
return obj.max()
|
||||
else:
|
||||
return None
|
||||
if attribute == "shape":
|
||||
if attribute == 'shape':
|
||||
return obj.shape
|
||||
if attribute == "dtype":
|
||||
if attribute == 'dtype':
|
||||
return obj.dtype
|
||||
if attribute == "size":
|
||||
if attribute == 'size':
|
||||
return obj.size
|
||||
if attribute.startswith("["):
|
||||
if attribute.startswith('['):
|
||||
container = NdArrayItemsContainer()
|
||||
i = 0
|
||||
format_str = "%0" + str(int(len(str(len(obj))))) + "d"
|
||||
format_str = '%0' + str(int(len(str(len(obj))))) + 'd'
|
||||
for item in obj:
|
||||
setattr(container, format_str % i, item)
|
||||
i += 1
|
||||
if i >= pydevd_constants.PYDEVD_CONTAINER_NUMPY_MAX_ITEMS:
|
||||
setattr(container, TOO_LARGE_ATTR, TOO_LARGE_MSG % (pydevd_constants.PYDEVD_CONTAINER_NUMPY_MAX_ITEMS,))
|
||||
if i > MAX_ITEMS_TO_HANDLE:
|
||||
setattr(container, TOO_LARGE_ATTR, TOO_LARGE_MSG)
|
||||
break
|
||||
return container
|
||||
if IS_PYCHARM and attribute == 'array':
|
||||
container = NdArrayItemsContainer()
|
||||
container.items = obj
|
||||
return container
|
||||
return None
|
||||
|
||||
def get_dictionary(self, obj):
|
||||
ret = dict()
|
||||
ret["__internals__"] = defaultResolver.get_dictionary(obj)
|
||||
if not IS_PYCHARM:
|
||||
ret['__internals__'] = defaultResolver.get_dictionary(obj)
|
||||
if obj.size > 1024 * 1024:
|
||||
ret["min"] = "ndarray too big, calculating min would slow down debugging"
|
||||
ret["max"] = "ndarray too big, calculating max would slow down debugging"
|
||||
elif obj.size == 0:
|
||||
ret["min"] = "array is empty"
|
||||
ret["max"] = "array is empty"
|
||||
ret['min'] = 'ndarray too big, calculating min would slow down debugging'
|
||||
ret['max'] = 'ndarray too big, calculating max would slow down debugging'
|
||||
else:
|
||||
if self.is_numeric(obj):
|
||||
ret["min"] = obj.min()
|
||||
ret["max"] = obj.max()
|
||||
ret['min'] = obj.min()
|
||||
ret['max'] = obj.max()
|
||||
else:
|
||||
ret["min"] = "not a numeric object"
|
||||
ret["max"] = "not a numeric object"
|
||||
ret["shape"] = obj.shape
|
||||
ret["dtype"] = obj.dtype
|
||||
ret["size"] = obj.size
|
||||
try:
|
||||
ret["[0:%s] " % (len(obj))] = list(obj[0 : pydevd_constants.PYDEVD_CONTAINER_NUMPY_MAX_ITEMS])
|
||||
except:
|
||||
# This may not work depending on the array shape.
|
||||
pass
|
||||
ret['min'] = 'not a numeric object'
|
||||
ret['max'] = 'not a numeric object'
|
||||
ret['shape'] = obj.shape
|
||||
ret['dtype'] = obj.dtype
|
||||
ret['size'] = obj.size
|
||||
if IS_PYCHARM:
|
||||
container = NdArrayItemsContainer()
|
||||
container.items = obj
|
||||
ret['array'] = container
|
||||
else:
|
||||
ret['[0:%s] ' % (len(obj))] = list(obj[0:MAX_ITEMS_TO_HANDLE])
|
||||
return ret
|
||||
|
||||
class NDArrayStrProvider(StrPresentationProvider):
|
||||
def can_provide(self, type_object, type_name):
|
||||
nd_array = find_mod_attr('numpy', 'ndarray')
|
||||
return nd_array is not None and inspect.isclass(type_object) and issubclass(type_object, nd_array)
|
||||
|
||||
def _to_str_no_trim(self, val):
|
||||
return str(val.tolist()).replace('\n', ',').strip()
|
||||
|
||||
def get_str(self, val, do_trim=True):
|
||||
if do_trim:
|
||||
return get_value_repr(val)
|
||||
try:
|
||||
import numpy as np
|
||||
with np.printoptions(threshold=sys.maxsize):
|
||||
return self._to_str_no_trim(val)
|
||||
except:
|
||||
return self._to_str_no_trim(val)
|
||||
|
||||
class NdArrayItemsContainerProvider(object):
|
||||
def can_provide(self, type_object, type_name):
|
||||
return inspect.isclass(type_object) and issubclass(type_object, NdArrayItemsContainer)
|
||||
|
||||
def resolve(self, obj, attribute):
|
||||
if attribute == '__len__':
|
||||
return None
|
||||
return obj.items[int(attribute)]
|
||||
|
||||
def get_dictionary(self, obj):
|
||||
obj, offset = get_var_and_offset(obj)
|
||||
|
||||
l = len(obj.items)
|
||||
d = OrderedDict()
|
||||
|
||||
format_str = '%0' + str(int(len(str(l)))) + 'd'
|
||||
|
||||
i = offset
|
||||
for item in obj.items[offset:offset + MAX_ITEMS_TO_HANDLE]:
|
||||
d[format_str % i] = item
|
||||
i += 1
|
||||
|
||||
if i > MAX_ITEMS_TO_HANDLE + offset:
|
||||
break
|
||||
d['__len__'] = l
|
||||
return d
|
||||
|
||||
import sys
|
||||
|
||||
if not sys.platform.startswith("java"):
|
||||
TypeResolveProvider.register(NDArrayTypeResolveProvider)
|
||||
if IS_PYCHARM:
|
||||
TypeResolveProvider.register(NdArrayItemsContainerProvider)
|
||||
StrPresentationProvider.register(NDArrayStrProvider)
|
||||
|
||||
+27
-8
@@ -1,5 +1,6 @@
|
||||
import sys
|
||||
|
||||
from _pydevd_bundle.custom.pydevd_repr_utils import get_value_repr
|
||||
from _pydevd_bundle.pydevd_constants import PANDAS_MAX_ROWS, PANDAS_MAX_COLS, PANDAS_MAX_COLWIDTH
|
||||
from _pydevd_bundle.pydevd_extension_api import TypeResolveProvider, StrPresentationProvider
|
||||
from _pydevd_bundle.pydevd_resolver import inspect, MethodWrapperType
|
||||
@@ -88,7 +89,7 @@ class PandasDataFrameTypeResolveProvider(object):
|
||||
replacements = {
|
||||
# This actually calls: DataFrame.transpose(), which can be expensive, so,
|
||||
# let's just add some string representation for it.
|
||||
"T": "<transposed dataframe -- debugger:skipped eval>",
|
||||
# "T": "<transposed dataframe -- debugger:skipped eval>",
|
||||
# This creates a whole new dict{index: Series) for each column. Doing a
|
||||
# subsequent repr() from this dict can be very slow, so, don't return it.
|
||||
"_series": "<dict[index:Series] -- debugger:skipped eval>",
|
||||
@@ -109,9 +110,18 @@ class PandasDataFrameTypeResolveProvider(object):
|
||||
return repr(df)
|
||||
return self.get_str(df)
|
||||
|
||||
def get_str(self, df):
|
||||
with customize_pandas_options():
|
||||
return repr(df)
|
||||
def _to_str_no_trim(self, val):
|
||||
return str(val.tolist()).replace('\n', ',').strip()
|
||||
|
||||
def get_str(self, val, do_trim=True):
|
||||
if do_trim:
|
||||
return get_value_repr(val)
|
||||
try:
|
||||
import numpy as np
|
||||
with np.printoptions(threshold=sys.maxsize):
|
||||
return self._to_str_no_trim(val)
|
||||
except:
|
||||
return self._to_str_no_trim(val)
|
||||
|
||||
|
||||
class PandasSeriesTypeResolveProvider(object):
|
||||
@@ -126,7 +136,7 @@ class PandasSeriesTypeResolveProvider(object):
|
||||
replacements = {
|
||||
# This actually calls: DataFrame.transpose(), which can be expensive, so,
|
||||
# let's just add some string representation for it.
|
||||
"T": "<transposed dataframe -- debugger:skipped eval>",
|
||||
# "T": "<transposed dataframe -- debugger:skipped eval>",
|
||||
# This creates a whole new dict{index: Series) for each column. Doing a
|
||||
# subsequent repr() from this dict can be very slow, so, don't return it.
|
||||
"_series": "<dict[index:Series] -- debugger:skipped eval>",
|
||||
@@ -147,9 +157,18 @@ class PandasSeriesTypeResolveProvider(object):
|
||||
return repr(df)
|
||||
return self.get_str(df)
|
||||
|
||||
def get_str(self, series):
|
||||
with customize_pandas_options():
|
||||
return repr(series)
|
||||
def _to_str_no_trim(self, val):
|
||||
return str(val.tolist()).replace('\n', ',').strip()
|
||||
|
||||
def get_str(self, val, do_trim=True):
|
||||
if do_trim:
|
||||
return get_value_repr(val)
|
||||
try:
|
||||
import numpy as np
|
||||
with np.printoptions(threshold=sys.maxsize):
|
||||
return self._to_str_no_trim(val)
|
||||
except:
|
||||
return self._to_str_no_trim(val)
|
||||
|
||||
|
||||
class PandasStylerTypeResolveProvider(object):
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
from typing import Any
|
||||
|
||||
from _pydevd_bundle.custom.pydevd_repr_utils import get_value_repr
|
||||
from _pydevd_bundle.pydevd_extension_api import StrPresentationProvider
|
||||
from pydevd_plugins.extensions.types.pydevd_plugin_numpy_types import NDArrayStrProvider
|
||||
from pydevd_plugins.extensions.types.pydevd_plugins_django_form_str import DjangoFormStr
|
||||
|
||||
|
||||
class PydevdReprStrProvider(StrPresentationProvider):
|
||||
def can_provide(self, type_object, type_name):
|
||||
# this provider can resolve anything that is not otherwise custom handled by our
|
||||
# other custom resolvers, such as NDArrayStrProvider
|
||||
return (not NDArrayStrProvider.can_provide(self, type_object, type_name)
|
||||
and not DjangoFormStr.can_provide(self, type_object, type_name))
|
||||
|
||||
def get_str_in_context(self, val: Any, context: str):
|
||||
return self.get_str(val)
|
||||
|
||||
def get_str(self, val, do_trim=True):
|
||||
return get_value_repr(val)
|
||||
|
||||
Reference in New Issue
Block a user