From 7ac28225ff99fc60eb609b3bb5ae766850b96a44 Mon Sep 17 00:00:00 2001 From: Karthik Nadig Date: Tue, 13 Mar 2018 15:06:30 -0700 Subject: [PATCH] Support display in hex --- ptvsd/safe_repr.py | 48 +++++++++++++++++++---------------- ptvsd/wrapper.py | 63 ++++++++++++++++++++++++++++++---------------- 2 files changed, 68 insertions(+), 43 deletions(-) diff --git a/ptvsd/safe_repr.py b/ptvsd/safe_repr.py index 435d39ee..4294a717 100644 --- a/ptvsd/safe_repr.py +++ b/ptvsd/safe_repr.py @@ -29,10 +29,12 @@ class SafeRepr(object): string_types = (str, bytes) set_info = (set, '{', '}', False) frozenset_info = (frozenset, 'frozenset({', '})', False) + int_types = (int) else: string_types = (str, unicode) set_info = (set, 'set([', '])', False) frozenset_info = (frozenset, 'frozenset([', '])', False) + int_types = (int, long) # Collection types are recursively iterated for each limit in # maxcollection. @@ -67,16 +69,16 @@ class SafeRepr(object): maxother_outer = 2 ** 16 maxother_inner = 30 - def __call__(self, obj): + def __call__(self, obj, convert_to_hex=False): try: - return ''.join(self._repr(obj, 0)) + return ''.join(self._repr(obj, 0, convert_to_hex=convert_to_hex)) except Exception: try: return 'An exception was raised: %r' % sys.exc_info()[1] except Exception: return 'An exception was raised' - def _repr(self, obj, level): + def _repr(self, obj, level, convert_to_hex=False): '''Returns an iterable of the parts in the final repr string.''' try: @@ -93,21 +95,21 @@ class SafeRepr(object): for t, prefix, suffix, comma in self.collection_types: if isinstance(obj, t) and has_obj_repr(t): - return self._repr_iter(obj, level, prefix, suffix, comma) + return self._repr_iter(obj, level, prefix, suffix, comma, convert_to_hex=convert_to_hex) for t, prefix, suffix, item_prefix, item_sep, item_suffix in self.dict_types: # noqa if isinstance(obj, t) and has_obj_repr(t): return self._repr_dict(obj, level, prefix, suffix, - item_prefix, item_sep, item_suffix) + item_prefix, item_sep, item_suffix, convert_to_hex=convert_to_hex) for t in self.string_types: if isinstance(obj, t) and has_obj_repr(t): - return self._repr_str(obj, level) + return self._repr_str(obj, level, convert_to_hex=convert_to_hex) if self._is_long_iter(obj): - return self._repr_long_iter(obj) + return self._repr_long_iter(obj, convert_to_hex=convert_to_hex) - return self._repr_other(obj, level) + return self._repr_other(obj, level, convert_to_hex=convert_to_hex) # Determines whether an iterable exceeds the limits set in # maxlimits, and is therefore unsafe to repr(). @@ -164,7 +166,7 @@ class SafeRepr(object): return True def _repr_iter(self, obj, level, prefix, suffix, - comma_after_single_element=False): + comma_after_single_element=False, convert_to_hex=False): yield prefix if level >= len(self.maxcollection): @@ -182,7 +184,7 @@ class SafeRepr(object): yield '...' break - for p in self._repr(item, 100 if item is obj else level + 1): + for p in self._repr(item, 100 if item is obj else level + 1, convert_to_hex=convert_to_hex): yield p else: if comma_after_single_element: @@ -190,9 +192,10 @@ class SafeRepr(object): yield ',' yield suffix - def _repr_long_iter(self, obj): + def _repr_long_iter(self, obj, convert_to_hex=False): try: - obj_repr = '<%s, len() = %s>' % (type(obj).__name__, len(obj)) + length = hex(len(obj)) if convert_to_hex else len(obj) + obj_repr = '<%s, len() = %s>' % (type(obj).__name__, length) except Exception: try: obj_repr = '<' + type(obj).__name__ + '>' @@ -201,7 +204,7 @@ class SafeRepr(object): yield obj_repr def _repr_dict(self, obj, level, prefix, suffix, - item_prefix, item_sep, item_suffix): + item_prefix, item_sep, item_suffix, convert_to_hex=False): if not obj: yield prefix + suffix return @@ -230,7 +233,7 @@ class SafeRepr(object): break yield item_prefix - for p in self._repr(key, level + 1): + for p in self._repr(key, level + 1, convert_to_hex=convert_to_hex): yield p yield item_sep @@ -240,23 +243,26 @@ class SafeRepr(object): except Exception: yield '' else: - for p in self._repr(item, 100 if item is obj else level + 1): + for p in self._repr(item, 100 if item is obj else level + 1, convert_to_hex=convert_to_hex): yield p yield item_suffix yield suffix - def _repr_str(self, obj, level): + def _repr_str(self, obj, level, convert_to_hex=False): return self._repr_obj(obj, level, - self.maxstring_inner, self.maxstring_outer) + self.maxstring_inner, self.maxstring_outer, convert_to_hex=convert_to_hex) - def _repr_other(self, obj, level): + def _repr_other(self, obj, level, convert_to_hex=False): return self._repr_obj(obj, level, - self.maxother_inner, self.maxother_outer) + self.maxother_inner, self.maxother_outer, convert_to_hex=convert_to_hex) - def _repr_obj(self, obj, level, limit_inner, limit_outer): + def _repr_obj(self, obj, level, limit_inner, limit_outer, convert_to_hex=False): try: - obj_repr = repr(obj) + if isinstance(obj, self.int_types) and convert_to_hex: + obj_repr = hex(obj) + else: + obj_repr = repr(obj) except Exception: try: obj_repr = object.__repr__(obj) diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 1d04f70b..252b95c5 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -47,6 +47,31 @@ ptvsd_sys_exit_code = 0 WAIT_FOR_DISCONNECT_REQUEST_TIMEOUT = 2 WAIT_FOR_THREAD_FINISH_TIMEOUT = 1 + +class SafeReprPresentationProvider(pydevd_extapi.StrPresentationProvider): + """ + Computes string representation of Python values by delegating them + to SafeRepr. + """ + + def __init__(self): + from ptvsd.safe_repr import SafeRepr + self.safe_repr = SafeRepr() + self.convert_to_hex = False + + def can_provide(self, type_object, type_name): + return True + + def get_str(self, val): + return self.safe_repr(val, self.convert_to_hex) + +# Register our presentation provider as the first item on the list, +# so that we're in full control of presentation. +str_handlers = pydevd_extutil.EXTENSION_MANAGER_INSTANCE.type_to_instance.setdefault(pydevd_extapi.StrPresentationProvider, []) # noqa +safe_repr_provider = SafeReprPresentationProvider() +str_handlers.insert(0, safe_repr_provider) + + class UnsupportedPyDevdCommandError(Exception): def __init__(self, cmdid): @@ -575,6 +600,7 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel): supportsSetVariable=True, supportsExceptionOptions=True, supportsEvaluateForHovers=True, + supportsValueFormattingOptions=True, exceptionBreakpointFilters=[ { 'filter': 'raised', @@ -759,6 +785,8 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel): # TODO: docstring vsc_var = int(args['variablesReference']) pyd_var = self.var_map.to_pydevd(vsc_var) + + safe_repr_provider.convert_to_hex = args.get('format', {}).get('hex', False) if len(pyd_var) == 3: cmd = pydevd_comm.CMD_GET_FRAME @@ -794,6 +822,9 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel): var['evaluateName'] = eval_name variables.append(var) + + # Reset hex format since this is per request. + safe_repr_provider.convert_to_hex = False self.send_response(request, variables=variables.get_sorted_variables()) def __get_variable_evaluate_name(self, pyd_var_parent, var_name): @@ -836,6 +867,8 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel): vsc_var = int(args['variablesReference']) pyd_var = self.var_map.to_pydevd(vsc_var) + safe_repr_provider.convert_to_hex = args.get('format', {}).get('hex', False) + # VSC gives us variablesReference to the parent of the variable # being set, and variable name; but pydevd wants the ID # (or rather path) of the variable itself. @@ -856,6 +889,9 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel): } if bool(xvar['isContainer']): response['variablesReference'] = vsc_var + + # Reset hex format since this is per request. + safe_repr_provider.convert_to_hex = False self.send_response(request, **response) @async_handler @@ -866,6 +902,8 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel): vsc_fid = int(args['frameId']) pyd_tid, pyd_fid = self.frame_map.to_pydevd(vsc_fid) + safe_repr_provider.convert_to_hex = args.get('format', {}).get('hex', False) + cmd_args = (pyd_tid, pyd_fid, 'LOCAL', expr, '1') _, _, resp_args = yield self.pydevd_request( pydevd_comm.CMD_EVALUATE_EXPRESSION, @@ -890,6 +928,9 @@ class VSCodeMessageProcessor(ipcjson.SocketIO, ipcjson.IpcChannel): } if bool(xvar['isContainer']): response['variablesReference'] = vsc_var + + # Reset hex format since this is per request. + safe_repr_provider.convert_to_hex = False self.send_response(request, **response) @async_handler @@ -1283,25 +1324,3 @@ def start_client(host, port): pydevd_comm.start_server = start_server pydevd_comm.start_client = start_client - -class SafeReprPresentationProvider(pydevd_extapi.StrPresentationProvider): - """ - Computes string representation of Python values by delegating them - to SafeRepr. - """ - - def __init__(self): - from ptvsd.safe_repr import SafeRepr - self.safe_repr = SafeRepr() - - def can_provide(self, type_object, type_name): - return True - - def get_str(self, val): - return self.safe_repr(val) - - -# Register our presentation provider as the first item on the list, -# so that we're in full control of presentation. -str_handlers = pydevd_extutil.EXTENSION_MANAGER_INSTANCE.type_to_instance.setdefault(pydevd_extapi.StrPresentationProvider, []) # noqa -str_handlers.insert(0, SafeReprPresentationProvider())