Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 21149c6

Browse files
authored
Print callback (#10)
* add print_callback to alter print output * fix multiline prints
1 parent a2c6dd1 commit 21149c6

File tree

3 files changed

+65
-5
lines changed

3 files changed

+65
-5
lines changed

pytest_examples/eval_example.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations as _annotations
22

33
from pathlib import Path
4-
from typing import TYPE_CHECKING, Any
4+
from typing import TYPE_CHECKING, Any, Callable
55

66
import pytest
77
from _pytest.assertion.rewrite import AssertionRewritingHook
@@ -30,6 +30,7 @@ def __init__(self, *, tmp_path: Path, pytest_request: pytest.FixtureRequest):
3030
self._test_id = pytest_request.node.nodeid
3131
self.to_update: list[CodeExample] = []
3232
self.config: ExamplesConfig = ExamplesConfig()
33+
self.print_callback: Callable[[str], str] | None = None
3334

3435
def set_config(
3536
self,
@@ -159,7 +160,9 @@ def _run(
159160
enable_print_mock = False
160161

161162
python_file = self._write_file(example)
162-
return run_code(example, python_file, loader, self.config, enable_print_mock, module_globals)
163+
return run_code(
164+
example, python_file, loader, self.config, enable_print_mock, self.print_callback, module_globals
165+
)
163166

164167
def lint(self, example: CodeExample) -> None:
165168
"""

pytest_examples/run_code.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from importlib.abc import Loader
1111
from pathlib import Path
1212
from textwrap import indent
13-
from typing import TYPE_CHECKING, Any
13+
from typing import TYPE_CHECKING, Any, Callable
1414
from unittest.mock import patch
1515

1616
import pytest
@@ -34,6 +34,7 @@ def run_code(
3434
loader: Loader | None,
3535
config: ExamplesConfig,
3636
enable_print_mock: bool,
37+
print_callback: Callable[[str], str] | None,
3738
module_globals: dict[str, Any] | None,
3839
) -> tuple[InsertPrintStatements, dict[str, Any]]:
3940
__tracebackhide__ = True
@@ -42,7 +43,7 @@ def run_code(
4243
module = importlib.util.module_from_spec(spec)
4344

4445
# does nothing if insert_print_statements is False
45-
insert_print = InsertPrintStatements(python_file, config, enable_print_mock)
46+
insert_print = InsertPrintStatements(python_file, config, enable_print_mock, print_callback)
4647

4748
if module_globals:
4849
module.__dict__.update(module_globals)
@@ -123,10 +124,13 @@ def __call__(self, *args: Any, sep: str = ' ', **kwargs: Any) -> None:
123124

124125

125126
class InsertPrintStatements:
126-
def __init__(self, python_path: Path, config: ExamplesConfig, enable: bool):
127+
def __init__(
128+
self, python_path: Path, config: ExamplesConfig, enable: bool, print_callback: Callable[[str], str] | None
129+
):
127130
self.file = python_path
128131
self.config = config
129132
self.print_func = MockPrintFunction(python_path) if enable else None
133+
self.print_callback = print_callback
130134
self.patch = None
131135

132136
def __enter__(self) -> None:
@@ -176,6 +180,8 @@ def _insert_print_args(
176180
self, lines: list[str], statement: PrintStatement, in_python: bool, line_index: int, col: int
177181
) -> None:
178182
single_line = statement.sep.join(map(str, statement.args))
183+
if self.print_callback:
184+
single_line = self.print_callback(single_line)
179185
indent_str = ' ' * col
180186
max_single_length = self.config.line_length - len(indent_str)
181187
if '\n' not in single_line and len(single_line) + len(comment_prefix) < max_single_length:
@@ -185,6 +191,8 @@ def _insert_print_args(
185191
sep = f'{statement.sep}\n'
186192
indent_config = dataclasses.replace(self.config, line_length=max_single_length)
187193
output = sep.join(arg.format(indent_config).strip('\n') for arg in statement.args)
194+
if self.print_callback:
195+
output = self.print_callback(output)
188196
# remove trailing whitespace
189197
output = re.sub(r' +$', '', output, flags=re.MULTILINE)
190198
# have to use triple single quotes in python since we're already in a double quotes docstring

tests/test_run_examples.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,3 +248,52 @@ def div(y):
248248

249249
assert exc_info.traceback[-2].frame.code.path == md_file
250250
assert exc_info.traceback[-2].lineno == 9
251+
252+
253+
def test_print_sub(pytester: pytest.Pytester):
254+
pytester.makefile(
255+
'.md',
256+
# language=Markdown
257+
my_file='''
258+
# My file
259+
260+
```py
261+
print('hello')
262+
#> hello
263+
print('1/2/3')
264+
#> X/X/X
265+
print({f'{i} key': i for i in range(8)})
266+
"""
267+
{
268+
'X key': X,
269+
'X key': X,
270+
'X key': X,
271+
'X key': X,
272+
'X key': X,
273+
'X key': X,
274+
'X key': X,
275+
'X key': X,
276+
}
277+
"""
278+
```
279+
''',
280+
)
281+
# language=Python
282+
pytester.makepyfile(
283+
r"""
284+
import re
285+
from pytest_examples import find_examples, CodeExample, EvalExample
286+
import pytest
287+
288+
def print_sub(print_statement):
289+
return re.sub(r'[0-9]+', 'X', print_statement)
290+
291+
@pytest.mark.parametrize('example', find_examples('.'), ids=str)
292+
def test_find_run_examples(example: CodeExample, eval_example: EvalExample):
293+
eval_example.print_callback = print_sub
294+
eval_example.run_print_check(example, rewrite_assertions=False)
295+
"""
296+
)
297+
298+
result = pytester.runpytest('-p', 'no:pretty', '-v')
299+
result.assert_outcomes(passed=1)

0 commit comments

Comments
 (0)