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

Add support for PEP701 #656

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ Release Notes
**pydocstyle** version numbers follow the
`Semantic Versioning <http://semver.org/>`_ specification.


Current development version
---------------------------

Bug Fixes

* Add support for PEP-701 fixing fstring parsing in python3.12 (#656).

6.3.0 - January 17th, 2023
--------------------------

Expand Down
23 changes: 23 additions & 0 deletions src/pydocstyle/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,29 @@ def parse_docstring(self):
)
self.stream.move()
return docstring
if (sys.version_info.major, sys.version_info.minor) >= (
3,
12,
) and self.current.kind == tk.FSTRING_START:

def fstring(string):
"""Recursively parse fstring tokens to output it as one string."""
while self.current.kind != tk.FSTRING_END:
self.stream.move()
string += self.current.value
if self.current.kind == tk.FSTRING_START:
string = fstring(string)
self.stream.move()
string += self.current.value
return string

# Reattach fstring tokens together into a string to deal with PEP 701 in python3.12
start = self.current.start[0]
string = fstring(self.current.value)
end = self.current.end[0]
docstring = Docstring(string, start, end)
self.stream.move()
return docstring
return None

def parse_decorators(self):
Expand Down
29 changes: 29 additions & 0 deletions src/tests/parser_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,35 @@ def do_something(pos_param0, pos_param1, kw_param0="default"):
assert str(function) == 'in public function `do_something`'


def test_nested_fstring():
"""Test parsing fstring with nested fstrings."""
parser = Parser()
code = CodeSnippet("""\
def do_something(pos_param0, pos_param1, kw_param0="default"):
f\"""Do something. {f"This is a nested fstring."}\"""
return None
""")
module = parser.parse(code, 'file_path')
assert module.is_public
assert module.dunder_all is None

function, = module.children
assert function.name == 'do_something'
assert function.decorators == []
assert function.children == []
assert function.docstring == 'f"""Do something. {f"This is a nested fstring."}"""'
assert function.docstring.start == 2
assert function.docstring.end == 2
assert function.kind == 'function'
assert function.parent == module
assert function.start == 1
assert function.end == 3
assert function.error_lineno == 2
assert function.source == code.getvalue()
assert function.is_public
assert str(function) == 'in public function `do_something`'


def test_decorated_function():
"""Test parsing of a simple function with a decorator."""
parser = Parser()
Expand Down