From 47fb2dfb01df95cdc711b88208ac652f78b4143e Mon Sep 17 00:00:00 2001 From: Scott Devoid Date: Mon, 20 Oct 2025 15:47:29 -0700 Subject: [PATCH] No public description PiperOrigin-RevId: 821837262 --- google/colab/_inspector.py | 16 +++++++++++++--- google/colab/_kernel.py | 20 ++++++++++++++++++-- google/colab/_shell.py | 32 +++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/google/colab/_inspector.py b/google/colab/_inspector.py index ef59a7cf..ee64ada4 100644 --- a/google/colab/_inspector.py +++ b/google/colab/_inspector.py @@ -23,6 +23,7 @@ import math import re import types +import warnings from IPython.core import oinspect from IPython.utils import dir2 @@ -475,6 +476,17 @@ def _getdef(self, obj, oname=''): except: # pylint: disable=bare-except logging.exception('Exception raised in ColabInspector._getdef') def info(self, obj, oname='', formatter=None, info=None, detail_level=0): + """Compute a dict with detailed information about an object.""" + if formatter is not None: + warnings.warn( + 'The `formatter` keyword argument to `Inspector.info`' + 'is deprecated as of IPython 5.0 and will have no effects.', + DeprecationWarning, + stacklevel=2, + ) + return self._info(obj, oname=oname, info=info, detail_level=detail_level) + + def _info(self, obj, oname='', info=None, detail_level=0): """Compute a dict with detailed information about an object. This overrides the superclass method for two main purposes: @@ -484,7 +496,6 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0): Args: obj: object to inspect. oname: (optional) string reference to this object - formatter: (optional) custom docstring formatter info: (optional) previously computed information about obj detail_level: (optional) 0 or 1; 1 means "include more detail" @@ -572,8 +583,7 @@ def info(self, obj, oname='', formatter=None, info=None, detail_level=0): if source is not None: out['source'] = source if 'source' not in out: - formatter = formatter or (lambda x: x) - docstring = formatter(getdoc(obj) or '') + docstring = getdoc(obj) or '' if docstring: out['docstring'] = docstring diff --git a/google/colab/_kernel.py b/google/colab/_kernel.py index 73eaab8b..fc25c889 100644 --- a/google/colab/_kernel.py +++ b/google/colab/_kernel.py @@ -19,6 +19,11 @@ from ipykernel import jsonutil from IPython.utils import tokenutil +# Only include info_text if less than 2 MiB. We have seen frontend lockup +# issues when this is very large. See b/401357469 for more details. +# 2 MiB is chosen as an arbitrary starting point. +_MAX_INSPECT_TEXT_SIZE = 2**20 # 2 MiB + class Kernel(ipkernel.IPythonKernel): """Kernel with additional Colab-specific features.""" @@ -31,7 +36,19 @@ def do_inspect(self, code, cursor_pos, detail_level=0, *args, **kwargs): info = self.shell.object_inspect(name) data = {} - if info['found']: + if info.get('found'): + info_text = self.shell.object_inspect_text( + name, detail_level=detail_level + ) + if len(info_text) < _MAX_INSPECT_TEXT_SIZE: + data['text/plain'] = info_text + else: + self.log.warning( + 'do_inspect text/plain output omitted as it was too large:' + ' size %d bytes', + len(info_text), + ) + # Provide the structured inspection information to allow the frontend to # format as desired. argspec = info.get('argspec') @@ -51,7 +68,6 @@ def do_inspect(self, code, cursor_pos, detail_level=0, *args, **kwargs): 'metadata': {}, 'found': info['found'], } - return reply_content def complete_request(self, stream, ident, parent): diff --git a/google/colab/_shell.py b/google/colab/_shell.py index 1471145c..31ab37e7 100644 --- a/google/colab/_shell.py +++ b/google/colab/_shell.py @@ -238,6 +238,36 @@ def _getattr_property(obj, attrname): # Nothing helped, fall back. return getattr(obj, attrname) + def object_inspect_text(self, oname, detail_level=0): + """Get object info as formatted text.""" + info = self._ofind(oname) + if not info.get('found'): + return '' + try: + info = self._object_find(oname) + # We need to avoid arbitrary python objects remaining in info (and + # potentially being serialized below); `obj` itself needs to be + # removed, but retained for use below, and `parent` isn't used at all. + obj = info.pop('obj', '') + info.pop('parent', '') + # Follow the InteractiveShell base class implementation and call + # directly into the inspector's _get_info method. + # pylint: disable=protected-access + result = self.inspector._get_info( + obj, + oname, + info=info, + detail_level=detail_level, + ) + return result['text/plain'] + except Exception as e: # pylint: disable=broad-except + self.kernel.log.info( + 'Exception caught during object text inspection: %s\nTraceback:\n%s', + repr(e), + ''.join(traceback.format_tb(sys.exc_info()[2])), + ) + return '' + def object_inspect(self, oname, detail_level=0): info = self._ofind(oname) @@ -259,7 +289,7 @@ def object_inspect(self, oname, detail_level=0): e, ''.join(traceback.format_tb(sys.exc_info()[2])) ) ) - result = oinspect.InfoDict() + result = oinspect.object_info() else: result = super(Shell, self).object_inspect( oname, detail_level=detail_level