alexmojaki / alexmojaki/stack_data

0.6.3: `pyupgrade --py38-plus` breaks test suite

Aberta
#57 3 comentários 0 reações 0 responsáveis Ver no GitHub
Linguagem predominante
Python
Estrelas
51
Forks
23
Métricas de merge de PRs
Nenhum PR com merge em 30d

Descrição

I've been trying to prepare PR to drop python<=3.7 (which has been EOSed almost year ago) support by filter all code over `pyupgrade --py38-plus` and found that generated patch breaks test suite.

Here is pytest output:

```console
+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-stack-data-0.6.3-8.fc37.x86_64/usr/lib64/python3.10/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-stack-data-0.6.3-8.fc37.x86_64/usr/lib/python3.10/site-packages
+ /usr/bin/pytest -ra -m 'not network' --deselect tests/test_core.py::test_pygments_example
============================= test session starts ==============================
platform linux -- Python 3.10.14, pytest-8.1.1, pluggy-1.4.0
rootdir: /home/tkloczko/rpmbuild/BUILD/stack_data-0.6.3
configfile: pyproject.toml
plugins: typeguard-4.2.1
collected 21 items / 1 deselected / 20 selected

tests/test_core.py ....F......... [ 70%]
tests/test_formatter.py F. [ 80%]
tests/test_serializer.py . [ 85%]
tests/test_utils.py ... [100%]

=================================== FAILURES ===================================
_________________________________ test_pieces __________________________________

def test_pieces():
filename = samples_dir / "pieces.py"
source = Source.for_filename(str(filename))
pieces = [
[
source.lines[i - 1]
for i in piece
]
for piece in source.pieces
]
> assert pieces == [
['import math'],
['def foo(x=1, y=2):'],
[' """',
' a docstring',
' """'],
[' z = 0'],
[' for i in range(5):'],
[' z += i * x * math.sin(y)'],
[' # comment1',
' # comment2'],
[' z += math.copysign(',
' -1,',
' 2,',
' )'],
[' for i in range(',
' 0,',
' 6',
' ):'],
[' try:'],
[' str(i)'],
[' except:'],
[' pass'],
[' try:'],
[' int(i)'],
[' except (ValueError,',
' TypeError):'],
[' pass'],
[' finally:'],
[' str("""',
' foo',
' """)'],
[' str(f"""',
' {str(str)}',
' """)'],
[' str(f"""',
' foo',
' {',
' str(',
' str',
' )',
' }',
' bar',
' {str(str)}',
' baz',
' {',
' str(',
' str',
' )',
' }',
' spam',
' """)'],
['def foo2(',
' x=1,',
' y=2,', '):'],
[' while 9:'],
[' while (',
' 9 + 9',
' ):'],
[' if 1:'],
[' pass'],
[' elif 2:'],
[' pass'],
[' elif (',
' 3 + 3',
' ):'],
[' pass'],
[' else:'],
[' pass'],
['class Foo(object):'],
[' @property',
' def foo(self):'],
[' return 3'],
['# noinspection PyTrailingSemicolon'],
['def semicolons():'],
[' if 1:'],
[' print(1,',
' 2); print(3,',
' 4); print(5,',
' 6)'],
[' if 2:'],
[' print(1,',
' 2); print(3, 4); print(5,',
' 6)'],
[' print(1, 2); print(3,',
' 4); print(5, 6)'],
[' print(1, 2);print(3, 4);print(5, 6)']
]
E assert [['import mat...sin(y)'], ...] == [['import mat...sin(y)'], ...]
E
E At index 18 diff: [' """', ' foo', ' """'] != [' str("""', ' foo', ' """)']
E Use -v to get more diff

tests/test_core.py:329: AssertionError
_________________________________ test_example _________________________________

capsys = <_pytest.capture.CaptureFixture object at 0x7f067b0d49a0>

def test_example(capsys):
from .samples.formatter_example import bar, print_stack1, format_stack1, format_frame, f_string, blank_lines

@contextmanager
def check_example(name):
yield
stderr = capsys.readouterr().err
compare_to_file(stderr, name)

with check_example("variables"):
try:
bar()
except Exception:
MyFormatter(show_variables=True).print_exception()

with check_example("pygmented"):
try:
bar()
except Exception:
MyFormatter(pygmented=True).print_exception()

with check_example("plain"):
MyFormatter().set_hook()
try:
bar()
except Exception:
sys.excepthook(*sys.exc_info())

with check_example("pygmented_error"):
h = pygments.highlight
pygments.highlight = lambda *args, **kwargs: 1/0
try:
bar()
except Exception:
MyFormatter(pygmented=True).print_exception()
finally:
pygments.highlight = h

with check_example("print_stack"):
print_stack1(MyFormatter())

with check_example("format_stack"):
formatter = MyFormatter()
formatted = format_stack1(formatter)
formatter.print_lines(formatted)

with check_example("format_frame"):
formatter = BaseFormatter()
formatted = format_frame(formatter)
formatter.print_lines(formatted)

if sys.version_info[:2] < (3, 8):
f_string_suffix = 'old'
elif not fstring_positions_work():
f_string_suffix = '3.8'
else:
f_string_suffix = 'new'

with check_example(f"f_string_{f_string_suffix}"):
try:
f_string()
except Exception:
MyFormatter().print_exception()

from .samples.formatter_example import block_right, block_left

> with check_example(f"block_right_{'old' if sys.version_info[:2] < (3, 8) else 'new'}"):

tests/test_formatter.py:99:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib64/python3.10/contextlib.py:142: in __exit__
next(self.gen)
tests/test_formatter.py:40: in check_example
compare_to_file(stderr, name)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

text = 'Traceback (most recent call last):\n File "formatter_example.py", line 65, in block_right\n 64 | def block_right... 68 | "words")\n ^^^^^^^^\nTypeError: object of type \'generator\' has no len()\n'
name = 'block_right_new'

def compare_to_file(text, name):
if old_pygments and "pygment" in name:
return
filename = os.path.join(
os.path.dirname(__file__),
'golden_files',
name + '.txt',
)
if os.environ.get('FIX_STACK_DATA_TESTS'):
string_to_file(text, filename)
else:
expected_output = file_to_string(filename)
> assert text == expected_output
E AssertionError

tests/utils.py:26: AssertionError
=========================== short test summary info ============================
FAILED tests/test_core.py::test_pieces - assert [['import mat...sin(y)'], ......
FAILED tests/test_formatter.py::test_example - AssertionError
================== 2 failed, 18 passed, 1 deselected in 2.51s ==================
```

