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

(WIP) Myst parser: .md + {eval-rst} support #31

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- internal: Switch from setup.py to a PEP-621 pyproject.toml, from
[Tony Narlock](https://www.git-pull.com).

- Support for markdown `.md` files and `{eval-rst}` myst-parser directives, from
[Tony Narlock](https://www.git-pull.com) (re: #30, via #31).

## [0.4.0] - 2022-03-30
###
- Drop python2.7 (Fixes #14)
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ lint = [
"black",
"mypy"
]
myst-parser = [
"myst-parser"
]

[project.urls]
homepage = "https://github.com/thisch/pytest-sphinx"
Expand Down
3 changes: 2 additions & 1 deletion src/pytest_sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def pytest_collect_file(


def _is_doctest(config: Config, path: Path, parent: Union[Session, Package]) -> bool:
if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path):
if path.suffix in (".txt", ".rst", ".md") and parent.session.isinitpath(path):
return True
globs = config.getoption("doctestglob") or ["test*.txt"]
assert isinstance(globs, list)
Expand All @@ -97,6 +97,7 @@ def _is_doctest(config: Config, path: Path, parent: Union[Session, Package]) ->

_DIRECTIVE_RE = re.compile(
r"""
(?P<myst_directive>`{3}{eval-rst}\n?)?
\s*\.\.\s
(?P<directive>(testcode|testoutput|testsetup|testcleanup|doctest))
::\s*
Expand Down
94 changes: 79 additions & 15 deletions tests/test_doc2test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
from pytest_sphinx import get_sections


@pytest.mark.parametrize("in_between_content", ["", "\nsome text\nmore text"])
def test_simple(in_between_content: str) -> None:
doc = """
@pytest.mark.parametrize(
"doc",
[
"""
.. testcode::

import pprint
Expand All @@ -20,9 +21,27 @@ def test_simple(in_between_content: str) -> None:

{{'3': 4,
'5': 6}}
""".format(
in_between_content
)
""",
"""
```{{eval-rst}}
.. testcode::

import pprint
pprint.pprint({{'3': 4, '5': 6}})
```
{}
```{{eval-rst}}
.. testoutput::

{{'3': 4,
'5': 6}}
```
""",
],
)
@pytest.mark.parametrize("in_between_content", ["", "\nsome text\nmore text"])
def test_simple(doc: str, in_between_content: str) -> None:
doc = doc.format(in_between_content)

examples = docstring2examples(doc)
assert len(examples) == 1
Expand All @@ -34,8 +53,10 @@ def test_simple(in_between_content: str) -> None:
assert example.lineno == 5


def test_with_options() -> None:
doc = """
@pytest.mark.parametrize(
"doc",
[
"""
.. testcode::

import pprint
Expand All @@ -45,8 +66,27 @@ def test_with_options() -> None:
:options: +NORMALIZE_WHITESPACE, +ELLIPSIS

{'3': 4,
'5': 6}"""
'5': 6}""",
"""
```{eval-rst}
.. testcode::

import pprint
pprint.pprint({'3': 4, '5': 6})
```

```{eval-rst}
.. testoutput::
:options: +NORMALIZE_WHITESPACE, +ELLIPSIS

{'3': 4,
'5': 6}
```
""",
],
ids=["rst", "myst-eval-rst"],
)
def test_with_options(doc: str) -> None:
examples = docstring2examples(doc)
assert len(examples) == 1
example = examples[0]
Expand All @@ -61,9 +101,11 @@ def test_with_options() -> None:
assert example.lineno == 5


def test_indented() -> None:
doc = textwrap.dedent(
"""
@pytest.mark.parametrize(
"doc",
[
textwrap.dedent(
"""
Examples:
some text

Expand All @@ -75,8 +117,29 @@ def test_indented() -> None:

Banana
"""
)
),
textwrap.dedent(
"""
Examples:
some text

```{eval-rst}
.. testcode::

print("Banana")
```

```{eval-rst}
.. testoutput::

Banana
```
"""
),
],
ids=["rst", "myst-eval-rst"],
)
def test_indented(doc: str) -> None:
examples = docstring2examples(doc)
assert len(examples) == 1
example = examples[0]
Expand All @@ -87,9 +150,10 @@ def test_indented() -> None:
assert example.lineno == 7


def test_cartopy() -> None:
@pytest.mark.parametrize("file_type", ["rst", "md"])
def test_cartopy(file_type: str) -> None:
rstpath = os.path.join(
os.path.dirname(__file__), "testdata", "using_the_shapereader.rst"
os.path.dirname(__file__), "testdata", f"using_the_shapereader.{file_type}"
)
with open(rstpath) as fh:
sections = get_sections(fh.read())
Expand Down
74 changes: 60 additions & 14 deletions tests/test_sphinx_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import textwrap
from pathlib import Path
from typing import Iterator
from typing import List
from typing import Optional
from typing import Union

import pytest
Expand Down Expand Up @@ -37,17 +39,27 @@ def __init__(self, tmp_path: Path) -> None:
)

def __call__(
self, rst_file_content: str, must_raise: bool = False, sphinxopts: None = None
self,
file_content: str,
must_raise: bool = False,
file_type: str = "rst",
sphinxopts: Optional[List[str]] = None,
) -> str:
index_rst = self.tmp_path / "source" / "index.rst"
rst_file_content = textwrap.dedent(rst_file_content)
index_rst.write_text(rst_file_content, encoding="utf-8")
index_file = self.tmp_path / "source" / f"index.{file_type}"
file_content = textwrap.dedent(file_content)
index_file.write_text(file_content, encoding="utf-8")
if file_type == "md": # Delete sphinx-quickstart's .rst file
index_rst.unlink()
logger.info("CWD: %s", os.getcwd())
logger.info("content of index.rst:\n%s", rst_file_content)
logger.info(f"content of index.{file_type}:\n%s", file_content)

cmd = ["sphinx-build", "-M", "doctest", "source", ""]
if sphinxopts:
cmd.append(sphinxopts)
if sphinxopts is not None:
if isinstance(sphinxopts, list):
cmd.extend(sphinxopts)
else:
cmd.append(sphinxopts)

def to_str(subprocess_output: Union[str, bytes]) -> str:
if isinstance(subprocess_output, bytes):
Expand All @@ -65,7 +77,9 @@ def to_str(subprocess_output: Union[str, bytes]) -> str:


@pytest.fixture
def sphinx_tester(tmpdir: LocalPath) -> Iterator[SphinxDoctestRunner]:
def sphinx_tester(
tmpdir: LocalPath, request: pytest.FixtureRequest
) -> Iterator[SphinxDoctestRunner]:
with tmpdir.as_cwd():
yield SphinxDoctestRunner(tmpdir)

Expand Down Expand Up @@ -128,20 +142,52 @@ def test_testcode(
plugin_result = testdir.runpytest("--doctest-glob=index.rst").stdout
plugin_result.fnmatch_lines(["*=== 1 passed in *"])

def test_doctest(
self, testdir: Testdir, sphinx_tester: SphinxDoctestRunner
) -> None:
code = """
@pytest.mark.parametrize(
"file_type,code",
[
[
"rst",
"""
.. doctest::

>>> print("msg from testcode directive")
msg from testcode directive
"""
""",
],
[
"md",
"""
```{eval-rst}
.. doctest::

sphinx_output = sphinx_tester(code)
>>> print("msg from testcode directive")
msg from testcode directive

```

""".strip(),
],
],
)
def test_doctest(
self,
testdir: Testdir,
sphinx_tester: SphinxDoctestRunner,
file_type: str,
code: str,
) -> None:
if file_type == "md": # Skip if no myst-parser
pytest.importorskip("myst_parser")
sphinx_output = sphinx_tester(
code,
file_type=file_type,
sphinxopts=None
if file_type == "rst"
else ["-D", "extensions=myst_parser,sphinx.ext.doctest"],
)
assert "1 items passed all tests" in sphinx_output

plugin_result = testdir.runpytest("--doctest-glob=index.rst").stdout
plugin_result = testdir.runpytest(f"--doctest-glob=index.{file_type}").stdout
plugin_result.fnmatch_lines(["*=== 1 passed in *"])

def test_doctest_multiple(
Expand Down
Loading