diff options
author | arcadia-devtools <arcadia-devtools@yandex-team.ru> | 2022-06-16 17:24:12 +0300 |
---|---|---|
committer | arcadia-devtools <arcadia-devtools@yandex-team.ru> | 2022-06-16 17:24:12 +0300 |
commit | f6202b38cf98f2ad0032a6efed86f4ce1ccf86a4 (patch) | |
tree | 86b4cfeefeb200e9ac8c1f24f60de089374b70dd | |
parent | 108a3f2a5b7ace8b0d68d936451961c889a94aa3 (diff) | |
download | ydb-f6202b38cf98f2ad0032a6efed86f4ce1ccf86a4.tar.gz |
intermediate changes
ref:e623d1e80066c7bc52d0c78d3c885faf82ccd263
-rw-r--r-- | contrib/python/stack-data/.dist-info/METADATA | 9 | ||||
-rw-r--r-- | contrib/python/stack-data/README.md | 4 | ||||
-rw-r--r-- | contrib/python/stack-data/stack_data/__init__.py | 1 | ||||
-rw-r--r-- | contrib/python/stack-data/stack_data/formatting.py | 5 | ||||
-rw-r--r-- | contrib/python/stack-data/stack_data/serializing.py | 200 | ||||
-rw-r--r-- | contrib/python/stack-data/stack_data/version.py | 2 |
6 files changed, 213 insertions, 8 deletions
diff --git a/contrib/python/stack-data/.dist-info/METADATA b/contrib/python/stack-data/.dist-info/METADATA index e77dd5f3c8..ec17c4a350 100644 --- a/contrib/python/stack-data/.dist-info/METADATA +++ b/contrib/python/stack-data/.dist-info/METADATA @@ -1,12 +1,11 @@ Metadata-Version: 2.1 Name: stack-data -Version: 0.2.0 +Version: 0.3.0 Summary: Extract data from python stack frames and tracebacks for informative displays Home-page: http://github.com/alexmojaki/stack_data Author: Alex Hall Author-email: alex.mojaki@gmail.com License: MIT -Platform: UNKNOWN Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 @@ -33,7 +32,9 @@ Requires-Dist: cython ; extra == 'tests' [![Tests](https://github.com/alexmojaki/stack_data/actions/workflows/pytest.yml/badge.svg)](https://github.com/alexmojaki/stack_data/actions/workflows/pytest.yml) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/stack_data/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/stack_data?branch=master) [![Supports Python versions 3.5+](https://img.shields.io/pypi/pyversions/stack_data.svg)](https://pypi.python.org/pypi/stack_data) -This is a library that extracts data from stack frames and tracebacks, particularly to display more useful tracebacks than the default. +This is a library that extracts data from stack frames and tracebacks, particularly to display more useful tracebacks than the default. It powers the tracebacks in IPython and [futurecoder](https://futurecoder.io/): + +![futurecoder example](https://futurecoder.io/static/img/features/traceback.png) You can install it from PyPI: @@ -438,5 +439,3 @@ x = -993 In addition to handling repeated frames, we've passed a traceback object to `FrameInfo.stack_data` instead of a frame. If you want, you can pass `collapse_repeated_frames=False` to `FrameInfo.stack_data` (not to `Options`) and it will just yield `FrameInfo` objects for the full stack. - - diff --git a/contrib/python/stack-data/README.md b/contrib/python/stack-data/README.md index a8bc532643..17bcba2990 100644 --- a/contrib/python/stack-data/README.md +++ b/contrib/python/stack-data/README.md @@ -2,7 +2,9 @@ [![Tests](https://github.com/alexmojaki/stack_data/actions/workflows/pytest.yml/badge.svg)](https://github.com/alexmojaki/stack_data/actions/workflows/pytest.yml) [![Coverage Status](https://coveralls.io/repos/github/alexmojaki/stack_data/badge.svg?branch=master)](https://coveralls.io/github/alexmojaki/stack_data?branch=master) [![Supports Python versions 3.5+](https://img.shields.io/pypi/pyversions/stack_data.svg)](https://pypi.python.org/pypi/stack_data) -This is a library that extracts data from stack frames and tracebacks, particularly to display more useful tracebacks than the default. +This is a library that extracts data from stack frames and tracebacks, particularly to display more useful tracebacks than the default. It powers the tracebacks in IPython and [futurecoder](https://futurecoder.io/): + +![futurecoder example](https://futurecoder.io/static/img/features/traceback.png) You can install it from PyPI: diff --git a/contrib/python/stack-data/stack_data/__init__.py b/contrib/python/stack-data/stack_data/__init__.py index e9bc429e62..a75f8846ac 100644 --- a/contrib/python/stack-data/stack_data/__init__.py +++ b/contrib/python/stack-data/stack_data/__init__.py @@ -1,6 +1,7 @@ from .core import Source, FrameInfo, markers_from_ranges, Options, LINE_GAP, Line, Variable, RangeInLine, \ RepeatedFrames, MarkerInLine, style_with_executing_node from .formatting import Formatter +from .serializing import Serializer try: from .version import __version__ diff --git a/contrib/python/stack-data/stack_data/formatting.py b/contrib/python/stack-data/stack_data/formatting.py index b37da99b62..7c5c0e5fc5 100644 --- a/contrib/python/stack-data/stack_data/formatting.py +++ b/contrib/python/stack-data/stack_data/formatting.py @@ -11,7 +11,7 @@ from stack_data.utils import assert_ class Formatter: def __init__( self, *, - options=Options(), + options=None, pygmented=False, show_executing_node=True, pygments_formatter_cls=None, @@ -29,6 +29,9 @@ class Formatter: chain=True, collapse_repeated_frames=True ): + if options is None: + options = Options() + if pygmented and not options.pygments_formatter: if show_executing_node: pygments_style = style_with_executing_node( diff --git a/contrib/python/stack-data/stack_data/serializing.py b/contrib/python/stack-data/stack_data/serializing.py new file mode 100644 index 0000000000..fb67d2906a --- /dev/null +++ b/contrib/python/stack-data/stack_data/serializing.py @@ -0,0 +1,200 @@ +import inspect +import logging +import sys +import traceback +from collections import Counter +from html import escape as escape_html +from types import FrameType, TracebackType +from typing import Union, Iterable, List + +from stack_data import ( + style_with_executing_node, + Options, + Line, + FrameInfo, + Variable, + RepeatedFrames, +) + +log = logging.getLogger(__name__) + + +class Serializer: + def __init__( + self, + *, + options=None, + pygmented=False, + show_executing_node=True, + pygments_formatter_cls=None, + pygments_formatter_kwargs=None, + pygments_style="monokai", + executing_node_modifier="bg:#005080", + use_code_qualname=True, + strip_leading_indent=True, + html=False, + chain=True, + collapse_repeated_frames=True, + show_variables=False, + ): + if options is None: + options = Options() + + if pygmented and not options.pygments_formatter: + if show_executing_node: + pygments_style = style_with_executing_node( + pygments_style, executing_node_modifier + ) + + if pygments_formatter_cls is None: + if html: + from pygments.formatters.html import ( + HtmlFormatter as pygments_formatter_cls, + ) + else: + from pygments.formatters.terminal256 import ( + Terminal256Formatter as pygments_formatter_cls, + ) + + options.pygments_formatter = pygments_formatter_cls( + style=pygments_style, + **pygments_formatter_kwargs or {}, + ) + + self.pygmented = pygmented + self.use_code_qualname = use_code_qualname + self.strip_leading_indent = strip_leading_indent + self.html = html + self.chain = chain + self.options = options + self.collapse_repeated_frames = collapse_repeated_frames + self.show_variables = show_variables + + def format_exception(self, e=None) -> List[dict]: + if e is None: + e = sys.exc_info()[1] + + result = [] + + if self.chain: + if e.__cause__ is not None: + result = self.format_exception(e.__cause__) + result[-1]["tail"] = traceback._cause_message.strip() + elif e.__context__ is not None and not e.__suppress_context__: + result = self.format_exception(e.__context__) + result[-1]["tail"] = traceback._context_message.strip() + + result.append(self.format_traceback_part(e)) + return result + + def format_traceback_part(self, e: BaseException) -> dict: + return dict( + frames=self.format_stack(e.__traceback__ or sys.exc_info()[2]), + exception=dict( + type=type(e).__name__, + message=traceback._some_str(e), + ), + tail="", + ) + + def format_stack(self, frame_or_tb=None) -> List[dict]: + if frame_or_tb is None: + frame_or_tb = inspect.currentframe().f_back + + return list( + self.format_stack_data( + FrameInfo.stack_data( + frame_or_tb, + self.options, + collapse_repeated_frames=self.collapse_repeated_frames, + ) + ) + ) + + def format_stack_data( + self, stack: Iterable[Union[FrameInfo, RepeatedFrames]] + ) -> Iterable[dict]: + for item in stack: + if isinstance(item, FrameInfo): + if not self.should_include_frame(item): + continue + yield dict(type="frame", **self.format_frame(item)) + else: + yield dict(type="repeated_frames", **self.format_repeated_frames(item)) + + def format_repeated_frames(self, repeated_frames: RepeatedFrames) -> dict: + counts = sorted( + Counter(repeated_frames.frame_keys).items(), + key=lambda item: (-item[1], item[0][0].co_name), + ) + return dict( + frames=[ + dict( + name=code.co_name, + lineno=lineno, + count=count, + ) + for (code, lineno), count in counts + ] + ) + + def format_frame(self, frame: Union[FrameInfo, FrameType, TracebackType]) -> dict: + if not isinstance(frame, FrameInfo): + frame = FrameInfo(frame, self.options) + + result = dict( + name=( + frame.executing.code_qualname() + if self.use_code_qualname + else frame.code.co_name + ), + filename=frame.filename, + lineno=frame.lineno, + lines=list(self.format_lines(frame.lines)), + ) + if self.show_variables: + result["variables"] = list(self.format_variables(frame)) + return result + + def format_lines(self, lines): + for line in lines: + if isinstance(line, Line): + yield dict(type="line", **self.format_line(line)) + else: + yield dict(type="line_gap") + + def format_line(self, line: Line) -> dict: + return dict( + is_current=line.is_current, + lineno=line.lineno, + text=line.render( + pygmented=self.pygmented, + escape_html=self.html, + strip_leading_indent=self.strip_leading_indent, + ), + ) + + def format_variables(self, frame_info: FrameInfo) -> Iterable[dict]: + try: + for var in sorted(frame_info.variables, key=lambda v: v.name): + yield self.format_variable(var) + except Exception: # pragma: no cover + log.exception("Error in getting frame variables") + + def format_variable(self, var: Variable) -> dict: + return dict( + name=self.format_variable_part(var.name), + value=self.format_variable_part(self.format_variable_value(var.value)), + ) + + def format_variable_part(self, text): + if self.html: + return escape_html(text) + else: + return text + + def format_variable_value(self, value) -> str: + return repr(value) + + def should_include_frame(self, frame_info: FrameInfo) -> bool: + return True # pragma: no cover diff --git a/contrib/python/stack-data/stack_data/version.py b/contrib/python/stack-data/stack_data/version.py index 7fd229a32b..0404d81037 100644 --- a/contrib/python/stack-data/stack_data/version.py +++ b/contrib/python/stack-data/stack_data/version.py @@ -1 +1 @@ -__version__ = '0.2.0' +__version__ = '0.3.0' |