Skip to content

Commit 68f5ee1

Browse files
committed
convert --doctest to pytest tests
1 parent e7cac78 commit 68f5ee1

14 files changed

+113
-143
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
*.egg
22
*.egg-info
33
*.pyc
4-
/.coverage
4+
/.coverage*
55
/.tox
66
/build/
77
/dist

docs/developer.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -89,16 +89,16 @@ Several docstrings contain examples directly from the `PEP 8`_ document.
8989
Okay: spam(ham[1], {eggs: 2})
9090
E201: spam( ham[1], {eggs: 2})
9191

92-
These examples are verified automatically when ``pycodestyle.py`` is run with
93-
the ``--doctest`` option. You can add examples for your own check functions.
92+
These examples are verified automatically by ``test_self_doctest.py``.
93+
You can add examples for your own check functions.
9494
The format is simple: ``"Okay"`` or error/warning code followed by colon and
9595
space, the rest of the line is example source code. If you put ``'r'`` before
9696
the docstring, you can use ``\n`` for newline and ``\t`` for tab.
9797

9898
Then be sure to pass the tests::
9999

100100
$ python pycodestyle.py --testsuite testsuite
101-
$ python pycodestyle.py --doctest
101+
$ pytest testsuite/test_self_doctest.py
102102
$ python pycodestyle.py --verbose pycodestyle.py
103103

104104
When contributing to pycodestyle, please observe our `Code of Conduct`_.

pycodestyle.py

+5-28
Original file line numberDiff line numberDiff line change
@@ -1136,10 +1136,6 @@ def module_imports_on_top_of_file(
11361136
Okay: # this is a comment\nimport os
11371137
Okay: '''this is a module docstring'''\nimport os
11381138
Okay: r'''this is a module docstring'''\nimport os
1139-
Okay:
1140-
try:\n\timport x\nexcept ImportError:\n\tpass\nelse:\n\tpass\nimport y
1141-
Okay:
1142-
try:\n\timport x\nexcept ImportError:\n\tpass\nfinally:\n\tpass\nimport y
11431139
E402: a=1\nimport os
11441140
E402: 'One string'\n"Two string"\nimport os
11451141
E402: a=1\nfrom sys import x
@@ -1730,15 +1726,6 @@ def expand_indent(line):
17301726
r"""Return the amount of indentation.
17311727
17321728
Tabs are expanded to the next multiple of 8.
1733-
1734-
>>> expand_indent(' ')
1735-
4
1736-
>>> expand_indent('\t')
1737-
8
1738-
>>> expand_indent(' \t')
1739-
8
1740-
>>> expand_indent(' \t')
1741-
16
17421729
"""
17431730
line = line.rstrip('\n\r')
17441731
if '\t' not in line:
@@ -1755,15 +1742,7 @@ def expand_indent(line):
17551742

17561743

17571744
def mute_string(text):
1758-
"""Replace contents with 'xxx' to prevent syntax matching.
1759-
1760-
>>> mute_string('"abc"')
1761-
'"xxx"'
1762-
>>> mute_string("'''abc'''")
1763-
"'''xxx'''"
1764-
>>> mute_string("r'abc'")
1765-
"r'xxx'"
1766-
"""
1745+
"""Replace contents with 'xxx' to prevent syntax matching."""
17671746
# String modifiers (e.g. u or r)
17681747
start = text.index(text[-1]) + 1
17691748
end = len(text) - 1
@@ -2323,7 +2302,7 @@ def __init__(self, *args, **kwargs):
23232302

23242303
options.select = tuple(options.select or ())
23252304
if not (options.select or options.ignore or
2326-
options.testsuite or options.doctest) and DEFAULT_IGNORE:
2305+
options.testsuite) and DEFAULT_IGNORE:
23272306
# The default choice: ignore controversial checks
23282307
options.ignore = tuple(DEFAULT_IGNORE.split(','))
23292308
else:
@@ -2496,8 +2475,6 @@ def get_parser(prog='pycodestyle', version=__version__):
24962475
if os.path.exists(TESTSUITE_PATH):
24972476
group.add_option('--testsuite', metavar='dir',
24982477
help="run regression tests from dir")
2499-
group.add_option('--doctest', action='store_true',
2500-
help="run doctest on myself")
25012478
group.add_option('--benchmark', action='store_true',
25022479
help="measure processing speed")
25032480
return parser
@@ -2574,7 +2551,7 @@ def read_config(options, args, arglist, parser):
25742551

25752552
# Third, overwrite with the command-line options
25762553
(options, __) = parser.parse_args(arglist, values=new_options)
2577-
options.doctest = options.testsuite = False
2554+
options.testsuite = False
25782555
return options
25792556

25802557

