From db9dd95e6bb6452c2684be02ece479d8354069d4 Mon Sep 17 00:00:00 2001 From: "maxim.popov" Date: Thu, 26 Jun 2025 15:31:12 +0200 Subject: [PATCH] PY-48306 [debugger] [IJ-MR-167160] added type check for objects, when accessing dtype. restricted to certain container types (cherry picked from commit 60808c3e70d39aad2efee45fa120eb08a8828079) IJ-MR-172080 GitOrigin-RevId: edada7f5ecf70fe905ff7fe1b56531fe311d668c --- .../pydev/_pydevd_bundle/pydevd_thrift.py | 11 +++-- .../pydev/_pydevd_bundle/pydevd_utils.py | 40 ++++++++++++++++++- .../pydev/_pydevd_bundle/pydevd_xml.py | 8 ++-- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_thrift.py b/python/helpers/pydev/_pydevd_bundle/pydevd_thrift.py index 0aafbab238c9..b34d0fed7bd6 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, GET_FRAME_RETURN_GROUP from _pydevd_bundle.pydevd_extension_api import TypeResolveProvider, StrPresentationProvider from _pydevd_bundle.pydevd_user_type_renderers_utils import try_get_type_renderer_for_var -from _pydevd_bundle.pydevd_utils import is_string, should_evaluate_full_value, should_evaluate_shape +from _pydevd_bundle.pydevd_utils import is_string, should_evaluate_full_value, \ + should_evaluate_shape, is_container_with_shape_dtype from _pydevd_bundle.pydevd_vars import get_label, array_default_format, is_able_to_format_number, MAXIMUM_ARRAY_SIZE, \ get_column_formatter_by_type, get_formatted_row_elements, IAtPolarsAccessor, DEFAULT_DF_FORMAT, DATAFRAME_HEADER_LOAD_MAX_SIZE from pydev_console.pydev_protocol import DebugValue, GetArrayResponse, ArrayData, ArrayHeaders, ColHeader, RowHeader, \ @@ -333,7 +334,7 @@ def var_to_struct(val, name, format='%s', do_trim=True, evaluate_full_value=True # shape to struct try: - if should_evaluate_shape(): + if should_evaluate_shape() and is_container_with_shape_dtype(type_qualifier, typeName, v): if hasattr(v, 'shape') and not callable(v.shape): debug_value.shape = str(tuple(v.shape)) elif hasattr(v, '__len__') and not is_string(v): @@ -344,8 +345,10 @@ def var_to_struct(val, name, format='%s', do_trim=True, evaluate_full_value=True # data type info to xml (for arrays and tensors) debug_value.arrayElementType = '' try: - if hasattr(v, 'dtype') and hasattr(v.dtype, 'name'): - debug_value.arrayElementType = v.dtype.name + if (is_container_with_shape_dtype(type_qualifier, typeName, v) + and hasattr(v, 'dtype') + and hasattr(v.dtype, 'name')): + debug_value.arrayElementType = str(v.dtype) except: pass diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_utils.py b/python/helpers/pydev/_pydevd_bundle/pydevd_utils.py index 55c72c172552..3c8467dffb65 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_utils.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_utils.py @@ -5,6 +5,16 @@ import os import signal import traceback +try: + import torch +except ImportError: + pass + +try: + import tensorflow as tf +except ImportError: + pass + import pydevd_file_utils try: @@ -606,6 +616,24 @@ def is_pandas_container(type_qualifier, var_type, var): def is_numpy_container(type_qualifier, var_type, var): return var_type == "ndarray" and type_qualifier == "numpy" and hasattr(var, "shape") +def is_pytorch_tensor(type_qualifier, var): + try: + import torch + return type_qualifier == "torch" and torch.is_tensor(var) + except ImportError: + return False # Can't be torch if it is not installed + +def is_tf_tensor(type_qualifier, var): + try: + import tensorflow as tf + return type_qualifier.startswith("tensorflow") and tf.is_tensor(var) + except ImportError: + return False # Can't be tensorflow if it is not installed + +def is_container_with_shape_dtype(type_qualifier, var_type, var): + return (is_numpy_container(type_qualifier, var_type, var) + or is_pytorch_tensor(type_qualifier, var) + or is_tf_tensor(type_qualifier, var)) def is_builtin(x): return getattr(x, '__module__', None) == BUILTINS_MODULE_NAME @@ -640,11 +668,21 @@ def is_safe_to_access(obj, attr_name): of attribute access in the most risk-free manner. As an example, it leverages the `inspect` module, facilitating attribute retrieval without triggering any descriptor functionality. + + Note + ---- + This function performs a strict check for potential side-effects, the access can be safe even if `False` is returned. + This might need to be checked more precisely for some special types. """ + attr = inspect.getattr_static(obj, attr_name, None) + # Filter out objects that don't contain the given attribute + if attr is None: + return False + # Should we check for other descriptor types here? - if inspect.isgetsetdescriptor(attr): + if inspect.isgetsetdescriptor(attr) or isinstance(attr, property): return False return True diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_xml.py b/python/helpers/pydev/_pydevd_bundle/pydevd_xml.py index 4912e46d8448..00baf4680deb 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_xml.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_xml.py @@ -21,7 +21,7 @@ from _pydevd_bundle.pydevd_repr_utils import get_value_repr from _pydevd_bundle.pydevd_user_type_renderers_utils import \ try_get_type_renderer_for_var from _pydevd_bundle.pydevd_utils import is_string, should_evaluate_full_value, \ - should_evaluate_shape + should_evaluate_shape, is_safe_to_access, is_container_with_shape_dtype try: import types @@ -344,8 +344,7 @@ def var_to_xml(val, name, do_trim=True, additional_in_xml='', evaluate_full_valu # shape to xml xml_shape = '' try: - # if should_evaluate_shape() and is_safe_to_access(v, 'shape'): - if should_evaluate_shape(): + if should_evaluate_shape() and is_container_with_shape_dtype(type_qualifier, typeName, v): if hasattr(v, 'shape') and not callable(v.shape): xml_shape = ' shape="%s"' % make_valid_xml_value(str(tuple(v.shape))) elif hasattr(v, '__len__') and not is_string(v): @@ -357,7 +356,8 @@ def var_to_xml(val, name, do_trim=True, additional_in_xml='', evaluate_full_valu # we use it for view as image xml_data_type = '' try: - if hasattr(v, 'dtype') and hasattr(v.dtype, 'name'): + if (is_container_with_shape_dtype(type_qualifier, typeName, v) + and hasattr(v.dtype, 'name')): xml_data_type = ' arrayElementType="%s"' % make_valid_xml_value(v.dtype.name) except: pass