Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expected output whitespace #97

Merged
merged 4 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
<p align="center">Test code blocks in your READMEs.</p>
</p>

[![PyPi Version](https://img.shields.io/pypi/v/pytest-codeblocks.svg?style=flat-square)](https://pypi.org/project/pytest-codeblocks/)
[![PyPi Version](https://img.shields.io/pypi/v/pytest-codeblocks.svg?style=flat-square)](https://pypi.org/project/pytest_codeblocks/)
[![Anaconda Cloud](https://anaconda.org/conda-forge/pytest-codeblocks/badges/version.svg?=style=flat-square)](https://anaconda.org/conda-forge/pytest-codeblocks/)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/pytest-codeblocks.svg?style=flat-square)](https://pypi.org/project/pytest-codeblocks/)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/pytest-codeblocks.svg?style=flat-square)](https://pypi.org/project/pytest_codeblocks/)
[![GitHub stars](https://img.shields.io/github/stars/nschloe/pytest-codeblocks.svg?style=flat-square&logo=github&label=Stars&logoColor=white)](https://github.com/nschloe/pytest-codeblocks)
[![Downloads](https://pepy.tech/badge/pytest-codeblocks/month?style=flat-square)](https://pepy.tech/project/pytest-codeblocks)
[![Downloads](https://static.pepy.tech/badge/pytest-codeblocks/month?style=flat-square)](https://www.pepy.tech/projects/pytest-codeblocks)

<!--[![PyPi downloads](https://img.shields.io/pypi/dm/pytest-codeblocks.svg?style=flat-square)](https://pypistats.org/packages/pytest-codeblocks)-->

[![gh-actions](https://img.shields.io/github/workflow/status/nschloe/pytest-codeblocks/ci?style=flat-square)](https://github.com/nschloe/pytest-codeblocks/actions?query=workflow%3Aci)
[![gh-actions](https://img.shields.io/github/actions/workflow/status/nschloe/pytest-codeblocks/tests?style=flat-square)](https://github.com/nschloe/pytest-codeblocks/actions?query=workflow%3Atests)
[![codecov](https://img.shields.io/codecov/c/github/nschloe/pytest-codeblocks.svg?style=flat-square)](https://app.codecov.io/gh/nschloe/pytest-codeblocks)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square)](https://github.com/psf/black)

Expand Down Expand Up @@ -156,5 +156,8 @@ gives
```
````

Use `expected-output-ignore-whitespace` if you'd like whitespace differences to
be ignored.

(Conditionally) Skipping the output verfication works by prepending the first
block with `skip`/`skipif` (see [above](#skipping-code-blocks)).
3 changes: 0 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

[tool.isort]
profile = "black"

[project]
name = "pytest_codeblocks"
authors = [{name = "Nico Schlömer", email = "nico.schloemer@gmail.com"}]
Expand Down
2 changes: 1 addition & 1 deletion src/pytest_codeblocks/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.16.1"
__version__ = "0.16.2"
16 changes: 12 additions & 4 deletions src/pytest_codeblocks/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class CodeBlock:
lineno: int
syntax: str | None = None
expected_output: str | None = None
expected_output_ignore_whitespace: bool = False
importorskip: str | None = None
marks: list[str] = field(default_factory=lambda: [])

Expand All @@ -34,6 +35,7 @@ def extract_from_buffer(f, max_num_lines: int = 10000) -> list[CodeBlock]:
marks = []
continued_block = None
expected_output_block = None
expected_output_ignore_whitespace = False
importorskip = None
k = 1

Expand All @@ -58,18 +60,21 @@ def extract_from_buffer(f, max_num_lines: int = 10000) -> list[CodeBlock]:
if m is not None:
keyword = m.group(1).strip("- ")
# handle special tags
if keyword == "expected-output":
if keyword in {"expected-output", "expected-output-ignore-whitespace"}:
if len(out) == 0:
raise RuntimeError(
"Found <!--pytest-codeblocks-expected-output--> "
f"Found <!--pytest-codeblocks-{keyword}--> "
+ "but no previous code block."
)
if out[-1].expected_output is not None:
raise RuntimeError(
"Found <!--pytest-codeblocks-expected-output--> "
f"Found <!--pytest-codeblocks-{keyword}--> "
+ "but block already has expected_output."
)
expected_output_block = out[-1]
if keyword == "expected-output-ignore-whitespace":
# \s: regex matches all whitespace characters
expected_output_ignore_whitespace = True

elif keyword == "cont":
if len(out) == 0:
Expand Down Expand Up @@ -115,7 +120,7 @@ def extract_from_buffer(f, max_num_lines: int = 10000) -> list[CodeBlock]:
marks.append("pytest.mark.xfail")

else:
raise RuntimeError(f'Unknown pytest-codeblocks keyword "{keyword}."')
raise RuntimeError(f'Unknown pytest-codeblocks keyword "{keyword}"')

continue

Expand Down Expand Up @@ -162,6 +167,9 @@ def extract_from_buffer(f, max_num_lines: int = 10000) -> list[CodeBlock]:

elif expected_output_block:
expected_output_block.expected_output = code
expected_output_block.expected_output_ignore_whitespace = (
expected_output_ignore_whitespace
)
expected_output_block = None

else:
Expand Down
10 changes: 9 additions & 1 deletion src/pytest_codeblocks/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
import subprocess
from pathlib import Path
import re

import pytest

Expand Down Expand Up @@ -103,7 +104,14 @@ def runtest(self):
output = ret.stdout.decode()

if output is not None and self.obj.expected_output is not None:
if self.obj.expected_output != output:
str0 = self.obj.expected_output
str1 = output

if self.obj.expected_output_ignore_whitespace:
str0 = re.sub(r"^\s+", "", str0, flags=re.MULTILINE)
str1 = re.sub(r"^\s+", "", str1, flags=re.MULTILINE)

if str0 != str1:
raise RuntimeError(
f"{self.name}, line {self.obj.lineno}:\n```\n"
+ f"Expected output\n```\n{self.obj.expected_output}```\n"
Expand Down
22 changes: 22 additions & 0 deletions tests/test_expected_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,25 @@ def test_expected_output_fail(testdir):
testdir.makefile(".md", string)
result = testdir.runpytest("--codeblocks")
result.assert_outcomes(failed=1)


def test_expected_output_ignore_whitespace(testdir):
string = """
Lorem ipsum
```python
print(1 + 3)
print(1 - 3)
print(1 * 3)
```
dolor sit amet
<!--pytest-codeblocks:expected-output-ignore-whitespace-->
```
4
-2

3
```
"""
testdir.makefile(".md", string)
result = testdir.runpytest("--codeblocks")
result.assert_outcomes(passed=1)