Skip to content

Commit 9a4604f

Browse files
authored
Lax whitespace (#42)
* more lax on trialing whitespaces * fix regex * add white_space_dot to config * pass config down
1 parent d7e8b5f commit 9a4604f

File tree

5 files changed

+96
-7
lines changed

5 files changed

+96
-7
lines changed

pytest_examples/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class ExamplesConfig:
2727
ruff_line_length: int | None = None
2828
ruff_select: list[str] | None = None
2929
ruff_ignore: list[str] | None = None
30+
white_space_dot: bool = False
31+
"""If True, replace spaces with `·` in example diffs."""
3032

3133
def black_mode(self):
3234
return BlackMode(

pytest_examples/lint.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,22 @@ def black_format(source: str, config: ExamplesConfig, *, remove_double_blank: bo
8181
def black_check(example: CodeExample, config: ExamplesConfig) -> None:
8282
after_black = black_format(example.source, config, remove_double_blank=example.in_py_file())
8383
if example.source != after_black:
84-
diff = code_diff(example, after_black)
84+
diff = code_diff(example, after_black, config)
8585
raise FormatError(f'black failed:\n{indent(diff, " ")}')
8686

8787

88-
def code_diff(example: CodeExample, after: str) -> str:
89-
diff = black_diff(example.source, after, 'before', 'after')
88+
def code_diff(example: CodeExample, after: str, config: ExamplesConfig) -> str:
89+
diff = black_diff(sub_space(example.source, config), sub_space(after, config), 'before', 'after')
9090

9191
def replace_at_line(match: re.Match) -> str:
9292
offset = re.sub(r'\d+', lambda m: str(int(m.group(0)) + example.start_line), match.group(2))
9393
return f'{match.group(1)}{offset}{match.group(3)}'
9494

9595
return re.sub(r'^(@@\s*)(.*)(\s*@@)$', replace_at_line, diff, flags=re.M)
96+
97+
98+
def sub_space(text: str, config: ExamplesConfig) -> str:
99+
if config.white_space_dot:
100+
return text.replace(' ', '·')
101+
else:
102+
return text

pytest_examples/run_code.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,16 @@ def __exit__(self, *args) -> None:
174174
self.patch.stop()
175175

176176
def check_print_statements(self, example: CodeExample) -> None:
177-
with_prints = self._insert_print_statements(example)
178-
if example.source != with_prints:
179-
diff = code_diff(example, with_prints)
177+
new_code = self.updated_print_statements(example)
178+
if new_code is not None:
179+
diff = code_diff(example, new_code, self.config)
180180
pytest.fail(f'Print output changed code:\n{indent(diff, " ")}', pytrace=False)
181181

182182
def updated_print_statements(self, example: CodeExample) -> str | None:
183183
with_prints = self._insert_print_statements(example)
184-
if example.source != with_prints:
184+
# we check against the raw `with_prints` and `with_prints` with trailing whitespace removed
185+
# since trailing white space will have already been stripped by pre-commit in `example.source`
186+
if example.source not in (with_prints, re.sub(r'[ \t]+\n', '\n', with_prints)):
185187
return with_prints
186188

187189
def print_statements(self) -> list[PrintStatement]:

tests/test_insert_print.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,20 @@ class Foo:
335335
''',
336336
id='hex_id',
337337
),
338+
pytest.param(
339+
"""\
340+
for i in range(3):
341+
print(i)
342+
#> 0
343+
#> 1
344+
#> 2
345+
""",
346+
id='look',
347+
),
348+
pytest.param(
349+
"""print('foobar ')\n#> foobar \n""",
350+
id='trailing-spaces',
351+
),
338352
]
339353

340354

tests/test_run_examples.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,40 @@ def test_find_run_examples(example: CodeExample, eval_example: EvalExample):
174174
]
175175

176176

177+
def test_black_error_dot_space(pytester: pytest.Pytester):
178+
pytester.makefile(
179+
'.md',
180+
my_file='line 1\nline 2\n```py\nx =[1,2, 3]\n```',
181+
)
182+
# language=Python
183+
pytester.makepyfile(
184+
"""
185+
from pytest_examples import find_examples, CodeExample, EvalExample
186+
import pytest
187+
188+
@pytest.mark.parametrize('example', find_examples('.'), ids=str)
189+
def test_find_run_examples(example: CodeExample, eval_example: EvalExample):
190+
eval_example.config.white_space_dot = True
191+
eval_example.lint_black(example)
192+
"""
193+
)
194+
195+
result = pytester.runpytest('-p', 'no:pretty', '-v')
196+
result.assert_outcomes(failed=1)
197+
198+
failures_start = next(index for index, line in enumerate(result.outlines) if 'FAILURES' in line)
199+
failures_end = next(index for index, line in enumerate(result.outlines) if 'short test summary' in line)
200+
e_lines = [line.strip() for line in result.outlines[failures_start + 2 : failures_end]]
201+
assert e_lines == [
202+
'black failed:',
203+
'--- before',
204+
'+++ after',
205+
'@@ -4 +4 @@',
206+
'-x·=[1,2,·3]',
207+
'+x·=·[1,·2,·3]',
208+
]
209+
210+
177211
def test_black_error_multiline(pytester: pytest.Pytester):
178212
pytester.makefile(
179213
'.md',
@@ -303,3 +337,33 @@ def test_find_run_examples(example: CodeExample, eval_example: EvalExample):
303337

304338
result = pytester.runpytest('-p', 'no:pretty', '-v')
305339
result.assert_outcomes(passed=1)
340+
341+
342+
def test_print_check_spaces(pytester: pytest.Pytester):
343+
pytester.makefile(
344+
'.md',
345+
# language=Markdown
346+
my_file="""
347+
# My file
348+
349+
```py
350+
# note trailing space
351+
352+
print('hello ')
353+
#> hello
354+
```""",
355+
)
356+
# language=Python
357+
pytester.makepyfile(
358+
r"""
359+
from pytest_examples import find_examples, CodeExample, EvalExample
360+
import pytest
361+
362+
@pytest.mark.parametrize('example', find_examples('.'), ids=str)
363+
def test_find_run_examples(example: CodeExample, eval_example: EvalExample):
364+
eval_example.run_print_check(example, rewrite_assertions=False)
365+
"""
366+
)
367+
368+
result = pytester.runpytest('-p', 'no:pretty', '-v')
369+
result.assert_outcomes(passed=1)

0 commit comments

Comments
 (0)