Skip to content
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
3 changes: 0 additions & 3 deletions .github/renovate.json

This file was deleted.

16 changes: 16 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Release

on:
release:
types: [released]

jobs:
publish-to-pypi:
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
- run: uv build --wheel
- uses: pypa/gh-action-pypi-publish@release/v1
26 changes: 10 additions & 16 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,26 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pre-commit/action@v3.0.0
- uses: actions/checkout@v6
- uses: j178/prek-action@v2

build:
needs: [lint]

strategy:
matrix:
platform: [ubuntu-latest]
python-version: ["3.7", "3.11"]
include:
- platform: macos-latest
python-version: "3.11"
- platform: windows-latest
python-version: "3.11"
platform: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.15"]

runs-on: ${{ matrix.platform }}

steps:
- uses: actions/setup-python@v4
- uses: actions/checkout@v6

- uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}
- uses: actions/checkout@v4
- name: Test with tox

- name: Test
run: |
pip install tox
tox -- --cov pytest_codeblocks --cov-report xml --cov-report term
- uses: codecov/codecov-action@v4-beta
if: ${{ matrix.python-version == '3.11' && matrix.os == 'ubuntu-latest' }}
uv run pytest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ dist/
*.png
.pytest_cache/
.tox/
uv.lock
15 changes: 5 additions & 10 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.290
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.17
hooks:
- id: ruff

- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
language_version: python3
- id: ruff-check
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.3
rev: v3.1.0
hooks:
- id: prettier
18 changes: 11 additions & 7 deletions justfile
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
version := `python3 -c "from src.pytest_codeblocks.__about__ import __version__; print(__version__)"`
version := `python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])"`

default:
@echo "\"just publish\"?"

publish:
publish: release

release:
@if [ "$(git rev-parse --abbrev-ref HEAD)" != "main" ]; then exit 1; fi
gh release create "v{{version}}"
flit publish
gh release create {{version}}

