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

blurb: replace spaces with underscores in news directory #499

Merged
merged 17 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 16 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
41 changes: 32 additions & 9 deletions blurb/blurb.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
## Licensed to the Python Software Foundation under a contributor agreement.
##


# TODO
#
# automatic git adds and removes
Expand Down Expand Up @@ -112,17 +111,37 @@

def sanitize_section(section):
"""
Cleans up a section string, making it viable as a directory name.
Clean up a section string, making it viable as a directory name.
"""
return section.replace("/", "-").replace(" ", "_")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make things more explicit/readable, this could use a dict similar to the one used below for unsanitize_section.
If new sections that require sanitation are added in the future the function won't work, but it will serve as a reminder to update both dicts.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.



def sanitize_section_legacy(section):
"""
Clean up a section string, making it viable as a directory name (allow spaces).
"""
return section.replace("/", "-")


_unsanitize_section = {
"C_API": "C API",
"Core_and_Builtins": "Core and Builtins",
hugovk marked this conversation as resolved.
Show resolved Hide resolved
"Tools-Demos": "Tools/Demos",
}


def unsanitize_section(section):
return _unsanitize_section.get(section, section)

def next_filename_unsanitize_sections(filename):
s = filename
for key, value in _unsanitize_section.items():
for separator in "/\\":
key = f"{separator}{key}{separator}"
value = f"{separator}{value}{separator}"
filename = filename.replace(key, value)
return filename


