Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for PEP 257 #2

Open
wants to merge 1 commit into
base: main
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
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,4 @@ Contributors
- Skyler Grey <sky@a.starrysky.fyi>
- Emil Velikov <emil.l.velikov@gmail.com>
- Linnea Gräf <nea@nea.moe>
- Charlie Jenkins <charlie@rivosinc.com>
2 changes: 2 additions & 0 deletions changelog.d/changed/removed-space-before-docstring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- To conform to PEP 257, do not put a space before a docstring at the beginning
of python formatted files. (#1136)
20 changes: 18 additions & 2 deletions src/reuse/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# SPDX-FileCopyrightText: 2022 Florian Snow <florian@familysnow.net>
# SPDX-FileCopyrightText: 2022 Yaman Qalieh
# SPDX-FileCopyrightText: 2022 Carmen Bianca Bakker <carmenbianca@fsfe.org>
# SPDX-FileCopyrightText: 2025 Charles Jenkins <charlie@rivosinc.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later

Expand Down Expand Up @@ -39,6 +40,7 @@
DEFAULT_TEMPLATE = _ENV.get_template("default_template.jinja2")

_NEWLINE_PATTERN = re.compile(r"\n", re.MULTILINE)
_DOCSTRING_PATTERN = re.compile(r'"""[\S\s]*"""', re.MULTILINE)


class _TextSections(NamedTuple):
Expand Down Expand Up @@ -215,6 +217,20 @@ def _extract_shebang(prefix: str, text: str) -> tuple[str, str]:
return (shebang, text)


def _get_separator(style: Type[CommentStyle], text: str) -> str:
"""Define separator between the license and the body of the text."""
separator = "\n"

if style == PythonCommentStyle:
# PEP 257 requires that docstrings are the first line of the file, so
# there must not be a space after the license.
docstring = _DOCSTRING_PATTERN.match(text)
if docstring:
separator = ""

return separator


# pylint: disable=too-many-arguments
def find_and_replace_header(
text: str,
Expand Down Expand Up @@ -289,7 +305,7 @@ def find_and_replace_header(
if before.strip():
new_text = f"{before.rstrip()}\n\n{new_text}"
if after.strip():
new_text = f"{new_text}\n{after.lstrip()}"
new_text = f"{new_text}{_get_separator(style, after)}{after.lstrip()}"
return new_text


Expand Down Expand Up @@ -335,5 +351,5 @@ def add_new_header(
if shebang.strip():
new_text = f"{shebang.rstrip()}\n\n{new_text}"
if text.strip():
new_text = f"{new_text}\n{text.lstrip()}"
new_text = f"{new_text}{_get_separator(style, text)}{text.lstrip()}"
return new_text
78 changes: 78 additions & 0 deletions tests/test_header.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,48 @@ def test_find_and_replace_no_header():
)


def test_find_and_replace_no_header_docstring():
"""Given text with docstring and without header, add a header."""
info = ReuseInfo({"GPL-3.0-or-later"}, {"SPDX-FileCopyrightText: Jane Doe"})
text = '"""docstring"""'
expected = cleandoc(
"""
# SPDX-FileCopyrightText: Jane Doe
#
# SPDX-License-Identifier: GPL-3.0-or-later
\"\"\"docstring\"\"\"
"""
)

assert (
find_and_replace_header(text, info)
== add_new_header(text, info)
== expected
)


def test_find_and_replace_no_header_multiline_docstring():
"""Given text with multiline docstring and without header, add a header."""
info = ReuseInfo({"GPL-3.0-or-later"}, {"SPDX-FileCopyrightText: Jane Doe"})
text = '"""docstring\ndocstring continued\n"""'
expected = cleandoc(
"""
# SPDX-FileCopyrightText: Jane Doe
#
# SPDX-License-Identifier: GPL-3.0-or-later
\"\"\"docstring
docstring continued
\"\"\"
"""
)

assert (
find_and_replace_header(text, info)
== add_new_header(text, info)
== expected
)


def test_find_and_replace_verbatim():
"""Replace a header with itself."""
info = ReuseInfo()
Expand Down Expand Up @@ -459,4 +501,40 @@ def test_find_and_replace_preserve_newline():
assert find_and_replace_header(text, info) == text


def test_find_and_replace_preserve_no_newline_docstring():
"""If the file content doesn't have a newline and has a docstring,
don't add a newline."""

info = ReuseInfo()
text = cleandoc(
"""
# SPDX-FileCopyrightText: Jane Doe
#
# SPDX-License-Identifier: GPL-3.0-or-later
\"\"\"docstring\"\"\"
"""
)

assert find_and_replace_header(text, info) == text


def test_find_and_replace_preserve_no_newline_mulitline_docstring():
"""If the file content doesn't have a newline and has a docstring,
don't add a newline."""

info = ReuseInfo()
text = cleandoc(
"""
# SPDX-FileCopyrightText: Jane Doe
#
# SPDX-License-Identifier: GPL-3.0-or-later
\"\"\"docstring
docstring continued
\"\"\"
"""
)

assert find_and_replace_header(text, info) == text


# REUSE-IgnoreEnd