clean:
@find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf
@rm -rf src/*.egg-info/ build/ dist/ .tox/

format:
ruff src/ tests/ --fix
black src/ tests/
ruff format .
ruff check --fix .
blacken-docs README.md

lint:
pre-commit run --all
prek run --all-files

test:
uv run pytest
25 changes: 7 additions & 18 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,27 @@ build-backend = "setuptools.build_meta"

[project]
name = "pytest_codeblocks"
authors = [{name = "Nico Schlömer", email = "nico.schloemer@gmail.com"}]
version = "0.18.0"
authors = [{ name = "Nico Schlömer", email = "nico.schloemer@gmail.com" }]
description = "Test code blocks in your READMEs"
readme = "README.md"
license = {file = "LICENSE.txt"}
license = { file = "LICENSE.txt" }
classifiers = [
"Development Status :: 5 - Production/Stable",
"Framework :: Pytest",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dynamic = ["version"]
requires-python = ">=3.7"
dependencies = [
"pytest >= 7.0.0"
]
requires-python = ">=3.10"
dependencies = ["pytest >= 7.0.0"]

[tool.setuptools.dynamic]
version = {attr = "pytest_codeblocks.__about__.__version__"}
[dependency-groups]
dev = ["pytest"]

[project.urls]
Homepage = "https://github.com/nschloe/pytest-codeblocks"
Code = "https://github.com/nschloe/pytest-codeblocks"
Issues = "https://github.com/nschloe/pytest-codeblocks/issues"
Funding = "https://github.com/sponsors/nschloe"

[project.entry-points.pytest11]
Expand Down
1 change: 0 additions & 1 deletion src/pytest_codeblocks/__about__.py

This file was deleted.

2 changes: 0 additions & 2 deletions src/pytest_codeblocks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from . import plugin
from .__about__ import __version__
from .main import CodeBlock, extract_from_buffer, extract_from_file

__all__ = [
"CodeBlock",
"extract_from_buffer",
"extract_from_file",
"plugin",
"__version__",
]
14 changes: 6 additions & 8 deletions src/pytest_codeblocks/main.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
from __future__ import annotations

import contextlib
import re
import sys
import warnings

# namedtuple with default arguments
# <https://stackoverflow.com/a/18348004/353337>
from dataclasses import dataclass, field
from io import StringIO
from pathlib import Path
Expand All @@ -20,7 +16,7 @@ class CodeBlock:
expected_output: str | None = None
expected_output_ignore_whitespace: bool = False
importorskip: str | None = None
marks: list[str] = field(default_factory=lambda: [])
marks: list[str] = field(default_factory=list)


def extract_from_file(
Expand Down Expand Up @@ -73,7 +69,6 @@ def extract_from_buffer(f, max_num_lines: int = 10000) -> list[CodeBlock]:
)
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":
Expand Down Expand Up @@ -171,6 +166,7 @@ def extract_from_buffer(f, max_num_lines: int = 10000) -> list[CodeBlock]:
expected_output_ignore_whitespace
)
expected_output_block = None
expected_output_ignore_whitespace = False

else:
out.append(
Expand All @@ -195,5 +191,7 @@ def stdout_io(stdout=None):
if stdout is None:
stdout = StringIO()
sys.stdout = stdout
yield stdout
sys.stdout = old
try:
yield stdout
finally:
sys.stdout = old
38 changes: 7 additions & 31 deletions src/pytest_codeblocks/plugin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
#
# Take a look at the example
# https://docs.pytest.org/en/stable/example/nonpython.html
#
import subprocess
from pathlib import Path
import re
import sys

import pytest

Expand All @@ -20,17 +16,13 @@ def pytest_addoption(parser):
)


def pytest_collect_file(path, parent):
def pytest_collect_file(file_path, parent):
config = parent.config
path = Path(path)
if config.option.codeblocks and path.suffix == ".md":
return MarkdownFile.from_parent(parent, path=path)
if config.option.codeblocks and file_path.suffix == ".md":
return MarkdownFile.from_parent(parent, path=file_path)


class MarkdownFile(pytest.File):
def __init__(self, **kwargs):
super().__init__(**kwargs)

def collect(self):
for block in extract_from_file(self.path):
if block.syntax not in ["python", "sh", "bash"]:
Expand All @@ -44,14 +36,7 @@ def collect(self):
out.obj = block

for mark in block.marks:
# A common thing is
#
# pytest.mark.skipif(sys.version_info < (3, 10), reason="...")
#
# which needs sys. Import it here.
import sys # noqa: F401

out.add_marker(eval(mark))
out.add_marker(eval(mark, {"sys": sys, "pytest": pytest}))

yield out

Expand All @@ -62,13 +47,12 @@ def __init__(self, name, parent, obj=None):
self.obj = obj

def runtest(self):
assert self.obj is not None
output = None

if self.obj.importorskip is not None:
try:
__import__(self.obj.importorskip)
except (ImportError, ModuleNotFoundError):
except ImportError:
pytest.skip()

if self.obj.syntax == "python":
Expand All @@ -85,20 +69,17 @@ def runtest(self):
)
output = s.getvalue()
else:
assert self.obj.syntax in ["sh", "bash"]
executable = {
"sh": None,
"bash": "/bin/bash",
"zsh": "/bin/zsh",
}[self.obj.syntax]

# TODO for python 3.7+, stdout=subprocess.PIPE can be replaced
# by capture_output=True
ret = subprocess.run(
self.obj.code,
shell=True,
check=True,
stdout=subprocess.PIPE,
capture_output=True,
executable=executable,
)
output = ret.stdout.decode()
Expand All @@ -119,12 +100,7 @@ def runtest(self):
)

def repr_failure(self, excinfo):
"""Called when self.runtest() raises an exception."""
# if isinstance(excinfo.value, CodeblockException):
return excinfo.value.args[0]
# if excinfo.errisinstance(RuntimeError):
# return excinfo.value.args[0].stdout
# return super().repr_failure(excinfo)

def reportinfo(self):
return (self.path, -1, "code block check")
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest_plugins = ["pytester"]
6 changes: 3 additions & 3 deletions tests/test_comment_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"<!--- pytest-codeblocks:cont --->",
],
)
def test_cont(testdir, comment):
def test_cont(pytester, comment):
string = """
Lorem ipsum
```python
Expand All @@ -22,6 +22,6 @@ def test_cont(testdir, comment):
a + 1
```
"""
testdir.makefile(".md", string)
result = testdir.runpytest("--codeblocks")
pytester.makefile(".md", string)
result = pytester.runpytest("--codeblocks")
result.assert_outcomes(passed=1)
18 changes: 9 additions & 9 deletions tests/test_cont.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def test_cont(testdir):
def test_cont(pytester):
string = """
Lorem ipsum
```python
Expand All @@ -10,12 +10,12 @@ def test_cont(testdir):
a + 1
```
"""
testdir.makefile(".md", string)
result = testdir.runpytest("--codeblocks")
pytester.makefile(".md", string)
result = pytester.runpytest("--codeblocks")
result.assert_outcomes(passed=1)


def test_hidden_cont(testdir):
def test_hidden_cont(pytester):
string = """
Lorem ipsum
<!--
Expand All @@ -29,18 +29,18 @@ def test_hidden_cont(testdir):
a + 1
```
"""
testdir.makefile(".md", string)
result = testdir.runpytest("--codeblocks")
pytester.makefile(".md", string)
result = pytester.runpytest("--codeblocks")
result.assert_outcomes(passed=1)


def test_nocont(testdir):
def test_nocont(pytester):
string = """
<!--pytest-codeblocks:cont-->
```python
1 + 2 + 3
```
"""
testdir.makefile(".md", string)
result = testdir.runpytest("--codeblocks")
pytester.makefile(".md", string)
result = pytester.runpytest("--codeblocks")
result.assert_outcomes(errors=1)
Loading