It would be god to prepare `stack_data` code ready for pyupgrade and than drop EOSed python version support.
Here is generated by pyupgrade patch

```patch
--- a/stack_data/core.py
+++ b/stack_data/core.py
@@ -21,10 +21,10 @@
frame_and_lineno, iter_stack, collapse_repeated, group_by_key_func,
cached_property, is_frame, _pygmented_with_ranges, assert_)

-RangeInLine = NamedTuple('RangeInLine',
- [('start', int),
- ('end', int),
- ('data', Any)])
+class RangeInLine(NamedTuple):
+ start: int
+ end: int
+ data: Any
RangeInLine.__doc__ = """
Represents a range of characters within one line of source code,
and some associated data.
@@ -32,10 +32,10 @@
Typically this will be converted to a pair of markers by markers_from_ranges.
"""

-MarkerInLine = NamedTuple('MarkerInLine',
- [('position', int),
- ('is_start', bool),
- ('string', str)])
+class MarkerInLine(NamedTuple):
+ position: int
+ is_start: bool
+ string: str
MarkerInLine.__doc__ = """
A string that is meant to be inserted at a given position in a line of source code.
For example, this could be an ANSI code or the opening or closing of an HTML tag.
@@ -209,11 +209,11 @@

def __repr__(self):
keys = sorted(self.__dict__)
- items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
+ items = (f"{k}={self.__dict__[k]!r}" for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))

-class LineGap(object):
+class LineGap:
"""
A singleton representing one or more lines of source code that were skipped
in FrameInfo.lines.
@@ -240,7 +240,7 @@
self.end_lineno = end_lineno

-class Line(object):
+class Line:
"""
A single line of source code for a particular stack frame.

@@ -508,7 +508,7 @@
return '<{self.__class__.__name__} {self.description}>'.format(self=self)

-class FrameInfo(object):
+class FrameInfo:
"""
Information about a frame!
Pass either a frame object or a traceback object,
--- a/stack_data/formatting.py
+++ b/stack_data/formatting.py
@@ -214,7 +214,7 @@
result = " "
if blank_line.begin_lineno == blank_line.end_lineno:
return result + self.line_number_format_string.format(blank_line.begin_lineno) + "\n"
- return result + " {}\n".format(self.line_number_gap_string)
+ return result + f" {self.line_number_gap_string}\n"

def format_variables(self, frame_info: FrameInfo) -> Iterable[str]:
--- a/stack_data/utils.py
+++ b/stack_data/utils.py
@@ -126,7 +126,7 @@
return result

-class cached_property(object):
+class cached_property:
"""
A property that is only computed once per instance and then replaces itself
with an ordinary attribute. Deleting the attribute resets the property.
--- a/tests/samples/pieces.py
+++ b/tests/samples/pieces.py
@@ -30,9 +30,9 @@
TypeError):
pass
finally:
- str("""
+ """
foo
- """)
+ """
str(f"""
{str(str)}
""")
@@ -75,7 +75,7 @@
pass

-class Foo(object):
+class Foo:
@property
def foo(self):
return 3
--- a/tests/samples/pygments_example.py
+++ b/tests/samples/pygments_example.py
@@ -36,7 +36,7 @@
HtmlFormatter,
]:
for style in ["native", style_with_executing_node("native", "bg:#444400")]:
- result += "{formatter_cls.__name__} {style}:\n\n".format(**locals())
+ result += f"{formatter_cls.__name__} {style}:\n\n"
formatter = formatter_cls(style=style)
options = Options(pygments_formatter=formatter)
frame = inspect.currentframe().f_back
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -28,7 +28,7 @@
def gather_lines():
frame = inspect.currentframe().f_back
frame_info = FrameInfo(frame, options)
- assert repr(frame_info) == "FrameInfo({})".format(frame)
+ assert repr(frame_info) == f"FrameInfo({frame})"
lines[:] = [
line.render(strip_leading_indent=dedented)
if isinstance(line, Line) else line
@@ -174,7 +174,7 @@
' [[line]] = [[only]]([[FrameInfo]]([[inspect]].[[currentframe]](), [[options]]).[[lines]])'

def convert_variable_range(r):
- return '[[', ' of type {}]]'.format(r.data[0].value.__class__.__name__)
+ return '[[', f' of type {r.data[0].value.__class__.__name__}]]'

markers = markers_from_ranges(line.variable_ranges, convert_variable_range)
assert sorted(markers) == [
```

Guia de contribuição

Nenhum guia de contribuição indexado para este repositório

Avaliação

Esta issue ainda não foi avaliada.

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.