Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pytest-sphinx for
openSUSE:Factory checked in at 2023-05-10 16:18:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pytest-sphinx (Old)
and /work/SRC/openSUSE:Factory/.python-pytest-sphinx.new.1533 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-sphinx"
Wed May 10 16:18:54 2023 rev:6 rq:1085834 version:0.5.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-pytest-sphinx/python-pytest-sphinx.changes
2022-04-25 23:35:10.878387369 +0200
+++
/work/SRC/openSUSE:Factory/.python-pytest-sphinx.new.1533/python-pytest-sphinx.changes
2023-05-10 16:18:55.623117586 +0200
@@ -1,0 +2,8 @@
+Mon May 8 08:26:45 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 0.5.0:
+ * Mention in the README that the doctest directive is
+ supported.
+ * CI improvements
+
+-------------------------------------------------------------------
Old:
----
pytest-sphinx-0.4.0.tar.gz
New:
----
pytest-sphinx-0.5.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pytest-sphinx.spec ++++++
--- /var/tmp/diff_new_pack.EMpAtM/_old 2023-05-10 16:18:56.063120189 +0200
+++ /var/tmp/diff_new_pack.EMpAtM/_new 2023-05-10 16:18:56.067120213 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-pytest-sphinx
#
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -19,13 +19,15 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-pytest-sphinx
-Version: 0.4.0
+Version: 0.5.0
Release: 0
Summary: Doctest plugin for pytest with support for Sphinx-specific
doctest-directives
License: BSD-3-Clause
URL: https://github.com/thisch/pytest-sphinx
Source:
https://github.com/thisch/pytest-sphinx/archive/v%{version}.tar.gz#/pytest-sphinx-%{version}.tar.gz
+BuildRequires: %{python_module pip}
BuildRequires: %{python_module setuptools}
+BuildRequires: %{python_module wheel}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
Requires: python-pytest >= 7.0.0
@@ -43,10 +45,10 @@
%setup -q -n pytest-sphinx-%{version}
%build
-%python_build
+%pyproject_wheel
%install
-%python_install
+%pyproject_install
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
@@ -55,6 +57,8 @@
%files %{python_files}
%doc README.rst
%license LICENSE
-%{python_sitelib}/*
+%{python_sitelib}/pytest_sphinx.py
+%{python_sitelib}/pytest_sphinx-%{version}.dist-info
+%pycache_only %{python_sitelib}/__pycache__/*
%changelog
++++++ pytest-sphinx-0.4.0.tar.gz -> pytest-sphinx-0.5.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-sphinx-0.4.0/.github/workflows/pypi-upload.yml
new/pytest-sphinx-0.5.0/.github/workflows/pypi-upload.yml
--- old/pytest-sphinx-0.4.0/.github/workflows/pypi-upload.yml 2022-03-30
22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/.github/workflows/pypi-upload.yml 2022-09-06
22:46:09.000000000 +0200
@@ -13,7 +13,10 @@
- uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v4
+ with:
+ cache: 'pip'
+ cache-dependency-path: 'pyproject.toml'
- name: Install latest pip, build, twine
run: |
@@ -26,4 +29,4 @@
env:
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: |
- twine upload --verbose -u '__token__' dist/*
\ No newline at end of file
+ twine upload --verbose -u '__token__' dist/*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/.github/workflows/test.yml
new/pytest-sphinx-0.5.0/.github/workflows/test.yml
--- old/pytest-sphinx-0.4.0/.github/workflows/test.yml 2022-03-30
22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/.github/workflows/test.yml 2022-09-06
22:46:09.000000000 +0200
@@ -22,22 +22,34 @@
strategy:
fail-fast: false
matrix:
- python-version: ["3.7", "3.8", "3.9", "3.10"]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11-dev"]
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
+ cache: 'pip'
+ cache-dependency-path: 'pyproject.toml'
- name: Install dependencies
run: |
+ python -m pip install -e .[lint]
python -m pip install --upgrade pip
python -m pip install --upgrade tox
+ - name: Lint (flake8)
+ run: flake8
+
+ - name: Lint (black)
+ run: black --check .
+
+ - name: Lint (isort)
+ run: isort --check .
+
- name: Unit tests
run: |
tox -e ci-py -- -v --color=yes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/.gitignore
new/pytest-sphinx-0.5.0/.gitignore
--- old/pytest-sphinx-0.4.0/.gitignore 2022-03-30 22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/.gitignore 2022-09-06 22:46:09.000000000 +0200
@@ -9,6 +9,7 @@
# Distribution / packaging
.Python
env/
+.*env*
build/
develop-eggs/
dist/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/.isort.cfg
new/pytest-sphinx-0.5.0/.isort.cfg
--- old/pytest-sphinx-0.4.0/.isort.cfg 1970-01-01 01:00:00.000000000 +0100
+++ new/pytest-sphinx-0.5.0/.isort.cfg 2022-09-06 22:46:09.000000000 +0200
@@ -0,0 +1,6 @@
+[settings]
+line_length=90
+default_section=THIRDPARTY
+no_lines_before=LOCALFOLDER
+force_single_line=True
+sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/.pre-commit-config.yaml
new/pytest-sphinx-0.5.0/.pre-commit-config.yaml
--- old/pytest-sphinx-0.4.0/.pre-commit-config.yaml 1970-01-01
01:00:00.000000000 +0100
+++ new/pytest-sphinx-0.5.0/.pre-commit-config.yaml 2022-09-06
22:46:09.000000000 +0200
@@ -0,0 +1,16 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+repos:
+ - repo: https://github.com/psf/black
+ rev: 22.3.0
+ hooks:
+ - id: black
+ # nice but doesn't support sphinx syntax - see
https://github.com/Lucas-C/pre-commit-hooks-markup/issues/13
+ # - repo: https://github.com/Lucas-C/pre-commit-hooks-markup
+ # rev: v1.0.1
+ # hooks:
+ # - id: rst-linter
+ - repo: https://github.com/pycqa/isort
+ rev: 5.10.1
+ hooks:
+ - id: isort
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/CHANGELOG.md
new/pytest-sphinx-0.5.0/CHANGELOG.md
--- old/pytest-sphinx-0.4.0/CHANGELOG.md 2022-03-30 22:29:23.000000000
+0200
+++ new/pytest-sphinx-0.5.0/CHANGELOG.md 2022-09-06 22:46:09.000000000
+0200
@@ -7,6 +7,16 @@
## [Unreleased]
###
+## [0.5.0] - 2022-09-06
+###
+ - Mention in the README that the doctest directive is supported.
+ - internal: Improve CI setup, from [Tony
+ Narlock](https://www.git-pull.com).
+ - internal: Switch to a src/ structure, from [Tony
+ Narlock](https://www.git-pull.com).
+ - internal: Switch from setup.py to a PEP-621 pyproject.toml, from
+ [Tony Narlock](https://www.git-pull.com).
+
## [0.4.0] - 2022-03-30
###
- Drop python2.7 (Fixes #14)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/MANIFEST.in
new/pytest-sphinx-0.5.0/MANIFEST.in
--- old/pytest-sphinx-0.4.0/MANIFEST.in 2022-03-30 22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-include LICENSE
-include README.rst
-
-recursive-exclude * __pycache__
-recursive-exclude * *.py[co]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/README.rst
new/pytest-sphinx-0.5.0/README.rst
--- old/pytest-sphinx-0.4.0/README.rst 2022-03-30 22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/README.rst 2022-09-06 22:46:09.000000000 +0200
@@ -14,6 +14,7 @@
Features
--------
+* support for the ``doctest`` directive
* support for ``testcode`` and ``testoutput`` directives
* support for ``testsetup`` and ``testcleanup`` is planned (pull-requests
welcome)
* support for parsing global optionflags (``doctest_optionflags``) from
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/pyproject.toml
new/pytest-sphinx-0.5.0/pyproject.toml
--- old/pytest-sphinx-0.4.0/pyproject.toml 1970-01-01 01:00:00.000000000
+0100
+++ new/pytest-sphinx-0.5.0/pyproject.toml 2022-09-06 22:46:09.000000000
+0200
@@ -0,0 +1,55 @@
+[project]
+name = "pytest-sphinx"
+version = "0.5.0"
+description = "Doctest plugin for pytest with support for Sphinx-specific
doctest-directives"
+readme = "README.rst"
+requires-python = ">=3.7"
+license = { file = "LICENSE" }
+keywords = ["sphinx", "pytest", "rst"]
+authors = [
+ { name="Thomas Hisch", email="[email protected]" }
+]
+maintainers = [
+ { name="Thomas Hisch", email="[email protected]" }
+]
+
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Framework :: Pytest",
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Testing",
+ "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 :: Implementation :: CPython",
+ "Operating System :: OS Independent",
+ "License :: OSI Approved :: BSD License",
+]
+
+# Requirements
+dependencies = [
+ "pytest >=7.0.0",
+]
+
+[project.optional-dependencies]
+lint = [
+ "isort >= 5",
+ "flake8",
+ "black"
+]
+
+[project.urls]
+homepage = "https://github.com/thisch/pytest-sphinx"
+
+[project.entry-points."pytest11"]
+"sphinx" = "pytest_sphinx"
+
+[tool.isort]
+profile = "black"
+
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/pytest_sphinx.py
new/pytest-sphinx-0.5.0/pytest_sphinx.py
--- old/pytest-sphinx-0.4.0/pytest_sphinx.py 2022-03-30 22:29:23.000000000
+0200
+++ new/pytest-sphinx-0.5.0/pytest_sphinx.py 1970-01-01 01:00:00.000000000
+0100
@@ -1,535 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-http://www.sphinx-doc.org/en/stable/ext/doctest.html
-https://github.com/sphinx-doc/sphinx/blob/master/sphinx/ext/doctest.py
-
-* TODO
-** CLEANUP: use the sphinx directive parser from the sphinx project
-"""
-
-import doctest
-import enum
-from pathlib import Path
-import re
-import sys
-import textwrap
-import traceback
-
-import _pytest.doctest
-import pytest
-from _pytest.doctest import _is_mocked
-from _pytest.doctest import _patch_unwrap_mock_aware
-from _pytest.doctest import DoctestItem
-from _pytest.pathlib import import_path
-
-
-class SphinxDoctestDirectives(enum.Enum):
- TESTCODE = 1
- TESTOUTPUT = 2
- TESTSETUP = 3
- TESTCLEANUP = 4
- DOCTEST = 5
-
-
-_DIRECTIVES_W_OPTIONS = (
- SphinxDoctestDirectives.TESTOUTPUT,
- SphinxDoctestDirectives.DOCTEST,
-)
-_DIRECTIVES_W_SKIPIF = (
- SphinxDoctestDirectives.TESTCODE,
- SphinxDoctestDirectives.TESTOUTPUT,
- SphinxDoctestDirectives.TESTSETUP,
- SphinxDoctestDirectives.TESTCLEANUP,
- SphinxDoctestDirectives.DOCTEST,
-)
-
-
-def pytest_collect_file(path, parent):
- config = parent.config
- if path.ext == ".py":
- if config.option.doctestmodules:
- return SphinxDoctestModule.from_parent(parent,
path=Path(path.strpath))
- elif _is_doctest(config, path, parent):
- return SphinxDoctestTextfile.from_parent(parent,
path=Path(path.strpath))
-
-
-def _is_doctest(config, path, parent):
- if path.ext in (".txt", ".rst") and parent.session.isinitpath(path):
- return True
- globs = config.getoption("doctestglob") or ["test*.txt"]
- for glob in globs:
- if path.check(fnmatch=glob):
- return True
- return False
-
-
-# This regular expression looks for option directives in the expected output
-# (testoutput) code of an example. Option directives are comments starting
-# with ":options:".
-_OPTION_DIRECTIVE_RE = re.compile(r':options:\s*([^\n\'"]*)$')
-_OPTION_SKIPIF_RE = re.compile(r':skipif:\s*([^\n\'"]*)$')
-
-_DIRECTIVE_RE = re.compile(
- r"\s*\.\. ("
- r"testcode|testoutput|testsetup|testcleanup|doctest"
- r')::\s*([^\n\'"]*)$'
-)
-
-
-def _split_into_body_and_options(section_content):
- """Parse the the full content of a directive and split it.
-
- It is split into a string, where the options (:options:, :hide: and
- :skipif:) are removed, and into options.
-
- If there are options in `section_content`, they have to appear at the
- very beginning. The first line that is not an option (:options:, :hide:
- and :skipif:) and not a newline is the first line of the string that is
- returned (`remaining`).
-
- Parameters
- ----------
- section_content : str
- String consisting of optional options (:skipif:, :hide:
- or :options:), and of a body.
-
- Returns
- -------
- body : str
- skipif_expr : str or None
- flag_settings : dict
-
- Raises
- ------
- ValueError
- * If options and the body of the section are not
- separated by a newline.
- * If the body of the section is empty.
-
- """
- lines = section_content.strip().splitlines()
-
- skipif_expr = None
- flag_settings = {}
- i = 0
- for line in lines:
- stripped = line.strip()
- if _OPTION_SKIPIF_RE.match(stripped):
- skipif_expr = _OPTION_SKIPIF_RE.match(stripped).group(1)
- i += 1
- elif _OPTION_DIRECTIVE_RE.match(stripped):
- option_strings = (
- _OPTION_DIRECTIVE_RE.match(stripped).group(1).replace(",", "
").split()
- )
- for option in option_strings:
- if (
- option[0] not in "+-"
- or option[1:] not in doctest.OPTIONFLAGS_BY_NAME
- ):
- raise ValueError(
- "doctest " "has an invalid option {}".format(option)
- )
- flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]]
- flag_settings[flag] = option[0] == "+"
- i += 1
- elif stripped == ":hide:":
- i += 1
- else:
- break
-
- if i == len(lines):
- raise ValueError("no code/output")
-
- body = "\n".join(lines[i:]).lstrip()
- if not body:
- raise ValueError("no code/output")
-
- if i and lines[i].strip():
- # no newline between option block and body
- raise ValueError("invalid option block: {!r}".format(section_content))
-
- return body, skipif_expr, flag_settings
-
-
-def _get_next_textoutputsections(sections, index):
- """Yield successive TESTOUTPUT sections."""
- for j in range(index, len(sections)):
- section = sections[j]
- if section.directive == SphinxDoctestDirectives.TESTOUTPUT:
- yield section
- else:
- break
-
-
-class Section(object):
- def __init__(self, directive, content, lineno, groups=None):
- super(Section, self).__init__()
- self.directive = directive
- self.groups = groups
- self.lineno = lineno
- body, skipif_expr, options = _split_into_body_and_options(content)
-
- if skipif_expr and self.directive not in _DIRECTIVES_W_SKIPIF:
- raise ValueError(":skipif: not allowed in
{}".format(self.directive))
- if options and self.directive not in _DIRECTIVES_W_OPTIONS:
- raise ValueError(":options: not allowed in
{}".format(self.directive))
- self.body = body
- self.skipif_expr = skipif_expr
- self.options = options
-
-
-def get_sections(docstring):
- lines = textwrap.dedent(docstring).splitlines()
- sections = []
-
- def _get_indentation(line):
- return len(line) - len(line.lstrip())
-
- def add_match(directive, i, j, groups):
- sections.append(
- Section(
- directive,
- textwrap.dedent("\n".join(lines[i + 1 : j])),
- lineno=j - 1,
- groups=groups,
- )
- )
-
- i = 0
- while True:
- try:
- line = lines[i]
- except IndexError:
- break
-
- match = _DIRECTIVE_RE.match(line)
- if match:
- directive = getattr(SphinxDoctestDirectives,
match.group(1).upper())
- groups = [x.strip() for x in (match.group(2) or
"default").split(",")]
- indentation = _get_indentation(line)
- # find the end of the block
- j = i
- while True:
- j += 1
- try:
- block_line = lines[j]
- except IndexError:
- add_match(directive, i, j, groups)
- break
- if block_line.lstrip() and _get_indentation(block_line) <=
indentation:
- add_match(directive, i, j, groups)
- i = j - 1
- break
- i += 1
- return sections
-
-
-def docstring2examples(docstring, globs=None):
- """
- Parse all sphinx test directives in the docstring and create a
- list of examples.
- """
- # TODO subclass doctest.DocTestParser instead?
-
- if not globs:
- globs = {}
-
- sections = get_sections(docstring)
-
- def get_testoutput_section_data(section):
- want = section.body
- exc_msg = None
- options = {}
-
- if section.skipif_expr and eval(section.skipif_expr, globs):
- want = ""
- else:
- options = section.options
- match = doctest.DocTestParser._EXCEPTION_RE.match(want)
- if match:
- exc_msg = match.group("msg")
-
- return want, options, section.lineno, exc_msg
-
- examples = []
- for i, current_section in enumerate(sections):
- # TODO support SphinxDoctestDirectives.TESTSETUP, ...
- if current_section.directive == SphinxDoctestDirectives.TESTCODE:
- next_testoutput_sections = _get_next_textoutputsections(sections,
i + 1)
- section_data_seq = [
- get_testoutput_section_data(s) for s in
next_testoutput_sections
- ]
-
- num_unskipped_sections = len([d for d in section_data_seq if d[0]])
- if num_unskipped_sections > 1:
- raise ValueError("There are multiple unskipped TESTOUTPUT
sections")
-
- if num_unskipped_sections:
- want, options, _, exc_msg = next(d for d in section_data_seq
if d[0])
- else:
- # no unskipped testoutput section
- # do we really need doctest.Example to test
- # independent TESTCODE sections?
- want, options, exc_msg = "", {}, None
-
- if current_section.skipif_expr and
eval(current_section.skipif_expr, globs):
- # TODO add the doctest.Example to `examples` but mark it as
- # skipped.
- continue
-
- examples.append(
- doctest.Example(
- source=current_section.body,
- want=want,
- exc_msg=exc_msg,
- # we want to see the ..testcode lines in the
- # console output but not the ..testoutput
- # lines
- # TODO why do we want to hide testoutput??
- lineno=current_section.lineno,
- options=options,
- )
- )
- return examples
-
-
-class SphinxDocTestRunner(doctest.DebugRunner):
- """
- overwrite doctest.DocTestRunner.__run, since it uses 'single' for the
- `compile` function instead of 'exec'.
- """
-
- def _DocTestRunner__run(self, test, compileflags, out):
- """
- Run the examples in `test`.
-
- Write the outcome of each example with one of the
- `DocTestRunner.report_*` methods, using the writer function
- `out`. `compileflags` is the set of compiler flags that should
- be used to execute examples. Return a tuple `(f, t)`, where `t`
- is the number of examples tried, and `f` is the number of
- examples that failed. The examples are run in the namespace
- `test.globs`.
-
- """
- # Keep track of the number of failures and tries.
- failures = tries = 0
-
- # Save the option flags (since option directives can be used
- # to modify them).
- original_optionflags = self.optionflags
-
- SUCCESS, FAILURE, BOOM = range(3) # `outcome` state
-
- check = self._checker.check_output
-
- # Process each example.
- for examplenum, example in enumerate(test.examples):
-
- # If REPORT_ONLY_FIRST_FAILURE is set, then suppress
- # reporting after the first failure.
- quiet = (
- self.optionflags & doctest.REPORT_ONLY_FIRST_FAILURE and
failures > 0
- )
-
- # Merge in the example's options.
- self.optionflags = original_optionflags
- if example.options:
- for (optionflag, val) in example.options.items():
- if val:
- self.optionflags |= optionflag
- else:
- self.optionflags &= ~optionflag
-
- # If 'SKIP' is set, then skip this example.
- if self.optionflags & doctest.SKIP:
- continue
-
- # Record that we started this example.
- tries += 1
- if not quiet:
- self.report_start(out, test, example)
-
- # Use a special filename for compile(), so we can retrieve
- # the source code during interactive debugging (see
- # __patched_linecache_getlines).
- filename = "<doctest %s[%d]>" % (test.name, examplenum)
-
- # Run the example in the given context (globs), and record
- # any exception that gets raised. (But don't intercept
- # keyboard interrupts.)
- try:
- # Don't blink! This is where the user's code gets run.
- exec(
- compile(example.source, filename, "exec", compileflags, 1),
- test.globs,
- )
- self.debugger.set_continue() # ==== Example Finished ====
- exception = None
- except KeyboardInterrupt:
- raise
- except Exception:
- exception = sys.exc_info()
- self.debugger.set_continue() # ==== Example Finished ====
-
- got = self._fakeout.getvalue() # the actual output
- self._fakeout.truncate(0)
- outcome = FAILURE # guilty until proved innocent or insane
-
- # If the example executed without raising any exceptions,
- # verify its output.
- if exception is None:
- if check(example.want, got, self.optionflags):
- outcome = SUCCESS
-
- # The example raised an exception: check if it was expected.
- else:
- exc_msg = traceback.format_exception_only(*exception[:2])[-1]
- if not quiet:
- got += doctest._exception_traceback(exception)
-
- # If `example.exc_msg` is None, then we weren't expecting
- # an exception.
- if example.exc_msg is None:
- outcome = BOOM
-
- # We expected an exception: see whether it matches.
- elif check(example.exc_msg, exc_msg, self.optionflags):
- outcome = SUCCESS
-
- # Another chance if they didn't care about the detail.
- elif self.optionflags & doctest.IGNORE_EXCEPTION_DETAIL:
- if check(
- doctest._strip_exception_details(example.exc_msg),
- doctest._strip_exception_details(exc_msg),
- self.optionflags,
- ):
- outcome = SUCCESS
-
- # Report the outcome.
- if outcome is SUCCESS:
- if not quiet:
- self.report_success(out, test, example, got)
- elif outcome is FAILURE:
- if not quiet:
- self.report_failure(out, test, example, got)
- failures += 1
- elif outcome is BOOM:
- if not quiet:
- self.report_unexpected_exception(out, test, example,
exception)
- failures += 1
- else:
- assert False, ("unknown outcome", outcome)
-
- if failures and self.optionflags & doctest.FAIL_FAST:
- break
-
- # Restore the option flags (in case they were modified)
- self.optionflags = original_optionflags
-
- # Record and return the number of failures and tries.
- self._DocTestRunner__record_outcome(test, failures, tries)
- return doctest.TestResults(failures, tries)
-
-
-class SphinxDocTestParser(object):
- def get_doctest(self, docstring, globs, name, filename, lineno):
- # TODO document why we need to overwrite? get_doctest
- return doctest.DocTest(
- examples=docstring2examples(docstring, globs=globs),
- globs=globs,
- name=name,
- filename=filename,
- lineno=lineno,
- docstring=docstring,
- )
-
-
-class SphinxDoctestTextfile(pytest.Module):
- obj = None
-
- def collect(self):
- # inspired by doctest.testfile; ideally we would use it directly,
- # but it doesn't support passing a custom checker
- encoding = self.config.getini("doctest_encoding")
- text = self.fspath.read_text(encoding)
- name = self.fspath.basename
-
- optionflags = _pytest.doctest.get_optionflags(self)
- runner = SphinxDocTestRunner(
- verbose=0,
- optionflags=optionflags,
- checker=_pytest.doctest._get_checker(),
- )
-
- test = doctest.DocTest(
- examples=docstring2examples(text),
- globs={},
- name=name,
- filename=name,
- lineno=0,
- docstring=text,
- )
-
- if test.examples:
- yield DoctestItem.from_parent(
- parent=self, name=test.name, runner=runner, dtest=test
- )
-
-
-class SphinxDoctestModule(pytest.Module):
- def collect(self):
- if self.fspath.basename == "conftest.py":
- module = self.config.pluginmanager._importconftest(
- self.path,
- self.config.getoption("importmode"),
- rootpath=self.config.rootpath,
- )
- else:
- try:
- module = import_path(self.path, root=self.config.rootpath)
- except ImportError:
- if self.config.getvalue("doctest_ignore_import_errors"):
- pytest.skip("unable to import module %r" % self.path)
- else:
- raise
- optionflags = _pytest.doctest.get_optionflags(self)
-
- class MockAwareDocTestFinder(doctest.DocTestFinder):
- """
- a hackish doctest finder that overrides stdlib internals to fix
- a stdlib bug
- https://github.com/pytest-dev/pytest/issues/3456
- https://bugs.python.org/issue25532
-
- fix taken from https://github.com/pytest-dev/pytest/pull/4212/
- """
-
- def _find(self, tests, obj, name, module, source_lines, globs,
seen):
- if _is_mocked(obj):
- return
- with _patch_unwrap_mock_aware():
- doctest.DocTestFinder._find(
- self,
- tests,
- obj,
- name,
- module,
- source_lines,
- globs,
- seen,
- )
-
- finder = MockAwareDocTestFinder(parser=SphinxDocTestParser())
-
- runner = SphinxDocTestRunner(
- verbose=0,
- optionflags=optionflags,
- checker=_pytest.doctest._get_checker(),
- )
-
- for test in finder.find(module, module.__name__):
- if test.examples:
- yield DoctestItem.from_parent(
- parent=self, name=test.name, runner=runner, dtest=test
- )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/setup.cfg
new/pytest-sphinx-0.5.0/setup.cfg
--- old/pytest-sphinx-0.4.0/setup.cfg 1970-01-01 01:00:00.000000000 +0100
+++ new/pytest-sphinx-0.5.0/setup.cfg 2022-09-06 22:46:09.000000000 +0200
@@ -0,0 +1,4 @@
+[flake8]
+exclude = .*/,.tox,*.egg
+max-line-length = 88
+extend-ignore = E203
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/setup.py
new/pytest-sphinx-0.5.0/setup.py
--- old/pytest-sphinx-0.4.0/setup.py 2022-03-30 22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/setup.py 1970-01-01 01:00:00.000000000 +0100
@@ -1,47 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import codecs
-import os
-
-from setuptools import setup
-
-
-def read(fname):
- file_path = os.path.join(os.path.dirname(__file__), fname)
- return codecs.open(file_path, encoding="utf-8").read()
-
-
-setup(
- name="pytest-sphinx",
- version="0.4.0",
- author="Thomas Hisch",
- author_email="[email protected]",
- maintainer="Thomas Hisch",
- maintainer_email="[email protected]",
- license="BSD-3",
- url="https://github.com/thisch/pytest-sphinx",
- description=(
- "Doctest plugin for pytest with support for "
- "Sphinx-specific doctest-directives"
- ),
- long_description=read("README.rst"),
- py_modules=["pytest_sphinx"],
- install_requires=["pytest>=7.0.0"],
- classifiers=[
- "Development Status :: 4 - Beta",
- "Framework :: Pytest",
- "Intended Audience :: Developers",
- "Topic :: Software Development :: Testing",
- "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 :: Implementation :: CPython",
- "Operating System :: OS Independent",
- "License :: OSI Approved :: BSD License",
- ],
- entry_points={"pytest11": ["sphinx = pytest_sphinx"]},
-)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/src/pytest_sphinx.py
new/pytest-sphinx-0.5.0/src/pytest_sphinx.py
--- old/pytest-sphinx-0.4.0/src/pytest_sphinx.py 1970-01-01
01:00:00.000000000 +0100
+++ new/pytest-sphinx-0.5.0/src/pytest_sphinx.py 2022-09-06
22:46:09.000000000 +0200
@@ -0,0 +1,541 @@
+"""
+http://www.sphinx-doc.org/en/stable/ext/doctest.html
+https://github.com/sphinx-doc/sphinx/blob/master/sphinx/ext/doctest.py
+
+* TODO
+** CLEANUP: use the sphinx directive parser from the sphinx project
+"""
+
+import doctest
+import enum
+import re
+import sys
+import textwrap
+import traceback
+from pathlib import Path
+
+import _pytest.doctest
+import pytest
+from _pytest.doctest import DoctestItem
+from _pytest.doctest import _is_mocked
+from _pytest.doctest import _patch_unwrap_mock_aware
+from _pytest.pathlib import import_path
+
+
+class SphinxDoctestDirectives(enum.Enum):
+ TESTCODE = 1
+ TESTOUTPUT = 2
+ TESTSETUP = 3
+ TESTCLEANUP = 4
+ DOCTEST = 5
+
+
+_DIRECTIVES_W_OPTIONS = (
+ SphinxDoctestDirectives.TESTOUTPUT,
+ SphinxDoctestDirectives.DOCTEST,
+)
+_DIRECTIVES_W_SKIPIF = (
+ SphinxDoctestDirectives.TESTCODE,
+ SphinxDoctestDirectives.TESTOUTPUT,
+ SphinxDoctestDirectives.TESTSETUP,
+ SphinxDoctestDirectives.TESTCLEANUP,
+ SphinxDoctestDirectives.DOCTEST,
+)
+
+
+def pytest_collect_file(path, parent):
+ config = parent.config
+ if path.ext == ".py":
+ if config.option.doctestmodules:
+ return SphinxDoctestModule.from_parent(parent,
path=Path(path.strpath))
+ elif _is_doctest(config, path, parent):
+ return SphinxDoctestTextfile.from_parent(parent,
path=Path(path.strpath))
+
+
+def _is_doctest(config, path, parent):
+ if path.ext in (".txt", ".rst") and parent.session.isinitpath(path):
+ return True
+ globs = config.getoption("doctestglob") or ["test*.txt"]
+ for glob in globs:
+ if path.check(fnmatch=glob):
+ return True
+ return False
+
+
+# This regular expression looks for option directives in the expected output
+# (testoutput) code of an example. Option directives are comments starting
+# with ":options:".
+_OPTION_DIRECTIVE_RE = re.compile(r':options:\s*([^\n\'"]*)$')
+_OPTION_SKIPIF_RE = re.compile(r':skipif:\s*([^\n\'"]*)$')
+
+_DIRECTIVE_RE = re.compile(
+ r"""
+ \s*\.\.\s
+ (?P<directive>(testcode|testoutput|testsetup|testcleanup|doctest))
+ ::\s*
+ (?P<argument>([^\n'"]*))
+ $
+ """,
+ re.VERBOSE,
+)
+
+
+def _split_into_body_and_options(section_content):
+ """Parse the the full content of a directive and split it.
+
+ It is split into a string, where the options (:options:, :hide: and
+ :skipif:) are removed, and into options.
+
+ If there are options in `section_content`, they have to appear at the
+ very beginning. The first line that is not an option (:options:, :hide:
+ and :skipif:) and not a newline is the first line of the string that is
+ returned (`remaining`).
+
+ Parameters
+ ----------
+ section_content : str
+ String consisting of optional options (:skipif:, :hide:
+ or :options:), and of a body.
+
+ Returns
+ -------
+ body : str
+ skipif_expr : str or None
+ flag_settings : dict
+
+ Raises
+ ------
+ ValueError
+ * If options and the body of the section are not
+ separated by a newline.
+ * If the body of the section is empty.
+
+ """
+ lines = section_content.strip().splitlines()
+
+ skipif_expr = None
+ flag_settings = {}
+ i = 0
+ for line in lines:
+ stripped = line.strip()
+ if _OPTION_SKIPIF_RE.match(stripped):
+ skipif_expr = _OPTION_SKIPIF_RE.match(stripped).group(1)
+ i += 1
+ elif _OPTION_DIRECTIVE_RE.match(stripped):
+ option_strings = (
+ _OPTION_DIRECTIVE_RE.match(stripped).group(1).replace(",", "
").split()
+ )
+ for option in option_strings:
+ if (
+ option[0] not in "+-"
+ or option[1:] not in doctest.OPTIONFLAGS_BY_NAME
+ ):
+ raise ValueError(f"doctest has an invalid option {option}")
+ flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]]
+ flag_settings[flag] = option[0] == "+"
+ i += 1
+ elif stripped == ":hide:":
+ i += 1
+ else:
+ break
+
+ if i == len(lines):
+ raise ValueError("no code/output")
+
+ body = "\n".join(lines[i:]).lstrip()
+ if not body:
+ raise ValueError("no code/output")
+
+ if i and lines[i].strip():
+ # no newline between option block and body
+ raise ValueError(f"invalid option block: {section_content!r}")
+
+ return body, skipif_expr, flag_settings
+
+
+def _get_next_textoutputsections(sections, index):
+ """Yield successive TESTOUTPUT sections."""
+ for j in range(index, len(sections)):
+ section = sections[j]
+ if section.directive == SphinxDoctestDirectives.TESTOUTPUT:
+ yield section
+ else:
+ break
+
+
+class Section:
+ def __init__(self, directive, content, lineno, groups=None):
+ super().__init__()
+ self.directive = directive
+ self.groups = groups
+ self.lineno = lineno
+ body, skipif_expr, options = _split_into_body_and_options(content)
+
+ if skipif_expr and self.directive not in _DIRECTIVES_W_SKIPIF:
+ raise ValueError(f":skipif: not allowed in {self.directive}")
+ if options and self.directive not in _DIRECTIVES_W_OPTIONS:
+ raise ValueError(f":options: not allowed in {self.directive}")
+ self.body = body
+ self.skipif_expr = skipif_expr
+ self.options = options
+
+
+def get_sections(docstring):
+ lines = textwrap.dedent(docstring).splitlines()
+ sections = []
+
+ def _get_indentation(line):
+ return len(line) - len(line.lstrip())
+
+ def add_match(directive, i, j, groups):
+ sections.append(
+ Section(
+ directive,
+ textwrap.dedent("\n".join(lines[i + 1 : j])),
+ lineno=j - 1,
+ groups=groups,
+ )
+ )
+
+ i = 0
+ while True:
+ try:
+ line = lines[i]
+ except IndexError:
+ break
+
+ match = _DIRECTIVE_RE.match(line)
+ if match:
+ group = match.groupdict()
+ directive = getattr(SphinxDoctestDirectives,
group["directive"].upper())
+ groups = [x.strip() for x in (group["argument"] or
"default").split(",")]
+ indentation = _get_indentation(line)
+ # find the end of the block
+ j = i
+ while True:
+ j += 1
+ try:
+ block_line = lines[j]
+ except IndexError:
+ add_match(directive, i, j, groups)
+ break
+ if block_line.lstrip() and _get_indentation(block_line) <=
indentation:
+ add_match(directive, i, j, groups)
+ i = j - 1
+ break
+ i += 1
+ return sections
+
+
+def docstring2examples(docstring, globs=None):
+ """
+ Parse all sphinx test directives in the docstring and create a
+ list of examples.
+ """
+ # TODO subclass doctest.DocTestParser instead?
+
+ if not globs:
+ globs = {}
+
+ sections = get_sections(docstring)
+
+ def get_testoutput_section_data(section):
+ want = section.body
+ exc_msg = None
+ options = {}
+
+ if section.skipif_expr and eval(section.skipif_expr, globs):
+ want = ""
+ else:
+ options = section.options
+ match = doctest.DocTestParser._EXCEPTION_RE.match(want)
+ if match:
+ exc_msg = match.group("msg")
+
+ return want, options, section.lineno, exc_msg
+
+ examples = []
+ for i, current_section in enumerate(sections):
+ # TODO support SphinxDoctestDirectives.TESTSETUP, ...
+ if current_section.directive == SphinxDoctestDirectives.TESTCODE:
+ next_testoutput_sections = _get_next_textoutputsections(sections,
i + 1)
+ section_data_seq = [
+ get_testoutput_section_data(s) for s in
next_testoutput_sections
+ ]
+
+ num_unskipped_sections = len([d for d in section_data_seq if d[0]])
+ if num_unskipped_sections > 1:
+ raise ValueError("There are multiple unskipped TESTOUTPUT
sections")
+
+ if num_unskipped_sections:
+ want, options, _, exc_msg = next(d for d in section_data_seq
if d[0])
+ else:
+ # no unskipped testoutput section
+ # do we really need doctest.Example to test
+ # independent TESTCODE sections?
+ want, options, exc_msg = "", {}, None
+
+ if current_section.skipif_expr and
eval(current_section.skipif_expr, globs):
+ # TODO add the doctest.Example to `examples` but mark it as
+ # skipped.
+ continue
+
+ examples.append(
+ doctest.Example(
+ source=current_section.body,
+ want=want,
+ exc_msg=exc_msg,
+ # we want to see the ..testcode lines in the
+ # console output but not the ..testoutput
+ # lines
+ # TODO why do we want to hide testoutput??
+ lineno=current_section.lineno,
+ options=options,
+ )
+ )
+ return examples
+
+
+class SphinxDocTestRunner(doctest.DebugRunner):
+ """
+ overwrite doctest.DocTestRunner.__run, since it uses 'single' for the
+ `compile` function instead of 'exec'.
+ """
+
+ def _DocTestRunner__run(self, test, compileflags, out):
+ """
+ Run the examples in `test`.
+
+ Write the outcome of each example with one of the
+ `DocTestRunner.report_*` methods, using the writer function
+ `out`. `compileflags` is the set of compiler flags that should
+ be used to execute examples. Return a tuple `(f, t)`, where `t`
+ is the number of examples tried, and `f` is the number of
+ examples that failed. The examples are run in the namespace
+ `test.globs`.
+
+ """
+ # Keep track of the number of failures and tries.
+ failures = tries = 0
+
+ # Save the option flags (since option directives can be used
+ # to modify them).
+ original_optionflags = self.optionflags
+
+ SUCCESS, FAILURE, BOOM = range(3) # `outcome` state
+
+ check = self._checker.check_output
+
+ # Process each example.
+ for examplenum, example in enumerate(test.examples):
+
+ # If REPORT_ONLY_FIRST_FAILURE is set, then suppress
+ # reporting after the first failure.
+ quiet = (
+ self.optionflags & doctest.REPORT_ONLY_FIRST_FAILURE and
failures > 0
+ )
+
+ # Merge in the example's options.
+ self.optionflags = original_optionflags
+ if example.options:
+ for (optionflag, val) in example.options.items():
+ if val:
+ self.optionflags |= optionflag
+ else:
+ self.optionflags &= ~optionflag
+
+ # If 'SKIP' is set, then skip this example.
+ if self.optionflags & doctest.SKIP:
+ continue
+
+ # Record that we started this example.
+ tries += 1
+ if not quiet:
+ self.report_start(out, test, example)
+
+ # Use a special filename for compile(), so we can retrieve
+ # the source code during interactive debugging (see
+ # __patched_linecache_getlines).
+ filename = "<doctest %s[%d]>" % (test.name, examplenum)
+
+ # Run the example in the given context (globs), and record
+ # any exception that gets raised. (But don't intercept
+ # keyboard interrupts.)
+ try:
+ # Don't blink! This is where the user's code gets run.
+ exec(
+ compile(example.source, filename, "exec", compileflags, 1),
+ test.globs,
+ )
+ self.debugger.set_continue() # ==== Example Finished ====
+ exception = None
+ except KeyboardInterrupt:
+ raise
+ except Exception:
+ exception = sys.exc_info()
+ self.debugger.set_continue() # ==== Example Finished ====
+
+ got = self._fakeout.getvalue() # the actual output
+ self._fakeout.truncate(0)
+ outcome = FAILURE # guilty until proved innocent or insane
+
+ # If the example executed without raising any exceptions,
+ # verify its output.
+ if exception is None:
+ if check(example.want, got, self.optionflags):
+ outcome = SUCCESS
+
+ # The example raised an exception: check if it was expected.
+ else:
+ exc_msg = traceback.format_exception_only(*exception[:2])[-1]
+ if not quiet:
+ got += doctest._exception_traceback(exception)
+
+ # If `example.exc_msg` is None, then we weren't expecting
+ # an exception.
+ if example.exc_msg is None:
+ outcome = BOOM
+
+ # We expected an exception: see whether it matches.
+ elif check(example.exc_msg, exc_msg, self.optionflags):
+ outcome = SUCCESS
+
+ # Another chance if they didn't care about the detail.
+ elif self.optionflags & doctest.IGNORE_EXCEPTION_DETAIL:
+ if check(
+ doctest._strip_exception_details(example.exc_msg),
+ doctest._strip_exception_details(exc_msg),
+ self.optionflags,
+ ):
+ outcome = SUCCESS
+
+ # Report the outcome.
+ if outcome is SUCCESS:
+ if not quiet:
+ self.report_success(out, test, example, got)
+ elif outcome is FAILURE:
+ if not quiet:
+ self.report_failure(out, test, example, got)
+ failures += 1
+ elif outcome is BOOM:
+ if not quiet:
+ self.report_unexpected_exception(out, test, example,
exception)
+ failures += 1
+ else:
+ assert False, ("unknown outcome", outcome)
+
+ if failures and self.optionflags & doctest.FAIL_FAST:
+ break
+
+ # Restore the option flags (in case they were modified)
+ self.optionflags = original_optionflags
+
+ # Record and return the number of failures and tries.
+ self._DocTestRunner__record_outcome(test, failures, tries)
+ return doctest.TestResults(failures, tries)
+
+
+class SphinxDocTestParser:
+ def get_doctest(self, docstring, globs, name, filename, lineno):
+ # TODO document why we need to overwrite? get_doctest
+ return doctest.DocTest(
+ examples=docstring2examples(docstring, globs=globs),
+ globs=globs,
+ name=name,
+ filename=filename,
+ lineno=lineno,
+ docstring=docstring,
+ )
+
+
+class SphinxDoctestTextfile(pytest.Module):
+ obj = None
+
+ def collect(self):
+ # inspired by doctest.testfile; ideally we would use it directly,
+ # but it doesn't support passing a custom checker
+ encoding = self.config.getini("doctest_encoding")
+ text = self.fspath.read_text(encoding)
+ name = self.fspath.basename
+
+ optionflags = _pytest.doctest.get_optionflags(self)
+ runner = SphinxDocTestRunner(
+ verbose=0,
+ optionflags=optionflags,
+ checker=_pytest.doctest._get_checker(),
+ )
+
+ test = doctest.DocTest(
+ examples=docstring2examples(text),
+ globs={},
+ name=name,
+ filename=name,
+ lineno=0,
+ docstring=text,
+ )
+
+ if test.examples:
+ yield DoctestItem.from_parent(
+ parent=self, name=test.name, runner=runner, dtest=test
+ )
+
+
+class SphinxDoctestModule(pytest.Module):
+ def collect(self):
+ if self.fspath.basename == "conftest.py":
+ module = self.config.pluginmanager._importconftest(
+ self.path,
+ self.config.getoption("importmode"),
+ rootpath=self.config.rootpath,
+ )
+ else:
+ try:
+ module = import_path(self.path, root=self.config.rootpath)
+ except ImportError:
+ if self.config.getvalue("doctest_ignore_import_errors"):
+ pytest.skip("unable to import module %r" % self.path)
+ else:
+ raise
+ optionflags = _pytest.doctest.get_optionflags(self)
+
+ class MockAwareDocTestFinder(doctest.DocTestFinder):
+ """
+ a hackish doctest finder that overrides stdlib internals to fix
+ a stdlib bug
+ https://github.com/pytest-dev/pytest/issues/3456
+ https://bugs.python.org/issue25532
+
+ fix taken from https://github.com/pytest-dev/pytest/pull/4212/
+ """
+
+ def _find(self, tests, obj, name, module, source_lines, globs,
seen):
+ if _is_mocked(obj):
+ return
+ with _patch_unwrap_mock_aware():
+ doctest.DocTestFinder._find(
+ self,
+ tests,
+ obj,
+ name,
+ module,
+ source_lines,
+ globs,
+ seen,
+ )
+
+ if sys.version_info < (3, 10):
+ finder = MockAwareDocTestFinder(parser=SphinxDocTestParser())
+ else:
+ finder = doctest.DocTestFinder(parser=SphinxDocTestParser())
+
+ runner = SphinxDocTestRunner(
+ verbose=0,
+ optionflags=optionflags,
+ checker=_pytest.doctest._get_checker(),
+ )
+
+ for test in finder.find(module, module.__name__):
+ if test.examples:
+ yield DoctestItem.from_parent(
+ parent=self, name=test.name, runner=runner, dtest=test
+ )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/tests/test_doc2test.py
new/pytest-sphinx-0.5.0/tests/test_doc2test.py
--- old/pytest-sphinx-0.4.0/tests/test_doc2test.py 2022-03-30
22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/tests/test_doc2test.py 2022-09-06
22:46:09.000000000 +0200
@@ -91,7 +91,7 @@
rstpath = os.path.join(
os.path.dirname(__file__), "testdata", "using_the_shapereader.rst"
)
- with open(rstpath, "r") as fh:
+ with open(rstpath) as fh:
sections = get_sections(fh.read())
assert len(sections) == 9
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/tests/test_options.py
new/pytest-sphinx-0.5.0/tests/test_options.py
--- old/pytest-sphinx-0.4.0/tests/test_options.py 2022-03-30
22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/tests/test_options.py 2022-09-06
22:46:09.000000000 +0200
@@ -2,6 +2,7 @@
import textwrap
import pytest
+
from pytest_sphinx import _split_into_body_and_options
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/tests/test_python_files.py
new/pytest-sphinx-0.5.0/tests/test_python_files.py
--- old/pytest-sphinx-0.4.0/tests/test_python_files.py 2022-03-30
22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/tests/test_python_files.py 2022-09-06
22:46:09.000000000 +0200
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
import textwrap
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/tests/test_sphinx_doctest.py
new/pytest-sphinx-0.5.0/tests/test_sphinx_doctest.py
--- old/pytest-sphinx-0.4.0/tests/test_sphinx_doctest.py 2022-03-30
22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/tests/test_sphinx_doctest.py 2022-09-06
22:46:09.000000000 +0200
@@ -83,7 +83,7 @@
"""
)
- assert expected in output, "{!r}\n\n{!r}".format(expected, output)
+ assert expected in output, f"{expected!r}\n\n{output!r}"
def test_simple_doctest_success(sphinx_tester):
@@ -111,6 +111,49 @@
"""
sphinx_output = sphinx_tester(code)
+ assert "1 items passed all tests" in sphinx_output
+
+ plugin_result = testdir.runpytest("--doctest-glob=index.rst").stdout
+ plugin_result.fnmatch_lines(["*=== 1 passed in *"])
+
+ def test_doctest(self, testdir, sphinx_tester):
+ code = """
+ .. doctest::
+
+ >>> print("msg from testcode directive")
+ msg from testcode directive
+ """
+
+ sphinx_output = sphinx_tester(code)
+ assert "1 items passed all tests" in sphinx_output
+
+ plugin_result = testdir.runpytest("--doctest-glob=index.rst").stdout
+ plugin_result.fnmatch_lines(["*=== 1 passed in *"])
+
+ def test_doctest_multiple(self, testdir, sphinx_tester):
+ code = """
+ .. doctest::
+
+ >>> import operator
+
+ >>> operator.lt(1, 3)
+ True
+
+ >>> operator.lt(6, 2)
+ False
+
+ .. doctest::
+
+ >>> four = 2 + 2
+
+ >>> four
+ 4
+
+ >>> print(f'Two plus two: {four}')
+ Two plus two: 4
+ """
+
+ sphinx_output = sphinx_tester(code)
assert "1 items passed all tests" in sphinx_output
plugin_result = testdir.runpytest("--doctest-glob=index.rst").stdout
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/tests/test_text_files.py
new/pytest-sphinx-0.5.0/tests/test_text_files.py
--- old/pytest-sphinx-0.4.0/tests/test_text_files.py 2022-03-30
22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/tests/test_text_files.py 2022-09-06
22:46:09.000000000 +0200
@@ -1,4 +1,5 @@
import _pytest.doctest
+
import pytest_sphinx
@@ -199,3 +200,36 @@
result = testdir.runpytest()
result.stdout.fnmatch_lines(["*=== 1 passed in *"])
+
+
+def test_doctest_directive(testdir):
+ testdir.maketxtfile(
+ test_something=r"""
+ This is a paragraph. This is the
+ next sentence.
+
+ .. doctest::
+
+ >>> assert False
+ Traceback (most recent call last):
+ ...
+ AssertionError
+ """
+ )
+
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*=== 1 passed in *"])
+
+ testdir.maketxtfile(
+ test_something=r"""
+ This is a paragraph. This is the
+ next sentence.
+
+ .. doctest::
+
+ >>> assert False
+ """
+ )
+
+ result = testdir.runpytest()
+ result.stdout.fnmatch_lines(["*=== 1 failed in *"])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-sphinx-0.4.0/tox.ini
new/pytest-sphinx-0.5.0/tox.ini
--- old/pytest-sphinx-0.4.0/tox.ini 2022-03-30 22:29:23.000000000 +0200
+++ new/pytest-sphinx-0.5.0/tox.ini 2022-09-06 22:46:09.000000000 +0200
@@ -1,6 +1,7 @@
# For more information about tox, see https://tox.readthedocs.io/en/latest/
[tox]
-envlist = py37,py38,py39,py310,ci-py
+envlist = py37,py38,py39,py310,py311,ci-py
+isolated_build = true
[testenv]
deps =