2024-01-28 21:12:42 +08:00
|
|
|
|
# mypy: allow-untyped-defs
|
2019-06-08 10:39:59 +08:00
|
|
|
|
import inspect
|
2020-12-19 20:02:24 +08:00
|
|
|
|
from pathlib import Path
|
2022-02-11 23:20:42 +08:00
|
|
|
|
import sys
|
2018-08-24 00:06:17 +08:00
|
|
|
|
import textwrap
|
2020-05-01 19:40:17 +08:00
|
|
|
|
from typing import Callable
|
|
|
|
|
from typing import Optional
|
2018-10-25 15:01:29 +08:00
|
|
|
|
|
2019-07-08 22:41:33 +08:00
|
|
|
|
from _pytest.doctest import _get_checker
|
2021-07-28 05:50:26 +08:00
|
|
|
|
from _pytest.doctest import _is_main_py
|
2019-06-08 10:39:59 +08:00
|
|
|
|
from _pytest.doctest import _is_mocked
|
2020-05-08 04:14:58 +08:00
|
|
|
|
from _pytest.doctest import _is_setup_py
|
2019-06-08 10:39:59 +08:00
|
|
|
|
from _pytest.doctest import _patch_unwrap_mock_aware
|
2018-10-25 15:01:29 +08:00
|
|
|
|
from _pytest.doctest import DoctestItem
|
|
|
|
|
from _pytest.doctest import DoctestModule
|
|
|
|
|
from _pytest.doctest import DoctestTextfile
|
2020-10-12 23:13:06 +08:00
|
|
|
|
from _pytest.pytester import Pytester
|
2015-08-13 08:41:31 +08:00
|
|
|
|
import pytest
|
2009-09-06 22:59:39 +08:00
|
|
|
|
|
2016-12-13 20:49:44 +08:00
|
|
|
|
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class TestDoctests:
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_collect_testtextfile(self, pytester: Pytester):
|
|
|
|
|
w = pytester.maketxtfile(whatever="")
|
|
|
|
|
checkfile = pytester.maketxtfile(
|
2009-09-06 22:59:39 +08:00
|
|
|
|
test_something="""
|
|
|
|
|
alskdjalsdk
|
|
|
|
|
>>> i = 5
|
|
|
|
|
>>> i-1
|
|
|
|
|
4
|
|
|
|
|
"""
|
|
|
|
|
)
|
2016-06-02 00:16:05 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
for x in (pytester.path, checkfile):
|
2017-07-17 07:25:09 +08:00
|
|
|
|
# print "checking that %s returns custom items" % (x,)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
items, reprec = pytester.inline_genitems(x)
|
2009-09-06 22:59:39 +08:00
|
|
|
|
assert len(items) == 1
|
2016-06-02 00:16:05 +08:00
|
|
|
|
assert isinstance(items[0], DoctestItem)
|
|
|
|
|
assert isinstance(items[0].parent, DoctestTextfile)
|
|
|
|
|
# Empty file has no items.
|
2020-10-12 23:13:06 +08:00
|
|
|
|
items, reprec = pytester.inline_genitems(w)
|
2016-06-02 00:16:05 +08:00
|
|
|
|
assert len(items) == 0
|
2009-09-06 22:59:39 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_collect_module_empty(self, pytester: Pytester):
|
|
|
|
|
path = pytester.makepyfile(whatever="#")
|
|
|
|
|
for p in (path, pytester.path):
|
|
|
|
|
items, reprec = pytester.inline_genitems(p, "--doctest-modules")
|
2013-05-17 23:18:22 +08:00
|
|
|
|
assert len(items) == 0
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_collect_module_single_modulelevel_doctest(self, pytester: Pytester):
|
|
|
|
|
path = pytester.makepyfile(whatever='""">>> pass"""')
|
|
|
|
|
for p in (path, pytester.path):
|
|
|
|
|
items, reprec = pytester.inline_genitems(p, "--doctest-modules")
|
2009-09-06 22:59:39 +08:00
|
|
|
|
assert len(items) == 1
|
2013-05-17 23:18:22 +08:00
|
|
|
|
assert isinstance(items[0], DoctestItem)
|
|
|
|
|
assert isinstance(items[0].parent, DoctestModule)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_collect_module_two_doctest_one_modulelevel(self, pytester: Pytester):
|
|
|
|
|
path = pytester.makepyfile(
|
2013-05-17 23:18:22 +08:00
|
|
|
|
whatever="""
|
|
|
|
|
'>>> x = None'
|
|
|
|
|
def my_func():
|
|
|
|
|
">>> magic = 42 "
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
for p in (path, pytester.path):
|
|
|
|
|
items, reprec = pytester.inline_genitems(p, "--doctest-modules")
|
2013-05-17 23:18:22 +08:00
|
|
|
|
assert len(items) == 2
|
|
|
|
|
assert isinstance(items[0], DoctestItem)
|
|
|
|
|
assert isinstance(items[1], DoctestItem)
|
|
|
|
|
assert isinstance(items[0].parent, DoctestModule)
|
|
|
|
|
assert items[0].parent is items[1].parent
|
|
|
|
|
|
2020-11-09 22:08:27 +08:00
|
|
|
|
@pytest.mark.parametrize("filename", ["__init__", "whatever"])
|
|
|
|
|
def test_collect_module_two_doctest_no_modulelevel(
|
2020-12-30 17:56:09 +08:00
|
|
|
|
self,
|
|
|
|
|
pytester: Pytester,
|
|
|
|
|
filename: str,
|
2020-11-09 22:08:27 +08:00
|
|
|
|
) -> None:
|
2020-10-12 23:13:06 +08:00
|
|
|
|
path = pytester.makepyfile(
|
2020-11-09 22:08:27 +08:00
|
|
|
|
**{
|
|
|
|
|
filename: """
|
2013-05-17 23:18:22 +08:00
|
|
|
|
'# Empty'
|
|
|
|
|
def my_func():
|
|
|
|
|
">>> magic = 42 "
|
2021-12-27 20:23:15 +08:00
|
|
|
|
def useless():
|
2013-05-17 23:18:22 +08:00
|
|
|
|
'''
|
|
|
|
|
# This is a function
|
|
|
|
|
# >>> # it doesn't have any doctest
|
|
|
|
|
'''
|
|
|
|
|
def another():
|
|
|
|
|
'''
|
|
|
|
|
# This is another function
|
|
|
|
|
>>> import os # this one does have a doctest
|
|
|
|
|
'''
|
2020-11-09 22:08:27 +08:00
|
|
|
|
""",
|
|
|
|
|
},
|
2013-05-17 23:18:22 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
for p in (path, pytester.path):
|
|
|
|
|
items, reprec = pytester.inline_genitems(p, "--doctest-modules")
|
2013-05-17 23:18:22 +08:00
|
|
|
|
assert len(items) == 2
|
|
|
|
|
assert isinstance(items[0], DoctestItem)
|
|
|
|
|
assert isinstance(items[1], DoctestItem)
|
|
|
|
|
assert isinstance(items[0].parent, DoctestModule)
|
|
|
|
|
assert items[0].parent is items[1].parent
|
2009-09-06 22:59:39 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_simple_doctestfile(self, pytester: Pytester):
|
|
|
|
|
p = pytester.maketxtfile(
|
2009-09-06 22:59:39 +08:00
|
|
|
|
test_doc="""
|
|
|
|
|
>>> x = 1
|
|
|
|
|
>>> x == 1
|
|
|
|
|
False
|
|
|
|
|
"""
|
2010-01-03 06:30:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p)
|
2010-01-03 06:30:46 +08:00
|
|
|
|
reprec.assertoutcome(failed=1)
|
|
|
|
|
|
2022-06-29 21:38:59 +08:00
|
|
|
|
def test_importmode(self, pytester: Pytester):
|
2023-05-27 19:42:19 +08:00
|
|
|
|
pytester.makepyfile(
|
2022-06-29 21:38:59 +08:00
|
|
|
|
**{
|
2024-02-28 03:33:42 +08:00
|
|
|
|
"src/namespacepkg/innerpkg/__init__.py": "",
|
|
|
|
|
"src/namespacepkg/innerpkg/a.py": """
|
2022-06-29 21:38:59 +08:00
|
|
|
|
def some_func():
|
|
|
|
|
return 42
|
|
|
|
|
""",
|
2024-02-28 03:33:42 +08:00
|
|
|
|
"src/namespacepkg/innerpkg/b.py": """
|
2022-06-29 21:38:59 +08:00
|
|
|
|
from namespacepkg.innerpkg.a import some_func
|
|
|
|
|
def my_func():
|
|
|
|
|
'''
|
|
|
|
|
>>> my_func()
|
|
|
|
|
42
|
|
|
|
|
'''
|
|
|
|
|
return some_func()
|
|
|
|
|
""",
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-02-28 03:33:42 +08:00
|
|
|
|
# For 'namespacepkg' to be considered a namespace package, its containing directory
|
|
|
|
|
# needs to be reachable from sys.path:
|
|
|
|
|
# https://packaging.python.org/en/latest/guides/packaging-namespace-packages
|
|
|
|
|
pytester.syspathinsert(pytester.path / "src")
|
2023-05-27 19:42:19 +08:00
|
|
|
|
reprec = pytester.inline_run("--doctest-modules", "--import-mode=importlib")
|
2022-06-29 21:38:59 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_new_pattern(self, pytester: Pytester):
|
|
|
|
|
p = pytester.maketxtfile(
|
2015-12-31 04:19:08 +08:00
|
|
|
|
xdoc="""
|
2010-01-03 06:30:46 +08:00
|
|
|
|
>>> x = 1
|
|
|
|
|
>>> x == 1
|
|
|
|
|
False
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-glob=x*.txt")
|
2009-09-06 22:59:39 +08:00
|
|
|
|
reprec.assertoutcome(failed=1)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_multiple_patterns(self, pytester: Pytester):
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Test support for multiple --doctest-glob arguments (#1255)."""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-12-31 04:19:08 +08:00
|
|
|
|
xdoc="""
|
|
|
|
|
>>> 1
|
|
|
|
|
1
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makefile(
|
2015-12-31 04:19:08 +08:00
|
|
|
|
".foo",
|
|
|
|
|
test="""
|
|
|
|
|
>>> 1
|
|
|
|
|
1
|
|
|
|
|
""",
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-12-31 04:19:08 +08:00
|
|
|
|
test_normal="""
|
|
|
|
|
>>> 1
|
|
|
|
|
1
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
expected = {"xdoc.txt", "test.foo", "test_normal.txt"}
|
2020-10-12 23:13:06 +08:00
|
|
|
|
assert {x.name for x in pytester.path.iterdir()} == expected
|
2015-12-31 04:19:08 +08:00
|
|
|
|
args = ["--doctest-glob=xdoc*.txt", "--doctest-glob=*.foo"]
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(*args)
|
2015-12-31 04:19:08 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*test.foo *", "*xdoc.txt *", "*2 passed*"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest()
|
2015-12-31 04:19:08 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*test_normal.txt *", "*1 passed*"])
|
|
|
|
|
|
2016-11-30 18:43:33 +08:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
" test_string, encoding",
|
2017-07-17 07:25:08 +08:00
|
|
|
|
[("foo", "ascii"), ("öäü", "latin1"), ("öäü", "utf-8")],
|
2016-11-30 18:43:33 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_encoding(self, pytester, test_string, encoding):
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Test support for doctest_encoding ini option."""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeini(
|
2016-11-30 18:43:33 +08:00
|
|
|
|
f"""
|
|
|
|
|
[pytest]
|
|
|
|
|
doctest_encoding={encoding}
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-07-18 17:35:13 +08:00
|
|
|
|
doctest = f"""
|
2016-11-30 18:43:33 +08:00
|
|
|
|
>>> "{test_string}"
|
2024-02-02 21:49:15 +08:00
|
|
|
|
{test_string!r}
|
2024-02-01 04:12:33 +08:00
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
fn = pytester.path / "test_encoding.txt"
|
|
|
|
|
fn.write_text(doctest, encoding=encoding)
|
2016-11-29 19:18:41 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest()
|
2016-11-29 19:18:41 +08:00
|
|
|
|
|
|
|
|
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_unexpected_exception(self, pytester: Pytester):
|
|
|
|
|
pytester.maketxtfile(
|
2013-10-12 21:39:22 +08:00
|
|
|
|
"""
|
2009-09-06 22:59:39 +08:00
|
|
|
|
>>> i = 0
|
2010-12-07 02:00:30 +08:00
|
|
|
|
>>> 0 / i
|
2009-09-06 22:59:39 +08:00
|
|
|
|
2
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2010-12-07 02:00:30 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2020-02-24 22:18:08 +08:00
|
|
|
|
"test_doctest_unexpected_exception.txt F *",
|
|
|
|
|
"",
|
|
|
|
|
"*= FAILURES =*",
|
|
|
|
|
"*_ [[]doctest[]] test_doctest_unexpected_exception.txt _*",
|
|
|
|
|
"001 >>> i = 0",
|
|
|
|
|
"002 >>> 0 / i",
|
|
|
|
|
"UNEXPECTED EXCEPTION: ZeroDivisionError*",
|
|
|
|
|
"Traceback (most recent call last):",
|
|
|
|
|
' File "*/doctest.py", line *, in __run',
|
|
|
|
|
" *",
|
2022-07-13 23:06:33 +08:00
|
|
|
|
*(
|
|
|
|
|
(" *^^^^*",)
|
|
|
|
|
if (3, 11, 0, "beta", 4) > sys.version_info >= (3, 11)
|
|
|
|
|
else ()
|
|
|
|
|
),
|
2020-02-24 22:18:08 +08:00
|
|
|
|
' File "<doctest test_doctest_unexpected_exception.txt[1]>", line 1, in <module>',
|
|
|
|
|
"ZeroDivisionError: division by zero",
|
|
|
|
|
"*/test_doctest_unexpected_exception.txt:2: UnexpectedException",
|
|
|
|
|
],
|
|
|
|
|
consecutive=True,
|
2010-12-07 02:00:30 +08:00
|
|
|
|
)
|
2009-09-06 22:59:39 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_outcomes(self, pytester: Pytester):
|
|
|
|
|
pytester.maketxtfile(
|
2020-02-19 20:16:37 +08:00
|
|
|
|
test_skip="""
|
2019-03-15 10:06:57 +08:00
|
|
|
|
>>> 1
|
|
|
|
|
1
|
|
|
|
|
>>> import pytest
|
|
|
|
|
>>> pytest.skip("")
|
2020-02-19 20:16:37 +08:00
|
|
|
|
>>> 2
|
|
|
|
|
3
|
|
|
|
|
""",
|
|
|
|
|
test_xfail="""
|
|
|
|
|
>>> import pytest
|
|
|
|
|
>>> pytest.xfail("xfail_reason")
|
|
|
|
|
>>> foo
|
|
|
|
|
bar
|
|
|
|
|
""",
|
|
|
|
|
test_importorskip="""
|
|
|
|
|
>>> import pytest
|
|
|
|
|
>>> pytest.importorskip("doesnotexist")
|
|
|
|
|
>>> foo
|
|
|
|
|
bar
|
|
|
|
|
""",
|
2019-03-15 10:06:57 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2020-02-19 20:16:37 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"collected 3 items",
|
|
|
|
|
"",
|
|
|
|
|
"test_importorskip.txt s *",
|
|
|
|
|
"test_skip.txt s *",
|
|
|
|
|
"test_xfail.txt x *",
|
|
|
|
|
"",
|
|
|
|
|
"*= 2 skipped, 1 xfailed in *",
|
|
|
|
|
]
|
|
|
|
|
)
|
2019-03-15 10:06:57 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_docstring_partial_context_around_error(self, pytester: Pytester):
|
2016-01-09 08:19:37 +08:00
|
|
|
|
"""Test that we show some context before the actual line of a failing
|
|
|
|
|
doctest.
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2016-01-09 08:19:37 +08:00
|
|
|
|
'''
|
|
|
|
|
def foo():
|
|
|
|
|
"""
|
|
|
|
|
text-line-1
|
|
|
|
|
text-line-2
|
|
|
|
|
text-line-3
|
|
|
|
|
text-line-4
|
|
|
|
|
text-line-5
|
|
|
|
|
text-line-6
|
|
|
|
|
text-line-7
|
|
|
|
|
text-line-8
|
|
|
|
|
text-line-9
|
|
|
|
|
text-line-10
|
|
|
|
|
text-line-11
|
|
|
|
|
>>> 1 + 1
|
|
|
|
|
3
|
|
|
|
|
|
|
|
|
|
text-line-after
|
|
|
|
|
"""
|
|
|
|
|
'''
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2016-01-09 08:19:37 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2017-11-04 02:37:18 +08:00
|
|
|
|
"*docstring_partial_context_around_error*",
|
2016-01-09 08:19:37 +08:00
|
|
|
|
"005*text-line-3",
|
|
|
|
|
"006*text-line-4",
|
|
|
|
|
"013*text-line-11",
|
|
|
|
|
"014*>>> 1 + 1",
|
|
|
|
|
"Expected:",
|
|
|
|
|
" 3",
|
|
|
|
|
"Got:",
|
|
|
|
|
" 2",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
# lines below should be trimmed out
|
2019-10-06 01:18:51 +08:00
|
|
|
|
result.stdout.no_fnmatch_line("*text-line-2*")
|
|
|
|
|
result.stdout.no_fnmatch_line("*text-line-after*")
|
2016-01-09 08:19:37 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_docstring_full_context_around_error(self, pytester: Pytester):
|
2017-11-04 02:37:18 +08:00
|
|
|
|
"""Test that we show the whole context before the actual line of a failing
|
|
|
|
|
doctest, provided that the context is up to 10 lines long.
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2017-11-04 02:37:18 +08:00
|
|
|
|
'''
|
|
|
|
|
def foo():
|
|
|
|
|
"""
|
|
|
|
|
text-line-1
|
|
|
|
|
text-line-2
|
|
|
|
|
|
|
|
|
|
>>> 1 + 1
|
|
|
|
|
3
|
|
|
|
|
"""
|
|
|
|
|
'''
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2017-11-04 02:37:18 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*docstring_full_context_around_error*",
|
|
|
|
|
"003*text-line-1",
|
|
|
|
|
"004*text-line-2",
|
|
|
|
|
"006*>>> 1 + 1",
|
|
|
|
|
"Expected:",
|
|
|
|
|
" 3",
|
|
|
|
|
"Got:",
|
|
|
|
|
" 2",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_linedata_missing(self, pytester: Pytester):
|
|
|
|
|
pytester.path.joinpath("hello.py").write_text(
|
2018-08-24 00:06:17 +08:00
|
|
|
|
textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
class Fun(object):
|
|
|
|
|
@property
|
|
|
|
|
def test(self):
|
|
|
|
|
'''
|
|
|
|
|
>>> a = 1
|
|
|
|
|
>>> 1/0
|
|
|
|
|
'''
|
2015-11-27 22:43:01 +08:00
|
|
|
|
"""
|
2023-06-20 19:55:39 +08:00
|
|
|
|
),
|
|
|
|
|
encoding="utf-8",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2019-10-28 05:28:21 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
["*hello*", "006*>>> 1/0*", "*UNEXPECTED*ZeroDivision*", "*1 failed*"]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_linedata_on_property(self, pytester: Pytester):
|
|
|
|
|
pytester.makepyfile(
|
2019-10-28 05:28:21 +08:00
|
|
|
|
"""
|
|
|
|
|
class Sample(object):
|
|
|
|
|
@property
|
|
|
|
|
def some_property(self):
|
|
|
|
|
'''
|
|
|
|
|
>>> Sample().some_property
|
|
|
|
|
'another thing'
|
|
|
|
|
'''
|
|
|
|
|
return 'something'
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2013-03-25 03:05:29 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2019-10-28 05:28:21 +08:00
|
|
|
|
"*= FAILURES =*",
|
|
|
|
|
"*_ [[]doctest[]] test_doctest_linedata_on_property.Sample.some_property _*",
|
|
|
|
|
"004 ",
|
|
|
|
|
"005 >>> Sample().some_property",
|
|
|
|
|
"Expected:",
|
|
|
|
|
" 'another thing'",
|
|
|
|
|
"Got:",
|
|
|
|
|
" 'something'",
|
|
|
|
|
"",
|
|
|
|
|
"*/test_doctest_linedata_on_property.py:5: DocTestFailure",
|
|
|
|
|
"*= 1 failed in *",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_no_linedata_on_overriden_property(self, pytester: Pytester):
|
|
|
|
|
pytester.makepyfile(
|
2019-10-28 05:28:21 +08:00
|
|
|
|
"""
|
|
|
|
|
class Sample(object):
|
|
|
|
|
@property
|
|
|
|
|
def some_property(self):
|
|
|
|
|
'''
|
|
|
|
|
>>> Sample().some_property
|
|
|
|
|
'another thing'
|
|
|
|
|
'''
|
|
|
|
|
return 'something'
|
|
|
|
|
some_property = property(some_property.__get__, None, None, some_property.__doc__)
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2019-10-28 05:28:21 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*= FAILURES =*",
|
|
|
|
|
"*_ [[]doctest[]] test_doctest_no_linedata_on_overriden_property.Sample.some_property _*",
|
|
|
|
|
"EXAMPLE LOCATION UNKNOWN, not showing all tests of that example",
|
|
|
|
|
"[?][?][?] >>> Sample().some_property",
|
|
|
|
|
"Expected:",
|
|
|
|
|
" 'another thing'",
|
|
|
|
|
"Got:",
|
|
|
|
|
" 'something'",
|
|
|
|
|
"",
|
|
|
|
|
"*/test_doctest_no_linedata_on_overriden_property.py:None: DocTestFailure",
|
|
|
|
|
"*= 1 failed in *",
|
2013-03-25 03:05:29 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_unex_importerror_only_txt(self, pytester: Pytester):
|
|
|
|
|
pytester.maketxtfile(
|
2016-06-20 21:05:50 +08:00
|
|
|
|
"""
|
|
|
|
|
>>> import asdalsdkjaslkdjasd
|
|
|
|
|
>>>
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest()
|
2016-06-20 21:05:50 +08:00
|
|
|
|
# doctest is never executed because of error during hello.py collection
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*>>> import asdals*",
|
2020-10-03 03:06:21 +08:00
|
|
|
|
"*UNEXPECTED*ModuleNotFoundError*",
|
|
|
|
|
"ModuleNotFoundError: No module named *asdal*",
|
2016-06-20 21:05:50 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
2013-03-25 03:05:29 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_unex_importerror_with_module(self, pytester: Pytester):
|
|
|
|
|
pytester.path.joinpath("hello.py").write_text(
|
2018-08-24 00:06:17 +08:00
|
|
|
|
textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
import asdalsdkjaslkdjasd
|
2015-11-27 22:43:01 +08:00
|
|
|
|
"""
|
2023-06-20 19:55:39 +08:00
|
|
|
|
),
|
|
|
|
|
encoding="utf-8",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2013-10-12 21:39:22 +08:00
|
|
|
|
"""
|
2011-05-28 20:38:15 +08:00
|
|
|
|
>>> import hello
|
|
|
|
|
>>>
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2016-06-20 21:05:50 +08:00
|
|
|
|
# doctest is never executed because of error during hello.py collection
|
2011-05-28 20:38:15 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2016-06-20 21:05:50 +08:00
|
|
|
|
"*ERROR collecting hello.py*",
|
2020-10-03 03:06:21 +08:00
|
|
|
|
"*ModuleNotFoundError: No module named *asdals*",
|
2019-10-27 23:02:37 +08:00
|
|
|
|
"*Interrupted: 1 error during collection*",
|
2011-05-28 20:38:15 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctestmodule(self, pytester: Pytester):
|
|
|
|
|
p = pytester.makepyfile(
|
2009-09-06 22:59:39 +08:00
|
|
|
|
"""
|
|
|
|
|
'''
|
|
|
|
|
>>> x = 1
|
|
|
|
|
>>> x == 1
|
|
|
|
|
False
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules")
|
2010-07-27 03:15:15 +08:00
|
|
|
|
reprec.assertoutcome(failed=1)
|
2009-09-06 22:59:39 +08:00
|
|
|
|
|
2023-08-16 14:54:38 +08:00
|
|
|
|
def test_doctest_cached_property(self, pytester: Pytester):
|
|
|
|
|
p = pytester.makepyfile(
|
|
|
|
|
"""
|
|
|
|
|
import functools
|
|
|
|
|
|
|
|
|
|
class Foo:
|
|
|
|
|
@functools.cached_property
|
|
|
|
|
def foo(self):
|
|
|
|
|
'''
|
|
|
|
|
>>> assert False, "Tacos!"
|
|
|
|
|
'''
|
|
|
|
|
...
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
result = pytester.runpytest(p, "--doctest-modules")
|
|
|
|
|
result.assert_outcomes(failed=1)
|
|
|
|
|
assert "Tacos!" in result.stdout.str()
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctestmodule_external_and_issue116(self, pytester: Pytester):
|
|
|
|
|
p = pytester.mkpydir("hello")
|
|
|
|
|
p.joinpath("__init__.py").write_text(
|
2018-08-24 00:06:17 +08:00
|
|
|
|
textwrap.dedent(
|
|
|
|
|
"""\
|
|
|
|
|
def somefunc():
|
|
|
|
|
'''
|
|
|
|
|
>>> i = 0
|
|
|
|
|
>>> i + 1
|
|
|
|
|
2
|
|
|
|
|
'''
|
2015-11-27 22:43:01 +08:00
|
|
|
|
"""
|
2023-06-20 19:55:39 +08:00
|
|
|
|
),
|
|
|
|
|
encoding="utf-8",
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(p, "--doctest-modules")
|
2009-09-06 22:59:39 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
2018-08-24 00:06:17 +08:00
|
|
|
|
"003 *>>> i = 0",
|
|
|
|
|
"004 *>>> i + 1",
|
2009-09-06 22:59:39 +08:00
|
|
|
|
"*Expected:",
|
|
|
|
|
"* 2",
|
|
|
|
|
"*Got:",
|
|
|
|
|
"* 1",
|
2018-08-24 00:06:17 +08:00
|
|
|
|
"*:4: DocTestFailure",
|
2009-09-06 22:59:39 +08:00
|
|
|
|
]
|
|
|
|
|
)
|
2010-07-27 03:15:15 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_txtfile_failing(self, pytester: Pytester):
|
|
|
|
|
p = pytester.maketxtfile(
|
2009-09-06 22:59:39 +08:00
|
|
|
|
"""
|
|
|
|
|
>>> i = 0
|
|
|
|
|
>>> i + 1
|
|
|
|
|
2
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(p, "-s")
|
2009-09-06 22:59:39 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"001 >>> i = 0",
|
|
|
|
|
"002 >>> i + 1",
|
|
|
|
|
"Expected:",
|
|
|
|
|
" 2",
|
|
|
|
|
"Got:",
|
|
|
|
|
" 1",
|
|
|
|
|
"*test_txtfile_failing.txt:2: DocTestFailure",
|
|
|
|
|
]
|
|
|
|
|
)
|
2013-03-21 00:14:28 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_txtfile_with_fixtures(self, pytester: Pytester):
|
|
|
|
|
p = pytester.maketxtfile(
|
2013-03-21 00:14:28 +08:00
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
>>> p = getfixture('tmp_path')
|
|
|
|
|
>>> p.is_dir()
|
|
|
|
|
True
|
2013-03-21 00:14:28 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p)
|
2013-03-21 00:14:28 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
2013-03-21 00:32:48 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_txtfile_with_usefixtures_in_ini(self, pytester: Pytester):
|
|
|
|
|
pytester.makeini(
|
2013-11-22 22:35:20 +08:00
|
|
|
|
"""
|
|
|
|
|
[pytest]
|
|
|
|
|
usefixtures = myfixture
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeconftest(
|
2013-11-22 22:35:20 +08:00
|
|
|
|
"""
|
|
|
|
|
import pytest
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def myfixture(monkeypatch):
|
|
|
|
|
monkeypatch.setenv("HELLO", "WORLD")
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.maketxtfile(
|
2013-11-22 22:35:20 +08:00
|
|
|
|
"""
|
|
|
|
|
>>> import os
|
|
|
|
|
>>> os.environ["HELLO"]
|
|
|
|
|
'WORLD'
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p)
|
2013-11-22 22:35:20 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
2013-09-06 16:07:06 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctestmodule_with_fixtures(self, pytester: Pytester):
|
|
|
|
|
p = pytester.makepyfile(
|
2013-03-21 00:32:48 +08:00
|
|
|
|
"""
|
|
|
|
|
'''
|
2020-10-12 23:13:06 +08:00
|
|
|
|
>>> p = getfixture('tmp_path')
|
|
|
|
|
>>> p.is_dir()
|
|
|
|
|
True
|
2013-03-21 00:32:48 +08:00
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules")
|
2013-03-21 00:32:48 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
2013-05-17 23:18:22 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctestmodule_three_tests(self, pytester: Pytester):
|
|
|
|
|
p = pytester.makepyfile(
|
2013-05-17 23:18:22 +08:00
|
|
|
|
"""
|
|
|
|
|
'''
|
2020-10-12 23:13:06 +08:00
|
|
|
|
>>> p = getfixture('tmp_path')
|
|
|
|
|
>>> p.is_dir()
|
|
|
|
|
True
|
2013-05-17 23:18:22 +08:00
|
|
|
|
'''
|
|
|
|
|
def my_func():
|
|
|
|
|
'''
|
|
|
|
|
>>> magic = 42
|
|
|
|
|
>>> magic - 42
|
|
|
|
|
0
|
|
|
|
|
'''
|
2021-12-27 20:23:15 +08:00
|
|
|
|
def useless():
|
2013-05-17 23:18:22 +08:00
|
|
|
|
pass
|
|
|
|
|
def another():
|
|
|
|
|
'''
|
|
|
|
|
>>> import os
|
|
|
|
|
>>> os is os
|
|
|
|
|
True
|
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules")
|
2013-05-17 23:18:22 +08:00
|
|
|
|
reprec.assertoutcome(passed=3)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctestmodule_two_tests_one_fail(self, pytester: Pytester):
|
|
|
|
|
p = pytester.makepyfile(
|
2013-05-17 23:18:22 +08:00
|
|
|
|
"""
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class MyClass(object):
|
2013-05-17 23:18:22 +08:00
|
|
|
|
def bad_meth(self):
|
|
|
|
|
'''
|
|
|
|
|
>>> magic = 42
|
|
|
|
|
>>> magic
|
|
|
|
|
0
|
|
|
|
|
'''
|
|
|
|
|
def nice_meth(self):
|
|
|
|
|
'''
|
|
|
|
|
>>> magic = 42
|
|
|
|
|
>>> magic - 42
|
|
|
|
|
0
|
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules")
|
2013-05-17 23:18:22 +08:00
|
|
|
|
reprec.assertoutcome(failed=1, passed=1)
|
2014-10-08 20:31:17 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_ignored_whitespace(self, pytester: Pytester):
|
|
|
|
|
pytester.makeini(
|
2014-10-08 20:31:17 +08:00
|
|
|
|
"""
|
|
|
|
|
[pytest]
|
|
|
|
|
doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2014-10-08 20:31:17 +08:00
|
|
|
|
"""
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class MyClass(object):
|
2014-10-08 20:31:17 +08:00
|
|
|
|
'''
|
|
|
|
|
>>> a = "foo "
|
|
|
|
|
>>> print(a)
|
|
|
|
|
foo
|
|
|
|
|
'''
|
|
|
|
|
pass
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules")
|
2014-10-08 20:31:17 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_non_ignored_whitespace(self, pytester: Pytester):
|
|
|
|
|
pytester.makeini(
|
2014-10-08 20:31:17 +08:00
|
|
|
|
"""
|
|
|
|
|
[pytest]
|
|
|
|
|
doctest_optionflags = ELLIPSIS
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2014-10-08 20:31:17 +08:00
|
|
|
|
"""
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class MyClass(object):
|
2014-10-08 20:31:17 +08:00
|
|
|
|
'''
|
|
|
|
|
>>> a = "foo "
|
|
|
|
|
>>> print(a)
|
|
|
|
|
foo
|
|
|
|
|
'''
|
|
|
|
|
pass
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules")
|
2014-10-08 20:31:17 +08:00
|
|
|
|
reprec.assertoutcome(failed=1, passed=0)
|
2014-10-08 21:54:08 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_ignored_whitespace_glob(self, pytester: Pytester):
|
|
|
|
|
pytester.makeini(
|
2014-10-08 21:54:08 +08:00
|
|
|
|
"""
|
|
|
|
|
[pytest]
|
|
|
|
|
doctest_optionflags = ELLIPSIS NORMALIZE_WHITESPACE
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.maketxtfile(
|
2014-10-08 21:54:08 +08:00
|
|
|
|
xdoc="""
|
|
|
|
|
>>> a = "foo "
|
|
|
|
|
>>> print(a)
|
|
|
|
|
foo
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-glob=x*.txt")
|
2014-10-08 21:54:08 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_non_ignored_whitespace_glob(self, pytester: Pytester):
|
|
|
|
|
pytester.makeini(
|
2014-10-08 21:54:08 +08:00
|
|
|
|
"""
|
|
|
|
|
[pytest]
|
|
|
|
|
doctest_optionflags = ELLIPSIS
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.maketxtfile(
|
2014-10-08 21:54:08 +08:00
|
|
|
|
xdoc="""
|
|
|
|
|
>>> a = "foo "
|
|
|
|
|
>>> print(a)
|
|
|
|
|
foo
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-glob=x*.txt")
|
2014-10-08 21:54:08 +08:00
|
|
|
|
reprec.assertoutcome(failed=1, passed=0)
|
2015-02-27 01:39:36 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_contains_unicode(self, pytester: Pytester):
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Fix internal error with docstrings containing non-ascii characters."""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2019-06-03 06:40:34 +08:00
|
|
|
|
'''\
|
2016-01-09 08:19:37 +08:00
|
|
|
|
def foo():
|
|
|
|
|
"""
|
|
|
|
|
>>> name = 'с' # not letter 'c' but instead Cyrillic 's'.
|
|
|
|
|
'anything'
|
|
|
|
|
"""
|
2024-02-02 22:07:29 +08:00
|
|
|
|
''' # noqa: RUF001
|
2016-01-09 08:19:37 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2016-01-09 08:19:37 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["Got nothing", "* 1 failed in*"])
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_ignore_import_errors_on_doctest(self, pytester: Pytester):
|
|
|
|
|
p = pytester.makepyfile(
|
2015-02-27 01:39:36 +08:00
|
|
|
|
"""
|
|
|
|
|
import asdf
|
|
|
|
|
|
|
|
|
|
def add_one(x):
|
|
|
|
|
'''
|
|
|
|
|
>>> add_one(1)
|
|
|
|
|
2
|
|
|
|
|
'''
|
|
|
|
|
return x + 1
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(
|
2015-02-27 01:39:36 +08:00
|
|
|
|
p, "--doctest-modules", "--doctest-ignore-import-errors"
|
|
|
|
|
)
|
|
|
|
|
reprec.assertoutcome(skipped=1, failed=1, passed=0)
|
2015-06-17 04:35:31 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_junit_report_for_doctest(self, pytester: Pytester):
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""#713: Fix --junit-xml option when used with --doctest-modules."""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2015-06-17 04:35:31 +08:00
|
|
|
|
"""
|
|
|
|
|
def foo():
|
|
|
|
|
'''
|
|
|
|
|
>>> 1 + 1
|
|
|
|
|
3
|
|
|
|
|
'''
|
|
|
|
|
pass
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules", "--junit-xml=junit.xml")
|
2015-06-17 04:35:31 +08:00
|
|
|
|
reprec.assertoutcome(failed=1)
|
2015-07-13 10:43:33 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_unicode_doctest(self, pytester: Pytester):
|
2017-05-26 04:08:32 +08:00
|
|
|
|
"""
|
|
|
|
|
Test case for issue 2434: DecodeError on Python 2 when doctest contains non-ascii
|
|
|
|
|
characters.
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.maketxtfile(
|
2017-05-26 04:08:32 +08:00
|
|
|
|
test_unicode_doctest="""
|
|
|
|
|
.. doctest::
|
|
|
|
|
|
2021-03-09 00:55:53 +08:00
|
|
|
|
>>> print("Hi\\n\\nByé")
|
2017-05-26 04:08:32 +08:00
|
|
|
|
Hi
|
|
|
|
|
...
|
|
|
|
|
Byé
|
2021-03-09 00:55:53 +08:00
|
|
|
|
>>> 1 / 0 # Byé
|
2017-05-26 04:08:32 +08:00
|
|
|
|
1
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(p)
|
2017-05-26 04:08:32 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
["*UNEXPECTED EXCEPTION: ZeroDivisionError*", "*1 failed*"]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_unicode_doctest_module(self, pytester: Pytester):
|
2017-06-14 06:34:05 +08:00
|
|
|
|
"""
|
|
|
|
|
Test case for issue 2434: DecodeError on Python 2 when doctest docstring
|
|
|
|
|
contains non-ascii characters.
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2017-06-14 06:34:05 +08:00
|
|
|
|
test_unicode_doctest_module="""
|
|
|
|
|
def fix_bad_unicode(text):
|
|
|
|
|
'''
|
|
|
|
|
>>> print(fix_bad_unicode('único'))
|
|
|
|
|
único
|
|
|
|
|
'''
|
|
|
|
|
return "único"
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(p, "--doctest-modules")
|
2017-06-14 06:34:05 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["* 1 passed *"])
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_print_unicode_value(self, pytester: Pytester):
|
2018-06-15 04:00:38 +08:00
|
|
|
|
"""
|
|
|
|
|
Test case for issue 3583: Printing Unicode in doctest under Python 2.7
|
|
|
|
|
doesn't work
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.maketxtfile(
|
2018-06-15 04:00:38 +08:00
|
|
|
|
test_print_unicode_value=r"""
|
|
|
|
|
Here is a doctest::
|
|
|
|
|
|
2019-06-05 08:48:06 +08:00
|
|
|
|
>>> print('\xE5\xE9\xEE\xF8\xFC')
|
2018-06-15 04:00:38 +08:00
|
|
|
|
åéîøü
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(p)
|
2018-06-15 04:00:38 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["* 1 passed *"])
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_reportinfo(self, pytester: Pytester):
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Make sure that DoctestItem.reportinfo() returns lineno."""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2017-07-25 00:07:45 +08:00
|
|
|
|
test_reportinfo="""
|
|
|
|
|
def foo(x):
|
|
|
|
|
'''
|
|
|
|
|
>>> foo('a')
|
|
|
|
|
'b'
|
|
|
|
|
'''
|
|
|
|
|
return 'c'
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
items, reprec = pytester.inline_genitems(p, "--doctest-modules")
|
2017-07-25 00:07:45 +08:00
|
|
|
|
reportinfo = items[0].reportinfo()
|
|
|
|
|
assert reportinfo[1] == 1
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_valid_setup_py(self, pytester: Pytester):
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2017-10-14 00:47:02 +08:00
|
|
|
|
Test to make sure that pytest ignores valid setup.py files when ran
|
|
|
|
|
with --doctest-modules
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2017-10-14 00:47:02 +08:00
|
|
|
|
setup="""
|
2022-02-08 04:10:03 +08:00
|
|
|
|
if __name__ == '__main__':
|
2022-03-01 08:35:36 +08:00
|
|
|
|
from setuptools import setup, find_packages
|
2022-02-08 04:10:03 +08:00
|
|
|
|
setup(name='sample',
|
|
|
|
|
version='0.0',
|
|
|
|
|
description='description',
|
|
|
|
|
packages=find_packages()
|
|
|
|
|
)
|
2017-10-14 00:47:02 +08:00
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(p, "--doctest-modules")
|
2017-10-14 00:47:02 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*collected 0 items*"])
|
|
|
|
|
|
2021-07-28 05:50:26 +08:00
|
|
|
|
def test_main_py_does_not_cause_import_errors(self, pytester: Pytester):
|
|
|
|
|
p = pytester.copy_example("doctest/main_py")
|
|
|
|
|
result = pytester.runpytest(p, "--doctest-modules")
|
|
|
|
|
result.stdout.fnmatch_lines(["*collected 2 items*", "*1 failed, 1 passed*"])
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_invalid_setup_py(self, pytester: Pytester):
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2017-10-14 00:47:02 +08:00
|
|
|
|
Test to make sure that pytest reads setup.py files that are not used
|
|
|
|
|
for python packages when ran with --doctest-modules
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2017-10-14 00:47:02 +08:00
|
|
|
|
setup="""
|
|
|
|
|
def test_foo():
|
|
|
|
|
return 'bar'
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(p, "--doctest-modules")
|
2017-10-14 00:47:02 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*collected 1 item*"])
|
|
|
|
|
|
2024-02-20 02:54:49 +08:00
|
|
|
|
def test_setup_module(self, pytester: Pytester) -> None:
|
|
|
|
|
"""Regression test for #12011 - setup_module not executed when running
|
|
|
|
|
with `--doctest-modules`."""
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
|
"""
|
|
|
|
|
CONSTANT = 0
|
|
|
|
|
|
|
|
|
|
def setup_module():
|
|
|
|
|
global CONSTANT
|
|
|
|
|
CONSTANT = 1
|
|
|
|
|
|
|
|
|
|
def test():
|
|
|
|
|
assert CONSTANT == 1
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
|
|
|
|
assert result.ret == 0
|
|
|
|
|
result.assert_outcomes(passed=1)
|
|
|
|
|
|
2015-12-30 06:55:19 +08:00
|
|
|
|
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class TestLiterals:
|
2015-08-13 08:41:31 +08:00
|
|
|
|
@pytest.mark.parametrize("config_mode", ["ini", "comment"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_allow_unicode(self, pytester, config_mode):
|
2015-08-13 08:41:31 +08:00
|
|
|
|
"""Test that doctests which output unicode work in all python versions
|
|
|
|
|
tested by pytest when the ALLOW_UNICODE option is used (either in
|
|
|
|
|
the ini file or by an inline comment).
|
|
|
|
|
"""
|
|
|
|
|
if config_mode == "ini":
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeini(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2015-08-13 08:41:31 +08:00
|
|
|
|
[pytest]
|
|
|
|
|
doctest_optionflags = ALLOW_UNICODE
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2015-08-13 08:41:31 +08:00
|
|
|
|
)
|
|
|
|
|
comment = ""
|
|
|
|
|
else:
|
|
|
|
|
comment = "#doctest: +ALLOW_UNICODE"
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-08-13 08:41:31 +08:00
|
|
|
|
test_doc=f"""
|
|
|
|
|
>>> b'12'.decode('ascii') {comment}
|
|
|
|
|
'12'
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2015-08-13 08:41:31 +08:00
|
|
|
|
foo=f"""
|
|
|
|
|
def foo():
|
|
|
|
|
'''
|
|
|
|
|
>>> b'12'.decode('ascii') {comment}
|
|
|
|
|
'12'
|
|
|
|
|
'''
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run("--doctest-modules")
|
2015-08-13 08:41:31 +08:00
|
|
|
|
reprec.assertoutcome(passed=2)
|
|
|
|
|
|
2015-12-30 06:55:19 +08:00
|
|
|
|
@pytest.mark.parametrize("config_mode", ["ini", "comment"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_allow_bytes(self, pytester, config_mode):
|
2015-12-30 06:55:19 +08:00
|
|
|
|
"""Test that doctests which output bytes work in all python versions
|
|
|
|
|
tested by pytest when the ALLOW_BYTES option is used (either in
|
|
|
|
|
the ini file or by an inline comment)(#1287).
|
|
|
|
|
"""
|
|
|
|
|
if config_mode == "ini":
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeini(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2015-12-30 06:55:19 +08:00
|
|
|
|
[pytest]
|
|
|
|
|
doctest_optionflags = ALLOW_BYTES
|
2018-05-23 22:48:46 +08:00
|
|
|
|
"""
|
2015-12-30 06:55:19 +08:00
|
|
|
|
)
|
|
|
|
|
comment = ""
|
|
|
|
|
else:
|
|
|
|
|
comment = "#doctest: +ALLOW_BYTES"
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-12-30 06:55:19 +08:00
|
|
|
|
test_doc=f"""
|
|
|
|
|
>>> b'foo' {comment}
|
|
|
|
|
'foo'
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2015-12-30 06:55:19 +08:00
|
|
|
|
foo=f"""
|
|
|
|
|
def foo():
|
|
|
|
|
'''
|
|
|
|
|
>>> b'foo' {comment}
|
|
|
|
|
'foo'
|
|
|
|
|
'''
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run("--doctest-modules")
|
2015-12-30 06:55:19 +08:00
|
|
|
|
reprec.assertoutcome(passed=2)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_unicode_string(self, pytester: Pytester):
|
2015-08-13 08:41:31 +08:00
|
|
|
|
"""Test that doctests which output unicode fail in Python 2 when
|
2015-08-13 09:46:13 +08:00
|
|
|
|
the ALLOW_UNICODE option is not used. The same test should pass
|
|
|
|
|
in Python 3.
|
2015-08-13 08:41:31 +08:00
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-08-13 08:41:31 +08:00
|
|
|
|
test_doc="""
|
2015-08-13 09:46:13 +08:00
|
|
|
|
>>> b'12'.decode('ascii')
|
2015-08-13 08:41:31 +08:00
|
|
|
|
'12'
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run()
|
2019-05-28 07:31:52 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
2015-08-13 08:41:31 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_bytes_literal(self, pytester: Pytester):
|
2015-12-30 06:55:19 +08:00
|
|
|
|
"""Test that doctests which output bytes fail in Python 3 when
|
2019-05-28 07:31:52 +08:00
|
|
|
|
the ALLOW_BYTES option is not used. (#1287).
|
2015-12-30 06:55:19 +08:00
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-12-30 06:55:19 +08:00
|
|
|
|
test_doc="""
|
|
|
|
|
>>> b'foo'
|
|
|
|
|
'foo'
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run()
|
2019-05-28 07:31:52 +08:00
|
|
|
|
reprec.assertoutcome(failed=1)
|
2015-12-30 06:55:19 +08:00
|
|
|
|
|
2019-08-30 15:35:08 +08:00
|
|
|
|
def test_number_re(self) -> None:
|
|
|
|
|
_number_re = _get_checker()._number_re # type: ignore
|
2019-07-08 22:41:33 +08:00
|
|
|
|
for s in [
|
|
|
|
|
"1.",
|
|
|
|
|
"+1.",
|
|
|
|
|
"-1.",
|
|
|
|
|
".1",
|
|
|
|
|
"+.1",
|
|
|
|
|
"-.1",
|
|
|
|
|
"0.1",
|
|
|
|
|
"+0.1",
|
|
|
|
|
"-0.1",
|
|
|
|
|
"1e5",
|
|
|
|
|
"+1e5",
|
|
|
|
|
"1e+5",
|
|
|
|
|
"+1e+5",
|
|
|
|
|
"1e-5",
|
|
|
|
|
"+1e-5",
|
|
|
|
|
"-1e-5",
|
|
|
|
|
"1.2e3",
|
|
|
|
|
"-1.2e-3",
|
|
|
|
|
]:
|
|
|
|
|
print(s)
|
2019-08-30 15:35:08 +08:00
|
|
|
|
m = _number_re.match(s)
|
2019-07-08 22:41:33 +08:00
|
|
|
|
assert m is not None
|
|
|
|
|
assert float(m.group()) == pytest.approx(float(s))
|
|
|
|
|
for s in ["1", "abc"]:
|
|
|
|
|
print(s)
|
2019-08-30 15:35:08 +08:00
|
|
|
|
assert _number_re.match(s) is None
|
2019-07-08 22:41:33 +08:00
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("config_mode", ["ini", "comment"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_number_precision(self, pytester, config_mode):
|
2019-07-08 22:41:33 +08:00
|
|
|
|
"""Test the NUMBER option."""
|
|
|
|
|
if config_mode == "ini":
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeini(
|
2019-07-08 22:41:33 +08:00
|
|
|
|
"""
|
|
|
|
|
[pytest]
|
|
|
|
|
doctest_optionflags = NUMBER
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
comment = ""
|
|
|
|
|
else:
|
|
|
|
|
comment = "#doctest: +NUMBER"
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2019-07-08 22:41:33 +08:00
|
|
|
|
test_doc=f"""
|
|
|
|
|
|
|
|
|
|
Scalars:
|
|
|
|
|
|
|
|
|
|
>>> import math
|
|
|
|
|
>>> math.pi {comment}
|
|
|
|
|
3.141592653589793
|
|
|
|
|
>>> math.pi {comment}
|
|
|
|
|
3.1416
|
|
|
|
|
>>> math.pi {comment}
|
|
|
|
|
3.14
|
|
|
|
|
>>> -math.pi {comment}
|
|
|
|
|
-3.14
|
|
|
|
|
>>> math.pi {comment}
|
|
|
|
|
3.
|
|
|
|
|
>>> 3. {comment}
|
|
|
|
|
3.0
|
|
|
|
|
>>> 3. {comment}
|
|
|
|
|
3.
|
|
|
|
|
>>> 3. {comment}
|
|
|
|
|
3.01
|
|
|
|
|
>>> 3. {comment}
|
|
|
|
|
2.99
|
|
|
|
|
>>> .299 {comment}
|
|
|
|
|
.3
|
|
|
|
|
>>> .301 {comment}
|
|
|
|
|
.3
|
|
|
|
|
>>> 951. {comment}
|
|
|
|
|
1e3
|
|
|
|
|
>>> 1049. {comment}
|
|
|
|
|
1e3
|
|
|
|
|
>>> -1049. {comment}
|
|
|
|
|
-1e3
|
|
|
|
|
>>> 1e3 {comment}
|
|
|
|
|
1e3
|
|
|
|
|
>>> 1e3 {comment}
|
|
|
|
|
1000.
|
|
|
|
|
|
|
|
|
|
Lists:
|
|
|
|
|
|
|
|
|
|
>>> [3.1415, 0.097, 13.1, 7, 8.22222e5, 0.598e-2] {comment}
|
|
|
|
|
[3.14, 0.1, 13., 7, 8.22e5, 6.0e-3]
|
|
|
|
|
>>> [[0.333, 0.667], [0.999, 1.333]] {comment}
|
|
|
|
|
[[0.33, 0.667], [0.999, 1.333]]
|
|
|
|
|
>>> [[[0.101]]] {comment}
|
|
|
|
|
[[[0.1]]]
|
|
|
|
|
|
|
|
|
|
Doesn't barf on non-numbers:
|
|
|
|
|
|
|
|
|
|
>>> 'abc' {comment}
|
|
|
|
|
'abc'
|
|
|
|
|
>>> None {comment}
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run()
|
2019-07-08 22:41:33 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"expression,output",
|
|
|
|
|
[
|
|
|
|
|
# ints shouldn't match floats:
|
|
|
|
|
("3.0", "3"),
|
|
|
|
|
("3e0", "3"),
|
|
|
|
|
("1e3", "1000"),
|
|
|
|
|
("3", "3.0"),
|
|
|
|
|
# Rounding:
|
|
|
|
|
("3.1", "3.0"),
|
|
|
|
|
("3.1", "3.2"),
|
|
|
|
|
("3.1", "4.0"),
|
|
|
|
|
("8.22e5", "810000.0"),
|
|
|
|
|
# Only the actual output is rounded up, not the expected output:
|
2019-07-11 16:57:44 +08:00
|
|
|
|
("3.0", "2.98"),
|
2019-07-08 22:41:33 +08:00
|
|
|
|
("1e3", "999"),
|
2019-07-11 17:04:43 +08:00
|
|
|
|
# The current implementation doesn't understand that numbers inside
|
|
|
|
|
# strings shouldn't be treated as numbers:
|
2020-10-17 21:54:54 +08:00
|
|
|
|
pytest.param("'3.1416'", "'3.14'", marks=pytest.mark.xfail),
|
2019-07-08 22:41:33 +08:00
|
|
|
|
],
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_number_non_matches(self, pytester, expression, output):
|
|
|
|
|
pytester.maketxtfile(
|
2019-07-08 22:41:33 +08:00
|
|
|
|
test_doc=f"""
|
|
|
|
|
>>> {expression} #doctest: +NUMBER
|
|
|
|
|
{output}
|
2019-07-11 16:57:44 +08:00
|
|
|
|
"""
|
2019-07-08 22:41:33 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run()
|
2019-07-08 22:41:33 +08:00
|
|
|
|
reprec.assertoutcome(passed=0, failed=1)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_number_and_allow_unicode(self, pytester: Pytester):
|
|
|
|
|
pytester.maketxtfile(
|
2019-07-08 22:41:33 +08:00
|
|
|
|
test_doc="""
|
|
|
|
|
>>> from collections import namedtuple
|
|
|
|
|
>>> T = namedtuple('T', 'a b c')
|
|
|
|
|
>>> T(a=0.2330000001, b=u'str', c=b'bytes') # doctest: +ALLOW_UNICODE, +ALLOW_BYTES, +NUMBER
|
|
|
|
|
T(a=0.233, b=u'str', c='bytes')
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run()
|
2019-07-08 22:41:33 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
2015-08-13 08:41:31 +08:00
|
|
|
|
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class TestDoctestSkips:
|
2015-08-26 10:12:02 +08:00
|
|
|
|
"""
|
|
|
|
|
If all examples in a doctest are skipped due to the SKIP option, then
|
|
|
|
|
the tests should be SKIPPED rather than PASSED. (#957)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(params=["text", "module"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def makedoctest(self, pytester, request):
|
2015-08-26 10:12:02 +08:00
|
|
|
|
def makeit(doctest):
|
|
|
|
|
mode = request.param
|
|
|
|
|
if mode == "text":
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(doctest)
|
2015-08-26 10:12:02 +08:00
|
|
|
|
else:
|
|
|
|
|
assert mode == "module"
|
2024-05-01 00:06:26 +08:00
|
|
|
|
pytester.makepyfile(f'"""\n{doctest}"""')
|
2015-08-26 10:12:02 +08:00
|
|
|
|
|
|
|
|
|
return makeit
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_one_skipped(self, pytester, makedoctest):
|
2015-08-26 10:12:02 +08:00
|
|
|
|
makedoctest(
|
|
|
|
|
"""
|
|
|
|
|
>>> 1 + 1 # doctest: +SKIP
|
|
|
|
|
2
|
|
|
|
|
>>> 2 + 2
|
|
|
|
|
4
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run("--doctest-modules")
|
2015-08-26 10:12:02 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_one_skipped_failed(self, pytester, makedoctest):
|
2015-08-26 10:12:02 +08:00
|
|
|
|
makedoctest(
|
|
|
|
|
"""
|
|
|
|
|
>>> 1 + 1 # doctest: +SKIP
|
|
|
|
|
2
|
|
|
|
|
>>> 2 + 2
|
|
|
|
|
200
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run("--doctest-modules")
|
2015-08-26 10:12:02 +08:00
|
|
|
|
reprec.assertoutcome(failed=1)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_all_skipped(self, pytester, makedoctest):
|
2015-08-26 10:12:02 +08:00
|
|
|
|
makedoctest(
|
|
|
|
|
"""
|
|
|
|
|
>>> 1 + 1 # doctest: +SKIP
|
|
|
|
|
2
|
|
|
|
|
>>> 2 + 2 # doctest: +SKIP
|
|
|
|
|
200
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run("--doctest-modules")
|
2015-08-26 10:12:02 +08:00
|
|
|
|
reprec.assertoutcome(skipped=1)
|
2015-10-02 09:59:37 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_vacuous_all_skipped(self, pytester, makedoctest):
|
2016-06-02 00:16:05 +08:00
|
|
|
|
makedoctest("")
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run("--doctest-modules")
|
2016-06-02 00:16:05 +08:00
|
|
|
|
reprec.assertoutcome(passed=0, skipped=0)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_continue_on_failure(self, pytester: Pytester):
|
|
|
|
|
pytester.maketxtfile(
|
2018-02-23 11:00:54 +08:00
|
|
|
|
test_something="""
|
|
|
|
|
>>> i = 5
|
|
|
|
|
>>> def foo():
|
|
|
|
|
... raise ValueError('error1')
|
|
|
|
|
>>> foo()
|
|
|
|
|
>>> i
|
|
|
|
|
>>> i + 2
|
|
|
|
|
7
|
|
|
|
|
>>> i + 1
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(
|
|
|
|
|
"--doctest-modules", "--doctest-continue-on-failure"
|
|
|
|
|
)
|
2018-02-23 11:00:54 +08:00
|
|
|
|
result.assert_outcomes(passed=0, failed=1)
|
2018-02-24 11:20:14 +08:00
|
|
|
|
# The lines that contains the failure are 4, 5, and 8. The first one
|
|
|
|
|
# is a stack trace and the other two are mismatches.
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
["*4: UnexpectedException*", "*5: DocTestFailure*", "*8: DocTestFailure*"]
|
|
|
|
|
)
|
2018-02-23 11:00:54 +08:00
|
|
|
|
|
2021-11-01 15:01:25 +08:00
|
|
|
|
def test_skipping_wrapped_test(self, pytester):
|
|
|
|
|
"""
|
|
|
|
|
Issue 8796: INTERNALERROR raised when skipping a decorated DocTest
|
|
|
|
|
through pytest_collection_modifyitems.
|
|
|
|
|
"""
|
|
|
|
|
pytester.makeconftest(
|
|
|
|
|
"""
|
|
|
|
|
import pytest
|
|
|
|
|
from _pytest.doctest import DoctestItem
|
|
|
|
|
|
|
|
|
|
def pytest_collection_modifyitems(config, items):
|
|
|
|
|
skip_marker = pytest.mark.skip()
|
|
|
|
|
|
|
|
|
|
for item in items:
|
|
|
|
|
if isinstance(item, DoctestItem):
|
|
|
|
|
item.add_marker(skip_marker)
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
|
"""
|
|
|
|
|
from contextlib import contextmanager
|
|
|
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
|
def my_config_context():
|
|
|
|
|
'''
|
|
|
|
|
>>> import os
|
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
|
|
|
|
assert "INTERNALERROR" not in result.stdout.str()
|
|
|
|
|
result.assert_outcomes(skipped=1)
|
|
|
|
|
|
2015-10-02 09:59:37 +08:00
|
|
|
|
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class TestDoctestAutoUseFixtures:
|
2015-10-02 09:59:37 +08:00
|
|
|
|
SCOPES = ["module", "session", "class", "function"]
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_module_session_fixture(self, pytester: Pytester):
|
2020-07-18 17:35:13 +08:00
|
|
|
|
"""Test that session fixtures are initialized for doctest modules (#768)."""
|
2015-10-02 09:59:37 +08:00
|
|
|
|
# session fixture which changes some global data, which will
|
|
|
|
|
# be accessed by doctests in a module
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeconftest(
|
2015-10-02 09:59:37 +08:00
|
|
|
|
"""
|
|
|
|
|
import pytest
|
|
|
|
|
import sys
|
|
|
|
|
|
2020-06-25 19:05:46 +08:00
|
|
|
|
@pytest.fixture(autouse=True, scope='session')
|
2015-10-02 09:59:37 +08:00
|
|
|
|
def myfixture():
|
|
|
|
|
assert not hasattr(sys, 'pytest_session_data')
|
|
|
|
|
sys.pytest_session_data = 1
|
|
|
|
|
yield
|
|
|
|
|
del sys.pytest_session_data
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2015-10-02 09:59:37 +08:00
|
|
|
|
foo="""
|
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
def foo():
|
|
|
|
|
'''
|
|
|
|
|
>>> assert sys.pytest_session_data == 1
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
def bar():
|
|
|
|
|
'''
|
|
|
|
|
>>> assert sys.pytest_session_data == 1
|
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2019-03-23 18:36:18 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*2 passed*"])
|
2015-10-02 09:59:37 +08:00
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("scope", SCOPES)
|
|
|
|
|
@pytest.mark.parametrize("enable_doctest", [True, False])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_fixture_scopes(self, pytester, scope, enable_doctest):
|
2015-10-02 09:59:37 +08:00
|
|
|
|
"""Test that auto-use fixtures work properly with doctest modules.
|
|
|
|
|
See #1057 and #1100.
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
f"""
|
2015-10-02 09:59:37 +08:00
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope="{scope}")
|
|
|
|
|
def auto(request):
|
|
|
|
|
return 99
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2015-10-02 09:59:37 +08:00
|
|
|
|
test_1='''
|
|
|
|
|
def test_foo():
|
|
|
|
|
"""
|
|
|
|
|
>>> getfixture('auto') + 1
|
|
|
|
|
100
|
|
|
|
|
"""
|
|
|
|
|
def test_bar():
|
|
|
|
|
assert 1
|
|
|
|
|
'''
|
|
|
|
|
)
|
|
|
|
|
params = ("--doctest-modules",) if enable_doctest else ()
|
|
|
|
|
passes = 3 if enable_doctest else 2
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest(*params)
|
2015-10-02 09:59:37 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*=== %d passed in *" % passes])
|
2018-05-23 22:48:46 +08:00
|
|
|
|
|
2015-10-02 09:59:37 +08:00
|
|
|
|
@pytest.mark.parametrize("scope", SCOPES)
|
|
|
|
|
@pytest.mark.parametrize("autouse", [True, False])
|
|
|
|
|
@pytest.mark.parametrize("use_fixture_in_doctest", [True, False])
|
|
|
|
|
def test_fixture_module_doctest_scopes(
|
2020-10-12 23:13:06 +08:00
|
|
|
|
self, pytester, scope, autouse, use_fixture_in_doctest
|
2015-10-02 09:59:37 +08:00
|
|
|
|
):
|
|
|
|
|
"""Test that auto-use fixtures work properly with doctest files.
|
|
|
|
|
See #1057 and #1100.
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
f"""
|
2015-10-02 09:59:37 +08:00
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse={autouse}, scope="{scope}")
|
|
|
|
|
def auto(request):
|
|
|
|
|
return 99
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2015-10-02 09:59:37 +08:00
|
|
|
|
if use_fixture_in_doctest:
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-10-02 09:59:37 +08:00
|
|
|
|
test_doc="""
|
|
|
|
|
>>> getfixture('auto')
|
|
|
|
|
99
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
else:
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-10-02 09:59:37 +08:00
|
|
|
|
test_doc="""
|
|
|
|
|
>>> 1 + 1
|
|
|
|
|
2
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2019-10-06 01:18:51 +08:00
|
|
|
|
result.stdout.no_fnmatch_line("*FAILURES*")
|
2015-10-02 09:59:37 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*=== 1 passed in *"])
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("scope", SCOPES)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_auto_use_request_attributes(self, pytester, scope):
|
2015-10-02 09:59:37 +08:00
|
|
|
|
"""Check that all attributes of a request in an autouse fixture
|
|
|
|
|
behave as expected when requested for a doctest item.
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
|
f"""
|
2015-10-02 09:59:37 +08:00
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope="{scope}")
|
|
|
|
|
def auto(request):
|
|
|
|
|
if "{scope}" == 'module':
|
|
|
|
|
assert request.module is None
|
|
|
|
|
if "{scope}" == 'class':
|
|
|
|
|
assert request.cls is None
|
|
|
|
|
if "{scope}" == 'function':
|
|
|
|
|
assert request.function is None
|
|
|
|
|
return 99
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.maketxtfile(
|
2015-10-02 09:59:37 +08:00
|
|
|
|
test_doc="""
|
|
|
|
|
>>> 1 + 1
|
|
|
|
|
2
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2019-10-06 01:18:51 +08:00
|
|
|
|
str(result.stdout.no_fnmatch_line("*FAILURES*"))
|
2015-12-31 04:19:08 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["*=== 1 passed in *"])
|
2016-03-02 20:43:57 +08:00
|
|
|
|
|
doctest: fix autouse fixtures possibly not getting picked up
Fix #11929.
Figured out what's going on. We have the following collection tree:
```
<Dir pyspacewar>
<Dir src>
<Package pyspacewar>
<Package tests>
<DoctestModule test_main.py>
<DoctestItem pyspacewar.tests.test_main.doctest_main>
```
And the `test_main.py` contains an autouse fixture (`fake_game_ui`) that
`doctest_main` needs in order to run properly. The fixture doesn't run!
It doesn't run because nothing collects the fixtures from (calls
`parsefactories()` on) the `test_main.py` `DoctestModule`.
How come it only started happening with commit
ab63ebb3dc07b89670b96ae97044f48406c44fa0? Turns out it mostly only
worked accidentally. Each `DoctestModule` is also collected as a normal
`Module`, with the `Module` collected after the `DoctestModule`. For
example, if we add a non-doctest test to `test_main.py`, the collection
tree looks like this:
```
<Dir pyspacewar>
<Dir src>
<Package pyspacewar>
<Package tests>
<DoctestModule test_main.py>
<DoctestItem pyspacewar.tests.test_main.doctest_main>
<Module test_main.py>
<Function test_it>
```
Now, `Module` *does* collect fixtures. When autouse fixtures are
collected, they are added to the `_nodeid_autousenames` dict.
Before ab63ebb3dc07b89670b96ae97044f48406c44fa0, `DoctestItem` consults
`_nodeid_autousenames` at *setup* time. At this point, the `Module` has
collected and so it ended up picking the autouse fixture (this relies on
another "accident", that the `DoctestModule` and `Module` have the same
node ID).
After ab63ebb3dc07b89670b96ae97044f48406c44fa0, `DoctestItem` consults
`_nodeid_autousenames` at *collection* time (= when it's created). At
this point, the `Module` hasn't collected yet, so the autouse fixture is
not picked out.
The fix is simple -- have `DoctestModule.collect()` call
`parsefactories`. From some testing I've done it shouldn't have negative
consequences (I hope).
2024-02-07 05:09:00 +08:00
|
|
|
|
@pytest.mark.parametrize("scope", [*SCOPES, "package"])
|
|
|
|
|
def test_auto_use_defined_in_same_module(
|
|
|
|
|
self, pytester: Pytester, scope: str
|
|
|
|
|
) -> None:
|
|
|
|
|
"""Autouse fixtures defined in the same module as the doctest get picked
|
|
|
|
|
up properly.
|
|
|
|
|
|
|
|
|
|
Regression test for #11929.
|
|
|
|
|
"""
|
|
|
|
|
pytester.makepyfile(
|
|
|
|
|
f"""
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
AUTO = "the fixture did not run"
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope="{scope}")
|
|
|
|
|
def auto(request):
|
|
|
|
|
global AUTO
|
|
|
|
|
AUTO = "the fixture ran"
|
|
|
|
|
|
|
|
|
|
def my_doctest():
|
|
|
|
|
'''My doctest.
|
|
|
|
|
|
|
|
|
|
>>> my_doctest()
|
|
|
|
|
'the fixture ran'
|
|
|
|
|
'''
|
|
|
|
|
return AUTO
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
|
|
|
|
result.assert_outcomes(passed=1)
|
|
|
|
|
|
2016-03-02 20:43:57 +08:00
|
|
|
|
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class TestDoctestNamespaceFixture:
|
2016-03-02 20:43:57 +08:00
|
|
|
|
SCOPES = ["module", "session", "class", "function"]
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("scope", SCOPES)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_namespace_doctestfile(self, pytester, scope):
|
2016-03-02 20:43:57 +08:00
|
|
|
|
"""
|
|
|
|
|
Check that inserting something into the namespace works in a
|
|
|
|
|
simple text file doctest
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeconftest(
|
2016-03-02 20:43:57 +08:00
|
|
|
|
f"""
|
|
|
|
|
import pytest
|
|
|
|
|
import contextlib
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope="{scope}")
|
|
|
|
|
def add_contextlib(doctest_namespace):
|
|
|
|
|
doctest_namespace['cl'] = contextlib
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.maketxtfile(
|
2016-03-02 20:43:57 +08:00
|
|
|
|
"""
|
|
|
|
|
>>> print(cl.__name__)
|
|
|
|
|
contextlib
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p)
|
2016-03-02 20:43:57 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("scope", SCOPES)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_namespace_pyfile(self, pytester, scope):
|
2016-03-02 20:43:57 +08:00
|
|
|
|
"""
|
|
|
|
|
Check that inserting something into the namespace works in a
|
|
|
|
|
simple Python file docstring doctest
|
|
|
|
|
"""
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makeconftest(
|
2016-03-02 20:43:57 +08:00
|
|
|
|
f"""
|
|
|
|
|
import pytest
|
|
|
|
|
import contextlib
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope="{scope}")
|
|
|
|
|
def add_contextlib(doctest_namespace):
|
|
|
|
|
doctest_namespace['cl'] = contextlib
|
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
p = pytester.makepyfile(
|
2016-03-02 20:43:57 +08:00
|
|
|
|
"""
|
|
|
|
|
def foo():
|
|
|
|
|
'''
|
|
|
|
|
>>> print(cl.__name__)
|
|
|
|
|
contextlib
|
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
reprec = pytester.inline_run(p, "--doctest-modules")
|
2016-03-02 20:43:57 +08:00
|
|
|
|
reprec.assertoutcome(passed=1)
|
2016-07-23 21:16:23 +08:00
|
|
|
|
|
|
|
|
|
|
2017-02-17 02:41:51 +08:00
|
|
|
|
class TestDoctestReportingOption:
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def _run_doctest_report(self, pytester, format):
|
|
|
|
|
pytester.makepyfile(
|
2016-07-23 21:16:23 +08:00
|
|
|
|
"""
|
|
|
|
|
def foo():
|
|
|
|
|
'''
|
|
|
|
|
>>> foo()
|
|
|
|
|
a b
|
|
|
|
|
0 1 4
|
|
|
|
|
1 2 4
|
|
|
|
|
2 3 6
|
|
|
|
|
'''
|
|
|
|
|
print(' a b\\n'
|
|
|
|
|
'0 1 4\\n'
|
|
|
|
|
'1 2 5\\n'
|
|
|
|
|
'2 3 6')
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
return pytester.runpytest("--doctest-modules", "--doctest-report", format)
|
2016-07-23 21:16:23 +08:00
|
|
|
|
|
2016-07-23 21:21:59 +08:00
|
|
|
|
@pytest.mark.parametrize("format", ["udiff", "UDIFF", "uDiFf"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_report_udiff(self, pytester, format):
|
|
|
|
|
result = self._run_doctest_report(pytester, format)
|
2016-07-23 21:16:23 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[" 0 1 4", " -1 2 4", " +1 2 5", " 2 3 6"]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_report_cdiff(self, pytester: Pytester):
|
|
|
|
|
result = self._run_doctest_report(pytester, "cdiff")
|
2016-07-23 21:16:23 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
" a b",
|
|
|
|
|
" 0 1 4",
|
|
|
|
|
" ! 1 2 4",
|
|
|
|
|
" 2 3 6",
|
|
|
|
|
" --- 1,4 ----",
|
|
|
|
|
" a b",
|
|
|
|
|
" 0 1 4",
|
|
|
|
|
" ! 1 2 5",
|
|
|
|
|
" 2 3 6",
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_report_ndiff(self, pytester: Pytester):
|
|
|
|
|
result = self._run_doctest_report(pytester, "ndiff")
|
2016-07-23 21:16:23 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
" a b",
|
|
|
|
|
" 0 1 4",
|
|
|
|
|
" - 1 2 4",
|
|
|
|
|
" ? ^",
|
|
|
|
|
" + 1 2 5",
|
|
|
|
|
" ? ^",
|
|
|
|
|
" 2 3 6",
|
|
|
|
|
]
|
|
|
|
|
)
|
2018-05-23 22:48:46 +08:00
|
|
|
|
|
2016-07-23 21:58:13 +08:00
|
|
|
|
@pytest.mark.parametrize("format", ["none", "only_first_failure"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_report_none_or_only_first_failure(self, pytester, format):
|
|
|
|
|
result = self._run_doctest_report(pytester, format)
|
2016-07-23 21:58:13 +08:00
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"Expected:",
|
|
|
|
|
" a b",
|
|
|
|
|
" 0 1 4",
|
|
|
|
|
" 1 2 4",
|
|
|
|
|
" 2 3 6",
|
|
|
|
|
"Got:",
|
|
|
|
|
" a b",
|
|
|
|
|
" 0 1 4",
|
|
|
|
|
" 1 2 5",
|
|
|
|
|
" 2 3 6",
|
|
|
|
|
]
|
|
|
|
|
)
|
2016-07-23 21:16:23 +08:00
|
|
|
|
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_report_invalid(self, pytester: Pytester):
|
|
|
|
|
result = self._run_doctest_report(pytester, "obviously_invalid_format")
|
2016-07-23 21:16:23 +08:00
|
|
|
|
result.stderr.fnmatch_lines(
|
|
|
|
|
[
|
|
|
|
|
"*error: argument --doctest-report: invalid choice: 'obviously_invalid_format' (choose from*"
|
|
|
|
|
]
|
|
|
|
|
)
|
2018-10-22 22:30:25 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("mock_module", ["mock", "unittest.mock"])
|
2020-10-12 23:13:06 +08:00
|
|
|
|
def test_doctest_mock_objects_dont_recurse_missbehaved(mock_module, pytester: Pytester):
|
2018-10-22 22:30:25 +08:00
|
|
|
|
pytest.importorskip(mock_module)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
pytester.makepyfile(
|
2018-10-22 22:30:25 +08:00
|
|
|
|
f"""
|
|
|
|
|
from {mock_module} import call
|
|
|
|
|
class Example(object):
|
|
|
|
|
'''
|
|
|
|
|
>>> 1 + 1
|
|
|
|
|
2
|
|
|
|
|
'''
|
|
|
|
|
"""
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
result = pytester.runpytest("--doctest-modules")
|
2018-10-22 22:30:25 +08:00
|
|
|
|
result.stdout.fnmatch_lines(["* 1 passed *"])
|
2019-06-08 10:39:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Broken:
|
|
|
|
|
def __getattr__(self, _):
|
|
|
|
|
raise KeyError("This should be an AttributeError")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize( # pragma: no branch (lambdas are not called)
|
|
|
|
|
"stop", [None, _is_mocked, lambda f: None, lambda f: False, lambda f: True]
|
|
|
|
|
)
|
2020-05-01 19:40:17 +08:00
|
|
|
|
def test_warning_on_unwrap_of_broken_object(
|
|
|
|
|
stop: Optional[Callable[[object], object]],
|
|
|
|
|
) -> None:
|
2019-06-08 10:39:59 +08:00
|
|
|
|
bad_instance = Broken()
|
|
|
|
|
assert inspect.unwrap.__module__ == "inspect"
|
|
|
|
|
with _patch_unwrap_mock_aware():
|
|
|
|
|
assert inspect.unwrap.__module__ != "inspect"
|
|
|
|
|
with pytest.warns(
|
|
|
|
|
pytest.PytestWarning, match="^Got KeyError.* when unwrapping"
|
|
|
|
|
):
|
|
|
|
|
with pytest.raises(KeyError):
|
2020-07-10 14:44:14 +08:00
|
|
|
|
inspect.unwrap(bad_instance, stop=stop) # type: ignore[arg-type]
|
2019-06-08 10:39:59 +08:00
|
|
|
|
assert inspect.unwrap.__module__ == "inspect"
|
2020-05-08 04:14:58 +08:00
|
|
|
|
|
|
|
|
|
|
2020-12-19 20:02:24 +08:00
|
|
|
|
def test_is_setup_py_not_named_setup_py(tmp_path: Path) -> None:
|
2020-10-12 23:13:06 +08:00
|
|
|
|
not_setup_py = tmp_path.joinpath("not_setup.py")
|
2023-06-20 19:55:39 +08:00
|
|
|
|
not_setup_py.write_text(
|
|
|
|
|
'from setuptools import setup; setup(name="foo")', encoding="utf-8"
|
|
|
|
|
)
|
2020-12-19 20:02:24 +08:00
|
|
|
|
assert not _is_setup_py(not_setup_py)
|
2020-05-08 04:14:58 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("mod", ("setuptools", "distutils.core"))
|
2020-12-19 20:02:24 +08:00
|
|
|
|
def test_is_setup_py_is_a_setup_py(tmp_path: Path, mod: str) -> None:
|
|
|
|
|
setup_py = tmp_path.joinpath("setup.py")
|
|
|
|
|
setup_py.write_text(f'from {mod} import setup; setup(name="foo")', "utf-8")
|
2020-05-08 04:14:58 +08:00
|
|
|
|
assert _is_setup_py(setup_py)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("mod", ("setuptools", "distutils.core"))
|
2020-12-19 20:02:24 +08:00
|
|
|
|
def test_is_setup_py_different_encoding(tmp_path: Path, mod: str) -> None:
|
2020-10-12 23:13:06 +08:00
|
|
|
|
setup_py = tmp_path.joinpath("setup.py")
|
2020-05-08 04:14:58 +08:00
|
|
|
|
contents = (
|
|
|
|
|
"# -*- coding: cp1252 -*-\n"
|
|
|
|
|
f'from {mod} import setup; setup(name="foo", description="€")\n'
|
|
|
|
|
)
|
2020-10-12 23:13:06 +08:00
|
|
|
|
setup_py.write_bytes(contents.encode("cp1252"))
|
2020-12-19 20:02:24 +08:00
|
|
|
|
assert _is_setup_py(setup_py)
|
2021-07-28 05:50:26 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"name, expected", [("__main__.py", True), ("__init__.py", False)]
|
|
|
|
|
)
|
|
|
|
|
def test_is_main_py(tmp_path: Path, name: str, expected: bool) -> None:
|
|
|
|
|
dunder_main = tmp_path.joinpath(name)
|
|
|
|
|
assert _is_main_py(dunder_main) == expected
|