././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709568493.5764556 pytest-doctestplus-1.2.0/0000755000175100001770000000000000000000000016217 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709568493.5684557 pytest-doctestplus-1.2.0/.github/0000755000175100001770000000000000000000000017557 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/.github/dependabot.yml0000644000175100001770000000111600000000000022406 0ustar00runnerdocker00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "github-actions" # See documentation for possible values directory: ".github/workflows" # Location of package manifests schedule: interval: "weekly" groups: actions: patterns: - "*" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1709568493.5684557 pytest-doctestplus-1.2.0/.github/workflows/0000755000175100001770000000000000000000000021614 5ustar00runnerdocker00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/.github/workflows/publish.yml0000644000175100001770000000237700000000000024016 0ustar00runnerdocker00000000000000name: Release on: pull_request: push: tags: - '*' jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI runs-on: ubuntu-latest if: ((github.event_name == 'push' && startsWith(github.ref, 'refs/tags')) || contains(github.event.pull_request.labels.*.name, 'Build wheels')) steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install python-build and twine run: python -m pip install build "twine>=3.3" - name: Build package run: python -m build --sdist --wheel . - name: List result run: ls -l dist - name: Check long_description run: python -m twine check --strict dist/* - name: Test package run: | cd .. python -m venv testenv testenv/bin/pip install pytest pytest-remotedata $(ls pytest-doctestplus/dist/*.whl)[test] testenv/bin/pytest pytest-doctestplus/tests --doctest-plus --doctest-rst - name: Publish distribution 📦 to PyPI if: startsWith(github.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.pypi_password }} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/.github/workflows/python-tests.yml0000644000175100001770000000514700000000000025027 0ustar00runnerdocker00000000000000name: Run unit tests on: pull_request: push: branches: [ main ] tags: - '*' workflow_dispatch: schedule: # Run every Tuesday at 03:53 UTC - cron: 53 3 * * 2 jobs: tests: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: ubuntu-latest python-version: 3.8 toxenv: py38-test-pytestoldest - os: windows-latest python-version: 3.8 toxenv: py38-test-pytest50 - os: macos-latest python-version: 3.8 toxenv: py38-test-pytest51 - os: ubuntu-latest python-version: 3.8 toxenv: py38-test-pytest52 - os: windows-latest python-version: 3.8 toxenv: py38-test-pytest53 - os: ubuntu-latest python-version: 3.8 toxenv: py38-test-pytest60 - os: ubuntu-latest python-version: 3.9 toxenv: py39-test-pytest61 - os: ubuntu-latest python-version: 3.9 toxenv: py39-test-pytest62 - os: ubuntu-latest python-version: '3.10' toxenv: py310-test-pytest70 - os: ubuntu-latest python-version: '3.10' toxenv: py310-test-pytest71 - os: windows-latest python-version: '3.11' toxenv: py311-test-pytest72 - os: ubuntu-latest python-version: '3.11' toxenv: py311-test-pytest73 - os: ubuntu-latest python-version: '3.11' toxenv: py311-test-pytest74 - os: ubuntu-latest python-version: '3.12' toxenv: py312-test-pytest80 - os: macos-latest python-version: '3.12' toxenv: py312-test-pytest80 - os: windows-latest python-version: '3.12' toxenv: py312-test-pytest80 - os: macos-latest python-version: '3.11' toxenv: py311-test-pytestdev - os: windows-latest python-version: '3.11' toxenv: py311-test-pytestdev - os: ubuntu-latest python-version: '3.12' toxenv: py312-test-pytestdev-numpydev steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install Tox run: python -m pip install tox - name: Run Tox run: tox ${{ matrix.toxargs }} -v -e ${{ matrix.toxenv }} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/.gitignore0000644000175100001770000000123300000000000020206 0ustar00runnerdocker00000000000000# Compiled files *.py[cod] *.a *.o *.so *.pyd __pycache__ # Ignore .c files by default to avoid including generated code. If you want to # add a non-generated .c extension, use `git add -f filename.c`. *.c # Other generated files MANIFEST # Sphinx _build _generated docs/api docs/generated # Packages/installer info *.egg *.egg-info dist build eggs .eggs parts bin var sdist develop-eggs .installed.cfg distribute-*.tar.gz # Other .cache .tox .*.swp .*.swo *~ .project .pydevproject .settings .coverage cover htmlcov .pytest_cache # Env .venv venv .env # Mac OSX .DS_Store # PyCharm .idea # VS code .vscode pytest_doctestplus/version.py pip-wheel-metadata/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/.mailmap0000644000175100001770000000222500000000000017641 0ustar00runnerdocker00000000000000Brigitta Sipőcz Brigitta Sipőcz Dan D'Avella Dan D'Avella E. Madison Bray E. Madison Bray Hans Moritz Günther Kirill Tchernyshyov Leo Singer Matt Davis Matteo Bachetti Matteo Bachetti Michael Seifert Pey Lian Lim <2090236+pllim@users.noreply.github.com> Philipp A. Pratik Patel Sebastian Berg Simon Conseil Simon Conseil Tinuade Adeleke ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/CHANGES.rst0000644000175100001770000001205200000000000020021 0ustar00runnerdocker000000000000001.2.0 (2024-03-04) ================== - Compatibility with pytest 8.1. [#236, #238] 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) ================== - Changing GitHub organization. 0.13.0 (2023-06-07) =================== - Compatibility with pytest 7.4 with respect to ``norecursedirs`` handling. [#201] - Respect ``--doctest-continue-on-failure`` flag. [#197] - Report doctests raising skip exceptions as skipped. [#196] 0.12.1 (2022-09-26) =================== - Allow floating point comparison in Python dictionary. [#186] 0.12.0 (2022-02-25) =================== - Run doctests in docstrings of Numpy ufuncs. [#174, #175] 0.11.2 (2021-12-09) =================== - Fix version check for pytest 7.0.0rc1. [#171] - Recognize text beginning with ``\n3", ) testdir.makefile( '.rst', foo_2=".. >>> 1 + 1\n3", ) testdir.makefile( '.tex', foo_3="% >>> 1 + 1\n3", ) testdir.makefile( '.txt', foo_4="# >>> 1 + 1\n3", ) testdir.inline_run( '--doctest-plus', '--doctest-glob', '*.md', '--doctest-glob', '*.rst', '--doctest-glob', '*.tex', '--doctest-glob', '*.txt' ).assertoutcome(passed=0) def test_text_file_comment_chars(testdir): # override default comment chars testdir.makeini( """ [pytest] text_file_extensions = .rst=# .tex=# """ ) testdir.makefile( '.rst', foo_1="# >>> 1 + 1\n3", ) testdir.makefile( '.tex', foo_2="# >>> 1 + 1\n3", ) testdir.inline_run( '--doctest-plus', '--doctest-glob', '*.rst', '--doctest-glob', '*.tex', '--doctest-glob', '*.txt' ).assertoutcome(passed=0) def test_ignore_option(testdir): testdir.makepyfile(foo=""" def f(): ''' >>> 1+1 2 ''' pass """) testdir.makepyfile(bar=""" def f(): ''' >>> 1+1 2 ''' pass """) testdir.makefile('.rst', foo='>>> 1+1\n2') testdir.inline_run('--doctest-plus').assertoutcome(passed=2) testdir.inline_run('--doctest-plus', '--doctest-rst').assertoutcome(passed=3) testdir.inline_run( '--doctest-plus', '--doctest-rst', '--ignore', '.' ).assertoutcome(passed=0) testdir.inline_run( '--doctest-plus', '--doctest-rst', '--ignore', 'bar.py' ).assertoutcome(passed=2) def test_ignore_glob_option(testdir): testdir.makepyfile(foo=""" def f(): ''' >>> 1+1 2 ''' pass """) testdir.makepyfile(bar=""" def f(): ''' >>> 1+1 2 ''' pass """) testdir.makefile('.rst', foo='>>> 1+1\n2') testdir.inline_run( '--doctest-plus', '--doctest-rst', '--ignore-glob', 'foo*' ).assertoutcome(passed=1) testdir.inline_run( '--doctest-plus', '--doctest-rst', '--ignore-glob', 'bar*' ).assertoutcome(passed=2) testdir.inline_run( '--doctest-plus', '--doctest-rst', '--ignore-glob', '*.rst' ).assertoutcome(passed=2) def test_doctest_only(testdir, makepyfile, maketestfile, makerstfile): # regular python files with doctests makepyfile(p1='>>> 1 + 1\n2') makepyfile(p2='>>> 1 + 1\n3') # regular test files maketestfile(test_1='foo') maketestfile(test_2='bar') # rst files makerstfile(r1='>>> 1 + 1\n2') makerstfile(r3='>>> 1 + 1\n3') makerstfile(r2='>>> 1 + 2\n3') # regular tests testdir.inline_run().assertoutcome(passed=2) # regular + doctests testdir.inline_run("--doctest-plus").assertoutcome(passed=3, failed=1) # regular + doctests + doctest in rst files testdir.inline_run("--doctest-plus", "--doctest-rst").assertoutcome(passed=5, failed=2) # only doctests in python files, implicit usage of doctest-plus testdir.inline_run("--doctest-only").assertoutcome(passed=1, failed=1) # only doctests in python files testdir.inline_run("--doctest-only", "--doctest-rst").assertoutcome(passed=3, failed=2) def test_doctest_float_replacement(tmp_path): test1 = dedent(""" This will demonstrate a doctest that fails due to a few extra decimal places:: >>> 1.0 / 3.0 0.333333333333333311 """) test2 = dedent(""" This is the same test, but it should pass with use of +FLOAT_CMP:: >>> 1.0 / 3.0 # doctest: +FLOAT_CMP 0.333333333333333311 """) test1_rst = tmp_path / "test1.rst" test2_rst = tmp_path / "test2.rst" test1_rst.write_text(test1) test2_rst.write_text(test2) with pytest.raises(doctest.DocTestFailure): doctest.testfile( test1_rst, module_relative=False, raise_on_error=True, verbose=False, encoding="utf-8", ) doctest.testfile( test2_rst, module_relative=False, raise_on_error=True, verbose=False, encoding="utf-8", ) # 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", ] """ ) @pytest.fixture() 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') c = test.mkdir('c') pyfile = dedent(""" def f(): ''' >>> 1 1 ''' pass """) a.join('testcode.py').write(pyfile) b.join('testcode.py').write(pyfile) c.join('testcode.py').write(pyfile) return config_file, testdir @pytest.mark.parametrize('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 == '' @pytest.mark.parametrize(('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( """ [pytest] doctestplus = enabled """) p = testdir.makefile( '.rst', """ .. doctest-skip:: >>> import asdf >>> asdf.open('file.asdf') # doctest: +IGNORE_WARNINGS """ ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) # We repeat all testst including remote data with and without it opted in def test_remote_data_url(testdir): testdir.makeini( """ [pytest] doctestplus = enabled """) p = testdir.makefile( '.rst', """ # This test should be skipped when remote data is not requested. .. doctest-remote-data:: >>> from contextlib import closing >>> from urllib.request import urlopen >>> with closing(urlopen('https://www.astropy.org')) as remote: ... remote.read() # doctest: +IGNORE_OUTPUT """ ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(passed=1) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) def test_remote_data_float_cmp(testdir): testdir.makeini( """ [pytest] doctestplus = enabled """) p = testdir.makefile( '.rst', """ #This test is skipped when remote data is not requested .. doctest-remote-data:: >>> x = 1/3. >>> x # doctest: +FLOAT_CMP 0.333333 """ ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(passed=1) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) def test_remote_data_ignore_whitespace(testdir): testdir.makeini( """ [pytest] doctest_optionflags = NORMALIZE_WHITESPACE doctestplus = enabled """) p = testdir.makefile( '.rst', """ #This test should be skipped when remote data is not requested, and should #pass when remote data is requested .. doctest-remote-data:: >>> a = "foo " >>> print(a) foo """ ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(passed=1) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) def test_remote_data_ellipsis(testdir): testdir.makeini( """ [pytest] doctest_optionflags = ELLIPSIS doctestplus = enabled """) p = testdir.makefile( '.rst', """ # This test should be skipped when remote data is not requested, and should # pass when remote data is requested .. doctest-remote-data:: >>> a = "freedom at last" >>> print(a) freedom ... """ ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(passed=1) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) def test_remote_data_requires(testdir): testdir.makeini( """ [pytest] doctestplus = enabled """) p = testdir.makefile( '.rst', """ # This test should be skipped when remote data is not requested. # It should also be skipped instead of failing when remote data is requested because # the module required does not exist .. doctest-remote-data:: .. doctest-requires:: does-not-exist >>> 1 + 1 3 """ ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(skipped=1) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) def test_remote_data_ignore_warnings(testdir): testdir.makeini( """ [pytest] doctestplus = enabled """) p = testdir.makefile( '.rst', """ # This test should be skipped if remote data is not requested. .. doctest-remote-data:: >>> import warnings >>> warnings.warn('A warning occurred', UserWarning) # doctest: +IGNORE_WARNINGS """ ) testdir.inline_run(p, '--doctest-plus', '--doctest-rst', '--remote-data').assertoutcome(passed=1) testdir.inline_run(p, '--doctest-plus', '--doctest-rst').assertoutcome(skipped=1) def test_skiptest(testdir): testdir.makeini( """ [pytest] doctestplus = enabled """ ) p = testdir.makepyfile( """ class MyClass: ''' >>> import pytest >>> pytest.skip("I changed my mind") >>> assert False, "This should not be reached" ''' pass """ ) reprec = testdir.inline_run(p, "--doctest-plus") reprec.assertoutcome(skipped=1, failed=0) @pytest.mark.parametrize('cont_on_fail', [False, True]) def test_fail_two_tests(testdir, cont_on_fail): p = testdir.makepyfile( """ class MyClass: ''' .. doctest:: >>> print(2) 1 .. doctest:: >>> print(3) 1 ''' pass """ ) arg = ("--doctest-continue-on-failure",) if cont_on_fail else () reprec = testdir.inline_run(p, "--doctest-plus", *arg) reprec.assertoutcome(skipped=0, failed=1) _, _, failed = reprec.listoutcomes() report = failed[0] assert "Expected:\n 1\nGot:\n 2" in report.longreprtext assert ("Expected:\n 1\nGot:\n 3" in report.longreprtext) is cont_on_fail @pytest.mark.parametrize('cont_on_fail', [False, True]) def test_fail_data_dependency(testdir, cont_on_fail): p = testdir.makepyfile( """ class MyClass: ''' .. doctest:: >>> import nonexistentmodule as nem >>> a = nem.calculate_something() ''' pass """ ) arg = ("--doctest-continue-on-failure",) if cont_on_fail else () reprec = testdir.inline_run(p, "--doctest-plus", *arg) reprec.assertoutcome(skipped=0, failed=1) _, _, failed = reprec.listoutcomes() # Both lines fail in a single error report = failed[0] assert " as nem\nUNEXPECTED EXCEPTION: ModuleNotFoundError" in report.longreprtext 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') # Create and build example module testdir.makepyfile(module1=""" def foo(): '''A doctest... >>> foo() 1 ''' return 1 """) testdir.makepyfile(module2=""" from _module2 import foo """) testdir.makepyfile(setup=""" from setuptools import setup, Extension import numpy as np ext = Extension('_module2', ['_module2.c'], extra_compile_args=['-std=c99'], include_dirs=[np.get_include()]) setup(name='example', py_modules=['module1', 'module2'], ext_modules=[ext]) """) testdir.makefile('.c', _module2=r""" #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #include static double foo_inner(double a, double b) { return a + b; } static void foo_loop( char **args, const npy_intp *dimensions, const npy_intp *steps, void *NPY_UNUSED(data) ) { const npy_intp n = dimensions[0]; for (npy_intp i = 0; i < n; i ++) { *(double *) &args[2][i * steps[2]] = foo_inner( *(double *) &args[0][i * steps[0]], *(double *) &args[1][i * steps[1]]); } } static PyUFuncGenericFunction foo_loops[] = {foo_loop}; static char foo_types[] = {NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE}; static void *foo_data[] = {NULL}; static const char foo_name[] = "foo"; static const char foo_docstring[] = ">>> foo(1, 2)\n3.0"; static PyModuleDef moduledef = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_module2", .m_size = -1 }; PyMODINIT_FUNC PyInit__module2(void) { import_array(); import_ufunc(); PyObject *module = PyModule_Create(&moduledef); if (!module) return NULL; PyObject *obj = PyUFunc_FromFuncAndData( foo_loops, foo_data, foo_types, 1, 2, 1, PyUFunc_None, foo_name, foo_docstring, 0); if (!obj) { Py_DECREF(module); return NULL; } if (PyModule_AddObject(module, foo_name, obj) < 0) { Py_DECREF(obj); Py_DECREF(module); return NULL; } return module; } """) testdir.run(sys.executable, 'setup.py', 'build') build_dir, = glob.glob(str(testdir.tmpdir / 'build/lib.*')) result = testdir.inline_run(build_dir, '--doctest-plus', '--doctest-modules') result.assertoutcome(passed=1, failed=0) result = testdir.inline_run(build_dir, '--doctest-plus', '--doctest-modules', '--doctest-ufunc') 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", ] """ ) @pytest.fixture() 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 @pytest.mark.parametrize('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( """ [pytest] norecursedirs = \"bad_dir\" doctestplus = enabled """ ) subdir = testdir.mkdir("bad_dir") badfile = subdir.join("test_foobar.py") badfile.write_text(""" def f(): ''' >>> x = 1/3. >>> x 0.333333 ''' fail """, "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") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/tests/test_utils.py0000644000175100001770000000052400000000000022133 0ustar00runnerdocker00000000000000from pytest_doctestplus.utils import ModuleChecker class TestModuleChecker: def test_simple(self): c = ModuleChecker() assert c.check('sys') assert not c.check('foobar') def test_with_version(self): c = ModuleChecker() assert c.check('pytest>1.0') assert not c.check('foobar>1.0') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1709568482.0 pytest-doctestplus-1.2.0/tox.ini0000644000175100001770000000332200000000000017532 0ustar00runnerdocker00000000000000[tox] envlist = py{38,39,310,311,312}-test codestyle requires = setuptools >= 30.3.0 pip >= 19.3.1 isolated_build = true [testenv] changedir = .tmp/{envname} setenv = numpydev: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/scientific-python-nightly-wheels/simple description = run tests deps = pytestoldest: pytest==4.6.0 pytest50: pytest==5.0.* pytest51: pytest==5.1.* pytest52: pytest==5.2.* pytest53: pytest==5.3.* pytest60: pytest==6.0.* pytest61: pytest==6.1.* pytest62: pytest==6.2.* pytest70: pytest==7.0.* pytest71: pytest==7.1.* pytest72: pytest==7.2.* pytest73: pytest==7.3.* pytest74: pytest==7.4.* pytest80: pytest==8.0.* pytestdev: git+https://github.com/pytest-dev/pytest#egg=pytest numpydev: numpy>=0.0.dev0 extras = test # Temporary measures to be able to test on 8.0.x in its RC cycle pip_pre = pytest80: true !pytest80: false commands = pip freeze # Ignore directly running tests in ``skip_some_remote_data.rst`` with # ``remote-data`` as there are some artificial failures included in there. pytest {toxinidir}/tests --ignore={toxinidir}/tests/docs/skip_some_remote_data.rst --doctest-plus --doctest-rst --remote-data {posargs} pytest {toxinidir}/tests {posargs} pytest {toxinidir}/tests --doctest-plus {posargs} pytest {toxinidir}/tests --doctest-plus --doctest-rst {posargs} pytest {toxinidir}/tests --doctest-plus --doctest-rst --text-file-format=tex {posargs} sphinx-build {toxinidir}/tests {toxinidir}/tests/_build/html -W [testenv:codestyle] changedir = skip_install = true description = check code style, e.g. with flake8 deps = flake8 commands = flake8 pytest_doctestplus --count