@@ -2609,7 +2586,7 @@ def process_options(arglist=None, parse_argv=False, config_file=None,
26092586

26102587
if options.ensure_value('testsuite', False):
26112588
args.append(options.testsuite)
2612-
elif not options.ensure_value('doctest', False):
2589+
else:
26132590
if parse_argv and not args:
26142591
if options.diff or any(os.path.exists(name)
26152592
for name in PROJECT_CONFIG):
@@ -2662,7 +2639,7 @@ def _main():
26622639
style_guide = StyleGuide(parse_argv=True)
26632640
options = style_guide.options
26642641

2665-
if options.doctest or options.testsuite:
2642+
if options.testsuite:
26662643
from testsuite.support import run_tests
26672644
report = run_tests(style_guide)
26682645
else:

testing/__init__.py

Whitespace-only changes.

testing/support.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from __future__ import annotations
2+
3+
from pycodestyle import BaseReport
4+
from pycodestyle import StyleGuide
5+
6+
7+
class InMemoryReport(BaseReport):
8+
"""
9+
Collect the results in memory, without printing anything.
10+
"""
11+
12+
def __init__(self, options):
13+
super().__init__(options)
14+
self.in_memory_errors = []
15+
16+
def error(self, line_number, offset, text, check):
17+
"""
18+
Report an error, according to options.
19+
"""
20+
code = text[:4]
21+
self.in_memory_errors.append(f'{code}:{line_number}:{offset + 1}')
22+
return super().error(line_number, offset, text, check)
23+
24+
25+
def errors_from_src(src: str) -> list[str]:
26+
guide = StyleGuide(select=('E', 'W'))
27+
reporter = guide.init_report(InMemoryReport)
28+
guide.input_file(
29+
filename='in-memory-test-file.py',
30+
lines=src.splitlines(True),
31+
)
32+
return reporter.in_memory_errors

testsuite/support.py

+1-88
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,8 @@
44
import re
55
import sys
66

7-
from pycodestyle import BaseReport
8-
from pycodestyle import Checker
97
from pycodestyle import readlines
108
from pycodestyle import StandardReport
11-
from pycodestyle import StyleGuide
129

1310
SELFTEST_REGEX = re.compile(r'\b(Okay|[EW]\d{3}):\s(.*)')
1411
ROOT_DIR = os.path.dirname(os.path.dirname(__file__))
@@ -87,68 +84,6 @@ def print_results(self):
8784
print("Test failed." if self.total_errors else "Test passed.")
8885

8986

90-
class InMemoryReport(BaseReport):
91-
"""
92-
Collect the results in memory, without printing anything.
93-
"""
94-
95-
def __init__(self, options):
96-
super().__init__(options)
97-
self.in_memory_errors = []
98-
99-
def error(self, line_number, offset, text, check):
100-
"""
101-
Report an error, according to options.
102-
"""
103-
code = text[:4]
104-
self.in_memory_errors.append('{}:{}:{}'.format(
105-
code, line_number, offset + 1))
106-
return super().error(
107-
line_number, offset, text, check)
108-
109-
110-
def selftest(options):
111-
"""
112-
Test all check functions with test cases in docstrings.
113-
"""
114-
count_failed = count_all = 0
115-
report = BaseReport(options)
116-
counters = report.counters
117-
checks = options.physical_checks + options.logical_checks
118-
for name, check, argument_names in checks:
119-
for line in check.__doc__.splitlines():
120-
line = line.lstrip()
121-
match = SELFTEST_REGEX.match(line)
122-
if match is None:
123-
continue
124-
code, source = match.groups()
125-
lines = [part.replace(r'\t', '\t') + '\n'
126-
for part in source.split(r'\n')]
127-
checker = Checker(lines=lines, options=options, report=report)
128-
checker.check_all()
129-
error = None
130-
if code == 'Okay':
131-
if len(counters) > len(options.benchmark_keys): # pragma: no cover # noqa: E501
132-
codes = [key for key in counters
133-
if key not in options.benchmark_keys]
134-
error = "incorrectly found %s" % ', '.join(codes)
135-
elif not counters.get(code): # pragma: no cover
136-
error = "failed to find %s" % code
137-
# Keep showing errors for multiple tests
138-
for key in set(counters) - set(options.benchmark_keys):
139-
del counters[key]
140-
count_all += 1
141-
if not error:
142-
if options.verbose: # pragma: no cover
143-
print(f"{code}: {source}")
144-
else: # pragma: no cover
145-
count_failed += 1
146-
print("pycodestyle.py: %s:" % error)
147-
for line in checker.lines:
148-
print(line.rstrip())
149-
return count_failed, count_all
150-
151-
15287
def init_tests(pep8style):
15388
"""
15489
Initialize testing framework.
@@ -211,28 +146,6 @@ def run_tests(filename):
211146

212147

213148
def run_tests(style):
214-
options = style.options
215-
if options.doctest:
216-
import doctest
217-
fail_d, done_d = doctest.testmod(report=False, verbose=options.verbose)
218-
fail_s, done_s = selftest(options)
219-
count_failed = fail_s + fail_d
220-
if not options.quiet:
221-
count_passed = done_d + done_s - count_failed
222-
print("%d passed and %d failed." % (count_passed, count_failed))
223-
print("Test failed." if count_failed else "Test passed.")
224-
if count_failed: # pragma: no cover
225-
sys.exit(1)
226-
if options.testsuite:
149+
if style.options.testsuite:
227150
init_tests(style)
228151
return style.check_files()
229-
230-
231-
def errors_from_src(src: str) -> list[str]:
232-
guide = StyleGuide()
233-
reporter = guide.init_report(InMemoryReport)
234-
guide.input_file(
235-
filename='in-memory-test-file.py',
236-
lines=src.splitlines(True),
237-
)
238-
return reporter.in_memory_errors

testsuite/test_E101.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33
import unittest
44

5-
from testsuite.support import errors_from_src
5+
from testing.support import errors_from_src
66

77

88
class E101Test(unittest.TestCase):

testsuite/test_E901.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33
import unittest
44

5-
from testsuite.support import errors_from_src
5+
from testing.support import errors_from_src
66

77

88
class E901Test(unittest.TestCase):

testsuite/test_all.py

-14
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import pycodestyle
55
from testsuite.support import init_tests
66
from testsuite.support import ROOT_DIR
7-
from testsuite.support import selftest
87

98

109
class PycodestyleTestCase(unittest.TestCase):
@@ -15,19 +14,6 @@ def setUp(self):
1514
paths=[os.path.join(ROOT_DIR, 'testsuite')],
1615
select='E,W', quiet=True)
1716

18-
def test_doctest(self):
19-
import doctest
20-
fail_d, done_d = doctest.testmod(
21-
pycodestyle, verbose=False, report=False
22-
)
23-
self.assertTrue(done_d, msg='tests not found')
24-
self.assertFalse(fail_d, msg='%s failure(s)' % fail_d)
25-
26-
def test_selftest(self):
27-
fail_s, done_s = selftest(self._style.options)
28-
self.assertTrue(done_s, msg='tests not found')
29-
self.assertFalse(fail_s, msg='%s failure(s)' % fail_s)
30-
3117
def test_checkers_testsuite(self):
3218
init_tests(self._style)
3319
report = self._style.check_files()

testsuite/test_api.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def test_styleguide_options(self):
148148

149149
# Check unset options
150150
for o in ('benchmark', 'config', 'count', 'diff',
151-
'doctest', 'quiet', 'show_pep8', 'show_source',
151+
'quiet', 'show_pep8', 'show_source',
152152
'statistics', 'testsuite', 'verbose'):
153153
oval = getattr(pep8style.options, o)
154154
self.assertTrue(oval in (None, False), msg=f'{o} = {oval!r}')
@@ -183,10 +183,6 @@ def parse_argv(argstring):
183183
('E121', 'E123', 'E126', 'E226', 'E24', 'E704', 'W503', 'W504')
184184
)
185185

186-
options = parse_argv('--doctest').options
187-
self.assertEqual(options.select, ())
188-
self.assertEqual(options.ignore, ())
189-
190186
options = parse_argv('--ignore E,W').options
191187
self.assertEqual(options.select, ())
192188
self.assertEqual(options.ignore, ('E', 'W'))

testsuite/test_blank_lines.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import unittest
77

88
import pycodestyle
9-
from testsuite.support import errors_from_src
9+
from testing.support import errors_from_src
1010

1111

1212
class BlankLinesTestCase(unittest.TestCase):

testsuite/test_pycodestyle.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import pytest
2+
3+
from pycodestyle import expand_indent
4+
from pycodestyle import mute_string
5+
6+
7+
@pytest.mark.parametrize(
8+
('s', 'expected'),
9+
(
10+
(' ', 4),
11+
('\t', 8),
12+
(' \t', 8),
13+
(' \t', 16),
14+
),
15+
)
16+
def test_expand_indent(s, expected):
17+
assert expand_indent(s) == expected
18+
19+
20+
@pytest.mark.parametrize(
21+
('s', 'expected'),
22+
(
23+
('"abc"', '"xxx"'),
24+
("'''abc'''", "'''xxx'''"),
25+
("r'abc'", "r'xxx'"),
26+
),
27+
)
28+
def test_mute_string(s, expected):
29+
assert mute_string(s) == expected

0 commit comments

Comments
 (0)