Skip to content
This repository was archived by the owner on Nov 3, 2023. It is now read-only.

Commit c987558

Browse files
lordmauvesambhav
authored andcommitted
Add error D303 when an f-string is used as a docstring
1 parent d85735e commit c987558

File tree

5 files changed

+62
-1
lines changed

5 files changed

+62
-1
lines changed

docs/release_notes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Major Updates
1515
New Features
1616

1717
* Add flag to disable `# noqa` comment processing in API (#485).
18+
* New error code D303 is emitted when an f-string is found in place of a
19+
docstring.
1820

1921
Bug Fixes
2022

src/pydocstyle/checker.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ def decorator(f):
4646
return decorator
4747

4848

49+
50+
FSTRING_RE = re(r'^[rR]?[fF]')
51+
52+
53+
def is_fstring(docstring):
54+
r"""Return True if docstring is an f-string.
55+
56+
>>> is_fstring('rf"abc"')
57+
True
58+
>>> is_fstring('F"abc"')
59+
True
60+
>>> is_fstring("u'''abc'''")
61+
False
62+
"""
63+
return FSTRING_RE.match(str(docstring))
64+
65+
66+
4967
class ConventionChecker:
5068
"""Checker for PEP 257, NumPy and Google conventions.
5169
@@ -180,6 +198,17 @@ def checks(self):
180198
]
181199
return sorted(all, key=lambda this_check: not this_check._terminal)
182200

201+
@check_for(Definition, terminal=True)
202+
def check_docstring_fstring(self, definition, docstring):
203+
"""D303: Docstrings may not be f-strings.
204+
205+
f-strings are not treated as string literals, but they look similar
206+
and users may attempt to use them as docstrings. This is an
207+
outright mistake so we issue a specific error code.
208+
"""
209+
if is_fstring(docstring):
210+
return violations.D303()
211+
183212
@check_for(Definition, terminal=True)
184213
def check_docstring_missing(self, definition, docstring):
185214
"""D10{0,1,2,3}: Public definitions should have docstrings.
@@ -194,6 +223,9 @@ def check_docstring_missing(self, definition, docstring):
194223
with a single underscore.
195224
196225
"""
226+
if is_fstring(docstring):
227+
return # checked above in check_docstring_fstring
228+
197229
if (
198230
not docstring
199231
and definition.is_public

src/pydocstyle/violations.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ def to_rst(cls) -> str:
312312
'D302',
313313
'Deprecated: Use u""" for Unicode docstrings',
314314
)
315+
D303 = D3xx.create_error('D303', 'f-strings are not valid as docstrings')
315316

316317
D4xx = ErrorRegistry.create_group('D4', 'Docstring Content Issues')
317318
D400 = D4xx.create_error(

src/tests/test_cases/fstrings.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""Test for warning about f-strings as docstrings."""
2+
3+
from .expected import Expectation
4+
5+
expectation = Expectation()
6+
expect = expectation.expect
7+
8+
9+
@expect("D303: f-strings are not valid as docstrings")
10+
def fstring():
11+
f"""Toggle the gizmo."""

src/tests/test_definitions.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Old parser tests."""
22

3+
import sys
34
import os
45
import re
56
import pytest
@@ -24,7 +25,21 @@
2425
'canonical_numpy_examples',
2526
'canonical_pep257_examples',
2627
])
27-
def test_complex_file(test_case):
28+
def test_all_interpreters(test_case):
29+
"""Complex test cases that run under all interpreters."""
30+
run_case(test_case)
31+
32+
33+
@pytest.mark.skipif(
34+
sys.version_info < (3, 6),
35+
reason="f-string support needed"
36+
)
37+
def test_fstrings():
38+
"""Run the f-string test case under Python 3.6+ only."""
39+
run_case('fstrings')
40+
41+
42+
def run_case(test_case):
2843
"""Run domain-specific tests from test.py file."""
2944
case_module = __import__(f'test_cases.{test_case}',
3045
globals=globals(),

0 commit comments

Comments
 (0)