Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-pytest-doctestplus for
openSUSE:Factory checked in at 2024-01-03 12:27:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-pytest-doctestplus (Old)
and /work/SRC/openSUSE:Factory/.python-pytest-doctestplus.new.28375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pytest-doctestplus"
Wed Jan 3 12:27:11 2024 rev:17 rq:1135878 version:1.1.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-pytest-doctestplus/python-pytest-doctestplus.changes
2023-10-05 20:03:54.408346718 +0200
+++
/work/SRC/openSUSE:Factory/.python-pytest-doctestplus.new.28375/python-pytest-doctestplus.changes
2024-01-03 12:27:13.636384893 +0100
@@ -1,0 +2,11 @@
+Sun Dec 31 13:32:06 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 1.1.0:
+ * Added --doctest-plus-generate-diff to update documentation
+ based on actual output.
+ * Fix module level __doctest_requires__.
+ * Versions of Python <3.8 are no longer supported.
+ * Fix erroneous attempt to import __main__.py by skipping it.
+ * Respect pytest --import-mode.
+
+-------------------------------------------------------------------
Old:
----
pytest-doctestplus-1.0.0.tar.gz
New:
----
pytest-doctestplus-1.1.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-pytest-doctestplus.spec ++++++
--- /var/tmp/diff_new_pack.j9b8JH/_old 2024-01-03 12:27:14.164404175 +0100
+++ /var/tmp/diff_new_pack.j9b8JH/_new 2024-01-03 12:27:14.164404175 +0100
@@ -27,16 +27,16 @@
%{?sle15_python_module_pythons}
Name: python-pytest-doctestplus%{psuffix}
-Version: 1.0.0
+Version: 1.1.0
Release: 0
Summary: Pytest plugin with advanced doctest features
License: BSD-3-Clause
URL: https://github.com/scientific-python/pytest-doctestplus
Source:
https://files.pythonhosted.org/packages/source/p/pytest-doctestplus/pytest-doctestplus-%{version}.tar.gz
-BuildRequires: %{python_module base >= 3.7}
-BuildRequires: %{python_module packaging >= 17.0}
+BuildRequires: %{python_module base >= 3.8}
+BuildRequires: %{python_module pip}
BuildRequires: %{python_module setuptools_scm}
-BuildRequires: %{python_module setuptools}
+BuildRequires: %{python_module wheel}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
Requires: python-packaging >= 17.0
@@ -49,6 +49,7 @@
BuildRequires: %{python_module pip >= 19.3.1}
BuildRequires: %{python_module pytest-doctestplus = %{version}}
BuildRequires: %{python_module pytest-remotedata >= 0.3.2}
+BuildRequires: git-core
%else
BuildArch: noarch
%endif
@@ -63,11 +64,11 @@
%setup -q -n pytest-doctestplus-%{version}
%build
-%python_build
+%pyproject_wheel
%install
%if !%{with test}
-%python_install
+%pyproject_install
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%endif
@@ -75,7 +76,7 @@
%check
export LANG=en_US.UTF8
export PY_IGNORE_IMPORTMISMATCH=1
-%pytest tests/ --doctest-plus --doctest-rst -k "not test_remote_data_url"
+%pytest tests/ --doctest-plus --doctest-rst -k "not test_remote_data_url and
not test_import_mode"
%endif
%if !%{with test}
@@ -83,6 +84,6 @@
%doc CHANGES.rst README.rst
%license LICENSE.rst
%{python_sitelib}/pytest_doctestplus
-%{python_sitelib}/pytest_doctestplus-%{version}*-info
+%{python_sitelib}/pytest_doctestplus-%{version}.dist-info
%endif
++++++ pytest-doctestplus-1.0.0.tar.gz -> pytest-doctestplus-1.1.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/.github/workflows/publish.yml
new/pytest-doctestplus-1.1.0/.github/workflows/publish.yml
--- old/pytest-doctestplus-1.0.0/.github/workflows/publish.yml 2023-08-11
18:21:57.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/.github/workflows/publish.yml 2023-12-13
19:12:44.000000000 +0100
@@ -13,7 +13,7 @@
if: ((github.event_name == 'push' && startsWith(github.ref, 'refs/tags'))
|| contains(github.event.pull_request.labels.*.name, 'Build wheels'))
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/.github/workflows/python-tests.yml
new/pytest-doctestplus-1.1.0/.github/workflows/python-tests.yml
--- old/pytest-doctestplus-1.0.0/.github/workflows/python-tests.yml
2023-08-11 18:21:57.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/.github/workflows/python-tests.yml
2023-12-13 19:12:44.000000000 +0100
@@ -8,8 +8,8 @@
- '*'
workflow_dispatch:
schedule:
- # Run every Sunday at 03:53 UTC
- - cron: 53 3 * * 0
+ # Run every Tuesday at 03:53 UTC
+ - cron: 53 3 * * 2
jobs:
tests:
@@ -19,17 +19,17 @@
matrix:
include:
- os: ubuntu-latest
- python-version: 3.7
- toxenv: py37-test-pytest46
+ python-version: 3.8
+ toxenv: py38-test-pytestoldest
- os: windows-latest
- python-version: 3.7
- toxenv: py37-test-pytest50
+ python-version: 3.8
+ toxenv: py38-test-pytest50
- os: macos-latest
- python-version: 3.7
- toxenv: py37-test-pytest51
+ python-version: 3.8
+ toxenv: py38-test-pytest51
- os: ubuntu-latest
- python-version: 3.7
- toxenv: py37-test-pytest52
+ python-version: 3.8
+ toxenv: py38-test-pytest52
- os: windows-latest
python-version: 3.8
toxenv: py38-test-pytest53
@@ -65,11 +65,11 @@
python-version: '3.11'
toxenv: py311-test-pytestdev
- os: ubuntu-latest
- python-version: '3.12-dev'
+ python-version: '3.12'
toxenv: py312-test-pytestdev
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
@@ -80,11 +80,3 @@
run: python -m pip install tox
- name: Run Tox
run: tox ${{ matrix.toxargs }} -v -e ${{ matrix.toxenv }}
-
- # - name: Slack Notification
- # uses: 8398a7/action-slack@v3
- # with:
- # status: ${{ job.status }}
- # env:
- # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
- # if: always() # TODO: cron
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/.mailmap
new/pytest-doctestplus-1.1.0/.mailmap
--- old/pytest-doctestplus-1.0.0/.mailmap 2023-08-11 18:21:57.000000000
+0200
+++ new/pytest-doctestplus-1.1.0/.mailmap 2023-12-13 19:12:44.000000000
+0100
@@ -12,7 +12,9 @@
Matteo Bachetti <[email protected]>
<[email protected]>
Michael Seifert <[email protected]>
Pey Lian Lim <[email protected]>
<[email protected]>
+Philipp A. <[email protected]>
Pratik Patel <[email protected]>
+Sebastian Berg <[email protected]> <[email protected]>
Simon Conseil <[email protected]> <[email protected]>
Simon Conseil <[email protected]> <[email protected]>
Tinuade Adeleke <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/CHANGES.rst
new/pytest-doctestplus-1.1.0/CHANGES.rst
--- old/pytest-doctestplus-1.0.0/CHANGES.rst 2023-08-11 18:21:57.000000000
+0200
+++ new/pytest-doctestplus-1.1.0/CHANGES.rst 2023-12-13 19:12:44.000000000
+0100
@@ -1,3 +1,18 @@
+1.1.0 (2023-12-13)
+==================
+
+- Added ``--doctest-plus-generate-diff`` to update documentation based on
+ actual output. [#227]
+
+- Fix module level ``__doctest_requires__``. [#228]
+
+- Versions of Python <3.8 are no longer supported. [#217]
+
+- Fix erroneous attempt to import ``__main__.py`` by skipping it. [#232]
+
+- Respect pytest ``--import-mode``. [#233]
+
+
1.0.0 (2023-08-11)
==================
@@ -69,7 +84,7 @@
0.9.0 (2021-01-14)
==================
-- Declare ``setuptools`` runtime dependency [#93]
+- Declare ``setuptools`` runtime dependency. [#132]
- Add ``SHOW_WARNINGS`` flag to show warnings. [#136]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/PKG-INFO
new/pytest-doctestplus-1.1.0/PKG-INFO
--- old/pytest-doctestplus-1.0.0/PKG-INFO 2023-08-11 18:22:14.703384200
+0200
+++ new/pytest-doctestplus-1.1.0/PKG-INFO 2023-12-13 19:12:59.113894500
+0100
@@ -1,12 +1,12 @@
Metadata-Version: 2.1
Name: pytest-doctestplus
-Version: 1.0.0
+Version: 1.1.0
Summary: Pytest plugin with advanced doctest features.
Home-page: https://github.com/scientific-python/pytest-doctestplus
Author: Scientific Python Developers
License: BSD
Keywords: doctest,rst,pytest,py.test
-Classifier: Development Status :: 3 - Alpha
+Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
@@ -14,7 +14,6 @@
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
@@ -23,10 +22,16 @@
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Utilities
-Requires-Python: >=3.7
+Requires-Python: >=3.8
Description-Content-Type: text/x-rst
-Provides-Extra: test
License-File: LICENSE.rst
+Requires-Dist: pytest>=4.6
+Requires-Dist: setuptools>=30.3.0
+Requires-Dist: packaging>=17.0
+Provides-Extra: test
+Requires-Dist: numpy; extra == "test"
+Requires-Dist: pytest-remotedata>=0.3.2; extra == "test"
+Requires-Dist: sphinx; extra == "test"
==================
pytest-doctestplus
@@ -71,6 +76,10 @@
* optional inclusion of ``*.rst`` files for doctests (see `Setup and
Configuration`_)
* optional inclusion of doctests in docstrings of Numpy ufuncs
+Further, ``pytest-doctestplus`` supports editing files to fix incorrect
docstrings
+(See `Fixing Existing Docstrings`_).
+
+.. _pytest-remotedata: https://github.com/astropy/pytest-remotedata
Installation
------------
@@ -132,6 +141,23 @@
doctest settings, see the `doctest documentation
<https://docs.python.org/3/library/doctest.html#option-flags>`_.
+Running Tests in Markdown Files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To run doctests in Markdown files, invoke pytest with the command line options
+``--doctest-plus --doctest-glob '*.md'``.
+
+If you write doctests inside `GitHub-style triple backtick fenced code blocks
+<https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks>`_,
+then in order for pytest-doctest to find and run them you need to include an
+extra trailing newline inside your code blocks, like this::
+
+ ```pycon
+ >>> 1 + 2
+ 2
+
+ ```
+
Doctest Directives
~~~~~~~~~~~~~~~~~~
@@ -387,6 +413,54 @@
``conf.py`` file.
+Fixing Existing Docstrings
+--------------------------
+The plugin has basic support to fix docstrings, this can be enabled by
+running ``pytest`` with ``--doctest-plus-generate-diff``.
+Without further options, this will print out a diff and a list of files that
+would be modified. Using ``--doctest-plus-generate-diff=overwrite`` will
+modify the files in-place, so it is recommended to run the check first to
+verify the paths.
+You may wish to review changes manually and only commit some patches e.g.
using ``git commit --patch``.
+
+The current diff generation is still very basic, for example, it does not
account for
+existing ``...``. By default a diff is only generated for *failing* doctests.
+
+In general, a mass edit may wish to focus on a specific change and
+possibly include passing tests. So you can opt-in into the behavior by
+adding a hook to your ``conftest.py``::
+
+ @pytest.hookimpl
+ def pytest_doctestplus_diffhook(info):
+ info["use"] = True # Overwrite all results (even successes)
+ if info["fileno"] is None:
+ # E.g. NumPy has C docstrings that cannot be found, we can add
+ # custom logic here to try and find these:
+ info["filename"] = ...
+ info["lineno"] = ...
+
+Where ``info`` is a dictionary containing the following items:
+
+* ``use``: ``True`` or ``False`` signalling whether to apply the diff. This is
+ set to ``False`` if a doctest succeeded and ``True`` if the doctest failed.
+* ``name``: The name of the test (e.g. the function being documented)
+* ``filename``: The file that contains the test (this can be wrong in certain
+ situation and in that case ``test_lineno`` will be wrong as well).
+* ``source``: The source code that was executed for this test
+* ``test_lineno``: The line of code where the example block (or function)
starts.
+ In some cases, the test file cannot be found and the lineno will be ``None``,
+ you can manually try to fix these.
+* ``example_lineno``: The line number of the example snippet
+ (individual ``>>>``).
+* ``want``: The current documentation.
+* ``got``: The result of executing the example.
+
+You can modify the dictionary in-place to modify the behavior.
+
+Please note that we assume that this API will be used only occasionally and
+reserve the right to change it at any time.
+
+
Development Status
------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/README.rst
new/pytest-doctestplus-1.1.0/README.rst
--- old/pytest-doctestplus-1.0.0/README.rst 2023-08-11 18:21:57.000000000
+0200
+++ new/pytest-doctestplus-1.1.0/README.rst 2023-12-13 19:12:44.000000000
+0100
@@ -41,6 +41,10 @@
* optional inclusion of ``*.rst`` files for doctests (see `Setup and
Configuration`_)
* optional inclusion of doctests in docstrings of Numpy ufuncs
+Further, ``pytest-doctestplus`` supports editing files to fix incorrect
docstrings
+(See `Fixing Existing Docstrings`_).
+
+.. _pytest-remotedata: https://github.com/astropy/pytest-remotedata
Installation
------------
@@ -102,6 +106,23 @@
doctest settings, see the `doctest documentation
<https://docs.python.org/3/library/doctest.html#option-flags>`_.
+Running Tests in Markdown Files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To run doctests in Markdown files, invoke pytest with the command line options
+``--doctest-plus --doctest-glob '*.md'``.
+
+If you write doctests inside `GitHub-style triple backtick fenced code blocks
+<https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks>`_,
+then in order for pytest-doctest to find and run them you need to include an
+extra trailing newline inside your code blocks, like this::
+
+ ```pycon
+ >>> 1 + 2
+ 2
+
+ ```
+
Doctest Directives
~~~~~~~~~~~~~~~~~~
@@ -357,6 +378,54 @@
``conf.py`` file.
+Fixing Existing Docstrings
+--------------------------
+The plugin has basic support to fix docstrings, this can be enabled by
+running ``pytest`` with ``--doctest-plus-generate-diff``.
+Without further options, this will print out a diff and a list of files that
+would be modified. Using ``--doctest-plus-generate-diff=overwrite`` will
+modify the files in-place, so it is recommended to run the check first to
+verify the paths.
+You may wish to review changes manually and only commit some patches e.g.
using ``git commit --patch``.
+
+The current diff generation is still very basic, for example, it does not
account for
+existing ``...``. By default a diff is only generated for *failing* doctests.
+
+In general, a mass edit may wish to focus on a specific change and
+possibly include passing tests. So you can opt-in into the behavior by
+adding a hook to your ``conftest.py``::
+
+ @pytest.hookimpl
+ def pytest_doctestplus_diffhook(info):
+ info["use"] = True # Overwrite all results (even successes)
+ if info["fileno"] is None:
+ # E.g. NumPy has C docstrings that cannot be found, we can add
+ # custom logic here to try and find these:
+ info["filename"] = ...
+ info["lineno"] = ...
+
+Where ``info`` is a dictionary containing the following items:
+
+* ``use``: ``True`` or ``False`` signalling whether to apply the diff. This is
+ set to ``False`` if a doctest succeeded and ``True`` if the doctest failed.
+* ``name``: The name of the test (e.g. the function being documented)
+* ``filename``: The file that contains the test (this can be wrong in certain
+ situation and in that case ``test_lineno`` will be wrong as well).
+* ``source``: The source code that was executed for this test
+* ``test_lineno``: The line of code where the example block (or function)
starts.
+ In some cases, the test file cannot be found and the lineno will be ``None``,
+ you can manually try to fix these.
+* ``example_lineno``: The line number of the example snippet
+ (individual ``>>>``).
+* ``want``: The current documentation.
+* ``got``: The result of executing the example.
+
+You can modify the dictionary in-place to modify the behavior.
+
+Please note that we assume that this API will be used only occasionally and
+reserve the right to change it at any time.
+
+
Development Status
------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/pytest_doctestplus/newhooks.py
new/pytest-doctestplus-1.1.0/pytest_doctestplus/newhooks.py
--- old/pytest-doctestplus-1.0.0/pytest_doctestplus/newhooks.py 1970-01-01
01:00:00.000000000 +0100
+++ new/pytest-doctestplus-1.1.0/pytest_doctestplus/newhooks.py 2023-12-13
19:12:44.000000000 +0100
@@ -0,0 +1,5 @@
+# Licensed under a 3-clause BSD style license - see LICENSE.rst
+
+
+def pytest_doctestplus_diffhook(info):
+ """ called when a diff would be generated normally. """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/pytest_doctestplus/output_checker.py
new/pytest-doctestplus-1.1.0/pytest_doctestplus/output_checker.py
--- old/pytest-doctestplus-1.0.0/pytest_doctestplus/output_checker.py
2023-08-11 18:21:57.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/pytest_doctestplus/output_checker.py
2023-12-13 19:12:44.000000000 +0100
@@ -43,8 +43,6 @@
rtol = 1e-05
atol = 1e-08
- _original_output_checker = doctest.OutputChecker
-
_str_literal_re = re.compile(
r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
_byteorder_re = re.compile(
@@ -55,9 +53,6 @@
r"([0-9]+)L", re.UNICODE)
def __init__(self):
- # NOTE OutputChecker is an old-style class with no __init__ method,
- # so we can't call the base class version of __init__ here
-
exp = r'(?:e[+-]?\d+)'
got_floats = (r'\s*([+-]?\d+\.\d*{0}?|'
@@ -80,6 +75,10 @@
fmidend = r'(?<={}){}(?={}|$)'.format(front_sep, want_floats, back_sep)
self.num_want_rgx = re.compile(r'({}|{})'.format(fbeg, fmidend))
+ # As of 2023-09-26, Python base class has no init, but just in case
+ # it acquires one.
+ super().__init__()
+
def do_fixes(self, want, got):
want = re.sub(self._str_literal_re, r'\1\2', want)
want = re.sub(self._byteorder_re, r'\1\2\3', want)
@@ -281,19 +280,13 @@
if flags & FLOAT_CMP:
return self.normalize_floats(want, got, flags)
- # Can't use super here because doctest.OutputChecker is not a
- # new-style class.
- return self._original_output_checker.check_output(
- self, want, got, flags)
+ return super().check_output(want, got, flags)
def output_difference(self, want, got, flags):
if flags & FIX:
want, got = self.do_fixes(want, got)
- # Can't use super here because doctest.OutputChecker is not a
- # new-style class.
- return self._original_output_checker.output_difference(
- self, want, got, flags)
+ return super().output_difference(want, got, flags)
try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/pytest_doctestplus/plugin.py
new/pytest-doctestplus-1.1.0/pytest_doctestplus/plugin.py
--- old/pytest-doctestplus-1.0.0/pytest_doctestplus/plugin.py 2023-08-11
18:21:57.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/pytest_doctestplus/plugin.py 2023-12-13
19:12:44.000000000 +0100
@@ -1,4 +1,5 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
+
"""
This plugin provides advanced doctest support and enables the testing of .rst
files.
@@ -8,8 +9,11 @@
import os
import re
import sys
+import tempfile
import warnings
+from collections import defaultdict
from pathlib import Path
+import subprocess
from textwrap import indent
from unittest import SkipTest
@@ -119,6 +123,20 @@
parser.addoption("--doctest-only", action="store_true",
help="Test only doctests. Implies usage of doctest-plus.")
+ parser.addoption("--doctest-plus-generate-diff",
+ help=(
+ "Generate a diff where expected output and actual "
+ "output differ. "
+ "The diff is printed to stdout if not using "
+ "`--doctest-plus-generate-diff=overwrite` which "
+ "causes editing of the original files.\n"
+ "NOTE: Unless an in-pace build is picked up, python "
+ "file paths may point to unexpected places. "
+ "If 'overwrite' is not used, will create a temporary "
+ "folder and use `git diff -p` to generate a diff."),
+ choices=["diff", "overwrite"],
+ action="store", nargs="?", default=False, const="diff")
+
parser.addini("text_file_format",
"Default format for docs. "
"This is no longer recommended, use --doctest-glob instead.")
@@ -160,6 +178,11 @@
default=[])
+def pytest_addhooks(pluginmanager):
+ from pytest_doctestplus import newhooks
+ pluginmanager.add_hookspecs(newhooks)
+
+
def get_optionflags(parent):
optionflags_str = parent.config.getini('doctest_optionflags')
flag_int = 0
@@ -185,6 +208,8 @@
def pytest_configure(config):
doctest_plugin = config.pluginmanager.getplugin('doctest')
run_regular_doctest = config.option.doctestmodules and not
config.option.doctest_plus
+ if config.option.doctest_plus_generate_diff:
+ config.option.doctest_only = True
use_doctest_plus = config.getini(
'doctest_plus') or config.option.doctest_plus or
config.option.doctest_only
use_doctest_ufunc = config.getini(
@@ -204,13 +229,17 @@
use_rst = config.getini('doctest_rst') or config.option.doctest_rst
file_ext = config.option.text_file_format or
config.getini('text_file_format') or 'rst'
if use_rst:
- config.option.doctestglob.append('*.{}'.format(file_ext))
+ config.option.doctestglob.append(f'*.{file_ext}')
# override default comment characters
ext_comment_pairs = [pair.split('=') for pair in
config.getini('text_file_comment_chars')]
for ext, chars in ext_comment_pairs:
comment_characters[ext] = chars
+ # Fetch the global hook function:
+ global doctestplus_diffhook
+ doctestplus_diffhook = config.hook.pytest_doctestplus_diffhook
+
class DocTestModulePlus(doctest_plugin.DoctestModule):
# pytest 2.4.0 defines "collect". Prior to that, it defined
# "runtest". The "collect" approach is better, because we can
@@ -228,7 +257,7 @@
fspath = self.fspath
filepath = self.fspath.basename
- if filepath == "setup.py":
+ if filepath in ("setup.py", "__main__.py"):
return
elif filepath == "conftest.py":
if PYTEST_GE_7_0:
@@ -245,11 +274,12 @@
try:
if PYTEST_GT_5:
from _pytest.pathlib import import_path
+ mode = self.config.getoption("importmode")
if PYTEST_GE_7_0:
- module = import_path(fspath, root=self.config.rootpath)
+ module = import_path(fspath, mode=mode,
root=self.config.rootpath)
elif PYTEST_GT_5:
- module = import_path(fspath)
+ module = import_path(fspath, mode=mode)
else:
module = fspath.pyimport()
except ImportError:
@@ -268,6 +298,7 @@
checker=OutputChecker(),
# Helper disables continue-on-failure when debugging is enabled
continue_on_failure=_get_continue_on_failure(config),
+ generate_diff=config.option.doctest_plus_generate_diff,
)
for test in finder.find(module):
@@ -332,6 +363,7 @@
runner = DebugRunnerPlus(
verbose=False, optionflags=optionflags,
checker=OutputChecker(),
continue_on_failure=_get_continue_on_failure(self.config),
+ generate_diff=self.config.option.doctest_plus_generate_diff,
)
parser = DocTestParserPlus()
@@ -394,7 +426,7 @@
skip_next = False
lines = entry.strip().splitlines()
if any(re.match(
- '{} doctest-skip-all'.format(comment_char),
x.strip()) for x in lines):
+ f'{comment_char} doctest-skip-all', x.strip()) for
x in lines):
skip_all = True
continue
@@ -405,7 +437,7 @@
# special environment to be in between, e.g. \begin{python}
last_lines = lines[-2:]
matches = [re.match(
- r'{}\s+doctest-skip\s*::(\s+.*)?'.format(comment_char),
+ fr'{comment_char}\s+doctest-skip\s*::(\s+.*)?',
last_line) for last_line in last_lines]
if len(matches) > 1:
@@ -423,7 +455,7 @@
if config.getoption('remote_data', 'none') != 'any':
matches = (re.match(
-
r'{}\s+doctest-remote-data\s*::'.format(comment_char),
+ fr'{comment_char}\s+doctest-remote-data\s*::',
last_line) for last_line in last_lines)
if any(matches):
@@ -431,7 +463,7 @@
continue
matches = [re.match(
-
r'{}\s+doctest-requires\s*::\s+(.*)'.format(comment_char),
+ fr'{comment_char}\s+doctest-requires\s*::\s+(.*)',
last_line) for last_line in last_lines]
if len(matches) > 1:
@@ -487,7 +519,7 @@
config.pluginmanager.unregister(doctest_plugin)
-class DoctestPlus(object):
+class DoctestPlus:
def __init__(self, doctest_module_item_cls, doctest_textfile_item_cls,
file_globs):
"""
doctest_module_item_cls should be a class inheriting
@@ -673,6 +705,7 @@
if mod in cls._import_cache:
if not cls._import_cache[mod]:
return False
+ continue
if cls._module_checker.check(mod):
cls._import_cache[mod] = True
@@ -714,9 +747,17 @@
for pats, mods in reqs.items():
if not isinstance(pats, tuple):
pats = (pats,)
+
for pat in pats:
- if not fnmatch.fnmatch(test.name, '.'.join((name,
pat))):
- continue
+ if pat == '*':
+ pass
+ elif pat == '.' and test.name == name:
+ pass
+ elif fnmatch.fnmatch(test.name, '.'.join((name, pat))):
+ pass
+ else:
+ continue # The pattern does not apply
+
if not self.check_required_modules(mods):
return False
return True
@@ -726,12 +767,143 @@
return tests
+def write_modified_file(fname, new_fname, changes):
+ # Sort in reversed order to edit the lines:
+ bad_tests = []
+ changes.sort(key=lambda x: (x["test_lineno"], x["example_lineno"]),
+ reverse=True)
+
+ with open(fname, "r") as f:
+ text = f.readlines()
+
+ for change in changes:
+ if change["test_lineno"] is None:
+ bad_tests.append(change["name"])
+ continue
+ lineno = change["test_lineno"] + change["example_lineno"] + 1
+
+ indentation = " " * change["nindent"]
+ want = indent(change["want"], indentation, lambda x: True)
+ # Replace fully blank lines with the required `<BLANKLINE>`
+ # (May need to do this also if line contains only whitespace)
+ got = change["got"].replace("\n\n", "\n<BLANKLINE>\n")
+ got = indent(got, indentation, lambda x: True)
+
+ text[lineno:lineno+want.count("\n")] = [got]
+
+ with open(new_fname, "w") as f:
+ f.write("".join(text))
+
+ return bad_tests
+
+
+def pytest_terminal_summary(terminalreporter, exitstatus, config):
+ changesets = DebugRunnerPlus._changesets
+ diff_mode = DebugRunnerPlus._generate_diff
+ DebugRunnerPlus._changesets = defaultdict(lambda: [])
+ DebugRunnerPlus._generate_diff = None
+ all_bad_tests = []
+ if not diff_mode:
+ return # we do not report or apply diffs
+
+ if diff_mode != "overwrite":
+ # In this mode, we write a corrected file to a temporary folder in
+ # order to compare them (rather than modifying the file).
+ terminalreporter.section("Reporting DoctestPlus Diffs")
+ if not changesets:
+ terminalreporter.write_line("No doc changes to show")
+ return
+
+ # Strip away the common part of the path to make it a bit clearner...
+ common_path = os.path.commonpath(changesets.keys())
+ if not os.path.isdir(common_path):
+ common_path = os.path.split(common_path)[0]
+
+ with tempfile.TemporaryDirectory() as tmpdirname:
+ for fname, changes in changesets.items():
+ # Create a new filename and ensure the path exists (in the
+ # temporary directory).
+ new_fname = fname.replace(common_path, tmpdirname)
+ os.makedirs(os.path.split(new_fname)[0], exist_ok=True)
+
+ bad_tests = write_modified_file(fname, new_fname, changes)
+ all_bad_tests.extend(bad_tests)
+
+ # git diff returns 1 to signal changes, so just ignore the
+ # exit status:
+ with subprocess.Popen(
+ ["git", "diff", "-p", "--no-index", fname, new_fname],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True) as p:
+ p.wait()
+ # Diff should be fine, but write error if not:
+ diff = p.stderr.read()
+ diff += p.stdout.read()
+
+ # hide the temporary directory (cleaning up anyway):
+ if not os.path.isabs(common_path):
+ diff = diff.replace(tmpdirname, "/" + common_path)
+ else:
+ # diff seems to not include extra /
+ diff = diff.replace(tmpdirname, common_path)
+ terminalreporter.write(diff)
+ terminalreporter.write_line(f"{tmpdirname}, {common_path}")
+
+ terminalreporter.section("Files with modifications", "-")
+ terminalreporter.write_line(
+ "The following files would be overwritten with "
+ "`--doctest-plus-generate-diff=overwrite`:")
+ for fname in changesets:
+ terminalreporter.write_line(f" {fname}")
+ terminalreporter.write_line(
+ "make sure these file paths are correct before calling
it!")
+ else:
+ # We are in overwrite mode so will write the modified version directly
+ # back into the same file and only report which files were changed.
+ terminalreporter.section("DoctestPlus Fixing File Docs")
+ if not changesets:
+ terminalreporter.write_line("No doc changes to apply")
+ return
+ terminalreporter.write_line("Applied fix to the following files:")
+ for fname, changes in changesets.items():
+ bad_tests = write_modified_file(fname, fname, changes)
+ all_bad_tests.extend(bad_tests)
+ terminalreporter.write_line(f" {fname}")
+
+ if all_bad_tests:
+ terminalreporter.section("Broken Linenumbers", "-")
+ terminalreporter.write_line(
+ "Doctestplus was unable to fix the following tests "
+ "(their source is hidden or `__module__` overridden?)")
+ for bad_test in all_bad_tests:
+ terminalreporter.write_line(f" {bad_test}")
+ terminalreporter.write_line(
+ "You can implementing a hook function to fix this (see README).")
+
+
class DebugRunnerPlus(doctest.DebugRunner):
- def __init__(self, checker=None, verbose=None, optionflags=0,
continue_on_failure=True):
+ _changesets = defaultdict(lambda: [])
+ _generate_diff = False
+
+ def __init__(self, checker=None, verbose=None, optionflags=0,
+ continue_on_failure=True, generate_diff=False):
+ # generated_diff is False, "diff", or "overwrite" (only need
truthiness)
+ DebugRunnerPlus._generate_diff = generate_diff
+
super().__init__(checker=checker, verbose=verbose,
optionflags=optionflags)
self.continue_on_failure = continue_on_failure
+ def report_success(self, out, test, example, got):
+ if self._generate_diff:
+ self.track_diff(False, out, test, example, got)
+ return
+
+ return super().report_success(out, test, example, got)
+
def report_failure(self, out, test, example, got):
+ if self._generate_diff:
+ self.track_diff(True, out, test, example, got)
+ return
+
failure = doctest.DocTestFailure(test, example, got)
if self.continue_on_failure:
out.append(failure)
@@ -747,3 +919,17 @@
out.append(failure)
else:
raise failure
+
+ def track_diff(self, use, out, test, example, got):
+ if example.want == got:
+ return
+
+ info = dict(use=use, name=test.name, filename=test.filename,
+ source=example.source, nindent=example.indent,
+ want=example.want, got=got, test_lineno=test.lineno,
+ example_lineno=example.lineno)
+ doctestplus_diffhook(info=info)
+ if not info["use"]:
+ return
+
+ self._changesets[info["filename"]].append(info)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/pytest_doctestplus/version.py
new/pytest-doctestplus-1.1.0/pytest_doctestplus/version.py
--- old/pytest-doctestplus-1.0.0/pytest_doctestplus/version.py 2023-08-11
18:22:14.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/pytest_doctestplus/version.py 2023-12-13
19:12:59.000000000 +0100
@@ -1,4 +1,16 @@
# file generated by setuptools_scm
# don't change, don't track in version control
-__version__ = version = '1.0.0'
-__version_tuple__ = version_tuple = (1, 0, 0)
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from typing import Tuple, Union
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
+else:
+ VERSION_TUPLE = object
+
+version: str
+__version__: str
+__version_tuple__: VERSION_TUPLE
+version_tuple: VERSION_TUPLE
+
+__version__ = version = '1.1.0'
+__version_tuple__ = version_tuple = (1, 1, 0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/pytest_doctestplus.egg-info/PKG-INFO
new/pytest-doctestplus-1.1.0/pytest_doctestplus.egg-info/PKG-INFO
--- old/pytest-doctestplus-1.0.0/pytest_doctestplus.egg-info/PKG-INFO
2023-08-11 18:22:14.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/pytest_doctestplus.egg-info/PKG-INFO
2023-12-13 19:12:59.000000000 +0100
@@ -1,12 +1,12 @@
Metadata-Version: 2.1
Name: pytest-doctestplus
-Version: 1.0.0
+Version: 1.1.0
Summary: Pytest plugin with advanced doctest features.
Home-page: https://github.com/scientific-python/pytest-doctestplus
Author: Scientific Python Developers
License: BSD
Keywords: doctest,rst,pytest,py.test
-Classifier: Development Status :: 3 - Alpha
+Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: Pytest
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
@@ -14,7 +14,6 @@
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
@@ -23,10 +22,16 @@
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Utilities
-Requires-Python: >=3.7
+Requires-Python: >=3.8
Description-Content-Type: text/x-rst
-Provides-Extra: test
License-File: LICENSE.rst
+Requires-Dist: pytest>=4.6
+Requires-Dist: setuptools>=30.3.0
+Requires-Dist: packaging>=17.0
+Provides-Extra: test
+Requires-Dist: numpy; extra == "test"
+Requires-Dist: pytest-remotedata>=0.3.2; extra == "test"
+Requires-Dist: sphinx; extra == "test"
==================
pytest-doctestplus
@@ -71,6 +76,10 @@
* optional inclusion of ``*.rst`` files for doctests (see `Setup and
Configuration`_)
* optional inclusion of doctests in docstrings of Numpy ufuncs
+Further, ``pytest-doctestplus`` supports editing files to fix incorrect
docstrings
+(See `Fixing Existing Docstrings`_).
+
+.. _pytest-remotedata: https://github.com/astropy/pytest-remotedata
Installation
------------
@@ -132,6 +141,23 @@
doctest settings, see the `doctest documentation
<https://docs.python.org/3/library/doctest.html#option-flags>`_.
+Running Tests in Markdown Files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To run doctests in Markdown files, invoke pytest with the command line options
+``--doctest-plus --doctest-glob '*.md'``.
+
+If you write doctests inside `GitHub-style triple backtick fenced code blocks
+<https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks>`_,
+then in order for pytest-doctest to find and run them you need to include an
+extra trailing newline inside your code blocks, like this::
+
+ ```pycon
+ >>> 1 + 2
+ 2
+
+ ```
+
Doctest Directives
~~~~~~~~~~~~~~~~~~
@@ -387,6 +413,54 @@
``conf.py`` file.
+Fixing Existing Docstrings
+--------------------------
+The plugin has basic support to fix docstrings, this can be enabled by
+running ``pytest`` with ``--doctest-plus-generate-diff``.
+Without further options, this will print out a diff and a list of files that
+would be modified. Using ``--doctest-plus-generate-diff=overwrite`` will
+modify the files in-place, so it is recommended to run the check first to
+verify the paths.
+You may wish to review changes manually and only commit some patches e.g.
using ``git commit --patch``.
+
+The current diff generation is still very basic, for example, it does not
account for
+existing ``...``. By default a diff is only generated for *failing* doctests.
+
+In general, a mass edit may wish to focus on a specific change and
+possibly include passing tests. So you can opt-in into the behavior by
+adding a hook to your ``conftest.py``::
+
+ @pytest.hookimpl
+ def pytest_doctestplus_diffhook(info):
+ info["use"] = True # Overwrite all results (even successes)
+ if info["fileno"] is None:
+ # E.g. NumPy has C docstrings that cannot be found, we can add
+ # custom logic here to try and find these:
+ info["filename"] = ...
+ info["lineno"] = ...
+
+Where ``info`` is a dictionary containing the following items:
+
+* ``use``: ``True`` or ``False`` signalling whether to apply the diff. This is
+ set to ``False`` if a doctest succeeded and ``True`` if the doctest failed.
+* ``name``: The name of the test (e.g. the function being documented)
+* ``filename``: The file that contains the test (this can be wrong in certain
+ situation and in that case ``test_lineno`` will be wrong as well).
+* ``source``: The source code that was executed for this test
+* ``test_lineno``: The line of code where the example block (or function)
starts.
+ In some cases, the test file cannot be found and the lineno will be ``None``,
+ you can manually try to fix these.
+* ``example_lineno``: The line number of the example snippet
+ (individual ``>>>``).
+* ``want``: The current documentation.
+* ``got``: The result of executing the example.
+
+You can modify the dictionary in-place to modify the behavior.
+
+Please note that we assume that this API will be used only occasionally and
+reserve the right to change it at any time.
+
+
Development Status
------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/pytest-doctestplus-1.0.0/pytest_doctestplus.egg-info/SOURCES.txt
new/pytest-doctestplus-1.1.0/pytest_doctestplus.egg-info/SOURCES.txt
--- old/pytest-doctestplus-1.0.0/pytest_doctestplus.egg-info/SOURCES.txt
2023-08-11 18:22:14.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/pytest_doctestplus.egg-info/SOURCES.txt
2023-12-13 19:12:59.000000000 +0100
@@ -13,6 +13,7 @@
licenses/README.rst
licenses/SYMPY_LICENSE.rst
pytest_doctestplus/__init__.py
+pytest_doctestplus/newhooks.py
pytest_doctestplus/output_checker.py
pytest_doctestplus/plugin.py
pytest_doctestplus/utils.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/setup.cfg
new/pytest-doctestplus-1.1.0/setup.cfg
--- old/pytest-doctestplus-1.0.0/setup.cfg 2023-08-11 18:22:14.703384200
+0200
+++ new/pytest-doctestplus-1.1.0/setup.cfg 2023-12-13 19:12:59.113894500
+0100
@@ -3,7 +3,7 @@
url = https://github.com/scientific-python/pytest-doctestplus
author = Scientific Python Developers
classifiers =
- Development Status :: 3 - Alpha
+ Development Status :: 5 - Production/Stable
Framework :: Pytest
Intended Audience :: Developers
License :: OSI Approved :: BSD License
@@ -11,7 +11,6 @@
Programming Language :: Python
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
- Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
@@ -29,7 +28,7 @@
[options]
zip_safe = False
packages = find:
-python_requires = >=3.7
+python_requires = >=3.8
setup_requires =
setuptools_scm
install_requires =
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/tests/conftest.py
new/pytest-doctestplus-1.1.0/tests/conftest.py
--- old/pytest-doctestplus-1.0.0/tests/conftest.py 2023-08-11
18:21:57.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/tests/conftest.py 2023-12-13
19:12:44.000000000 +0100
@@ -8,7 +8,7 @@
# Keep this until we require numpy to be >=2.0 or there is a directive in
doctestplus
# to support multiple ways of repr
-if Version(np.__version__) > Version("2.0.0.dev0+151"):
+if Version(np.__version__) >= Version("2.0.dev"):
np.set_printoptions(legacy="1.25")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/tests/python/doctests.py
new/pytest-doctestplus-1.1.0/tests/python/doctests.py
--- old/pytest-doctestplus-1.0.0/tests/python/doctests.py 2023-08-11
18:21:57.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/tests/python/doctests.py 2023-12-13
19:12:44.000000000 +0100
@@ -1,5 +1,12 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
+"""
+Also module level skips should be matched with `*` and `.`, test at least
+the `.` version (the star would match all others too).
+
+>>> import foobar
+"""
+
__doctest_skip__ = [
'skip_this_test',
'ClassWithSomeBadDocTests.this_test_fails',
@@ -7,6 +14,7 @@
]
__doctest_requires__ = {
+ '.': ['foobar'],
'depends_on_foobar': ['foobar'],
'depends_on_foobar_submodule': ['foobar.baz'],
'depends_on_two_modules': ['os', 'foobar'],
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/tests/test_doctestplus.py
new/pytest-doctestplus-1.1.0/tests/test_doctestplus.py
--- old/pytest-doctestplus-1.0.0/tests/test_doctestplus.py 2023-08-11
18:21:57.000000000 +0200
+++ new/pytest-doctestplus-1.1.0/tests/test_doctestplus.py 2023-12-13
19:12:44.000000000 +0100
@@ -3,6 +3,8 @@
from textwrap import dedent
import sys
+from packaging.version import Version
+
import pytest
import doctest
@@ -12,6 +14,9 @@
pytest_plugins = ['pytester']
+PYTEST_LT_6 = Version(pytest.__version__) < Version('6.0.0')
+
+
def test_ignored_whitespace(testdir):
testdir.makeini(
"""
@@ -593,6 +598,32 @@
).assertoutcome(passed=1)
[email protected](reason='known issue, fenced code blocks require an extra
trailing newline')
+def test_markdown_fenced_code(testdir):
+ testdir.makefile('.md', foo="""\
+```
+>>> 1 + 1
+2
+```
+""")
+ testdir.inline_run(
+ '--doctest-plus', '--doctest-glob', '*.md'
+ ).assertoutcome(passed=1)
+
+
+def test_markdown_fenced_code_with_extra_newline(testdir):
+ testdir.makefile('.md', foo="""\
+```
+>>> 1 + 1
+2
+
+```
+""")
+ testdir.inline_run(
+ '--doctest-plus', '--doctest-glob', '*.md'
+ ).assertoutcome(passed=1)
+
+
def test_text_file_comments(testdir):
testdir.makefile(
'.md',
@@ -769,20 +800,38 @@
)
-def test_doctest_subpackage_requires(testdir, caplog):
+# Note that each entry under doctest_subpackage_requires has different
whitespace
+# around the = to make sure that all cases work properly.
+SUBPACKAGE_REQUIRES_INI = (
+ "makeini",
+ """
+ [pytest]
+ doctest_subpackage_requires =
+ test/a/* = pytest>1
+ test/b/*= pytest>1;averyfakepackage>99999.9
+ test/c/*=anotherfakepackage>=22000.1.2
+ """
+)
+SUBPACKAGE_REQUIRES_PYPROJECT = (
+ "makepyprojecttoml",
+ """
+ [tool.pytest.ini_options]
+ doctest_subpackage_requires = [
+ "test/a/* = pytest>1",
+ "test/b/*= pytest>1;averyfakepackage>99999.9",
+ "test/c/*=anotherfakepackage>=22000.1.2",
+ ]
+ """
+)
- # Note that each entry below has different whitespace around the = to
- # make sure that all cases work properly.
- testdir.makeini(
- """
- [pytest]
- doctest_subpackage_requires =
- test/a/* = pytest>1
- test/b/*= pytest>1;averyfakepackage>99999.9
- test/c/*=anotherfakepackage>=22000.1.2
- """
- )
[email protected]()
+def subpackage_requires_testdir(testdir, request):
+ if request.param[0] == 'makepyprojecttoml' and PYTEST_LT_6:
+ return None, None
+
+ config_file = getattr(testdir, request.param[0])(request.param[1])
+
test = testdir.mkdir('test')
a = test.mkdir('a')
b = test.mkdir('b')
@@ -801,12 +850,46 @@
b.join('testcode.py').write(pyfile)
c.join('testcode.py').write(pyfile)
- reprec = testdir.inline_run(test, "--doctest-plus")
+ return config_file, testdir
+
+
[email protected]('subpackage_requires_testdir',
[SUBPACKAGE_REQUIRES_INI, SUBPACKAGE_REQUIRES_PYPROJECT], indirect=True)
+def test_doctest_subpackage_requires(subpackage_requires_testdir, caplog):
+ config_file, testdir = subpackage_requires_testdir
+ if config_file is None:
+ pytest.skip("pyproject.toml not supported in pytest<6")
+
+ reprec = testdir.inline_run(str(testdir), f"-c={config_file}",
"--doctest-plus")
reprec.assertoutcome(passed=1)
assert reprec.listoutcomes()[0][0].location[0] == os.path.join('test',
'a', 'testcode.py')
assert caplog.text == ''
[email protected](('import_mode', 'expected'), [
+ pytest.param('importlib', dict(passed=2),
marks=pytest.mark.skipif(PYTEST_LT_6, reason="importlib import mode not
supported on Pytest <6"), id="importlib"),
+ pytest.param('append', dict(failed=1), id="append"),
+ pytest.param('prepend', dict(failed=1), id="prepend"),
+])
+def test_import_mode(testdir, import_mode, expected):
+ """Test that two files with the same name but in different folders work
with --import-mode=importlib."""
+ a = testdir.mkdir('a')
+ b = testdir.mkdir('b')
+
+ pyfile = dedent("""
+ def f():
+ '''
+ >>> 1
+ 1
+ '''
+ """)
+
+ a.join('testcode.py').write(pyfile)
+ b.join('testcode.py').write(pyfile)
+
+ reprec = testdir.inline_run(str(testdir), "--doctest-plus",
f"--import-mode={import_mode}")
+ reprec.assertoutcome(**expected)
+
+
def test_doctest_skip(testdir):
testdir.makeini(
"""
@@ -1039,6 +1122,24 @@
assert ("something()\nUNEXPECTED EXCEPTION: NameError" in
report.longreprtext) is cont_on_fail
+def test_main(testdir):
+ pkg = testdir.mkdir('pkg')
+ code = dedent(
+ '''
+ def f():
+ raise RuntimeError("This is a CLI, do not execute module while
doctesting")
+
+ f()
+ '''
+ )
+ pkg.join('__init__.py').write_text("", "utf-8")
+ main_path = pkg.join('__main__.py')
+ main_path.write_text(code, "utf-8")
+
+ testdir.inline_run(pkg).assertoutcome(passed=0)
+ testdir.inline_run(pkg, '--doctest-plus').assertoutcome(passed=0)
+
+
def test_ufunc(testdir):
pytest.importorskip('numpy')
@@ -1144,6 +1245,88 @@
result.assertoutcome(passed=2, failed=0)
+NORCURSEDIRS_INI = (
+ "makeini",
+ """
+ [pytest]
+ doctest_norecursedirs =
+ "bad_dir"
+ "*/bad_file.py"
+ """
+)
+NORCURSEDIRS_PYPROJECT = (
+ "makepyprojecttoml",
+ """
+ [tool.pytest.ini_options]
+ doctest_norecursedirs = [
+ "bad_dir",
+ "*/bad_file.py",
+ ]
+ """
+)
+
+
[email protected]()
+def norecursedirs_testdir(testdir, request):
+ if request.param[0] == 'makepyprojecttoml' and PYTEST_LT_6:
+ return None, None
+
+ config_file = getattr(testdir, request.param[0])(request.param[1])
+
+ bad_text = dedent("""
+ def f():
+ '''
+ This should fail doc testing
+ >>> 1
+ 2
+ '''
+ pass
+ """)
+
+ good_text = dedent("""
+ def g():
+ '''
+ This should pass doc testing
+ >>> 1
+ 1
+ '''
+ pass
+ """)
+
+ # Create a bad file that should be by its folder
+ bad_subdir = testdir.mkdir("bad_dir")
+ bad_file = bad_subdir.join("test_foobar.py")
+ bad_file.write_text(bad_text, "utf-8")
+
+ # Create a bad file that should be skipped by its name
+ okay_subdir1 = testdir.mkdir("okay_foo_dir")
+ bad_file = okay_subdir1.join("bad_file.py")
+ bad_file.write_text(bad_text, "utf-8")
+ # Create a good file in that directory that doctest won't skip
+ good_file1 = okay_subdir1.join("good_file1.py")
+ good_file1.write_text(good_text, "utf-8")
+
+ # Create another bad file that should be skipped by its name
+ okay_subdir2 = testdir.mkdir("okay_bar_dir")
+ bad_file = okay_subdir2.join("bad_file.py")
+ bad_file.write_text(bad_text, "utf-8")
+ # Create a good file in that directory that doctest won't skip
+ good_file2 = okay_subdir2.join("good_file2.py")
+ good_file2.write_text(good_text, "utf-8")
+
+ return config_file, testdir
+
+
[email protected]('norecursedirs_testdir', [NORCURSEDIRS_INI,
NORCURSEDIRS_PYPROJECT], indirect=True)
+def test_doctest_norecursedirs(norecursedirs_testdir):
+ config_file, testdir = norecursedirs_testdir
+ if config_file is None:
+ pytest.skip("pyproject.toml not supported in pytest<6")
+
+ reprec = testdir.inline_run(str(testdir), f"-c={config_file}",
"--doctest-plus")
+ reprec.assertoutcome(passed=2)
+
+
def test_norecursedirs(testdir):
testdir.makeini(
"""
@@ -1165,3 +1348,39 @@
""", "utf-8")
reprec = testdir.inline_run(str(testdir), "--doctest-plus")
reprec.assertoutcome(failed=0, passed=0)
+
+
+def test_generate_diff_basic(testdir, capsys):
+ p = testdir.makepyfile("""
+ def f():
+ '''
+ >>> print(2)
+ 4
+ >>> print(3)
+ 5
+ '''
+ pass
+ """)
+ with open(p) as f:
+ original = f.read()
+
+ testdir.inline_run(p, "--doctest-plus-generate-diff")
+ diff = dedent("""
+ >>> print(2)
+ - 4
+ + 2
+ >>> print(3)
+ - 5
+ + 3
+ """)
+ captured = capsys.readouterr()
+ assert diff in captured.out
+
+ testdir.inline_run(p, "--doctest-plus-generate-diff=overwrite")
+ captured = capsys.readouterr()
+ assert "Applied fix to the following files" in captured.out
+
+ with open(p) as f:
+ result = f.read()
+
+ assert result == original.replace("4", "2").replace("5", "3")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/pytest-doctestplus-1.0.0/tox.ini
new/pytest-doctestplus-1.1.0/tox.ini
--- old/pytest-doctestplus-1.0.0/tox.ini 2023-08-11 18:21:57.000000000
+0200
+++ new/pytest-doctestplus-1.1.0/tox.ini 2023-12-13 19:12:44.000000000
+0100
@@ -1,6 +1,6 @@
[tox]
envlist =
- py{37,38,39,310,311,312}-test
+ py{38,39,310,311,312}-test
codestyle
requires =
setuptools >= 30.3.0
@@ -14,7 +14,7 @@
py312: PIP_EXTRA_INDEX_URL =
https://pypi.anaconda.org/scientific-python-nightly-wheels/simple
description = run tests
deps =
- pytest46: pytest==4.6.*
+ pytestoldest: pytest==4.6.0
pytest50: pytest==5.0.*
pytest51: pytest==5.1.*
pytest52: pytest==5.2.*