def textwrap_body(body, *, subsequent_indent=''):
"""
Expand Down Expand Up @@ -300,14 +319,18 @@ def glob_blurbs(version):
wildcard = base + ".rst"
filenames.extend(glob.glob(wildcard))
else:
for section in sections:
wildcard = os.path.join(base, sanitize_section(section), "*.rst")
sanitized_sections = (
{sanitize_section(section) for section in sections} |
{sanitize_section_legacy(section) for section in sections}
)
for section in sanitized_sections:
wildcard = os.path.join(base, section, "*.rst")
entries = glob.glob(wildcard)
entries.sort(reverse=True)
deletables = [x for x in entries if x.endswith("/README.rst")]
for filename in deletables:
entries.remove(filename)
filenames.extend(entries)
filenames.sort(reverse=True, key=next_filename_unsanitize_sections)
return filenames


Expand Down Expand Up @@ -537,8 +560,8 @@ def save(self, path):
@staticmethod
def _parse_next_filename(filename):
"""
Parses a "next" filename into its equivalent blurb metadata.
Returns a dict.
Parses a "next" filename into its equivalent blurb metadata.
Returns a dict.
"""
components = filename.split(os.sep)
section, filename = components[-2:]
Expand All @@ -552,7 +575,7 @@ def _parse_next_filename(filename):
metadata = {"date": fields[0], "nonce": fields[-2], "section": section}

for field in fields[1:-2]:
for name in ("gh-issue","bpo"):
for name in ("gh-issue", "bpo"):
_, got, value = field.partition(name + "-")
if got:
metadata[name] = value.strip()
Expand Down Expand Up @@ -589,7 +612,7 @@ def _extract_next_filename(self):
metadata, body = self[-1]
metadata['section'] = sanitize_section(metadata['section'])
metadata['root'] = root
if int(metadata["gh-issue"]) > 0 :
if int(metadata["gh-issue"]) > 0:
path = "{root}/Misc/NEWS.d/next/{section}/{date}.gh-issue-{gh-issue}.{nonce}.rst".format_map(metadata)
elif int(metadata["bpo"]) > 0:
# assume it's a GH issue number
Expand Down
85 changes: 63 additions & 22 deletions blurb/tests/test_blurb.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,31 @@


UNCHANGED_SECTIONS = (
"C API",
"Core and Builtins",
"Library",
)


@pytest.mark.parametrize("section", UNCHANGED_SECTIONS)
def test_sanitize_section_no_change(section: str) -> None:
def test_sanitize_section_no_change(section):
sanitized = blurb.sanitize_section(section)
assert sanitized == section


@pytest.mark.parametrize(
"section, expected",
(
("C API", "C_API"),
("Core and Builtins", "Core_and_Builtins"),
("Tools/Demos", "Tools-Demos"),
),
)
def test_sanitize_section_changed(section: str, expected: str) -> None:
def test_sanitize_section_changed(section, expected):
sanitized = blurb.sanitize_section(section)
assert sanitized == expected


@pytest.mark.parametrize("section", UNCHANGED_SECTIONS)
def test_unsanitize_section_no_change(section: str) -> None:
def test_unsanitize_section_no_change(section):
unsanitized = blurb.unsanitize_section(section)
assert unsanitized == section

Expand All @@ -40,12 +40,12 @@ def test_unsanitize_section_no_change(section: str) -> None:
("Tools-Demos", "Tools/Demos"),
),
)
def test_unsanitize_section_changed(section: str, expected: str) -> None:
def test_unsanitize_section_changed(section, expected):
unsanitized = blurb.unsanitize_section(section)
assert unsanitized == expected


def test_glob_blurbs_next(fs: FakeFilesystem) -> None:
def test_glob_blurbs_next(fs):
# Arrange
fake_news_entries = (
"Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-11111.pC7gnM.rst",
Expand All @@ -69,28 +69,71 @@ def test_glob_blurbs_next(fs: FakeFilesystem) -> None:
assert set(filenames) == set(fake_news_entries)


def test_glob_blurbs_sort_order(fs):
"""
It shouldn't make a difference to sorting whether
section names have spaces or underscores.
"""
# Arrange
fake_news_entries = (
"Misc/NEWS.d/next/Core and Builtins/2023-07-23-12-01-00.gh-issue-33331.Pf_BI1.rst",
"Misc/NEWS.d/next/Core_and_Builtins/2023-07-23-12-02-00.gh-issue-33332.Pf_BI2.rst",
"Misc/NEWS.d/next/Core and Builtins/2023-07-23-12-03-00.gh-issue-33333.Pf_BI3.rst",
"Misc/NEWS.d/next/Core_and_Builtins/2023-07-23-12-04-00.gh-issue-33334.Pf_BI4.rst",
)
# As fake_news_entries, but reverse sorted by *filename* only
expected = [
"Misc/NEWS.d/next/Core_and_Builtins/2023-07-23-12-04-00.gh-issue-33334.Pf_BI4.rst",
"Misc/NEWS.d/next/Core and Builtins/2023-07-23-12-03-00.gh-issue-33333.Pf_BI3.rst",
"Misc/NEWS.d/next/Core_and_Builtins/2023-07-23-12-02-00.gh-issue-33332.Pf_BI2.rst",
"Misc/NEWS.d/next/Core and Builtins/2023-07-23-12-01-00.gh-issue-33331.Pf_BI1.rst",
]
fake_readmes = (
"Misc/NEWS.d/next/Library/README.rst",
"Misc/NEWS.d/next/Core and Builtins/README.rst",
"Misc/NEWS.d/next/Tools-Demos/README.rst",
"Misc/NEWS.d/next/C API/README.rst",
)
for fn in fake_news_entries + fake_readmes:
fs.create_file(fn)

# Act
filenames = blurb.glob_blurbs("next")

# Assert
assert filenames == expected


@pytest.mark.parametrize(
"news_entry, expected_section",
(
(
"Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-55555.pC7gnM.rst",
"Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst",
"Library",
),
(
"Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-33333.Pf_BI7.rst",
"Misc/NEWS.d/next/Core_and_Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst",
"Core and Builtins",
),
(
"Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-55555.Pf_BI7.rst",
"Core and Builtins",
),
(
"Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-44444.2F1Byz.rst",
"Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-66666.2F1Byz.rst",
"Tools/Demos",
),
(
"Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst",
"Misc/NEWS.d/next/C_API/2023-03-27-22-09-07.gh-issue-77777.3SN8Bs.rst",
"C API",
),
(
"Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-88888.3SN8Bs.rst",
"C API",
),
),
)
def test_load_next(news_entry: str, expected_section: str, fs: FakeFilesystem) -> None:
def test_load_next(news_entry, expected_section, fs):
# Arrange
fs.create_file(news_entry, contents="testing")
blurbs = blurb.Blurbs()
Expand All @@ -107,26 +150,24 @@ def test_load_next(news_entry: str, expected_section: str, fs: FakeFilesystem) -
"news_entry, expected_path",
(
(
"Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-55555.pC7gnM.rst",
"root/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-55555.pC7gnM.rst",
"Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst",
"root/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-33333.pC7gnM.rst",
),
(
"Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-33333.Pf_BI7.rst",
"root/Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-33333.Pf_BI7.rst",
"Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst",
"root/Misc/NEWS.d/next/Core_and_Builtins/2023-03-17-12-09-45.gh-issue-44444.Pf_BI7.rst",
),
(
"Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-44444.2F1Byz.rst",
"root/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-44444.2F1Byz.rst",
"Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-55555.2F1Byz.rst",
"root/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-55555.2F1Byz.rst",
),
(
"Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst",
"root/Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst",
"root/Misc/NEWS.d/next/C_API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst",
),
),
)
def test_extract_next_filename(
news_entry: str, expected_path: str, fs: FakeFilesystem
) -> None:
def test_extract_next_filename(news_entry, expected_path, fs):
# Arrange
fs.create_file(news_entry, contents="testing")
blurb.root = "root"
Expand Down